Ejemplo n.º 1
0
    def test_solve_ode_model_with_dae_solver(self):
        # Create model
        model = pybamm.BaseModel()
        var = pybamm.Variable("var")
        model.rhs = {var: 0.1 * var}
        model.initial_conditions = {var: 1}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
Ejemplo n.º 2
0
    def test_model_step_nonsmooth_events(self):
        # Create model
        model = pybamm.BaseModel()
        model.timescale = pybamm.Scalar(1)
        var1 = pybamm.Variable("var1")
        var2 = pybamm.Variable("var2")
        a = 0.6
        discontinuities = (np.arange(3) + 1) * a

        model.rhs = {var1: pybamm.Modulo(pybamm.t * model.timescale, a)}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 0, var2: 0}
        model.events = [
            pybamm.Event("var1 = 0.55", pybamm.min(var1 - 0.55)),
            pybamm.Event("var2 = 1.2", pybamm.min(var2 - 1.2)),
        ]
        for discontinuity in discontinuities:
            model.events.append(
                pybamm.Event("nonsmooth rate", pybamm.Scalar(discontinuity)))
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        step_solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        dt = 0.05
        time = 0
        end_time = 3
        step_solution = None
        while time < end_time:
            step_solution = step_solver.step(step_solution,
                                             model,
                                             dt=dt,
                                             npts=10)
            time += dt
        np.testing.assert_array_less(step_solution.y[0, :-1], 0.55)
        np.testing.assert_array_less(step_solution.y[-1, :-1], 1.2)
        np.testing.assert_equal(step_solution.t_event[0], step_solution.t[-1])
        np.testing.assert_array_equal(step_solution.y_event[:, 0],
                                      step_solution.y[:, -1])
        var1_soln = (step_solution.t %
                     a)**2 / 2 + a**2 / 2 * (step_solution.t // a)
        var2_soln = 2 * var1_soln
        np.testing.assert_array_almost_equal(step_solution.y[0],
                                             var1_soln,
                                             decimal=5)
        np.testing.assert_array_almost_equal(step_solution.y[-1],
                                             var2_soln,
                                             decimal=5)
Ejemplo n.º 3
0
    def test_discretisation(self):
        param = pybamm.standard_parameters_lithium_ion
        model_n = pybamm.interface.lithium_ion.ButlerVolmer(param, "Negative")
        j_n = model_n.get_coupled_variables(
            self.variables)["Negative electrode interfacial current density"]
        model_p = pybamm.interface.lithium_ion.ButlerVolmer(param, "Positive")
        j_p = model_p.get_coupled_variables(
            self.variables)["Positive electrode interfacial current density"]
        j = pybamm.Concatenation(j_n,
                                 pybamm.PrimaryBroadcast(0,
                                                         ["separator"]), j_p)

        # Process parameters and discretise
        parameter_values = pybamm.lithium_ion.BaseModel(
        ).default_parameter_values
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        disc.set_variable_slices([
            self.c_e_n,
            self.c_e_p,
            self.delta_phi_s_n,
            self.delta_phi_s_p,
            self.c_s_n_surf,
            self.c_s_p_surf,
        ])

        j_n = disc.process_symbol(parameter_values.process_symbol(j_n))
        j_p = disc.process_symbol(parameter_values.process_symbol(j_p))
        j = disc.process_symbol(parameter_values.process_symbol(j))

        # test butler-volmer in each electrode
        submesh = np.concatenate([
            mesh["negative electrode"][0].nodes,
            mesh["positive electrode"][0].nodes
        ])
        y = np.concatenate([submesh**2, submesh**3, submesh**4])
        self.assertEqual(
            j_n.evaluate(None, y).shape,
            (mesh["negative electrode"][0].npts, 1))
        self.assertEqual(
            j_p.evaluate(None, y).shape,
            (mesh["positive electrode"][0].npts, 1))

        # test concatenated butler-volmer
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        whole_cell_mesh = disc.mesh.combine_submeshes(*whole_cell)
        self.assertEqual(
            j.evaluate(None, y).shape, (whole_cell_mesh[0].npts, 1))
Ejemplo n.º 4
0
    def test_model_solver_ode_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=whole_cell)
        model.rhs = {var: 0.1 * var}
        model.initial_conditions = {var: 1}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
Ejemplo n.º 5
0
    def test_process_with_no_check(self):
        # create model
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        c = pybamm.Variable("c", domain=whole_cell)
        N = pybamm.grad(c)
        model = pybamm.BaseModel()
        model.rhs = {c: pybamm.div(N)}
        model.initial_conditions = {c: pybamm.Scalar(3)}
        model.boundary_conditions = {
            c: {"left": (0, "Neumann"), "right": (0, "Neumann")}
        }
        model.variables = {"c": c, "N": N}

        # create discretisation
        disc = get_discretisation_for_testing()
        disc.process_model(model, check_model=False)
Ejemplo n.º 6
0
    def test_save(self):
        model = pybamm.BaseModel()
        # create both 1D and 2D variables
        c = pybamm.Variable("c")
        d = pybamm.Variable("d", domain="negative electrode")
        model.rhs = {c: -c, d: 1}
        model.initial_conditions = {c: 1, d: 2}
        model.variables = {"c": c, "d": d, "2c": 2 * c}

        disc = get_discretisation_for_testing()
        disc.process_model(model)
        solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1))

        # test save data
        with self.assertRaises(ValueError):
            solution.save_data("test.pickle")
        # set variables first then save
        solution.update(["c", "d"])
        solution.save_data("test.pickle")
        data_load = pybamm.load("test.pickle")
        np.testing.assert_array_equal(solution.data["c"], data_load["c"])
        np.testing.assert_array_equal(solution.data["d"], data_load["d"])

        # to matlab
        solution.save_data("test.mat", to_format="matlab")
        data_load = loadmat("test.mat")
        np.testing.assert_array_equal(solution.data["c"], data_load["c"].flatten())
        np.testing.assert_array_equal(solution.data["d"], data_load["d"])

        # to csv
        with self.assertRaisesRegex(
            ValueError, "only 0D variables can be saved to csv"
        ):
            solution.save_data("test.csv", to_format="csv")
        # only save "c" and "2c"
        solution.save_data("test.csv", ["c", "2c"], to_format="csv")
        # read csv
        df = pd.read_csv("test.csv")
        np.testing.assert_array_almost_equal(df["c"], solution.data["c"])
        np.testing.assert_array_almost_equal(df["2c"], solution.data["2c"])

        # test save whole solution
        solution.save("test.pickle")
        solution_load = pybamm.load("test.pickle")
        self.assertEqual(solution.model.name, solution_load.model.name)
        np.testing.assert_array_equal(solution["c"].entries, solution_load["c"].entries)
        np.testing.assert_array_equal(solution["d"].entries, solution_load["d"].entries)
Ejemplo n.º 7
0
    def test_secondary_broadcast_2D(self):
        # secondary broadcast in 2D --> Matrix multiplication
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        var = pybamm.Variable("var", domain=["negative particle"])
        broad = pybamm.SecondaryBroadcast(var, "negative electrode")

        disc.set_variable_slices([var])
        broad_disc = disc.process_symbol(broad)
        self.assertIsInstance(broad_disc, pybamm.MatrixMultiplication)
        self.assertIsInstance(broad_disc.children[0], pybamm.Matrix)
        self.assertIsInstance(broad_disc.children[1], pybamm.StateVector)
        self.assertEqual(
            broad_disc.shape,
            (mesh["negative particle"][0].npts *
             mesh["negative electrode"][0].npts, 1),
        )
Ejemplo n.º 8
0
    def test_check_tab_bcs_error(self):
        a = pybamm.Variable("a", domain=["current collector"])
        b = pybamm.Variable("b", domain=["negative electrode"])
        bcs = {
            "negative tab": (0, "Dirichlet"),
            "positive tab": (0, "Neumann")
        }

        disc = get_discretisation_for_testing()

        # for 0D bcs keys should be unchanged
        new_bcs = disc.check_tab_conditions(a, bcs)
        self.assertListEqual(list(bcs.keys()), list(new_bcs.keys()))

        # error if domain not "current collector"
        with self.assertRaisesRegex(pybamm.ModelError, "Boundary conditions"):
            disc.check_tab_conditions(b, bcs)
Ejemplo n.º 9
0
    def test_model_solver_dae_inputs_events(self):
        # Create model
        for form in ["python", "casadi"]:
            model = pybamm.BaseModel()
            model.convert_to_format = form
            whole_cell = [
                "negative electrode", "separator", "positive electrode"
            ]
            var1 = pybamm.Variable("var1", domain=whole_cell)
            var2 = pybamm.Variable("var2", domain=whole_cell)
            model.rhs = {var1: pybamm.InputParameter("rate 1") * var1}
            model.algebraic = {
                var2: pybamm.InputParameter("rate 2") * var1 - var2
            }
            model.initial_conditions = {var1: 1, var2: 2}
            model.events = [
                pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
                pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
            ]
            disc = get_discretisation_for_testing()
            disc.process_model(model)

            # Solve
            if form == "python":
                solver = pybamm.ScikitsDaeSolver(rtol=1e-8,
                                                 atol=1e-8,
                                                 root_method="lm")
            else:
                solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
            t_eval = np.linspace(0, 5, 100)
            solution = solver.solve(model,
                                    t_eval,
                                    inputs={
                                        "rate 1": 0.1,
                                        "rate 2": 2
                                    })
            np.testing.assert_array_less(solution.y[0, :-1], 1.5)
            np.testing.assert_array_less(solution.y[-1, :-1], 2.5)
            np.testing.assert_array_equal(solution.y_event[:, 0],
                                          solution.y[:, -1])
            np.testing.assert_equal(solution.t_event[0], solution.t[-1])

            np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
            np.testing.assert_allclose(solution.y[-1],
                                       2 * np.exp(0.1 * solution.t))
Ejemplo n.º 10
0
    def test_process_complex_expression(self):
        var1 = pybamm.Variable("var1")
        var2 = pybamm.Variable("var2")
        scal1 = pybamm.Scalar(1)
        scal2 = pybamm.Scalar(2)
        scal3 = pybamm.Scalar(3)
        scal4 = pybamm.Scalar(4)
        expression = (scal1 * (scal3 + var2)) / ((var1 - scal4) + scal2)

        # create discretisation
        disc = get_discretisation_for_testing()

        disc.y_slices = {var1.id: [slice(53)], var2.id: [slice(53, 106)]}
        exp_disc = disc.process_symbol(expression)
        self.assertIsInstance(exp_disc, pybamm.Division)
        # left side
        self.assertIsInstance(exp_disc.children[0], pybamm.Multiplication)
        self.assertIsInstance(exp_disc.children[0].children[0], pybamm.Scalar)
        self.assertIsInstance(exp_disc.children[0].children[1], pybamm.Addition)
        self.assertTrue(
            isinstance(exp_disc.children[0].children[1].children[0], pybamm.Scalar)
        )
        self.assertTrue(
            isinstance(exp_disc.children[0].children[1].children[1], pybamm.StateVector)
        )
        self.assertEqual(
            exp_disc.children[0].children[1].children[1].y_slices[0],
            disc.y_slices[var2.id][0],
        )
        # right side
        self.assertIsInstance(exp_disc.children[1], pybamm.Addition)
        self.assertTrue(
            isinstance(exp_disc.children[1].children[0], pybamm.Subtraction)
        )
        self.assertTrue(
            isinstance(exp_disc.children[1].children[0].children[0], pybamm.StateVector)
        )
        self.assertEqual(
            exp_disc.children[1].children[0].children[0].y_slices[0],
            disc.y_slices[var1.id][0],
        )
        self.assertTrue(
            isinstance(exp_disc.children[1].children[0].children[1], pybamm.Scalar)
        )
        self.assertIsInstance(exp_disc.children[1].children[1], pybamm.Scalar)
Ejemplo n.º 11
0
    def test_simplify_concatenation_state_vectors(self):
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        a = pybamm.Variable("a", domain=["negative electrode"])
        b = pybamm.Variable("b", domain=["separator"])
        c = pybamm.Variable("c", domain=["positive electrode"])
        conc = pybamm.Concatenation(a, b, c)
        disc.set_variable_slices([a, b, c])
        conc_disc = disc.process_symbol(conc)
        conc_simp = conc_disc.simplify()

        y = mesh.combine_submeshes(*conc.domain)[0].nodes ** 2
        self.assertIsInstance(conc_simp, pybamm.StateVector)
        self.assertEqual(len(conc_simp.y_slices), 1)
        self.assertEqual(conc_simp.y_slices[0].start, 0)
        self.assertEqual(conc_simp.y_slices[0].stop, len(y))
        np.testing.assert_array_equal(conc_disc.evaluate(y=y), conc_simp.evaluate(y=y))
Ejemplo n.º 12
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,
            )
    def test_concatenated_parameters(self):
        # create
        s_param = pybamm.standard_parameters_lead_acid.s_plus_S
        self.assertIsInstance(s_param, pybamm.Concatenation)
        self.assertEqual(
            s_param.domain,
            ["negative electrode", "separator", "positive electrode"])

        # process parameters and discretise
        parameter_values = pybamm.ParameterValues(
            chemistry=pybamm.parameter_sets.Sulzer2019)
        disc = get_discretisation_for_testing()
        processed_s = disc.process_symbol(
            parameter_values.process_symbol(s_param))

        # test output
        combined_submeshes = disc.mesh.combine_submeshes(
            "negative electrode", "separator", "positive electrode")
        self.assertEqual(processed_s.shape, (combined_submeshes.npts, 1))
Ejemplo n.º 14
0
    def test_concatenation_of_scalars(self):
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        a = pybamm.PrimaryBroadcast(5, ["negative electrode"])
        b = pybamm.PrimaryBroadcast(4, ["separator"])

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        variables = [pybamm.Variable("var", domain=whole_cell)]
        disc.set_variable_slices(variables)

        eqn = pybamm.Concatenation(a, b)
        eqn_disc = disc.process_symbol(eqn)
        expected_vector = np.concatenate([
            5 * np.ones_like(mesh["negative electrode"][0].nodes),
            4 * np.ones_like(mesh["separator"][0].nodes),
        ])[:, np.newaxis]
        np.testing.assert_allclose(eqn_disc.evaluate(), expected_vector)
Ejemplo n.º 15
0
    def test_model_solver_dae_bad_ics_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 3}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm")
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
    def test_solve_with_symbolic_input_1D_scalar_input(self):
        var = pybamm.Variable("var", "negative electrode")
        model = pybamm.BaseModel()
        param = pybamm.InputParameter("param")
        model.algebraic = {var: var + param}
        model.initial_conditions = {var: 2}
        model.variables = {"var": var}

        # create discretisation
        disc = tests.get_discretisation_for_testing()
        disc.process_model(model)

        # Solve - scalar input
        solver = pybamm.CasadiAlgebraicSolver()
        solution = solver.solve(model, [0])
        np.testing.assert_array_equal(solution["var"].value({"param": 7}), -7)
        np.testing.assert_array_equal(solution["var"].value({"param": 3}), -3)
        np.testing.assert_array_equal(
            solution["var"].sensitivity({"param": 3}), -1)
Ejemplo n.º 17
0
    def test_processed_variable_2D(self):
        t = pybamm.t
        var = pybamm.Variable("var",
                              domain=["negative electrode", "separator"])
        x = pybamm.SpatialVariable("x",
                                   domain=["negative electrode", "separator"])
        eqn = t * var + x

        # On nodes
        disc = tests.get_discretisation_for_testing()
        disc.set_variable_slices([var])
        x_sol = disc.process_symbol(x).entries[:, 0]
        var_sol = disc.process_symbol(var)
        eqn_sol = disc.process_symbol(eqn)
        t_sol = np.linspace(0, 1)
        y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5)

        processed_var = pybamm.ProcessedVariable(var_sol,
                                                 t_sol,
                                                 y_sol,
                                                 mesh=disc.mesh)
        np.testing.assert_array_equal(processed_var.entries[1:-1], y_sol)
        np.testing.assert_array_equal(processed_var(t_sol, x_sol), y_sol)
        processed_eqn = pybamm.ProcessedVariable(eqn_sol,
                                                 t_sol,
                                                 y_sol,
                                                 mesh=disc.mesh)
        np.testing.assert_array_equal(processed_eqn(t_sol, x_sol),
                                      t_sol * y_sol + x_sol[:, np.newaxis])

        # Test extrapolation
        np.testing.assert_array_equal(processed_var.entries[0],
                                      2 * y_sol[0] - y_sol[1])
        np.testing.assert_array_equal(processed_var.entries[1],
                                      2 * y_sol[-1] - y_sol[-2])

        # On edges
        x_s_edge = pybamm.Matrix(disc.mesh["separator"][0].edges,
                                 domain="separator")
        processed_x_s_edge = pybamm.ProcessedVariable(x_s_edge, t_sol, y_sol,
                                                      disc.mesh)
        np.testing.assert_array_equal(x_s_edge.entries[:, 0],
                                      processed_x_s_edge.entries[1:-1, 0])
Ejemplo n.º 18
0
    def test_broadcast(self):
        whole_cell = ["negative electrode", "separator", "positive electrode"]

        a = pybamm.InputParameter("a")
        var = pybamm.Variable("var")

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        combined_submesh = mesh.combine_submeshes(*whole_cell)

        # scalar
        broad = disc.process_symbol(pybamm.FullBroadcast(a, whole_cell, {}))
        np.testing.assert_array_equal(
            broad.evaluate(inputs={"a": 7}),
            7 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis]),
        )
        self.assertEqual(broad.domain, whole_cell)

        broad_disc = disc.process_symbol(broad)
        self.assertIsInstance(broad_disc, pybamm.Multiplication)
        self.assertIsInstance(broad_disc.children[0], pybamm.InputParameter)
        self.assertIsInstance(broad_disc.children[1], pybamm.Vector)

        # process Broadcast variable
        disc.y_slices = {var.id: [slice(1)]}
        broad1 = pybamm.FullBroadcast(var, ["negative electrode"], None)
        broad1_disc = disc.process_symbol(broad1)
        self.assertIsInstance(broad1_disc, pybamm.Multiplication)
        self.assertIsInstance(broad1_disc.children[0], pybamm.StateVector)
        self.assertIsInstance(broad1_disc.children[1], pybamm.Vector)

        # broadcast to edges
        broad_to_edges = pybamm.FullBroadcastToEdges(a, ["negative electrode"],
                                                     None)
        broad_to_edges_disc = disc.process_symbol(broad_to_edges)
        np.testing.assert_array_equal(
            broad_to_edges_disc.evaluate(inputs={"a": 7}),
            7 *
            np.ones_like(mesh["negative electrode"][0].edges[:, np.newaxis]),
        )
Ejemplo n.º 19
0
    def test_discretise_spatial_operator(self):
        # create discretisation
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=whole_cell)
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        variables = [var]
        disc.set_variable_slices(variables)

        # Simple expressions
        for eqn in [pybamm.grad(var), pybamm.div(var)]:
            eqn_disc = disc.process_symbol(eqn)

            self.assertIsInstance(eqn_disc, pybamm.MatrixMultiplication)
            self.assertIsInstance(eqn_disc.children[0], pybamm.Matrix)
            self.assertIsInstance(eqn_disc.children[1], pybamm.StateVector)

            combined_submesh = mesh.combine_submeshes(*whole_cell)
            y = combined_submesh[0].nodes**2
            var_disc = disc.process_symbol(var)
            # grad and var are identity operators here (for testing purposes)
            np.testing.assert_array_equal(eqn_disc.evaluate(None, y),
                                          var_disc.evaluate(None, y))

        # More complex expressions
        for eqn in [var * pybamm.grad(var), var * pybamm.div(var)]:
            eqn_disc = disc.process_symbol(eqn)

            self.assertIsInstance(eqn_disc, pybamm.Multiplication)
            self.assertIsInstance(eqn_disc.children[0], pybamm.StateVector)
            self.assertIsInstance(eqn_disc.children[1],
                                  pybamm.MatrixMultiplication)
            self.assertIsInstance(eqn_disc.children[1].children[0],
                                  pybamm.Matrix)
            self.assertIsInstance(eqn_disc.children[1].children[1],
                                  pybamm.StateVector)

            y = combined_submesh[0].nodes**2
            var_disc = disc.process_symbol(var)
            # grad and var are identity operators here (for testing purposes)
            np.testing.assert_array_equal(eqn_disc.evaluate(None, y),
                                          var_disc.evaluate(None, y)**2)
Ejemplo n.º 20
0
    def test_model_solver_ode_events(self):
        # Create model
        model = pybamm.BaseModel()
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=whole_cell)
        model.rhs = {var: 0.1 * var}
        model.initial_conditions = {var: 1}
        model.events = {
            "2 * var = 2.5": pybamm.min(2 * var - 2.5),
            "var = 1.5": pybamm.min(var - 1.5),
        }
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9)
        t_eval = np.linspace(0, 10, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_array_less(solution.y[0], 1.5)
        np.testing.assert_array_less(solution.y[0], 1.25)
Ejemplo n.º 21
0
    def test_exceptions(self):
        c_n = pybamm.Variable("c", domain=["negative electrode"])
        N_n = pybamm.grad(c_n)
        c_s = pybamm.Variable("c", domain=["separator"])
        N_s = pybamm.grad(c_s)
        model = pybamm.BaseModel()
        model.rhs = {c_n: pybamm.div(N_n), c_s: pybamm.div(N_s)}
        model.initial_conditions = {
            c_n: pybamm.Scalar(3),
            c_s: pybamm.Scalar(1)
        }
        model.boundary_conditions = {
            c_n: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
            c_s: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
        }

        disc = get_discretisation_for_testing()

        # check raises error if different sized key and output var
        model.variables = {c_n.name: c_s}
        with self.assertRaisesRegex(pybamm.ModelError, "variable and its eqn"):
            disc.process_model(model)

        # check doesn't raise if concatenation
        model.variables = {c_n.name: pybamm.Concatenation(c_n, c_s)}
        disc.process_model(model, inplace=False)

        # check doesn't raise if broadcast
        model.variables = {
            c_n.name:
            pybamm.PrimaryBroadcast(pybamm.InputParameter("a"),
                                    ["negative electrode"])
        }
        disc.process_model(model)
Ejemplo n.º 22
0
    def test_model_step_dae_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.use_jacobian = False
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        solver = pybamm.ScikitsDaeSolver(rtol=1e-8,
                                         atol=1e-8,
                                         root_method="lm")

        # Step once
        dt = 1
        step_sol = solver.step(None, model, dt)
        np.testing.assert_array_equal(step_sol.t, [0, dt])
        np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t))
        np.testing.assert_allclose(step_sol.y[-1],
                                   2 * np.exp(0.1 * step_sol.t))

        # Step again (return 5 points)
        step_sol_2 = solver.step(step_sol, model, dt, npts=5)
        np.testing.assert_array_equal(
            step_sol_2.t,
            np.concatenate([np.array([0]),
                            np.linspace(dt, 2 * dt, 5)]))
        np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t))
        np.testing.assert_allclose(step_sol_2.y[-1],
                                   2 * np.exp(0.1 * step_sol_2.t))

        # Check steps give same solution as solve
        t_eval = step_sol.t
        solution = solver.solve(model, t_eval)
        np.testing.assert_allclose(solution.y[0], step_sol.y[0, :])
        np.testing.assert_allclose(solution.y[-1], step_sol.y[-1, :])
Ejemplo n.º 23
0
    def test_processed_var_1D_fixed_t_interpolation(self):
        var = pybamm.Variable("var", domain=["negative electrode", "separator"])
        x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"])
        eqn = var + x

        disc = tests.get_discretisation_for_testing()
        disc.set_variable_slices([var])
        x_sol = disc.process_symbol(x).entries[:, 0]
        eqn_sol = disc.process_symbol(eqn)
        t_sol = np.array([1])
        y_sol = x_sol[:, np.newaxis]

        processed_var = pybamm.ProcessedVariable(
            eqn_sol, pybamm.Solution(t_sol, y_sol), warn=False
        )

        # vector
        np.testing.assert_array_almost_equal(
            processed_var(x=x_sol), 2 * x_sol[:, np.newaxis]
        )
        # scalar
        np.testing.assert_array_almost_equal(processed_var(x=0.5), 1)
Ejemplo n.º 24
0
    def test_discretisation_main_reaction(self):
        # With intercalation
        param = pybamm.standard_parameters_lead_acid
        model_n = pybamm.interface.BaseInterface(param, "Negative", "lead-acid main")
        j0_n = model_n._get_exchange_current_density(self.variables)
        model_p = pybamm.interface.BaseInterface(param, "Positive", "lead-acid main")
        j0_p = model_p._get_exchange_current_density(self.variables)
        # Process parameters and discretise
        parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        disc.set_variable_slices([self.c_e])
        j0_n = disc.process_symbol(parameter_values.process_symbol(j0_n))
        j0_p = disc.process_symbol(parameter_values.process_symbol(j0_p))

        # Test
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        submesh = mesh.combine_submeshes(*whole_cell)
        y = submesh.nodes ** 2
        # should evaluate to vectors with the right shape
        self.assertEqual(j0_n.evaluate(y=y).shape, (mesh["negative electrode"].npts, 1))
        self.assertEqual(j0_p.evaluate(y=y).shape, (mesh["positive electrode"].npts, 1))
Ejemplo n.º 25
0
    def test_domain_concatenation_simplify(self):
        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        a_dom = ["negative electrode"]
        b_dom = ["positive electrode"]
        a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom)
        b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom)

        conc = pybamm.DomainConcatenation([a, b], mesh)
        conc_simp = conc.simplify()

        # should be simplified to a vector
        self.assertIsInstance(conc_simp, pybamm.Vector)
        np.testing.assert_array_equal(
            conc_simp.evaluate(),
            np.concatenate([
                np.full((mesh[a_dom[0]].npts, 1), 2),
                np.full((mesh[b_dom[0]].npts, 1), 1),
            ]),
        )
Ejemplo n.º 26
0
    def test_model_solver(self):
        # Create model
        model = pybamm.BaseModel()
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.algebraic = {var1: var1 - 3, var2: 2 * var1 - var2}
        model.initial_conditions = {var1: pybamm.Scalar(1), var2: pybamm.Scalar(4)}
        model.variables = {"var1": var1, "var2": var2}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        sol = np.concatenate((np.ones(100) * 3, np.ones(100) * 6))[:, np.newaxis]

        # Solve
        solver = pybamm.AlgebraicSolver()
        solution = solver.solve(model)
        np.testing.assert_array_equal(
            model.variables["var1"].evaluate(t=None, y=solution.y), sol[:100]
        )
        np.testing.assert_array_equal(
            model.variables["var2"].evaluate(t=None, y=solution.y), sol[100:]
        )

        # Test time
        self.assertGreater(
            solution.total_time, solution.solve_time + solution.set_up_time
        )

        # Test without jacobian
        model.use_jacobian = False
        solution_no_jac = solver.solve(model)
        np.testing.assert_array_equal(
            model.variables["var1"].evaluate(t=None, y=solution_no_jac.y), sol[:100]
        )
        np.testing.assert_array_equal(
            model.variables["var2"].evaluate(t=None, y=solution_no_jac.y), sol[100:]
        )
Ejemplo n.º 27
0
    def test_model_solver_dae_with_jacobian_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1.0, var2: 2.0}
        model.initial_conditions_ydot = {var1: 0.1, var2: 0.2}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Add user-supplied Jacobian to model
        mesh = get_mesh_for_testing()
        combined_submesh = mesh.combine_submeshes("negative electrode",
                                                  "separator",
                                                  "positive electrode")
        N = combined_submesh.npts

        def jacobian(t, y):
            return np.block([
                [0.1 * np.eye(N), np.zeros((N, N))],
                [2.0 * np.eye(N), -1.0 * np.eye(N)],
            ])

        model.jacobian = jacobian
        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8,
                                         atol=1e-8,
                                         root_method="lm")
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))
Ejemplo n.º 28
0
    def test_model_solver_dae_no_nonsmooth_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        discontinuity = 5.6

        def nonsmooth_rate(t):
            return 0.1 * int(t < discontinuity) + 0.1

        def nonsmooth_mult(t):
            return int(t < discontinuity) + 1.0

        # put in an extra heaviside with no time dependence, this should be ignored by
        # the solver i.e. no extra discontinuities added
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-9,
                                         atol=1e-9,
                                         root_method="lm")

        # create two time series, one without a time point on the discontinuity,
        # and one with
        t_eval = np.linspace(0, 5, 10)
        solution = solver.solve(model, t_eval)

        # test solution, discontinuity should not be triggered
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))
Ejemplo n.º 29
0
    def test_model_solver_ode_jacobian_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: var1, var2: 1 - var1}
        model.initial_conditions = {var1: 1.0, var2: -1.0}
        model.variables = {"var1": var1, "var2": var2}

        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Add user-supplied Jacobian to model
        mesh = get_mesh_for_testing()
        combined_submesh = mesh.combine_submeshes("negative electrode",
                                                  "separator",
                                                  "positive electrode")
        N = combined_submesh.npts

        # Solve
        solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)

        T, Y = solution.t, solution.y
        np.testing.assert_array_almost_equal(
            model.variables["var1"].evaluate(T, Y),
            np.ones((N, T.size)) * np.exp(T[np.newaxis, :]),
        )
        np.testing.assert_array_almost_equal(
            model.variables["var2"].evaluate(T, Y),
            np.ones(
                (N, T.size)) * (T[np.newaxis, :] - np.exp(T[np.newaxis, :])),
        )
Ejemplo n.º 30
0
    def test_process_model_concatenation(self):
        # concatenation of variables as the key
        cn = pybamm.Variable("c", domain=["negative electrode"])
        cs = pybamm.Variable("c", domain=["separator"])
        cp = pybamm.Variable("c", domain=["positive electrode"])
        c = pybamm.Concatenation(cn, cs, cp)
        N = pybamm.grad(c)
        model = pybamm.BaseModel()
        model.rhs = {c: pybamm.div(N)}
        model.initial_conditions = {c: pybamm.Scalar(3)}

        model.boundary_conditions = {
            c: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            }
        }
        model.check_well_posedness()

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        combined_submesh = mesh.combine_submeshes("negative electrode",
                                                  "separator",
                                                  "positive electrode")

        disc.process_model(model)
        y0 = model.concatenated_initial_conditions.evaluate()
        np.testing.assert_array_equal(
            y0, 3 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis]))

        # grad and div are identity operators here
        np.testing.assert_array_equal(
            y0, model.concatenated_rhs.evaluate(None, y0))
        model.check_well_posedness()