Skip to content

Commit bbd68d4

Browse files
committed
address broken steady state tests, add options for time step regridding controls, add tests for new options
1 parent 2f668f2 commit bbd68d4

File tree

5 files changed

+145
-7
lines changed

5 files changed

+145
-7
lines changed

include/cantera/numerics/SteadyStateSystem.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,54 @@ class SteadyStateSystem
209209
m_tfactor = tfactor;
210210
}
211211

212+
//! Set the default factor by which the time step is increased after a
213+
//! successful step when the Jacobian is re-used.
214+
//!
215+
//! The default value is 1.5, matching historical behavior.
216+
//! @param tfactor factor time step is multiplied by after a successful step
217+
void setTimeStepGrowthFactor(double tfactor) {
218+
if (tfactor < 1.0) {
219+
throw CanteraError("SteadyStateSystem::setTimeStepGrowthFactor",
220+
"Time step growth factor must be >= 1.0. Got {}.", tfactor);
221+
}
222+
m_tstep_growth = tfactor;
223+
}
224+
225+
//! Get the successful-step time step growth factor.
226+
double timeStepGrowthFactor() const {
227+
return m_tstep_growth;
228+
}
229+
230+
//! Enable or disable adaptive time-step growth heuristics.
231+
//!
232+
//! If disabled, steady solvers use the fixed growth factor from
233+
//! setTimeStepGrowthFactor(). Disabled by default.
234+
void setAdaptiveTimeStepGrowth(bool enabled) {
235+
m_adaptive_tstep_growth = enabled;
236+
}
237+
238+
//! Get whether adaptive time-step growth heuristics are enabled.
239+
bool adaptiveTimeStepGrowth() const {
240+
return m_adaptive_tstep_growth;
241+
}
242+
243+
//! Select which adaptive time-step growth heuristic is used.
244+
//!
245+
//! Supported values are 1-4.
246+
void setTimeStepGrowthHeuristic(int heuristic) {
247+
if (heuristic < 1 || heuristic > 4) {
248+
throw CanteraError("SteadyStateSystem::setTimeStepGrowthHeuristic",
249+
"Time step growth heuristic must be in the range [1, 4]. Got {}.",
250+
heuristic);
251+
}
252+
m_tstep_growth_heuristic = heuristic;
253+
}
254+
255+
//! Get the selected adaptive time-step growth heuristic.
256+
int timeStepGrowthHeuristic() const {
257+
return m_tstep_growth_heuristic;
258+
}
259+
212260
//! Set the maximum number of timeteps allowed before successful steady-state solve
213261
void setMaxTimeStepCount(int nmax) {
214262
m_nsteps_max = nmax;
@@ -286,6 +334,17 @@ class SteadyStateSystem
286334
//! Factor time step is multiplied by if time stepping fails ( < 1 )
287335
double m_tfactor = 0.5;
288336

337+
//! Factor time step is multiplied by after successful steps when no new Jacobian
338+
//! evaluation is needed.
339+
double m_tstep_growth = 1.5;
340+
341+
//! If `true`, use residual- or iteration-based heuristics to gate time-step
342+
//! growth; otherwise use the fixed growth factor.
343+
bool m_adaptive_tstep_growth = false;
344+
345+
//! Selected adaptive growth heuristic (1-4). See timeStepIncreaseFactor().
346+
int m_tstep_growth_heuristic = 2;
347+
289348
shared_ptr<vector<double>> m_state; //!< Solution vector
290349

291350
//! Work array used to hold the residual or the new solution

interfaces/cython/cantera/_onedim.pxd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ cdef extern from "cantera/oneD/Sim1D.h":
149149
void getResidual(double, double*) except +translate_exception
150150
void setJacAge(int, int)
151151
void setTimeStepFactor(double)
152+
void setTimeStepGrowthFactor(double) except +translate_exception
153+
double timeStepGrowthFactor()
154+
void setAdaptiveTimeStepGrowth(cbool)
155+
cbool adaptiveTimeStepGrowth()
156+
void setTimeStepGrowthHeuristic(int) except +translate_exception
157+
int timeStepGrowthHeuristic()
152158
void setMinTimeStep(double)
153159
void setMaxTimeStep(double)
154160
void setTimeStepRegridMax(int)

interfaces/cython/cantera/_onedim.pyx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,11 +1420,54 @@ cdef class Sim1D:
14201420

14211421
def set_time_step_factor(self, tfactor):
14221422
"""
1423-
Set the factor by which the time step will be increased after a
1424-
successful step, or decreased after an unsuccessful one.
1423+
Set the factor by which the time step will be decreased after an
1424+
unsuccessful step.
14251425
"""
14261426
self.sim.setTimeStepFactor(tfactor)
14271427

1428+
def set_time_step_growth_factor(self, tfactor):
1429+
"""
1430+
Set the factor by which the time step will be increased after a
1431+
successful step when the Jacobian is reused.
1432+
1433+
The default value is 1.5.
1434+
"""
1435+
self.sim.setTimeStepGrowthFactor(tfactor)
1436+
1437+
def time_step_growth_factor(self):
1438+
"""
1439+
Get the factor used to increase time step size after successful steps.
1440+
"""
1441+
return self.sim.timeStepGrowthFactor()
1442+
1443+
def set_adaptive_time_step_growth(self, enabled=True):
1444+
"""
1445+
Enable adaptive time-step growth heuristics.
1446+
1447+
Disabled by default to preserve legacy behavior.
1448+
"""
1449+
self.sim.setAdaptiveTimeStepGrowth(enabled)
1450+
1451+
def adaptive_time_step_growth(self):
1452+
"""
1453+
Get whether adaptive time-step growth heuristics are enabled.
1454+
"""
1455+
return self.sim.adaptiveTimeStepGrowth()
1456+
1457+
def set_time_step_growth_heuristic(self, heuristic):
1458+
"""
1459+
Select the adaptive time-step growth heuristic.
1460+
1461+
Supported values are integers from 1 to 4.
1462+
"""
1463+
self.sim.setTimeStepGrowthHeuristic(heuristic)
1464+
1465+
def time_step_growth_heuristic(self):
1466+
"""
1467+
Get the selected adaptive time-step growth heuristic.
1468+
"""
1469+
return self.sim.timeStepGrowthHeuristic()
1470+
14281471
def set_time_step_regrid(self, max_tries=10):
14291472
"""
14301473
Set the maximum number of regrid attempts after a time step failure.

src/numerics/SteadyStateSystem.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,13 @@ void SteadyStateSystem::solve(int loglevel)
110110

111111
double SteadyStateSystem::timeStepIncreaseFactor(double* x_before, double* x_after)
112112
{
113-
int heuristic = 2; // Can be 1-4
113+
if (!m_adaptive_tstep_growth) {
114+
return m_tstep_growth;
115+
}
116+
117+
int heuristic = m_tstep_growth_heuristic; // Can be 1-4
114118
m_work1.resize(m_size);
115-
const double grow_factor = 1.25;
119+
const double grow_factor = m_tstep_growth;
116120

117121
if (heuristic == 1) {
118122
// Steady-state residual gate. If the steady-state residual decreased, allow
@@ -134,7 +138,7 @@ double SteadyStateSystem::timeStepIncreaseFactor(double* x_before, double* x_aft
134138
if (!(ts_after > 0.0) || !(ts_before > ts_after)) {
135139
return 1.0;
136140
}
137-
const double max_growth = 1.5;
141+
const double max_growth = m_tstep_growth;
138142
const double exponent = 0.2;
139143
double ratio = ts_before / ts_after;
140144
double factor = std::pow(ratio, exponent);
@@ -145,7 +149,7 @@ double SteadyStateSystem::timeStepIncreaseFactor(double* x_before, double* x_aft
145149
const int max_iters_for_growth = 3;
146150
return (newton().lastIterations() <= max_iters_for_growth) ? grow_factor : 1.0;
147151
}
148-
return 1.0;
152+
return m_tstep_growth;
149153
}
150154

151155
double SteadyStateSystem::timeStep(int nsteps, double dt, double* x, double* r, int loglevel)
@@ -314,7 +318,7 @@ void SteadyStateSystem::initTimeInteg(double dt, double* x)
314318
m_rdt = 1.0/dt;
315319

316320
// if the stepsize has changed, then update the transient part of the Jacobian
317-
if (fabs(rdt_old - m_rdt) > Tiny && m_jac_ok) {
321+
if (fabs(rdt_old - m_rdt) > Tiny) {
318322
m_jac->updateTransient(m_rdt, m_mask.data());
319323
}
320324
}

test/python/test_onedim.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,32 @@ def test_tolerances(self):
139139
assert rtol_ss == set((5e-3, 3e-4, 7e-7))
140140
assert rtol_ts == set((6e-3, 4e-4, 2e-7))
141141

142+
def test_time_step_growth_controls(self):
143+
gas = ct.Solution("h2o2.yaml")
144+
left = ct.Inlet1D(gas)
145+
flame = ct.FreeFlow(gas)
146+
right = ct.Outlet1D(gas)
147+
sim = ct.Sim1D((left, flame, right))
148+
149+
assert sim.time_step_growth_factor() == approx(1.5)
150+
assert not sim.adaptive_time_step_growth()
151+
assert sim.time_step_growth_heuristic() == 2
152+
153+
sim.set_time_step_growth_factor(1.2)
154+
assert sim.time_step_growth_factor() == approx(1.2)
155+
156+
sim.set_adaptive_time_step_growth(True)
157+
assert sim.adaptive_time_step_growth()
158+
159+
sim.set_time_step_growth_heuristic(4)
160+
assert sim.time_step_growth_heuristic() == 4
161+
162+
with pytest.raises(ct.CanteraError, match=">= 1.0"):
163+
sim.set_time_step_growth_factor(0.9)
164+
165+
with pytest.raises(ct.CanteraError, match=r"\[1, 4\]"):
166+
sim.set_time_step_growth_heuristic(5)
167+
142168
def test_switch_transport(self):
143169
gas = ct.Solution('h2o2.yaml')
144170
gas.set_equivalence_ratio(0.9, 'H2', 'O2:0.21, N2:0.79')

0 commit comments

Comments
 (0)