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)
예제 #6
0
 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)
예제 #10
0
    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)
예제 #13
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)
예제 #14
0
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
예제 #15
0
 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")
예제 #16
0
 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)