def test_model_solver_minimize(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.algebraic = {var1: var1 - 3, var2: 2 * var1 - var2} model.initial_conditions = {var1: pybamm.Scalar(1), var2: pybamm.Scalar(4)} model.variables = {"var1": var1, "var2": var2} disc = get_discretisation_for_testing() disc.process_model(model) sol = np.concatenate((np.ones(100) * 3, np.ones(100) * 6))[:, np.newaxis] # Solve solver = pybamm.AlgebraicSolver("minimize", tol=1e-8) solution = solver.solve(model) np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(t=None, y=solution.y), sol[:100] ) np.testing.assert_array_almost_equal( model.variables["var2"].evaluate(t=None, y=solution.y), sol[100:] ) # Test without jacobian and with a different method model.use_jacobian = False solver = pybamm.AlgebraicSolver("minimize__BFGS") solution_no_jac = solver.solve(model) np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(t=None, y=solution_no_jac.y), sol[:100] ) np.testing.assert_array_almost_equal( model.variables["var2"].evaluate(t=None, y=solution_no_jac.y), sol[100:] )
def test_root_find_fail(self): class Model: y0 = np.array([2]) rhs = {} timescale_eval = 1 length_scales = {} jac_algebraic_eval = None convert_to_format = "casadi" def algebraic_eval(self, t, y, inputs): # algebraic equation has no real root return y ** 2 + 1 model = Model() solver = pybamm.AlgebraicSolver(method="hybr") with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: The iteration is not making", ): solver._integrate(model, np.array([0])) solver = pybamm.AlgebraicSolver() with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: solver terminated" ): solver._integrate(model, np.array([0]))
def test_model_solver_with_time(self): # Create model model = pybamm.BaseModel() var1 = pybamm.Variable("var1") var2 = pybamm.Variable("var2") model.algebraic = {var1: var1 - 3 * pybamm.t, var2: 2 * var1 - var2} model.initial_conditions = {var1: pybamm.Scalar(1), var2: pybamm.Scalar(4)} model.variables = {"var1": var1, "var2": var2} disc = pybamm.Discretisation() disc.process_model(model) # Solve t_eval = np.linspace(0, 1) solver = pybamm.AlgebraicSolver() solution = solver.solve(model, t_eval) sol = np.vstack((3 * t_eval, 6 * t_eval)) np.testing.assert_array_equal(solution.y, sol) np.testing.assert_array_equal( model.variables["var1"].evaluate(t=t_eval, y=solution.y).flatten(), sol[0, :], ) np.testing.assert_array_equal( model.variables["var2"].evaluate(t=t_eval, y=solution.y).flatten(), sol[1, :], )
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_root_find_fail(self): def algebraic(y): # algebraic equation has no real root return y**2 + 1 solver = pybamm.AlgebraicSolver(method="hybr") y0 = np.array([2]) with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: The iteration is not making", ): solver.root(algebraic, y0) solver = pybamm.AlgebraicSolver() with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: solver terminated"): solver.root(algebraic, y0)
def test_simple_root_find(self): # Simple system: a single algebraic equation def algebraic(y): return y + 2 solver = pybamm.AlgebraicSolver() y0 = np.array([2]) solution = solver.root(algebraic, y0) np.testing.assert_array_equal(solution.y, -2)
def test_algebraic_solver_init(self): solver = pybamm.AlgebraicSolver(method="hybr", tol=1e-4) self.assertEqual(solver.method, "hybr") self.assertEqual(solver.tol, 1e-4) solver.method = "krylov" self.assertEqual(solver.method, "krylov") solver.tol = 1e-5 self.assertEqual(solver.tol, 1e-5)
def root_method(self, method): if method == "casadi": method = pybamm.CasadiAlgebraicSolver(self.root_tol) elif isinstance(method, str): method = pybamm.AlgebraicSolver(method, self.root_tol) elif not (method is None or (isinstance(method, pybamm.BaseSolver) and method.algebraic_solver is True)): raise pybamm.SolverError("Root method must be an algebraic solver") self._root_method = method
def test_algebraic_solver_init(self): solver = pybamm.AlgebraicSolver( method="hybr", tol=1e-4, extra_options={"maxfev": 100} ) self.assertEqual(solver.method, "hybr") self.assertEqual(solver.extra_options, {"maxfev": 100}) self.assertEqual(solver.tol, 1e-4) solver.method = "krylov" self.assertEqual(solver.method, "krylov") solver.tol = 1e-5 self.assertEqual(solver.tol, 1e-5)
def test_simple_root_find(self): # Simple system: a single algebraic equation class Model: y0 = np.array([2]) rhs = {} timescale_eval = 1 jac_algebraic_eval = None convert_to_format = "python" def algebraic_eval(self, t, y, inputs): return y + 2 # Try passing extra options to solver solver = pybamm.AlgebraicSolver(extra_options={"maxiter": 100}) model = Model() solution = solver._integrate(model, np.array([0])) np.testing.assert_array_equal(solution.y, -2) # Relax options and see worse results solver = pybamm.AlgebraicSolver(extra_options={"ftol": 1}) solution = solver._integrate(model, np.array([0])) self.assertNotEqual(solution.y, -2)
def test_simple_root_find(self): # Simple system: a single algebraic equation class Model: y0 = np.array([2]) jacobian_eval = None convert_to_format = "python" def algebraic_eval(self, t, y, inputs): return y + 2 solver = pybamm.AlgebraicSolver() model = Model() solution = solver._integrate(model, np.array([0])) np.testing.assert_array_equal(solution.y, -2)
def test_wrong_solver(self): # Create model model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: var} model.algebraic = {var: var - 1} # test errors solver = pybamm.AlgebraicSolver() with self.assertRaisesRegex( pybamm.SolverError, "Cannot use algebraic solver to solve model with time derivatives", ): solver.solve(model)
def test_solve_with_input(self): # Simple system: a single algebraic equation var = pybamm.Variable("var") model = pybamm.BaseModel() model.algebraic = {var: var + pybamm.InputParameter("value")} model.initial_conditions = {var: 2} # create discretisation disc = pybamm.Discretisation() disc.process_model(model) # Solve solver = pybamm.AlgebraicSolver() solution = solver.solve(model, np.linspace(0, 1, 10), inputs={"value": 7}) np.testing.assert_array_equal(solution.y, -7)
def test_root_method_init(self): solver = pybamm.BaseSolver(root_method="casadi") self.assertIsInstance(solver.root_method, pybamm.CasadiAlgebraicSolver) solver = pybamm.BaseSolver(root_method="lm") self.assertIsInstance(solver.root_method, pybamm.AlgebraicSolver) self.assertEqual(solver.root_method.method, "lm") root_solver = pybamm.AlgebraicSolver() solver = pybamm.BaseSolver(root_method=root_solver) self.assertEqual(solver.root_method, root_solver) with self.assertRaisesRegex(pybamm.SolverError, "Root method must be an algebraic solver"): pybamm.BaseSolver(root_method=pybamm.ScipySolver())
def test_pure_neumann_poisson(self): # grad^2 u = 1, du/dz = 1 at z = 1, du/dn = 0 elsewhere, u has zero average u = pybamm.Variable("u", domain="current collector") c = pybamm.Variable("c") # lagrange multiplier y = pybamm.SpatialVariable("y", ["current collector"]) z = pybamm.SpatialVariable("z", ["current collector"]) model = pybamm.BaseModel() # 0*c hack otherwise gives KeyError model.algebraic = { u: pybamm.laplacian(u) - pybamm.source(1, u) + c * pybamm.DefiniteIntegralVector(u, vector_type="column"), c: pybamm.Integral(u, [y, z]) + 0 * c, } model.initial_conditions = {u: pybamm.Scalar(0), c: pybamm.Scalar(0)} # set boundary conditions ("negative tab" = bottom of unit square, # "positive tab" = top of unit square, elsewhere normal derivative is zero) model.boundary_conditions = { u: { "negative tab": (0, "Neumann"), "positive tab": (1, "Neumann") } } model.variables = {"c": c, "u": u} # create discretisation mesh = get_unit_2p1D_mesh_for_testing(ypts=32, zpts=32, include_particles=False) spatial_methods = { "macroscale": pybamm.FiniteVolume(), "current collector": pybamm.ScikitFiniteElement(), } disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) # solve model solver = pybamm.AlgebraicSolver() solution = solver.solve(model) z = mesh["current collector"].coordinates[1, :][:, np.newaxis] u_exact = z**2 / 2 - 1 / 6 np.testing.assert_array_almost_equal(solution.y[:-1], u_exact, decimal=1)
def test_model_solver_minimize_with_bounds(self): # Note: we need a better test case to test this functionality properly # Create model model = pybamm.BaseModel() var1 = pybamm.Variable("var1", bounds=(0, 10)) model.algebraic = {var1: pybamm.sin(var1) + 1} model.initial_conditions = {var1: pybamm.Scalar(3)} model.variables = {"var1": var1} # Solve solver = pybamm.AlgebraicSolver("minimize", tol=1e-16) solution = solver.solve(model) np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(t=None, y=solution.y), 3 * np.pi / 2, decimal=4, )
def test_with_jacobian(self): A = np.array([[4, 3], [1, -1]]) b = np.array([0, 7]) def algebraic(y): return A @ y - b def jac(y): return A y0 = np.zeros(2) sol = np.array([3, -4])[:, np.newaxis] solver = pybamm.AlgebraicSolver() solution_no_jac = solver.root(algebraic, y0) solution_with_jac = solver.root(algebraic, y0, jacobian=jac) np.testing.assert_array_almost_equal(solution_no_jac.y, sol) np.testing.assert_array_almost_equal(solution_with_jac.y, sol)
def test_with_jacobian(self): A = np.array([[4, 3], [1, -1]]) b = np.array([0, 7]) class Model: y0 = np.zeros(2) convert_to_format = "python" def algebraic_eval(self, t, y, inputs): return A @ y - b def jacobian_eval(self, t, y, inputs): return A model = Model() sol = np.array([3, -4])[:, np.newaxis] solver = pybamm.AlgebraicSolver() solution = solver._integrate(model, np.array([0])) np.testing.assert_array_almost_equal(solution.y, sol)
def test_dirichlet_bcs(self): # manufactured solution u = a*z^2 + b*z + c model = pybamm.BaseModel() a = 3 b = 4 c = 5 u = pybamm.Variable("variable", domain="current collector") model.algebraic = {u: -pybamm.laplacian(u) + pybamm.source(2 * a, u)} # set boundary conditions ("negative tab" = bottom of unit square, # "positive tab" = top of unit square, elsewhere normal derivative is zero) model.boundary_conditions = { u: { "negative tab": (pybamm.Scalar(c), "Dirichlet"), "positive tab": (pybamm.Scalar(a + b + c), "Dirichlet"), } } # bad initial guess (on purpose) model.initial_conditions = {u: pybamm.Scalar(1)} model.variables = {"u": u} # create discretisation mesh = get_unit_2p1D_mesh_for_testing(ypts=8, zpts=32, include_particles=False) spatial_methods = { "macroscale": pybamm.FiniteVolume(), "current collector": pybamm.ScikitFiniteElement(), } disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) # solve model solver = pybamm.AlgebraicSolver() solution = solver.solve(model) # indepedent of y, so just check values for one y z = mesh["current collector"].edges["z"][:, np.newaxis] u_exact = a * z**2 + b * z + c np.testing.assert_array_almost_equal(solution.y[0:len(z)], u_exact)
def default_solver(self): return pybamm.AlgebraicSolver()
def default_solver(self): # Use AlgebraicSolver as CasadiAlgebraicSolver gives unnecessary warnings return pybamm.AlgebraicSolver()
} mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) cc_mesh = pybamm.Mesh(cc_geometry, cc_model.default_submesh_types, var_pts) disc = pybamm.Discretisation(mesh, models[0].default_spatial_methods) cc_disc = pybamm.Discretisation(cc_mesh, cc_models[0].default_spatial_methods) disc.process_model(model, check_model=False) cc_disc.process_model(cc_model, check_model=False) # solve tau = param.evaluate(pybamm.standard_parameters_lithium_ion.tau_discharge) time = comsol_t / tau solver = pybamm.CasadiSolver( atol=1e-6, rtol=1e-6, root_tol=1e-3, root_method="hybr", mode="fast" ) solution = solver.solve(model, time) cc_solver = pybamm.AlgebraicSolver(tol=1e-6) cc_solution = cc_solver.solve(cc_model) sol_times[i] = solution.solve_time + cc_solution.solve_time # create comsol vars interpolated onto pybamm mesh to compare errors comsol_model = shared.make_comsol_model( comsol_variables, cc_mesh, param, thermal=True ) # compute "error" using times up to voltage cut off t = solution.t # Note: casadi doesnt support events so we find this time after the solve if isinstance(solver, pybamm.CasadiSolver): V_cutoff = param.evaluate( pybamm.standard_parameters_lithium_ion.voltage_low_cut_dimensional )