Source code for beat.monodomain_model

from __future__ import annotations
import logging
from typing import Sequence
import dolfin

try:
    import ufl_legacy as ufl
    from ufl_legacy.core.expr import Expr
except ImportError:
    import ufl
    from ufl.core.expr import Expr

from .base_model import BaseModel, Stimulus

logger = logging.getLogger(__name__)


[docs] class MonodomainModel(BaseModel): r"""Solve .. math:: \frac{\partial V}{\partial t} - \nabla \cdot \left( M \nabla V \right) - I_{\mathrm{stim}} = 0 """ def __init__( self, time: dolfin.Constant, mesh: dolfin.Mesh, M: ufl.Coefficient | float, I_s: Stimulus | Sequence[Stimulus] | ufl.Coefficient | None = None, params=None, C_m: float = 1.0, ) -> None: self._M = M self.C_m = dolfin.Constant(C_m) super().__init__(mesh=mesh, time=time, params=params, I_s=I_s) def _setup_state_space(self) -> None: # Set-up function spaces k = self.parameters["degree"] family = self.parameters["family"] element = dolfin.FiniteElement( family=family, cell=self._mesh.ufl_cell(), degree=k, quad_scheme="default", ) self.V = dolfin.FunctionSpace(self._mesh, element) # Set-up solution fields: self.v_ = dolfin.Function(self.V, name="v_") self._state = dolfin.Function(self.V, name="v") @property def state(self) -> dolfin.Function: return self._state def assign_previous(self): self.v_.assign(self.state) @staticmethod def default_parameters(): params = super(MonodomainModel, MonodomainModel).default_parameters() params["use_custom_preconditioner"] = True return params
[docs] def variational_forms(self, k_n: Expr | float) -> tuple[ufl.Form, ufl.Form]: """Create the variational forms corresponding to the given discretization of the given system of equations. *Arguments* k_n (:py:class:`ufl.Expr` or float) The time step *Returns* (lhs, rhs, prec) (:py:class:`tuple` of :py:class:`ufl.Form`) """ theta = self.parameters["theta"] # Define variational formulation v = dolfin.TrialFunction(self.V) w = dolfin.TestFunction(self.V) # Set-up variational problem Dt_v_k_n = v - self.v_ v_mid = theta * v + (1.0 - theta) * self.v_ theta_parabolic = ufl.inner(self._M * ufl.grad(v_mid), ufl.grad(w)) G = (self.C_m * Dt_v_k_n * w + k_n * theta_parabolic) * dolfin.dx( domain=self._mesh ) - k_n * self.G_stim(w) # Define preconditioner based on educated(?) guess by Marie if self.parameters["use_custom_preconditioner"]: prec = (v * w + k_n / 2.0 * ufl.inner(self._M * ufl.grad(v), ufl.grad(w))) * dolfin.dx( domain=self._mesh ) else: prec = None return (G, prec)