https://mooseframework.inl.gov
Functions
NS::FV Namespace Reference

Functions

void relaxMatrix (SparseMatrix< Number > &matrix_in, const Real relaxation_parameter, NumericVector< Number > &diff_diagonal)
 Relax the matrix to ensure diagonal dominance, we hold onto the difference in diagonals for later use in relaxing the right hand side. More...
 
void relaxRightHandSide (NumericVector< Number > &rhs_in, const NumericVector< Number > &solution_in, const NumericVector< Number > &diff_diagonal)
 Relax the right hand side of an equation, this needs to be called once and the system matrix has been relaxed and the field describing the difference in the diagonals of the system matrix is already available. More...
 
void relaxSolutionUpdate (NumericVector< Number > &vec_new, const NumericVector< Number > &vec_old, const Real relaxation_factor)
 Relax the update on a solution field using the following approach: $u = u_{old}+ (u - u_{old})$. More...
 
void limitSolutionUpdate (NumericVector< Number > &solution, const Real min_limit=std::numeric_limits< Real >::epsilon(), const Real max_limit=1e10)
 Limit a solution to its minimum and maximum bounds: $u = min(max(u, min_limit), max_limit)$. More...
 
Real computeNormalizationFactor (const NumericVector< Number > &solution, const SparseMatrix< Number > &mat, const NumericVector< Number > &rhs)
 Compute a normalization factor which is applied to the linear residual to determine convergence. More...
 
void constrainSystem (SparseMatrix< Number > &mx, NumericVector< Number > &rhs, const Real desired_value, const dof_id_type dof_id)
 Implicitly constrain the system by adding a factor*(u-u_desired) to it at a desired dof value. More...
 
dof_id_type findPointDoFID (const MooseVariableFieldBase &variable, const MooseMesh &mesh, const Point &point)
 Find the ID of the degree of freedom which corresponds to the variable and a given point on the mesh. More...
 
bool converged (const std::vector< std::pair< unsigned int, Real >> &residuals, const std::vector< Real > &abs_tolerances)
 Based on the residuals, determine if the iterative process converged or not. More...
 

Function Documentation

◆ computeNormalizationFactor()

Real NS::FV::computeNormalizationFactor ( const NumericVector< Number > &  solution,
const SparseMatrix< Number > &  mat,
const NumericVector< Number > &  rhs 
)

Compute a normalization factor which is applied to the linear residual to determine convergence.

This function is based on the description provided here: {greenshields2022notes, title={Notes on computational fluid dynamics: General principles}, author={Greenshields, Christopher J and Weller, Henry G}, journal={(No Title)}, year={2022} }

Parameters
solutionThe solution vector
matThe system matrix
rhsThe system right hand side

Definition at line 135 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveNonlinearAssembly::solveAdvectedSystem(), LinearAssemblySegregatedSolve::solveAdvectedSystem(), SIMPLESolveNonlinearAssembly::solveMomentumPredictor(), LinearAssemblySegregatedSolve::solveMomentumPredictor(), SIMPLESolveNonlinearAssembly::solvePressureCorrector(), LinearAssemblySegregatedSolve::solvePressureCorrector(), LinearAssemblySegregatedSolve::solveSolidEnergy(), and SIMPLESolveNonlinearAssembly::solveSolidEnergySystem().

138 {
139  // This function is based on the description provided here:
140  // @article{greenshields2022notes,
141  // title={Notes on computational fluid dynamics: General principles},
142  // author={Greenshields, Christopher J and Weller, Henry G},
143  // journal={(No Title)},
144  // year={2022}
145  // }
146  // so basically we normalize the residual with the following number:
147  // sum(|Ax-Ax_avg|+|b-Ax_avg|)
148  // where A is the system matrix, b is the system right hand side while x and x_avg are
149  // the solution and average solution vectors
150 
151  // We create a vector for Ax_avg and Ax
152  auto A_times_average_solution = solution.zero_clone();
153  auto A_times_solution = solution.zero_clone();
154 
155  // Beware, trickery here! To avoid allocating unused vectors, we
156  // first compute Ax_avg using the storage used for Ax, then we
157  // overwrite Ax with the right value
158  *A_times_solution = solution.sum() / solution.size();
159  mat.vector_mult(*A_times_average_solution, *A_times_solution);
160  mat.vector_mult(*A_times_solution, solution);
161 
162  // We create Ax-Ax_avg
163  A_times_solution->add(-1.0, *A_times_average_solution);
164  // We create Ax_avg - b (ordering shouldn't matter we will take absolute value soon)
165  A_times_average_solution->add(-1.0, rhs);
166  A_times_solution->abs();
167  A_times_average_solution->abs();
168 
169  // Create |Ax-Ax_avg|+|b-Ax_avg|
170  A_times_average_solution->add(*A_times_solution);
171 
172  // Since use the l2 norm of the solution vectors in the linear solver, we will
173  // make this consistent and use the l2 norm of the vector. We add a small number to
174  // avoid normalizing with 0.
175  // TODO: Would be nice to see if we can do l1 norms in the linear solve.
176  return (A_times_average_solution->l2_norm() + libMesh::TOLERANCE * libMesh::TOLERANCE);
177 }
static constexpr Real TOLERANCE
void vector_mult(NumericVector< Number > &dest, const NumericVector< Number > &arg) const
virtual Number sum() const =0
virtual numeric_index_type size() const =0
virtual std::unique_ptr< NumericVector< Number > > zero_clone() const =0

◆ constrainSystem()

void NS::FV::constrainSystem ( SparseMatrix< Number > &  mx,
NumericVector< Number > &  rhs,
const Real  desired_value,
const dof_id_type  dof_id 
)

Implicitly constrain the system by adding a factor*(u-u_desired) to it at a desired dof value.

To make sure the conditioning of the matrix does not change significantly, factor is chosen to be the diagonal component of the matrix coefficients for a given dof.

Parameters
mxThe matrix of the system which needs to be constrained
rhsThe right hand side of the system which needs to be constrained
valueThe desired value for the solution field at a dof
dof_idThe ID of the dof which needs to be constrained

Definition at line 180 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveNonlinearAssembly::solvePressureCorrector(), and LinearAssemblySegregatedSolve::solvePressureCorrector().

184 {
185  // Modify the given matrix and right hand side. We use the matrix diagonal
186  // to enforce the constraint instead of 1, to make sure we don't mess up the matrix conditioning
187  // too much.
188  if (dof_id >= mx.row_start() && dof_id < mx.row_stop())
189  {
190  Real diag = mx(dof_id, dof_id);
191  rhs.add(dof_id, desired_value * diag);
192  mx.add(dof_id, dof_id, diag);
193  }
194 }
virtual numeric_index_type row_stop() const =0
virtual void add(const numeric_index_type i, const numeric_index_type j, const Number value)=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual numeric_index_type row_start() const =0
virtual void add(const numeric_index_type i, const Number value)=0

◆ converged()

bool NS::FV::converged ( const std::vector< std::pair< unsigned int, Real >> &  residuals,
const std::vector< Real > &  abs_tolerances 
)

Based on the residuals, determine if the iterative process converged or not.

Parameters
residualsThe current (number of iterations, residual) pairs
abs_tolerancesThe corresponding absolute tolerances.

Definition at line 229 of file SegregatedSolverUtils.C.

Referenced by MechanicalContactConstraint::AugmentedLagrangianContactConverged(), GeneralizedReturnMappingSolutionTempl< is_ad >::convergedAcceptable(), SingleVariableReturnMappingSolutionTempl< is_ad >::convergedAcceptable(), SingularShapeTensorEliminatorUserObjectPD::execute(), externalPETScDiffusionFDMSolve(), GeneralizedReturnMappingSolutionTempl< is_ad >::internalSolve(), SingleVariableReturnMappingSolutionTempl< is_ad >::internalSolve(), FluidPropertiesUtils::NewtonSolve(), FluidPropertiesUtils::NewtonSolve2D(), ExplicitMixedOrder::performExplicitSolve(), LinearAssemblySegregatedSolve::solve(), SIMPLESolveNonlinearAssembly::solve(), ExplicitMixedOrder::solve(), and AdjointTransientSolve::solve().

231 {
232  mooseAssert(its_and_residuals.size() == abs_tolerances.size(),
233  "The number of residuals should (now " + std::to_string(its_and_residuals.size()) +
234  ") be the same as the number of tolerances (" +
235  std::to_string(abs_tolerances.size()) + ")!");
236 
237  bool converged = true;
238  for (const auto system_i : index_range(its_and_residuals))
239  {
240  converged = converged && (its_and_residuals[system_i].second < abs_tolerances[system_i]);
241  if (!converged)
242  return converged;
243  }
244  return converged;
245 }
bool converged(const std::vector< std::pair< unsigned int, Real >> &residuals, const std::vector< Real > &abs_tolerances)
Based on the residuals, determine if the iterative process converged or not.
auto index_range(const T &sizable)

◆ findPointDoFID()

dof_id_type NS::FV::findPointDoFID ( const MooseVariableFieldBase variable,
const MooseMesh mesh,
const Point &  point 
)

Find the ID of the degree of freedom which corresponds to the variable and a given point on the mesh.

Parameters
variableReference to the moose variable whose dof should be fetched
meshThe moose mesh where the element is
pointThe point on the mesh

Definition at line 197 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveBase::setupPressurePin().

198 {
199  // Find the element containing the point
200  auto point_locator = PointLocatorBase::build(TREE_LOCAL_ELEMENTS, mesh);
201  point_locator->enable_out_of_mesh_mode();
202 
203  unsigned int var_num = variable.sys().system().variable_number(variable.name());
204 
205  // We only check in the restricted blocks, if needed
206  const bool block_restricted =
207  variable.blockIDs().find(Moose::ANY_BLOCK_ID) == variable.blockIDs().end();
208  const Elem * elem =
209  block_restricted ? (*point_locator)(point, &variable.blockIDs()) : (*point_locator)(point);
210 
211  // We communicate the results and if there is conflict between processes,
212  // the minimum cell ID is chosen
213  const dof_id_type elem_id = elem ? elem->id() : DofObject::invalid_id;
214  dof_id_type min_elem_id = elem_id;
215  variable.sys().comm().min(min_elem_id);
216 
217  if (min_elem_id == DofObject::invalid_id)
218  mooseError("Variable ",
219  variable.name(),
220  " is not defined at ",
221  Moose::stringify(point),
222  "! Try alleviating block restrictions or using another point!");
223 
224  return min_elem_id == elem_id ? elem->dof_number(variable.sys().number(), var_num, 0)
225  : DofObject::invalid_id;
226 }
void mooseError(Args &&... args)
virtual libMesh::System & system()=0
const std::string & name() const override
const Parallel::Communicator & comm() const
virtual const std::set< SubdomainID > & blockIDs() const
unsigned int variable_number(std::string_view var) const
void min(const T &r, T &o, Request &req) const
unsigned int number() const
std::string stringify(const T &t)
const SubdomainID ANY_BLOCK_ID
SystemBase & sys()
uint8_t dof_id_type

◆ limitSolutionUpdate()

void NS::FV::limitSolutionUpdate ( NumericVector< Number > &  solution,
const Real  min_limit = std::numeric_limits<Real>::epsilon(),
const Real  max_limit = 1e10 
)

Limit a solution to its minimum and maximum bounds: $u = min(max(u, min_limit), max_limit)$.

Parameters
system_inThe system whose solution shall be limited
min_limit= 0.0 The minimum limit for the solution
max_limit= 1e10 The maximum limit for the solution

Definition at line 123 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveNonlinearAssembly::solve().

124 {
125  PetscVector<Number> & solution_vector = dynamic_cast<PetscVector<Number> &>(solution);
126  auto value = solution_vector.get_array();
127 
128  for (auto i : make_range(solution_vector.local_size()))
129  value[i] = std::min(std::max(min_limit, value[i]), max_limit);
130 
131  solution_vector.restore_array();
132 }
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
IntRange< T > make_range(T beg, T end)

◆ relaxMatrix()

void NS::FV::relaxMatrix ( SparseMatrix< Number > &  matrix_in,
const Real  relaxation_parameter,
NumericVector< Number > &  diff_diagonal 
)

Relax the matrix to ensure diagonal dominance, we hold onto the difference in diagonals for later use in relaxing the right hand side.

For the details of this relaxation process, see

Juretic, Franjo. Error analysis in finite volume CFD. Diss. Imperial College London (University of London), 2005.

Parameters
matrix_inThe matrix that needs to be relaxed
relaxation_parameterThe scale which described how much the matrix is relaxed
diff_diagonalA vector holding the $A_{diag}-A_{diag, relaxed}$ entries for further use in the relaxation of the right hand side

Definition at line 25 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveNonlinearAssembly::solveAdvectedSystem(), LinearAssemblySegregatedSolve::solveAdvectedSystem(), SIMPLESolveNonlinearAssembly::solveMomentumPredictor(), and LinearAssemblySegregatedSolve::solveMomentumPredictor().

28 {
29  PetscMatrix<Number> * mat = dynamic_cast<PetscMatrix<Number> *>(&matrix);
30  mooseAssert(mat, "This should be a PetscMatrix!");
31  PetscVector<Number> * diff_diag = dynamic_cast<PetscVector<Number> *>(&diff_diagonal);
32  mooseAssert(diff_diag, "This should be a PetscVector!");
33 
34  // Zero the diagonal difference vector
35  *diff_diag = 0;
36 
37  // Get the diagonal of the matrix
38  mat->get_diagonal(*diff_diag);
39 
40  // Create a copy of the diagonal for later use and cast it
41  auto original_diagonal = diff_diag->clone();
42 
43  // We cache the inverse of the relaxation parameter because doing divisions might
44  // be more expensive for every row
45  const Real inverse_relaxation = 1 / relaxation_parameter;
46 
47  // Now we loop over the matrix row by row and sum the absolute values of the
48  // offdiagonal values, If these sums are larger than the diagonal entry,
49  // we switch the diagonal value with the sum. At the same time we increase the
50  // diagonal by dividing it with the relaxation parameter. So the new diagonal will be:
51  // D* = 1/lambda*max(|D|,sum(|Offdiag|))
52  // For more information see
53  //
54  // Juretic, Franjo. Error analysis in finite volume CFD. Diss.
55  // Imperial College London (University of London), 2005.
56  //
57  // The trickery comes with storing everything in the diff-diagonal vector
58  // to avoid the allocation and manipulation of a third vector
59  const unsigned int local_size = matrix.row_stop() - matrix.row_start();
60  std::vector<dof_id_type> indices(local_size);
61  std::iota(indices.begin(), indices.end(), matrix.row_start());
62  std::vector<Real> new_diagonal(local_size, 0.0);
63 
64  {
65  PetscVectorReader diff_diga_reader(*diff_diag);
66  for (const auto row_i : make_range(local_size))
67  {
68  const unsigned int global_index = matrix.row_start() + row_i;
69  std::vector<numeric_index_type> indices;
70  std::vector<Real> values;
71  mat->get_row(global_index, indices, values);
72  const Real abs_sum = std::accumulate(
73  values.cbegin(), values.cend(), 0.0, [](Real a, Real b) { return a + std::abs(b); });
74  const Real abs_diagonal = std::abs(diff_diga_reader(global_index));
75  new_diagonal[row_i] = inverse_relaxation * std::max(abs_sum - abs_diagonal, abs_diagonal);
76  }
77  }
78  diff_diag->insert(new_diagonal, indices);
79 
80  // Time to modify the diagonal of the matrix. TODO: add this function to libmesh
81  LibmeshPetscCallA(mat->comm().get(), MatDiagonalSet(mat->mat(), diff_diag->vec(), INSERT_VALUES));
82  mat->close();
83  diff_diag->close();
84 
85  // Finally, we can create (D*-D) vector which is used for the relaxation of the
86  // right hand side later
87  diff_diag->add(-1.0, *original_diagonal);
88 }
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)

◆ relaxRightHandSide()

void NS::FV::relaxRightHandSide ( NumericVector< Number > &  rhs_in,
const NumericVector< Number > &  solution_in,
const NumericVector< Number > &  diff_diagonal 
)

Relax the right hand side of an equation, this needs to be called once and the system matrix has been relaxed and the field describing the difference in the diagonals of the system matrix is already available.

The relaxation process needs modification to both the system matrix and the right hand side. For more information see:

Juretic, Franjo. Error analysis in finite volume CFD. Diss. Imperial College London (University of London), 2005.

Parameters
rhs_inThe unrelaxed right hand side that needs to be relaxed
solution_inThe solution
diff_diagonalThe diagonal correction used for the corresponding system matrix

Definition at line 91 of file SegregatedSolverUtils.C.

Referenced by SIMPLESolveNonlinearAssembly::solveAdvectedSystem(), LinearAssemblySegregatedSolve::solveAdvectedSystem(), SIMPLESolveNonlinearAssembly::solveMomentumPredictor(), and LinearAssemblySegregatedSolve::solveMomentumPredictor().

94 {
95 
96  // We need a working vector here to make sure we don't modify the
97  // (D*-D) vector
98  auto working_vector = diff_diagonal.clone();
99  working_vector->pointwise_mult(solution, *working_vector);
100 
101  // The correction to the right hand side is just
102  // (D*-D)*old_solution
103  // For more information see
104  //
105  // Juretic, Franjo. Error analysis in finite volume CFD. Diss.
106  // Imperial College London (University of London), 2005.
107  rhs.add(*working_vector);
108  rhs.close();
109 }
virtual std::unique_ptr< NumericVector< Number > > clone() const =0

◆ relaxSolutionUpdate()

void NS::FV::relaxSolutionUpdate ( NumericVector< Number > &  vec_new,
const NumericVector< Number > &  vec_old,
const Real  relaxation_factor 
)

Relax the update on a solution field using the following approach: $u = u_{old}+ (u - u_{old})$.

Parameters
vector_newThe new solution vector
vec_oldThe old solution vector
relaxation_factorThe lambda parameter in the expression above

Definition at line 112 of file SegregatedSolverUtils.C.

Referenced by LinearAssemblySegregatedSolve::correctVelocity(), and SIMPLESolveNonlinearAssembly::solve().

115 {
116  // The relaxation is just u = lambda * u* + (1-lambda) u_old
117  vec_new.scale(relaxation_factor);
118  vec_new.add(1 - relaxation_factor, vec_old);
119  vec_new.close();
120 }
virtual void scale(const Number factor)=0
virtual void close()=0
virtual void add(const numeric_index_type i, const Number value)=0