Cantera  4.0.0a2
Loading...
Searching...
No Matches
SteadyStateSystem.cpp
Go to the documentation of this file.
1//! @file SteadyStateSystem.cpp
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
9
10using namespace std;
11
12namespace Cantera
13{
14
15SteadyStateSystem::SteadyStateSystem()
16{
17 m_state = make_shared<vector<double>>();
18 m_newt = make_unique<MultiNewton>(1);
19}
20
21SteadyStateSystem::~SteadyStateSystem()
22{
23}
24
25void SteadyStateSystem::setInitialGuess(span<const double> x)
26{
29 m_state->assign(x.begin(), x.end());
30}
31
32void SteadyStateSystem::getState(span<double> x) const
33{
34 copy(m_xnew.begin(), m_xnew.end(), x.begin());
35}
36
37void SteadyStateSystem::solve(int loglevel)
38{
39 size_t istep = 0;
40 int nsteps = m_steps[istep];
41 m_nsteps = 0;
42 double dt = m_tstep;
43
44 while (true) {
45 // Keep the attempt_counter in the range of [1, max_history]
47
48 // Attempt to solve the steady problem
51 debuglog("\nAttempt Newton solution of steady-state problem.", loglevel);
52 if (!m_jac_ok) {
54 try {
55 m_jac->updateTransient(m_rdt, m_mask);
56 m_jac_ok = true;
57 } catch (CanteraError& err) {
58 // Allow solver to continue after failure to factorize the steady-state
59 // Jacobian by returning to time stepping mode
60 if (m_rdt == 0.0) {
61 if (loglevel > 1) {
62 writelog("\nSteady Jacobian factorization failed:"
63 "\n {}:\n {}",
64 err.getMethod(), err.getMessage());
65 }
66 } else {
67 throw;
68 }
69 }
70 }
71 int m = -1;
72 if (m_jac_ok) {
73 m = newton().solve(*m_state, m_xnew, *this, loglevel);
74 }
75 if (m == 1) {
76 *m_state = m_xnew;
77 writeDebugInfo("NewtonSuccess", "After successful Newton solve",
78 loglevel, m_attempt_counter);
79
80 return;
81 } else {
82 debuglog("\nNewton steady-state solve failed.\n", loglevel);
83 writeDebugInfo("NewtonFail", "After unsuccessful Newton solve",
84 loglevel, m_attempt_counter);
85
86 if (loglevel > 0) {
87 writelog("\nAttempt {} timesteps.", nsteps);
88 }
89
90 dt = timeStep(nsteps, dt, *m_state, m_xnew, loglevel-1);
92 writeDebugInfo("Timestepping", "After timestepping", loglevel,
94
95 // Repeat the last timestep's data for logging purposes
96 if (loglevel == 1) {
97 writelog("\nFinal timestep info: dt= {:<10.4g} log(ss)= {:<10.4g}\n", dt,
98 log10(ssnorm(*m_state, m_xnew)));
99 }
100 istep++;
101 if (istep >= m_steps.size()) {
102 nsteps = m_steps.back();
103 } else {
104 nsteps = m_steps[istep];
105 }
106 dt = std::min(dt, m_tmax);
107 }
108 }
109}
110
111SteadyStateSystem::TimeStepGrowthStrategy
112SteadyStateSystem::parseTimeStepGrowthStrategy(const string& strategy)
113{
114 if (strategy == "fixed-growth") {
115 return TimeStepGrowthStrategy::fixed;
116 } else if (strategy == "steady-norm") {
117 return TimeStepGrowthStrategy::steadyNorm;
118 } else if (strategy == "transient-residual") {
119 return TimeStepGrowthStrategy::transientResidual;
120 } else if (strategy == "residual-ratio") {
121 return TimeStepGrowthStrategy::residualRatio;
122 } else if (strategy == "newton-iterations") {
123 return TimeStepGrowthStrategy::newtonIterations;
124 }
125 throw CanteraError("SteadyStateSystem::setTimeStepGrowthStrategy",
126 "Unknown time step growth strategy '{}'; must be one of "
127 "'fixed-growth', 'steady-norm', 'transient-residual', "
128 "'residual-ratio', or 'newton-iterations'.", strategy);
129}
130
131string SteadyStateSystem::timeStepGrowthStrategyName(TimeStepGrowthStrategy strategy)
132{
133 switch (strategy) {
134 case TimeStepGrowthStrategy::fixed:
135 return "fixed-growth";
136 case TimeStepGrowthStrategy::steadyNorm:
137 return "steady-norm";
138 case TimeStepGrowthStrategy::transientResidual:
139 return "transient-residual";
140 case TimeStepGrowthStrategy::residualRatio:
141 return "residual-ratio";
142 case TimeStepGrowthStrategy::newtonIterations:
143 return "newton-iterations";
144 }
145 throw CanteraError("SteadyStateSystem::timeStepGrowthStrategyName",
146 "Unknown time step growth strategy.");
147}
148
150{
151 m_tstep_growth_strategy = parseTimeStepGrowthStrategy(strategy);
152}
153
155{
156 return timeStepGrowthStrategyName(m_tstep_growth_strategy);
157}
158
159double SteadyStateSystem::calculateTimeStepGrowthFactor(span<const double> x_before,
160 span<const double> x_after)
161{
162 if (m_tstep_growth_strategy == TimeStepGrowthStrategy::fixed) {
163 return m_tstep_growth;
164 }
165
166 m_work1.resize(m_size);
167 const double growth = m_tstep_growth;
168
169 switch (m_tstep_growth_strategy) {
170 case TimeStepGrowthStrategy::fixed:
171 return growth;
172 case TimeStepGrowthStrategy::steadyNorm: {
173 double ss_before = ssnorm(x_before, m_work1);
174 double ss_after = ssnorm(x_after, m_work1);
175 return (ss_after < ss_before) ? growth : 1.0;
176 }
177 case TimeStepGrowthStrategy::transientResidual: {
178 double ts_before = tsnorm(x_before, m_work1);
179 double ts_after = tsnorm(x_after, m_work1);
180 return (ts_after < ts_before) ? growth : 1.0;
181 }
182 case TimeStepGrowthStrategy::residualRatio: {
183 double ts_before = tsnorm(x_before, m_work1);
184 double ts_after = tsnorm(x_after, m_work1);
185 if (!(ts_after > 0.0) || !(ts_before > ts_after)) {
186 return 1.0;
187 }
188 const double exponent = 0.2;
189 double ratio = ts_before / ts_after;
190 double factor = std::pow(ratio, exponent);
191 return std::min(growth, std::max(1.0, factor));
192 }
193 case TimeStepGrowthStrategy::newtonIterations: {
194 const int max_iters_for_growth = 3;
195 return (newton().lastIterations() <= max_iters_for_growth) ? growth : 1.0;
196 }
197 }
198 throw CanteraError("SteadyStateSystem::calculateTimeStepGrowthFactor",
199 "Unknown time step growth strategy '{}'.", timeStepGrowthStrategy());
200}
201
202double SteadyStateSystem::timeStep(int nsteps, double dt, span<double> x,
203 span<double> r, int loglevel)
204{
205 // set the Jacobian age parameter to the transient value
207
208 int n = 0;
209 int successiveFailures = 0;
210
211 // Only output this if nothing else under this function call will be output
212 if (loglevel == 1) {
213 writelog("\n============================");
214 writelog("\n{:<5s} {:<11s} {:<7s}\n", "step", "dt (s)", "log(ss)");
215 writelog("============================");
216 }
217 while (n < nsteps) {
218 if (loglevel == 1) { // At level 1, output concise information
219 double ss = ssnorm(x, r);
220 writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss));
221 } else if (loglevel > 1) {
222 double ss = ssnorm(x, r);
223 writelog("\nTimestep ({}) dt= {:<11.4e} log(ss)= {:<7.4f}", n, dt, log10(ss));
224 }
225
226 // set up for time stepping with stepsize dt
227 initTimeInteg(dt, x);
228
229 int j0 = m_jac->nEvals(); // Store the current number of Jacobian evaluations
230
231 // solve the transient problem
232 int status = newton().solve(x, r, *this, loglevel);
233
234 // successful time step. Copy the new solution in r to
235 // the current solution in x.
236 if (status >= 0) {
237 if (loglevel > 1) {
238 writelog("\nTimestep ({}) succeeded", n);
239 }
240 successiveFailures = 0;
241 m_nsteps++;
242 n += 1;
243 if (m_jac->nEvals() == j0) {
245 }
246 copy(r.begin(), r.end(), x.begin());
249 }
250 dt = std::min(dt, m_tmax);
251 if (m_nsteps >= m_nsteps_max) {
252 throw TimeStepError("SteadyStateSystem::timeStep",
253 "Took maximum number of timesteps allowed ({}) without "
254 "reaching steady-state solution.", m_nsteps_max);
255 }
256 } else {
257 // No solution could be found with this time step.
258 // Decrease the stepsize and try again.
259 successiveFailures++;
260 if (loglevel == 1) {
261 writelog("\nTimestep failed");
262 } else if (loglevel > 1) {
263 writelog("\nTimestep ({}) failed", n);
264 }
265 if (successiveFailures > 2) {
266 debuglog("--> Resetting negative species concentrations", loglevel);
268 successiveFailures = 0;
269 } else {
270 debuglog("--> Reducing timestep", loglevel);
271 dt *= m_tfactor;
272 if (dt < m_tmin) {
273 throw TimeStepError("SteadyStateSystem::timeStep",
274 "Time integration failed. Minimum timestep ({}) reached.", m_tmin);
275 }
276 }
277 }
278 }
279
280 // Write the final step to the log
281 if (loglevel == 1) {
282 double ss = ssnorm(x, r);
283 writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss));
284 writelog("\n============================");
285 } else if (loglevel > 1) {
286 double ss = ssnorm(x, r);
287 writelog("\nTimestep ({}) dt= {:<11.4e} log10(ss)= {:<7.4f}\n", n, dt, log10(ss));
288 }
289
290 // return the value of the last stepsize, which may be smaller
291 // than the initial stepsize
292 return dt;
293}
294
295double SteadyStateSystem::ssnorm(span<const double> x, span<double> r)
296{
297 eval(x, r, 0.0, 0);
298 double ss = 0.0;
299 for (size_t i = 0; i < m_size; i++) {
300 ss = std::max(fabs(r[i]),ss);
301 }
302 return ss;
303}
304
305double SteadyStateSystem::tsnorm(span<const double> x, span<double> r)
306{
307 eval(x, r, m_rdt, 0);
308 double ts = 0.0;
309 for (size_t i = 0; i < m_size; i++) {
310 ts = std::max(fabs(r[i]), ts);
311 }
312 return ts;
313}
314
315void SteadyStateSystem::setTimeStep(double stepsize, span<const int> tsteps)
316{
317 m_tstep = stepsize;
318 m_steps.assign(tsteps.begin(), tsteps.end());
319}
320
322{
323 m_state->resize(size());
324 m_xnew.resize(size());
325 m_newt->resize(size());
326 m_mask.resize(size());
327 if (!m_jac) {
328 throw CanteraError("SteadyStateSystem::resize",
329 "Jacobian evaluator must be instantiated before calling resize()");
330 }
331 m_jac->initialize(size());
332 m_jac->setBandwidth(bandwidth());
333 m_jac->clearStats();
334 m_jac_ok = false;
335}
336
337void SteadyStateSystem::setLinearSolver(shared_ptr<SystemJacobian> solver)
338{
339 m_jac = solver;
340 m_jac->initialize(size());
341 m_jac->setBandwidth(bandwidth());
342 m_jac->clearStats();
343 m_jac_ok = false;
344}
345
346void SteadyStateSystem::evalSSJacobian(span<const double> x)
347{
348 double rdt_save = m_rdt;
349 m_jac_ok = false;
351 evalJacobian(x);
352 m_rdt = rdt_save;
353}
354
355void SteadyStateSystem::initTimeInteg(double dt, span<const double> x)
356{
357 double rdt_old = m_rdt;
358 m_rdt = 1.0/dt;
359
360 // if the stepsize has changed, then update the transient part of the Jacobian
361 if (fabs(rdt_old - m_rdt) > Tiny) {
362 m_jac->updateTransient(m_rdt, m_mask);
363 }
364}
365
367{
368 if (m_rdt == 0) {
369 return;
370 }
371
372 m_rdt = 0.0;
373 if (m_jac_ok) {
374 m_jac->updateTransient(m_rdt, m_mask);
375 }
376}
377
378void SteadyStateSystem::setJacAge(int ss_age, int ts_age)
379{
380 m_ss_jac_age = ss_age;
381 if (ts_age > 0) {
382 m_ts_jac_age = ts_age;
383 } else {
385 }
386}
387
389{
390 return *m_newt;
391}
392
393}
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.
virtual double eval(double t) const
Evaluate the function.
Definition Func1.cpp:28
Newton iterator for multi-domain, one-dimensional problems.
Definition MultiNewton.h:24
void setOptions(int maxJacAge=5)
Set options.
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_nsteps
Number of time steps taken in the current call to solve()
double calculateTimeStepGrowthFactor(span< const double > x_before, span< const double > x_after)
Determine the timestep growth factor after a successful step.
size_t m_size
Solution vector size
int m_nsteps_max
Maximum number of timesteps allowed per call to solve()
virtual void resize()
Call to set the size of internal data structures after first defining the system or if the problem si...
double timeStep(int nsteps, double dt, span< double > x, span< double > r, int loglevel)
Take time steps using Backward Euler.
virtual void resetBadValues(span< double > x)
Reset values such as negative species concentrations.
vector< double > m_xnew
Work array used to hold the residual or the new solution.
unique_ptr< MultiNewton > m_newt
Newton iterator.
void getState(span< double > x) const
Get the converged steady-state solution after calling solve().
size_t size() const
Total solution vector length;.
virtual void initTimeInteg(double dt, span< const double > x)
Prepare for time stepping beginning with solution x and timestep dt.
double tsnorm(span< const double > x, span< double > r)
Transient max norm (infinity norm) of the residual evaluated using solution x and the current timeste...
void evalSSJacobian(span< const double > x)
Evaluate the steady-state Jacobian, accessible via linearSolver()
size_t bandwidth() const
Jacobian bandwidth.
double m_rdt
Reciprocal of time step.
shared_ptr< SystemJacobian > m_jac
Jacobian evaluator.
void setInitialGuess(span< const double > x)
Set the initial guess. Should be called before solve().
shared_ptr< vector< double > > m_state
Solution vector.
void setTimeStepGrowthStrategy(const string &strategy)
Set the strategy used to grow the timestep after a successful step that reuses the current Jacobian.
virtual void eval(span< const double > x, span< double > r, double rdt=-1.0, int count=1)=0
Evaluate the residual function.
vector< int > m_mask
Transient mask.
void solve(int loglevel=0)
Solve the steady-state problem, taking internal timesteps as necessary until the Newton solver can co...
double ssnorm(span< const double > x, span< double > r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x.
string timeStepGrowthStrategy() const
Get the configured timestep growth strategy.
void setTimeStep(double stepsize, span< const int > tsteps)
Set the number of time steps to try when the steady Newton solver is unsuccessful.
vector< int > m_steps
Array of number of steps to take after each unsuccessful steady-state solve before re-attempting the ...
TimeStepGrowthStrategy m_tstep_growth_strategy
Selected strategy for successful-step growth.
double m_tfactor
Factor time step is multiplied by if time stepping fails ( < 1 )
bool m_jac_ok
If true, Jacobian is current.
double m_tstep
Initial timestep.
int m_ts_jac_age
Maximum age of the Jacobian in time-stepping mode.
void setJacAge(int ss_age, int ts_age=-1)
Set the maximum number of steps that can be taken using the same Jacobian before it must be re-evalua...
virtual void writeDebugInfo(const string &header_suffix, const string &message, int loglevel, int attempt_counter)
Write solver debugging based on the specified log level.
double m_tmin
Minimum timestep size.
double m_tmax
Maximum timestep size.
int m_ss_jac_age
Maximum age of the Jacobian in steady-state mode.
virtual void evalJacobian(span< const double > x0)=0
Evaluates the Jacobian at x0 using finite differences.
virtual void setSteadyMode()
Prepare to solve the steady-state problem.
virtual void clearDebugFile()
Delete debug output file that may be created by writeDebugInfo() when solving with high loglevel.
int m_attempt_counter
Counter used to manage the number of states stored in the debug log file generated by writeDebugInfo(...
void setLinearSolver(shared_ptr< SystemJacobian > solver)
Set the linear solver used to hold the Jacobian matrix and solve linear systems as part of each Newto...
int m_max_history
Constant that determines the maximum number of states stored in the debug log file generated by write...
Func1 * m_time_step_callback
User-supplied function called after each successful timestep.
vector< double > m_xlast_ts
State vector after the last successful set of time steps.
vector< double > m_work1
Work arrays used during Jacobian evaluation.
MultiNewton & newton()
Return a reference to the Newton iterator.
double m_tstep_growth
Growth factor for successful steps that reuse the Jacobian.
Error thrown when time stepping cannot proceed and the steady-state solver should be given a chance t...
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 Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:176