def test_model_solver_with_inputs(self): # Create model model = pybamm.BaseModel() domain = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=domain) model.rhs = {var: -pybamm.InputParameter("rate") * var} model.initial_conditions = {var: 1} model.events = [pybamm.Event("var=0.5", pybamm.min(var - 0.5))] # No need to set parameters; can use base discretisation (no spatial # operators) # create discretisation mesh = get_mesh_for_testing() spatial_methods = {"macroscale": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) # Solve solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 10, 100) solution = solver.solve(model, t_eval, inputs={"rate": 0.1}) self.assertLess(len(solution.t), len(t_eval)) np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t), rtol=1e-04) # Without grid solver = pybamm.CasadiSolver(mode="safe without grid", rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 10, 100) solution = solver.solve(model, t_eval, inputs={"rate": 0.1}) self.assertLess(len(solution.t), len(t_eval)) np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t), rtol=1e-04) solution = solver.solve(model, t_eval, inputs={"rate": 1.1}) self.assertLess(len(solution.t), len(t_eval)) np.testing.assert_allclose(solution.y[0], np.exp(-1.1 * solution.t), rtol=1e-04)
def test_model_solver_failure(self): # Create model model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: -pybamm.sqrt(var)} model.initial_conditions = {var: 1} # add events so that safe mode is used (won't be triggered) model.events = [pybamm.Event("10", var - 10)] # No need to set parameters; can use base discretisation (no spatial operators) # create discretisation disc = pybamm.Discretisation() model_disc = disc.process_model(model, inplace=False) solver = pybamm.CasadiSolver( extra_options_call={"regularity_check": False}) solver_old = pybamm.CasadiSolver( mode="old safe", extra_options_call={"regularity_check": False}) # Solve with failure at t=2 t_eval = np.linspace(0, 20, 100) with self.assertRaises(pybamm.SolverError): solver.solve(model_disc, t_eval) with self.assertRaises(pybamm.SolverError): solver_old.solve(model_disc, t_eval) # Solve with failure at t=0 model.initial_conditions = {var: 0} model_disc = disc.process_model(model, inplace=False) t_eval = np.linspace(0, 20, 100) with self.assertRaises(pybamm.SolverError): solver.solve(model_disc, t_eval)
def test_model_solver(self): # Create model model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} # No need to set parameters; can use base discretisation (no spatial operators) # create discretisation disc = pybamm.Discretisation() model_disc = disc.process_model(model, inplace=False) # Solve solver = pybamm.CasadiSolver(mode="fast", rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 1, 100) solution = solver.solve(model_disc, t_eval) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_array_almost_equal(solution.y[0], np.exp(0.1 * solution.t), decimal=5) # Safe mode (enforce events that won't be triggered) model.events = [pybamm.Event("an event", var + 1)] disc.process_model(model) solver = pybamm.CasadiSolver(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_array_almost_equal(solution.y[0], np.exp(0.1 * solution.t), decimal=5)
def test_integrate(self): # Constant solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") y = casadi.SX.sym("y") constant_growth = casadi.SX(0.5) problem = {"x": y, "ode": constant_growth} y0 = np.array([0]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate_casadi(problem, 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.CasadiSolver(rtol=1e-8, atol=1e-8, method="cvodes") exponential_decay = -0.1 * y problem = {"x": y, "ode": exponential_decay} y0 = np.array([1]) t_eval = np.linspace(0, 1, 100) solution = solver.integrate_casadi(problem, y0, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) self.assertEqual(solution.termination, "final time")
def test_save_at_cycles(self): experiment = pybamm.Experiment([ ( "Discharge at 1C until 3.3V", "Charge at 1C until 4.1 V", "Hold at 4.1V until C/10", ), ] * 10, ) model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model, experiment=experiment) sol = sim.solve(solver=pybamm.CasadiSolver("fast with events"), save_at_cycles=2) # Solution saves "None" for the cycles that are not saved for cycle_num in [2, 4, 6, 8]: self.assertIsNone(sol.cycles[cycle_num]) for cycle_num in [0, 1, 3, 5, 7, 9]: self.assertIsNotNone(sol.cycles[cycle_num]) # Summary variables are not None self.assertIsNotNone(sol.summary_variables["Capacity [A.h]"]) sol = sim.solve(solver=pybamm.CasadiSolver("fast with events"), save_at_cycles=[3, 4, 5, 9]) # Note offset by 1 (0th cycle is cycle 1) for cycle_num in [1, 5, 6, 7, 9]: self.assertIsNone(sol.cycles[cycle_num]) for cycle_num in [0, 2, 3, 4, 8]: self.assertIsNotNone(sol.cycles[cycle_num]) # Summary variables are not None self.assertIsNotNone(sol.summary_variables["Capacity [A.h]"])
def test_compare_full(self): basic_full = pybamm.lead_acid.BasicFull() full = pybamm.lead_acid.Full({"convection": "uniform transverse"}) parameter_values = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Sulzer2019 ) parameter_values["Current function [A]"] = 10 # Solve basic Full mode basic_sim = pybamm.Simulation( basic_full, solver=pybamm.CasadiSolver(), parameter_values=parameter_values ) t_eval = np.linspace(0, 400) basic_sim.solve(t_eval) basic_sol = basic_sim.solution # Solve main Full model sim = pybamm.Simulation( full, solver=pybamm.CasadiSolver(), parameter_values=parameter_values ) t_eval = np.linspace(0, 400) sim.solve(t_eval) sol = sim.solution # Compare solution data # np.testing.assert_array_almost_equal(basic_sol.y, sol.y, decimal=4) np.testing.assert_array_almost_equal(basic_sol.t, sol.t, decimal=4) # Compare variables for name in basic_full.variables: np.testing.assert_array_almost_equal( basic_sol[name].entries, sol[name].entries, decimal=4 )
def test_model_solver_dae_inputs_in_initial_conditions(self): # Create model model = pybamm.BaseModel() var1 = pybamm.Variable("var1") var2 = pybamm.Variable("var2") model.rhs = {var1: pybamm.InputParameter("rate") * var1} model.algebraic = {var2: var1 - var2} model.initial_conditions = { var1: pybamm.InputParameter("ic 1"), var2: pybamm.InputParameter("ic 2"), } # Solve solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 5, 100) solution = solver.solve( model, t_eval, inputs={"rate": -1, "ic 1": 0.1, "ic 2": 2} ) np.testing.assert_array_almost_equal( solution.y[0], 0.1 * np.exp(-solution.t), decimal=5 ) np.testing.assert_array_almost_equal( solution.y[-1], 0.1 * np.exp(-solution.t), decimal=5 ) # Solve again with different initial conditions solution = solver.solve( model, t_eval, inputs={"rate": -0.1, "ic 1": 1, "ic 2": 3} ) np.testing.assert_array_almost_equal( solution.y[0], 1 * np.exp(-0.1 * solution.t), decimal=5 ) np.testing.assert_array_almost_equal( solution.y[-1], 1 * np.exp(-0.1 * solution.t), decimal=5 )
def get_max_error(current): pybamm.logger.info("current = {}".format(current)) # Solve, make sure times are the same and use tight tolerances t_eval = np.linspace(0, 3600 * 17 / current) solver = pybamm.CasadiSolver() solver.rtol = 1e-8 solver.atol = 1e-8 solution_loqs = solver.solve( leading_order_model, t_eval, inputs={"Current function [A]": current} ) solution_comp = solver.solve( composite_model, t_eval, inputs={"Current function [A]": current} ) solution_full = solver.solve( full_model, t_eval, inputs={"Current function [A]": current} ) # Post-process variables voltage_loqs = solution_loqs["Terminal voltage"] voltage_comp = solution_comp["Terminal voltage"] voltage_full = solution_full["Terminal voltage"] # Compare t_loqs = solution_loqs.t t_comp = solution_comp.t t_full = solution_full.t t = t_full[: np.min([len(t_loqs), len(t_comp), len(t_full)])] loqs_error = np.max(np.abs(voltage_loqs(t) - voltage_full(t))) comp_error = np.max(np.abs(voltage_comp(t) - voltage_full(t))) return (loqs_error, comp_error)
def test_model_solver_events(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: 0.1 * var1} model.algebraic = {var2: 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 solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 5, 100) solution = solver.solve(model, t_eval) np.testing.assert_array_less(solution.y[0], 1.5) np.testing.assert_array_less(solution.y[-1], 2.5) np.testing.assert_array_almost_equal(solution.y[0], np.exp(0.1 * solution.t), decimal=5) np.testing.assert_array_almost_equal(solution.y[-1], 2 * np.exp(0.1 * solution.t), decimal=5)
def test_model_with_inputs(self): chemistry = pybamm.parameter_sets.Chen2020 parameter_values = pybamm.ParameterValues(chemistry=chemistry) model = pybamm.lithium_ion.SPMe() parameter_values.update({"Electrode height [m]": "[input]"}) solver = pybamm.CasadiSolver(mode="safe") sim1 = pybamm.Simulation(model, parameter_values=parameter_values, solver=solver) inputs1 = {"Electrode height [m]": 1.00} sol1 = sim1.solve(t_eval=np.linspace(0, 1000, 101), inputs=inputs1) sim2 = pybamm.Simulation(model, parameter_values=parameter_values, solver=solver) inputs2 = {"Electrode height [m]": 2.00} sol2 = sim2.solve(t_eval=np.linspace(0, 1000, 101), inputs=inputs2) output_variables = [ "Terminal voltage [V]", "Current [A]", "Negative electrode potential [V]", "Positive electrode potential [V]", "Electrolyte potential [V]", "Electrolyte concentration", "Negative particle surface concentration", "Positive particle surface concentration", ] quick_plot = pybamm.QuickPlot(solutions=[sol1, sol2], output_variables=output_variables) quick_plot.dynamic_plot(testing=True) quick_plot.slider_update(1) pybamm.close_plots()
def test_model_step_with_input(self): # Create model model = pybamm.BaseModel() var = pybamm.Variable("var") a = pybamm.InputParameter("a") model.rhs = {var: a * var} model.initial_conditions = {var: 1} model.variables = {"a": a} # No need to set parameters; can use base discretisation (no spatial operators) # create discretisation disc = pybamm.Discretisation() disc.process_model(model) solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) # Step with an input dt = 0.1 step_sol = solver.step(None, model, dt, npts=5, inputs={"a": 0.1}) np.testing.assert_array_equal(step_sol.t, np.linspace(0, dt, 5)) np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) # Step again with different inputs step_sol_2 = solver.step(step_sol, model, dt, npts=5, inputs={"a": -1}) np.testing.assert_array_equal(step_sol_2.t, np.linspace(0, 2 * dt, 9)) np.testing.assert_array_equal( step_sol_2["a"].entries, np.array([0.1, 0.1, 0.1, 0.1, 0.1, -1, -1, -1, -1])) np.testing.assert_allclose( step_sol_2.y[0], np.concatenate([ np.exp(0.1 * step_sol.t[:5]), np.exp(0.1 * step_sol.t[4]) * np.exp(-(step_sol.t[5:] - dt)), ]), )
def test_model_step(self): # Create model model = pybamm.BaseModel() domain = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=domain) model.rhs = {var: 0.1 * var} model.initial_conditions = {var: 1} # No need to set parameters; can use base discretisation (no spatial operators) # create discretisation mesh = get_mesh_for_testing() spatial_methods = {"macroscale": pybamm.FiniteVolume} disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") # 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)) # append solutions step_sol.append(step_sol_2) # 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_basic_processing(self): options = {"thermal": "isothermal"} model = pybamm.lead_acid.Full(options) modeltest = tests.StandardModelTest(model) modeltest.test_all( t_eval=np.linspace(0, 3600 * 17), solver=pybamm.CasadiSolver() )
def test_model_solver_with_non_identity_mass(self): model = pybamm.BaseModel() var1 = pybamm.Variable("var1", domain="negative electrode") var2 = pybamm.Variable("var2", domain="negative electrode") model.rhs = {var1: var1} model.algebraic = {var2: 2 * var1 - var2} model.initial_conditions = {var1: 1, var2: 2} disc = get_discretisation_for_testing() disc.process_model(model) # FV discretisation has identity mass. Manually set the mass matrix to # be a diag of 10s here for testing. Note that the algebraic part is all # zeros mass_matrix = 10 * model.mass_matrix.entries model.mass_matrix = pybamm.Matrix(mass_matrix) # Note that mass_matrix_inv is just the inverse of the ode block of the # mass matrix mass_matrix_inv = 0.1 * eye(int(mass_matrix.shape[0] / 2)) model.mass_matrix_inv = pybamm.Matrix(mass_matrix_inv) # Solve solver = pybamm.CasadiSolver(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.full()[0], np.exp(0.1 * solution.t)) np.testing.assert_allclose(solution.y.full()[-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.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() t_eval = np.linspace(0, 1) solution = solver.solve(model, t_eval) np.testing.assert_array_almost_equal( solution["var"].value({"param": 7}), np.repeat(2 * np.exp(-7 * t_eval), 40)[:, np.newaxis], decimal=4, ) np.testing.assert_array_almost_equal( solution["var"].value({"param": 3}), np.repeat(2 * np.exp(-3 * t_eval), 40)[:, np.newaxis], decimal=4, ) np.testing.assert_array_almost_equal( solution["var"].sensitivity({"param": 3}), np.repeat(-2 * t_eval * np.exp(-3 * t_eval), disc.mesh["negative electrode"].npts)[:, np.newaxis], decimal=4, )
def test_on_dfn(self): e_height = 0.25 model = pybamm.lithium_ion.DFN() geometry = model.default_geometry param = model.default_parameter_values param.update({"Electrode height [m]": "[input]"}) param.process_model(model) param.process_geometry(geometry) inputs = {"Electrode height [m]": e_height} var = pybamm.standard_spatial_vars var_pts = { var.x_n: 5, var.x_s: 5, var.x_p: 5, var.r_n: 10, var.r_p: 10 } spatial_methods = model.default_spatial_methods solver = pybamm.CasadiSolver() sim = pybamm.Simulation( model=model, geometry=geometry, parameter_values=param, var_pts=var_pts, spatial_methods=spatial_methods, solver=solver, ) sim.solve(t_eval=np.linspace(0, 3600, 100), inputs=inputs)
def test_solve_with_symbolic_input_in_initial_conditions(self): # Simple system: a single algebraic equation var = pybamm.Variable("var") model = pybamm.BaseModel() model.rhs = {var: -var} model.initial_conditions = {var: pybamm.InputParameter("param")} model.variables = {"var": var} # create discretisation disc = pybamm.Discretisation() disc.process_model(model) # Solve solver = pybamm.CasadiSolver(atol=1e-10, rtol=1e-10) t_eval = np.linspace(0, 1) solution = solver.solve(model, t_eval) np.testing.assert_array_almost_equal( solution["var"].value({"param": 7}), 7 * np.exp(-t_eval)[np.newaxis, :]) np.testing.assert_array_almost_equal( solution["var"].value({"param": 3}), 3 * np.exp(-t_eval)[np.newaxis, :]) np.testing.assert_array_almost_equal( solution["var"].sensitivity({"param": 3}), np.exp(-t_eval)[:, np.newaxis])
def test_dae_external_temperature(self): model_options = { "thermal": "x-full", "external submodels": ["thermal"] } model = pybamm.lithium_ion.DFN(model_options) neg_pts = 5 sep_pts = 3 pos_pts = 5 tot_pts = neg_pts + sep_pts + pos_pts var_pts = { pybamm.standard_spatial_vars.x_n: neg_pts, pybamm.standard_spatial_vars.x_s: sep_pts, pybamm.standard_spatial_vars.x_p: pos_pts, pybamm.standard_spatial_vars.r_n: 5, pybamm.standard_spatial_vars.r_p: 5, } solver = pybamm.CasadiSolver() sim = pybamm.Simulation(model, var_pts=var_pts, solver=solver) sim.build() t_eval = np.linspace(0, 100, 3) x = np.linspace(0, 1, tot_pts) for i in np.arange(1, len(t_eval) - 1): dt = t_eval[i + 1] - t_eval[i] T = (np.sin(2 * np.pi * x) * np.sin(2 * np.pi * 100 * t_eval[i]))[:, np.newaxis] external_variables = {"Cell temperature": T} sim.step(dt, external_variables=external_variables)
def test_model_step_events(self): # Create model model = pybamm.BaseModel() var1 = pybamm.Variable("var1") var2 = pybamm.Variable("var2") model.rhs = {var1: 0.1 * var1} model.algebraic = {var2: 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 = pybamm.Discretisation() disc.process_model(model) # Solve step_solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) dt = 0.05 time = 0 end_time = 5 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.5) np.testing.assert_array_less(step_solution.y[-1], 2.5001) np.testing.assert_array_almost_equal(step_solution.y[0], np.exp(0.1 * step_solution.t), decimal=5) np.testing.assert_array_almost_equal(step_solution.y[-1], 2 * np.exp(0.1 * step_solution.t), decimal=4)
def test_least_squares_fit_input_in_initial_conditions(self): # Simple system: a single algebraic equation var1 = pybamm.Variable("var1", domain="negative electrode") var2 = pybamm.Variable("var2", domain="negative electrode") model = pybamm.BaseModel() p = pybamm.InputParameter("p") q = pybamm.InputParameter("q") model.rhs = {var1: -var1} model.algebraic = {var2: (var2 - p)} model.initial_conditions = {var1: 1, var2: p} model.variables = {"objective": (var2 - q)**2 + (p - 3)**2} # create discretisation disc = get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.CasadiSolver() solution = solver.solve(model, np.linspace(0, 1)) sol_var = solution["objective"] def objective(x): return sol_var.value({"p": x[0], "q": x[1]}).full().flatten() # without jacobian lsq_sol = least_squares(objective, [2, 2], method="lm") np.testing.assert_array_almost_equal(lsq_sol.x, [3, 3], decimal=3)
def test_solve_with_symbolic_input(self): # Simple system: a single differential equation var = pybamm.Variable("var") model = pybamm.BaseModel() model.rhs = {var: pybamm.InputParameter("param")} model.initial_conditions = {var: 2} model.variables = {"var": var} # create discretisation disc = pybamm.Discretisation() disc.process_model(model) # Solve solver = pybamm.CasadiSolver() t_eval = np.linspace(0, 1) solution = solver.solve(model, t_eval) np.testing.assert_array_almost_equal( solution["var"].value({ "param": 7 }).full().flatten(), 2 + 7 * t_eval) np.testing.assert_array_almost_equal( solution["var"].value({ "param": -3 }).full().flatten(), 2 - 3 * t_eval) np.testing.assert_array_almost_equal( solution["var"].sensitivity({ "param": 3 }).full().flatten(), t_eval)
def test_model_solver_with_external(self): # Create model model = pybamm.BaseModel() domain = ["negative electrode", "separator", "positive electrode"] var1 = pybamm.Variable("var1", domain=domain) var2 = pybamm.Variable("var2", domain=domain) model.rhs = {var1: -var2} model.initial_conditions = {var1: 1} model.external_variables = [var2] model.variables = {"var1": var1, "var2": var2} # No need to set parameters; can use base discretisation (no spatial # operators) # create discretisation mesh = get_mesh_for_testing() spatial_methods = {"macroscale": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) # Solve solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 10, 100) solution = solver.solve(model, t_eval, external_variables={"var2": 0.5}) np.testing.assert_allclose(solution.y.full()[0], 1 - 0.5 * solution.t, rtol=1e-06)
def test_run_experiment_termination(self): # with percent experiment = pybamm.Experiment( [ ( "Discharge at 1C until 3V", "Charge at 1C until 4.2 V", "Hold at 4.2V until C/10", ), ] * 10, termination="99% capacity", ) model = pybamm.lithium_ion.SPM({"SEI": "ec reaction limited"}) param = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Chen2020) param["SEI kinetic rate constant [m.s-1]"] = 1e-14 sim = pybamm.Simulation(model, experiment=experiment, parameter_values=param) sol = sim.solve(solver=pybamm.CasadiSolver()) C = sol.summary_variables["Capacity [A.h]"] np.testing.assert_array_less(np.diff(C), 0) # all but the last value should be above the termination condition np.testing.assert_array_less(0.99 * C[0], C[:-1]) # with Ah value experiment = pybamm.Experiment( [ ( "Discharge at 1C until 3V", "Charge at 1C until 4.2 V", "Hold at 4.2V until C/10", ), ] * 10, termination="5.04Ah capacity", ) model = pybamm.lithium_ion.SPM({"SEI": "ec reaction limited"}) param = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Chen2020) param["SEI kinetic rate constant [m.s-1]"] = 1e-14 sim = pybamm.Simulation(model, experiment=experiment, parameter_values=param) sol = sim.solve(solver=pybamm.CasadiSolver()) # all but the last value should be above the termination condition np.testing.assert_array_less(5.04, C[:-1])
def default_solver(self): "Return default solver based on whether model is ODE model or DAE model" if len(self.algebraic) == 0: return pybamm.ScipySolver() elif pybamm.have_idaklu() and self.use_jacobian is True: # KLU solver requires jacobian to be provided return pybamm.IDAKLUSolver() else: return pybamm.CasadiSolver(mode="safe")
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_run_experiment_breaks_early(self): experiment = pybamm.Experiment(["Discharge at 2 C for 1 hour"]) model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model, experiment=experiment) pybamm.set_logging_level("ERROR") # giving the time, should get ignored t_eval = [0, 1] sim.solve(t_eval, solver=pybamm.CasadiSolver()) pybamm.set_logging_level("WARNING") self.assertEqual(sim._solution, None)
def test_compare_particle_shape(self): models = [ pybamm.lithium_ion.SPM({"particle shape": "spherical"}, name="spherical"), pybamm.lithium_ion.SPM({"particle shape": "user"}, name="user"), ] params = [ models[0].default_parameter_values, models[0].default_parameter_values, ] # set same mesh for all models var = pybamm.standard_spatial_vars var_pts = {var.x_n: 5, var.x_s: 5, var.x_p: 5, var.r_n: 5, var.r_p: 5} for model, param in zip(models, params): if model.name == "user": R_n = param["Negative particle radius [m]"] R_p = param["Positive particle radius [m]"] eps_s_n = param[ "Negative electrode active material volume fraction"] eps_s_p = param[ "Positive electrode active material volume fraction"] param.update( { "Negative electrode surface area to volume ratio [m-1]": 3 * eps_s_n / R_n, "Positive electrode surface area to volume ratio [m-1]": 3 * eps_s_p / R_p, "Negative surface area per unit volume distribution in x": 1, "Positive surface area per unit volume distribution in x": 1, }, check_already_exists=False, ) param.process_model(model) geometry = model.default_geometry param.process_geometry(geometry) mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) disc = pybamm.Discretisation(mesh, model.default_spatial_methods) disc.process_model(model) # solve model solutions = [] t_eval = np.linspace(0, 3600, 100) for model in models: solution = pybamm.CasadiSolver().solve(model, t_eval) solutions.append(solution) # compare outputs comparison = StandardOutputComparison(solutions) comparison.test_all(skip_first_timestep=True)
def test_run_experiment(self): experiment = pybamm.Experiment([ "Discharge at C/20 for 1 hour", "Charge at 1 A until 4.1 V", "Hold at 4.1 V until C/2", "Discharge at 2 W for 1 hour", ]) model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model, experiment=experiment) sim.solve(solver=pybamm.CasadiSolver()) self.assertEqual(sim._solution.termination, "final time")
def test_compare_outputs_thermal(self): # load models - for the default params we expect x-full and lumped to # agree as the temperature is practically independent of x options = [{"thermal": opt} for opt in ["lumped", "x-full"]] model_combos = [ ([pybamm.lithium_ion.SPM(opt) for opt in options]), ([pybamm.lithium_ion.SPMe(opt) for opt in options]), ([pybamm.lithium_ion.DFN(opt) for opt in options]), ] for models in model_combos: # load parameter values (same for all models) param = models[0].default_parameter_values # for x-full, cooling is only implemented on the surfaces # so set other forms of cooling to zero for comparison. param.update( { "Negative current collector" + " surface heat transfer coefficient [W.m-2.K-1]": 5, "Positive current collector" + " surface heat transfer coefficient [W.m-2.K-1]": 5, "Negative tab heat transfer coefficient [W.m-2.K-1]": 0, "Positive tab heat transfer coefficient [W.m-2.K-1]": 0, "Edge heat transfer coefficient [W.m-2.K-1]": 0, } ) for model in models: param.process_model(model) # set mesh var = pybamm.standard_spatial_vars var_pts = {var.x_n: 5, var.x_s: 5, var.x_p: 5, var.r_n: 5, var.r_p: 5} # discretise models discs = {} for model in models: geometry = model.default_geometry param.process_geometry(geometry) mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) disc = pybamm.Discretisation(mesh, model.default_spatial_methods) disc.process_model(model) discs[model] = disc # solve model solutions = [] t_eval = np.linspace(0, 3600, 100) for model in models: solution = pybamm.CasadiSolver().solve(model, t_eval) solutions.append(solution) # compare outputs comparison = StandardOutputComparison(solutions) comparison.test_all(skip_first_timestep=True)
def default_solver(self): """ Return default solver based on whether model is ODE model or DAE model. There are bugs with KLU on the lead-acid models. """ if len(self.algebraic) == 0: return pybamm.ScipySolver() elif pybamm.have_scikits_odes(): return pybamm.ScikitsDaeSolver() else: # pragma: no cover return pybamm.CasadiSolver(mode="safe")