tpsanewtonmethod.hpp
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*
4 Copyright 2025 NORCE AS
5
6 This file is part of the Open Porous Media project (OPM).
7
8 OPM is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 OPM is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with OPM. If not, see <http://www.gnu.org/licenses/>.
20
21 Consult the COPYING file in the top-level source directory of this
22 module for the precise wording of the license and the list of
23 copyright holders.
24*/
25#ifndef TPSA_NEWTON_METHOD_HPP
26#define TPSA_NEWTON_METHOD_HPP
27
28#include <opm/common/Exceptions.hpp>
29#include <opm/common/OpmLog/OpmLog.hpp>
30
35
36#include <cmath>
37#include <cstddef>
38#include <iomanip>
39#include <sstream>
40#include <string>
41
42
43namespace Opm {
44
55template <class TypeTag>
57{
62
70
71 using IstlMatrix = typename SparseMatrixAdapter::IstlMatrix;
72
73public:
79 explicit TpsaNewtonMethod(Simulator& simulator)
80 : simulator_(simulator)
81 , linearSolver_(simulator)
82 , error_(1e100)
83 , lastError_(1e100)
84 , initialError_(1e100)
88 {
89 // Read runtime/default Newton parameters
90 params_.read();
91 }
92
96 static void registerParameters()
97 {
98 LinearSolverBackend::registerParameters();
100 }
101
107 bool apply()
108 {
109 // Make sure all timers are prestine
114
115 // Get vectors and linearizer
116 SolutionVector& nextSolution = model().solution(/*historyIdx=*/0);
117 SolutionVector currentSolution(nextSolution);
118 GlobalEqVector solutionUpdate(nextSolution.size());
119
120 Linearizer& linearizer = model().linearizer();
121
122 TimerGuard prePostProcessTimerGuard(prePostProcessTimer_);
123
124 // Tell the implementation that we begin solving
126 begin_();
128
129 try {
130 TimerGuard innerPrePostProcessTimerGuard(prePostProcessTimer_);
131 TimerGuard linearizeTimerGuard(linearizeTimer_);
132 TimerGuard updateTimerGuard(updateTimer_);
133 TimerGuard solveTimerGuard(solveTimer_);
134
135 // Execute the method as long as the implementation thinks that we should do another iteration
136 while (proceed_()) {
137 // Notify the implementation that we're about to start a new iteration
141
142 // Make the current solution to the old one
143 currentSolution = nextSolution;
144
145 // Do the actual linearization
149
150 // Get residual and Jacobian for convergence check and preparation of linear solver
152 auto& residual = linearizer.residual();
153 const auto& jacobian = linearizer.jacobian();
154 linearSolver_.prepare(jacobian, residual);
155 linearSolver_.getResidual(residual);
157
158 // TODO: should its costs be counted to the linearization or to the update?
160 preSolve_(currentSolution, residual);
162
163 // Check convergence criteria
164 if (converged()) {
165 // Tell the implementation that we're done with this iteration
169
170 break;
171 }
172
173 // Solve A x = b, where b is the residual, A is its Jacobian and x is the update of the solution
175 solutionUpdate = 0.0;
176 const bool conv = linearSolver_.solve(solutionUpdate);
179
180 if (!conv) {
182 if (verbosity_() > 0) {
183 OpmLog::warning("TPSA: Linear solver did not converge!");
184 }
185
187 failed_();
189
190 return false;
191 }
192
193 // Update the current solution with the delta
195 update_(nextSolution, currentSolution, solutionUpdate, residual);
197
198 // End of iteration calculations
202 }
203 }
204 catch (const Dune::Exception& e)
205 {
206 if (verbosity_() > 0) {
207 OpmLog::error("TPSA: Newton method caught exception: \"" +
208 std::string(e.what()) + "\"");
209 }
210
212 failed_();
214
215 return false;
216 }
217 catch (const NumericalProblem& e)
218 {
219 if (verbosity_() > 0) {
220 OpmLog::error("TPSA: Newton method caught exception: \"" +
221 std::string(e.what()) + "\"");
222 }
223
225 failed_();
227
228 return false;
229 }
230
231 // print the timing summary of the time step
232 if (verbosity_() > 0) {
233 std::ostringstream oss;
234 oss << std::setprecision(2)
235 << "TPSA: "
236 << "Newton iter = " << numIterations();
237 if (!singleIteration_()) {
238 oss << " (error=" << error_ << ")";
239 }
240 oss << " | "
241 << "Linearizations = "
242 << numLinearizations() << " ("
243 << linearizeTimer_.realTimeElapsed() << "s) | "
244 << "Linear iter = "
245 << numTotLinearIterations() << " ("
246 << solveTimer_.realTimeElapsed() << "s)";
247 OpmLog::info(oss.str());
248 }
249
250 // if we're not converged, tell the implementation that we've failed; ignored for
251 // max. iteration = 1.
252 if (!singleIteration_() && !converged()) {
254 failed_();
255 if (verbosity_() > 0) {
256 OpmLog::warning("TPSA: Newton iterations did not converge!");
257 }
259 return false;
260 }
261 return true;
262 }
263
272 bool converged() const
273 { return error_ <= tolerance(); }
274
280 Scalar initialError() const
281 { return initialError_; }
282
289 { return initialError_ <= tolerance(); }
290
296 Problem& problem()
297 { return simulator_.problem(); }
298
304 const Problem& problem() const
305 { return simulator_.problem(); }
306
312 Model& model()
313 { return simulator_.problem().geoMechModel(); }
314
320 const Model& model() const
321 { return simulator_.problem().geoMechModel(); }
322
328 LinearSolverBackend& linearSolver()
329 { return linearSolver_; }
330
336 const LinearSolverBackend& linearSolver() const
337 { return linearSolver_; }
338
344 int numIterations() const
345 { return numIterations_; }
346
353 { return numLinearizations_; }
354
361 { return numTotLinearIterations_; }
362
368 Scalar tolerance() const
369 { return params_.tolerance_; }
370
376 Scalar minIterations() const
377 { return params_.minIterations_; }
378
385 { return prePostProcessTimer_; }
386
392 const Timer& linearizeTimer() const
393 { return linearizeTimer_; }
394
400 const Timer& solveTimer() const
401 { return solveTimer_; }
402
408 const Timer& updateTimer() const
409 { return updateTimer_; }
410
411protected:
419 int verbosity_() const
420 { return simulator_.gridView().comm().rank() == 0 ? params_.verbosity_ : 0; }
421
430 bool singleIteration_() const
431 { return params_.maxIterations_ <= 1; }
432
436 void begin_()
437 {
438 numIterations_ = 0;
441 error_ = 1e100;
442 initialError_ = 1e100;
443 }
444
449 {
450 // Reset last Newton error to previous
452 }
453
458 {
459 model().linearizer().linearizeDomain();
461 }
462
469 void preSolve_(const SolutionVector& /*currentSolution*/,
470 const GlobalEqVector& currentResidual)
471 {
473 Scalar newtonMaxError = params_.maxError_;
474
475 // Calculate the error as the maximum weighted tolerance of the solution's residual
476 error_ = 0;
477 const auto& elemMapper = simulator_.model().elementMapper();
478 for (const auto& elem : elements(simulator_.gridView(), Dune::Partitions::interior)) {
479 unsigned dofIdx = elemMapper.index(elem);
480 const auto& r = currentResidual[dofIdx];
481 for (unsigned eqIdx = 0; eqIdx < r.size(); ++eqIdx) {
482 error_ = max(std::abs(r[eqIdx] * model().eqWeight(dofIdx, eqIdx)), error_);
483 }
484 }
485
486 // Take the other processes into account
487 error_ = simulator_.gridView().comm().max(error_);
488
489 // Remember what the state handed to the Newton method was worth, see initiallyConverged()
490 if (numLinearizations_ == 1) {
492 }
493
494 // Make sure that the error never grows beyond the maximum allowed one
495 if (error_ > newtonMaxError) {
496 throw NumericalProblem("TPSA: Newton error " + std::to_string(double(error_)) +
497 " is larger than maximum allowed error of " +
498 std::to_string(double(newtonMaxError)));
499 }
500 }
501
513 void update_(SolutionVector& nextSolution,
514 const SolutionVector& currentSolution,
515 const GlobalEqVector& solutionUpdate,
516 const GlobalEqVector& currentResidual)
517 {
518 // make sure not to swallow non-finite values at this point
519 if (!std::isfinite(solutionUpdate.one_norm())) {
520 throw NumericalProblem("TPSA: Non-finite update in Newton!");
521 }
522
523 std::size_t numGridDof = model().numGridDof();
524 for (unsigned dofIdx = 0; dofIdx < numGridDof; ++dofIdx) {
526 nextSolution[dofIdx],
527 currentSolution[dofIdx],
528 solutionUpdate[dofIdx],
529 currentResidual[dofIdx]);
530 }
531
532 // Update material state
533 model().updateMaterialState(/*timeIdx=*/0);
534 }
535
544 void updatePrimaryVariables_(unsigned /*dofIdx*/,
545 PrimaryVariables& nextValue,
546 const PrimaryVariables& currentValue,
547 const EqVector& update,
548 const EqVector& /*currentResidual*/)
549 {
550 nextValue = currentValue;
551 nextValue -= update;
552 }
553
558 {
559 // Increase Newton iterations
561
562 // Output error info
563 if (verbosity_() > 1) {
564 OpmLog::info("TPSA: End Newton iteration " + std::to_string(numIterations_) +
565 " with error = " + std::to_string(error_));
566 }
567 }
568
574 bool proceed_() const
575 {
576 // Exactly one linearization and one linear solve, whatever the error did
577 if (singleIteration_()) {
578 return numIterations() < 1;
579 }
580
581 if (numIterations() < params_.minIterations_) {
582 return true;
583 }
584 else if (converged()) {
585 // we are below the specified tolerance, so we don't have to
586 // do more iterations
587 return false;
588 }
589 else if (numIterations() >= params_.maxIterations_) {
590 // we have exceeded the allowed number of steps. If the
591 // error was reduced by a factor of at least 4,
592 // in the last iterations we proceed even if we are above
593 // the maximum number of steps
594 return error_ * 4.0 < lastError_;
595 }
596
597 return true;
598 }
599
603 void failed_()
604 { }
605
606 Simulator& simulator_;
607 LinearSolverBackend linearSolver_;
608
613
614 Scalar error_;
618
622}; // class TpsaNewtonMethod
623
624} // namespace Opm
625
626#endif
A simple class which makes sure that a timer gets stopped if an exception is thrown.
Definition: timerguard.hh:42
Provides an encapsulation to measure the system time.
Definition: timer.hpp:46
void start()
Start counting the time resources used by the simulation.
void halt()
Stop the measurement reset all timing values.
double realTimeElapsed() const
Return the real time [s] elapsed during the periods the timer was active since the last reset.
double stop()
Stop counting the time resources.
Newton method solving for generic TPSA model.
Definition: tpsanewtonmethod.hpp:57
int numTotLinearIterations_
Definition: tpsanewtonmethod.hpp:621
Scalar tolerance() const
Return the current tolerance at which the Newton method considers itself to be converged.
Definition: tpsanewtonmethod.hpp:368
void update_(SolutionVector &nextSolution, const SolutionVector &currentSolution, const GlobalEqVector &solutionUpdate, const GlobalEqVector &currentResidual)
Update the current solution with a delta vector.
Definition: tpsanewtonmethod.hpp:513
Simulator & simulator_
Definition: tpsanewtonmethod.hpp:606
int numTotLinearIterations() const
Returns the number of linear solver iterations done since the Newton method was invoked.
Definition: tpsanewtonmethod.hpp:360
bool singleIteration_() const
Whether the Newton method is limited to a single iteration.
Definition: tpsanewtonmethod.hpp:430
int numLinearizations() const
Returns the number of linearizations that has done since the Newton method was invoked.
Definition: tpsanewtonmethod.hpp:352
Problem & problem()
Returns a reference to the object describing the current physical problem.
Definition: tpsanewtonmethod.hpp:296
static void registerParameters()
Register all run-time parameters for the Newton method.
Definition: tpsanewtonmethod.hpp:96
void endIteration_()
Indicates that one Newton iteration was finished.
Definition: tpsanewtonmethod.hpp:557
const Timer & solveTimer() const
Return linear solver timer.
Definition: tpsanewtonmethod.hpp:400
Timer linearizeTimer_
Definition: tpsanewtonmethod.hpp:610
int numIterations_
Definition: tpsanewtonmethod.hpp:619
Scalar error_
Definition: tpsanewtonmethod.hpp:614
const Model & model() const
Returns a reference to the geomechanics model.
Definition: tpsanewtonmethod.hpp:320
Timer prePostProcessTimer_
Definition: tpsanewtonmethod.hpp:609
TpsaNewtonMethod(Simulator &simulator)
Constructor.
Definition: tpsanewtonmethod.hpp:79
Timer solveTimer_
Definition: tpsanewtonmethod.hpp:611
LinearSolverBackend linearSolver_
Definition: tpsanewtonmethod.hpp:607
const Timer & linearizeTimer() const
Return linearization timer.
Definition: tpsanewtonmethod.hpp:392
Scalar minIterations() const
Returns minimum number of Newton iterations used.
Definition: tpsanewtonmethod.hpp:376
void beginIteration_()
Calculations at the beginning of a Newton iteration.
Definition: tpsanewtonmethod.hpp:448
bool converged() const
Returns true if the error of the solution is below the tolerance.
Definition: tpsanewtonmethod.hpp:272
Timer updateTimer_
Definition: tpsanewtonmethod.hpp:612
bool proceed_() const
Returns true iff another Newton iteration should be done.
Definition: tpsanewtonmethod.hpp:574
int numIterations() const
Returns the number of iterations done since the Newton method was invoked.
Definition: tpsanewtonmethod.hpp:344
Scalar initialError() const
Returns the error of the first linearization of the last apply().
Definition: tpsanewtonmethod.hpp:280
void linearizeDomain_()
Linearize the global non-linear system of equations associated with the spatial domain.
Definition: tpsanewtonmethod.hpp:457
const LinearSolverBackend & linearSolver() const
Returns the linear solver backend object for external use.
Definition: tpsanewtonmethod.hpp:336
void updatePrimaryVariables_(unsigned, PrimaryVariables &nextValue, const PrimaryVariables &currentValue, const EqVector &update, const EqVector &)
Update a single primary variables object.
Definition: tpsanewtonmethod.hpp:544
bool apply()
Run the Newton method.
Definition: tpsanewtonmethod.hpp:107
bool initiallyConverged() const
Returns true if the state handed to the last apply() already solved the system.
Definition: tpsanewtonmethod.hpp:288
int numLinearizations_
Definition: tpsanewtonmethod.hpp:620
LinearSolverBackend & linearSolver()
Returns the linear solver backend object for external use.
Definition: tpsanewtonmethod.hpp:328
Scalar lastError_
Definition: tpsanewtonmethod.hpp:615
const Timer & prePostProcessTimer() const
Return post-process timer.
Definition: tpsanewtonmethod.hpp:384
const Problem & problem() const
Returns a reference to the object describing the current physical problem.
Definition: tpsanewtonmethod.hpp:304
Model & model()
Returns a reference to the geomechanics model.
Definition: tpsanewtonmethod.hpp:312
void begin_()
Called before the Newton method is applied to an non-linear system of equations.
Definition: tpsanewtonmethod.hpp:436
TpsaNewtonMethodParams< Scalar > params_
Definition: tpsanewtonmethod.hpp:617
void failed_()
Called if the Newton method broke down.
Definition: tpsanewtonmethod.hpp:603
void preSolve_(const SolutionVector &, const GlobalEqVector &currentResidual)
Compute error before a Newton iteration.
Definition: tpsanewtonmethod.hpp:469
Scalar initialError_
Definition: tpsanewtonmethod.hpp:616
const Timer & updateTimer() const
Return solution update timer.
Definition: tpsanewtonmethod.hpp:408
int verbosity_() const
Verbosity level of Newton print messages.
Definition: tpsanewtonmethod.hpp:419
Definition: blackoilbioeffectsmodules.hh:45
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:233
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Struct holding the parameters for TpsaNewtonMethod.
Definition: tpsanewtonmethodparams.hpp:57
static void registerParameters()