def __init__(self, mesh, time, model, I_s=None, params=None): import ufl.classes # Store input self._mesh = mesh self._time = time self._model = model # Extract some information from cell model self._F = self._model.F self._I_ion = self._model.I self._num_states = self._model.num_states() self._I_s = handle_markerwise(I_s, GenericFunction) # Create time if not given, otherwise use given time if time is None: self._time = Constant(0.0) else: self._time = time # Initialize and update parameters if given self.parameters = self.default_parameters() if params is not None: self.parameters.update(params) # Create (vector) function space for potential + states self.VS = VectorFunctionSpace(self._mesh, "CG", 1, dim=self._num_states + 1) # Initialize solution field self.vs_ = Function(self.VS, name="vs_") self.vs = Function(self.VS, name="vs") # Initialize scheme (v, s) = splat(self.vs, self._num_states + 1) (w, q) = splat(TestFunction(self.VS), self._num_states + 1) # Workaround to get algorithm in RL schemes working as it only # works for scalar expressions F_exprs = self._F(v, s, self._time) # MER: This looks much more complicated than it needs to be! # If we have a as_vector expression F_exprs_q = zero() if isinstance(F_exprs, ufl.classes.ListTensor): #for i, expr_i in enumerate(F_exprs.operands()): for i, expr_i in enumerate(F_exprs.ufl_operands): F_exprs_q += expr_i * q[i] else: F_exprs_q = F_exprs * q rhs = F_exprs_q - self._I_ion(v, s, self._time) * w # Handle stimulus: only handle single function case for now msg = "Markerwise stimulus not supported by PointIntegralSolver." assert (not isinstance(self._I_s, Markerwise)), msg if self._I_s: rhs += self._I_s * w # FIXME: The application of dP was moved so adding an integral # is done just once. Otherwise ufl could not figure out that # we had only one integral... self._rhs = rhs * dP() #sys.exit() name = self.parameters["scheme"] Scheme = self._name_to_scheme(name) self._scheme = Scheme(self._rhs, self.vs, self._time) # Figure out whether we should annotate or not self._annotate_kwargs = annotate_kwargs(self.parameters) # Initialize solver and update its parameters self._pi_solver = PointIntegralSolver(self._scheme) self._pi_solver.parameters.update( self.parameters["point_integral_solver"])
def step(self, interval): """ Solve on the given time step (t0, t1). End users are recommended to use solve instead. *Arguments* interval (:py:class:`tuple`) The time interval (t0, t1) for the step """ timer = Timer("ODE step") # Check for cell meshs dim = self._mesh.topology().dim() # Extract time mesh (t0, t1) = interval k_n = Constant(t1 - t0) # Extract previous solution(s) (v_, s_) = splat(self.vs_, self._num_states + 1) # Set-up current variables self.vs.assign(self.vs_) # Start with good guess (v, s) = splat(self.vs, self._num_states + 1) (w, r) = splat(TestFunction(self.VS), self._num_states + 1) # Define equation based on cell model Dt_v = (v - v_) / k_n Dt_s = (s - s_) / k_n theta = self.parameters["theta"] # Set time (propagates to time-dependent variables defined via # self.time) t = t0 + theta * (t1 - t0) self.time.assign(t) v_mid = theta * v + (1.0 - theta) * v_ s_mid = theta * s + (1.0 - theta) * s_ if isinstance(self._model, MultiCellModel): #assert(model.mesh() == self._mesh) model = self._model mesh = model.mesh() dy = Measure("dx", domain=mesh, subdomain_data=model.markers()) # Only allowing trivial forcing functions here if isinstance(self._I_s, Markerwise): error("Not implemented") rhs = self._I_s * w * dy() n = model.num_states() # Extract number of global states # Collect contributions to lhs by iterating over the different cell models domains = self._model.keys() lhs = list() for (k, model_k) in enumerate(model.models()): n_k = model_k.num_states( ) # Extract number of local (non-trivial) states # Extract right components of coefficients and test functions # () is not the same as (1,) if n_k == 1: s_mid_k = s_mid[0] r_k = r[0] Dt_s_k = Dt_s[0] else: s_mid_k = as_vector(tuple(s_mid[j] for j in range(n_k))) r_k = as_vector(tuple(r[j] for j in range(n_k))) Dt_s_k = as_vector(tuple(Dt_s[j] for j in range(n_k))) i_k = domains[k] # Extract domain index of cell model k # Extract right currents and ion channel expressions F_theta_k = self._F(v_mid, s_mid_k, time=self.time, index=i_k) I_theta_k = -self._I_ion( v_mid, s_mid_k, time=self.time, index=i_k) # Variational contribution over the relevant domain a_k = ((Dt_v - I_theta_k) * w + inner(Dt_s_k, r_k) + inner(-F_theta_k, r_k)) * dy(i_k) # Add s_trivial = 0 on Omega_{i_k} in variational form: a_k += sum(s[j] * r[j] for j in range(n_k, n)) * dy(i_k) lhs.append(a_k) lhs = sum(lhs) else: (dz, rhs) = rhs_with_markerwise_field(self._I_s, self._mesh, w) # Evaluate currents at averaged v and s. Note sign for I_theta F_theta = self._F(v_mid, s_mid, time=self.time) I_theta = -self._I_ion(v_mid, s_mid, time=self.time) lhs = (Dt_v - I_theta) * w * dz + inner(Dt_s - F_theta, r) * dz # Set-up system of equations G = lhs - rhs # Solve system pde = NonlinearVariationalProblem(G, self.vs, J=derivative(G, self.vs)) solver = NonlinearVariationalSolver(pde) solver_params = self.parameters["nonlinear_variational_solver"] solver.parameters.update(solver_params) solver.solve() timer.stop()