Beispiel #1
0
    def _run_solve(self, model, time, theta):
        "Run two time steps for the given model with the given theta solver."
        dt = 0.01
        T = 2 * dt
        interval = (0.0, T)

        # Initialize solver
        params = BasicSingleCellSolver.default_parameters()
        params["theta"] = theta

        params["enable_adjoint"] = False
        solver = BasicSingleCellSolver(model, time, params=params)

        # Assign initial conditions
        (vs_, vs) = solver.solution_fields()
        vs_.assign(model.initial_conditions())

        # Solve for a couple of steps
        solutions = solver.solve(interval, dt)
        for ((t0, t1), vs) in solutions:
            pass

        # Check that we are at the end time
        assert_almost_equal(t1, T, 1e-10)
        return vs.vector()
Beispiel #2
0
    def test_compare_direct_iterative(self):
        "Test that direct and iterative solution give comparable results."
        self.setUp()

        # Create solver and solve
        params = MonodomainSolver.default_parameters()
        params["linear_solver_type"] = "direct"
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  params=params)
        solutions = solver.solve((self.t0, self.t0 + 3 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, v) = fields
            a = v.vector().norm("l2")

        # Create solver and solve using iterative means
        params = MonodomainSolver.default_parameters()
        params["linear_solver_type"] = "iterative"
        params["krylov_solver"]["monitor_convergence"] = True
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  params=params)
        solutions = solver.solve((self.t0, self.t0 + 3 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, v) = fields
            b = v.vector().norm("l2")

        print "lu gives ", a
        print "krylov gives ", b
        assert_almost_equal(a, b, 1e-4)
Beispiel #3
0
    def test_default_basic_single_cell_solver(self, cell_model, theta):
        "Test basic single cell solver."
        time = Constant(0.0)
        model = cell_model
        Model = cell_model.__class__

        if Model == supported_cell_models[3] and theta > 0:
            pytest.xfail("failing configuration (but should work)")

        model.stimulus = Expression("1000*t", t=time, degree=1)

        info_green("\nTesting %s" % model)
        vec_solve = self._run_solve(model, time, theta)

        if Model == supported_cell_models[3] and theta == 0:
            pytest.xfail("failing configuration (but should work)")

        if Model in self.references and theta in self.references[Model]:
            ind, ref_value = self.references[Model][theta]
            print("vec_solve", vec_solve.get_local())
            print("ind", ind, "ref", ref_value)

            assert_almost_equal(vec_solve[ind], ref_value, 1e-10)
        else:
            info_red("Missing references for %r, %r" % (Model, theta))
Beispiel #4
0
    def test_compare_with_basic_solve(self):
        """Test that solver with direct linear algebra gives same
        results as basic monodomain solver."""
        self.setUp()

        # Create solver and solve
        params = MonodomainSolver.default_parameters()
        params["linear_solver_type"] = "direct"
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  params=params)
        solutions = solver.solve((self.t0, self.t0 + 2 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vur) = fields
        monodomain_result = vur.vector().norm("l2")

        # Create other solver and solve
        solver = BasicMonodomainSolver(self.mesh,
                                       self.time,
                                       self.M_i,
                                       I_s=self.stimulus)
        solutions = solver.solve((self.t0, self.t0 + 2 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vur) = fields
        basic_monodomain_result = vur.vector().norm("l2")

        print "monodomain_result = ", monodomain_result
        print "basic_monodomain_result = ", basic_monodomain_result
        assert_almost_equal(monodomain_result, basic_monodomain_result, 1e-13)
def test_ode_pde(theta, pde_solver):
    "Test that the ode-pde coupling reproduces the ode solution."

    params = SingleCellSolver.default_parameters()
    params["scheme"] = "RK4"
    time = Constant(0.0)
    stimulus = Expression("100", degree=1)
    model = FitzHughNagumoManual()
    model.stimulus = stimulus

    dt = 0.1
    T = 10 * dt

    # Just propagate ODE solver
    solver = SingleCellSolver(model, time, params)
    ode_vs_, ode_vs = solver.solution_fields()
    ode_vs_.assign(model.initial_conditions())
    for _ in solver.solve((0, T), dt):
        pass

    print("ODE")
    print("v(T) = ", ode_vs.vector().get_local()[0])
    print("s(T) = ", ode_vs.vector().get_local()[1])

    # Propagate with Bidomain+ODE solver
    mesh = UnitSquareMesh(1, 1)
    brain = CardiacModel(mesh, time, 1.0, 1.0, model, stimulus=stimulus)
    ps = SplittingSolver.default_parameters()
    ps["pde_solver"] = pde_solver
    ps["theta"] = float(theta)
    ps["CardiacODESolver"]["scheme"] = "RK4"
    ps["enable_adjoint"] = False
    solver = SplittingSolver(brain, params=ps)

    pde_vs_, pde_vs, vur = solver.solution_fields()
    pde_vs_.assign(model.initial_conditions())

    solutions = solver.solve((0, T), dt)
    for _ in solutions:
        pass

    n = mesh.num_vertices()
    print("PDE (%s, %g)" % (pde_solver, theta))
    print("v(T) = ", [pde_vs.vector().get_local()[2 * i] for i in range(n)])
    print("s(T) = ",
          [pde_vs.vector().get_local()[2 * i + 1] for i in range(n)])

    pde_vec = pde_vs.vector().get_local()
    ode_vec = ode_vs.vector().get_local()

    # Compare PDE and ODE solutions, we expect these to be essentially equal
    tolerance = 1.e-3
    print(abs(pde_vec[0] - ode_vec[0]))
    print(abs(pde_vec[1] - ode_vec[1]))
    assert_almost_equal(abs(pde_vec[0] - ode_vec[0]), 0.0, tolerance)
    assert_almost_equal(abs(pde_vec[1] - ode_vec[1]), 0.0, tolerance)
    print()
Beispiel #6
0
    def compare_against_reference(self, sol, Model, Scheme):
        ''' Compare the model solution with the reference solution. '''
        try:
            ind, ref_value = self.references[Model][Scheme]
        except KeyError:
            info_red("Missing references for %s, %s: value is %g" %
                     (Model, Scheme, sol[0]))
            return

        info("Value for %s, %s is %g" % (Model, Scheme, sol[ind]))
        if ref_value != "nan":
            assert_almost_equal(float(sol[ind]),
                                float(ref_value),
                                tolerance=1e-6)
Beispiel #7
0
    def test_compare_with_basic_solve(self) -> None:
        """Test that direct solver gives same result as basic solver."""
        self.setUp()

        # Create solver and solve
        parameters = BidomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        parameters["use_avg_u_constraint"] = True
        parameters["Chi"] = 1.0
        parameters["Cm"] = 1.0
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                parameters=parameters)

        for _, (v_, vur) in solver.solve(self.t0, self.t0 + 2 * self.dt,
                                         self.dt):
            pass

        bidomain_result = vur.vector().norm("l2")

        parameters = BasicBidomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        parameters["use_avg_u_constraint"] = True
        parameters["Chi"] = 1.0
        parameters["Cm"] = 1.0

        # Create other solver and solve
        solver = BasicBidomainSolver(self.mesh,
                                     self.time,
                                     self.M_i,
                                     self.M_e,
                                     I_s=self.stimulus,
                                     I_a=self.applied_current,
                                     parameters=parameters)

        for _, (v_, vur) in solver.solve(self.t0, self.t0 + 2 * self.dt,
                                         self.dt):
            pass

        basic_bidomain_result = vur.vector().norm("l2")

        print(bidomain_result)
        print(basic_bidomain_result)
        assert_almost_equal(bidomain_result, basic_bidomain_result, 1e-13)
def test_fitzhugh():

    parameters["reorder_dofs_serial"] = False
    parameters["form_compiler"]["cpp_optimize"] = True
    parameters["form_compiler"]["optimize"] = True

    # Set-up cardiac model
    heart = setup_model()

    # Set-up solver
    ps = BasicSplittingSolver.default_parameters()
    ps["BasicBidomainSolver"]["linear_variational_solver"][
        "linear_solver"] = "direct"
    ps["BasicBidomainSolver"]["theta"] = 1.0
    ps["theta"] = 1.0
    ps["BasicCardiacODESolver"]["S_polynomial_family"] = "CG"
    ps["BasicCardiacODESolver"]["S_polynomial_degree"] = 1
    ps["BasicCardiacODESolver"]["V_polynomial_family"] = "CG"
    ps["BasicCardiacODESolver"]["V_polynomial_degree"] = 1
    solver = BasicSplittingSolver(heart, ps)

    # Define end-time and (constant) timesteps
    dt = 0.25  # mS
    T = 1.0  # + 1.e-6  # mS

    # Define initial condition(s)
    ic = InitialCondition(degree=1)  # Should use degree=0 here for
    # correctness, but to match
    # reference, using 1
    vs0 = project(ic, solver.VS)
    (vs_, vs, u) = solver.solution_fields()
    vs_.assign(vs0)

    # Solve
    solutions = solver.solve((0, T), dt)
    for (timestep, (vs_, vs, vur)) in solutions:
        continue

    u = project(vur[1], vur.function_space().sub(1).collapse())
    norm_u = norm(u)
    reference = 10.3756526773
    print(("norm_u = ", norm_u))
    print(("reference = ", reference))

    assert_almost_equal(reference, norm_u, 1.e-4)
Beispiel #9
0
    def test_compare_direct_iterative(self) -> None:
        """Test that direct and iterative solution give comparable results."""
        self.setUp()

        # Create solver and solve
        parameters = BidomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        parameters["use_avg_u_constraint"] = False
        parameters["Cm"] = 1.0
        parameters["Chi"] = 1.0
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                parameters=parameters)

        for _, (v_, vur), in solver.solve(self.t0, self.t0 + 3 * self.dt,
                                          self.dt):
            v, *_ = vur.split(deepcopy=True)
            a = v.vector().norm("l2")

        # Create solver and solve using iterative means
        parameters = BidomainSolver.default_parameters()
        parameters["linear_solver_type"] = "iterative"
        parameters["use_avg_u_constraint"] = False
        parameters["Cm"] = 1.0
        parameters["Chi"] = 1.0
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                parameters=parameters)

        for _, (v_, vur) in solver.solve(self.t0, self.t0 + 3 * self.dt,
                                         self.dt):
            v, *_ = vur.split(deepcopy=True)
            b = v.vector().norm("l2")

        print("lu gives ", a)
        print("krylov gives ", b)
        assert_almost_equal(a, b, 1e-4)
Beispiel #10
0
    def test_basic_and_optimised_splitting_solver_exact(self,
                                                        solver_type) -> None:
        """
        Test that the optimised and basic solvers yield similar results.
        """
        # Create basic solver
        parameters = BasicSplittingSolver.default_parameters()
        parameters["BasicBidomainSolver"]["linear_solver_type"] = solver_type
        parameters["BasicBidomainSolver"]["Chi"] = 1.0
        parameters["BasicBidomainSolver"]["Cm"] = 1.0
        parameters["theta"] = 0.5
        if solver_type == "direct":
            parameters["BasicBidomainSolver"]["use_avg_u_constraint"] = True
        else:
            parameters["BasicBidomainSolver"]["use_avg_u_constraint"] = False

        solver = BasicSplittingSolver(self.cardiac_model,
                                      parameters=parameters)

        vs_, vs, vur = solver.solution_fields()
        vs_.assign(self.ics)

        # Solve
        solutions = solver.solve(self.t0, self.T, self.dt)
        for (t0, t1), fields in solutions:
            vs_, vs, vur = fields

        foo = vs.vector()
        basic_vs = vs.vector().norm("l2")
        basic_vur = vur.vector().norm("l2")
        assert_almost_equal(t1, self.T, 1e-10)

        # Create optimised solver with direct solution algorithm
        parameters = SplittingSolver.default_parameters()
        parameters["BidomainSolver"]["linear_solver_type"] = solver_type
        parameters["BidomainSolver"]["Cm"] = 1.0
        parameters["BidomainSolver"]["Chi"] = 1.0
        solver = SplittingSolver(self.cardiac_model, parameters=parameters)

        vs_, vs, vur = solver.solution_fields()
        vs_.assign(self.ics)

        # Solve again
        solutions = solver.solve(self.t0, self.T, self.dt)
        for (t0, t1), fields in solutions:
            vs_, vs, vur = fields

        assert_almost_equal(t1, self.T, 1e-10)
        optimised_vs = vs.vector().norm("l2")
        optimised_vur = vur.vector().norm("l2")

        # Compare results, discrepancy is in difference in ODE solves.
        assert_almost_equal(optimised_vs, basic_vs, tolerance=1)
        assert_almost_equal(optimised_vur, basic_vur, tolerance=1)
Beispiel #11
0
    def test_basic_and_optimised_splitting_solver_exact(self, solver_type):
        """Test that basic and optimised splitting solvers yield
        very comparative results when configured identically."""

        # Create basic solver
        params = BasicSplittingSolver.default_parameters()
        params["BasicCardiacODESolver"]["S_polynomial_family"] = "CG"
        params["BasicCardiacODESolver"]["S_polynomial_degree"] = 1
        solver = BasicSplittingSolver(self.cardiac_model, params=params)

        (vs_, vs, vur) = solver.solution_fields()
        vs_.assign(self.ics)

        # Solve
        solutions = solver.solve((self.t0, self.T), self.dt)
        for (interval, fields) in solutions:
            (vs_, vs, vur) = fields
        a = vs.vector().norm("l2")
        c = vur.vector().norm("l2")
        assert_almost_equal(interval[1], self.T, 1e-10)

        if dolfin_adjoint:
            adj_reset()

        # Create optimised solver with direct solution algorithm
        params = SplittingSolver.default_parameters()
        params["BidomainSolver"]["linear_solver_type"] = solver_type
        params["enable_adjoint"] = False
        if solver_type == "direct":
            params["BidomainSolver"]["use_avg_u_constraint"] = True
        solver = SplittingSolver(self.cardiac_model, params=params)

        (vs_, vs, vur) = solver.solution_fields()
        vs_.assign(self.ics)

        # Solve again
        solutions = solver.solve((self.t0, self.T), self.dt)
        for (interval, fields) in solutions:
            (vs_, vs, vur) = fields
        assert_almost_equal(interval[1], self.T, 1e-10)
        b = vs.vector().norm("l2")
        d = vur.vector().norm("l2")

        print("a, b = ", a, b)
        print("c, d = ", c, d)
        print("a - b = ", (a - b))
        print("c - d = ", (c - d))

        # Compare results, discrepancy is in difference in ODE
        # solves.
        assert_almost_equal(a, b, tolerance=1.)
        assert_almost_equal(c, d, tolerance=1.)
Beispiel #12
0
    def test_point_integral_solver(self, cell_model):
        "Compare form compilation result with and without optimizations."

        parameters["form_compiler"]["representation"] = "quadrature"
        parameters["form_compiler"]["quadrature_degree"] = 2
        tolerance = 1.e-12

        # Run with no particular optimizations
        vs = self.point_integral_step(cell_model)
        non_opt_result = vs.vector().array()

        # Compare with results using aggresssive optimizations
        flags = "-O3 -ffast-math -march=native"
        parameters["form_compiler"]["cpp_optimize"] = True
        parameters["form_compiler"]["cpp_optimize_flags"] = flags
        vs = self.point_integral_step(cell_model)
        assert_almost_equal(non_opt_result, vs.vector().array(), tolerance)

        # Compare with results using standard optimizations
        parameters["form_compiler"]["cpp_optimize"] = True
        parameters["form_compiler"]["cpp_optimize_flags"] = "-O2"
        vs = self.point_integral_step(cell_model)
        assert_almost_equal(non_opt_result, vs.vector().array(), tolerance)

        # Compare with results using uflacs if installed
        try:
            parameters["form_compiler"]["representation"] = "uflacs"
            vs = self.point_integral_step(cell_model)
            assert_almost_equal(non_opt_result, vs.vector().array(), tolerance)
        except:
            pass

        # Reset parameters
        parameters["form_compiler"]["representation"] = "auto"
Beispiel #13
0
    def test_compare_direct_iterative(self):
        "Test that direct and iterative solution give comparable results."
        self.setUp()

        # Create solver and solve
        params = BidomainSolver.default_parameters()
        params["linear_solver_type"] = "direct"
        params["use_avg_u_constraint"] = True
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                params=params)
        solutions = solver.solve((self.t0, self.t0 + 3 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vur) = fields
            (v, u, r) = vur.split(deepcopy=True)
            a = v.vector().norm("l2")

        # Create solver and solve using iterative means
        params = BidomainSolver.default_parameters()
        params["petsc_krylov_solver"]["monitor_convergence"] = True
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                params=params)
        solutions = solver.solve((self.t0, self.t0 + 3 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vu) = fields
            (v, u) = vu.split(deepcopy=True)
            b = v.vector().norm("l2")

        print "lu gives ", a
        print "krylov gives ", b
        assert_almost_equal(a, b, 1e-4)
Beispiel #14
0
    def test_compare_with_basic_solve(self):
        """Test that solver with direct linear algebra gives same
        results as basic bidomain solver."""
        self.setUp()

        # Create solver and solve
        params = BidomainSolver.default_parameters()
        params["linear_solver_type"] = "direct"
        params["use_avg_u_constraint"] = True
        solver = BidomainSolver(self.mesh,
                                self.time,
                                self.M_i,
                                self.M_e,
                                I_s=self.stimulus,
                                I_a=self.applied_current,
                                params=params)
        solutions = solver.solve((self.t0, self.t0 + 2 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vur) = fields
        bidomain_result = vur.vector().norm("l2")

        # Create other solver and solve
        solver = BasicBidomainSolver(self.mesh,
                                     self.time,
                                     self.M_i,
                                     self.M_e,
                                     I_s=self.stimulus,
                                     I_a=self.applied_current)
        solutions = solver.solve((self.t0, self.t0 + 2 * self.dt), self.dt)
        for (interval, fields) in solutions:
            (v_, vur) = fields
        basic_bidomain_result = vur.vector().norm("l2")

        print bidomain_result
        print basic_bidomain_result
        assert_almost_equal(bidomain_result, basic_bidomain_result, 1e-13)
Beispiel #15
0
    def test_compare_with_basic_solve(self) -> None:
        """Test thant the optimised and non optimised solvers give the same answer."""
        self.setUp()

        # Create solver and solve
        parameters = MonodomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  parameters=parameters)

        for _, (v_, vur) in solver.solve(self.t0, self.t0 + 2 * self.dt,
                                         self.dt):
            pass
        monodomain_result = vur.vector()

        # Create other solver and solve
        parameters = BasicMonodomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        solver = BasicMonodomainSolver(self.mesh,
                                       self.time,
                                       self.M_i,
                                       I_s=self.stimulus,
                                       parameters=parameters)

        for _, (v_, vur) in solver.solve(self.t0, self.t0 + 2 * self.dt,
                                         self.dt):
            pass
        basic_monodomain_result = vur.vector()

        # print("monodomain_result = ", monodomain_result.array())
        # print("basic_monodomain_result = ", basic_monodomain_result.array())
        assert_almost_equal(monodomain_result.norm("l2"),
                            basic_monodomain_result.norm("l2"), 1e-13)
Beispiel #16
0
    def test_compare_direct_iterative(self) -> None:
        """Test that direct and iterative solution give comparable results."""
        self.setUp()

        # Create solver and solve
        parameters = MonodomainSolver.default_parameters()
        parameters["linear_solver_type"] = "direct"
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  parameters=parameters)

        for _, (v_, v) in solver.solve(self.t0, self.t0 + 3 * self.dt,
                                       self.dt):
            pass
        a = v.vector().norm("l2")

        # Create solver and solve using iterative means
        parameters = MonodomainSolver.default_parameters()
        parameters["linear_solver_type"] = "iterative"
        # parameters["krylov_solver"]["monitor_convergence"] = True
        solver = MonodomainSolver(self.mesh,
                                  self.time,
                                  self.M_i,
                                  I_s=self.stimulus,
                                  parameters=parameters)

        for _, (v_, v) in solver.solve(self.t0, self.t0 + 3 * self.dt,
                                       self.dt):
            pass
        b = v.vector().norm("l2")

        print("lu gives ", a)
        print("krylov gives ", b)
        assert_almost_equal(a, b, 1e-4)
Beispiel #17
0
    def long_run_compare(self):

        mesh = UnitIntervalMesh(5)

        # FIXME: We need to make this run in paralell.
        if MPI.size(mesh.mpi_comm()) > 1:
            return

        Model = Tentusscher_2004_mcell
        tstop = 10
        ind_V = 0
        dt_ref = 0.1
        time_ref = np.linspace(0, tstop, int(tstop / dt_ref) + 1)
        dir_path = os.path.dirname(__file__)
        Vm_reference = np.fromfile(os.path.join(dir_path, "Vm_reference.npy"))
        params = Model.default_parameters()

        time = Constant(0.0)
        stim = Expression("(time >= stim_start) && (time < stim_start + stim_duration)"\
                             " ? stim_amplitude : 0.0 ", time=time, stim_amplitude=52.0, \
                             stim_start=1.0, stim_duration=1.0, degree=1)

        # Initiate solver, with model and Scheme
        if dolfin_adjoint:
            adj_reset()

        solver = self._setup_solver(Model, Scheme, mesh, time, stim, params)
        solver._pi_solver.parameters["newton_solver"][
            "relative_tolerance"] = 1e-8
        solver._pi_solver.parameters["newton_solver"][
            "maximum_iterations"] = 30
        solver._pi_solver.parameters["newton_solver"]["report"] = False

        scheme = solver._scheme
        (vs_, vs) = solver.solution_fields()

        vs.assign(vs_)

        dof_to_vertex_map_values = dof_to_vertex_map(vs.function_space())
        scheme.t().assign(0.0)

        vs_array = np.zeros(mesh.num_vertices()*\
                            vs.function_space().dofmap().num_entity_dofs(0))
        vs_array[dof_to_vertex_map_values] = vs.vector().get_local()
        output = [vs_array[ind_V]]
        time_output = [0.0]
        dt = dt_org

        # Time step
        next_dt = max(min(tstop - float(scheme.t()), dt), 0.0)
        t0 = 0.0

        while next_dt > 0.0:

            # Step solver
            solver.step((t0, t0 + next_dt))
            vs_.assign(vs)

            # Collect plt output data
            vs_array[dof_to_vertex_map_values] = vs.vector().get_local()
            output.append(vs_array[ind_V])
            time_output.append(float(scheme.t()))

            # Next time step
            t0 += next_dt
            next_dt = max(min(tstop - float(scheme.t()), dt), 0.0)

        # Compare solution from CellML run using opencell
        assert_almost_equal(output[-1], Vm_reference[-1], abs_tol)

        output = np.array(output)
        time_output = np.array(time_output)

        output = np.interp(time_ref, time_output, output)

        value = np.sqrt(np.sum(
            ((Vm_reference - output) / Vm_reference)**2)) / len(Vm_reference)
        assert_almost_equal(value, 0.0, rel_tol)