Skip to content

Commit 53cc394

Browse files
Pass the integrator and the stage buffer to stage limiters (#4356)
1 parent 82f3d96 commit 53cc394

7 files changed

Lines changed: 71 additions & 11 deletions

File tree

lib/OrdinaryDiffEqLowOrderRK/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "OrdinaryDiffEqLowOrderRK"
22
uuid = "1344f307-1e59-4825-a18e-ace9aa3fa4c6"
33
authors = ["ParamThakkar123 <paramthakkar864@gmail.com>"]
4-
version = "2.2.3"
4+
version = "2.2.4"
55

66
[deps]
77
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"

lib/OrdinaryDiffEqLowOrderRK/src/fixed_timestep_perform_step.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ end
219219
stage_limiter! = integrator.opts.stage_limiter!
220220
halfdt = dt / 2
221221
@.. broadcast = false thread = thread tmp = uprev + halfdt * fsalfirst
222-
stage_limiter!(k, tmp, p, t + halfdt)
222+
stage_limiter!(tmp, integrator, p, t + halfdt)
223223
f(k, tmp, p, t + halfdt)
224224
OrdinaryDiffEqCore.increment_nf!(integrator.stats, 1)
225225
@.. broadcast = false thread = thread u = uprev + dt * k

lib/OrdinaryDiffEqQPRK/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "OrdinaryDiffEqQPRK"
22
uuid = "04162be5-8125-4266-98ed-640baecc6514"
33
authors = ["ParamThakkar123 <paramthakkar864@gmail.com>"]
4-
version = "2.2.0"
4+
version = "2.2.1"
55

66
[deps]
77
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"

lib/OrdinaryDiffEqQPRK/src/qprk_perform_step.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ end
134134
stage_limiter!(tmp, integrator, p, t + d4 * dt)
135135
f(k4, tmp, p, t + d4 * dt)
136136
@.. broadcast = false thread = thread tmp = uprev + dt * (b51 * k1 + b53 * k3 + b54 * k4)
137-
stage_limiter!(uprev, integrator, p, t + d5 * dt)
137+
stage_limiter!(tmp, integrator, p, t + d5 * dt)
138138
f(k5, tmp, p, t + d5 * dt)
139139
@.. broadcast = false thread = thread tmp = uprev + dt * (b61 * k1 + b64 * k4 + b65 * k5)
140140
stage_limiter!(tmp, integrator, p, t + d6 * dt)
@@ -212,7 +212,7 @@ end
212212
+ b16_10 * k10 + b16_11 * k11 + b16_12 * k12
213213
+ b16_13 * k13 + b16_14 * k14
214214
)
215-
stage_limiter!(u, integrator, p, t + dt)
215+
stage_limiter!(tmp, integrator, p, t + dt)
216216
f(k16, tmp, p, t + dt)
217217

218218
OrdinaryDiffEqCore.increment_nf!(integrator.stats, 16)

lib/OrdinaryDiffEqTsit5/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "OrdinaryDiffEqTsit5"
22
uuid = "b1df2697-797e-41e3-8120-5422d3b24e4a"
3-
version = "2.1.3"
3+
version = "2.1.4"
44
authors = ["ParamThakkar123 <paramthakkar864@gmail.com>"]
55

66
[deps]

lib/OrdinaryDiffEqTsit5/src/tsit_perform_step.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,24 +210,24 @@ end
210210
stage_limiter! = integrator.opts.stage_limiter!
211211
a = dt * a21
212212
@.. broadcast = false thread = thread tmp = uprev + a * k1
213-
stage_limiter!(tmp, f, p, t + c1 * dt)
213+
stage_limiter!(tmp, integrator, p, t + c1 * dt)
214214
f(k2, tmp, p, t + c1 * dt)
215215
@.. broadcast = false thread = thread tmp = uprev + dt * (a31 * k1 + a32 * k2)
216-
stage_limiter!(tmp, f, p, t + c2 * dt)
216+
stage_limiter!(tmp, integrator, p, t + c2 * dt)
217217
f(k3, tmp, p, t + c2 * dt)
218218
@.. broadcast = false thread = thread tmp = uprev + dt * (a41 * k1 + a42 * k2 + a43 * k3)
219-
stage_limiter!(tmp, f, p, t + c3 * dt)
219+
stage_limiter!(tmp, integrator, p, t + c3 * dt)
220220
f(k4, tmp, p, t + c3 * dt)
221221
@.. broadcast = false thread = thread tmp = uprev +
222222
dt * (a51 * k1 + a52 * k2 + a53 * k3 + a54 * k4)
223-
stage_limiter!(tmp, f, p, t + c4 * dt)
223+
stage_limiter!(tmp, integrator, p, t + c4 * dt)
224224
f(k5, tmp, p, t + c4 * dt)
225225
@.. broadcast = false thread = thread tmp = uprev +
226226
dt * (
227227
a61 * k1 + a62 * k2 + a63 * k3 + a64 * k4 +
228228
a65 * k5
229229
)
230-
stage_limiter!(tmp, f, p, t + dt)
230+
stage_limiter!(tmp, integrator, p, t + dt)
231231
f(k6, tmp, p, t + dt)
232232
@.. broadcast = false thread = thread u = uprev +
233233
dt * (

test/Integrators_I/step_limiter_test.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,63 @@ end
191191
@test_throws ErrorException solve(prob, alg, dt = 0.1; stage_limiter = slim!)
192192
end
193193
end
194+
195+
# The stage limiter contract is `limiter!(u, integrator, p, t)`
196+
# (`OrdinaryDiffEqCore.trivial_limiter!`). The limiters above ignore their
197+
# arguments, so they accept whatever is handed to them; that is how `Tsit5` shipped
198+
# with the `ODEFunction` in the integrator slot, `Midpoint` with a stage buffer, and
199+
# `QPRK98` limiting `uprev`. These testsets pin down which objects get passed rather
200+
# than how many times the limiter is called.
201+
const LIMITER_ALGS = [
202+
Euler, Heun, Ralston, Midpoint, RK4, BS3, OwrenZen3, DP5, Tsit5,
203+
Vern6, Vern9, DP8, TanYam7, TsitPap8, QPRK98,
204+
SSPRK22, SSPRK43, SSPRK104, SSPRK932,
205+
CarpenterKennedy2N54, ORK256, RDPK3Sp35, NDBLSRK124,
206+
Rosenbrock23, ROS3P, Rodas4, Rodas5P,
207+
]
208+
209+
@testset "stage limiter receives the integrator" begin
210+
prob = ODEProblem((du, u, p, t) -> du .= u, [1.0, 1.0], (0.0, 1.0))
211+
for A in LIMITER_ALGS
212+
ok = Ref(true)
213+
calls = Ref(0)
214+
limiter! = function (u, integrator, p, t)
215+
calls[] += 1
216+
integrator isa SciMLBase.DEIntegrator || (ok[] = false)
217+
return nothing
218+
end
219+
solve(prob, A(), dt = 0.1; stage_limiter = limiter!)
220+
@test calls[] > 0
221+
@test ok[]
222+
end
223+
end
224+
225+
# The limiter mutates whatever it is given, so handing it `uprev` corrupts every
226+
# later stage and the step update. `QPRK98` stage 5 did exactly that.
227+
@testset "stage limiter is never handed uprev" begin
228+
prob = ODEProblem((du, u, p, t) -> du .= u, [1.0, 1.0], (0.0, 1.0))
229+
for A in LIMITER_ALGS
230+
ok = Ref(true)
231+
limiter! = function (u, integrator, p, t)
232+
u === integrator.uprev && (ok[] = false)
233+
return nothing
234+
end
235+
solve(prob, A(), dt = 0.1; stage_limiter = limiter!)
236+
@test ok[]
237+
end
238+
end
239+
240+
# End-to-end: with `QPRK98` stage 5 clamping `uprev` in place, `uprev` changed
241+
# value partway through a single step.
242+
@testset "uprev is stable across one step" begin
243+
prob = ODEProblem((du, u, p, t) -> du .= u, [-1.0], (0.0, 0.1))
244+
seen = Float64[]
245+
positivity! = function (u, integrator, p, t)
246+
push!(seen, integrator.uprev[1])
247+
@. u = max(u, 0.0)
248+
return nothing
249+
end
250+
solve(prob, QPRK98(), dt = 0.1, adaptive = false; stage_limiter = positivity!)
251+
@test !isempty(seen)
252+
@test all(==(seen[1]), seen)
253+
end

0 commit comments

Comments
 (0)