def test_root_find_fail(self): class Model: y0 = np.array([2]) t = casadi.MX.sym("t") y = casadi.MX.sym("y") p = casadi.MX.sym("p") casadi_algebraic = casadi.Function("alg", [t, y, p], [y**2 + 1]) def algebraic_eval(self, t, y, inputs): # algebraic equation has no real root return y**2 + 1 model = Model() solver = pybamm.CasadiAlgebraicSolver() with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: .../casadi", ): solver._integrate(model, np.array([0]), {}) solver = pybamm.CasadiAlgebraicSolver(error_on_fail=False) with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: solver terminated", ): solver._integrate(model, np.array([0]), {})
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.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]) n = disc.mesh["negative electrode"].npts solver = pybamm.CasadiAlgebraicSolver() solution = solver.solve(model, [0]) p = np.linspace(0, 1, n)[:, np.newaxis] np.testing.assert_array_almost_equal( solution["var"].value({"param": 3 * np.ones(n)}), -3) np.testing.assert_array_almost_equal( solution["var"].value({"param": 2 * p}), -2 * p) np.testing.assert_array_almost_equal( solution["var"].sensitivity({"param": 3 * np.ones(n)}), -np.eye(40)) np.testing.assert_array_almost_equal( solution["var"].sensitivity({"param": p}), -np.eye(40))
def test_root_find_fail(self): class Model: y0 = np.array([2]) t = casadi.MX.sym("t") y = casadi.MX.sym("y") p = casadi.MX.sym("p") length_scales = {} rhs = {} casadi_algebraic = casadi.Function("alg", [t, y, p], [y**2 + 1]) bounds = (np.array([-np.inf]), np.array([np.inf])) interpolant_extrapolation_events_eval = [] def algebraic_eval(self, t, y, inputs): # algebraic equation has no real root return y**2 + 1 model = Model() solver = pybamm.CasadiAlgebraicSolver() with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: .../casadi"): solver._integrate(model, np.array([0]), {}) solver = pybamm.CasadiAlgebraicSolver( extra_options={"error_on_fail": False}) with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: solver terminated"): solver._integrate(model, np.array([0]), {}) # Model returns Nan class NaNModel: y0 = np.array([-2]) t = casadi.MX.sym("t") y = casadi.MX.sym("y") p = casadi.MX.sym("p") rhs = {} casadi_algebraic = casadi.Function("alg", [t, y, p], [y**0.5]) bounds = (np.array([-np.inf]), np.array([np.inf])) interpolant_extrapolation_events_eval = [] def algebraic_eval(self, t, y, inputs): # algebraic equation has no real root return y**0.5 model = NaNModel() with self.assertRaisesRegex( pybamm.SolverError, "Could not find acceptable solution: solver returned NaNs", ): 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.CasadiAlgebraicSolver() solution = solver.solve(model, t_eval) sol = np.vstack((3 * t_eval, 6 * t_eval)) np.testing.assert_array_almost_equal(solution.y, sol) np.testing.assert_array_almost_equal( model.variables["var1"].evaluate(t=t_eval, y=solution.y).flatten(), sol[0, :], ) np.testing.assert_array_almost_equal( model.variables["var2"].evaluate(t=t_eval, y=solution.y).flatten(), sol[1, :], )
def test_least_squares_fit_input_in_initial_conditions(self): # Simple system: a single algebraic equation var = pybamm.Variable("var", domain="negative electrode") model = pybamm.BaseModel() p = pybamm.InputParameter("p") q = pybamm.InputParameter("q") model.algebraic = {var: (var - p)} model.initial_conditions = {var: p} model.variables = {"objective": (var - q)**2 + (p - 3)**2} # create discretisation disc = tests.get_discretisation_for_testing() disc.process_model(model) # Solve solver = pybamm.CasadiAlgebraicSolver() solution = solver.solve(model, [0]) 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 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_model_solver_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.CasadiAlgebraicSolver(tol=1e-12) solution = solver.solve(model) np.testing.assert_array_almost_equal(solution["var1"].data, 3 * np.pi / 2)
def test_simple_root_find(self): # Simple system: a single algebraic equation var = pybamm.Variable("var") model = pybamm.BaseModel() model.algebraic = {var: var + 2} model.initial_conditions = {var: 2} # create discretisation disc = pybamm.Discretisation() disc.process_model(model) # Solve solver = pybamm.CasadiAlgebraicSolver() solution = solver.solve(model, np.linspace(0, 1, 10)) np.testing.assert_array_equal(solution.y, -2)
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.CasadiAlgebraicSolver() solution = solver.solve(model, np.linspace(0, 1, 10), inputs={"value": 7}) np.testing.assert_array_equal(solution.y, -7)
def __init__( self, T_inf=5.0, npoints=129, tol=1e-8, lambda_reg=1e-5, plot=True, savesol=False, ): self.T_inf = T_inf # Temperature of the one-dimensional body # Boolean flag whether to plot the solution at the end of simulation self.plot = plot # Boolean flag whether to save the converged temperatures self.savesol = savesol self.lambda_reg = lambda_reg # Regularization constant for objective function # Initialize beta self.beta = 1 # Initialize flags self.has_obj_and_jac_funs = False # Define model model = RHTModel() # Define settings parameter_values = model.default_parameter_values parameter_values.update({"T_inf": T_inf, "beta": "[input]"}) var_pts = {model.x: npoints} solver = pybamm.CasadiAlgebraicSolver(tol=tol) # Create simulation sim = pybamm.Simulation(model, parameter_values=parameter_values, solver=solver, var_pts=var_pts) t_eval = [0] sim.solve(t_eval, inputs={"beta": "[sym]{}".format(npoints)}) self.sol = sim.solution self.T = self.sol["Temperature"] self.x = self.T.x_sol fig, self.ax = plt.subplots()
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_solve_with_symbolic_input_in_initial_conditions(self): # Simple system: a single algebraic equation var = pybamm.Variable("var") model = pybamm.BaseModel() model.algebraic = {var: var + 2} model.initial_conditions = {var: pybamm.InputParameter("param")} model.variables = {"var": var} # create discretisation disc = pybamm.Discretisation() disc.process_model(model) # Solve solver = pybamm.CasadiAlgebraicSolver() solution = solver.solve(model, [0]) np.testing.assert_array_equal(solution["var"].value({"param": 7}), -2) np.testing.assert_array_equal(solution["var"].value({"param": 3}), -2) np.testing.assert_array_equal( solution["var"].sensitivity({"param": 3}), 0)
def test_algebraic_solver_init(self): solver = pybamm.CasadiAlgebraicSolver(tol=1e-4) self.assertEqual(solver.tol, 1e-4) solver.tol = 1e-5 self.assertEqual(solver.tol, 1e-5)
def get_cycle_summary_variables(cycle_solution, esoh_sim): Q = cycle_solution["Discharge capacity [A.h]"].data min_Q = np.min(Q) max_Q = np.max(Q) cycle_summary_variables = pybamm.FuzzyDict( { "Minimum measured discharge capacity [A.h]": min_Q, "Maximum measured discharge capacity [A.h]": max_Q, "Measured capacity [A.h]": max_Q - min_Q, } ) degradation_variables = [ "Negative electrode capacity [A.h]", "Positive electrode capacity [A.h]", # LAM, LLI "Loss of active material in negative electrode [%]", "Loss of active material in positive electrode [%]", "Loss of lithium inventory [%]", "Loss of lithium inventory, including electrolyte [%]", # Total lithium "Total lithium [mol]", "Total lithium in electrolyte [mol]", "Total lithium in positive electrode [mol]", "Total lithium in negative electrode [mol]", "Total lithium in particles [mol]", # Lithium lost "Total lithium lost [mol]", "Total lithium lost from particles [mol]", "Total lithium lost from electrolyte [mol]", "Loss of lithium to negative electrode SEI [mol]", "Loss of lithium to positive electrode SEI [mol]", "Loss of lithium to negative electrode lithium plating [mol]", "Loss of lithium to positive electrode lithium plating [mol]", "Loss of capacity to negative electrode SEI [A.h]", "Loss of capacity to positive electrode SEI [A.h]", "Loss of capacity to negative electrode lithium plating [A.h]", "Loss of capacity to positive electrode lithium plating [A.h]", "Total lithium lost to side reactions [mol]", "Total capacity lost to side reactions [A.h]", # Resistance "Local ECM resistance [Ohm]", ] first_state = cycle_solution.first_state last_state = cycle_solution.last_state for var in degradation_variables: data_first = first_state[var].data data_last = last_state[var].data cycle_summary_variables[var] = data_last[0] var_lowercase = var[0].lower() + var[1:] cycle_summary_variables["Change in " + var_lowercase] = ( data_last[0] - data_first[0] ) if esoh_sim is not None: V_min = esoh_sim.parameter_values["Lower voltage cut-off [V]"] V_max = esoh_sim.parameter_values["Upper voltage cut-off [V]"] C_n = last_state["Negative electrode capacity [A.h]"].data[0] C_p = last_state["Positive electrode capacity [A.h]"].data[0] n_Li = last_state["Total lithium in particles [mol]"].data[0] if esoh_sim.solution is not None: # initialize with previous solution if it is available esoh_sim.built_model.set_initial_conditions_from(esoh_sim.solution) solver = None else: x_100_init = np.max(cycle_solution["Negative electrode SOC"].data) # make sure x_0 > 0 C_init = np.minimum(0.95 * (C_n * x_100_init), max_Q - min_Q) # Solve the esoh model and add outputs to the summary variables # use CasadiAlgebraicSolver if there are interpolants if isinstance( esoh_sim.parameter_values["Negative electrode OCP [V]"], tuple ) or isinstance( esoh_sim.parameter_values["Positive electrode OCP [V]"], tuple ): solver = pybamm.CasadiAlgebraicSolver() # Choose x_100_init so as not to violate the interpolation limits if isinstance( esoh_sim.parameter_values["Positive electrode OCP [V]"], tuple ): y_100_min = np.min( esoh_sim.parameter_values["Positive electrode OCP [V]"][1][:, 0] ) x_100_max = ( n_Li * pybamm.constants.F.value / 3600 - y_100_min * C_p ) / C_n x_100_init = np.minimum(x_100_init, 0.99 * x_100_max) else: solver = None # Update initial conditions using the cycle solution esoh_sim.build() esoh_sim.built_model.set_initial_conditions_from( {"x_100": x_100_init, "C": C_init} ) esoh_sol = esoh_sim.solve( [0], inputs={ "V_min": V_min, "V_max": V_max, "C_n": C_n, "C_p": C_p, "n_Li": n_Li, }, solver=solver, ) for var in esoh_sim.built_model.variables: cycle_summary_variables[var] = esoh_sol[var].data[0] cycle_summary_variables["Capacity [A.h]"] = cycle_summary_variables["C"] return cycle_summary_variables
def default_solver(self): """Return default solver based on whether model is ODE/DAE or algebraic""" if len(self.rhs) == 0 and len(self.algebraic) != 0: return pybamm.CasadiAlgebraicSolver() else: return pybamm.CasadiSolver(mode="safe")
def default_solver(self): return pybamm.CasadiAlgebraicSolver()
save_folder = "example/RHT_pybamm/True_solutions" os.makedirs(save_folder, exist_ok=True) fig, ax = plt.subplots() ax.set_xlabel("x") ax.set_ylabel("Temperature") # Solve for various T_inf for T_inf in np.linspace(5.0, 50, 10): # Define model model = RHTModel() # Define settings parameter_values = model.default_parameter_values parameter_values.update({"T_inf": T_inf}) var_pts = {model.x: n_points} solver = pybamm.CasadiAlgebraicSolver(tol=tol) t_eval = np.array([0, 1]) # Create simulation sim = pybamm.Simulation( model, parameter_values=parameter_values, solver=solver, var_pts=var_pts ) sim.solve(t_eval, inputs={"T_inf": T_inf}) sol = sim.solution x = sol["x"].data[:, -1] T_final = sol["Temperature"].data[:, -1] beta_decoupled_final = sol["beta_decoupled"].data[:, -1] np.savetxt("{}/solution_{}".format(save_folder, T_inf), T_final) ax.plot(x, T_final, label=T_inf)