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()
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)
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))
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()
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)
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)
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)
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)
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.)
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"
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)
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)
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)
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)
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)