Source code for beat.monodomain_model

from __future__ import annotations

import logging
from typing import Sequence

import basix
import dolfinx
import ufl
from ufl.core.expr import Expr

from .base_model import BaseModel
from .stimulation import 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: dolfinx.fem.Constant, mesh: dolfinx.mesh.Mesh, M: ufl.Coefficient | float, I_s: Stimulus | Sequence[Stimulus] | ufl.Coefficient | None = None, params=None, C_m: float = 1.0, dx: ufl.Measure | None = None, **kwargs, ) -> None: self._M = M self.C_m = dolfinx.fem.Constant(mesh, C_m) super().__init__(mesh=mesh, time=time, params=params, I_s=I_s, dx=dx, **kwargs) def _setup_state_space(self) -> None: # Set-up function spaces k = self.parameters["degree"] family = self.parameters["family"] element = basix.ufl.element(family=family, cell=self._mesh.basix_cell(), degree=k) self.V = dolfinx.fem.functionspace(self._mesh, element) # Set-up solution fields: self.v_ = dolfinx.fem.Function(self.V, name="v_") self._state = dolfinx.fem.Function(self.V, name="v") @property def state(self) -> dolfinx.fem.Function: return self._state def assign_previous(self): self.v_.x.array[:] = self.state.x.array[:] @staticmethod def default_parameters(): params = super(MonodomainModel, MonodomainModel).default_parameters() params["use_custom_preconditioner"] = True return params
[docs] def variational_forms(self, dt: Expr | float) -> tuple[ufl.Form, ufl.Form]: """Create the variational forms corresponding to the given discretization of the given system of equations. Parameters ---------- dt : float Time step size Returns ------- tuple[ufl.Form, ufl.Form] The variational form and the precondition """ theta = self.parameters["theta"] # Define variational formulation v = ufl.TrialFunction(self.V) w = ufl.TestFunction(self.V) # # Set-up variational problem Dt_v_dt = 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_dt * w + dt * theta_parabolic) * self.dx - dt * self._G_stim(w) a, L = ufl.system(G) return a, L