Worked examples — Adaptive step-size — RK45, error control
This page is the "no surprises" drill for the parent note Adaptive step-size — RK45, error control. Every knob in the step controller can point in different directions — the error can be too big, too small, or exactly on budget; the raw step factor can want to grow, shrink, or blow past the safety clamps; the tolerance itself changes as grows. Below we first lay out every case that can happen, then work an example for each so you never meet a scenario you have not already seen.
Before we touch numbers, one reminder of what each symbol means, in plain words:
The scenario matrix
Every step the controller takes is a combination of two independent questions. Think of it as a grid:
| Cell | Error vs tol ( vs ) | Raw factor | Outcome we must show |
|---|---|---|---|
| C1 | (spare room) | between and | accept, grow moderately |
| C2 | (way over) | below | reject, shrink but clamp at |
| C3 | (tiny error) | above | accept, but clamp growth at |
| C4 | (mildly over) | between and | reject, shrink moderately, stay at |
| C5 | exactly (boundary) | accept, barely changes | |
| C6 | (degenerate: perfect step) | division danger → must clamp | |
| C7 | tolerance is relative-dominated ( large, incl. negative ) | any | show growing |
| C8 | a system of ODEs (vector ) | any | error is a norm, not a scalar |
| C9 | word problem / real integration to | any | last step capped so we land exactly on |
| C10 | non-finite or (NaN / Inf from overflow) | undefined | force reject + shrink, never trust the number |
The examples below hit every cell. Watch the label on each.
Example 1 — C1: accept and grow moderately
Steps.
- Accept or reject? Compare with . Since , the step is good → accept and advance to . Why this step? The whole point of is to be a gatekeeper; under budget means the answer is trustworthy.
- Ratio. . Why this step? We form because that ratio is precisely the factor by which we may enlarge the error budget; feeding it through the law (next step) converts "spare error" into "allowed step change." It is the one quantity that cancels the unknown constant , so it drives everything.
- Raw factor. . Why this step? Error grows like , so to spend more error we may only stretch by — a much gentler factor.
- Clamp check. sits between and , so no clamp fires.
- New step. .
Verify: predicted new error , using the error constant defined in the symbol list. Since , we have , so cancels: predicted . That is just under — exactly what the safety factor was aiming for. ✔ (Here is scalar, so .)
Example 2 — C4: reject and shrink moderately
Steps.
- Accept or reject? → reject. We do not advance ; we stay at . Why this step? A rejected step means is untrustworthy — using it would carry a too-big error forward forever (see Local vs Global Truncation Error).
- Ratio. . Why this step? Same reason as always — this ratio, once passed through the law, tells us how much to resize ; a value below already signals "shrink." It also cancels the unknown .
- Raw factor. . Why this step? A factor below means shrink — correct, since we overshot the budget.
- Clamp check. lies between and ; no clamp.
- New step. . We retry from the same with this.
Verify: predicted retried error . So the retry should pass first time — the safety margin at work. ✔
Example 3 — C2: reject, but the shrink hits the floor clamp
Steps.
- Reject — . Stay at .
- Ratio. . Why this step? As before, we build so the law can turn "how far over budget" into "how much to shrink"; a ratio far below warns of a big shrink, which the clamp will later tame.
- Raw factor. . Why this step? — the fifth root tames even a huge overshoot.
- Clamp check. → the floor clamp fires. Use instead. Why this step? The error model was fitted near the current ; a wild shrink might land in a region the model never saw, so we cap how fast we retreat and simply try again if still too big.
- New step. .
Verify: the unclamped prediction would have been — under tol. With the clamp we only shrink to , giving predicted , still above tol. So this step will likely be rejected again and shrink once more — that is the intended, safe, gradual behaviour. ✔

Example 4 — C3: accept, but growth hits the ceiling clamp
Steps.
- Accept — , advance .
- Ratio. . Why this step? We form the ratio for the same reason every time — it is the dimensionless "error headroom" that the law converts into a step-change factor; here it is enormous, foreshadowing a growth the ceiling clamp must cap.
- Raw factor. .
- Clamp check. → ceiling clamp fires. Use . Why this step? A jump could skip right over an oncoming sharp feature of the solution; we cap growth and let the next step grow again if there is still room.
- New step. .
Verify: predicted new error with the clamped step , still safely under . Room to keep growing next step — exactly the "sprint gradually" behaviour. ✔

Example 5 — C5: sitting exactly on the tolerance boundary
Steps.
- Accept or reject? The rule is → accept. Since , we accept (the boundary is inclusive). Why this step? Rejecting an exactly-on-budget step would loop forever near the boundary; the makes the decision definite.
- Ratio. . Why this step? We still form the ratio because the update formula demands it; a ratio of exactly means "no error headroom," so any change to comes purely from the safety factor.
- Raw factor. . Why this step? With no error headroom, the only change comes from the safety factor, which nudges slightly down so future steps keep a margin.
- Clamp check. is between and ; no clamp.
- New step. .
Verify: the safety factor should always give when . Indeed . ✔ This is why : even on-budget, we drift toward safety rather than the edge.
Example 6 — C6: degenerate perfect step ()
Steps.
- Accept — an error of is certainly . Why this step? Zero estimated error means the two embedded answers agree perfectly, so the step is as trustworthy as it can be.
- Guard the division. The ratio is undefined at . Why this step? Forming the ratio is the usual move, but here it would divide by zero; we must protect it before the law can use it. In code we replace by , where is a tiny positive number tied to machine precision — a common concrete choice is (roughly double-precision round-off). Then the ratio is finite and huge, so the raw factor overshoots and the ceiling clamp takes over.
- New step. With the floor in place the raw factor exceeds , so . Why this step? The ceiling clamp already turns "wants to grow enormously" into "grow by ," so no separate rule for is needed.
Verify: take the limit instead of the floor and you get the same answer. As , raw factor , and . So the limiting new step is — the case is just the endpoint of that limit, and the floor merely makes the computer reach it without crashing. ✔
Example 7 — C7: relative tolerance takes over as grows (and stays positive for negative )
Steps.
- Take the absolute value first. The relative term uses , not . Why this step? A tolerance is a size of allowed error; it must be . The in exists precisely so that a negative solution contributes its magnitude — otherwise a negative would give a negative "budget," which is meaningless (you can't allow an error smaller than zero).
- Build the tolerance. . Why this step? When is large, demanding error below a fixed is absurdly strict; the relative part says "I care about of the magnitude of ", which here is about .
- Ratio. . Why this step? We form the ratio for the same reason as every example — it is the dimensionless headroom the law converts into a step-change factor. A value above signals the step is inside budget and may grow slightly.
- Accept or reject? → accept. Why this step? Against the properly scaled budget the step is comfortably inside; only the naive absolute-only view called it bad.
- Raw factor and new step. , so (a slight shrink because we are only just inside budget).
Verify: confirm the sign fix and that relative dominates. Since , the budget as required. The atol term is times smaller than the rtol term , so to 6 significant figures. With absolute-only, we would have rejected a perfectly fine step and stalled — the classic Mistake C from the parent note. ✔
Example 8 — C8: a system of ODEs, error is a norm
Steps.
- Scale each component. Divide each error by its own tolerance: , . Why this step? Different components can live on different scales; dividing each error by its own turns every component into a dimensionless "fraction of its own budget," so they become comparable. A scaled value below means that component is within budget.
- Combine with a root-mean-square norm. . Why this step? This is where actually does work that cannot: the accept/reject test needs a single scalar, so we collapse the vector of scaled errors into one number. The RMS norm (used by scipy's Dormand–Prince solver) averages the squared scaled errors so no single component is ignored and no component alone can dominate unfairly.
- Accept or reject? Because we already scaled by the tolerances, the budget is now simply : a scaled means "every component is, on average, within its own budget." Here → accept. Why this step? Scaling folded the tolerance into the error itself, so the comparison " vs " becomes the clean " vs " — it is the same test, just measured in units of the budget.
- Ratio and raw factor. With the scaled budget playing the role of tol, the ratio is , and the raw factor is . Why this step? We form the ratio for the exact same reason as in every scalar case — it is the error headroom the law converts into a step-change factor; the only difference is that "budget" is now the scaled value .
- New step. (grow, since the vector step was comfortably inside budget). Why this step? A scaled error well under means spare room in all components, so we may speed up — the fifth root keeps the growth gentle.
Verify: RMS norm check: , , and . ✔ Since the step grows, exactly as expected for a comfortable vector step. Note this is the same controller as the scalar case — only the meaning of "" (now a norm) and "budget" (now ) was generalised.
Example 9 — C9: word problem, land exactly on
Steps.
- Check the remaining distance. . Why this step? We must finish exactly at the requested time; overshooting would give an answer at the wrong .
- Cap the step. . Why this step? Capping (not rejecting) is correct because a smaller step only reduces error, so it stays acceptable.
- Take the final step from to . Integration is complete. Why this step? Once reaches exactly, the requested integration is done; there is nothing left to advance.
Verify: exactly — we land on the deadline. And since , predicted error is smaller than the already-accepted estimate, so the capped step is guaranteed acceptable. ✔ Compare with the fixed-step RK4 method, which would need this cap hand-coded too.

Example 10 — C10: non-finite or (NaN / Inf guard)
Steps.
- Detect the non-finite value. Before comparing, test
isfinite(eps)andisfinite(tol). Here fails the test. Why this step? A NaN breaks every comparison:NaN <= tol,NaN > tol, andNaN == tolare all false, so the plain accept/reject logic would silently do the wrong thing (often accept). We must catch it before the comparison, not after. - Force a rejection. Treat any non-finite as "infinitely over budget": reject the step and stay at . Why this step? An overflowed error is certainly not within tolerance; the only safe reading is "the biggest possible error," which is a reject.
- Shrink at the floor clamp. Do not feed into (that gives or another NaN). Instead directly use the floor factor: . Why this step? The step-update formula assumes a finite ; bypassing it and shrinking by the maximum allowed amount is the fastest safe retreat toward a step small enough that the arithmetic no longer overflows.
- (If itself is non-finite — e.g. overflowed — the same rule applies: reject and shrink, because a budget you cannot compute cannot be met.)
Verify: confirm the NaN trap logic. In IEEE arithmetic
NaN <= xisFalsefor everyx, so a naiveif eps <= tol: acceptwould fail to accept and fall through unpredictably; an explicitisfiniteguard is the only correct fix. Numerically the forced shrink gives , the same floor-clamped retreat as Example 3. ✔
Recall Quick self-test across all cells
Which cell needs a division guard? ::: C6 () — guard by flooring at round-off (e.g. ) or return . After a rejected step, what happens to ? ::: Nothing — stay at and retry (Examples 2, 3, 10). When is huge (or negative), which tolerance term dominates and why is it positive? ::: The relative one, ; the keeps the budget positive even when (Example 7). How is error turned into one number for a system? ::: A scaled RMS norm of per-component errors, then compared against a budget of (Example 8). When do you use vs for ? ::: Scalar equation → ; a system (vector ) → the norm . Why cap the last step instead of rejecting it? ::: A smaller step only lowers error, so it stays acceptable while landing exactly on (Example 9). What must you do if or is NaN/Inf? ::: Detect with an isfinite check, then force a reject and shrink by — never trust the comparison (Example 10).
Connections
- Parent: Adaptive step-size — RK45, error control — the theory these examples drill.
- Runge–Kutta Methods (RK4) — the fixed-step method underneath the embedded pair.
- Local vs Global Truncation Error — why drives every factor above.
- Order of a Numerical Method — the that fixes the exponent.
- Taylor Series Expansion — origin of the leading error constant .
- Stiff ODEs and Stability — when even a perfect controller must take tiny steps (and overflow can trigger Example 10).
- Dormand–Prince (RK45 used by scipy solve_ivp) — the RMS-norm error test in Example 8.