Skip to content

Commit 2f668f2

Browse files
committed
first commit for feature
1 parent 630ba99 commit 2f668f2

File tree

9 files changed

+138
-15
lines changed

9 files changed

+138
-15
lines changed

include/cantera/numerics/SteadyStateSystem.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ class SteadyStateSystem
7272
//! x. On return, array r contains the steady-state residual values.
7373
double ssnorm(double* x, double* r);
7474

75+
//! Transient max norm (infinity norm) of the residual evaluated using solution
76+
//! x and the current timestep (rdt). On return, array r contains the
77+
//! transient residual values.
78+
double tsnorm(double* x, double* r);
79+
7580
//! Total solution vector length;
7681
size_t size() const {
7782
return m_size;
@@ -266,6 +271,9 @@ class SteadyStateSystem
266271
//! @param[out] rsd Storage for the residual, length size()
267272
void evalSSJacobian(double* x, double* rsd);
268273

274+
//! Determine the timestep growth factor after a successful step.
275+
double timeStepIncreaseFactor(double* x_before, double* x_after);
276+
269277
//! Array of number of steps to take after each unsuccessful steady-state solve
270278
//! before re-attempting the steady-state solution. For subsequent time stepping
271279
//! calls, the final value is reused. See setTimeStep().
@@ -325,6 +333,7 @@ class SteadyStateSystem
325333
double m_jacobianRelPerturb = 1e-5;
326334
//! Absolute perturbation of each component in finite difference Jacobian
327335
double m_jacobianAbsPerturb = 1e-10;
336+
328337
};
329338

330339
}

include/cantera/oneD/MultiNewton.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class MultiNewton
3535
return m_n;
3636
}
3737

38+
//! Number of Newton iterations taken in the most recent solve() call.
39+
int lastIterations() const {
40+
return m_lastIterations;
41+
}
42+
3843
//! Compute the undamped Newton step. The residual function is evaluated
3944
//! at `x`, but the Jacobian is not recomputed.
4045
//! @since Starting in %Cantera 3.2, the Jacobian is accessed via the OneDim object.
@@ -164,13 +169,16 @@ class MultiNewton
164169
double m_dampFactor = sqrt(2.0);
165170

166171
//! Maximum number of damping iterations
167-
size_t m_maxDampIter = 7;
172+
size_t m_maxDampIter = 14;
168173

169174
//! number of variables
170175
size_t m_n;
171176

172177
//! Elapsed CPU time spent computing the Jacobian.
173178
double m_elapsed = 0.0;
179+
180+
//! Number of Newton iterations taken in the last solve() call.
181+
int m_lastIterations = 0;
174182
};
175183
}
176184

include/cantera/oneD/Sim1D.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,17 @@ class Sim1D : public OneDim
337337
m_steady_callback = callback;
338338
}
339339

340+
//! Set the maximum number of regrid attempts after a timestep failure.
341+
//! Set to 0 to disable regrid-on-timestep-failure retries.
342+
void setTimeStepRegridMax(int nmax) {
343+
m_ts_regrid_max = nmax;
344+
}
345+
346+
//! Get the maximum number of regrid attempts after a timestep failure.
347+
int timeStepRegridMax() const {
348+
return m_ts_regrid_max;
349+
}
350+
340351
protected:
341352
//! the solution vector after the last successful steady-state solve (stored
342353
//! before grid refinement)
@@ -349,6 +360,9 @@ class Sim1D : public OneDim
349360
//! User-supplied function called after a successful steady-state solve.
350361
Func1* m_steady_callback;
351362

363+
//! 0 disables regrid-on-timestep-failure retries
364+
int m_ts_regrid_max = 10;
365+
352366
private:
353367
//! Calls method _finalize in each domain.
354368
void finalize();

interfaces/cython/cantera/_onedim.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ cdef extern from "cantera/oneD/Sim1D.h":
151151
void setTimeStepFactor(double)
152152
void setMinTimeStep(double)
153153
void setMaxTimeStep(double)
154+
void setTimeStepRegridMax(int)
155+
int timeStepRegridMax()
154156
void setMaxGridPoints(int, size_t) except +translate_exception
155157
size_t maxGridPoints(size_t) except +translate_exception
156158
void setGridMin(int, double) except +translate_exception

interfaces/cython/cantera/_onedim.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ class Sim1D:
297297
def max_time_step_count(self) -> int: ...
298298
@max_time_step_count.setter
299299
def max_time_step_count(self, nmax: int) -> None: ...
300+
def set_time_step_regrid(self, max_tries: int = 10) -> None: ...
300301
def set_jacobian_perturbation(
301302
self, relative: float, absolute: float, threshold: float
302303
) -> None: ...

interfaces/cython/cantera/_onedim.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,14 @@ cdef class Sim1D:
14251425
"""
14261426
self.sim.setTimeStepFactor(tfactor)
14271427

1428+
def set_time_step_regrid(self, max_tries=10):
1429+
"""
1430+
Set the maximum number of regrid attempts after a time step failure.
1431+
1432+
Setting ``max_tries`` to zero disables regrid-on-timestep-failure retries.
1433+
"""
1434+
self.sim.setTimeStepRegridMax(max_tries)
1435+
14281436
def set_min_time_step(self, tsmin):
14291437
""" Set the minimum time step. """
14301438
self.sim.setMinTimeStep(tsmin)

src/numerics/SteadyStateSystem.cpp

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,46 @@ void SteadyStateSystem::solve(int loglevel)
108108
}
109109
}
110110

111+
double SteadyStateSystem::timeStepIncreaseFactor(double* x_before, double* x_after)
112+
{
113+
int heuristic = 2; // Can be 1-4
114+
m_work1.resize(m_size);
115+
const double grow_factor = 1.25;
116+
117+
if (heuristic == 1) {
118+
// Steady-state residual gate. If the steady-state residual decreased, allow
119+
// increase in timestep.
120+
double ss_before = ssnorm(x_before, m_work1.data());
121+
double ss_after = ssnorm(x_after, m_work1.data());
122+
return (ss_after < ss_before) ? grow_factor : 1.0;
123+
} else if (heuristic == 2) {
124+
// Transient residual gate. If the transient residual decreased, allow
125+
// increase in timestep.
126+
double ts_before = tsnorm(x_before, m_work1.data());
127+
double ts_after = tsnorm(x_after, m_work1.data());
128+
return (ts_after < ts_before) ? grow_factor : 1.0;
129+
} else if (heuristic == 3) {
130+
// Residual-ratio scaling gate (transient residual). If the transient residual
131+
// decreased significantly, allow larger increase in timestep.
132+
double ts_before = tsnorm(x_before, m_work1.data());
133+
double ts_after = tsnorm(x_after, m_work1.data());
134+
if (!(ts_after > 0.0) || !(ts_before > ts_after)) {
135+
return 1.0;
136+
}
137+
const double max_growth = 1.5;
138+
const double exponent = 0.2;
139+
double ratio = ts_before / ts_after;
140+
double factor = std::pow(ratio, exponent);
141+
return std::min(max_growth, std::max(1.0, factor));
142+
} else if (heuristic == 4) {
143+
// Newton-iteration gate. If the last Newton solve required only a few
144+
// iterations, allow increase in timestep.
145+
const int max_iters_for_growth = 3;
146+
return (newton().lastIterations() <= max_iters_for_growth) ? grow_factor : 1.0;
147+
}
148+
return 1.0;
149+
}
150+
111151
double SteadyStateSystem::timeStep(int nsteps, double dt, double* x, double* r, int loglevel)
112152
{
113153
// set the Jacobian age parameter to the transient value
@@ -123,12 +163,14 @@ double SteadyStateSystem::timeStep(int nsteps, double dt, double* x, double* r,
123163
writelog("============================");
124164
}
125165
while (n < nsteps) {
126-
if (loglevel == 1) { // At level 1, output concise information
127-
double ss = ssnorm(x, r);
128-
writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss));
129-
} else if (loglevel > 1) {
130-
double ss = ssnorm(x, r);
131-
writelog("\nTimestep ({}) dt= {:<11.4e} log(ss)= {:<7.4f}", n, dt, log10(ss));
166+
if (loglevel >= 1) {
167+
double ss_before = ssnorm(x, r);
168+
if (loglevel == 1) { // At level 1, output concise information
169+
writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss_before));
170+
} else {
171+
writelog("\nTimestep ({}) dt= {:<11.4e} log(ss)= {:<7.4f}", n, dt,
172+
log10(ss_before));
173+
}
132174
}
133175

134176
// set up for time stepping with stepsize dt
@@ -148,17 +190,18 @@ double SteadyStateSystem::timeStep(int nsteps, double dt, double* x, double* r,
148190
successiveFailures = 0;
149191
m_nsteps++;
150192
n += 1;
151-
copy(r, r + m_size, x);
152-
// No Jacobian evaluations were performed, so a larger timestep can be used
193+
double grow_factor = 1.0;
153194
if (m_jac->nEvals() == j0) {
154-
dt *= 1.5;
195+
grow_factor = timeStepIncreaseFactor(x, r);
155196
}
197+
copy(r, r + m_size, x);
198+
dt *= grow_factor;
156199
if (m_time_step_callback) {
157200
m_time_step_callback->eval(dt);
158201
}
159202
dt = std::min(dt, m_tmax);
160203
if (m_nsteps >= m_nsteps_max) {
161-
throw CanteraError("OneDim::timeStep",
204+
throw CanteraError("SteadyStateSystem::timeStep",
162205
"Took maximum number of timesteps allowed ({}) without "
163206
"reaching steady-state solution.", m_nsteps_max);
164207
}
@@ -181,7 +224,7 @@ double SteadyStateSystem::timeStep(int nsteps, double dt, double* x, double* r,
181224
if (dt < m_tmin) {
182225
string err_msg = fmt::format(
183226
"Time integration failed. Minimum timestep ({}) reached.", m_tmin);
184-
throw CanteraError("OneDim::timeStep", err_msg);
227+
throw CanteraError("SteadyStateSystem::timeStep", err_msg);
185228
}
186229
}
187230
}
@@ -212,6 +255,16 @@ double SteadyStateSystem::ssnorm(double* x, double* r)
212255
return ss;
213256
}
214257

258+
double SteadyStateSystem::tsnorm(double* x, double* r)
259+
{
260+
eval(x, r, m_rdt, 0);
261+
double ts = 0.0;
262+
for (size_t i = 0; i < m_size; i++) {
263+
ts = std::max(fabs(r[i]), ts);
264+
}
265+
return ts;
266+
}
267+
215268
void SteadyStateSystem::setTimeStep(double stepsize, size_t n, const int* tsteps)
216269
{
217270
m_tstep = stepsize;
@@ -261,7 +314,7 @@ void SteadyStateSystem::initTimeInteg(double dt, double* x)
261314
m_rdt = 1.0/dt;
262315

263316
// if the stepsize has changed, then update the transient part of the Jacobian
264-
if (fabs(rdt_old - m_rdt) > Tiny) {
317+
if (fabs(rdt_old - m_rdt) > Tiny && m_jac_ok) {
265318
m_jac->updateTransient(m_rdt, m_mask.data());
266319
}
267320
}
@@ -273,7 +326,9 @@ void SteadyStateSystem::setSteadyMode()
273326
}
274327

275328
m_rdt = 0.0;
276-
m_jac->updateTransient(m_rdt, m_mask.data());
329+
if (m_jac_ok) {
330+
m_jac->updateTransient(m_rdt, m_mask.data());
331+
}
277332
}
278333

279334
void SteadyStateSystem::setJacAge(int ss_age, int ts_age)

src/oneD/MultiNewton.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,13 @@ int MultiNewton::solve(double* x0, double* x1, SteadyStateSystem& r, int logleve
206206
double s1=1.e30;
207207

208208
copy(x0, x0 + m_n, &m_x[0]);
209+
m_lastIterations = 0;
209210

210211
double rdt = r.rdt();
211212
int nJacReeval = 0;
212213
auto jac = r.linearSolver();
213214
while (true) {
215+
m_lastIterations++;
214216
// Check whether the Jacobian should be re-evaluated.
215217
if (jac->age() > m_maxAge) {
216218
if (loglevel > 1) {

src/oneD/Sim1D.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,32 @@ void Sim1D::solve(int loglevel, bool refine_grid)
344344
clearDebugFile();
345345
}
346346

347+
int retries = 0;
347348
while (new_points > 0) {
348-
SteadyStateSystem::solve(loglevel);
349+
if (refine_grid == true) {
350+
try {
351+
SteadyStateSystem::solve(loglevel);
352+
} catch (CanteraError& err) {
353+
if (err.getMethod() == "SteadyStateSystem::timeStep" && retries < m_ts_regrid_max) {
354+
writelog("\nTime stepping failed; attempting to refine the grid and retry "
355+
"({}/{})...\n", retries+1, m_ts_regrid_max);
356+
new_points = refine(loglevel);
357+
if (new_points > 0) {
358+
retries++;
359+
continue;
360+
}
361+
if (loglevel > 0) {
362+
if (new_points == 0) {
363+
writelog("Regrid retry aborted: no new points added.\n");
364+
}
365+
}
366+
}
367+
throw;
368+
}
369+
} else {
370+
SteadyStateSystem::solve(loglevel);
371+
}
372+
349373
if (loglevel > 0) {
350374
writelog("\nNewton steady-state solve succeeded.\n\n");
351375
writelog("Problem solved on [");

0 commit comments

Comments
 (0)