Source code for beat.monodomain_solver
import logging
from dataclasses import dataclass
from typing import Protocol
import numpy as np
from .monodomain_model import MonodomainModel
logger = logging.getLogger(__name__)
EPS = 1e-12
[docs]
class ODESolver(Protocol):
def to_dolfin(self) -> None: ...
def from_dolfin(self) -> None: ...
def ode_to_pde(self) -> None: ...
def pde_to_ode(self) -> None: ...
def step(self, t0: float, dt: float) -> None: ...
[docs]
@dataclass
class MonodomainSplittingSolver:
pde: MonodomainModel
ode: ODESolver
theta: float = 1.0
def __post_init__(self) -> None:
assert np.isclose(self.theta, 1.0), "Only first order splitting is implemented"
self.ode.to_dolfin() # numpy array (ODE solver) -> dolfin function
self.ode.ode_to_pde() # dolfin function in ODE space (quad?) -> CG1 dolfin function
self.pde.assign_previous()
def solve(self, interval, dt):
(T0, T) = interval
if dt is None:
dt = T - T0
t0 = T0
t1 = T0 + dt
while t1 < T + EPS:
logger.info(f"Solving on t = ({t0:.2f}, {t0:.2f})")
self.step((t0, t1))
t0 = t1
t1 = t0 + dt
def step(self, interval):
# Extract some parameters for readability
theta = self.theta
# Extract time domain
(t0, t1) = interval
logger.info(f"Stepping from {t0} to {t1} using theta = {theta}")
dt = t1 - t0
t = t0 + theta * dt
logger.info(f"Tentative ODE step with t0={t0:.5f} dt={theta * dt:.5f}")
# Solve ODE
self.ode.step(t0=t0, dt=theta * dt)
# Move voltage to FEniCS
self.ode.to_dolfin() # numpy array (ODE solver) -> dolfin function
self.ode.ode_to_pde() # dolfin function in ODE space (quad?) -> CG1 dolfin function
self.pde.assign_previous()
logger.info("PDE step")
# Solve PDE
self.pde.step((t0, t1))
self.ode.pde_to_ode() # CG1 dolfin function -> dolfin function in ODE space (quad?)
# Copy voltage from PDE to ODE
self.ode.from_dolfin()
# If first order splitting, we are done.
if np.isclose(theta, 1.0):
# But first update previous value in PDE
self.pde.assign_previous()
return
# Otherwise, we do another ode_step:
logger.info(f"Corrective ODE step with t0={t:5f} and dt={(1.0 - theta) * dt:.5f}")
# To the correction step
self.ode.step(t, (1.0 - theta) * dt)
# And copy the solution back to FEniCS
self.ode.to_dolfin() # numpy array (ODE solver) -> dolfin function
self.ode.ode_to_pde() # dolfin function in ODE space (quad?) -> CG1 dolfin function
self.pde.assign_previous()