Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions include/cantera/numerics/SteadyStateSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#ifndef CT_STEADYSTATESYSTEM_H
#define CT_STEADYSTATESYSTEM_H

#include <cmath>

#include "cantera/base/ct_defs.h"
#include "SystemJacobian.h"

Expand All @@ -15,6 +17,20 @@ namespace Cantera
class Func1;
class MultiNewton;

//! Error thrown when time stepping cannot proceed and the steady-state solver
//! should be given a chance to recover by other means.
class TimeStepError : public CanteraError
{
public:
template <typename... Args>
TimeStepError(const string& func, const string& msg, const Args&... args) :
CanteraError(func, msg, args...) {}

string getClass() const override {
return "TimeStepError";
}
};

//! Base class for representing a system of differential-algebraic equations and solving
//! for its steady-state response.
//!
Expand Down Expand Up @@ -73,6 +89,11 @@ class SteadyStateSystem
//! x. On return, array r contains the steady-state residual values.
double ssnorm(span<const double> x, span<double> r);

//! Transient max norm (infinity norm) of the residual evaluated using solution
//! x and the current timestep (rdt). On return, array r contains the
//! transient residual values.
double tsnorm(span<const double> x, span<double> r);

//! Total solution vector length;
size_t size() const {
return m_size;
Expand Down Expand Up @@ -194,6 +215,49 @@ class SteadyStateSystem
m_tfactor = tfactor;
}

//! Set the growth factor used after successful timesteps when the Jacobian is
//! re-used.
//!
//! This factor is used directly by the `fixed-growth` strategy, and as the
//! accepted growth factor or upper bound for the other named strategies.
//!
//! The default value is 1.5, matching historical behavior.
//! @param tfactor Finite growth factor applied to successful timesteps;
//! must be >= 1.0.
void setTimeStepGrowthFactor(double tfactor) {
if (!std::isfinite(tfactor) || tfactor < 1.0) {
throw CanteraError("SteadyStateSystem::setTimeStepGrowthFactor",
"Time step growth factor must be finite and >= 1.0. Got {}.",
tfactor);
}
m_tstep_growth = tfactor;
}
Comment thread
wandadars marked this conversation as resolved.

//! Get the successful-step time step growth factor.
double timeStepGrowthFactor() const {
return m_tstep_growth;
}

//! Set the strategy used to grow the timestep after a successful step that
//! reuses the current Jacobian.
//!
//! Available options are:
//! - `fixed-growth`: Always apply timeStepGrowthFactor().
//! - `steady-norm`: Apply timeStepGrowthFactor() only if the steady-state
//! residual norm decreases.
//! - `transient-residual`: Apply timeStepGrowthFactor() only if the transient
//! residual norm decreases.
//! - `residual-ratio`: Scale the growth factor based on transient residual
//! improvement, capped by timeStepGrowthFactor().
//! - `newton-iterations`: Apply timeStepGrowthFactor() only if the most recent
//! Newton solve used at most 3 iterations.
//!
//! The default strategy is `fixed-growth`, which matches historical behavior.
void setTimeStepGrowthStrategy(const string& strategy);

//! Get the configured timestep growth strategy.
string timeStepGrowthStrategy() const;

//! Set the maximum number of timeteps allowed before successful steady-state solve
void setMaxTimeStepCount(int nmax) {
m_nsteps_max = nmax;
Expand Down Expand Up @@ -251,10 +315,27 @@ class SteadyStateSystem
virtual void clearDebugFile() {}

protected:
enum class TimeStepGrowthStrategy {
fixed,
steadyNorm,
transientResidual,
residualRatio,
newtonIterations
};

//! Evaluate the steady-state Jacobian, accessible via linearSolver()
//! @param[in] x Current state vector, length size()
void evalSSJacobian(span<const double> x);

static TimeStepGrowthStrategy parseTimeStepGrowthStrategy(const string& strategy);
static string timeStepGrowthStrategyName(TimeStepGrowthStrategy strategy);

//! Determine the timestep growth factor after a successful step.
//!
//! Called only when a successful step reuses the current Jacobian.
double calculateTimeStepGrowthFactor(span<const double> x_before,
span<const double> x_after);

//! Array of number of steps to take after each unsuccessful steady-state solve
//! before re-attempting the steady-state solution. For subsequent time stepping
//! calls, the final value is reused. See setTimeStep().
Expand All @@ -267,6 +348,15 @@ class SteadyStateSystem
//! Factor time step is multiplied by if time stepping fails ( < 1 )
double m_tfactor = 0.5;

//! Growth factor for successful steps that reuse the Jacobian.
//!
//! Used directly for `fixed-growth`, and as the base / cap value for the
//! other named growth strategies.
double m_tstep_growth = 1.5;

//! Selected strategy for successful-step growth.
TimeStepGrowthStrategy m_tstep_growth_strategy = TimeStepGrowthStrategy::fixed;

shared_ptr<vector<double>> m_state; //!< Solution vector

//! Work array used to hold the residual or the new solution
Expand Down Expand Up @@ -314,6 +404,7 @@ class SteadyStateSystem
double m_jacobianRelPerturb = 1e-5;
//! Absolute perturbation of each component in finite difference Jacobian
double m_jacobianAbsPerturb = 1e-10;

};

}
Expand Down
8 changes: 8 additions & 0 deletions include/cantera/oneD/MultiNewton.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class MultiNewton
return m_n;
}

//! Number of Newton iterations taken in the most recent solve() call.
int lastIterations() const {
return m_lastIterations;
}

//! Compute the undamped Newton step. The residual function is evaluated
//! at `x`, but the Jacobian is not recomputed.
//! @since Starting in %Cantera 3.2, the Jacobian is accessed via the OneDim object.
Expand Down Expand Up @@ -174,6 +179,9 @@ class MultiNewton

//! Elapsed CPU time spent computing the Jacobian.
double m_elapsed = 0.0;

//! Number of Newton iterations taken in the last solve() call.
int m_lastIterations = 0;
};
}

Expand Down
22 changes: 22 additions & 0 deletions include/cantera/oneD/Sim1D.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,25 @@ class Sim1D : public OneDim
m_steady_callback = callback;
}

//! Set the maximum number of regrid attempts after a timestep failure.
//!
//! This fallback is used during solve(loglevel, refine_grid=true). Set to `0`
//! to disable regrid-on-timestep-failure retries.
//!
//! @param nmax Maximum retry attempts; must be >= 0.
void setTimeStepRegridMax(int nmax) {
if (nmax < 0) {
throw CanteraError("Sim1D::setTimeStepRegridMax",
"Time step regrid retry count must be >= 0. Got {}.", nmax);
}
m_ts_regrid_max = nmax;
}

//! Get the maximum number of regrid attempts after a timestep failure.
int timeStepRegridMax() const {
return m_ts_regrid_max;
}

Comment thread
wandadars marked this conversation as resolved.
protected:
//! the solution vector after the last successful steady-state solve (stored
//! before grid refinement)
Expand All @@ -349,6 +368,9 @@ class Sim1D : public OneDim
//! User-supplied function called after a successful steady-state solve.
Func1* m_steady_callback;

//! 0 disables regrid-on-timestep-failure retries
int m_ts_regrid_max = 3;

Comment on lines +371 to +373
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a lot as a default. Are there actually cases where it helps to regrid this many times in a row?

private:
//! Calls method _finalize in each domain.
void finalize();
Expand Down
6 changes: 6 additions & 0 deletions interfaces/cython/cantera/_onedim.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,14 @@ cdef extern from "cantera/oneD/Sim1D.h":
void getResidual(double, span[double]) except +translate_exception
void setJacAge(int, int)
void setTimeStepFactor(double)
void setTimeStepGrowthFactor(double) except +translate_exception
double timeStepGrowthFactor()
void setTimeStepGrowthStrategy(const string&) except +translate_exception
string timeStepGrowthStrategy()
void setMinTimeStep(double)
void setMaxTimeStep(double)
void setTimeStepRegridMax(int) except +translate_exception
int timeStepRegridMax()
void setMaxGridPoints(int, size_t) except +translate_exception
size_t maxGridPoints(size_t) except +translate_exception
void setGridMin(int, double) except +translate_exception
Expand Down
12 changes: 12 additions & 0 deletions interfaces/cython/cantera/_onedim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,18 @@ class Sim1D:
) -> None: ...
def set_max_jac_age(self, ss_age: int, ts_age: int) -> None: ...
def set_time_step_factor(self, tfactor: float) -> None: ...
@property
def time_step_growth_factor(self) -> float: ...
@time_step_growth_factor.setter
def time_step_growth_factor(self, tfactor: float) -> None: ...
@property
def time_step_growth_strategy(self) -> str: ...
@time_step_growth_strategy.setter
def time_step_growth_strategy(self, strategy: str) -> None: ...
@property
def time_step_regrid(self) -> int: ...
@time_step_regrid.setter
def time_step_regrid(self, max_tries: int) -> None: ...
def set_min_time_step(self, tsmin: float) -> None: ...
def set_max_time_step(self, tsmax: float) -> None: ...
@property
Expand Down
66 changes: 64 additions & 2 deletions interfaces/cython/cantera/_onedim.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1425,11 +1425,73 @@ cdef class Sim1D:

def set_time_step_factor(self, tfactor):
"""
Set the factor by which the time step will be increased after a
successful step, or decreased after an unsuccessful one.
Set the factor by which the time step will be decreased after an
unsuccessful step.

:param tfactor:
Multiplicative reduction factor applied after failed steps.
"""
self.sim.setTimeStepFactor(tfactor)

property time_step_growth_factor:
"""
Get/Set the factor by which the time step will be increased after a
successful step when the Jacobian is reused.

This value is used directly by the ``"fixed-growth"`` strategy, and as
the accepted growth factor or cap value for the other growth
strategies.

:param tfactor:
Finite growth factor >= 1.0. The default value is 1.5.
"""
def __get__(self):
return self.sim.timeStepGrowthFactor()
def __set__(self, tfactor):
self.sim.setTimeStepGrowthFactor(tfactor)

property time_step_growth_strategy:
"""
Get/Set the strategy used to grow the time step after a successful
step that reuses the Jacobian.

Available options are:

``"fixed-growth"``:
Always apply ``time_step_growth_factor``.
``"steady-norm"``:
Apply growth only when the steady-state residual norm decreases.
``"transient-residual"``:
Apply growth only when the transient residual norm decreases.
``"residual-ratio"``:
Scale the growth factor based on transient residual improvement,
capped by ``time_step_growth_factor``.
``"newton-iterations"``:
Apply growth only if the most recent Newton solve used at most
three iterations.
"""
def __get__(self):
return pystr(self.sim.timeStepGrowthStrategy())
def __set__(self, strategy):
self.sim.setTimeStepGrowthStrategy(stringify(strategy))

property time_step_regrid:
"""
Get/Set the maximum number of regrid attempts after a time step
failure.

This fallback is used by :meth:`Sim1D.solve` when ``refine_grid=True``.
Set to zero to disable regrid-on-failure retries. The default value is
3.

:param max_tries:
Maximum retry attempts. Values less than zero are invalid.
"""
def __get__(self):
return self.sim.timeStepRegridMax()
def __set__(self, max_tries):
self.sim.setTimeStepRegridMax(max_tries)

def set_min_time_step(self, tsmin):
""" Set the minimum time step. """
self.sim.setMinTimeStep(tsmin)
Expand Down
Loading
Loading