Initial release
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
-- dopri5.lua: Dormand-Prince 5(4) adaptive solver (7+ NFE)
|
||||
-- Adaptive sub-stepping with error estimation for optimal accuracy.
|
||||
|
||||
solver = {
|
||||
name = "dopri5",
|
||||
display = "DOPRI5 (7+ NFE)",
|
||||
description = "Adaptive Dormand-Prince 5th order with error control",
|
||||
nfe = 0, -- variable
|
||||
order = 5,
|
||||
needs_model = true,
|
||||
stateful = false,
|
||||
stochastic = false,
|
||||
}
|
||||
|
||||
-- Butcher tableau constants
|
||||
local C = {1/5, 3/10, 4/5, 8/9, 1, 1}
|
||||
local A = {
|
||||
{1/5},
|
||||
{3/40, 9/40},
|
||||
{44/45, -56/15, 32/9},
|
||||
{19372/6561, -25360/2187, 64448/6561, -212/729},
|
||||
{9017/3168, -355/33, 46732/5247, 49/176, -5103/18656},
|
||||
{35/384, 0, 500/1113, 125/192, -2187/6784, 11/84},
|
||||
}
|
||||
local B = {35/384, 0, 500/1113, 125/192, -2187/6784, 11/84, 0}
|
||||
local E = {
|
||||
35/384 - 1951/21600,
|
||||
0,
|
||||
500/1113 - 22642/50085,
|
||||
125/192 - 451/720,
|
||||
-2187/6784 + 12231/42400,
|
||||
11/84 - 649/6300,
|
||||
-1/60,
|
||||
}
|
||||
|
||||
-- Generic ERK step: compute all stages, return result in xt_out
|
||||
-- xt_fa is a FloatArray used as scratch space for model_fn calls
|
||||
local function erk_step(x, k1, t, h, n_elem, model_fn, vt_buf, xt_fa, num_extra, a_rows, c_vals, b_vals)
|
||||
local ks = {k1}
|
||||
for s = 1, num_extra do
|
||||
local a_row = a_rows[s]
|
||||
for i = 0, n_elem-1 do
|
||||
local combo = 0
|
||||
for j = 1, #a_row do
|
||||
if a_row[j] ~= 0 then combo = combo + a_row[j] * ks[j][i] end
|
||||
end
|
||||
xt_fa[i] = x[i] - h * combo
|
||||
end
|
||||
-- model_fn expects a FloatArray, writes result into vt_buf
|
||||
model_fn(xt_fa, t - c_vals[s] * h)
|
||||
ks[s+1] = {}
|
||||
for i = 0, n_elem-1 do ks[s+1][i] = vt_buf[i] end
|
||||
end
|
||||
|
||||
local result = {}
|
||||
for i = 0, n_elem-1 do
|
||||
local sol = 0
|
||||
for j = 1, #b_vals do
|
||||
if b_vals[j] ~= 0 and ks[j] then sol = sol + b_vals[j] * ks[j][i] end
|
||||
end
|
||||
result[i] = x[i] - h * sol
|
||||
end
|
||||
return result, ks
|
||||
end
|
||||
|
||||
function step(xt, vt, t_curr, t_prev, n, model_fn, vt_buf)
|
||||
local atol = 1e-3
|
||||
local rtol = 1e-2
|
||||
local max_sub = 8
|
||||
local safety = 0.9
|
||||
|
||||
local t = t_curr
|
||||
local t_end = t_prev
|
||||
local h = t - t_end
|
||||
|
||||
-- Working copy (Lua tables for intermediate math)
|
||||
local x_cur = {}
|
||||
local v_cur = {}
|
||||
for i = 0, n-1 do x_cur[i] = xt[i]; v_cur[i] = vt[i] end
|
||||
|
||||
local sub = 0
|
||||
while sub < max_sub and (t - t_end) > 1e-10 do
|
||||
h = math.min(h, t - t_end)
|
||||
local k1 = v_cur
|
||||
|
||||
-- Full DOPRI5 step (6 extra stages for 7 total including FSAL)
|
||||
-- Pass xt as scratch FloatArray for model_fn calls
|
||||
local x_next, ks = erk_step(x_cur, k1, t, h, n, model_fn, vt_buf, xt, 6, A, C, B)
|
||||
|
||||
-- Error estimate
|
||||
local err_sq_sum = 0
|
||||
for i = 0, n-1 do
|
||||
local err_i = 0
|
||||
for j = 1, 7 do
|
||||
if E[j] ~= 0 and ks[j] then err_i = err_i + E[j] * ks[j][i] end
|
||||
end
|
||||
err_i = err_i * h
|
||||
local scale = atol + rtol * math.max(math.abs(x_cur[i]), math.abs(x_next[i]))
|
||||
local ratio = err_i / scale
|
||||
err_sq_sum = err_sq_sum + ratio * ratio
|
||||
end
|
||||
local err_norm = math.sqrt(err_sq_sum / n)
|
||||
|
||||
if err_norm <= 1 then
|
||||
t = t - h
|
||||
x_cur = x_next
|
||||
v_cur = ks[7] -- FSAL
|
||||
if err_norm > 1e-10 then
|
||||
h = h * math.min(5, safety * err_norm ^ (-0.2))
|
||||
else
|
||||
h = h * 5
|
||||
end
|
||||
else
|
||||
h = h * math.max(0.2, safety * err_norm ^ (-0.2))
|
||||
end
|
||||
sub = sub + 1
|
||||
end
|
||||
|
||||
for i = 0, n-1 do xt[i] = x_cur[i] end
|
||||
end
|
||||
Reference in New Issue
Block a user