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)
Exemple #4
0
    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")
Exemple #5
0
    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]"])
Exemple #6
0
    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
        )
Exemple #8
0
        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)
Exemple #10
0
 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)),
            ]),
        )
Exemple #12
0
    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])
Exemple #13
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()
     )
Exemple #14
0
    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))
Exemple #15
0
    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)
Exemple #17
0
    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)
Exemple #20
0
    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)
Exemple #21
0
    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)
Exemple #22
0
    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)
Exemple #23
0
    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])
Exemple #24
0
 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")
Exemple #25
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.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,
            )
Exemple #26
0
 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)
Exemple #27
0
    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)
Exemple #28
0
 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")
Exemple #29
0
    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)
Exemple #30
0
 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")