https://mooseframework.inl.gov
NavierStokesMethods.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "NavierStokesMethods.h"
11 #include "MooseError.h"
12 #include "libmesh/vector_value.h"
13 #include "NS.h"
14 
15 namespace NS
16 {
17 int
18 delta(unsigned int i, unsigned int j)
19 {
20  if (i == j)
21  return 1;
22  else
23  return 0;
24 }
25 
26 int
27 computeSign(const Real & a)
28 {
29  return a > 0 ? 1 : (a < 0 ? -1 : 0);
30 }
31 
32 unsigned int
33 getIndex(const Real & p, const std::vector<Real> & bounds)
34 {
35  if (p < bounds.front() || p > bounds.back())
36  mooseError("Point exceeds bounds of domain!");
37 
38  for (unsigned int i = 1; i < bounds.size(); ++i)
39  if (p <= bounds[i])
40  return i - 1;
41 
42  return bounds.size() - 2;
43 }
44 
45 Real
47  const Real & Re, const Real & rho, const Real & mu, const Real & drho, const Real & dmu)
48 {
49  return Re * (drho / std::max(rho, 1e-6) - dmu / std::max(mu, 1e-8));
50 }
51 
52 Real
54  const Real & cp,
55  const Real & k,
56  const Real & dmu,
57  const Real & dcp,
58  const Real & dk)
59 {
60  return (k * (mu * dcp + cp * dmu) - mu * cp * dk) / std::max(k * k, 1e-8);
61 }
62 
63 template <typename T>
64 T
65 findUStar(const T & mu, const T & rho, const T & u, const Real dist)
66 {
67  using std::log, std::sqrt, std::pow, std::max, std::abs;
68 
69  // usually takes about 3-4 iterations
70  constexpr int MAX_ITERS{50};
71  constexpr Real REL_TOLERANCE{1e-6};
72 
73  // Check inputs
74  mooseAssert(mu > 0, "Need a strictly positive viscosity");
75  mooseAssert(rho > 0, "Need a strictly positive density");
76  mooseAssert(u > 0, "Need a strictly positive velocity");
77  mooseAssert(dist > 0, "Need a strictly positive wall distance");
78 
79  const T nu = mu / rho;
80 
81  // Wall-function linearized guess
82  const Real a_c = 1 / NS::von_karman_constant;
83  const T b_c = 1.0 / NS::von_karman_constant * (log(NS::E_turb_constant * dist / mu) + 1.0);
84  const T & c_c = u;
85 
87  T u_star = max(1e-20, (-b_c + sqrt(pow(b_c, 2) + 4.0 * a_c * c_c)) / (2.0 * a_c));
88 
89  // Newton-Raphson method to solve for u_star (friction velocity).
90  for (int i = 0; i < MAX_ITERS; ++i)
91  {
92  T residual =
93  u_star / NS::von_karman_constant * log(NS::E_turb_constant * u_star * dist / nu) - u;
94  T deriv = (1.0 + log(NS::E_turb_constant * u_star * dist / nu)) / NS::von_karman_constant;
95  T new_u_star = max(1e-20, u_star - residual / deriv);
96 
97  Real rel_err =
98  abs(MetaPhysicL::raw_value(new_u_star - u_star) / MetaPhysicL::raw_value(new_u_star));
99 
100  u_star = new_u_star;
101  if (rel_err < REL_TOLERANCE)
102  return u_star;
103  }
104 
105  mooseException("Could not find the wall friction velocity (mu: ",
106  mu,
107  " rho: ",
108  rho,
109  " velocity: ",
110  u,
111  " wall distance: ",
112  dist,
113  ")");
114 }
115 template Real findUStar<Real>(const Real & mu, const Real & rho, const Real & u, const Real dist);
116 template ADReal
117 findUStar<ADReal>(const ADReal & mu, const ADReal & rho, const ADReal & u, const Real dist);
118 
119 template <typename T>
120 T
121 findyPlus(const T & mu, const T & rho, const T & u, const Real dist)
122 {
123  using std::max, std::log, std::abs;
124 
125  // Fixed point iteration method to find y_plus
126  // It should take 3 or 4 iterations
127  constexpr int MAX_ITERS{10};
128  constexpr Real REL_TOLERANCE{1e-2};
129 
130  // Check inputs
131  mooseAssert(mu > 0, "Need a strictly positive viscosity");
132  mooseAssert(u > 0, "Need a strictly positive velocity");
133  mooseAssert(rho > 0, "Need a strictly positive density");
134  mooseAssert(dist > 0, "Need a strictly positive wall distance");
135 
136  Real yPlusLast = 0.0;
137  T yPlus = dist * u * rho / mu; // Assign initial value to laminar
138  const Real rev_yPlusLam = 1.0 / MetaPhysicL::raw_value(yPlus);
139  const T kappa_time_Re = NS::von_karman_constant * u * dist / (mu / rho);
140  unsigned int iters = 0;
141 
142  do
143  {
144  yPlusLast = MetaPhysicL::raw_value(yPlus);
145  // Negative y plus does not make sense
146  yPlus = max(NS::min_y_plus, yPlus);
147  yPlus = (kappa_time_Re + yPlus) / (1.0 + log(NS::E_turb_constant * yPlus));
148  } while (abs(rev_yPlusLam * (MetaPhysicL::raw_value(yPlus) - yPlusLast)) > REL_TOLERANCE &&
149  ++iters < MAX_ITERS);
150 
151  return max(NS::min_y_plus, yPlus);
152 }
153 
154 template Real findyPlus<Real>(const Real & mu, const Real & rho, const Real & u, Real dist);
155 template ADReal
156 findyPlus<ADReal>(const ADReal & mu, const ADReal & rho, const ADReal & u, Real dist);
157 
158 template <typename T>
159 T
161 {
162  // if the velocity is zero, then the norm function call fails because AD tries to calculate the
163  // derivatives which causes a divide by zero - because d/dx(sqrt(f(x))) = 1/2/sqrt(f(x))*df/dx.
164  // So add a bit of noise (based on hitchhiker's guide to the galaxy's meaning of life number) to
165  // avoid this failure mode.
166  return isZero(velocity) ? 1e-42 : velocity.norm();
167 }
170 
171 template <typename T>
172 T
174  const Moose::Functor<T> * v,
175  const Moose::Functor<T> * w,
176  const Moose::ElemArg & elem_arg,
177  const Moose::StateArg & state)
178 {
179  const auto & grad_u = u.gradient(elem_arg, state);
180  const T Sij_xx = 2.0 * grad_u(0);
181  T Sij_xy = 0.0;
182  T Sij_xz = 0.0;
183  T Sij_yy = 0.0;
184  T Sij_yz = 0.0;
185  T Sij_zz = 0.0;
186 
187  const T grad_xx = grad_u(0);
188  T grad_xy = 0.0;
189  T grad_xz = 0.0;
190  T grad_yx = 0.0;
191  T grad_yy = 0.0;
192  T grad_yz = 0.0;
193  T grad_zx = 0.0;
194  T grad_zy = 0.0;
195  T grad_zz = 0.0;
196 
197  T trace = Sij_xx / 3.0;
198 
199  if (v) // dim >= 2
200  {
201  const auto & grad_v = (*v).gradient(elem_arg, state);
202  Sij_xy = grad_u(1) + grad_v(0);
203  Sij_yy = 2.0 * grad_v(1);
204 
205  grad_xy = grad_u(1);
206  grad_yx = grad_v(0);
207  grad_yy = grad_v(1);
208 
209  trace += Sij_yy / 3.0;
210 
211  if (w) // dim >= 3
212  {
213  const auto & grad_w = (*w).gradient(elem_arg, state);
214 
215  Sij_xz = grad_u(2) + grad_w(0);
216  Sij_yz = grad_v(2) + grad_w(1);
217  Sij_zz = 2.0 * grad_w(2);
218 
219  grad_xz = grad_u(2);
220  grad_yz = grad_v(2);
221  grad_zx = grad_w(0);
222  grad_zy = grad_w(1);
223  grad_zz = grad_w(2);
224 
225  trace += Sij_zz / 3.0;
226  }
227  }
228 
229  return (Sij_xx - trace) * grad_xx + Sij_xy * grad_xy + Sij_xz * grad_xz + Sij_xy * grad_yx +
230  (Sij_yy - trace) * grad_yy + Sij_yz * grad_yz + Sij_xz * grad_zx + Sij_yz * grad_zy +
231  (Sij_zz - trace) * grad_zz;
232 }
234  const Moose::Functor<Real> * v,
235  const Moose::Functor<Real> * w,
236  const Moose::ElemArg & elem_arg,
237  const Moose::StateArg & state);
239  const Moose::Functor<ADReal> * v,
240  const Moose::Functor<ADReal> * w,
241  const Moose::ElemArg & elem_arg,
242  const Moose::StateArg & state);
243 
245 void
246 getWallBoundedElements(const std::vector<BoundaryName> & wall_boundary_names,
247  const FEProblemBase & fe_problem,
248  const SubProblem & subproblem,
249  const std::set<SubdomainID> & block_ids,
250  std::unordered_set<const Elem *> & wall_bounded)
251 {
252 
253  wall_bounded.clear();
254  const auto wall_boundary_ids = subproblem.mesh().getBoundaryIDs(wall_boundary_names);
255 
256  // We define these lambdas so that we can fetch the bounded elements from other
257  // processors.
258  auto gather_functor = [&subproblem, &wall_bounded](const processor_id_type libmesh_dbg_var(pid),
259  const std::vector<dof_id_type> & elem_ids,
260  std::vector<unsigned char> & data_to_fill)
261  {
262  mooseAssert(pid != subproblem.processor_id(), "We shouldn't be gathering from ourselves.");
263  data_to_fill.resize(elem_ids.size());
264 
265  const auto & mesh = subproblem.mesh().getMesh();
266 
267  for (const auto i : index_range(elem_ids))
268  {
269  const auto elem = mesh.elem_ptr(elem_ids[i]);
270  data_to_fill[i] = wall_bounded.count(elem) != 0;
271  }
272  };
273 
274  auto action_functor = [&subproblem, &wall_bounded](const processor_id_type libmesh_dbg_var(pid),
275  const std::vector<dof_id_type> & elem_ids,
276  const std::vector<unsigned char> & filled_data)
277  {
278  mooseAssert(pid != subproblem.processor_id(),
279  "The request filler shouldn't have been ourselves");
280  mooseAssert(elem_ids.size() == filled_data.size(), "I think these should be the same size");
281 
282  const auto & mesh = subproblem.mesh().getMesh();
283 
284  for (const auto i : index_range(elem_ids))
285  {
286  const auto elem = mesh.elem_ptr(elem_ids[i]);
287  if (filled_data[i])
288  wall_bounded.insert(elem);
289  }
290  };
291 
292  // We need these elements from other processors
293  std::unordered_map<processor_id_type, std::vector<dof_id_type>> elem_ids_requested;
294 
295  for (const auto & elem : fe_problem.mesh().getMesh().active_local_element_ptr_range())
296  if (block_ids.find(elem->subdomain_id()) != block_ids.end())
297  for (const auto i_side : elem->side_index_range())
298  {
299  // This is needed because in some cases the internal boundary is registered
300  // to the neighbor element
301  std::set<BoundaryID> combined_side_bds;
302  const auto & side_bnds = subproblem.mesh().getBoundaryIDs(elem, i_side);
303  combined_side_bds.insert(side_bnds.begin(), side_bnds.end());
304  if (const auto neighbor = elem->neighbor_ptr(i_side))
305  {
306  const auto neighbor_side = neighbor->which_neighbor_am_i(elem);
307  const auto & neighbor_bnds = subproblem.mesh().getBoundaryIDs(neighbor, neighbor_side);
308  combined_side_bds.insert(neighbor_bnds.begin(), neighbor_bnds.end());
309 
310  // If the neighbor lives on the first layer of the ghost region then we would
311  // like to grab its value as well (if it exists)
312  if (neighbor->processor_id() != subproblem.processor_id() &&
313  block_ids.find(neighbor->subdomain_id()) != block_ids.end())
314  elem_ids_requested[neighbor->processor_id()].push_back(neighbor->id());
315  }
316 
317  for (const auto wall_id : wall_boundary_ids)
318  if (combined_side_bds.count(wall_id))
319  {
320  wall_bounded.insert(elem);
321  break;
322  }
323  }
324 
325  unsigned char * bool_ex = nullptr;
327  subproblem.comm(), elem_ids_requested, gather_functor, action_functor, bool_ex);
328 }
329 
331 void
332 getWallDistance(const std::vector<BoundaryName> & wall_boundary_name,
333  const FEProblemBase & fe_problem,
334  const SubProblem & subproblem,
335  const std::set<SubdomainID> & block_ids,
336  std::map<const Elem *, std::vector<Real>> & dist_map)
337 {
338  dist_map.clear();
339  const auto wall_boundary_ids = subproblem.mesh().getBoundaryIDs(wall_boundary_name);
340 
341  for (const auto & elem : fe_problem.mesh().getMesh().active_local_element_ptr_range())
342  if (block_ids.find(elem->subdomain_id()) != block_ids.end())
343  for (const auto i_side : elem->side_index_range())
344  {
345  // This is needed because in some cases the internal boundary is registered
346  // to the neighbor element
347  std::set<BoundaryID> combined_side_bds;
348  const auto & side_bnds = subproblem.mesh().getBoundaryIDs(elem, i_side);
349  combined_side_bds.insert(side_bnds.begin(), side_bnds.end());
350  if (const auto neighbor = elem->neighbor_ptr(i_side))
351  {
352  const auto neighbor_side = neighbor->which_neighbor_am_i(elem);
353  const std::vector<BoundaryID> & neighbor_bnds =
354  subproblem.mesh().getBoundaryIDs(neighbor, neighbor_side);
355  combined_side_bds.insert(neighbor_bnds.begin(), neighbor_bnds.end());
356  }
357 
358  for (const auto wall_id : wall_boundary_ids)
359  if (combined_side_bds.count(wall_id))
360  {
361  // The list below stores the face infos with respect to their owning elements,
362  // depending on the block restriction we might encounter situations where the
363  // element outside of the block owns the face info.
364  const auto & neighbor = elem->neighbor_ptr(i_side);
365  const auto elem_has_fi = Moose::FV::elemHasFaceInfo(*elem, neighbor);
366  const auto & elem_for_fi = elem_has_fi ? elem : neighbor;
367  const auto side = elem_has_fi ? i_side : neighbor->which_neighbor_am_i(elem);
368 
369  const FaceInfo * const fi = subproblem.mesh().faceInfo(elem_for_fi, side);
370  const auto & elem_centroid = elem_has_fi ? fi->elemCentroid() : fi->neighborCentroid();
371  const Real dist = std::abs((elem_centroid - fi->faceCentroid()) * fi->normal());
372  dist_map[elem].push_back(dist);
373  }
374  }
375 }
376 
378 void
379 getElementFaceArgs(const std::vector<BoundaryName> & wall_boundary_name,
380  const FEProblemBase & fe_problem,
381  const SubProblem & subproblem,
382  const std::set<SubdomainID> & block_ids,
383  std::map<const Elem *, std::vector<const FaceInfo *>> & face_info_map)
384 {
385  face_info_map.clear();
386  const auto wall_boundary_ids = subproblem.mesh().getBoundaryIDs(wall_boundary_name);
387 
388  for (const auto & elem : fe_problem.mesh().getMesh().active_local_element_ptr_range())
389  if (block_ids.find(elem->subdomain_id()) != block_ids.end())
390  for (const auto i_side : elem->side_index_range())
391  {
392  // This is needed because in some cases the internal boundary is registered
393  // to the neighbor element
394  std::set<BoundaryID> combined_side_bds;
395  const auto & side_bnds = subproblem.mesh().getBoundaryIDs(elem, i_side);
396  combined_side_bds.insert(side_bnds.begin(), side_bnds.end());
397  if (elem->neighbor_ptr(i_side) && !elem->neighbor_ptr(i_side)->is_remote())
398  {
399  const auto neighbor = elem->neighbor_ptr(i_side);
400  const auto neighbor_side = neighbor->which_neighbor_am_i(elem);
401  const std::vector<BoundaryID> & neighbor_bnds =
402  subproblem.mesh().getBoundaryIDs(neighbor, neighbor_side);
403  combined_side_bds.insert(neighbor_bnds.begin(), neighbor_bnds.end());
404  }
405 
406  for (const auto wall_id : wall_boundary_ids)
407  if (combined_side_bds.count(wall_id))
408  {
409  // The list below stores the face infos with respect to their owning elements,
410  // depending on the block restriction we might encounter situations where the
411  // element outside of the block owns the face info.
412  const auto & neighbor = elem->neighbor_ptr(i_side);
413  const auto elem_has_fi = Moose::FV::elemHasFaceInfo(*elem, neighbor);
414  const auto & elem_for_fi = elem_has_fi ? elem : neighbor;
415  const auto side = elem_has_fi ? i_side : neighbor->which_neighbor_am_i(elem);
416 
417  const FaceInfo * const fi = subproblem.mesh().faceInfo(elem_for_fi, side);
418  face_info_map[elem].push_back(fi);
419  }
420  }
421 }
422 }
virtual MooseMesh & mesh()=0
int computeSign(const Real &a)
Sign function, returns $+1$ if $a$ is positive and $-1$ if $a$ is negative.
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
T computeShearStrainRateNormSquared(const Moose::Functor< T > &u, const Moose::Functor< T > *v, const Moose::Functor< T > *w, const Moose::ElemArg &elem_arg, const Moose::StateArg &state)
Utility function to compute the shear strain rate.
static constexpr Real von_karman_constant
Definition: NS.h:201
bool elemHasFaceInfo(const Elem &elem, const Elem *const neighbor)
static constexpr Real min_y_plus
Definition: NS.h:208
void mooseError(Args &&... args)
T computeSpeed(const libMesh::VectorValue< T > &velocity)
Compute the speed (velocity norm) given the supplied velocity.
Real prandtlPropertyDerivative(const Real &mu, const Real &cp, const Real &k, const Real &dmu, const Real &dcp, const Real &dk)
Computes the derivative of the Prandtl number, $Pr{ C_p}{k}$, with respect to an arbitrary variale $$...
template ADReal computeShearStrainRateNormSquared< ADReal >(const Moose::Functor< ADReal > &u, const Moose::Functor< ADReal > *v, const Moose::Functor< ADReal > *w, const Moose::ElemArg &elem_arg, const Moose::StateArg &state)
const Point & faceCentroid() const
Real trace(const RealTensor &A, const unsigned int &dim)
MeshBase & mesh
auto raw_value(const Eigen::Map< T > &in)
int delta(unsigned int i, unsigned int j)
Delta function, which returns zero if $i j$ and unity if $i=j$.
const Parallel::Communicator & comm() const
template Real findUStar< Real >(const Real &mu, const Real &rho, const Real &u, const Real dist)
void getWallBoundedElements(const std::vector< BoundaryName > &wall_boundary_name, const FEProblemBase &fe_problem, const SubProblem &subproblem, const std::set< SubdomainID > &block_ids, std::unordered_set< const Elem *> &wall_bounded)
Map marking wall bounded elements The map passed in wall_bounded_map gets cleared and re-populated...
const Point & neighborCentroid() const
DualNumber< Real, DNDerivativeType, true > ADReal
auto max(const L &left, const R &right)
template Real findyPlus< Real >(const Real &mu, const Real &rho, const Real &u, Real dist)
uint8_t processor_id_type
static const std::string cp
Definition: NS.h:121
const std::vector< const FaceInfo *> & faceInfo() const
const Point & elemCentroid() const
MeshBase & getMesh()
Real deriv(unsigned n, unsigned alpha, unsigned beta, Real x)
static const std::string mu
Definition: NS.h:123
template ADReal findUStar< ADReal >(const ADReal &mu, const ADReal &rho, const ADReal &u, const Real dist)
Real reynoldsPropertyDerivative(const Real &Re, const Real &rho, const Real &mu, const Real &drho, const Real &dmu)
Computes the derivative of the Reynolds number, $Re { Vd}{}$, with respect to an arbitrary variable $...
const Point & normal() const
auto log(const T &)
unsigned int getIndex(const Real &p, const std::vector< Real > &bounds)
Determines the index $i$ in a sorted array such that the input point is within the $i$-th and $i+1$-t...
void getWallDistance(const std::vector< BoundaryName > &wall_boundary_name, const FEProblemBase &fe_problem, const SubProblem &subproblem, const std::set< SubdomainID > &block_ids, std::map< const Elem *, std::vector< Real >> &dist_map)
Map storing wall ditance for near-wall marked elements The map passed in dist_map gets cleared and re...
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const std::string v
Definition: NS.h:84
template ADReal computeSpeed< ADReal >(const libMesh::VectorValue< ADReal > &velocity)
static constexpr Real E_turb_constant
Definition: NS.h:202
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
GradientType gradient(const ElemArg &elem, const StateArg &state) const
template ADReal findyPlus< ADReal >(const ADReal &mu, const ADReal &rho, const ADReal &u, Real dist)
virtual MooseMesh & mesh() override
void getElementFaceArgs(const std::vector< BoundaryName > &wall_boundary_name, const FEProblemBase &fe_problem, const SubProblem &subproblem, const std::set< SubdomainID > &block_ids, std::map< const Elem *, std::vector< const FaceInfo *>> &face_info_map)
Map storing face arguments to wall bounded faces The map passed in face_info_map gets cleared and re-...
static const std::string velocity
Definition: NS.h:45
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
T findyPlus(const T &mu, const T &rho, const T &u, Real dist)
Finds the non-dimensional wall distance normalized with the friction velocity Implements a fixed-poin...
template Real computeSpeed< Real >(const libMesh::VectorValue< Real > &velocity)
std::vector< BoundaryID > getBoundaryIDs(const Elem *const elem, const unsigned short int side) const
T findUStar(const T &mu, const T &rho, const T &u, Real dist)
Finds the friction velocity using standard velocity wall functions formulation.
processor_id_type processor_id() const
MooseUnits pow(const MooseUnits &, int)
static const std::string k
Definition: NS.h:130
auto index_range(const T &sizable)
template Real computeShearStrainRateNormSquared< Real >(const Moose::Functor< Real > &u, const Moose::Functor< Real > *v, const Moose::Functor< Real > *w, const Moose::ElemArg &elem_arg, const Moose::StateArg &state)