[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
channel_flow.cpp
1 #include "channel_flow.h"
2 #include <deal.II/dofs/dof_tools.h>
3 #include <deal.II/fe/fe_values.h>
4 #include "math.h"
5 #include <deal.II/base/quadrature_lib.h>
6 #include <deal.II/grid/grid_generator.h>
7 #include <deal.II/grid/grid_tools.h>
8 // #include "mesh/gmsh_reader.hpp" // uncomment this to use the gmsh reader
9 #include "physics/physics_factory.h"
10 
11 namespace PHiLiP {
12 
13 namespace FlowSolver {
14 
15 //=========================================================
16 // CHANNEL FLOW CLASS
17 //=========================================================
18 template <int dim, int nspecies, int nstate>
20  : PeriodicTurbulence<dim, nspecies, nstate>(parameters_input)
21  , channel_height(this->all_param.flow_solver_param.turbulent_channel_domain_length_y_direction)
22  , half_channel_height(channel_height/2.0)
23  , channel_friction_velocity_reynolds_number(this->all_param.flow_solver_param.turbulent_channel_friction_velocity_reynolds_number)
24  , number_of_cells_x_direction(this->all_param.flow_solver_param.turbulent_channel_number_of_cells_x_direction)
25  , number_of_cells_y_direction(this->all_param.flow_solver_param.turbulent_channel_number_of_cells_y_direction)
26  , number_of_cells_z_direction(this->all_param.flow_solver_param.turbulent_channel_number_of_cells_z_direction)
27  , pi_val(3.141592653589793238)
28  , domain_length_x(this->all_param.flow_solver_param.turbulent_channel_domain_length_x_direction)
29  , domain_length_y(channel_height)
30  , domain_length_z(this->all_param.flow_solver_param.turbulent_channel_domain_length_z_direction)
31  , domain_volume(domain_length_x*domain_length_y*domain_length_z)
32  , channel_bulk_velocity_reynolds_number(pow(0.073, -4.0/7.0)*pow(2.0, 5.0/7.0)*pow(channel_friction_velocity_reynolds_number, 8.0/7.0))
33  , channel_centerline_velocity_reynolds_number(1.28*pow(2.0, -0.0116)*pow(channel_bulk_velocity_reynolds_number,1.0-0.0116))
34 {
35  // initialize zero tensor
36  for (int d1=0; d1<dim; ++d1) {
37  for (int d2=0; d2<dim; ++d2) {
38  zero_tensor[d1][d2] = 0.0;
39  }
40  }
41 
42  // NavierStokes_ChannelFlowConstantSourceTerm_WallModel object; create using dynamic_pointer_cast and the create_Physics factory
44  PHiLiP::Parameters::AllParameters parameters_navier_stokes_channel_flow_constant_source_term_wall_model = this->all_param;
45  parameters_navier_stokes_channel_flow_constant_source_term_wall_model.pde_type = PDE_enum::navier_stokes_channel_flow_constant_source_term_wall_model;
48  Physics::PhysicsFactory<dim,nspecies,dim+2,double>::create_Physics(&parameters_navier_stokes_channel_flow_constant_source_term_wall_model));
49 }
50 
51 template <int dim, int nspecies, int nstate>
53  const std::shared_ptr <ODE::ODESolverBase<dim, nspecies, double>> ode_solver,
54  const std::shared_ptr <DGBase<dim, nspecies, double>> dg,
55  const std::shared_ptr <dealii::TableHandler> unsteady_data_table,
56  const bool do_write_unsteady_data_table_file)
57 {
58  // unpack current iteration and current time from ode solver
59  const unsigned int current_iteration = ode_solver->current_iteration;
60  const double current_time = ode_solver->current_time;
61  // Update maximum local wave speed for adaptive time_step
63  // get averaged wall shear stress
64  double average_wall_shear_stress = 0.0;
65  if(this->all_param.using_wall_model) average_wall_shear_stress = get_average_wall_shear_stress_from_wall_model(*dg);
66  else average_wall_shear_stress = get_average_wall_shear_stress(*dg);
67 
69  const double skin_friction_coefficient = get_skin_friction_coefficient_from_average_wall_shear_stress(average_wall_shear_stress);
70 
71  if(this->mpi_rank==0) {
72  // Add values to data table
73  this->add_value_to_data_table(current_time,"time",unsteady_data_table);
74  this->add_value_to_data_table(average_wall_shear_stress,"tau_w",unsteady_data_table);
75  this->add_value_to_data_table(skin_friction_coefficient,"skin_friction_coefficient",unsteady_data_table);
76  this->add_value_to_data_table(bulk_density,"bulk_density",unsteady_data_table);
77  this->add_value_to_data_table(bulk_velocity,"bulk_velocity",unsteady_data_table);
78  // Write to file
79  if(do_write_unsteady_data_table_file) {
80  std::ofstream unsteady_data_table_file(this->unsteady_data_table_filename_with_extension);
81  unsteady_data_table->write_text(unsteady_data_table_file);
82  }
83  }
84 
85  // Print to console
86  this->pcout << " Iter: " << current_iteration
87  << " Time: " << current_time
88  << " Cf: " << skin_friction_coefficient
89  << " Ub: " << this->bulk_velocity
90  << " BulkMassFlow: " << this->bulk_mass_flow_rate
91  << std::endl;
92 
93  // NOTE: Would be good to print t/2pi and Re_b calculated to track the convergence of the flow; add these to the table
94 
95  // Abort if average_wall_shear_stress is nan
96  if(std::isnan(average_wall_shear_stress)) {
97  this->pcout << " ERROR: Wall shear stress at time " << current_time << " is nan." << std::endl;
98  this->pcout << " Consider decreasing the time step / CFL number." << std::endl;
99  std::abort();
100  }
101 
102  // Output velocity field if current time is output file
104 }
105 
106 template <int dim, int nspecies, int nstate>
108 {
110  this->pcout << "- - Courant-Friedrichs-Lewy number: " << this->all_param.flow_solver_param.courant_friedrichs_lewy_number << std::endl;
111  else
112  this->pcout << "- - Constant time step: " << this->all_param.flow_solver_param.constant_time_step << std::endl;
113  this->pcout << "- - Freestream Mach number: " << this->all_param.euler_param.mach_inf << std::endl;
114  this->pcout << "- - Freestream Reynolds number: " << this->all_param.navier_stokes_param.reynolds_number_inf << std::endl;
115  this->pcout << "- - Reynolds number based on wall friction velocity: " << this->channel_friction_velocity_reynolds_number << std::endl;
116  this->pcout << "- - Reynolds number based on bulk velocity: " << this->channel_bulk_velocity_reynolds_number << std::endl;
117  this->pcout << "- - Reynolds number based on centerline velocity: " << this->channel_centerline_velocity_reynolds_number << std::endl;
118  this->pcout << "- - Half channel height: " << this->half_channel_height << std::endl;
119  this->display_grid_parameters();
120 }
121 
122 template <int dim, int nspecies, int nstate>
124 {
125  const std::string grid_type_string = "subdivided_hyper_rectangle_for_channel_flow";
126  // Display the information about the grid
127  this->pcout << "- Grid type: " << grid_type_string << std::endl;
128  this->pcout << "- - Grid degree: " << this->all_param.flow_solver_param.grid_degree << std::endl;
129  this->pcout << "- - Domain dimensionality: " << dim << std::endl;
130  this->pcout << "- - Domain length x: " << this->domain_length_x << std::endl;
131  this->pcout << "- - Domain length y: " << this->domain_length_y << std::endl;
132  this->pcout << "- - Domain length z: " << this->domain_length_z << std::endl;
133  this->pcout << "- - Number of cells in x-direction: " << this->number_of_cells_x_direction << std::endl;
134  this->pcout << "- - Number of cells in y-direction: " << this->number_of_cells_y_direction << std::endl;
135  this->pcout << "- - Number of cells in z-direction: " << this->number_of_cells_z_direction << std::endl;
136  using turbulent_channel_mesh_stretching_function_enum = Parameters::FlowSolverParam::TurbulentChannelMeshStretchingFunctionType;
137  const turbulent_channel_mesh_stretching_function_enum turbulent_channel_mesh_stretching_function_type = this->all_param.flow_solver_param.turbulent_channel_mesh_stretching_function_type;
138  std::string turbulent_channel_mesh_stretching_function_type_string;
139  if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::gullbrand){
140  turbulent_channel_mesh_stretching_function_type_string = "Gullbrand";
141  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::hopw){
142  turbulent_channel_mesh_stretching_function_type_string = "HOPW";
143  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::carton_de_wiart_et_al){
144  turbulent_channel_mesh_stretching_function_type_string = "carton_de_wiart_et_al";
145  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::uniform_mesh_no_stretching){
146  turbulent_channel_mesh_stretching_function_type_string = "uniform_mesh_no_stretching";
147  }
148  this->pcout << "- - Mesh stretching function: " << turbulent_channel_mesh_stretching_function_type_string << std::endl;
149 }
150 
151 template <int dim, int nspecies, int nstate>
153 {
154  // compute time step based on advection speed (i.e. maximum local wave speed)
155  const double cfl_number = this->all_param.flow_solver_param.courant_friedrichs_lewy_number;
156  const double time_step = cfl_number * this->minimum_approximate_grid_spacing / this->maximum_local_wave_speed;
157  return time_step;
158 }
159 
160 template <int dim, int nspecies, int nstate>
162 {
163  // initialize the maximum local wave speed
165  // set the minimum approximate grid spacing
166  const double minimum_element_size = get_mesh_step_size_y_direction()[0]; // smallest spacing occurs next to wall (i.e. first/last element)
167  this->minimum_approximate_grid_spacing = minimum_element_size/double(this->all_param.flow_solver_param.poly_degree+1);
168  // compute time step based on advection speed (i.e. maximum local wave speed)
169  const double time_step = get_adaptive_time_step(dg);
170  return time_step;
171 }
172 
173 template <int dim, int nspecies, int nstate>
175 {
176  using turbulent_channel_mesh_stretching_function_enum = Parameters::FlowSolverParam::TurbulentChannelMeshStretchingFunctionType;
177  const turbulent_channel_mesh_stretching_function_enum turbulent_channel_mesh_stretching_function_type = this->all_param.flow_solver_param.turbulent_channel_mesh_stretching_function_type;
178  std::vector<double> step_size_y_direction;
179  if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::gullbrand){
180  step_size_y_direction = get_mesh_step_size_y_direction_Gullbrand();
181  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::hopw){
182  step_size_y_direction = get_mesh_step_size_y_direction_HOPW();
183  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::carton_de_wiart_et_al){
185  } else if(turbulent_channel_mesh_stretching_function_type == turbulent_channel_mesh_stretching_function_enum::uniform_mesh_no_stretching){
186  // for wall model use uniform grid (i.e. no stretching)
187  const double uniform_spacing_y = this->domain_length_y/double(this->number_of_cells_y_direction);
188  for (int j=0; j<this->number_of_cells_y_direction; j++) {
189  step_size_y_direction.push_back(uniform_spacing_y);
190  }
191  } else {
192  this->pcout << "ERROR: Invalid turbulent_channel_mesh_stretching_function_type. Aborting..." << std::endl;
193  std::abort();
194  }
195  return step_size_y_direction;
196 }
197 
198 template <int dim, int nspecies, int nstate>
200 {
201  const int number_of_edges_y_direction = number_of_cells_y_direction+1;
202  std::vector<double> element_edges_y_direction(number_of_edges_y_direction);
203  // - Note: This stretching function comes from the structured GMSH .geo file obtained from https://how5.cenaero.be/content/ws2-les-plane-channel-ret550
204  const double N_streching_param = 1.0;
205  const double r_streching_param = pow(1.2,N_streching_param/2.0);
206  const double num_cells_y = (double)number_of_cells_y_direction;
207  const double h0_streching_param = 0.5*(1.0-r_streching_param)/(1.0-pow(r_streching_param,(num_cells_y/2.0)));
208  const int max_loop_index = (int)((num_cells_y-2.0)/2.0);
209  double h_streching_param = 0.0;
210  element_edges_y_direction[0] = h_streching_param;
211  for (int i=0; i<max_loop_index; i++) {
212  h_streching_param += h0_streching_param*pow(r_streching_param,(double)i);
213  element_edges_y_direction[i+1] = h_streching_param;
214  element_edges_y_direction[number_of_cells_y_direction-i-1] = 1.0-h_streching_param;
215  }
216  element_edges_y_direction[(int)(num_cells_y/2.0)] = 0.5;
217  element_edges_y_direction[number_of_cells_y_direction] = 1.0;
218  // - now multiply these defined for $y\in[0,1]$ by the length of the domain in the y-direction
219  for (int j=0; j<number_of_edges_y_direction; j++) {
220  element_edges_y_direction[j] *= domain_length_y;
221  }
222  // - compute the step size in y-direction as the difference between element edges in y-direction
223  std::vector<double> step_size_y_direction(number_of_cells_y_direction);
224  for (int j=0; j<number_of_cells_y_direction; j++) {
225  step_size_y_direction[j] = element_edges_y_direction[j+1] - element_edges_y_direction[j];
226  }
227  return step_size_y_direction;
228 }
229 
230 template <int dim, int nspecies, int nstate>
232 {
233  // Domain lower bound in y-direction
234  const double desired_domain_lower_bound_y = 0.0; // for convenient wall distance calculation
235  const double domain_shift = desired_domain_lower_bound_y+1.0; // +1 since domain lower bound from original function is -1
236  // - get stretched spacing for y-direction to capture boundary layer
237  const int number_of_edges_y_direction = number_of_cells_y_direction+1;
238  std::vector<double> element_edges_y_direction(number_of_edges_y_direction);
242  const double num_cells_y = (double)number_of_cells_y_direction;
243  const double stretching_parameter = 2.75;
244  const double tanh_stretching_parameter = tanh(stretching_parameter);
245  for (int j=0; j<number_of_edges_y_direction; j++) {
246  element_edges_y_direction[j] = -1.0*tanh(stretching_parameter*(1.0 - 2.0*((double)j)/num_cells_y))/tanh_stretching_parameter;
247  }
248  // - now apply the domain shift since these are currently defined in the domain $y\in[-1,1]$
249  // (NOTE: this has no affect on the returned vector of step sizes, but is included for completeness)
250  for (int j=0; j<number_of_edges_y_direction; j++) {
251  element_edges_y_direction[j] += domain_shift;
252  element_edges_y_direction[j] /= 2.0;
253  element_edges_y_direction[j] *= domain_length_y;
254  }
255  // - compute the step size in y-direction as the difference between element edges in y-direction
256  std::vector<double> step_size_y_direction(number_of_cells_y_direction);
257  for (int j=0; j<number_of_cells_y_direction; j++) {
258  step_size_y_direction[j] = element_edges_y_direction[j+1] - element_edges_y_direction[j];
259  }
260  return step_size_y_direction;
261 }
262 
263 template <int dim, int nspecies, int nstate>
265 {
266  // - get stretched spacing for y-direction to capture boundary layer
267  const int number_of_edges_y_direction = number_of_cells_y_direction+1;
268  std::vector<double> element_edges_y_direction(number_of_edges_y_direction);
272  const double num_cells_y = (double)number_of_cells_y_direction;
273  const double uniform_spacing = domain_length_y/num_cells_y;
274  for (int j=0; j<(number_of_cells_y_direction/2+1); j++) {
275  element_edges_y_direction[j] = 1.0 - cos(this->pi_val*((double)j)*uniform_spacing/2.0);
276  element_edges_y_direction[number_of_cells_y_direction-j] = domain_length_y-element_edges_y_direction[j];
277  }
278  // - compute the step size in y-direction as the difference between element edges in y-direction
279  std::vector<double> step_size_y_direction(number_of_cells_y_direction);
280  for (int j=0; j<number_of_cells_y_direction; j++) {
281  step_size_y_direction[j] = element_edges_y_direction[j+1] - element_edges_y_direction[j];
282  }
283  return step_size_y_direction;
284 }
285 
286 template <int dim, int nspecies, int nstate>
287 std::shared_ptr<Triangulation> ChannelFlow<dim, nspecies, nstate>::generate_grid() const
288 {
289  // // uncomment this to use the gmsh reader
290  // // Dummy triangulation
291  // // NOTE: Avoid reading the mesh twice (here and in set_high_order_grid -- need a default dummy triangulation)
292  // const std::string mesh_filename = this->all_param.flow_solver_param.input_mesh_filename+std::string(".msh");
293  // const bool use_mesh_smoothing = false;
294  // const int grid_order = 0;
295  // std::shared_ptr<HighOrderGrid<dim,double>> mesh = read_gmsh<dim, dim> (mesh_filename, grid_order, use_mesh_smoothing);
296  // return mesh->triangulation;
297 
298  // define domain to be centered about x, y, and z axes
299  const dealii::Point<dim> p1(-0.5*domain_length_x, -0.5*domain_length_y, -0.5*domain_length_z);
300  const dealii::Point<dim> p2(0.5*domain_length_x, 0.5*domain_length_y, 0.5*domain_length_z);
301 
302  // get step size for each cell
303  // - uniform spacing in x and z
304  const double uniform_spacing_x = domain_length_x/double(number_of_cells_x_direction);
305  const double uniform_spacing_z = domain_length_z/double(number_of_cells_z_direction);
306  // - get stretched spacing for y-direction to capture boundary layer
307  std::vector<double> step_size_y_direction = get_mesh_step_size_y_direction();
308 
309  std::vector<std::vector<double> > step_sizes(dim);
310  // x-direction
311  for (int i=0; i<number_of_cells_x_direction; i++) {
312  step_sizes[0].push_back(uniform_spacing_x);
313  }
314  // y-direction
315  for (int j=0; j<number_of_cells_y_direction; j++) {
316  step_sizes[1].push_back(step_size_y_direction[j]);
317  }
318  // z-direction
319  for (int k=0; k<number_of_cells_z_direction; k++) {
320  step_sizes[2].push_back(uniform_spacing_z);
321  }
322 
323  // generate grid usign dealii
324  std::shared_ptr<Triangulation> grid = std::make_shared<Triangulation> (this->mpi_communicator);
325  const bool colorize = true;
326  dealii::GridGenerator::subdivided_hyper_rectangle(*grid, step_sizes, p1, p2, colorize);
327 
328  // assign periodic boundary conditions in x and z
329  std::vector<dealii::GridTools::PeriodicFacePair<typename dealii::Triangulation<dim>::cell_iterator> > matched_pairs;
330  dealii::GridTools::collect_periodic_faces(*grid,0,1,0,matched_pairs); // x-direction
331  dealii::GridTools::collect_periodic_faces(*grid,4,5,2,matched_pairs); // z-direction
332  grid->add_periodicity(matched_pairs);
333 
334  // assign wall boundary conditions
335  for (typename Triangulation::active_cell_iterator cell = grid->begin_active(); cell != grid->end(); ++cell) {
336  if (!cell->is_locally_owned()) continue;
337 
338  for (unsigned int face=0; face<dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
339  if (cell->face(face)->at_boundary()) {
340  unsigned int current_id = cell->face(face)->boundary_id();
341  if (current_id == 2 || current_id == 3) cell->face(face)->set_boundary_id (1001); // Bottom and top wall
342  }
343  }
344  }
345 
346  return grid;
347 }
348 
349 template <int dim, int nspecies, int nstate>
351 {
352  // // uncomment this to use the gmsh reader
353  // const std::string mesh_filename = this->all_param.flow_solver_param.input_mesh_filename+std::string(".msh");
354  // const bool use_mesh_smoothing = false;
355  // const int grid_order = this->all_param.flow_solver_param.grid_degree;
356  // std::shared_ptr<HighOrderGrid<dim,double>> mesh = read_gmsh<dim, dim> (mesh_filename, grid_order, use_mesh_smoothing);
357  // dg->set_high_order_grid(mesh);
358 
359  // do nothing if using dealii mesh generator
360 }
361 
362 template <int dim, int nspecies, int nstate>
364 {
365  // expression for the channel flow grid (i.e. different number of cells in each direction)
366  const unsigned int number_of_degrees_of_freedom_per_state = this->number_of_cells_x_direction*(poly_degree_input+1)*
367  this->number_of_cells_y_direction*(poly_degree_input+1)*
368  this->number_of_cells_z_direction*(poly_degree_input+1);
369  return number_of_degrees_of_freedom_per_state;
370 }
371 
372 template<int dim, int nspecies, int nstate>
374 {
376  const dealii::UpdateFlags face_update_flags = dealii::update_values | dealii::update_gradients | dealii::update_quadrature_points | dealii::update_JxW_values | dealii::update_normal_vectors;
377  double integral_value = 0.0;
378  double integral_area_value = 0.0;
379 
380  // Overintegrate the error to make sure there is not integration error in the error estimate
381  int overintegrate = 10;
382  dealii::QGauss<dim-1> quad_extra(dg.max_degree+1+overintegrate);
383  dealii::FEFaceValues<dim,dim> fe_face_values_extra(*(dg.high_order_grid->mapping_fe_field), dg.fe_collection[dg.max_degree], quad_extra,
384  face_update_flags);
385 
386 
387  std::array<double,nstate> soln_at_q;
388  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
389 
390  std::vector<dealii::types::global_dof_index> dofs_indices (fe_face_values_extra.dofs_per_cell);
391  for (auto cell : dg.dof_handler.active_cell_iterators()) {
392  if (!cell->is_locally_owned()) continue;
393 
394  cell->get_dof_indices (dofs_indices);
395 
396  for(unsigned int iface = 0; iface < dealii::GeometryInfo<dim>::faces_per_cell; ++iface){
397  auto face = cell->face(iface);
398 
399  if(face->at_boundary()){
400  const unsigned int boundary_id = face->boundary_id();
401  if(boundary_id==1001){
402  fe_face_values_extra.reinit (cell,iface);
403  const unsigned int n_quad_pts = fe_face_values_extra.n_quadrature_points;
404  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
405  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
406  for (int s=0; s<nstate; ++s) {
407  for (int d=0; d<dim; ++d) {
408  soln_grad_at_q[s][d] = 0.0;
409  }
410  }
411  for (unsigned int idof=0; idof<fe_face_values_extra.dofs_per_cell; ++idof) {
412  const unsigned int istate = fe_face_values_extra.get_fe().system_to_component_index(idof).first;
413  soln_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_face_values_extra.shape_value_component(idof, iquad, istate);
414  soln_grad_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_face_values_extra.shape_grad_component(idof,iquad,istate);
415  }
416  // const dealii::Point<dim> qpoint = (fe_face_values_extra.quadrature_point(iquad));
417  const dealii::Tensor<1,dim,double> normal_vector = -fe_face_values_extra.normal_vector(iquad); // minus for wall normal from face normal
418  const double integrand_value = this->navier_stokes_physics->compute_wall_shear_stress(soln_at_q,soln_grad_at_q,normal_vector);
419  integral_value += integrand_value * fe_face_values_extra.JxW(iquad);
420  integral_area_value += fe_face_values_extra.JxW(iquad);
421  }
422  }
423  }
424  }
425  }
426  const double mpi_sum_integral_value = dealii::Utilities::MPI::sum(integral_value, this->mpi_communicator);
427  const double mpi_sum_integral_area_value = dealii::Utilities::MPI::sum(integral_area_value, this->mpi_communicator);
428  const double averaged_value = mpi_sum_integral_value/mpi_sum_integral_area_value;
429  return averaged_value;
430 }
431 
432 template<int dim, int nspecies, int nstate>
434 {
436  const dealii::UpdateFlags face_update_flags = dealii::update_values /*| dealii::update_gradients*/ | dealii::update_quadrature_points | dealii::update_JxW_values | dealii::update_normal_vectors;
437  double integral_value = 0.0;
438  double integral_area_value = 0.0;
439 
440  // Overintegrate the error to make sure there is not integration error in the error estimate
441  int overintegrate = 10;
442  dealii::QGauss<dim-1> quad_extra(dg.max_degree+1+overintegrate);
443  dealii::FEFaceValues<dim,dim> fe_face_values_extra(*(dg.high_order_grid->mapping_fe_field), dg.fe_collection[dg.max_degree], quad_extra,
444  face_update_flags);
445 
446 
447  std::array<double,nstate> soln_at_q;
448  // std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
449 
450  std::vector<dealii::types::global_dof_index> dofs_indices (fe_face_values_extra.dofs_per_cell);
451  for (auto cell : dg.dof_handler.active_cell_iterators()) {
452  if (!cell->is_locally_owned()) continue;
453 
454  cell->get_dof_indices (dofs_indices);
455 
456  for(unsigned int iface = 0; iface < dealii::GeometryInfo<dim>::faces_per_cell; ++iface){
457  auto face = cell->face(iface);
458 
459  if(face->at_boundary()){
460  const unsigned int boundary_id = face->boundary_id();
461  if(boundary_id==1001){
462  // Opposite surface solution for wall model
463  // Get opposite face index
464  const int opposite_iface = (iface == 0) ? 1 : (
465  (iface == 1) ? 0 : (
466  (iface == 2) ? 3 : (
467  (iface == 3) ? 2 : (
468  (iface == 4) ? 5 : (
469  (iface == 5) ? 4 : -1)))));
470  if(opposite_iface == -1) {
471  this->pcout << "ERROR: Invalid iface, opposite_iface is -1. Aborting..."<<std::endl;
472  std::abort();
473  }
474 
475  fe_face_values_extra.reinit (cell,opposite_iface);
476  const unsigned int n_quad_pts = fe_face_values_extra.n_quadrature_points;
477  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
478  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
479  // for (int s=0; s<nstate; ++s) {
480  // for (int d=0; d<dim; ++d) {
481  // soln_grad_at_q[s][d] = 0.0;
482  // }
483  // }
484  for (unsigned int idof=0; idof<fe_face_values_extra.dofs_per_cell; ++idof) {
485  const unsigned int istate = fe_face_values_extra.get_fe().system_to_component_index(idof).first;
486  soln_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_face_values_extra.shape_value_component(idof, iquad, istate);
487  // soln_grad_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_face_values_extra.shape_grad_component(idof,iquad,istate);
488  }
489  // const dealii::Point<dim> qpoint = (fe_face_values_extra.quadrature_point(iquad));
490  // const dealii::Tensor<1,dim,double> normal_vector = -fe_face_values_extra.normal_vector(iquad); // minus for wall normal from face normal
491  // double integrand_value = this->navier_stokes_physics->compute_wall_shear_stress(soln_at_q,soln_grad_at_q,normal_vector);
492  // Get wall shear stress magnitude from wall model
493  const double density = soln_at_q[0];
494 
495 
496  // constant viscosity coefficient for this case
497  const double viscosity_coefficient = this->navier_stokes_channel_flow_constant_source_term_wall_model_physics->constant_viscosity; // non-dimensional
498 
499  const double reynolds_number_inf = this->navier_stokes_channel_flow_constant_source_term_wall_model_physics->reynolds_number_inf;
500 
501  // Get normal vector
502  const dealii::Tensor<1,dim,double> normal = -fe_face_values_extra.normal_vector(iquad); // minus for wall normal from face normal
503 
504  // Get wall parallel velocity component; Frere thesis eq.(2.41)
505  // const double velocity_parallel_to_wall = soln_at_q[1]/soln_at_q[0];
506  const double velocity_parallel_to_wall =
507  this->navier_stokes_channel_flow_constant_source_term_wall_model_physics->get_velocity_component_parallel_to_wall_from_solution_and_normal_vector(soln_at_q,normal);
508 
509  const double wall_shear_stress_magnitude =
510  this->navier_stokes_channel_flow_constant_source_term_wall_model_physics->wall_model_look_up_table->get_wall_shear_stress_magnitude(
511  velocity_parallel_to_wall,
512  this->navier_stokes_channel_flow_constant_source_term_wall_model_physics->distance_from_wall_for_wall_model_input_velocity,
513  viscosity_coefficient,
514  density,
515  reynolds_number_inf);
516 
517  const double integrand_value = wall_shear_stress_magnitude;
518  integral_value += integrand_value * fe_face_values_extra.JxW(iquad);
519  integral_area_value += fe_face_values_extra.JxW(iquad);
520  }
521  }
522  }
523  }
524  }
525  const double mpi_sum_integral_value = dealii::Utilities::MPI::sum(integral_value, this->mpi_communicator);
526  const double mpi_sum_integral_area_value = dealii::Utilities::MPI::sum(integral_area_value, this->mpi_communicator);
527  const double averaged_value = mpi_sum_integral_value/mpi_sum_integral_area_value;
528  return averaged_value;
529 }
530 
531 template <int dim, int nspecies, int nstate>
533 {
534  // Reference: Equation 34 of Lodato G, Castonguay P, Jameson A. Discrete filter operators for large-eddy simulation using high-order spectral difference methods. International Journal for Numerical Methods in Fluids2013;72(2):231–258.
535  const double skin_friction_coefficient = 2.0*avg_wall_shear_stress/(this->bulk_density*this->bulk_velocity*this->bulk_velocity);
536  return skin_friction_coefficient;
537 }
538 
539 template <int dim, int nspecies, int nstate>
541 {
542  return this->bulk_density;
543 }
544 
545 template <int dim, int nspecies, int nstate>
547 {
548  return this->bulk_mass_flow_rate;
549 }
550 
551 template <int dim, int nspecies, int nstate>
553 {
554  return this->bulk_velocity;
555 }
556 
557 template <int dim, int nspecies, int nstate>
559 {
560  const int NUMBER_OF_INTEGRATED_QUANTITIES = 2;
561  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integrated_quantities;
564  bulk_density,
566  };
567  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integral_values;
568  std::fill(integral_values.begin(), integral_values.end(), 0.0);
569 
570  // Overintegrate the error to make sure there is not integration error in the error estimate
571  int overintegrate = 10; // NOTE: could reduce this to reduce computational cost
572  dealii::QGauss<dim> quad_extra(dg.max_degree+1+overintegrate);
573  dealii::FEValues<dim,dim> fe_values_extra(*(dg.high_order_grid->mapping_fe_field), dg.fe_collection[dg.max_degree], quad_extra,
574  dealii::update_values /*| dealii::update_gradients*/ | dealii::update_JxW_values | dealii::update_quadrature_points);
575 
576  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
577  std::array<double,nstate> soln_at_q;
578  // std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
579 
580  std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
581  for (auto cell : dg.dof_handler.active_cell_iterators()) {
582  if (!cell->is_locally_owned()) continue;
583  fe_values_extra.reinit (cell);
584  cell->get_dof_indices (dofs_indices);
585 
586  // double cellwise_integrand_value = 0.0;
587  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
588 
589  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
590  // for (int s=0; s<nstate; ++s) {
591  // for (int d=0; d<dim; ++d) {
592  // soln_grad_at_q[s][d] = 0.0;
593  // }
594  // }
595  for (unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
596  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
597  soln_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
598  // soln_grad_at_q[istate] += dg.solution[dofs_indices[idof]] * fe_values_extra.shape_grad_component(idof,iquad,istate);
599  }
600  // const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
601 
602  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integrand_values;
603  std::fill(integrand_values.begin(), integrand_values.end(), 0.0);
604  integrand_values[IntegratedQuantitiesEnum::bulk_density] = soln_at_q[0]; // density
605  integrand_values[IntegratedQuantitiesEnum::bulk_mass_flow_rate] = soln_at_q[1]; // x-momentum
606 
607  // cellwise_integrand_value += integrand_value * fe_values_extra.JxW(iquad);
608 
609  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
610  integral_values[i_quantity] += integrand_values[i_quantity] * fe_values_extra.JxW(iquad);
611  }
612  }
613  // // get cell index
614  // const dealii::types::global_dof_index cell_index = cell->active_cell_index();
615  // const double cellwise_average = cellwise_integrand_value/dg.pde_model_double->cellwise_volume[cell_index];
616  // integral_value += cellwise_average;
617  }
618  // update integrated quantities
619  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
620  integrated_quantities[i_quantity] = dealii::Utilities::MPI::sum(integral_values[i_quantity], this->mpi_communicator);
621  integrated_quantities[i_quantity] /= this->domain_volume; // divide by total domain volume
622  }
623  // set the bulk density, mass flow rate, and velocity for the source term used to force the mass flow rate
624  this->bulk_density = integrated_quantities[IntegratedQuantitiesEnum::bulk_density];
625  this->bulk_mass_flow_rate = integrated_quantities[IntegratedQuantitiesEnum::bulk_mass_flow_rate];
626  this->bulk_velocity = this->bulk_mass_flow_rate/this->bulk_density;
627 }
628 
629 #if PHILIP_DIM==3
631 #endif
632 
633 } // FlowSolver namespace
634 } // PHiLiP namespace
void update_maximum_local_wave_speed(DGBase< dim, nspecies, double > &dg) override
Updates the maximum local wave speed.
double get_adaptive_time_step_initial(std::shared_ptr< DGBase< dim, nspecies, double >> dg) override
Function to compute the initial adaptive time step.
const double half_channel_height
Half channel height.
Definition: channel_flow.h:34
PartialDifferentialEquation pde_type
Store the PDE type to be solved.
double bulk_density
Bulk density.
Definition: channel_flow.h:130
const Parameters::AllParameters all_param
All parameters.
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.
FlowSolverParam flow_solver_param
Contains the parameters for simulation cases (flow solver test)
const double channel_bulk_velocity_reynolds_number
Definition: channel_flow.h:52
double get_skin_friction_coefficient_from_average_wall_shear_stress(const double avg_wall_shear_stress) const
Get the skin friction coefficient from the average wall shear stress.
double get_bulk_velocity() const
Getter for the bulk velocity.
unsigned int grid_degree
Parameters related to mesh generation.
double constant_time_step
Constant time step.
TurbulentChannelMeshStretchingFunctionType
For turbulent channel flow, selects the type of mesh stretching function.
double mach_inf
Mach number at infinity.
const double domain_length_x
Domain length in x-direction.
Definition: channel_flow.h:40
std::shared_ptr< Physics::NavierStokes_ChannelFlowConstantSourceTerm_WallModel< dim, nspecies, dim+2, double > > navier_stokes_channel_flow_constant_source_term_wall_model_physics
Pointer to Navier-Stokes physics object for computing things on the fly.
Definition: channel_flow.h:92
std::shared_ptr< Triangulation > generate_grid() const override
Function to generate the grid.
PartialDifferentialEquation
Possible Partial Differential Equations to solve.
const double domain_length_y
Domain length in y-direction.
Definition: channel_flow.h:41
Files for the baseline physics.
Definition: ADTypes.hpp:10
const double pi_val
Value of pi.
Definition: channel_flow.h:39
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.
double get_average_wall_shear_stress_from_wall_model(DGBase< dim, nspecies, double > &dg) const
Get the average wall shear stress from wall model.
ChannelFlow(const Parameters::AllParameters *const parameters_input)
Constructor.
std::shared_ptr< HighOrderGrid< dim, real, MeshType > > high_order_grid
High order grid that will provide the MappingFEField.
Definition: dg_base.hpp:1178
unsigned int get_number_of_degrees_of_freedom_per_state_from_poly_degree(const unsigned int poly_degree_input) const override
Get the number of degrees of freedom per state from a given poly degree.
double reynolds_number_inf
Farfield Reynolds number.
void display_grid_parameters() const override
Display grid parameters.
EulerParam euler_param
Contains parameters for the Euler equations non-dimensionalization.
double bulk_velocity
Bulk velocity.
Definition: channel_flow.h:132
double get_adaptive_time_step(std::shared_ptr< DGBase< dim, nspecies, double >> dg) const override
Function to compute the adaptive time step.
unsigned int poly_degree
Polynomial order (P) of the basis functions for DG.
Main parameter class that contains the various other sub-parameter classes.
dealii::DoFHandler< dim > dof_handler
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:1175
const double domain_length_z
Domain length in z-direction.
Definition: channel_flow.h:42
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.
double maximum_local_wave_speed
Maximum local wave speed (i.e. convective eigenvalue)
const double domain_volume
Domain volume.
Definition: channel_flow.h:43
const int number_of_cells_y_direction
Number of cells in y-direction.
Definition: channel_flow.h:37
double get_bulk_mass_flow_rate() const
Getter for the bulk mass flow rate.
double get_bulk_density() const
Getter for the bulk density.
const int number_of_cells_z_direction
Number of cells in z-direction.
Definition: channel_flow.h:38
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
Definition: dg_base.hpp:409
dealii::Tensor< 2, dim, double > zero_tensor
Tensor of zeros.
Definition: channel_flow.h:89
std::vector< double > get_mesh_step_size_y_direction_HOPW() const
const int number_of_cells_x_direction
Number of cells in x-direction.
Definition: channel_flow.h:36
TurbulentChannelMeshStretchingFunctionType turbulent_channel_mesh_stretching_function_type
Selected DensityInitialConditionType from the input file.
Navier-Stokes equations with constant physical source term for the turbulent channel flow case and wa...
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.
IntegratedQuantitiesEnum
List of possible integrated quantities over the domain.
double get_average_wall_shear_stress(DGBase< dim, nspecies, double > &dg) const
Get the average wall shear stress.
dealii::ConditionalOStream pcout
ConditionalOStream.
double minimum_approximate_grid_spacing
Minimum approximate grid spacing.
Definition: channel_flow.h:63
void set_bulk_flow_quantities(DGBase< dim, nspecies, double > &dg)
Set the bulk flow quantities.
std::vector< double > get_mesh_step_size_y_direction_Gullbrand() const
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::vector< double > get_mesh_step_size_y_direction_carton_de_wiart_et_al() const
void set_higher_order_grid(std::shared_ptr< DGBase< dim, nspecies, double >> dg) const override
Function to set the higher order grid.
const dealii::hp::FECollection< dim > fe_collection
Finite Element Collection for p-finite-element to represent the solution.
Definition: dg_base.hpp:1120
const double channel_friction_velocity_reynolds_number
Channel Reynolds number based on wall friction velocity.
Definition: channel_flow.h:35
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
std::vector< double > get_mesh_step_size_y_direction() const
Return a vector of mesh step sizes in the y-direction based on the desired stretching function...
void display_additional_flow_case_specific_parameters() const override
Display additional more specific flow case parameters.
double bulk_mass_flow_rate
Bulk mass flow rate.
Definition: channel_flow.h:131
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.
const double channel_centerline_velocity_reynolds_number
Definition: channel_flow.h:61
bool using_wall_model
Flag for using wall model (initialized as false)