def test_solve_ode_model_with_dae_solver(self): # Create model model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) 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_model_step_nonsmooth_events(self): # Create model model = pybamm.BaseModel() model.timescale = pybamm.Scalar(1) var1 = pybamm.Variable("var1") var2 = pybamm.Variable("var2") a = 0.6 discontinuities = (np.arange(3) + 1) * a model.rhs = {var1: pybamm.Modulo(pybamm.t * model.timescale, a)} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 0, var2: 0} model.events = [ pybamm.Event("var1 = 0.55", pybamm.min(var1 - 0.55)), pybamm.Event("var2 = 1.2", pybamm.min(var2 - 1.2)), ] for discontinuity in discontinuities: model.events.append( pybamm.Event("nonsmooth rate", pybamm.Scalar(discontinuity))) disc = get_discretisation_for_testing() disc.process_model(model) # Solve step_solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) dt = 0.05 time = 0 end_time = 3 step_solution = None while time < end_time: step_solution = step_solver.step(step_solution, model, dt=dt, npts=10) time += dt np.testing.assert_array_less(step_solution.y[0, :-1], 0.55) np.testing.assert_array_less(step_solution.y[-1, :-1], 1.2) np.testing.assert_equal(step_solution.t_event[0], step_solution.t[-1]) np.testing.assert_array_equal(step_solution.y_event[:, 0], step_solution.y[:, -1]) var1_soln = (step_solution.t % a)**2 / 2 + a**2 / 2 * (step_solution.t // a) var2_soln = 2 * var1_soln np.testing.assert_array_almost_equal(step_solution.y[0], var1_soln, decimal=5) np.testing.assert_array_almost_equal(step_solution.y[-1], var2_soln, decimal=5)
def test_discretisation(self): param = pybamm.standard_parameters_lithium_ion model_n = pybamm.interface.lithium_ion.ButlerVolmer(param, "Negative") j_n = model_n.get_coupled_variables( self.variables)["Negative electrode interfacial current density"] model_p = pybamm.interface.lithium_ion.ButlerVolmer(param, "Positive") j_p = model_p.get_coupled_variables( self.variables)["Positive electrode interfacial current density"] j = pybamm.Concatenation(j_n, pybamm.PrimaryBroadcast(0, ["separator"]), j_p) # Process parameters and discretise parameter_values = pybamm.lithium_ion.BaseModel( ).default_parameter_values disc = get_discretisation_for_testing() mesh = disc.mesh disc.set_variable_slices([ self.c_e_n, self.c_e_p, self.delta_phi_s_n, self.delta_phi_s_p, self.c_s_n_surf, self.c_s_p_surf, ]) j_n = disc.process_symbol(parameter_values.process_symbol(j_n)) j_p = disc.process_symbol(parameter_values.process_symbol(j_p)) j = disc.process_symbol(parameter_values.process_symbol(j)) # test butler-volmer in each electrode submesh = np.concatenate([ mesh["negative electrode"][0].nodes, mesh["positive electrode"][0].nodes ]) y = np.concatenate([submesh**2, submesh**3, submesh**4]) self.assertEqual( j_n.evaluate(None, y).shape, (mesh["negative electrode"][0].npts, 1)) self.assertEqual( j_p.evaluate(None, y).shape, (mesh["positive electrode"][0].npts, 1)) # test concatenated butler-volmer whole_cell = ["negative electrode", "separator", "positive electrode"] whole_cell_mesh = disc.mesh.combine_submeshes(*whole_cell) self.assertEqual( j.evaluate(None, y).shape, (whole_cell_mesh[0].npts, 1))
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_process_with_no_check(self): # create model whole_cell = ["negative electrode", "separator", "positive electrode"] c = pybamm.Variable("c", domain=whole_cell) N = pybamm.grad(c) model = pybamm.BaseModel() model.rhs = {c: pybamm.div(N)} model.initial_conditions = {c: pybamm.Scalar(3)} model.boundary_conditions = { c: {"left": (0, "Neumann"), "right": (0, "Neumann")} } model.variables = {"c": c, "N": N} # create discretisation disc = get_discretisation_for_testing() disc.process_model(model, check_model=False)
def test_save(self): model = pybamm.BaseModel() # create both 1D and 2D variables c = pybamm.Variable("c") d = pybamm.Variable("d", domain="negative electrode") model.rhs = {c: -c, d: 1} model.initial_conditions = {c: 1, d: 2} model.variables = {"c": c, "d": d, "2c": 2 * c} disc = get_discretisation_for_testing() disc.process_model(model) solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1)) # test save data with self.assertRaises(ValueError): solution.save_data("test.pickle") # set variables first then save solution.update(["c", "d"]) solution.save_data("test.pickle") data_load = pybamm.load("test.pickle") np.testing.assert_array_equal(solution.data["c"], data_load["c"]) np.testing.assert_array_equal(solution.data["d"], data_load["d"]) # to matlab solution.save_data("test.mat", to_format="matlab") data_load = loadmat("test.mat") np.testing.assert_array_equal(solution.data["c"], data_load["c"].flatten()) np.testing.assert_array_equal(solution.data["d"], data_load["d"]) # to csv with self.assertRaisesRegex( ValueError, "only 0D variables can be saved to csv" ): solution.save_data("test.csv", to_format="csv") # only save "c" and "2c" solution.save_data("test.csv", ["c", "2c"], to_format="csv") # read csv df = pd.read_csv("test.csv") np.testing.assert_array_almost_equal(df["c"], solution.data["c"]) np.testing.assert_array_almost_equal(df["2c"], solution.data["2c"]) # test save whole solution solution.save("test.pickle") solution_load = pybamm.load("test.pickle") self.assertEqual(solution.model.name, solution_load.model.name) np.testing.assert_array_equal(solution["c"].entries, solution_load["c"].entries) np.testing.assert_array_equal(solution["d"].entries, solution_load["d"].entries)
def test_secondary_broadcast_2D(self): # secondary broadcast in 2D --> Matrix multiplication disc = get_discretisation_for_testing() mesh = disc.mesh var = pybamm.Variable("var", domain=["negative particle"]) broad = pybamm.SecondaryBroadcast(var, "negative electrode") disc.set_variable_slices([var]) broad_disc = disc.process_symbol(broad) self.assertIsInstance(broad_disc, pybamm.MatrixMultiplication) self.assertIsInstance(broad_disc.children[0], pybamm.Matrix) self.assertIsInstance(broad_disc.children[1], pybamm.StateVector) self.assertEqual( broad_disc.shape, (mesh["negative particle"][0].npts * mesh["negative electrode"][0].npts, 1), )
def test_check_tab_bcs_error(self): a = pybamm.Variable("a", domain=["current collector"]) b = pybamm.Variable("b", domain=["negative electrode"]) bcs = { "negative tab": (0, "Dirichlet"), "positive tab": (0, "Neumann") } disc = get_discretisation_for_testing() # for 0D bcs keys should be unchanged new_bcs = disc.check_tab_conditions(a, bcs) self.assertListEqual(list(bcs.keys()), list(new_bcs.keys())) # error if domain not "current collector" with self.assertRaisesRegex(pybamm.ModelError, "Boundary conditions"): disc.check_tab_conditions(b, bcs)
def test_model_solver_dae_inputs_events(self): # Create model for form in ["python", "casadi"]: model = pybamm.BaseModel() model.convert_to_format = form whole_cell = [ "negative electrode", "separator", "positive electrode" ] var1 = pybamm.Variable("var1", domain=whole_cell) var2 = pybamm.Variable("var2", domain=whole_cell) model.rhs = {var1: pybamm.InputParameter("rate 1") * var1} model.algebraic = { var2: pybamm.InputParameter("rate 2") * var1 - var2 } model.initial_conditions = {var1: 1, var2: 2} model.events = [ pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)), pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)), ] disc = get_discretisation_for_testing() disc.process_model(model) # Solve if form == "python": solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm") else: solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 5, 100) solution = solver.solve(model, t_eval, inputs={ "rate 1": 0.1, "rate 2": 2 }) np.testing.assert_array_less(solution.y[0, :-1], 1.5) np.testing.assert_array_less(solution.y[-1, :-1], 2.5) np.testing.assert_array_equal(solution.y_event[:, 0], solution.y[:, -1]) np.testing.assert_equal(solution.t_event[0], solution.t[-1]) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
def test_process_complex_expression(self): var1 = pybamm.Variable("var1") var2 = pybamm.Variable("var2") scal1 = pybamm.Scalar(1) scal2 = pybamm.Scalar(2) scal3 = pybamm.Scalar(3) scal4 = pybamm.Scalar(4) expression = (scal1 * (scal3 + var2)) / ((var1 - scal4) + scal2) # create discretisation disc = get_discretisation_for_testing() disc.y_slices = {var1.id: [slice(53)], var2.id: [slice(53, 106)]} exp_disc = disc.process_symbol(expression) self.assertIsInstance(exp_disc, pybamm.Division) # left side self.assertIsInstance(exp_disc.children[0], pybamm.Multiplication) self.assertIsInstance(exp_disc.children[0].children[0], pybamm.Scalar) self.assertIsInstance(exp_disc.children[0].children[1], pybamm.Addition) self.assertTrue( isinstance(exp_disc.children[0].children[1].children[0], pybamm.Scalar) ) self.assertTrue( isinstance(exp_disc.children[0].children[1].children[1], pybamm.StateVector) ) self.assertEqual( exp_disc.children[0].children[1].children[1].y_slices[0], disc.y_slices[var2.id][0], ) # right side self.assertIsInstance(exp_disc.children[1], pybamm.Addition) self.assertTrue( isinstance(exp_disc.children[1].children[0], pybamm.Subtraction) ) self.assertTrue( isinstance(exp_disc.children[1].children[0].children[0], pybamm.StateVector) ) self.assertEqual( exp_disc.children[1].children[0].children[0].y_slices[0], disc.y_slices[var1.id][0], ) self.assertTrue( isinstance(exp_disc.children[1].children[0].children[1], pybamm.Scalar) ) self.assertIsInstance(exp_disc.children[1].children[1], pybamm.Scalar)
def test_simplify_concatenation_state_vectors(self): disc = get_discretisation_for_testing() mesh = disc.mesh a = pybamm.Variable("a", domain=["negative electrode"]) b = pybamm.Variable("b", domain=["separator"]) c = pybamm.Variable("c", domain=["positive electrode"]) conc = pybamm.Concatenation(a, b, c) disc.set_variable_slices([a, b, c]) conc_disc = disc.process_symbol(conc) conc_simp = conc_disc.simplify() y = mesh.combine_submeshes(*conc.domain)[0].nodes ** 2 self.assertIsInstance(conc_simp, pybamm.StateVector) self.assertEqual(len(conc_simp.y_slices), 1) self.assertEqual(conc_simp.y_slices[0].start, 0) self.assertEqual(conc_simp.y_slices[0].stop, len(y)) np.testing.assert_array_equal(conc_disc.evaluate(y=y), conc_simp.evaluate(y=y))
def test_solve_with_symbolic_input_1D_vector_input(self): var = pybamm.Variable("var", "negative electrode") model = pybamm.BaseModel() param = pybamm.InputParameter("param", "negative electrode") model.rhs = {var: -param * var} model.initial_conditions = {var: 2} model.variables = {"var": var} # create discretisation disc = get_discretisation_for_testing() disc.process_model(model) # Solve - scalar input solver = pybamm.CasadiSolver() solution = solver.solve(model, np.linspace(0, 1)) n = disc.mesh["negative electrode"].npts solver = pybamm.CasadiSolver() t_eval = np.linspace(0, 1) solution = solver.solve(model, t_eval) p = np.linspace(0, 1, n)[:, np.newaxis] np.testing.assert_array_almost_equal( solution["var"].value({"param": 3 * np.ones(n)}), np.repeat(2 * np.exp(-3 * t_eval), 40)[:, np.newaxis], decimal=4, ) np.testing.assert_array_almost_equal( solution["var"].value({"param": 2 * p}), 2 * np.exp(-2 * p * t_eval).T.reshape(-1, 1), decimal=4, ) np.testing.assert_array_almost_equal( solution["var"].sensitivity({"param": 3 * np.ones(n)}), np.kron(-2 * t_eval * np.exp(-3 * t_eval), np.eye(40)).T, decimal=4, ) sens = solution["var"].sensitivity({"param": p}).full() for idx, t in enumerate(t_eval): np.testing.assert_array_almost_equal( sens[40 * idx:40 * (idx + 1), :], -2 * t * np.exp(-p * t) * np.eye(40), decimal=4, )
def test_concatenated_parameters(self): # create s_param = pybamm.standard_parameters_lead_acid.s_plus_S self.assertIsInstance(s_param, pybamm.Concatenation) self.assertEqual( s_param.domain, ["negative electrode", "separator", "positive electrode"]) # process parameters and discretise parameter_values = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Sulzer2019) disc = get_discretisation_for_testing() processed_s = disc.process_symbol( parameter_values.process_symbol(s_param)) # test output combined_submeshes = disc.mesh.combine_submeshes( "negative electrode", "separator", "positive electrode") self.assertEqual(processed_s.shape, (combined_submeshes.npts, 1))
def test_concatenation_of_scalars(self): whole_cell = ["negative electrode", "separator", "positive electrode"] a = pybamm.PrimaryBroadcast(5, ["negative electrode"]) b = pybamm.PrimaryBroadcast(4, ["separator"]) # create discretisation disc = get_discretisation_for_testing() mesh = disc.mesh variables = [pybamm.Variable("var", domain=whole_cell)] disc.set_variable_slices(variables) eqn = pybamm.Concatenation(a, b) eqn_disc = disc.process_symbol(eqn) expected_vector = np.concatenate([ 5 * np.ones_like(mesh["negative electrode"][0].nodes), 4 * np.ones_like(mesh["separator"][0].nodes), ])[:, np.newaxis] np.testing.assert_allclose(eqn_disc.evaluate(), expected_vector)
def test_model_solver_dae_bad_ics_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: 0.1 * var1} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 1, var2: 3} disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm") 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)) np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
def test_solve_with_symbolic_input_1D_scalar_input(self): var = pybamm.Variable("var", "negative electrode") model = pybamm.BaseModel() param = pybamm.InputParameter("param") model.algebraic = {var: var + param} model.initial_conditions = {var: 2} model.variables = {"var": var} # create discretisation disc = tests.get_discretisation_for_testing() disc.process_model(model) # Solve - scalar input solver = pybamm.CasadiAlgebraicSolver() solution = solver.solve(model, [0]) np.testing.assert_array_equal(solution["var"].value({"param": 7}), -7) np.testing.assert_array_equal(solution["var"].value({"param": 3}), -3) np.testing.assert_array_equal( solution["var"].sensitivity({"param": 3}), -1)
def test_processed_variable_2D(self): t = pybamm.t var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = t * var + x # On nodes disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) eqn_sol = disc.process_symbol(eqn) t_sol = np.linspace(0, 1) y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) np.testing.assert_array_equal(processed_var.entries[1:-1], y_sol) np.testing.assert_array_equal(processed_var(t_sol, x_sol), y_sol) processed_eqn = pybamm.ProcessedVariable(eqn_sol, t_sol, y_sol, mesh=disc.mesh) np.testing.assert_array_equal(processed_eqn(t_sol, x_sol), t_sol * y_sol + x_sol[:, np.newaxis]) # Test extrapolation np.testing.assert_array_equal(processed_var.entries[0], 2 * y_sol[0] - y_sol[1]) np.testing.assert_array_equal(processed_var.entries[1], 2 * y_sol[-1] - y_sol[-2]) # On edges x_s_edge = pybamm.Matrix(disc.mesh["separator"][0].edges, domain="separator") processed_x_s_edge = pybamm.ProcessedVariable(x_s_edge, t_sol, y_sol, disc.mesh) np.testing.assert_array_equal(x_s_edge.entries[:, 0], processed_x_s_edge.entries[1:-1, 0])
def test_broadcast(self): whole_cell = ["negative electrode", "separator", "positive electrode"] a = pybamm.InputParameter("a") var = pybamm.Variable("var") # create discretisation disc = get_discretisation_for_testing() mesh = disc.mesh combined_submesh = mesh.combine_submeshes(*whole_cell) # scalar broad = disc.process_symbol(pybamm.FullBroadcast(a, whole_cell, {})) np.testing.assert_array_equal( broad.evaluate(inputs={"a": 7}), 7 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis]), ) self.assertEqual(broad.domain, whole_cell) broad_disc = disc.process_symbol(broad) self.assertIsInstance(broad_disc, pybamm.Multiplication) self.assertIsInstance(broad_disc.children[0], pybamm.InputParameter) self.assertIsInstance(broad_disc.children[1], pybamm.Vector) # process Broadcast variable disc.y_slices = {var.id: [slice(1)]} broad1 = pybamm.FullBroadcast(var, ["negative electrode"], None) broad1_disc = disc.process_symbol(broad1) self.assertIsInstance(broad1_disc, pybamm.Multiplication) self.assertIsInstance(broad1_disc.children[0], pybamm.StateVector) self.assertIsInstance(broad1_disc.children[1], pybamm.Vector) # broadcast to edges broad_to_edges = pybamm.FullBroadcastToEdges(a, ["negative electrode"], None) broad_to_edges_disc = disc.process_symbol(broad_to_edges) np.testing.assert_array_equal( broad_to_edges_disc.evaluate(inputs={"a": 7}), 7 * np.ones_like(mesh["negative electrode"][0].edges[:, np.newaxis]), )
def test_discretise_spatial_operator(self): # create discretisation whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) disc = get_discretisation_for_testing() mesh = disc.mesh variables = [var] disc.set_variable_slices(variables) # Simple expressions for eqn in [pybamm.grad(var), pybamm.div(var)]: eqn_disc = disc.process_symbol(eqn) self.assertIsInstance(eqn_disc, pybamm.MatrixMultiplication) self.assertIsInstance(eqn_disc.children[0], pybamm.Matrix) self.assertIsInstance(eqn_disc.children[1], pybamm.StateVector) combined_submesh = mesh.combine_submeshes(*whole_cell) y = combined_submesh[0].nodes**2 var_disc = disc.process_symbol(var) # grad and var are identity operators here (for testing purposes) np.testing.assert_array_equal(eqn_disc.evaluate(None, y), var_disc.evaluate(None, y)) # More complex expressions for eqn in [var * pybamm.grad(var), var * pybamm.div(var)]: eqn_disc = disc.process_symbol(eqn) self.assertIsInstance(eqn_disc, pybamm.Multiplication) self.assertIsInstance(eqn_disc.children[0], pybamm.StateVector) self.assertIsInstance(eqn_disc.children[1], pybamm.MatrixMultiplication) self.assertIsInstance(eqn_disc.children[1].children[0], pybamm.Matrix) self.assertIsInstance(eqn_disc.children[1].children[1], pybamm.StateVector) y = combined_submesh[0].nodes**2 var_disc = disc.process_symbol(var) # grad and var are identity operators here (for testing purposes) np.testing.assert_array_equal(eqn_disc.evaluate(None, y), var_disc.evaluate(None, y)**2)
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_exceptions(self): c_n = pybamm.Variable("c", domain=["negative electrode"]) N_n = pybamm.grad(c_n) c_s = pybamm.Variable("c", domain=["separator"]) N_s = pybamm.grad(c_s) model = pybamm.BaseModel() model.rhs = {c_n: pybamm.div(N_n), c_s: pybamm.div(N_s)} model.initial_conditions = { c_n: pybamm.Scalar(3), c_s: pybamm.Scalar(1) } model.boundary_conditions = { c_n: { "left": (0, "Neumann"), "right": (0, "Neumann") }, c_s: { "left": (0, "Neumann"), "right": (0, "Neumann") }, } disc = get_discretisation_for_testing() # check raises error if different sized key and output var model.variables = {c_n.name: c_s} with self.assertRaisesRegex(pybamm.ModelError, "variable and its eqn"): disc.process_model(model) # check doesn't raise if concatenation model.variables = {c_n.name: pybamm.Concatenation(c_n, c_s)} disc.process_model(model, inplace=False) # check doesn't raise if broadcast model.variables = { c_n.name: pybamm.PrimaryBroadcast(pybamm.InputParameter("a"), ["negative electrode"]) } disc.process_model(model)
def test_model_step_dae_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: 0.1 * var1} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 1, var2: 2} model.use_jacobian = False disc = get_discretisation_for_testing() disc.process_model(model) solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm") # 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)) np.testing.assert_allclose(step_sol.y[-1], 2 * 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)) np.testing.assert_allclose(step_sol_2.y[-1], 2 * 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, :]) np.testing.assert_allclose(solution.y[-1], step_sol.y[-1, :])
def test_processed_var_1D_fixed_t_interpolation(self): var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = var + x disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] eqn_sol = disc.process_symbol(eqn) t_sol = np.array([1]) y_sol = x_sol[:, np.newaxis] processed_var = pybamm.ProcessedVariable( eqn_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # vector np.testing.assert_array_almost_equal( processed_var(x=x_sol), 2 * x_sol[:, np.newaxis] ) # scalar np.testing.assert_array_almost_equal(processed_var(x=0.5), 1)
def test_discretisation_main_reaction(self): # With intercalation param = pybamm.standard_parameters_lead_acid model_n = pybamm.interface.BaseInterface(param, "Negative", "lead-acid main") j0_n = model_n._get_exchange_current_density(self.variables) model_p = pybamm.interface.BaseInterface(param, "Positive", "lead-acid main") j0_p = model_p._get_exchange_current_density(self.variables) # Process parameters and discretise parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values disc = get_discretisation_for_testing() mesh = disc.mesh disc.set_variable_slices([self.c_e]) j0_n = disc.process_symbol(parameter_values.process_symbol(j0_n)) j0_p = disc.process_symbol(parameter_values.process_symbol(j0_p)) # Test whole_cell = ["negative electrode", "separator", "positive electrode"] submesh = mesh.combine_submeshes(*whole_cell) y = submesh.nodes ** 2 # should evaluate to vectors with the right shape self.assertEqual(j0_n.evaluate(y=y).shape, (mesh["negative electrode"].npts, 1)) self.assertEqual(j0_p.evaluate(y=y).shape, (mesh["positive electrode"].npts, 1))
def test_domain_concatenation_simplify(self): # create discretisation disc = get_discretisation_for_testing() mesh = disc.mesh a_dom = ["negative electrode"] b_dom = ["positive electrode"] a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom) b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom) conc = pybamm.DomainConcatenation([a, b], mesh) conc_simp = conc.simplify() # should be simplified to a vector self.assertIsInstance(conc_simp, pybamm.Vector) np.testing.assert_array_equal( conc_simp.evaluate(), np.concatenate([ np.full((mesh[a_dom[0]].npts, 1), 2), np.full((mesh[b_dom[0]].npts, 1), 1), ]), )
def test_model_solver(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() solution = solver.solve(model) np.testing.assert_array_equal( model.variables["var1"].evaluate(t=None, y=solution.y), sol[:100] ) np.testing.assert_array_equal( model.variables["var2"].evaluate(t=None, y=solution.y), sol[100:] ) # Test time self.assertGreater( solution.total_time, solution.solve_time + solution.set_up_time ) # Test without jacobian model.use_jacobian = False solution_no_jac = solver.solve(model) np.testing.assert_array_equal( model.variables["var1"].evaluate(t=None, y=solution_no_jac.y), sol[:100] ) np.testing.assert_array_equal( model.variables["var2"].evaluate(t=None, y=solution_no_jac.y), sol[100:] )
def test_model_solver_dae_with_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: 0.1 * var1} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 1.0, var2: 2.0} model.initial_conditions_ydot = {var1: 0.1, var2: 0.2} 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.npts def jacobian(t, y): return np.block([ [0.1 * np.eye(N), np.zeros((N, N))], [2.0 * np.eye(N), -1.0 * np.eye(N)], ]) model.jacobian = jacobian # Solve solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm") 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)) np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
def test_model_solver_dae_no_nonsmooth_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) discontinuity = 5.6 def nonsmooth_rate(t): return 0.1 * int(t < discontinuity) + 0.1 def nonsmooth_mult(t): return int(t < discontinuity) + 1.0 # put in an extra heaviside with no time dependence, this should be ignored by # the solver i.e. no extra discontinuities added model.rhs = {var1: 0.1 * var1} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 1, var2: 2} disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.ScikitsDaeSolver(rtol=1e-9, atol=1e-9, root_method="lm") # create two time series, one without a time point on the discontinuity, # and one with t_eval = np.linspace(0, 5, 10) solution = solver.solve(model, t_eval) # test solution, discontinuity should not be triggered np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
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.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_process_model_concatenation(self): # concatenation of variables as the key cn = pybamm.Variable("c", domain=["negative electrode"]) cs = pybamm.Variable("c", domain=["separator"]) cp = pybamm.Variable("c", domain=["positive electrode"]) c = pybamm.Concatenation(cn, cs, cp) N = pybamm.grad(c) model = pybamm.BaseModel() model.rhs = {c: pybamm.div(N)} model.initial_conditions = {c: pybamm.Scalar(3)} model.boundary_conditions = { c: { "left": (0, "Neumann"), "right": (0, "Neumann") } } model.check_well_posedness() # create discretisation disc = get_discretisation_for_testing() mesh = disc.mesh combined_submesh = mesh.combine_submeshes("negative electrode", "separator", "positive electrode") disc.process_model(model) y0 = model.concatenated_initial_conditions.evaluate() np.testing.assert_array_equal( y0, 3 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis])) # grad and div are identity operators here np.testing.assert_array_equal( y0, model.concatenated_rhs.evaluate(None, y0)) model.check_well_posedness()