WarpX
Loading...
Searching...
No Matches
NewtonSolver.H
Go to the documentation of this file.
1/* Copyright 2024 Justin Angus
2 *
3 * This file is part of WarpX.
4 *
5 * License: BSD-3-Clause-LBNL
6 */
7#ifndef NEWTON_SOLVER_H_
8#define NEWTON_SOLVER_H_
9
10#include "LinearSolverLibrary.H"
11#include "NonlinearSolver.H"
12#include "JacobianFunctionMF.H"
13#include "Preconditioner.H"
14#include "Utils/TextMsg.H"
15
16#include <AMReX_ParmParse.H>
17#include <AMReX_Config.H>
18
19#include <vector>
20#include <istream>
21#include <filesystem>
22
29
30template<class Vec, class Ops>
31class NewtonSolver : public NonlinearSolver<Vec,Ops>
32{
33public:
34
35 NewtonSolver() = default;
36
37 ~NewtonSolver() override = default;
38
39 // Prohibit Move and Copy operations
40 NewtonSolver(const NewtonSolver&) = delete;
42 NewtonSolver(NewtonSolver&&) noexcept = delete;
43 NewtonSolver& operator=(NewtonSolver&&) noexcept = delete;
44
45 void Define ( const Vec& a_U,
46 Ops* a_ops ) override;
47
48 void Solve ( Vec& a_U,
49 const Vec& a_b,
50 amrex::Real a_time,
51 amrex::Real a_dt,
52 int a_step) const override;
53
54 void GetSolverParams ( amrex::Real& a_rtol,
55 amrex::Real& a_atol,
56 int& a_maxits ) override
57 {
58 a_rtol = m_rtol;
59 a_atol = m_atol;
60 a_maxits = m_maxits;
61 }
62
63 inline void CurTime ( amrex::Real a_time ) const
64 {
65 m_cur_time = a_time;
66 m_linear_function->curTime( a_time );
67 }
68
69 inline void CurTimeStep ( amrex::Real a_dt ) const
70 {
71 m_dt = a_dt;
72 m_linear_function->curTimeStep( a_dt );
73 }
74
76 {
77 return m_linear_function->pcType();
78 }
79
80 void PrintParams () const override
81 {
82 amrex::Print() << "Newton verbose: " << (this->m_verbose?"true":"false") << "\n";
83 amrex::Print() << "Newton max iterations: " << m_maxits << "\n";
84 amrex::Print() << "Newton relative tolerance: " << m_rtol << "\n";
85 amrex::Print() << "Newton absolute tolerance: " << m_atol << "\n";
86 amrex::Print() << "Newton require convergence: " << (m_require_convergence?"true":"false") << "\n";
88 amrex::Print() << "Newton linear solver: " << linsol_name << "\n";
89 amrex::Print() << "Linear solver (" << linsol_name << ") verbose: " << m_linsol_verbose_int << "\n";
90 amrex::Print() << "Linear solver (" << linsol_name << ") restart length: " << m_linsol_restart_length << "\n";
91 amrex::Print() << "Linear solver (" << linsol_name << ") max iterations: " << m_linsol_maxits << "\n";
92 amrex::Print() << "Linear solver (" << linsol_name << ") relative tolerance: " << m_linsol_rtol << "\n";
93 amrex::Print() << "Linear solver (" << linsol_name << ") absolute tolerance: " << m_linsol_atol << "\n";
94 amrex::Print() << "Preconditioner type: " << amrex::getEnumNameString(m_pc_type) << "\n";
95
96 m_linear_function->printParams();
97 }
98
99private:
100
104 mutable Vec m_dU, m_F, m_R;
105
109 Ops* m_ops = nullptr;
110
115
120
125
129 int m_maxits = 100;
130
134 mutable int m_total_iters = 0;
135
140
145
149 int m_linsol_maxits = 1000;
150
154 mutable int m_total_linsol_iters = 0;
155
160
165
170
172
177 std::unique_ptr<JacobianFunctionMF<Vec,Ops>> m_linear_function;
178
183
187 std::unique_ptr<LinearSolver<Vec,JacobianFunctionMF<Vec,Ops>>> m_linear_solver;
188
189 void ParseParameters ();
190
194 void EvalResidual ( Vec& a_F,
195 const Vec& a_U,
196 const Vec& a_b,
197 amrex::Real a_time,
198 int a_iter ) const;
199
200};
201
202template <class Vec, class Ops>
203void NewtonSolver<Vec,Ops>::Define ( const Vec& a_U,
204 Ops* a_ops )
205{
206 BL_PROFILE("NewtonSolver::Define()");
208 !this->m_is_defined,
209 "Newton nonlinear solver object is already defined!");
210
212
213 m_dU.Define(a_U);
214 m_F.Define(a_U); // residual function F(U) = U - b - R(U) = 0
215 m_R.Define(a_U); // right hand side function R(U)
216
217 m_ops = a_ops;
218
219 m_linear_function = std::make_unique<JacobianFunctionMF<Vec,Ops>>();
221 this->m_usePC = m_linear_function->usePreconditioner();
222
224 m_linear_solver = std::make_unique<AMReXGMRES<Vec,JacobianFunctionMF<Vec,Ops>>>();
226#ifdef AMREX_USE_PETSC
227 m_linear_solver = std::make_unique<PETScKSP<Vec,JacobianFunctionMF<Vec,Ops>>>();
228#else
229 WARPX_ABORT_WITH_MESSAGE("NewtonSolver::Define(): must compile with PETSc to use petsc_ksp (AMREX_USE_PETSC must be defined)");
230#endif
231 } else {
232 WARPX_ABORT_WITH_MESSAGE("NewtonSolver::Define(): invalid linear solver type");
233 }
236 m_linear_solver->setRestartLength( m_linsol_restart_length );
237 m_linear_solver->setMaxIters( m_linsol_maxits );
238
239 this->m_is_defined = true;
240
241 // Create diagnostic file and write header
243 && !this->m_diagnostic_file.empty()
244 && !amrex::FileExists(this->m_diagnostic_file)) {
245
246 std::filesystem::path const diagnostic_path(this->m_diagnostic_file);
247 std::filesystem::path const diagnostic_dir = diagnostic_path.parent_path();
248 if (!diagnostic_dir.empty()) {
249 std::filesystem::create_directories(diagnostic_dir);
250 }
251
252 std::ofstream diagnostic_file{this->m_diagnostic_file, std::ofstream::out | std::ofstream::trunc};
253 int c = 0;
254 diagnostic_file << "#";
255 diagnostic_file << "[" << c++ << "]step()";
256 diagnostic_file << " ";
257 diagnostic_file << "[" << c++ << "]time(s)";
258 diagnostic_file << " ";
259 diagnostic_file << "[" << c++ << "]iters";
260 diagnostic_file << " ";
261 diagnostic_file << "[" << c++ << "]total_iters";
262 diagnostic_file << " ";
263 diagnostic_file << "[" << c++ << "]norm_abs";
264 diagnostic_file << " ";
265 diagnostic_file << "[" << c++ << "]norm_rel";
266 diagnostic_file << " ";
267 diagnostic_file << "[" << c++ << "]gmres_iters";
268 diagnostic_file << " ";
269 diagnostic_file << "[" << c++ << "]gmres_total_iters";
270 diagnostic_file << " ";
271 diagnostic_file << "[" << c++ << "]gmres_last_res";
272 diagnostic_file << "\n";
273 diagnostic_file.close();
274 }
275}
276
277template <class Vec, class Ops>
279{
280 const amrex::ParmParse pp_newton("newton");
281 pp_newton.query("verbose", this->m_verbose);
282 pp_newton.query("linear_solver", m_linear_solver_type);
283 pp_newton.query("absolute_tolerance", m_atol);
284 pp_newton.query("relative_tolerance", m_rtol);
285 pp_newton.query("max_iterations", m_maxits);
286 pp_newton.query("require_convergence", m_require_convergence);
287 pp_newton.query("diagnostic_file", this->m_diagnostic_file);
288 pp_newton.query("diagnostic_interval", this->m_diagnostic_interval);
289
291 pp_l.query("verbose_int", m_linsol_verbose_int);
292 pp_l.query("restart_length", m_linsol_restart_length);
293 pp_l.query("absolute_tolerance", m_linsol_atol);
294 pp_l.query("relative_tolerance", m_linsol_rtol);
295 pp_l.query("max_iterations", m_linsol_maxits);
296
297 /* backward compatibility */
298 const amrex::ParmParse pp_gmres("gmres");
299 pp_gmres.query("verbose_int", m_linsol_verbose_int);
300 pp_gmres.query("restart_length", m_linsol_restart_length);
301 pp_gmres.query("absolute_tolerance", m_linsol_atol);
302 pp_gmres.query("relative_tolerance", m_linsol_rtol);
303 pp_gmres.query("max_iterations", m_linsol_maxits);
304
305 const amrex::ParmParse pp_jac("jacobian");
306 pp_jac.query("pc_type", m_pc_type);
307}
308
309template <class Vec, class Ops>
311 const Vec& a_b,
312 amrex::Real a_time,
313 amrex::Real a_dt,
314 int a_step) const
315{
316 BL_PROFILE("NewtonSolver::Solve()");
318 this->m_is_defined,
319 "NewtonSolver::Solve() called on undefined object");
320 using namespace amrex::literals;
321
322 //
323 // Newton routine to solve nonlinear equation of form:
324 // F(U) = U - b - R(U) = 0
325 //
326
327 CurTime(a_time);
328 CurTimeStep(a_dt);
329
330 amrex::Real norm_abs = 0.;
331 amrex::Real norm0 = 1._rt;
332 amrex::Real norm_rel = 0.;
333
334 int iter;
335 int linear_solver_iters = 0;
336 for (iter = 0; iter < m_maxits;) {
337
338 // Compute residual: F(U) = U - b - R(U)
339 EvalResidual(m_F, a_U, a_b, a_time, iter);
340
341 // Compute norm of the residual
342 norm_abs = m_F.norm2();
343 if (iter == 0) {
344 if (norm_abs > 0.) { norm0 = norm_abs; }
345 else { norm0 = 1._rt; }
346 }
347 norm_rel = norm_abs/norm0;
348
349 // Check for convergence criteria
350 if (this->m_verbose || iter == m_maxits) {
351 amrex::Print() << "Newton: iteration = " << std::setw(3) << iter << ", norm = "
352 << std::scientific << std::setprecision(5) << norm_abs << " (abs.), "
353 << std::scientific << std::setprecision(5) << norm_rel << " (rel.)" << "\n";
354 }
355
356 if (norm_abs < m_atol) {
357 if (this->m_verbose) {
358 amrex::Print() << "Newton: exiting at iteration = " << std::setw(3) << iter
359 << ". Satisfied absolute tolerance " << m_atol << "\n";
360 }
361 break;
362 }
363
364 if (norm_rel < m_rtol) {
365 if (this->m_verbose) {
366 amrex::Print() << "Newton: exiting at iteration = " << std::setw(3) << iter
367 << ". Satisfied relative tolerance " << m_rtol << "\n";
368 }
369 break;
370 }
371
372 if (norm_abs > 100._rt*norm0) {
373 amrex::Print() << "Newton: exiting at iteration = " << std::setw(3) << iter
374 << ". SOLVER DIVERGED! relative tolerance = " << m_rtol << "\n";
375 std::stringstream convergenceMsg;
376 convergenceMsg << "Newton: exiting at iteration " << std::setw(3) << iter <<
377 ". SOLVER DIVERGED! absolute norm = " << norm_abs <<
378 " has increased by 100X from that after first iteration.";
379 WARPX_ABORT_WITH_MESSAGE(convergenceMsg.str());
380 }
381
382 // Solve linear system for Newton step [Jac]*dU = F
383 m_dU.zero();
384 m_linear_function->updatePreCondMat(a_U);
386 linear_solver_iters += m_linear_solver->getNumIters();
387
388 // Update solution
389 a_U -= m_dU;
390
391 iter++;
392 if (iter >= m_maxits) {
393 if (this->m_verbose) {
394 amrex::Print() << "Newton: exiting at iter = " << std::setw(3) << iter
395 << ". Maximum iteration reached: iter = " << m_maxits << "\n";
396 }
397 break;
398 }
399
400 }
401
402 // Update total iteration count
403 m_total_iters += iter;
404 m_total_linsol_iters += linear_solver_iters;
405
406 if (m_rtol > 0. && iter == m_maxits) {
407 std::stringstream convergenceMsg;
408 convergenceMsg << "Newton solver failed to converge after " << iter <<
409 " iterations. Relative norm is " << norm_rel <<
410 " and the relative tolerance is " << m_rtol <<
411 ". Absolute norm is " << norm_abs <<
412 " and the absolute tolerance is " << m_atol;
413 if (this->m_verbose) { amrex::Print() << convergenceMsg.str() << "\n"; }
415 WARPX_ABORT_WITH_MESSAGE(convergenceMsg.str());
416 } else {
417 ablastr::warn_manager::WMRecordWarning("NewtonSolver", convergenceMsg.str());
418 }
419 }
420
422 ((a_step+1)%this->m_diagnostic_interval==0 || a_step==0)) {
423 std::ofstream diagnostic_file{this->m_diagnostic_file, std::ofstream::out | std::ofstream::app};
424 diagnostic_file << std::setprecision(14);
425 diagnostic_file << a_step+1;
426 diagnostic_file << " ";
427 diagnostic_file << a_time + a_dt;
428 diagnostic_file << " ";
429 diagnostic_file << iter;
430 diagnostic_file << " ";
431 diagnostic_file << m_total_iters;
432 diagnostic_file << " ";
433 diagnostic_file << norm_abs;
434 diagnostic_file << " ";
435 diagnostic_file << norm_rel;
436 diagnostic_file << " ";
437 diagnostic_file << linear_solver_iters;
438 diagnostic_file << " ";
439 diagnostic_file << m_total_linsol_iters;
440 diagnostic_file << " ";
441 diagnostic_file << m_linear_solver->getResidualNorm();
442 diagnostic_file << "\n";
443 diagnostic_file.close();
444 }
445
446}
447
448template <class Vec, class Ops>
450 const Vec& a_U,
451 const Vec& a_b,
452 amrex::Real a_time,
453 int a_iter ) const
454{
455 BL_PROFILE("NewtonSolver::EvalResidual()");
456 m_ops->ComputeRHS( m_R, a_U, a_time, a_iter, false );
457
458 // set base U and R(U) for matrix-free Jacobian action calculation
459 m_linear_function->setBaseSolution(a_U);
460 m_linear_function->setBaseRHS(m_R);
461
462 // Compute residual: F(U) = U - b - R(U)
463 a_F.Copy(a_U);
464 a_F -= m_R;
465 a_F -= a_b;
466
467}
468
469#endif
#define BL_PROFILE(a)
LinearSolverType
struct to select the linear solver for implicit schemes
Definition LinearSolverLibrary.H:14
@ petsc_ksp
Definition LinearSolverLibrary.H:14
@ amrex_gmres
Definition LinearSolverLibrary.H:14
PreconditionerType
Types for preconditioners for field solvers.
Definition PreconditionerLibrary.H:14
@ none
Definition PreconditionerLibrary.H:14
#define WARPX_ABORT_WITH_MESSAGE(MSG)
Definition TextMsg.H:15
#define WARPX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition TextMsg.H:13
Vec m_dU
Intermediate Vec containers used by the solver.
Definition NewtonSolver.H:104
int m_maxits
Maximum iterations for the Newton solver.
Definition NewtonSolver.H:129
int m_linsol_verbose_int
Verbosity flag for linear solver.
Definition NewtonSolver.H:159
int m_total_linsol_iters
Total linear iterations for the diagnostic file.
Definition NewtonSolver.H:154
~NewtonSolver() override=default
amrex::Real m_linsol_atol
Absolute tolerance for linear solver.
Definition NewtonSolver.H:144
PreconditionerType m_pc_type
Preconditioner type.
Definition NewtonSolver.H:169
int m_linsol_restart_length
Restart iteration for linear solver.
Definition NewtonSolver.H:164
PreconditionerType GetPreconditionerType() const override
Return preconditioner type.
Definition NewtonSolver.H:75
void Solve(Vec &a_U, const Vec &a_b, amrex::Real a_time, amrex::Real a_dt, int a_step) const override
Solve the specified nonlinear equation for U. Picard: U = b + R(U). Newton: F(U) = U - b - R(U) = 0.
Definition NewtonSolver.H:310
void Define(const Vec &a_U, Ops *a_ops) override
Read user-provided parameters that control the nonlinear solver. Allocate intermediate data container...
Definition NewtonSolver.H:203
void GetSolverParams(amrex::Real &a_rtol, amrex::Real &a_atol, int &a_maxits) override
Return the convergence parameters used by the nonlinear solver.
Definition NewtonSolver.H:54
void ParseParameters()
Definition NewtonSolver.H:278
void PrintParams() const override
Print parameters used by the nonlinear solver.
Definition NewtonSolver.H:80
std::unique_ptr< JacobianFunctionMF< Vec, Ops > > m_linear_function
The linear function used by linear solver to compute A*v. In the contect of JFNK, A = dF/dU (i....
Definition NewtonSolver.H:177
amrex::Real m_atol
Absolute tolerance for the Newton solver.
Definition NewtonSolver.H:124
bool m_require_convergence
Flag to determine whether convergence is required.
Definition NewtonSolver.H:114
NewtonSolver & operator=(const NewtonSolver &)=delete
LinearSolverType m_linear_solver_type
Choice of linear solver.
Definition NewtonSolver.H:182
NewtonSolver(const NewtonSolver &)=delete
amrex::Real m_cur_time
Definition NewtonSolver.H:171
NewtonSolver(NewtonSolver &&) noexcept=delete
std::unique_ptr< LinearSolver< Vec, JacobianFunctionMF< Vec, Ops > > > m_linear_solver
The linear solver object.
Definition NewtonSolver.H:187
Vec m_F
Definition NewtonSolver.H:104
amrex::Real m_linsol_rtol
Relative tolerance for linear solver.
Definition NewtonSolver.H:139
Vec m_R
Definition NewtonSolver.H:104
amrex::Real m_dt
Definition NewtonSolver.H:171
void CurTime(amrex::Real a_time) const
Definition NewtonSolver.H:63
int m_total_iters
Total nonlinear iterations for the diagnostic file.
Definition NewtonSolver.H:134
void CurTimeStep(amrex::Real a_dt) const
Definition NewtonSolver.H:69
NewtonSolver()=default
int m_linsol_maxits
Maximum iterations for linear solver.
Definition NewtonSolver.H:149
void EvalResidual(Vec &a_F, const Vec &a_U, const Vec &a_b, amrex::Real a_time, int a_iter) const
Compute the nonlinear residual: F(U) = U - b - R(U).
Definition NewtonSolver.H:449
amrex::Real m_rtol
Relative tolerance for the Newton solver.
Definition NewtonSolver.H:119
Ops * m_ops
Pointer to Ops class.
Definition NewtonSolver.H:109
bool m_usePC
Definition NonlinearSolver.H:94
bool m_is_defined
Definition NonlinearSolver.H:90
std::string m_diagnostic_file
Definition NonlinearSolver.H:92
NonlinearSolver()=default
int m_diagnostic_interval
Definition NonlinearSolver.H:93
bool m_verbose
Definition NonlinearSolver.H:91
int query(std::string_view name, bool &ref, int ival=FIRST) const
amrex_real Real
bool IOProcessor() noexcept
void WMRecordWarning(const std::string &topic, const std::string &text, const WarnPriority &priority=WarnPriority::medium)
Helper function to abbreviate the call to WarnManager::GetInstance().RecordWarning (recording a warni...
Definition WarnManager.cpp:320
std::string getEnumNameString(T const &v)
bool FileExists(const std::string &filename)