23 #include "libmesh/dense_matrix.h" 42 template <
typename T,
typename Functor>
46 const Real z_initial_guess,
49 const std::string & caller_name,
50 const unsigned int max_its = 100,
51 const bool verbose =
false)
55 std::function<bool(const T &, const T &)> abs_tol_check =
56 [tolerance](
const T &
R,
const T & )
58 std::function<bool(const T &, const T &)> rel_tol_check = [tolerance](
const T &
R,
const T &
y)
64 T z = z_initial_guess,
R, new_y, dy_dx, dy_dz;
65 unsigned int iteration = 0;
69 Moose::out <<
"Target value for 1D Newton inversion:\n" <<
y << std::endl;
73 y_from_x_z(
x, z, new_y, dy_dx, dy_dz);
82 static constexpr
Real perturbation_factor = 1 + 1e-8;
83 T perturbed_y, dummy, dummy2;
84 y_from_x_z(
x, perturbation_factor * z, perturbed_y, dummy, dummy2);
86 auto J_differenced = (perturbed_y - new_y) / (1e-8 * z);
87 if (!MooseUtils::relativeFuzzyEqual(J_differenced, dy_dz, 1e-2))
88 mooseDoOnce(
mooseWarning(caller_name +
": Bad Jacobian in NewtonSolve"));
95 Moose::out <<
"Iteration " << iteration << std::endl;
96 Moose::out <<
"Current solution vector: " << z << std::endl;
97 Moose::out <<
"Current (minus) residual: " << -
R << std::endl;
98 Moose::out <<
"Current Jacobian: " << dy_dz << std::endl;
103 mooseException(caller_name +
": NaN detected in Newton solve");
107 }
while (++iteration < max_its);
110 if (iteration >= max_its)
111 mooseException(caller_name +
112 ": Newton solve convergence failed: maximum number of iterations, ",
117 y_from_x_z(
x, z, new_y, dy_dx, dy_dz);
142 template <
typename T,
typename Functor1,
typename Functor2>
152 const Functor1 & f_from_x_y,
153 const Functor2 & g_from_x_y,
154 const std::string & caller_name =
"",
155 const unsigned int max_its = 100,
159 constexpr
unsigned int system_size = 2;
160 DenseVector<T> targets = {{
f, g}};
161 DenseVector<Real> tolerances = {{f_tol, g_tol}};
163 auto convergence_check = [&targets, &tolerances](
const auto & minus_R)
169 const auto error =
abs(MooseUtils::absoluteFuzzyEqual(targets(i), 0, tolerances(i))
171 : minus_R(i) / targets(i));
172 if (error >= tolerances(i))
178 DenseVector<T> u = {{x0, y0}};
179 DenseVector<T> minus_R(system_size), func_evals(system_size), u_update(system_size);
180 DenseMatrix<T> J(system_size, system_size);
181 unsigned int iteration = 0;
183 DenseVector<Real> svs(system_size), evs_real(system_size), evs_imag(system_size);
184 DenseMatrix<Real> raw_J(system_size, system_size), raw_J2(system_size, system_size);
187 typedef std::function<void(const T &, const T &, T &, T &, T &)> FuncType;
188 std::array<FuncType, 2> func = {{f_from_x_y, g_from_x_y}};
190 auto assign_solution = [&u, &x_final, &y_final]()
195 auto status_string = [&u, &func_evals, &targets, &minus_R](
unsigned int comp) -> std::stringstream
197 std::stringstream ss;
198 ss <<
"Current solution for component " << comp <<
": " << u(comp)
199 <<
" (current ordinate: " << func_evals(comp) <<
" -> target: " << targets(comp)
200 <<
", scaled residual: " << minus_R(comp) <<
")";
204 Moose::out <<
"Target values for 2D Newton inversion:\n" << targets << std::endl;
206 using std::isnan, std::max, std::abs;
211 func[i](u(0), u(1), func_evals(i), J(i, 0), J(i, 1));
214 minus_R(i) = targets(i) - func_evals(i);
219 const bool converged = convergence_check(minus_R);
224 if (isnan(minus_R(i)))
227 mooseException(caller_name +
": NaN detected in Newton solve");
232 Moose::out <<
"Iteration " << iteration << std::endl;
233 Moose::out <<
"Current solution vector:\n" << u << std::endl;
234 Moose::out <<
"Current (minus) residual:\n" << minus_R << std::endl;
235 Moose::out <<
"Current Jacobian:\n" << J << std::endl;
239 int degenerate_row = -1;
242 const auto rowmax =
max(
abs(J(i, 0)),
abs(J(i, 1)));
247 minus_R(i) /= rowmax;
251 if (degenerate_row != -1)
252 mooseException(caller_name +
": Jacobian is all zeros in NewtonSolve2D");
268 raw_J2.
evd(evs_real, evs_imag);
270 Moose::out <<
"Jacobian singular values:\n" << svs << std::endl;
273 if (degenerate_row == -1)
274 J.lu_solve(minus_R, u_update);
278 const auto other_row = system_size - 1 - degenerate_row;
279 u_update(other_row) = minus_R(other_row) / J(other_row, other_row);
280 u_update(degenerate_row) = 0;
291 mooseException(caller_name +
": NaN detected in NewtonSolve2D\n" + status_string(0).str() +
292 "\n" + status_string(1).str());
297 }
while (++iteration < max_its);
302 if (iteration >= max_its)
303 mooseException(caller_name +
304 ": Newton solve convergence failed: maximum number of iterations, ",
306 ", exceeded.\n" + status_string(0).str() +
"\n" + status_string(1).str());
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
void mooseWarning(Args &&... args)
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 &f_from_x_y, const Functor2 &g_from_x_y, const std::string &caller_name="", const unsigned int max_its=100, bool debug=false)
NewtonSolve2D does a 2D Newton Solve to solve for the x and y such that: f = f_from_x_y(x, y) and g = g_from_x_y(x, y).
const std::vector< double > y
auto max(const L &left, const R &right)
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.
FunctorEnvelope< T > Functor
const std::vector< double > x
Real f(Real x)
Test function for Brents method.
std::pair< T, T > NewtonSolve(const T &x, const T &y, const Real z_initial_guess, const Real tolerance, const Functor &y_from_x_z, const std::string &caller_name, const unsigned int max_its=100, const bool verbose=false)
NewtonSolve does a 1D Newton Solve to solve the equation y = f(x, z) for variable z...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void evd(DenseVector< Real > &lambda_real, DenseVector< Real > &lambda_imag)
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)