Math-RungeKutta
view release on metacpan or search on metacpan
lua/RungeKutta.lua view on Meta::CPAN
-- RungeKutta.lua
-- ----------------------------------------------------------------- --
-- This Lua5 module is Copyright (c) 2010, Peter J Billam --
-- www.pjb.com.au --
-- --
-- This module is free software; you can redistribute it and/or --
-- modify it under the same terms as Lua5 itself. --
-- ----------------------------------------------------------------- --
local M = {} -- public interface
M.Version = '1.07'
M.VersionDate = '20aug2010'
-- Example usage:
-- local RK = require 'RungeKutta'
-- RK.rk4()
--------------------- infrastructure ----------------------
local function arr2txt(...) -- neat printing of arrays for debug use
local txt = {}
for e in ... do txt[#txt+1] = string.format('%g',e) end
return table.concat(txt,' ') .. "\n"
end
local function warn(str)
io.stderr:write(str,'\n')
end
local function die(str)
io.stderr:write(str,'\n')
os.exit(1)
end
local flag = false
local a
local b
local function gaussn(standdev)
-- returns normal distribution around 0.0 by the Box-Muller rules
if not flag then
a = math.sqrt(-2.0 * math.log(math.random()))
b = 6.28318531 * math.random()
flag = true
return standdev * a * math.sin(b)
else
flag = false
return standdev * a * math.cos(b)
end
end
-------------------------------------------------------
function M.rk2(yn, dydt, t, dt)
if type(yn) ~= 'table' then
warn("RungeKutta.rk2: 1st arg must be an table\n")
return false
end
if type(dydt) ~= 'function' then
warn("RungeKutta.rk2: 2nd arg must be a function\n")
return false
end
local gamma = .75; -- Ralston's minimisation of error bounds
local alpha = 0.5/gamma; local beta = 1.0-gamma;
local alphadt=alpha*dt; local betadt=beta*dt; local gammadt=gamma*dt;
local ny = #yn;
local ynp1 = {}
local dydtn = {}
local ynpalpha = {} -- Gear calls this q
local dydtnpalpha = {}
dydtn = dydt(t, yn);
-- for i=1, ny do
for i in pairs(yn) do
ynpalpha[i] = yn[i] + alphadt*dydtn[i];
end
dydtnpalpha = dydt(t+alphadt, ynpalpha);
for i in pairs(yn) do
ynp1[i] = yn[i]+betadt*dydtn[i]+gammadt*dydtnpalpha[i];
end
return t+dt, ynp1
lua/RungeKutta.lua view on Meta::CPAN
for i in pairs(yn) do k3[i] = dt * k3[i] end
local ynp1 = {}
for i in pairs(yn) do
ynp1[i] = yn[i] + 0.17476028*k0[i]
- 0.55148053*k1[i] + 1.20553547*k2[i] + 0.17118478*k3[i]
end
return t+dt, ynp1
end
function M.rk4_classical(yn, dydt, t, dt)
if (type(yn) ~= 'table') then
warn("RungeKutta.rk4_classical: 1st arg must be arrayref\n")
return false
end
if (type(dydt) ~= 'function') then
warn("RungeKutta.rk4_classical: 2nd arg must be subroutine ref\n")
return false
end
local ny = #yn; local i;
-- The Classical 4th-order Runge-Kutta Method, see Gear p35
local k0 = dydt(t, yn)
for i in pairs(yn) do k0[i] = dt * k0[i] end
local eta1 = {}
for i in pairs(yn) do eta1[i] = yn[i] + 0.5*k0[i] end
local k1 = dydt(t+0.5*dt, eta1)
for i in pairs(yn) do k1[i] = dt * k1[i] end
local eta2 = {}
for i in pairs(yn) do eta2[i] = yn[i] + 0.5*k1[i] end
local k2 = dydt(t+0.5*dt, eta2)
for i in pairs(yn) do k2[i] = dt * k2[i] end
local eta3 = {}
for i in pairs(yn) do eta3[i] = yn[i] + k2[i] end
local k3 = dydt(t+dt, eta3)
for i in pairs(yn) do k3[i] = dt * k3[i] end
local ynp1 = {}
for i in pairs(yn) do
ynp1[i] = yn[i] + (k0[i] + 2.0*k1[i] + 2.0*k2[i] + k3[i]) / 6.0;
end
return t+dt, ynp1
end
return M
--[[
=pod
=head1 NAME
RungeKutta.lua - Integrating Systems of Differential Equations
=head1 SYNOPSIS
local RK = require 'RungeKutta'
function dydt(t, y) -- the derivative function
-- y is the table of the values, dydt the table of the derivatives
-- the table can be an array (1...n), or a dictionary; whichever,
-- the same indices must be used for the return table: dydt
local dydt = {}; ... ; return dydt
end
y = initial_y(); t=0; dt=0.4; -- the initial conditions
-- For automatic timestep adjustment ...
while t < tfinal do
t, dt, y = RK.rk4_auto(y, dydt, t, dt, 0.00001)
display(t, y)
end
-- Or, for fixed timesteps ...
while t < tfinal do
t, y = RK.rk4(y, dydt, t, dt) -- Merson's 4th-order method
display(t, y)
end
-- alternatively, though not so accurate ...
t, y = RK.rk2(y, dydt, t, dt) -- Heun's 2nd-order method
-- or, also available ...
t, y = RK.rk4_classical(y, dydt, t, dt) -- Runge-Kutta 4th-order
t, y = RK.rk4_ralston(y, dydt, t, dt) -- Ralston's 4th-order
=head1 DESCRIPTION
RungeKutta.lua offers algorithms for the numerical integration
of simultaneous differential equations of the form
dY/dt = F(t,Y)
where Y is an array of variables whose initial values Y(0) are
known, and F is a function known from the dynamics of the problem.
The Runge-Kutta methods all involve evaluating the derivative function
F(t,Y) more than once, at various points within the timestep, and
combining the results to reach an accurate answer for the Y(t+dt).
This module only uses explicit Runge-Kutta methods; the implicit methods
involve, at each timestep, solving a set of simultaneous equations
involving both Y(t) and F(t,Y), and this is generally intractable.
Three main algorithms are offered. I<rk2> is Heun's 2nd-order
Runge-Kutta algorithm, which is relatively imprecise, but does have
a large range of stability which might be useful in some problems. I<rk4>
is Merson's 4th-order Runge-Kutta algorithm, which should be the normal
choice in situations where the step-size must be specified. I<rk4_auto>
uses the step-doubling method to adjust the step-size of I<rk4> automatically
to achieve a specified precision; this saves much fiddling around trying
to choose a good step-size, and can also save CPU time by automatically
increasing the step-size when the solution is changing only slowly.
This module is the translation into I<Lua> of the I<Perl> CPAN module
Math::RungeKutta, and comes in its C<./lua> subdirectory.
There also exists a translation into I<JavaScript>
which comes in its C<./js> subdirectory.
The calling-interfaces are identical in all three versions.
This module has been designed to be robust and easy to use, and should
be helpful in solving systems of differential equations which arise
( run in 0.872 second using v1.01-cache-2.11-cpan-39bf76dae61 )