2. Time integration
Can a decaying quantity oscillate numerically?
Section titled “Can a decaying quantity oscillate numerically?”A positive quantity obeying , with , never changes sign. Yet a numerical approximation can alternate between positive and negative values. This lesson explains why before running the same decay model with an adaptive method.
The equation describes a normalized temperature difference in a simple cooling model and appears in first-order circuit dynamics. Its physical derivation is in the ODE lesson. Here we need only and the exact solution .
Replace a derivative by a step
Section titled “Replace a derivative by a step”Approximate the derivative at by a forward difference:
This is forward Euler. Its amplification factor is , where . The exact factor is . Taylor expansion gives : the difference per step starts at second order, but accumulated error over a fixed interval is generally first order.
For positive , decay in magnitude requires , or . Preserving nonnegative values further requires . Stability and positivity are different requirements.
| Forward Euler factor | Predicted behavior | |
|---|---|---|
| Positive decay, faster than exact decay | ||
| Alternating signs with decaying magnitude | ||
| Persistent sign alternation | ||
| Growing oscillation |
These entries follow from the recurrence, not from an Eqiora run. Starting from 1 with , the first three steps are , , and . The differential equation remains a decay equation throughout this failure.
Move the slope to the new time
Section titled “Move the slope to the new time”Backward Euler instead uses :
Its factor lies between zero and one for every positive . A very large step is therefore stable for this problem, but need not be accurate. At , one step gives , compared with .
For and , taking equal steps gives the following formulas, which you can evaluate without implementing a solver:
Both tend to . Their errors approach zero from opposite sides. Expand the logarithms to see the leading error proportional to .
Express the model once
Section titled “Express the model once”// A minimal implicit ODE: x decays at the rate supplied by the user.model decay() { // `x` is dimensionless and starts at 1. `rate` has inverse-time units. state x: 1; initial { x = 1; } parameter rate: 1 / s = 1;
// Eqiora writes the evolution law as a residual equal to zero. relation flow { derivative(x) + rate * x = 0; }}There is no time-step size in this source. The rate and initial value describe the mathematical problem; the method describes how to approximate it.
Run the adaptive method
Section titled “Run the adaptive method”Use the files and environment from Get started:
uv run python run.py"""Run the decay model shared by Get started and the ODE lesson."""
from __future__ import annotations
import argparsefrom pathlib import Path
import eqiora
OUTPUT_TIMES_S = (0.25, 0.5, 1.0)
def solve(source_path: Path) -> tuple[tuple[float, float], ...]: """Return the requested (time in seconds, dimensionless value) samples."""
model = eqiora.compile(path=source_path) field = model.field(model.field_ids[0]) plan = eqiora.resolve( model, temporal=eqiora.time.Tsitouras45( initial_step_s=0.01, relative_tolerance=1.0e-9, absolute_tolerances={(field, 0, 0, False): 1.0e-11}, ), ) result = eqiora.run( plan, state=eqiora.State.initial(plan), until_s=OUTPUT_TIMES_S[-1], output_times_s=OUTPUT_TIMES_S, ) series = result.series(field) times = series.time.numpy() values = series.values.numpy() return tuple( (float(time_s), float(value)) for time_s, value in zip(times, values, strict=True) )
def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("model", nargs="?", type=Path, default=Path("decay.eqi")) args = parser.parse_args() for time_s, value in solve(args.model): print(f"t={time_s:.2f}, x={value:.10f}")
if __name__ == "__main__": main()This program uses Tsitouras45, an explicit embedded Runge–Kutta method.
The pair supplies two accuracy estimates from related stage evaluations; their
difference guides time-step adaptation. See Tsitouras’s
original paper for the method’s
construction.
initial_step_s starts the adaptive process; it does not fix every subsequent
step. output_times_s requests observations and is also independent of the
internal steps. Requesting more output samples does not make the integration
more accurate.
Compare the three values with . Then hold the model, interval, and observation times fixed and change the requested relative tolerance from to , together with the absolute tolerance from to . Record the actual errors; a local error-control parameter is not an exact bound on every reported global error.
The program wraps the compile, resolve, and run calls in a solve function,
which you can reuse for repeated comparisons; the equation itself stays in
.eqi. Open the script.
Recognize stiffness without confusing it with instability
Section titled “Recognize stiffness without confusing it with instability”If a system contains both and , the fast component may force a small stable explicit step long after it becomes physically small. That mismatch between time scales is a reason to investigate stiff methods. One scalar run at a single rate says little about a coupled stiff system. Note also that the Euler formulas above are worked by hand; the script uses Tsitouras45, which has its own stability limits.
Exercises
Section titled “Exercises”- Compute both Euler endpoint formulas for . Divide successive absolute errors and compare with the predicted factor of two.
- For , derive the forward Euler stability and nonnegativity bounds on .
- Add an output time at . Explain why this is a different experiment from tightening the integration tolerances.
- Repeat the adaptive run with rate 2. State the new exact comparison before running; identify the time scale that changed.
- Why can a stable backward Euler answer still be unsuitable for estimating a short transient’s peak?
Previous: Errors and residuals · Book map · Next: Weak forms and finite elements