Cantera  4.0.0a2
Loading...
Searching...
No Matches
MultiNewton.cpp
Go to the documentation of this file.
1//! @file MultiNewton.cpp Damped Newton solver for 1D multi-domain problems
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8
9#include <chrono>
10#include <ctime>
11
12using namespace std;
13
14namespace Cantera
15{
16
18 : m_n(sz)
19{
20}
21
22void MultiNewton::resize(size_t sz)
23{
24 m_n = sz;
25 m_x.resize(m_n);
26 m_stp.resize(m_n);
27 m_stp1.resize(m_n);
28}
29
30void MultiNewton::step(span<const double> x, span<double> step,
31 SteadyStateSystem& r, int loglevel)
32{
33 r.eval(x, step);
34 for (size_t n = 0; n < r.size(); n++) {
35 step[n] = -step[n];
36 }
37
38 auto jac = r.linearSolver();
39 try {
40 auto ts0 = std::chrono::steady_clock::now();
41 jac->solve(step, step);
42 r.recordLinearSolve(std::chrono::duration<double>(
43 std::chrono::steady_clock::now() - ts0).count());
44 } catch (CanteraError&) {
45 if (jac->info() > 0) {
46 // Positive value for "info" indicates the row where factorization failed
47 size_t row = static_cast<size_t>(jac->info() - 1);
48 throw CanteraError("MultiNewton::step",
49 "Jacobian is singular for matrix row {}:\n{}",
50 row, r.componentName(row));
51 }
52 throw;
53 }
54}
55
56double MultiNewton::boundStep(span<const double> x0, span<const double> step0,
57 const SteadyStateSystem& r, int loglevel)
58{
59 const static string separator = fmt::format("\n {:=>71}", ""); // equals sign separator
60 double fbound = 1.0;
61 bool wroteTitle = false;
62
63 for (size_t i = 0; i < size(); i++) {
64 double above = r.upperBound(i);
65 double below = r.lowerBound(i);
66
67 double val = x0[i];
68 if (loglevel > 0 && (val > above + 1.0e-12 || val < below - 1.0e-12)) {
69 writelog("\nERROR: solution component {} out of bounds.\n", i);
70 writelog("{}: value = {:10.3e} (lower = {:10.3e}, upper = {:10.3e})\n",
71 r.componentName(i), val, below, above);
72 }
73
74 double newval = val + step0[i];
75
76 if (newval > above) {
77 fbound = std::max(0.0, std::min(fbound,
78 (above - val)/(newval - val)));
79 } else if (newval < below) {
80 fbound = std::min(fbound, (val - below)/(val - newval));
81 }
82
83 if (loglevel > 1 && (newval > above || newval < below)) {
84 if (!wroteTitle){
85 string header = fmt::format(" {:=>10}", "") +
86 " Undamped Newton step takes solution out of bounds " +
87 fmt::format("{:=>10}", "");
88 writelog("\n{}", header);
89 writelog(separator);
90 const auto& [custom1, custom2] = r.componentTableHeader();
91 // Split header across 2 lines to shorten the line length
92 writelog("\n {:<24s} {:<10s} {:<10s} {:<9s} {:<9s}",
93 custom1, "", "Value", "Min", "Max");
94 writelog("\n {:<24s} {:<10s} {:<10s} {:<9s} {:<9s}",
95 custom2, "Value", "Change", "Bound", "Bound");
96 writelog(separator);
97 wroteTitle = true;
98 }
99 string comp_info = r.componentTableLabel(i);
100 writelog("\n {:<24s} {:>10.3e} {:>10.3e} {:>9.2e} {:>9.2e}",
101 comp_info, val, step0[i], below, above);
102 }
103 }
104 if (loglevel > 1 && wroteTitle) { // If a title was written, close up the table
105 writelog(separator);
106 }
107 return fbound;
108}
109
110int MultiNewton::dampStep(span<const double> x0, span<const double> step0,
111 span<double> x1, span<double> step1, double& s1,
112 SteadyStateSystem& r, int loglevel, bool writetitle)
113{
114 // write header
115 if (loglevel > 0 && writetitle) {
116 writelog("\n\n {:-^70}", " Damped Newton iteration ");
117 writelog("\n {:<4s} {:<10s} {:<10s} {:<7s} {:<7s} {:<7s} {:<5s} {:<3s}\n",
118 "Iter", "F_damp", "F_bound", "log(ss)",
119 "log(s0)", "log(s1)", "N_jac", "Age");
120 writelog(" {:->70}", "");
121 }
122
123 // compute the weighted norm of the undamped step size step0
124 double s0 = r.weightedNorm(step0);
125
126 // compute the multiplier to keep all components in bounds
127 double fbound = boundStep(x0, step0, r, loglevel-1);
128
129 // if fbound is very small, then x0 is already close to the boundary and
130 // step0 points out of the allowed domain. In this case, the Newton
131 // algorithm fails, so return an error condition.
132 if (fbound < 1.e-10) {
133 debuglog("\n No damped step can be taken without violating solution component bounds.", loglevel);
134 return -3;
135 }
136
137 // ---------- Attempt damped step ----------
138
139 // damping coefficient starts at 1.0, but must be scaled by the
140 // fbound factor to ensure that the solution remains within bounds.
141 double alpha = fbound*1.0;
142 size_t m;
143 auto jac = r.linearSolver();
144 for (m = 0; m < m_maxDampIter; m++) {
145 // step the solution by the damped step size
146 // x_{k+1} = x_k + alpha_k*J(x_k)^-1 F(x_k)
147 for (size_t j = 0; j < m_n; j++) {
148 x1[j] = x0[j] + alpha*step0[j];
149 }
150
151 // compute the next undamped step that would result if x1 is accepted
152 // J(x_k)^-1 F(x_k+1)
153 try {
154 // Allow solver to continue after step failure (which can involve problems
155 // such as errors setting the thermodynamic state) by returning to time
156 // stepping mode.
157 step(x1, step1, r, loglevel-1);
158 } catch (CanteraError& err) {
159 if (loglevel > 1) {
160 writelog("\n Reducing step size due to evaluation failure on trial step:"
161 "\n {}:\n {}", err.getMethod(), err.getMessage());
162 }
163 alpha /= m_dampFactor;
164 continue;
165 }
166
167 // compute the weighted norm of step1
168 s1 = r.weightedNorm(step1);
169
170 if (loglevel > 0) {
171 double ss = r.ssnorm(x1, step1);
172 writelog("\n {:<4d} {:<9.3e} {:<9.3e} {:>6.3f} {:>6.3f} {:>6.3f} {:<5d} {:d}/{:d}",
173 m, alpha, fbound, log10(ss+SmallNumber),
174 log10(s0+SmallNumber), log10(s1+SmallNumber),
175 jac->nEvals(), jac->age(), m_maxAge);
176 }
177
178 // If the norm of s1 is less than the norm of s0, then accept this
179 // damping coefficient. Also accept it if this step would result in a
180 // converged solution. Otherwise, decrease the damping coefficient and
181 // try again.
182 if (s1 < 1.0 || s1 < s0) {
183 break;
184 }
185 alpha /= m_dampFactor;
186 }
187
188 // If a damping coefficient was found, return 1 if the solution after
189 // stepping by the damped step would represent a converged solution, and
190 // return 0 otherwise. If no damping coefficient could be found, return -2.
191 if (m < m_maxDampIter) {
192 if (s1 > 1.0) {
193 debuglog("\n Damping coefficient found (solution has not converged yet)", loglevel);
194 return 0;
195 } else {
196 debuglog("\n Damping coefficient found (solution has converged)", loglevel);
197 return 1;
198 }
199 } else {
200 debuglog("\n No damping coefficient found (max damping iterations reached)", loglevel);
201 return -2;
202 }
203}
204
205int MultiNewton::solve(span<const double> x0, span<double> x1,
206 SteadyStateSystem& r, int loglevel)
207{
208 clock_t t0 = clock();
209 int status = 0;
210 bool forceNewJac = false;
211 bool write_header = true;
212 double s1=1.e30;
213
214 copy(x0.begin(), x0.end(), m_x.begin());
216
217 double rdt = r.rdt();
218 int nJacReeval = 0;
219 auto jac = r.linearSolver();
220 while (true) {
222 // Check whether the Jacobian should be re-evaluated.
223 if (jac->age() > m_maxAge) {
224 if (loglevel > 1) {
225 writelog("\n Maximum Jacobian age reached ({}), updating it.", m_maxAge);
226 }
227 forceNewJac = true;
228 }
229
230 if (forceNewJac) {
231 r.evalJacobian(m_x);
232 try {
234 } catch (CanteraError& err) {
235 // Allow solver to continue after failure to factorize the steady-state
236 // Jacobian by returning to time stepping mode
237 if (rdt == 0.0) {
238 if (loglevel > 1) {
239 writelog("\n Steady Jacobian factorization failed:"
240 "\n {}:\n {}",
241 err.getMethod(), err.getMessage());
242 }
243 return -4;
244 } else {
245 throw;
246 }
247 }
248 forceNewJac = false;
249 }
250
251 // compute the undamped Newton step
252 step(m_x, m_stp, r, loglevel-1);
253
254 // increment the Jacobian age
255 jac->incrementAge();
256
257 // damp the Newton step
258 status = dampStep(m_x, m_stp, x1, m_stp1, s1, r, loglevel-1, write_header);
259 write_header = false;
260
261 // Successful step, but not converged yet. Take the damped step, and try
262 // again.
263 if (status == 0) {
264 copy(x1.begin(), x1.end(), m_x.begin());
265 } else if (status == 1) { // convergence
266 if (rdt == 0) {
267 jac->setAge(0); // for efficient sensitivity analysis
268 }
269 break;
270 } else if (status < 0) {
271 // If dampStep fails, first try a new Jacobian if an old one was
272 // being used. If it was a new Jacobian, then return -1 to signify
273 // failure.
274 if (jac->age() > 1) {
275 forceNewJac = true;
276 if (nJacReeval > 3) {
277 break;
278 }
279 nJacReeval++;
280 if (loglevel > 1) {
281 writelog("\n Re-evaluating Jacobian (damping coefficient not found"
282 " with this Jacobian)");
283 }
284 } else {
285 break;
286 }
287 }
288 }
289 // Close off the damped iteration table that is written by the dampedStep() method
290 if (loglevel > 1) {
291 writelog("\n {:->70}", "");
292 }
293
294 if (status < 0) { // Reset x1 to x0 if the solution failed
295 copy(m_x.begin(), m_x.end(), x1.begin());
296 }
297 m_elapsed += (clock() - t0)/(1.0*CLOCKS_PER_SEC);
298 return status;
299}
300
301} // end namespace Cantera
Base class for exceptions thrown by Cantera classes.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
virtual string getMethod() const
Get the name of the method that threw the exception.
double m_dampFactor
Factor by which the damping coefficient is reduced in each iteration.
size_t size()
Get the number of variables in the system.
Definition MultiNewton.h:34
void resize(size_t points)
Change the problem size.
vector< double > m_x
Work array holding the system state after the last successful step. Size m_n.
int dampStep(span< const double > x0, span< const double > step0, span< double > x1, span< double > step1, double &s1, SteadyStateSystem &r, int loglevel, bool writetitle)
Performs a damped Newton step to solve the system of nonlinear equations.
double m_elapsed
Elapsed CPU time spent computing the Jacobian.
vector< double > m_stp1
Work array holding the damped Newton step. Size m_n.
vector< double > m_stp
Work array holding the undamped Newton step or the system residual. Size m_n.
size_t m_maxDampIter
Maximum number of damping iterations.
int m_maxAge
Maximum allowable Jacobian age before it is recomputed.
MultiNewton(int sz)
Constructor.
double boundStep(span< const double > x0, span< const double > step0, const SteadyStateSystem &r, int loglevel)
Return the factor by which the undamped Newton step 'step0' must be multiplied in order to keep all s...
size_t m_n
number of variables
void step(span< const double > x, span< double > step, SteadyStateSystem &r, int loglevel)
Compute the undamped Newton step.
int solve(span< const double > x0, span< double > x1, SteadyStateSystem &r, int loglevel)
Find the solution to F(x) = 0 by damped Newton iteration.
int m_lastIterations
Number of Newton iterations taken in the last solve() call.
Base class for representing a system of differential-algebraic equations and solving for its steady-s...
shared_ptr< SystemJacobian > linearSolver() const
Get the the linear solver being used to hold the Jacobian matrix and solve linear systems as part of ...
size_t size() const
Total solution vector length;.
virtual double upperBound(size_t i) const =0
Get the upper bound for global component i in the state vector.
double rdt() const
Reciprocal of the time step.
virtual string componentName(size_t i) const
Get the name of the i-th component of the state vector.
virtual string componentTableLabel(size_t i) const
Get elements of the component name, aligned with the column headings given by componentTableHeader().
void factorizeJacobian()
Update the transient term of the Jacobian (forming and factorizing ) and record the elapsed wall-cloc...
virtual double weightedNorm(span< const double > step) const =0
Compute the weighted norm of step.
virtual void eval(span< const double > x, span< double > r, double rdt=-1.0, int count=1)=0
Evaluate the residual function.
virtual double lowerBound(size_t i) const =0
Get the lower bound for global component i in the state vector.
void recordLinearSolve(double wallTime)
Record wall-clock time [s] spent in a linear solve.
double ssnorm(span< const double > x, span< double > r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x.
virtual void evalJacobian(span< const double > x0)=0
Evaluates the Jacobian at x0 using finite differences.
virtual pair< string, string > componentTableHeader() const
Get header lines describing the column names included in a component label.
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:154
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:171
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:161
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...