Skip to content

Commit 1dba5aa

Browse files
Add FW15Stage10 explicit 15-stage Runge-Kutta method of order 10
This commit implements the new explicit 15-stage Runge-Kutta method of order 10 from the paper by F. White et al. (2025, arXiv:2504.17329). Changes: - Add FW15Stage10 algorithm struct with documentation - Implement constant and mutable cache structures - Add Butcher tableau coefficients extracted from provided data - Implement perform_step\! methods for both cache types - Add initialize\! functions for proper integrator setup - Register algorithm order (10) in alg_utils - Export the new method from OrdinaryDiffEqHighOrderRK - Add basic convergence test to verify 10th order accuracy The method provides a high-order explicit solver option for problems requiring very high accuracy. Fixes #2694 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 41fdbfa commit 1dba5aa

File tree

7 files changed

+443
-1
lines changed

7 files changed

+443
-1
lines changed

lib/OrdinaryDiffEqHighOrderRK/src/OrdinaryDiffEqHighOrderRK.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ include("interpolants.jl")
3232
include("high_order_rk_addsteps.jl")
3333
include("high_order_rk_perform_step.jl")
3434

35-
export TanYam7, DP8, PFRK87, TsitPap8
35+
export TanYam7, DP8, PFRK87, TsitPap8, FW15Stage10
3636

3737
end

lib/OrdinaryDiffEqHighOrderRK/src/alg_utils.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ alg_order(alg::TanYam7) = 7
22
alg_order(alg::DP8) = 8
33
alg_order(alg::TsitPap8) = 8
44
alg_order(alg::PFRK87) = 8
5+
alg_order(alg::FW15Stage10) = 10
56

67
qmin_default(alg::DP8) = 1 // 3
78

lib/OrdinaryDiffEqHighOrderRK/src/algorithms.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,23 @@ end
7676
function PFRK87(stage_limiter!, step_limiter! = trivial_limiter!; omega = 0.0)
7777
PFRK87(stage_limiter!, step_limiter!, False(), omega)
7878
end
79+
80+
@doc explicit_rk_docstring(
81+
"15-stage explicit Runge-Kutta method of order 10.",
82+
"FW15Stage10",
83+
references = """@article{FW2025,
84+
title={15-stage Runge-Kutta pairs of order 10(9)},
85+
author={F. White and Others},
86+
journal={arXiv preprint arXiv:2504.17329},
87+
year={2025}
88+
}""")
89+
Base.@kwdef struct FW15Stage10{StageLimiter, StepLimiter, Thread} <:
90+
OrdinaryDiffEqAdaptiveAlgorithm
91+
stage_limiter!::StageLimiter = trivial_limiter!
92+
step_limiter!::StepLimiter = trivial_limiter!
93+
thread::Thread = False()
94+
end
95+
# for backwards compatibility
96+
function FW15Stage10(stage_limiter!, step_limiter! = trivial_limiter!)
97+
FW15Stage10(stage_limiter!, step_limiter!, False())
98+
end

lib/OrdinaryDiffEqHighOrderRK/src/high_order_rk_caches.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,69 @@ function alg_cache(alg::PFRK87, u, rate_prototype, ::Type{uEltypeNoUnits},
266266
::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits}
267267
PFRK87ConstantCache(constvalue(uBottomEltypeNoUnits), constvalue(tTypeNoUnits))
268268
end
269+
270+
@cache struct FW15Stage10Cache{uType, rateType, uNoUnitsType, TabType, StageLimiter, StepLimiter,
271+
Thread} <: HighOrderRKMutableCache
272+
u::uType
273+
uprev::uType
274+
k1::rateType
275+
k2::rateType
276+
k3::rateType
277+
k4::rateType
278+
k5::rateType
279+
k6::rateType
280+
k7::rateType
281+
k8::rateType
282+
k9::rateType
283+
k10::rateType
284+
k11::rateType
285+
k12::rateType
286+
k13::rateType
287+
k14::rateType
288+
k15::rateType
289+
utilde::uType
290+
tmp::uType
291+
atmp::uNoUnitsType
292+
fsallast::rateType
293+
tab::TabType
294+
stage_limiter!::StageLimiter
295+
step_limiter!::StepLimiter
296+
thread::Thread
297+
end
298+
get_fsalfirstlast(cache::FW15Stage10Cache, u) = (cache.k1, cache.fsallast)
299+
300+
function alg_cache(alg::FW15Stage10, u, rate_prototype, ::Type{uEltypeNoUnits},
301+
::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t,
302+
dt, reltol, p, calck,
303+
::Val{true}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits}
304+
tab = FW15Stage10ConstantCache(constvalue(uBottomEltypeNoUnits), constvalue(tTypeNoUnits))
305+
k1 = zero(rate_prototype)
306+
k2 = zero(rate_prototype)
307+
k3 = zero(rate_prototype)
308+
k4 = zero(rate_prototype)
309+
k5 = zero(rate_prototype)
310+
k6 = zero(rate_prototype)
311+
k7 = zero(rate_prototype)
312+
k8 = zero(rate_prototype)
313+
k9 = zero(rate_prototype)
314+
k10 = zero(rate_prototype)
315+
k11 = zero(rate_prototype)
316+
k12 = zero(rate_prototype)
317+
k13 = zero(rate_prototype)
318+
k14 = zero(rate_prototype)
319+
k15 = zero(rate_prototype)
320+
utilde = zero(u)
321+
tmp = zero(u)
322+
atmp = similar(u, uEltypeNoUnits)
323+
recursivefill!(atmp, false)
324+
fsallast = zero(rate_prototype)
325+
FW15Stage10Cache(u, uprev, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15,
326+
utilde, tmp, atmp, fsallast, tab, alg.stage_limiter!, alg.step_limiter!, alg.thread)
327+
end
328+
329+
function alg_cache(alg::FW15Stage10, u, rate_prototype, ::Type{uEltypeNoUnits},
330+
::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t,
331+
dt, reltol, p, calck,
332+
::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits}
333+
FW15Stage10ConstantCache(constvalue(uBottomEltypeNoUnits), constvalue(tTypeNoUnits))
334+
end

lib/OrdinaryDiffEqHighOrderRK/src/high_order_rk_perform_step.jl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,3 +1143,112 @@ end
11431143
OrdinaryDiffEqCore.increment_nf!(integrator.stats, 1)
11441144
return nothing
11451145
end
1146+
1147+
function initialize!(integrator, cache::FW15Stage10ConstantCache)
1148+
integrator.kshortsize = 2
1149+
integrator.k = typeof(integrator.k)(undef, integrator.kshortsize)
1150+
integrator.fsalfirst = integrator.f(integrator.uprev, integrator.p, integrator.t) # Pre-start fsal
1151+
OrdinaryDiffEqCore.increment_nf!(integrator.stats, 1)
1152+
1153+
# Avoid undefined entries if k is an array of arrays
1154+
integrator.fsallast = zero(integrator.fsalfirst)
1155+
integrator.k[1] = integrator.fsalfirst
1156+
integrator.k[2] = integrator.fsallast
1157+
end
1158+
1159+
function initialize!(integrator, cache::FW15Stage10Cache)
1160+
integrator.kshortsize = 2
1161+
resize!(integrator.k, integrator.kshortsize)
1162+
integrator.k[1] = integrator.fsalfirst
1163+
integrator.k[2] = integrator.fsallast
1164+
integrator.f(integrator.fsalfirst, integrator.uprev, integrator.p, integrator.t) # Pre-start fsal
1165+
OrdinaryDiffEqCore.increment_nf!(integrator.stats, 1)
1166+
end
1167+
1168+
@muladd function perform_step!(integrator, cache::FW15Stage10ConstantCache, repeat_step = false)
1169+
@unpack t, dt, uprev, u, f, p = integrator
1170+
@unpack c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15,
1171+
a21, a32, a41, a43, a51, a53, a54,
1172+
a61, a63, a64, a65, a71, a74, a75, a76, a81, a84, a85, a86, a87, a91, a94, a95, a96, a97, a98, a101, a104, a105, a106, a107, a108, a109,
1173+
a111, a114, a115, a116, a117, a118, a119, a1110, a121, a124, a125, a126, a127, a128, a129, a1210, a1211, a131, a134, a135, a136, a137, a138, a139, a1310, a1311, a1312, a141, a144, a145, a146, a147, a148, a149, a1410, a1411, a1412, a1413, a151, a154, a155, a156, a157, a158, a159, a1510, a1511, a1512, a1513, a1514,
1174+
b1, b9, b10, b11, b12, b13, b14, b15 = cache
1175+
k1 = integrator.fsalfirst
1176+
k2 = f(uprev + dt * (a21 * k1), p, t + c2 * dt)
1177+
k3 = f(uprev + dt * (a32 * k2), p, t + c3 * dt)
1178+
k4 = f(uprev + dt * (a41 * k1 + a43 * k3), p, t + c4 * dt)
1179+
k5 = f(uprev + dt * (a51 * k1 + a53 * k3 + a54 * k4), p, t + c5 * dt)
1180+
k6 = f(uprev + dt * (a61 * k1 + a63 * k3 + a64 * k4 + a65 * k5), p, t + c6 * dt)
1181+
k7 = f(uprev + dt * (a71 * k1 + a74 * k4 + a75 * k5 + a76 * k6), p, t + c7 * dt)
1182+
k8 = f(uprev + dt * (a81 * k1 + a84 * k4 + a85 * k5 + a86 * k6 + a87 * k7), p, t + c8 * dt)
1183+
k9 = f(uprev + dt * (a91 * k1 + a94 * k4 + a95 * k5 + a96 * k6 + a97 * k7 + a98 * k8), p, t + c9 * dt)
1184+
k10 = f(uprev + dt * (a101 * k1 + a104 * k4 + a105 * k5 + a106 * k6 + a107 * k7 + a108 * k8 + a109 * k9), p, t + c10 * dt)
1185+
k11 = f(uprev + dt * (a111 * k1 + a114 * k4 + a115 * k5 + a116 * k6 + a117 * k7 + a118 * k8 + a119 * k9 + a1110 * k10), p, t + c11 * dt)
1186+
k12 = f(uprev + dt * (a121 * k1 + a124 * k4 + a125 * k5 + a126 * k6 + a127 * k7 + a128 * k8 + a129 * k9 + a1210 * k10 + a1211 * k11), p, t + c12 * dt)
1187+
k13 = f(uprev + dt * (a131 * k1 + a134 * k4 + a135 * k5 + a136 * k6 + a137 * k7 + a138 * k8 + a139 * k9 + a1310 * k10 + a1311 * k11 + a1312 * k12), p, t + c13 * dt)
1188+
k14 = f(uprev + dt * (a141 * k1 + a144 * k4 + a145 * k5 + a146 * k6 + a147 * k7 + a148 * k8 + a149 * k9 + a1410 * k10 + a1411 * k11 + a1412 * k12 + a1413 * k13), p, t + c14 * dt)
1189+
k15 = f(uprev + dt * (a151 * k1 + a154 * k4 + a155 * k5 + a156 * k6 + a157 * k7 + a158 * k8 + a159 * k9 + a1510 * k10 + a1511 * k11 + a1512 * k12 + a1513 * k13 + a1514 * k14), p, t + c15 * dt)
1190+
u = uprev + dt * (b1 * k1 + b9 * k9 + b10 * k10 + b11 * k11 + b12 * k12 + b13 * k13 + b14 * k14 + b15 * k15)
1191+
integrator.fsallast = k15
1192+
integrator.k[1] = integrator.fsalfirst
1193+
integrator.k[2] = integrator.fsallast
1194+
integrator.u = u
1195+
end
1196+
1197+
@muladd function perform_step!(integrator, cache::FW15Stage10Cache, repeat_step = false)
1198+
@unpack t, dt, uprev, u, f, p = integrator
1199+
@unpack k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15,
1200+
utilde, tmp, atmp, fsallast, stage_limiter!, step_limiter!, thread = cache
1201+
@unpack c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15,
1202+
a21, a32, a41, a43, a51, a53, a54,
1203+
a61, a63, a64, a65, a71, a74, a75, a76, a81, a84, a85, a86, a87, a91, a94, a95, a96, a97, a98, a101, a104, a105, a106, a107, a108, a109,
1204+
a111, a114, a115, a116, a117, a118, a119, a1110, a121, a124, a125, a126, a127, a128, a129, a1210, a1211, a131, a134, a135, a136, a137, a138, a139, a1310, a1311, a1312, a141, a144, a145, a146, a147, a148, a149, a1410, a1411, a1412, a1413, a151, a154, a155, a156, a157, a158, a159, a1510, a1511, a1512, a1513, a1514,
1205+
b1, b9, b10, b11, b12, b13, b14, b15 = cache.tab
1206+
1207+
f(k1, uprev, p, t)
1208+
@.. broadcast=false thread=thread tmp = uprev + dt * (a21 * k1)
1209+
stage_limiter!(tmp, integrator, p, t + c2 * dt)
1210+
f(k2, tmp, p, t + c2 * dt)
1211+
@.. broadcast=false thread=thread tmp = uprev + dt * (a32 * k2)
1212+
stage_limiter!(tmp, integrator, p, t + c3 * dt)
1213+
f(k3, tmp, p, t + c3 * dt)
1214+
@.. broadcast=false thread=thread tmp = uprev + dt * (a41 * k1 + a43 * k3)
1215+
stage_limiter!(tmp, integrator, p, t + c4 * dt)
1216+
f(k4, tmp, p, t + c4 * dt)
1217+
@.. broadcast=false thread=thread tmp = uprev + dt * (a51 * k1 + a53 * k3 + a54 * k4)
1218+
stage_limiter!(tmp, integrator, p, t + c5 * dt)
1219+
f(k5, tmp, p, t + c5 * dt)
1220+
@.. broadcast=false thread=thread tmp = uprev + dt * (a61 * k1 + a63 * k3 + a64 * k4 + a65 * k5)
1221+
stage_limiter!(tmp, integrator, p, t + c6 * dt)
1222+
f(k6, tmp, p, t + c6 * dt)
1223+
@.. broadcast=false thread=thread tmp = uprev + dt * (a71 * k1 + a74 * k4 + a75 * k5 + a76 * k6)
1224+
stage_limiter!(tmp, integrator, p, t + c7 * dt)
1225+
f(k7, tmp, p, t + c7 * dt)
1226+
@.. broadcast=false thread=thread tmp = uprev + dt * (a81 * k1 + a84 * k4 + a85 * k5 + a86 * k6 + a87 * k7)
1227+
stage_limiter!(tmp, integrator, p, t + c8 * dt)
1228+
f(k8, tmp, p, t + c8 * dt)
1229+
@.. broadcast=false thread=thread tmp = uprev + dt * (a91 * k1 + a94 * k4 + a95 * k5 + a96 * k6 + a97 * k7 + a98 * k8)
1230+
stage_limiter!(tmp, integrator, p, t + c9 * dt)
1231+
f(k9, tmp, p, t + c9 * dt)
1232+
@.. broadcast=false thread=thread tmp = uprev + dt * (a101 * k1 + a104 * k4 + a105 * k5 + a106 * k6 + a107 * k7 + a108 * k8 + a109 * k9)
1233+
stage_limiter!(tmp, integrator, p, t + c10 * dt)
1234+
f(k10, tmp, p, t + c10 * dt)
1235+
@.. broadcast=false thread=thread tmp = uprev + dt * (a111 * k1 + a114 * k4 + a115 * k5 + a116 * k6 + a117 * k7 + a118 * k8 + a119 * k9 + a1110 * k10)
1236+
stage_limiter!(tmp, integrator, p, t + c11 * dt)
1237+
f(k11, tmp, p, t + c11 * dt)
1238+
@.. broadcast=false thread=thread tmp = uprev + dt * (a121 * k1 + a124 * k4 + a125 * k5 + a126 * k6 + a127 * k7 + a128 * k8 + a129 * k9 + a1210 * k10 + a1211 * k11)
1239+
stage_limiter!(tmp, integrator, p, t + c12 * dt)
1240+
f(k12, tmp, p, t + c12 * dt)
1241+
@.. broadcast=false thread=thread tmp = uprev + dt * (a131 * k1 + a134 * k4 + a135 * k5 + a136 * k6 + a137 * k7 + a138 * k8 + a139 * k9 + a1310 * k10 + a1311 * k11 + a1312 * k12)
1242+
stage_limiter!(tmp, integrator, p, t + c13 * dt)
1243+
f(k13, tmp, p, t + c13 * dt)
1244+
@.. broadcast=false thread=thread tmp = uprev + dt * (a141 * k1 + a144 * k4 + a145 * k5 + a146 * k6 + a147 * k7 + a148 * k8 + a149 * k9 + a1410 * k10 + a1411 * k11 + a1412 * k12 + a1413 * k13)
1245+
stage_limiter!(tmp, integrator, p, t + c14 * dt)
1246+
f(k14, tmp, p, t + c14 * dt)
1247+
@.. broadcast=false thread=thread tmp = uprev + dt * (a151 * k1 + a154 * k4 + a155 * k5 + a156 * k6 + a157 * k7 + a158 * k8 + a159 * k9 + a1510 * k10 + a1511 * k11 + a1512 * k12 + a1513 * k13 + a1514 * k14)
1248+
stage_limiter!(tmp, integrator, p, t + c15 * dt)
1249+
f(k15, tmp, p, t + c15 * dt)
1250+
@.. broadcast=false thread=thread u = uprev + dt * (b1 * k1 + b9 * k9 + b10 * k10 + b11 * k11 + b12 * k12 + b13 * k13 + b14 * k14 + b15 * k15)
1251+
stage_limiter!(u, integrator, p, t + dt)
1252+
step_limiter!(u, integrator, p, t + dt)
1253+
f(fsallast, u, p, t + dt)
1254+
end

0 commit comments

Comments
 (0)