https://mooseframework.inl.gov
FaceCenteredMapFunctor.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 "FaceCenteredMapFunctor.h"
11 #include "FaceInfo.h"
12 #include "FVUtils.h"
13 #include "libmesh/compare_types.h"
14 #include "libmesh/type_tensor.h"
15 #include "libmesh/tensor_tools.h"
16 #include "libmesh/dense_matrix.h"
17 #include "libmesh/elem.h"
18 #include "libmesh/point.h"
19 
20 using namespace libMesh;
21 
22 namespace Moose
23 {
24 template <typename T, typename T2, typename std::enable_if<ScalarTraits<T>::value, int>::type = 0>
26 outer_product(const T & a, const TypeVector<T2> & b)
27 {
29  for (const auto i : make_range(Moose::dim))
30  ret(i) = a * b(i);
31 
32  return ret;
33 }
34 
35 template <typename T, typename T2>
38 {
39  return libMesh::outer_product(a, b);
40 }
41 }
42 
43 template <typename T, typename Map>
46 {
47  // The following reconstruction is based on Weller's method. For more information on this,
48  // we recommend:
49  //
50  // Weller, Hilary. "Non-orthogonal version of the arbitrary polygonal C-grid and a new diamond
51  // grid." Geoscientific Model Development 7.3 (2014): 779-797.
52  //
53  // and
54  //
55  // Aguerre, Horacio J., et al. "An oscillation-free flow solver based on flux reconstruction."
56  // Journal of Computational Physics 365 (2018): 135-148.
57  //
58  // This basically reconstructs the cell value based on flux values as follows:
59  //
60  // $\left( \sum_f n_f \outer S_f \right)^{-1} \sum_f (\phi_f \cdot S_f)n_f$
61  //
62  // where $S_f$ is the surface normal vector, $n_f$ is the unit surface vector and $\phi_f$ is a
63  // vector value on the field. Hence the restriction to vector values.
64 
66 
67  // Primitive type one rank below the type of the stored data (T). If we are storing a rank two
68  // tensor, this is a vector, if we are storing a vector this is just a number
69  using PrimitiveType = typename MetaPhysicL::ReplaceAlgebraicType<
70  T,
72 
74  {
75  const auto dim = _mesh.dimension();
76  // The reason why these are DenseVector/Matrix is that when the mesh dimension is lower than 3,
77  // we get singular matrixes if we try to invert TensorValues.
79  DenseVector<PrimitiveType> sum_normal_flux(dim);
80 
81  const Elem * const elem = elem_arg.elem;
82 
83  for (const auto side : make_range(elem->n_sides()))
84  {
85  const Elem * const neighbor = elem->neighbor_ptr(side);
86 
87  // We need to check if the faceinfo belongs to the element or the neighbor. Based on that we
88  // query the faceinfo and adjust the normal to point outward of the current cell
89  const bool elem_has_fi = Moose::FV::elemHasFaceInfo(*elem, neighbor);
90  const FaceInfo * const fi = _mesh.faceInfo(
91  elem_has_fi ? elem : neighbor, elem_has_fi ? side : neighbor->which_neighbor_am_i(elem));
92  const Point & normal = elem_has_fi ? fi->normal() : Point(-fi->normal());
93 
94  const Point area_vector = normal * fi->faceArea();
95  const ValueType face_value = this->evaluate(fi);
96 
97  const auto product = Moose::outer_product(normal, area_vector);
98 
99  const auto flux_contrib = normal * (face_value * area_vector);
100  for (const auto i : make_range(dim))
101  {
102  sum_normal_flux(i) += flux_contrib(i);
103  for (const auto j : make_range(dim))
104  n_x_Sf(i, j) += product(i, j);
105  }
106  }
107 
108  // We do the inversion of the surface vector matrix here. It is symmetric
109  // and small so we can do it using a Cholesky decomposition.
110  DenseVector<PrimitiveType> dense_result(dim);
111  n_x_Sf.cholesky_solve(sum_normal_flux, dense_result);
112 
113  ValueType result;
114  for (const auto i : make_range(dim))
115  result(i) = dense_result(i);
116 
117  return result;
118  }
119  else
120  mooseError("Cell center reconstruction is not implemented!");
121 }
122 
123 template <typename T, typename Map>
126 {
127  return this->evaluate(face.fi);
128 }
129 
130 template <typename T, typename Map>
133 {
134  try
135  {
136  return libmesh_map_find(*this, fi->id());
137  }
138  catch (libMesh::LogicError &)
139  {
140  if (!_sub_ids.empty() && !_sub_ids.count(fi->elem().subdomain_id()))
141  {
142  if (fi->neighborPtr() && !_sub_ids.count(fi->neighborPtr()->subdomain_id()))
143  mooseError("Attempted to evaluate FaceCenteredMapFunctor '",
144  this->functorName(),
145  "' with an element subdomain id of '",
146  fi->elem().subdomain_id(),
147  fi->neighborPtr() ? " or neighbor subdomain id of '" +
148  std::to_string(fi->neighborPtr()->subdomain_id()) + "'"
149  : "",
150  "' but that subdomain id is not one of the subdomain ids the functor is "
151  "restricted to.");
152  }
153  else
154  mooseError("Attempted access into FaceCenteredMapFunctor '",
155  this->functorName(),
156  "' with a key that does not yet exist in the map. Make sure to fill your "
157  "FaceCenteredMapFunctor for all elements you will attempt to access later.");
158 
160  }
161 }
162 
164  std::unordered_map<dof_id_type, ADRealVectorValue>>;
166  std::unordered_map<dof_id_type, RealVectorValue>>;
bool elemHasFaceInfo(const Elem &elem, const Elem *const neighbor)
TypeTensor< typename CompareTypes< T, T2 >::supertype > outer_product(const TypeVector< T > &a, const TypeVector< T2 > &b)
A functor whose evaluation relies on querying a map where the keys are face info ids and the values c...
VectorValue< Real > RealVectorValue
void mooseError(Args &&... args)
unsigned int dim
const Elem & elem() const
static constexpr std::size_t dim
TypeVector< typename CompareTypes< T, T2 >::supertype > outer_product(const T &a, const TypeVector< T2 > &b)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
Real faceArea() const
ValueType evaluate(const FaceInfo *const fi) const
Evaluate the face functor using a FaceInfo argument.
const Elem * neighborPtr() const
const FaceInfo * fi
unsigned int which_neighbor_am_i(const Elem *e) const
const libMesh::Elem * elem
const Point & normal() const
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
void evaluate(const std::vector< Real > &q_vector, const DenseMatrix< Real > &data, std::vector< Real > &out)
subdomain_id_type subdomain_id() const
IntRange< T > make_range(T beg, T end)
dof_id_type id() const
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
void cholesky_solve(const DenseVector< T2 > &b, DenseVector< T2 > &x)