def test_ode_integrate_with_event(self): # Constant solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def constant_decay(t, y): return -2 * np.ones_like(y) def y_equal_0(t, y): return y[0] y0 = np.array([1]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(constant_decay, y0, t_eval, events=[y_equal_0]) np.testing.assert_allclose(1 - 2 * solution.t, solution.y[0]) self.assertLess(len(solution.t), len(t_eval)) np.testing.assert_array_less(0, solution.y[0]) np.testing.assert_array_less(solution.t, 0.5) np.testing.assert_allclose(solution.t_event, 0.5) np.testing.assert_allclose(solution.y_event, 0) self.assertEqual(solution.termination, "event") # Expnonential growth solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def exponential_growth(t, y): return y def y_eq_9(t, y): return y - 9 def ysq_eq_7(t, y): return y**2 - 7 y0 = np.array([1]) t_eval = np.linspace(0, 3, 100) solution = solver.integrate(exponential_growth, y0, t_eval, events=[ysq_eq_7, y_eq_9]) self.assertLess(len(solution.t), len(t_eval)) np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) np.testing.assert_array_less(solution.y, 9) np.testing.assert_array_less(solution.y**2, 7) np.testing.assert_allclose(solution.t_event, np.log(7) / 2, rtol=1e-4) np.testing.assert_allclose(solution.y_event**2, 7, rtol=1e-4) self.assertEqual(solution.termination, "event")
def test_solver_citations(self): # Test that solving each solver adds the right citations citations = pybamm.citations citations._reset() self.assertNotIn("virtanen2020scipy", citations._papers_to_cite) pybamm.ScipySolver() self.assertIn("virtanen2020scipy", citations._papers_to_cite) citations._reset() self.assertNotIn("virtanen2020scipy", citations._papers_to_cite) pybamm.AlgebraicSolver() self.assertIn("virtanen2020scipy", citations._papers_to_cite) if pybamm.have_scikits_odes(): citations._reset() self.assertNotIn("scikits-odes", citations._papers_to_cite) pybamm.ScikitsOdeSolver() self.assertIn("scikits-odes", citations._papers_to_cite) citations._reset() self.assertNotIn("scikits-odes", citations._papers_to_cite) pybamm.ScikitsDaeSolver() self.assertIn("scikits-odes", citations._papers_to_cite) if pybamm.have_idaklu(): citations._reset() self.assertNotIn("hindmarsh2005sundials", citations._papers_to_cite) pybamm.IDAKLUSolver() self.assertIn("hindmarsh2005sundials", citations._papers_to_cite)
def test_model_step_ode_python(self): model = pybamm.BaseModel() model.convert_to_format = "python" whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) model.rhs = {var: -0.1 * var} model.initial_conditions = {var: 1} disc = get_discretisation_for_testing() disc.process_model(model) solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) # Step once dt = 1 step_sol = solver.step(None, model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) np.testing.assert_allclose(step_sol.y[0], np.exp(-0.1 * step_sol.t)) # Step again (return 5 points) step_sol_2 = solver.step(step_sol, model, dt, npts=5) np.testing.assert_array_equal( step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)])) np.testing.assert_allclose(step_sol_2.y[0], np.exp(-0.1 * step_sol_2.t)) # Check steps give same solution as solve t_eval = step_sol.t solution = solver.solve(model, t_eval) np.testing.assert_allclose(solution.y[0], step_sol.y[0])
def test_model_solver_ode_events_casadi(self): # Create model model = pybamm.BaseModel() model.convert_to_format = "casadi" whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} model.events = [ pybamm.Event("2 * var = 2.5", pybamm.min(2 * var - 2.5)), pybamm.Event("var = 1.5", pybamm.min(var - 1.5)), ] disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 10, 100) solution = solver.solve(model, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) np.testing.assert_array_less(solution.y[0:, -1], 1.5) np.testing.assert_array_less(solution.y[0:, -1], 1.25 + 1e-6) np.testing.assert_equal(solution.t_event[0], solution.t[-1]) np.testing.assert_array_equal(solution.y_event[:, 0], solution.y[:, -1])
def test_model_step_ode(self): # Create model model = pybamm.BaseModel() whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} disc = get_discretisation_for_testing() disc.process_model(model) solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) # Step once dt = 0.1 step_sol = solver.step(model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) # Step again (return 5 points) step_sol_2 = solver.step(model, dt, npts=5) np.testing.assert_array_equal(step_sol_2.t, np.linspace(dt, 2 * dt, 5)) np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t)) # Check steps give same solution as solve t_eval = np.concatenate((step_sol.t, step_sol_2.t[1:])) solution = solver.solve(model, t_eval) concatenated_steps = np.concatenate((step_sol.y[0], step_sol_2.y[0, 1:])) np.testing.assert_allclose(solution.y[0], concatenated_steps)
def test_model_solver_ode_jacobian_python(self): model = pybamm.BaseModel() model.convert_to_format = "python" whole_cell = ["negative electrode", "separator", "positive electrode"] var1 = pybamm.Variable("var1", domain=whole_cell) var2 = pybamm.Variable("var2", domain=whole_cell) model.rhs = {var1: var1, var2: 1 - var1} model.initial_conditions = {var1: 1.0, var2: -1.0} model.variables = {"var1": var1, "var2": var2} disc = get_discretisation_for_testing() disc.process_model(model) # Add user-supplied Jacobian to model mesh = get_mesh_for_testing() combined_submesh = mesh.combine_submeshes( "negative electrode", "separator", "positive electrode" ) N = combined_submesh[0].npts # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 1, 100) solution = solver.solve(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) T, Y = solution.t, solution.y np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(T, Y), np.ones((N, T.size)) * np.exp(T[np.newaxis, :]), ) np.testing.assert_array_almost_equal( model.variables["var2"].evaluate(T, Y), np.ones((N, T.size)) * (T[np.newaxis, :] - np.exp(T[np.newaxis, :])), )
def test_ode_solver_fail_with_dae(self): model = pybamm.BaseModel() a = pybamm.Scalar(1) model.algebraic = {a: a} model.concatenated_initial_conditions = a solver = pybamm.ScikitsOdeSolver() with self.assertRaisesRegex(pybamm.SolverError, "Cannot use ODE solver"): solver.set_up(model)
def test_model_solver_ode_jacobian(self): # Create model model = pybamm.BaseModel() whole_cell = ["negative electrode", "separator", "positive electrode"] var1 = pybamm.Variable("var1", domain=whole_cell) var2 = pybamm.Variable("var2", domain=whole_cell) model.rhs = {var1: var1, var2: 1 - var1} model.initial_conditions = {var1: 1.0, var2: -1.0} model.variables = {"var1": var1, "var2": var2} disc = get_discretisation_for_testing() disc.process_model(model) # Add user-supplied Jacobian to model mesh = get_mesh_for_testing() combined_submesh = mesh.combine_submeshes("negative electrode", "separator", "positive electrode") N = combined_submesh[0].npts # construct jacobian in order of model.rhs J = [] for var in model.rhs.keys(): if var.id == var1.id: J.append([np.eye(N), np.zeros((N, N))]) else: J.append([-1.0 * np.eye(N), np.zeros((N, N))]) J = np.block(J) def jacobian(t, y): return J model.jacobian = jacobian # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 1, 100) solution = solver.solve(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) T, Y = solution.t, solution.y np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(T, Y), np.ones((N, T.size)) * np.exp(T[np.newaxis, :]), ) np.testing.assert_array_almost_equal( model.variables["var2"].evaluate(T, Y), np.ones( (N, T.size)) * (T[np.newaxis, :] - np.exp(T[np.newaxis, :])), )
def test_ode_integrate(self): # Constant solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def constant_growth(t, y): return 0.5 * np.ones_like(y) y0 = np.array([0]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(constant_growth, y0, t_eval) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) # Exponential decay solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def exponential_decay(t, y): return -0.1 * y y0 = np.array([1]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(exponential_decay, y0, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) self.assertEqual(solution.termination, "final time")
def test_model_solver_ode_python(self): model = pybamm.BaseModel() model.convert_to_format = "python" whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 1, 100) solution = solver.solve(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
def test_ode_integrate_failure(self): # Turn off warnings to ignore sqrt error warnings.simplefilter("ignore") def sqrt_decay(t, y): return -np.sqrt(y) y0 = np.array([1]) t_eval = np.linspace(0, 3, 100) solver = pybamm.ScikitsOdeSolver() # Expect solver to fail when y goes negative with self.assertRaises(pybamm.SolverError): solver.integrate(sqrt_decay, y0, t_eval) # Turn warnings back on warnings.simplefilter("default")
def test_model_ode_integrate_failure(self): # Turn off warnings to ignore sqrt error warnings.simplefilter("ignore") model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: -pybamm.sqrt(var)} model.initial_conditions = {var: 1} disc = pybamm.Discretisation() disc.process_model(model) t_eval = np.linspace(0, 3, 100) solver = pybamm.ScikitsOdeSolver() # Expect solver to fail when y goes negative with self.assertRaises(pybamm.SolverError): solver.solve(model, t_eval) # Turn warnings back on warnings.simplefilter("default")
def test_model_solver_ode_events(self): # Create model model = pybamm.BaseModel() whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} model.events = { "2 * var = 2.5": pybamm.min(2 * var - 2.5), "var = 1.5": pybamm.min(var - 1.5), } disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 10, 100) solution = solver.solve(model, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) np.testing.assert_array_less(solution.y[0], 1.5) np.testing.assert_array_less(solution.y[0], 1.25)
def test_init(self): # linsolver deprecated with self.assertRaisesRegex(ValueError, "linsolver has been deprecated"): pybamm.ScikitsOdeSolver(linsolver="lapackdense")
def test_ode_integrate_with_jacobian(self): # Linear solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def linear_ode(t, y): return np.array([0.5, 2 - y[0]]) J = np.array([[0.0, 0.0], [-1.0, 0.0]]) sJ = sparse.csr_matrix(J) def jacobian(t, y): return J def sparse_jacobian(t, y): return sJ y0 = np.array([0.0, 0.0]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(linear_ode, y0, t_eval, jacobian=jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) np.testing.assert_allclose(2.0 * solution.t - 0.25 * solution.t**2, solution.y[1], rtol=1e-4) y0 = np.array([0.0, 0.0]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(linear_ode, y0, t_eval, jacobian=sparse_jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(2.0 * solution.t - 0.25 * solution.t**2, solution.y[1], rtol=1e-4) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8, linsolver="spgmr") solution = solver.integrate(linear_ode, y0, t_eval, jacobian=jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) np.testing.assert_allclose(2.0 * solution.t - 0.25 * solution.t**2, solution.y[1], rtol=1e-4) solution = solver.integrate(linear_ode, y0, t_eval, jacobian=sparse_jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(2.0 * solution.t - 0.25 * solution.t**2, solution.y[1], rtol=1e-4) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) # Nonlinear exponential grwoth solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) def exponential_growth(t, y): return np.array([y[0], (1.0 - y[0]) * y[1]]) def jacobian(t, y): return np.array([[1.0, 0.0], [-y[1], 1 - y[0]]]) def sparse_jacobian(t, y): return sparse.csr_matrix(jacobian(t, y)) y0 = np.array([1.0, 1.0]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) np.testing.assert_allclose(np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4) solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=sparse_jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) np.testing.assert_allclose(np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4) solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8, linsolver="spgmr") solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) np.testing.assert_allclose(np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4) solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=sparse_jacobian) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) np.testing.assert_allclose(np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4)
def default_solver(self): """ Create and return the default solver for this model """ return pybamm.ScikitsOdeSolver()