https://mooseframework.inl.gov
Functions
FluidPropertiesUtils Namespace Reference

Functions

template<typename T , typename Functor >
std::pair< T, T > NewtonSolve (const T &x, const T &y, const Real z_initial_guess, const Real tolerance, const Functor &func, const std::string &caller_name, const unsigned int max_its=100)
 NewtonSolve does a 1D Newton Solve to solve the equation y = f(x, z) for variable z. More...
 
template<typename T , typename Functor1 , typename Functor2 >
void NewtonSolve2D (const T &f, const T &g, const Real x0, const Real y0, T &x_final, T &y_final, const Real f_tol, const Real g_tol, const Functor1 &func1, const Functor2 &func2, const unsigned int max_its=100)
 NewtonSolve2D does a 2D Newton Solve to solve for the x and y such that: f = func1(x, y) and g = func2(x, y). More...
 

Function Documentation

◆ NewtonSolve()

template<typename T , typename Functor >
std::pair<T, T> FluidPropertiesUtils::NewtonSolve ( const T &  x,
const T &  y,
const Real  z_initial_guess,
const Real  tolerance,
const Functor func,
const std::string &  caller_name,
const unsigned int  max_its = 100 
)

NewtonSolve does a 1D Newton Solve to solve the equation y = f(x, z) for variable z.

Parameters
[in]xconstant first argument of the f(x, z) term
[in]yconstant which should be equal to f(x, z) with a converged z
[in]z_initial_guessinitial guess for return variables
[in]tolerancecriterion for relative or absolute (if y is sufficiently close to zero) convergence checking
[in]functiontwo-variable function returning both values and derivatives as references
[in]caller_namename of the fluid properties appended to name of the routine calling the method
[in]max_itsthe maximum number of iterations for Newton's method
Returns
a pair in which the first member is the value z such that f(x, z) = y and the second member is dy/dz

Definition at line 43 of file NewtonInversion.h.

Referenced by TabulatedFluidProperties::e_from_v_h(), TemperaturePressureFunctionFluidProperties::p_from_v_e(), Water97FluidProperties::p_from_v_e_template(), Water97FluidProperties::T_drhodT_from_p_rho(), HelmholtzFluidProperties::T_from_p_h(), TemperaturePressureFunctionFluidProperties::T_from_p_h(), TabulatedFluidProperties::T_from_p_h(), SimpleFluidProperties::T_from_p_h(), NaKFluidProperties::T_from_p_rho(), TemperaturePressureFunctionFluidProperties::T_from_p_rho(), TabulatedFluidProperties::T_from_p_rho(), TabulatedFluidProperties::T_from_p_s(), and TEST().

50 {
51  // R represents residual
52 
53  std::function<bool(const T &, const T &)> abs_tol_check =
54  [tolerance](const T & R, const T & /*y*/)
55  { return MetaPhysicL::raw_value(std::abs(R)) < tolerance; };
56  std::function<bool(const T &, const T &)> rel_tol_check = [tolerance](const T & R, const T & y)
57  { return MetaPhysicL::raw_value(std::abs(R / y)) < tolerance; };
58  auto convergence_check = MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(y), 0, tolerance)
59  ? abs_tol_check
60  : rel_tol_check;
61 
62  T z = z_initial_guess, R, new_y, dy_dx, dy_dz;
63  unsigned int iteration = 0;
64 
65  do
66  {
67  func(x, z, new_y, dy_dx, dy_dz);
68  R = new_y - y;
69 
70  // We always want to perform at least one update in order to get derivatives on z correct (z
71  // corresponding to the initial guess will have no derivative information), so we don't
72  // immediately return if we are converged
73  const bool converged = convergence_check(R, y);
74 
75 #ifndef NDEBUG
76  static constexpr Real perturbation_factor = 1 + 1e-8;
77  T perturbed_y, dummy, dummy2;
78  func(x, perturbation_factor * z, perturbed_y, dummy, dummy2);
79  // Check the accuracy of the Jacobian
80  auto J_differenced = (perturbed_y - new_y) / (1e-8 * z);
81  if (!MooseUtils::relativeFuzzyEqual(J_differenced, dy_dz, 1e-2))
82  mooseDoOnce(mooseWarning(caller_name + ": Bad Jacobian in NewtonSolve"));
83 #endif
84 
85  z += -(R / dy_dz);
86 
87  // Check for NaNs
88  if (std::isnan(z))
89  mooseException(caller_name + ": NaN detected in Newton solve");
90 
91  if (converged)
92  break;
93  } while (++iteration < max_its);
94 
95  // Check for divergence or slow convergence of Newton's method
96  if (iteration >= max_its)
97  mooseException(caller_name +
98  ": Newton solve convergence failed: maximum number of iterations, ",
99  max_its,
100  ", exceeded");
101 
102  return {z, dy_dz};
103 }
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
void mooseWarning(Args &&... args)
auto raw_value(const Eigen::Map< T > &in)
const std::vector< double > y
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.
const std::vector< double > x
static const std::string R
Definition: NS.h:162
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool relativeFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)

◆ NewtonSolve2D()

template<typename T , typename Functor1 , typename Functor2 >
void FluidPropertiesUtils::NewtonSolve2D ( const T &  f,
const T &  g,
const Real  x0,
const Real  y0,
T &  x_final,
T &  y_final,
const Real  f_tol,
const Real  g_tol,
const Functor1 &  func1,
const Functor2 &  func2,
const unsigned int  max_its = 100 
)

NewtonSolve2D does a 2D Newton Solve to solve for the x and y such that: f = func1(x, y) and g = func2(x, y).

This is done for example in the constant of (v, e) to (p, T) variable set conversion.

Parameters
[in]ftarget value for func1
[in]gtarget value for func2
[in]x0initial guess for first output variable
[in]y0initial guess for second output variable
[out]x_finaloutput for first variable
[out]y_finaloutput for second variable
[in]f_tolcriterion for relative or absolute (if f is sufficently close to zero) convergence checking
[in]g_tolcriterion for relative or absolute (if g is sufficently close to zero) convergence checking
[in]func1two-variable function returning both values and derivatives as references
[in]func2two-variable function returning both values and derivatives as references
[in]max_itsthe maximum number of iterations for Newton's method

Definition at line 125 of file NewtonInversion.h.

Referenced by SinglePhaseFluidProperties::p_T_from_h_s(), SinglePhaseFluidProperties::p_T_from_v_e(), SinglePhaseFluidProperties::p_T_from_v_h(), and TEST().

136 {
137 
138  constexpr unsigned int system_size = 2;
139  DenseVector<T> targets = {{f, g}};
140  DenseVector<Real> tolerances = {{f_tol, g_tol}};
141  // R represents a residual equal to y - y_in
142  auto convergence_check = [&targets, &tolerances](const auto & minus_R)
143  {
144  for (const auto i : index_range(minus_R))
145  {
146  const auto error = std::abs(MooseUtils::absoluteFuzzyEqual(targets(i), 0, tolerances(i))
147  ? minus_R(i)
148  : minus_R(i) / targets(i));
149  if (error >= tolerances(i))
150  return false;
151  }
152  return true;
153  };
154 
155  DenseVector<T> u = {{x0, y0}};
156  DenseVector<T> minus_R(system_size), func_evals(system_size), u_update;
157  DenseMatrix<T> J(system_size, system_size);
158  unsigned int iteration = 0;
159 #ifndef NDEBUG
160  DenseVector<Real> svs(system_size), evs_real(system_size), evs_imag(system_size);
161  DenseMatrix<Real> raw_J(system_size, system_size), raw_J2(system_size, system_size);
162 #endif
163 
164  typedef std::function<void(const T &, const T &, T &, T &, T &)> FuncType;
165  std::array<FuncType, 2> func = {{func1, func2}};
166 
167  auto assign_solution = [&u, &x_final, &y_final]()
168  {
169  x_final = u(0);
170  y_final = u(1);
171  };
172 
173  do
174  {
175  for (const auto i : make_range(system_size))
176  func[i](u(0), u(1), func_evals(i), J(i, 0), J(i, 1));
177 
178  for (const auto i : make_range(system_size))
179  minus_R(i) = targets(i) - func_evals(i);
180 
181  // We always want to perform at least one update in order to get derivatives on z correct (z
182  // corresponding to the initial guess will have no derivative information), so we don't
183  // immediately return if we are converged
184  const bool converged = convergence_check(minus_R);
185 
186  // Check for NaNs before proceeding to system solve. We may simultaneously not have NaNs in z
187  // but have NaNs in the function evaluation
188  for (const auto i : make_range(system_size))
189  if (std::isnan(minus_R(i)))
190  {
191  assign_solution();
192  mooseException("NaN detected in Newton solve");
193  }
194 
195  // Do some Jacobi (rowmax) preconditioning
196  for (const auto i : make_range(system_size))
197  {
198  const auto rowmax = std::max(std::abs(J(i, 0)), std::abs(J(i, 1)));
199  for (const auto j : make_range(system_size))
200  J(i, j) /= rowmax;
201  minus_R(i) /= rowmax;
202  }
203 
204 #ifndef NDEBUG
205  //
206  // Check nature of linearized system
207  //
208  for (const auto i : make_range(system_size))
209  for (const auto j : make_range(system_size))
210  {
211  raw_J(i, j) = MetaPhysicL::raw_value(J(i, j));
212  raw_J2(i, j) = MetaPhysicL::raw_value(J(i, j));
213  }
214  raw_J.svd(svs);
215  raw_J2.evd(evs_real, evs_imag);
216 #endif
217 
218  J.lu_solve(minus_R, u_update);
219  // reset the decomposition
220  J.zero();
221  u += u_update;
222 
223  // Check for NaNs
224  for (const auto i : make_range(system_size))
225  if (std::isnan(u(i)))
226  {
227  assign_solution();
228  mooseException("NaN detected in Newton solve");
229  }
230 
231  if (converged)
232  break;
233  } while (++iteration < max_its);
234 
235  assign_solution();
236 
237  // Check for divergence or slow convergence of Newton's method
238  if (iteration >= max_its)
239  mooseException(
240  "Newton solve convergence failed: maximum number of iterations, ", max_its, ", exceeded");
241 }
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
auto raw_value(const Eigen::Map< T > &in)
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.
Real f(Real x)
Test function for Brents method.
IntRange< T > make_range(T beg, T end)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
auto index_range(const T &sizable)