Source code for vivyd.solvers.midpoint
from .solver import Solver
from ..core import ti_or_fallback as ti
from ..typing import arrf64
from numpy import zeros
from typing import Callable
[docs]
@ti.data_oriented
class Midpoint(Solver):
"""
A midpoint integrator for solving systems of ordinary differential
equations of the form
.. math::
\\dfrac{ds}{dt} = f(t, s).
The method is a second-order integrator that updates the state of the
system at each time step as
.. math::
t_{n+1/2} &= \\dfrac{t_{n+1} + t_n}{2}, \\\\
s_{n+1/2} &= s_n + f(t_n, s_n) \\cdot (t_{n+1/2} - t_n), \\\\
s_{n+1} &= s_n + f(t_{n+1/2}, s_{n+1/2}) \\cdot (t_{n+1} - t_n).
The integrator supports both pure Python and Taichi-accelerated
implementations (as defined by the :class:`vivyd.models.TaichiCompatible`
and :class:`vivyd.models.TaichiConvertible` protocols).
"""
def _integrate_python(
self,
model : Callable,
t_tab : arrf64,
state0 : arrf64,
handle : Callable
) -> arrf64:
n = len(t_tab)
out = zeros((n, *state0.shape), dtype=float)
out[0] = state0
progress = 0.0
for i in range(1, n):
if self.verbose:
p = (i+1) / n
if p >= progress + 0.01:
progress = p
print(f"\rProgress: {int(progress*100)} %", end="")
s = out[i-1]
t = t_tab[i-1]
dt = t_tab[i] - t
ds = model(t, s, handle=handle)
s_mid = s + ds * (dt / 2)
t_mid = t + dt / 2
ds_mid = model(t_mid, s_mid, handle=handle)
out[i] = s + ds_mid * dt
if self.verbose:
print("\rProgress: 100 %")
return out
def _make_buffer(
self,
state0: arrf64,
t_tab: arrf64
) -> arrf64:
return zeros(state0.shape[0], dtype=float)
@ti.func
def _integrate_taichi_func(
self,
model : ti.template(), # type: ignore
t_tab : ti.types.ndarray(dtype=float, ndim=1), # type: ignore
state0: ti.types.ndarray(dtype=float, ndim=1), # type: ignore
handle: ti.template(), # type: ignore
out : ti.types.ndarray(dtype=float, ndim=2), # type: ignore
):
n_state = ti.static(model.state_size)
n_time = t_tab.shape[0]
s = ti.Vector.zero(float, n_state)
s_mid = ti.Vector.zero(float, n_state)
progress = 0.0
for j in range(n_state):
out[0, j] = state0[j]
ti.loop_config(serialize=True)
for i in range(1, n_time):
if ti.static(self.verbose):
p = (i+1) / n_time
if p >= progress + 0.01:
progress = p
print(f"\rProgress: {int(progress*100)} %", end="")
dt = t_tab[i] - t_tab[i - 1]
for j in range(n_state):
s[j] = out[i - 1, j]
rhs = model._call_taichi_func(t_tab[i - 1], s, handle)
for j in range(n_state):
s_mid[j] = out[i - 1, j] + rhs[j] * dt / 2.0
t_mid = t_tab[i - 1] + dt / 2.0
rhs_mid = model._call_taichi_func(t_mid, s_mid, handle)
for j in range(n_state):
out[i, j] = out[i - 1, j] + rhs_mid[j] * dt
if ti.static(self.verbose):
print("\rProgress: 100 %")