CompatHelper: bump compat for SciMLBase to 3, (keep existing compat)#194
Conversation
1217f08 to
7cbea1a
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Coverage Report for CI Build 25966466785Coverage remained the same at 97.305%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
JoshuaLampert
left a comment
There was a problem hiding this comment.
We need to wait for other upstream packages to update their compat bounds (especially OrdinaryDiffEq*.jl packages).
|
The remaining errors look like a bug in OrdinaryDiffEqCore.jl to me. @ChrisRackauckas, in https://github.com/SciML/OrdinaryDiffEq.jl/blob/3a72e5e213ee1bdb8d731361b192f0c9accf05ee/lib/OrdinaryDiffEqCore/src/integrators/integrator_utils.jl#L71 |
|
Yup thanks, looks like the isoutofdomain code path must've been missed on this. |
`qmin` (alongside `qmax`, `gamma`, `beta1/beta2`, `qsteady_*`, `qoldinit`) moved off `DEOptions` and onto the controller object in v7. 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. Surface this through the controller interface rather than a one-off `hasfield` walk: a new `get_qmin(integrator)` (with implementations dispatched on each concrete controller cache) returns the minimum step-size shrinkage factor used by the integrator's controller. `handle_step_rejection!` calls it instead of `integrator.opts.qmin`. Per-cache implementations: - `IControllerCache`, `PIControllerCache`, `PredictiveControllerCache` return their controller's `qmin` field. - `PIDControllerCache` has no `qmin` (it limits dt via `limiter` + `accept_safety` instead) and returns the historical default `1 // 5` so the out-of-domain shrink path still has something to multiply by. - `DummyControllerCache` (BDF / Nordsieck — algorithms that own the step-size logic) reads `integrator.alg.qmin` if present, else falls back to the historical default. - `CompositeControllerCache` delegates to the currently active sub-cache, mirroring how `stepsize_controller!` and friends dispatch. Refresh the `PredictiveController` docstring (which still showed the old `integrator.opts.qmin/qmax/qsteady_*/gamma` interface) to match the actual v7 implementation that destructures from `cache.controller`, and update two stale `# equivalent to integrator.opts.gamma` comments in `lib/OrdinaryDiffEqBDF/src/controllers.jl`. Reported in NumericalMathematics/PositiveIntegrators.jl#194 (comment) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
… 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: ## `BasicController` + accessors A new `BasicController` struct holds `qmin`, `qmax`, `qmax_first_step`, `gamma`, `qsteady_min`, `qsteady_max`, `failfactor` — the seven scalars the integrator-level paths actually read. 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 `BasicController` 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 (mirroring how `stepsize_controller!` already dispatched). `DummyControllerCache` keeps its alg-field fallback for any SDE algorithms still using it. ## `handle_step_rejection!` / `post_newton_controller!` `integrator.opts.qmin` → `get_qmin(integrator)`, `integrator.opts.failfactor` → `get_failfactor(integrator)`. Same in the BDF post-Newton paths. ## `BDFController` (replaces `DummyController` for QNDF / FBDF / DFBDF) 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, so the controller surface was unsettable. `BDFController` embeds `BasicController` and has a cache that delegates back to alg-level dispatch (the existing BDF order-selection logic is left intact). `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, but users can now also pass `controller = BDFController(qmin = …, gamma = …)` to set knobs that previously had no surface (and weren't read by the BDF logic, which still gets default behavior). BDF-tuned per-algorithm defaults (`qmax = 5//1`, `qsteady_min = 9//10`, `qsteady_max = 12//10`) are encoded as `qmax_default(::QNDF)` etc. ## `JVODEController` (replaces `DummyController` for Nordsieck JVODE) Same pattern. `setη!` / `chooseη!` / `step_accept_controller!(::JVODE)` now read `get_qmin(integrator)` / `get_qmax(integrator)` / `get_qsteady_*(integrator)` instead of `alg.qmin` etc. ## Other refactors for consistency - `IController` / `PIController` / `PredictiveController` / `PIDController` shed their flat `qmin/qmax/...` fields and embed `BasicController` 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 `BasicController` so the accessors work uniformly. ## Verification Reproducer (`isoutofdomain` predicate that fires once on the first proposed step) plus a smoke test of every controller path (default `solve`, user-supplied `BDFController`, BasicController 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>
…ntroller 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. ## `CommonControllerOptions` + accessors 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. ## `handle_step_rejection!` / `post_newton_controller!` `integrator.opts.qmin` → `get_qmin(integrator)`, `integrator.opts.failfactor` → `get_failfactor(integrator)`. Same in the BDF post-Newton paths. ## `BDFController` (replaces `DummyController` for QNDF / FBDF / DFBDF) 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. ## `JVODEController` (replaces `DummyController` for Nordsieck JVODE) Same pattern. `setη!` / `chooseη!` / `step_accept_controller!(::JVODE)` now read `get_qmin(integrator)` / `get_qmax(integrator)` / `get_qsteady_*(integrator)` instead of `alg.qmin` etc. ## Other refactors for consistency - `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 orchestration `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. ## Verification 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
…ntroller 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>
|
It would probably be hard to maintain compatibility with the older versions of the SciML ecosystem because, e.g., we use something like PositiveIntegrators.jl/src/mprk.jl Line 726 in f977ba8 which has to change since EEst moved out of the integrator and into the controller. Do you think it is acceptable to drop support for the old versions, @ranocha, @SKopecz? It would make our lives easier and would reduce some boilerplate code.
|
|
I would be fine with this 👍 |
JoshuaLampert
left a comment
There was a problem hiding this comment.
CI is green. This is ready for review from my side.
This pull request changes the compat entry for the
SciMLBasepackage from2.128to2.128, 3.This keeps the compat entries for earlier versions.
Note: I have not tested your package with this new compat entry.
It is your responsibility to make sure that your package tests pass before you merge this pull request.
Closes #195, closes #196, closes #197, closes #198, closes #199, closes #201, closes #202, closes #203, closes #204, closes #205, closes #207, closes #208, closes #209, closes #210.