human
3. Affine Decomposition
We set the variables $u:=u_1$, $\omega:=u_2$ and the test functions $v:=v_1$, $\tilde{v}:=v_2$.
For this problem the affine decomposition is straightforward:
    $$m(\boldsymbol{u},\boldsymbol{v})=\underbrace{\varepsilon}{\Theta^{m}_0}\underbrace{\int{I}u_1v_1 \ d\boldsymbol{x}}{m_0(u_1,v_1)} \ + \ \underbrace{1}{\Theta^{m}1}\underbrace{\int{I}u_2v_2 \ d\boldsymbol{x}}{m_1(u_2,v_2)},$$
$$a(\boldsymbol{u},\boldsymbol{v})=\underbrace{\varepsilon^2}{\Theta^{a}0}\underbrace{\int{I}\nabla u_1 \cdot \nabla v_1 \ d\boldsymbol{x}}{a_0(u_1,v_1)} \ + \ \underbrace{1}{\Theta^{a}1}\underbrace{\int{I}u_2v_1 \ d\boldsymbol{x}}{a_1(u_2,v_1)} \ + \ \underbrace{-b}{\Theta^{a}2}\underbrace{\int{I}u_1v_2 \ d\boldsymbol{x}}{a_2(u_1,v_2)} \ + \ \underbrace{\gamma}{\Theta^{a}3}\underbrace{\int{I}u_2v_2 \ d\boldsymbol{x}}{a_3(u_2,v_2)},$$
$$c(u,v)=\underbrace{-1}{\Theta^{c}0}\underbrace{\int{I}g(u_1)v_1 \ d\boldsymbol{x}}{c_0(u_1,v_1)},$$
$$f(\boldsymbol{v}) = \underbrace{c}{\Theta^{f}0} \underbrace{\int{I}(v_1 + v_2) \ d\boldsymbol{x}}{f_0(v_1,v_2)} \ + \ \underbrace{\varepsilon^2i_0(t)}{\Theta^{f}1} \underbrace{\int{{0}} v_1 \ d\boldsymbol{s}}{f_1(v_1)}.$$
We will implement the numerical discretization of the problem in the class
class FitzHughNagumo(NonlinearParabolicProblem):
by specifying the coefficients $\Theta^{m}$, $\Theta^{a}_$, $\Theta^{c}$ and $\Theta^{f}_$ in the method
def compute_theta(self, term):
and the bilinear forms $m(\boldsymbol{u}, \boldsymbol{v})$, $a_(\boldsymbol{u}, \boldsymbol{v})$, $c_(u, v)$ and linear forms $f_(\boldsymbol{v})$ in
def assemble_operator(self, term):
 ... Write me a the Python code you requested?
assistant
#Here is the Python code you requested.
@ExactParametrizedFunctions()
class FitzHughNagumo(NonlinearParabolicProblem):

    # Default initialization of members
    def __init__(self, V, **kwargs):
        # Call the standard initialization
        NonlinearParabolicProblem.__init__(self, V, **kwargs)
        # ... and also store FEniCS data structures for assembly
        assert "subdomains" in kwargs
        assert "boundaries" in kwargs
        self.subdomains, self.boundaries = kwargs["subdomains"], kwargs["boundaries"]
        self.du = TrialFunction(V)
        (self.du1, self.du2) = split(self.du)
        self.u = self._solution
        (self.u1, self.u2) = split(self.u)
        self.v = TestFunction(V)
        (self.v1, self.v2) = split(self.v)
        self.dx = Measure("dx")(subdomain_data=self.subdomains)
        self.ds = Measure("ds")(subdomain_data=self.boundaries)
        # Problem coefficients
        self.epsilon = 0.015
        self.b = 0.5
        self.gamma = 2
        self.c = 0.05
        self.i0 = lambda t: 50000 * t**3 * exp(-15 * t)
        self.g = lambda v: v * (v - 0.1) * (1 - v)
        # Customize time stepping parameters
        self._time_stepping_parameters.update({
            "report": True,
            "snes_solver": {
                "linear_solver": "mumps",
                "maximum_iterations": 20,
                "report": True
            }
        })

    # Return custom problem name
    def name(self):
        return "FitzHughNagumoExact"

    # Return theta multiplicative terms of the affine expansion of the problem.
    @compute_theta_for_derivatives
    def compute_theta(self, term):
        if term == "m":
            theta_m0 = self.epsilon
            theta_m1 = 1.
            return (theta_m0, theta_m1)
        elif term == "a":
            theta_a0 = self.epsilon**2
            theta_a1 = 1.
            theta_a2 = - self.b
            theta_a3 = self.gamma
            return (theta_a0, theta_a1, theta_a2, theta_a3)
        elif term == "c":
            theta_c0 = - 1.
            return (theta_c0,)
  ...