You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Compose step-size knobs into CommonControllerOptions; replace DummyController for BDF/JVODE
In v7, `qmin` (alongside `qmax`, `gamma`, `beta1/beta2`, `qsteady_*`,
`qoldinit`) moved off `DEOptions` and onto the controller object. The
out-of-domain rejection path in `handle_step_rejection!` was still
reaching for the old `integrator.opts.qmin`, which throws on the v7
`DEOptions` struct — only the legacy DelayDiffEq constructor still
mentions it. The same was true of `integrator.opts.failfactor` in
`post_newton_controller!`.
Reported in
NumericalMathematics/PositiveIntegrators.jl#194 (comment)
Rather than papering over with a one-off `hasfield` walk, this lifts
the standard step-size knobs into a composable building block and
retires the `DummyController` workaround that BDF / Nordsieck were
using to keep the knobs on their algorithm structs.
A new `CommonControllerOptions{T}` struct holds `qmin`, `qmax`,
`qmax_first_step`, `gamma`, `qsteady_min`, `qsteady_max`, `failfactor`
— the seven scalars the integrator-level paths actually read. A single
type parameter `T` keeps the type signatures simple even if more knobs
are added later. All fields default to `nothing`; algorithm-specific
defaults are filled in by `resolve_basic` at `setup_controller_cache`
time. Concrete controllers (`IController`, `PIController`,
`PIDController`, `PredictiveController`, `ExtrapolationController`,
`KantorovichTypeController`, plus the new `BDFController` and
`JVODEController`) all embed a `CommonControllerOptions` as
`controller.basic`.
Seven generic accessors — `get_qmin`, `get_qmax`, `get_qmax_first_step`,
`get_gamma`, `get_qsteady_min`, `get_qsteady_max`, `get_failfactor` —
dispatch on `cache::AbstractControllerCache` and read through
`cache.controller.basic`. `CompositeControllerCache` overrides each one
to delegate to the active sub-cache. `DummyControllerCache` keeps an
alg-field fallback for any SDE algorithm still using it.
`integrator.opts.qmin` → `get_qmin(integrator)`,
`integrator.opts.failfactor` → `get_failfactor(integrator)`. Same in
the BDF post-Newton paths.
QNDF/FBDF/DFBDF used to keep `qmax`, `qsteady_min`, `qsteady_max` as
fields on the algorithm struct itself, with a `DummyController`
hard-wired into `default_controller`. The stepsize logic read
`alg.qmax` / `alg.qsteady_min` / `alg.qsteady_max` directly, plus a
hard-coded `zₛ = 1.2` magic-number gamma, so the controller surface was
unsettable.
`BDFController` embeds `CommonControllerOptions` and has a cache that
delegates back to alg-level dispatch (the existing BDF order-selection
logic is left intact). The hard-coded `zₛ = 1.2` is now
`get_gamma(integrator)`. `default_controller(QT, alg::Union{QNDF, FBDF, DFBDF})`
threads `alg.qmax` / `alg.qsteady_min` / `alg.qsteady_max` through to
the controller, so existing usage like `QNDF(qmax = 20)` keeps working.
Users can now also pass `controller = BDFController(qmin = …, gamma = …)`
to set knobs that previously had no surface (incl. `qmin` and `gamma`).
BDF-tuned per-algorithm defaults (`qmax = 5//1`, `qsteady_min = 9//10`,
`qsteady_max = 12//10`, `gamma = 12//10`) are encoded as
`qmax_default(::QNDF)` / `gamma_default(::QNDF)` etc.
Same pattern. `setη!` / `chooseη!` / `step_accept_controller!(::JVODE)`
now read `get_qmin(integrator)` / `get_qmax(integrator)` /
`get_qsteady_*(integrator)` instead of `alg.qmin` etc.
- `IController` / `PIController` / `PredictiveController` /
`PIDController` shed their flat `qmin/qmax/...` fields and embed
`CommonControllerOptions` instead. PI-specific knobs (`beta1`,
`beta2`, `qoldinit`) and PID-specific knobs (`beta`, `accept_safety`,
`limiter`) stay on the controller alongside `basic`.
- `ExtrapolationController` and `KantorovichTypeController` likewise
embed `CommonControllerOptions`. Their stepsize logic reads
`get_qmax(integrator)` / `get_qmin(integrator)` rather than direct
field access.
`test/runtests.jl` walks transitive `[sources]` dependencies and
`Pkg.develop`s them. Pre-seed the `developed` set with the active
project so a `[sources]` entry that points back to it (e.g. via the
umbrella `OrdinaryDiffEq`'s transitive sources) is skipped — `Pkg.develop`
cannot develop the active project itself, and that error was the
"package X has the same name or UUID as the active project" failure
across the sublibrary CI matrix.
Reproducer (`isoutofdomain` predicate that fires once on the first
proposed step) plus a smoke test of every controller path (default
`solve`, user-supplied `BDFController`, `CommonControllerOptions`
construction, controller-composition invariants) — 21/21 pass on Julia
1.12.
- On master (without this fix): all algorithms error out — accessing
`integrator.opts.qmin` throws because the v7 `DEOptions` struct
doesn't have the field.
- With this fix: `Tsit5` / `Vern7` / `Rosenbrock23` / `FBDF` / `QNDF`
all complete the isout-rejection problem successfully, and
`BDFController(qmax = 3)` is honored end-to-end.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
0 commit comments