From 3e138356f081d3e1fdc25327f0a07dff16e9c4eb Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 23 Apr 2026 03:07:49 -0400 Subject: [PATCH 1/2] Support SciMLBase v3 and DiffEqBase v7 - Widen compat for SciMLBase to "2, 3" and DiffEqBase to "6.62, 7". - Bump version to 1.2.0. - Accept `DEVerbosity` objects for the `verbose` kwarg by treating any non-`false` value as "show warnings". DiffEqBase v7 now passes a structured verbosity object instead of a plain `Bool`. - Unwrap `SciMLBase.unwrapped_f(prob.f.f)` (and `prob.f.f1.f` for second-order problems) before invoking the user function. SciMLBase v3 changed the default specialization of `ODEFunction{iip}` to `AutoSpecialize`, which wraps the function in a FunctionWrappersWrapper that only accepts the exact argument types captured at problem construction (e.g. `Matrix{Float64}`). Passing a `Base.ReshapedArray` through the wrapper fails; using the unwrapped function avoids the type-specialization trap. Supersedes #60 and #61. Co-Authored-By: Chris Rackauckas --- Project.toml | 6 +++--- src/solve.jl | 25 +++++++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index f6f1036..b5cec0c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "GeometricIntegratorsDiffEq" uuid = "5a33fad7-5ce4-5983-9f5d-5f26ceab5c96" -version = "1.1.0" +version = "1.2.0" authors = ["Chris Rackauckas "] [deps] @@ -10,11 +10,11 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" [compat] -DiffEqBase = "6.62" +DiffEqBase = "6.62, 7" ExplicitImports = "1.14.0" GeometricIntegrators = "0.15, 0.16" Reexport = "0.2, 1" -SciMLBase = "2" +SciMLBase = "2, 3" julia = "1.10" [extras] diff --git a/src/solve.jl b/src/solve.jl index 01346f2..7361677 100644 --- a/src/solve.jl +++ b/src/solve.jl @@ -15,6 +15,10 @@ function DiffEqBase.__solve( error("dt required for fixed timestep methods.") end + # DiffEqBase v7 passes `verbose` as a `DEVerbosity` object instead of a Bool. + # Treat anything that is not literally `false` as "show warnings". + verbose_bool = verbose !== false + isstiff = !( alg isa Union{ GIImplicitEuler, GIImplicitMidpoint, @@ -22,7 +26,7 @@ function DiffEqBase.__solve( } ) - if verbose + if verbose_bool warned = !isempty(kwargs) && check_keywords(alg, kwargs, warnlist) if !(prob.f isa DiffEqBase.AbstractParameterizedFunction) && isstiff if DiffEqBase.has_tgrad(prob.f) @@ -68,15 +72,20 @@ function DiffEqBase.__solve( # Create function wrapper for GeometricIntegrators API # GeometricIntegrators expects: v(v, t, q, params) # DiffEqBase provides: f(du, u, p, t) for inplace or f(u, p, t) for out-of-place + # SciMLBase v3's default AutoSpecialize wraps f in a FunctionWrappersWrapper that + # only accepts the exact argument types captured at problem construction (e.g. + # `Matrix{Float64}`), so passing a `Base.ReshapedArray` to it errors. Unwrap the + # underlying user-defined function before invoking it. + raw_f = SciMLBase.unwrapped_f(prob.f.f) if !isinplace && u isa AbstractArray - v! = (v, t, q, params) -> (v .= vec(prob.f(reshape(q, sizeu), p, t)); nothing) + v! = (v, t, q, params) -> (v .= vec(raw_f(reshape(q, sizeu), p, t)); nothing) elseif !(u isa Vector{Float64}) v! = ( v, t, q, params, - ) -> (prob.f(reshape(v, sizeu), reshape(q, sizeu), p, t); nothing) + ) -> (raw_f(reshape(v, sizeu), reshape(q, sizeu), p, t); nothing) else - v! = (v, t, q, params) -> prob.f(v, q, p, t) + v! = (v, t, q, params) -> raw_f(v, q, p, t) end ode = GeometricIntegrators.ODEProblem(v!, prob.tspan, dt, vec(prob.u0)) @@ -111,17 +120,21 @@ function DiffEqBase.__solve( v! = (v, t, q, p_state, params) -> (v .= p_state) # dq/dt = p + # Unwrap past SciMLBase v3 AutoSpecialize FunctionWrappers for the acceleration + # function so it accepts argument types other than those captured at construction. + raw_f1 = SciMLBase.unwrapped_f(prob.f.f1.f) + # Handle both inplace and out-of-place problems if isinplace f! = ( f_out, t, q, p_state, params, - ) -> (prob.f.f1.f(f_out, p_state, q, p, t); nothing) # dp/dt = f1(p, q) + ) -> (raw_f1(f_out, p_state, q, p, t); nothing) # dp/dt = f1(p, q) else f! = ( f_out, t, q, p_state, params, - ) -> (f_out .= prob.f.f1.f(p_state, q, p, t); nothing) + ) -> (f_out .= raw_f1(p_state, q, p, t); nothing) end pode = GeometricIntegrators.PODEProblem( From fca13bd7473780b5038e7ffbf951befb75c322e8 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 24 Apr 2026 16:01:12 -0400 Subject: [PATCH 2/2] Route solver warnings through DEVerbosity instead of `verbose !== false` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DiffEqBase v7 passes `verbose` as a `DEVerbosity` object. Even silent modes like `DEVerbosity(None())` are not `===false`, so the prior `verbose !== false` guard always admitted warnings and ignored the caller's silence request. - Route every compat warning (unsupported kwargs, explicit t-gradient / Jacobian on stiff solvers, compat-chart link) through `@SciMLMessage`. `@SciMLMessage` handles both `Bool` (true→WarnLevel, false→Silent) and `AbstractVerbositySpecifier` (reads the named toggle), so v6 callers and v7 callers are both honored. - Use the `:mismatched_input_output_type` toggle from `DEVerbosity`, which is the right bucket for "input the GeometricIntegrators solver cannot honor" messages. - Inline the `check_keywords` loop since the SciMLBase helper always `@warn`s and can't be parameterized by verbosity. - Add `SciMLLogging` as a direct dep for `@SciMLMessage`. Co-Authored-By: Chris Rackauckas --- Project.toml | 2 ++ src/GeometricIntegratorsDiffEq.jl | 3 +- src/solve.jl | 55 ++++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/Project.toml b/Project.toml index b5cec0c..8437b37 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" GeometricIntegrators = "dcce2d33-59f6-5b8d-9047-0defad88ae06" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLLogging = "a6db7da4-7206-11f0-1eab-35f2a5dbe1d1" [compat] DiffEqBase = "6.62, 7" @@ -15,6 +16,7 @@ ExplicitImports = "1.14.0" GeometricIntegrators = "0.15, 0.16" Reexport = "0.2, 1" SciMLBase = "2, 3" +SciMLLogging = "1" julia = "1.10" [extras] diff --git a/src/GeometricIntegratorsDiffEq.jl b/src/GeometricIntegratorsDiffEq.jl index 172c2e7..1f1b178 100644 --- a/src/GeometricIntegratorsDiffEq.jl +++ b/src/GeometricIntegratorsDiffEq.jl @@ -2,7 +2,8 @@ module GeometricIntegratorsDiffEq using Reexport: Reexport, @reexport @reexport using DiffEqBase: DiffEqBase -using SciMLBase: SciMLBase, ReturnCode, check_keywords, warn_compat +using SciMLBase: SciMLBase, ReturnCode +using SciMLLogging: @SciMLMessage using GeometricIntegrators: GeometricIntegrators, CrankNicolson, Crouzeix, ExplicitEuler, ExplicitMidpoint, Gauss, Heun2, Heun3, ImplicitEuler, diff --git a/src/solve.jl b/src/solve.jl index 7361677..d8e3bad 100644 --- a/src/solve.jl +++ b/src/solve.jl @@ -15,10 +15,6 @@ function DiffEqBase.__solve( error("dt required for fixed timestep methods.") end - # DiffEqBase v7 passes `verbose` as a `DEVerbosity` object instead of a Bool. - # Treat anything that is not literally `false` as "show warnings". - verbose_bool = verbose !== false - isstiff = !( alg isa Union{ GIImplicitEuler, GIImplicitMidpoint, @@ -26,19 +22,46 @@ function DiffEqBase.__solve( } ) - if verbose_bool - warned = !isempty(kwargs) && check_keywords(alg, kwargs, warnlist) - if !(prob.f isa DiffEqBase.AbstractParameterizedFunction) && isstiff - if DiffEqBase.has_tgrad(prob.f) - @warn "Explicit t-gradient given to this stiff solver is ignored." - warned = true - end - if DiffEqBase.has_jac(prob.f) - @warn "Explicit Jacobian given to this stiff solver is ignored." - warned = true - end + # `verbose` may be a `Bool` (DiffEqBase v6) or a `DEVerbosity` / other + # `AbstractVerbositySpecifier` (DiffEqBase v7+). Route each warning through + # `@SciMLMessage` so a silent spec (e.g. `DEVerbosity(None())`) actually + # suppresses it — a `verbose !== false` guard can't, since a silent + # `DEVerbosity` is not `false`. The `:mismatched_input_output_type` toggle + # is the right DEVerbosity bucket for these: every message here reports an + # input the GeometricIntegrators solver cannot honor. For `Bool` verbose, + # `@SciMLMessage` ignores the toggle name and just maps true→WarnLevel, + # false→Silent. + warned = false + for (kw, val) in kwargs + if kw in warnlist && val !== nothing + @SciMLMessage( + string("The ", kw, " argument is ignored by ", alg, "."), + verbose, :mismatched_input_output_type + ) + warned = true + end + end + if !(prob.f isa DiffEqBase.AbstractParameterizedFunction) && isstiff + if DiffEqBase.has_tgrad(prob.f) + @SciMLMessage( + "Explicit t-gradient given to this stiff solver is ignored.", + verbose, :mismatched_input_output_type + ) + warned = true end - warned && warn_compat() + if DiffEqBase.has_jac(prob.f) + @SciMLMessage( + "Explicit Jacobian given to this stiff solver is ignored.", + verbose, :mismatched_input_output_type + ) + warned = true + end + end + if warned + @SciMLMessage( + "https://docs.sciml.ai/DiffEqDocs/stable/basics/compatibility_chart/", + verbose, :mismatched_input_output_type + ) end if callback !== nothing