def test_processed_variable_2Dspace_scikit(self):
        var = pybamm.Variable("var", domain=["current collector"])

        disc = tests.get_2p1d_discretisation_for_testing()
        disc.set_variable_slices([var])
        y = disc.mesh["current collector"][0].edges["y"]
        z = disc.mesh["current collector"][0].edges["z"]
        var_sol = disc.process_symbol(var)
        var_sol.mesh = disc.mesh["current collector"]
        t_sol = np.array([0])
        u_sol = np.ones(var_sol.shape[0])[:, np.newaxis]

        processed_var = pybamm.ProcessedVariable(var_sol,
                                                 pybamm.Solution(t_sol, u_sol))
        np.testing.assert_array_equal(processed_var.entries,
                                      np.reshape(u_sol,
                                                 [len(y), len(z)]))
Example #2
0
    def get_fundamental_variables(self):

        phi_s_cn = pybamm.standard_variables.phi_s_cn

        variables = self._get_standard_negative_potential_variables(phi_s_cn)

        # TODO: grad not implemented for 2D yet
        i_cc = pybamm.Scalar(0)
        i_boundary_cc = pybamm.standard_variables.i_boundary_cc

        variables.update(self._get_standard_current_variables(i_cc, i_boundary_cc))

        # Lagrange multiplier for the composite current (enforce average)
        c = pybamm.Variable("Lagrange multiplier")
        variables.update({"Lagrange multiplier": c})

        return variables
Example #3
0
    def test_solve_ode_model_with_dae_solver_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        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,
                                         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))
Example #4
0
    def test_spherical_grad_div_shapes_Neumann_bcs(self):
        """Test grad and div with Neumann boundary conditions (applied by div on N)"""

        # create discretisation
        mesh = get_mesh_for_testing()
        spatial_methods = {"negative particle": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        combined_submesh = mesh.combine_submeshes("negative particle")

        # grad
        var = pybamm.Variable("var", domain="negative particle")
        grad_eqn = pybamm.grad(var)
        disc.set_variable_slices([var])
        grad_eqn_disc = disc.process_symbol(grad_eqn)

        constant_y = np.ones_like(combined_submesh[0].nodes[:, np.newaxis])
        np.testing.assert_array_equal(
            grad_eqn_disc.evaluate(None, constant_y),
            np.zeros_like(combined_submesh[0].edges[1:-1][:, np.newaxis]),
        )

        linear_y = combined_submesh[0].nodes
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, linear_y),
            np.ones_like(combined_submesh[0].edges[1:-1][:, np.newaxis]),
        )
        # div
        # div ( grad(r^2) ) == 6 , N_left = N_right = 0
        N = pybamm.grad(var)
        div_eqn = pybamm.div(N)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Scalar(0), "Neumann"),
                "right": (pybamm.Scalar(0), "Neumann"),
            }
        }
        disc.bcs = boundary_conditions
        div_eqn_disc = disc.process_symbol(div_eqn)

        linear_y = combined_submesh[0].nodes
        const = 6 * np.ones(combined_submesh[0].npts)

        np.testing.assert_array_almost_equal(
            div_eqn_disc.evaluate(None, const),
            np.zeros((combined_submesh[0].npts, 1)))
Example #5
0
    def test_solver_sensitivities(self):
        # Create model
        model = pybamm.BaseModel()
        model.convert_to_format = "jax"
        domain = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=domain)
        model.rhs = {var: -pybamm.InputParameter("rate") * var}
        model.initial_conditions = {var: 1}

        # create discretisation
        mesh = get_mesh_for_testing(xpts=10)
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)
        disc.process_model(model)

        # Solve
        t_eval = np.linspace(0, 10, 4)
        y0 = model.concatenated_initial_conditions.evaluate().reshape(-1)
        rhs = pybamm.EvaluatorJax(model.concatenated_rhs)

        def fun(y, t, inputs):
            return rhs.evaluate(t=t, y=y, inputs=inputs).reshape(-1)

        h = 0.0001
        rate = 0.1

        # create a dummy "model" where we calculate the sum of the time series
        @jax.jit
        def solve_bdf(rate):
            return jax.numpy.sum(
                pybamm.jax_bdf_integrate(fun,
                                         y0,
                                         t_eval, {'rate': rate},
                                         rtol=1e-9,
                                         atol=1e-9))

        # check answers with finite difference
        eval_plus = solve_bdf(rate + h)
        eval_neg = solve_bdf(rate - h)
        grad_num = (eval_plus - eval_neg) / (2 * h)

        grad_solve_bdf = jax.jit(jax.grad(solve_bdf))
        grad_bdf = grad_solve_bdf(rate)

        self.assertAlmostEqual(grad_bdf, grad_num, places=3)
    def test_processed_var_2D_scikit_interpolation(self):
        var = pybamm.Variable("var", domain=["current collector"])

        disc = tests.get_2p1d_discretisation_for_testing()
        disc.set_variable_slices([var])
        y_sol = disc.mesh["current collector"].edges["y"]
        z_sol = disc.mesh["current collector"].edges["z"]
        var_sol = disc.process_symbol(var)
        var_sol.mesh = disc.mesh["current collector"]
        t_sol = np.linspace(0, 1)
        u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] * np.linspace(0, 5)

        var_casadi = to_casadi(var_sol, u_sol)
        processed_var = pybamm.ProcessedVariable(
            [var_sol],
            [var_casadi],
            pybamm.Solution(t_sol, u_sol, pybamm.BaseModel(), {}),
            warn=False,
        )
        # 3 vectors
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=z_sol).shape, (15, 15, 50))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=z_sol),
            np.reshape(
                u_sol,
                [len(y_sol), len(z_sol), len(t_sol)]),
        )
        # 2 vectors, 1 scalar
        np.testing.assert_array_equal(
            processed_var(0.5, y=y_sol, z=z_sol).shape, (15, 15))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=0.2, z=z_sol).shape, (15, 50))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=0.5).shape, (15, 50))
        # 1 vectors, 2 scalar
        np.testing.assert_array_equal(
            processed_var(0.5, y=0.2, z=z_sol).shape, (15, ))
        np.testing.assert_array_equal(
            processed_var(0.5, y=y_sol, z=0.5).shape, (15, ))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=0.2, z=0.5).shape, (50, ))
        # 3 scalars
        np.testing.assert_array_equal(
            processed_var(0.2, y=0.2, z=0.2).shape, ())
Example #7
0
    def test_extrapolate_on_nonuniform_grid(self):
        geometry = pybamm.Geometry("1D micro")

        submesh_types = {
            "negative particle":
            pybamm.MeshGenerator(pybamm.Exponential1DSubMesh),
            "positive particle":
            pybamm.MeshGenerator(pybamm.Exponential1DSubMesh),
        }

        var = pybamm.standard_spatial_vars
        rpts = 10
        var_pts = {
            var.r_n: rpts,
            var.r_p: rpts,
        }
        mesh = pybamm.Mesh(geometry, submesh_types, var_pts)
        method_options = {
            "extrapolation": {
                "order": "linear",
                "use bcs": False
            }
        }
        spatial_methods = {
            "negative particle": pybamm.FiniteVolume(method_options),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        var = pybamm.Variable("var", domain="negative particle")
        surf_eqn = pybamm.surf(var)
        disc.set_variable_slices([var])
        surf_eqn_disc = disc.process_symbol(surf_eqn)

        micro_submesh = mesh["negative particle"]

        # check constant extrapolates to constant
        constant_y = np.ones_like(micro_submesh[0].nodes[:, np.newaxis])
        np.testing.assert_array_almost_equal(
            surf_eqn_disc.evaluate(None, constant_y), 1)

        # check linear variable extrapolates correctly
        linear_y = micro_submesh[0].nodes
        y_surf = micro_submesh[0].edges[-1]
        np.testing.assert_array_almost_equal(
            surf_eqn_disc.evaluate(None, linear_y), y_surf)
Example #8
0
    def test_grad_div_shapes_Neumann_bcs(self):
        """Test grad and div with Neumann boundary conditions (applied by div on N)"""
        whole_cell = ["negative electrode", "separator", "positive electrode"]

        # create discretisation
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.SpectralVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        combined_submesh = mesh.combine_submeshes(*whole_cell)

        # grad
        var = pybamm.Variable("var", domain=whole_cell)
        grad_eqn = pybamm.grad(var)
        disc.set_variable_slices([var])
        grad_eqn_disc = disc.process_symbol(grad_eqn)

        constant_y = np.ones_like(combined_submesh.nodes[:, np.newaxis])
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, constant_y),
            np.zeros_like(combined_submesh.edges[:][:, np.newaxis]),
        )

        # div
        N = pybamm.grad(var)
        div_eqn = pybamm.div(N)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Scalar(1), "Neumann"),
                "right": (pybamm.Scalar(1), "Neumann"),
            }
        }
        disc.bcs = boundary_conditions
        div_eqn_disc = disc.process_symbol(div_eqn)

        # Linear y should have laplacian zero
        linear_y = combined_submesh.nodes
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, linear_y),
            np.ones_like(combined_submesh.edges[:][:, np.newaxis]),
        )
        np.testing.assert_array_almost_equal(
            div_eqn_disc.evaluate(None, linear_y),
            np.zeros_like(combined_submesh.nodes[:, np.newaxis]),
        )
    def test_processed_variable_2D_x_z(self):
        var = pybamm.Variable(
            "var",
            domain=["negative electrode", "separator"],
            auxiliary_domains={"secondary": "current collector"},
        )
        x = pybamm.SpatialVariable(
            "x",
            domain=["negative electrode", "separator"],
            auxiliary_domains={"secondary": "current collector"},
        )
        z = pybamm.SpatialVariable("z", domain=["current collector"])

        disc = tests.get_1p1d_discretisation_for_testing()
        disc.set_variable_slices([var])
        z_sol = disc.process_symbol(z).entries[:, 0]
        x_sol = disc.process_symbol(x).entries[:, 0]
        # Keep only the first iteration of entries
        x_sol = x_sol[: len(x_sol) // len(z_sol)]
        var_sol = disc.process_symbol(var)
        t_sol = np.linspace(0, 1)
        y_sol = np.ones(len(x_sol) * len(z_sol))[:, np.newaxis] * np.linspace(0, 5)

        processed_var = pybamm.ProcessedVariable(
            var_sol, pybamm.Solution(t_sol, y_sol), warn=False
        )
        np.testing.assert_array_equal(
            processed_var.entries,
            np.reshape(y_sol, [len(x_sol), len(z_sol), len(t_sol)]),
        )

        # On edges
        x_s_edge = pybamm.Matrix(
            np.tile(disc.mesh["separator"].edges, len(z_sol)),
            domain="separator",
            auxiliary_domains={"secondary": "current collector"},
        )
        x_s_edge.mesh = disc.mesh["separator"]
        x_s_edge.secondary_mesh = disc.mesh["current collector"]
        processed_x_s_edge = pybamm.ProcessedVariable(
            x_s_edge, pybamm.Solution(t_sol, y_sol), warn=False
        )
        np.testing.assert_array_equal(
            x_s_edge.entries.flatten(), processed_x_s_edge.entries[:, :, 0].T.flatten()
        )
    def test_add_ghost_nodes(self):
        # Set up

        # create discretisation
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        # Add ghost nodes
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=whole_cell)
        disc.set_variable_slices([var])
        discretised_symbol = pybamm.StateVector(*disc.y_slices[var.id])
        bcs = {
            "left": (pybamm.Scalar(0), "Dirichlet"),
            "right": (pybamm.Scalar(3), "Dirichlet"),
        }

        # Test
        sp_meth = pybamm.FiniteVolume()
        sp_meth.build(mesh)
        sym_ghost, _ = sp_meth.add_ghost_nodes(var, discretised_symbol, bcs)
        combined_submesh = mesh.combine_submeshes(*whole_cell)
        y_test = np.linspace(0, 1, combined_submesh[0].npts)
        np.testing.assert_array_equal(
            sym_ghost.evaluate(y=y_test)[1:-1], discretised_symbol.evaluate(y=y_test)
        )
        self.assertEqual(
            (sym_ghost.evaluate(y=y_test)[0] + sym_ghost.evaluate(y=y_test)[1]) / 2, 0
        )
        self.assertEqual(
            (sym_ghost.evaluate(y=y_test)[-2] + sym_ghost.evaluate(y=y_test)[-1]) / 2, 3
        )

        # test errors
        bcs = {"left": (pybamm.Scalar(0), "x"), "right": (pybamm.Scalar(3), "Neumann")}
        with self.assertRaisesRegex(ValueError, "boundary condition must be"):
            sp_meth.add_ghost_nodes(var, discretised_symbol, bcs)
        with self.assertRaisesRegex(ValueError, "boundary condition must be"):
            sp_meth.add_neumann_values(var, discretised_symbol, bcs, var.domain)
        bcs = {"left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(3), "x")}
        with self.assertRaisesRegex(ValueError, "boundary condition must be"):
            sp_meth.add_ghost_nodes(var, discretised_symbol, bcs)
        with self.assertRaisesRegex(ValueError, "boundary condition must be"):
            sp_meth.add_neumann_values(var, discretised_symbol, bcs, var.domain)
Example #11
0
 def setUp(self):
     self.delta_phi_s_n = pybamm.Variable(
         "surface potential difference",
         ["negative electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.delta_phi_s_p = pybamm.Variable(
         "surface potential difference",
         ["positive electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.c_e_n = pybamm.Variable(
         "concentration",
         domain=["negative electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.c_e_p = pybamm.Variable(
         "concentration",
         domain=["positive electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.c_s_n_surf = pybamm.Variable(
         "particle surface conc",
         domain=["negative electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.c_s_p_surf = pybamm.Variable(
         "particle surface conc",
         domain=["positive electrode"],
         auxiliary_domains={"secondary": "current collector"},
     )
     self.variables = {
         "Negative electrode surface potential difference": self.delta_phi_s_n,
         "Positive electrode surface potential difference": self.delta_phi_s_p,
         "Negative electrolyte concentration": self.c_e_n,
         "Positive electrolyte concentration": self.c_e_p,
         "Negative particle surface concentration": self.c_s_n_surf,
         "Positive particle surface concentration": self.c_s_p_surf,
         "Current collector current density": pybamm.Scalar(1),
         "Negative electrode temperature": 0,
         "Positive electrode temperature": 0,
         "Sum of electrolyte reaction source terms": pybamm.Scalar(1),
         "Sum of interfacial current densities": pybamm.Scalar(1),
         "Sum of negative electrode interfacial current densities": pybamm.Scalar(1),
         "Sum of positive electrode interfacial current densities": pybamm.Scalar(1),
         "Sum of x-averaged negative electrode interfacial current densities": 1,
         "Sum of x-averaged positive electrode interfacial current densities": 1,
         "Sum of negative electrode electrolyte reaction source terms": 1,
         "Sum of positive electrode electrolyte reaction source terms": 1,
         "Sum of x-averaged negative electrode electrolyte reaction source terms": 1,
         "Sum of x-averaged positive electrode electrolyte reaction source terms": 1,
     }
Example #12
0
    def test_failures(self):
        # this test implements a python version of the ida Roberts
        # example provided in sundials
        # see sundials ida examples pdf
        model = pybamm.BaseModel()
        model.use_jacobian = False
        u = pybamm.Variable("u")
        model.rhs = {u: -0.1 * u}
        model.initial_conditions = {u: 1}

        disc = pybamm.Discretisation()
        disc.process_model(model)

        solver = pybamm.IDAKLUSolver(root_method="lm")

        t_eval = np.linspace(0, 3, 100)
        with self.assertRaisesRegex(pybamm.SolverError, "KLU requires the Jacobian"):
            solver.solve(model, t_eval)
Example #13
0
    def set_soc_variables(self):
        "Set variables relating to the state of charge."
        # State of Charge defined as function of dimensionless electrolyte concentration
        z = pybamm.standard_spatial_vars.z
        soc = (pybamm.Integral(
            self.variables["X-averaged electrolyte concentration"], z) * 100)
        self.variables.update({
            "State of Charge": soc,
            "Depth of Discharge": 100 - soc
        })

        # Fractional charge input
        if "Fractional Charge Input" not in self.variables:
            fci = pybamm.Variable("Fractional Charge Input",
                                  domain="current collector")
            self.variables["Fractional Charge Input"] = fci
            self.rhs[fci] = -self.variables["Total current density"] * 100
            self.initial_conditions[fci] = self.param.q_init * 100
Example #14
0
    def test_function_of_multiple_variables(self):
        a = pybamm.Variable("a")
        b = pybamm.Parameter("b")
        func = pybamm.Function(test_multi_var_function, a, b)
        self.assertEqual(func.name, "function (test_multi_var_function)")
        self.assertEqual(func.children[0].name, a.name)
        self.assertEqual(func.children[1].name, b.name)

        # test eval and diff
        a = pybamm.StateVector(slice(0, 1))
        b = pybamm.StateVector(slice(1, 2))
        y = np.array([5, 2])
        func = pybamm.Function(test_multi_var_function, a, b)

        self.assertEqual(func.evaluate(y=y), 7)
        self.assertEqual(func.diff(a).evaluate(y=y), 1)
        self.assertEqual(func.diff(b).evaluate(y=y), 1)
        self.assertEqual(func.diff(func).evaluate(), 1)
Example #15
0
    def test_get_solve(self):
        # Create model
        model = pybamm.BaseModel()
        model.convert_to_format = "jax"
        domain = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=domain)
        model.rhs = {var: -pybamm.InputParameter("rate") * 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)

        # test that another method string gives error
        with self.assertRaises(ValueError):
            solver = pybamm.JaxSolver(method="not_real")

        # Solve
        solver = pybamm.JaxSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 5, 80)

        with self.assertRaisesRegex(RuntimeError,
                                    "Model is not set up for solving"):
            solver.get_solve(model, t_eval)

        solver.solve(model, t_eval, inputs={"rate": 0.1})
        solver = solver.get_solve(model, t_eval)
        y = solver({"rate": 0.1})

        np.testing.assert_allclose(y[0],
                                   np.exp(-0.1 * t_eval),
                                   rtol=1e-6,
                                   atol=1e-6)

        y = solver({"rate": 0.2})

        np.testing.assert_allclose(y[0],
                                   np.exp(-0.2 * t_eval),
                                   rtol=1e-6,
                                   atol=1e-6)
Example #16
0
    def test_boundary_value(self):
        a = pybamm.Scalar(1)
        boundary_a = pybamm.boundary_value(a, "right")
        self.assertEqual(boundary_a.id, a.id)

        boundary_broad_a = pybamm.boundary_value(
            pybamm.Broadcast(a, ["negative electrode"]), "left")
        self.assertEqual(boundary_broad_a.evaluate(), np.array([1]))

        a = pybamm.Symbol("a", domain=["separator"])
        boundary_a = pybamm.boundary_value(a, "right")
        self.assertIsInstance(boundary_a, pybamm.BoundaryValue)
        self.assertEqual(boundary_a.side, "right")
        self.assertEqual(boundary_a.domain, [])
        self.assertEqual(boundary_a.auxiliary_domains, {})
        # test with secondary domain
        a_sec = pybamm.Symbol(
            "a",
            domain=["separator"],
            auxiliary_domains={"secondary": "current collector"},
        )
        boundary_a_sec = pybamm.boundary_value(a_sec, "right")
        self.assertEqual(boundary_a_sec.domain, ["current collector"])
        self.assertEqual(boundary_a_sec.auxiliary_domains, {})
        # test with secondary domain and tertiary domain
        a_tert = pybamm.Symbol(
            "a",
            domain=["separator"],
            auxiliary_domains={
                "secondary": "current collector",
                "tertiary": "bla"
            },
        )
        boundary_a_tert = pybamm.boundary_value(a_tert, "right")
        self.assertEqual(boundary_a_tert.domain, ["current collector"])
        self.assertEqual(boundary_a_tert.auxiliary_domains,
                         {"secondary": ["bla"]})

        # error if boundary value on tabs and domain is not "current collector"
        var = pybamm.Variable("var", domain=["negative electrode"])
        with self.assertRaisesRegex(pybamm.ModelError,
                                    "Can only take boundary"):
            pybamm.boundary_value(var, "negative tab")
            pybamm.boundary_value(var, "positive tab")
Example #17
0
    def test_delta_function(self):
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        var = pybamm.Variable("var")
        delta_fn_left = pybamm.DeltaFunction(var, "left", "negative electrode")
        delta_fn_right = pybamm.DeltaFunction(var, "right",
                                              "negative electrode")
        disc.set_variable_slices([var])
        delta_fn_left_disc = disc.process_symbol(delta_fn_left)
        delta_fn_right_disc = disc.process_symbol(delta_fn_right)

        # Basic shape and type tests
        y = np.ones_like(mesh["negative electrode"][0].nodes[:, np.newaxis])
        # Left
        self.assertEqual(delta_fn_left_disc.domain, delta_fn_left.domain)
        self.assertEqual(delta_fn_left_disc.auxiliary_domains,
                         delta_fn_left.auxiliary_domains)
        self.assertIsInstance(delta_fn_left_disc, pybamm.Multiplication)
        self.assertIsInstance(delta_fn_left_disc.left, pybamm.Matrix)
        np.testing.assert_array_equal(
            delta_fn_left_disc.left.evaluate()[:, 1:], 0)
        self.assertEqual(delta_fn_left_disc.shape, y.shape)
        # Right
        self.assertEqual(delta_fn_right_disc.domain, delta_fn_right.domain)
        self.assertEqual(delta_fn_right_disc.auxiliary_domains,
                         delta_fn_right.auxiliary_domains)
        self.assertIsInstance(delta_fn_right_disc, pybamm.Multiplication)
        self.assertIsInstance(delta_fn_right_disc.left, pybamm.Matrix)
        np.testing.assert_array_equal(
            delta_fn_right_disc.left.evaluate()[:, :-1], 0)
        self.assertEqual(delta_fn_right_disc.shape, y.shape)

        # Value tests
        # Delta function should integrate to the same thing as variable
        var_disc = disc.process_symbol(var)
        x = pybamm.standard_spatial_vars.x_n
        delta_fn_int_disc = disc.process_symbol(
            pybamm.Integral(delta_fn_left, x))
        np.testing.assert_array_equal(
            var_disc.evaluate(y=y) * mesh["negative electrode"][0].edges[-1],
            np.sum(delta_fn_int_disc.evaluate(y=y)),
        )
Example #18
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,
            )
Example #19
0
    def test_solver_sensitivities(self):
        # Create model
        model = pybamm.BaseModel()
        model.convert_to_format = "jax"
        domain = ["negative electrode", "separator", "positive electrode"]
        var = pybamm.Variable("var", domain=domain)
        model.rhs = {var: -pybamm.InputParameter("rate") * var}
        model.initial_conditions = {var: 1.0}
        # 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)

        for method in ['RK45', 'BDF']:
            # Solve
            solver = pybamm.JaxSolver(
                method=method, rtol=1e-8, atol=1e-8
            )
            t_eval = np.linspace(0, 1, 80)

            h = 0.0001
            rate = 0.1

            # need to solve the model once to get it set up by the base solver
            solver.solve(model, t_eval, inputs={'rate': rate})
            solve = solver.get_solve(model, t_eval)

            # create a dummy "model" where we calculate the sum of the time series
            def solve_model(rate):
                return jax.numpy.sum(solve({'rate': rate}))

            # check answers with finite difference
            eval_plus = solve_model(rate + h)
            eval_neg = solve_model(rate - h)
            grad_num = (eval_plus - eval_neg) / (2 * h)

            grad_solve = jax.jit(jax.grad(solve_model))
            grad = grad_solve(rate)

            self.assertAlmostEqual(grad, grad_num, places=1)
    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])
Example #21
0
    def test_model_ode_integrate_failure(self):
        # Turn off warnings to ignore sqrt error
        warnings.simplefilter("ignore")

        model = pybamm.BaseModel()
        var = pybamm.Variable("var")
        model.rhs = {var: -pybamm.sqrt(var)}
        model.initial_conditions = {var: 1}
        disc = pybamm.Discretisation()
        disc.process_model(model)

        t_eval = np.linspace(0, 3, 100)
        solver = pybamm.ScikitsOdeSolver()
        # Expect solver to fail when y goes negative
        with self.assertRaises(pybamm.SolverError):
            solver.solve(model, t_eval)

        # Turn warnings back on
        warnings.simplefilter("default")
Example #22
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)
    def test_processed_var_3D_scikit_interpolation(self):
        var = pybamm.Variable("var", domain=["current collector"])
        y = pybamm.SpatialVariable("y", domain=["current collector"])
        z = pybamm.SpatialVariable("z", domain=["current collector"])

        disc = tests.get_2p1d_discretisation_for_testing()
        disc.set_variable_slices([var])
        y_sol = disc.process_symbol(y).entries[:, 0]
        z_sol = disc.process_symbol(z).entries[:, 0]
        var_sol = disc.process_symbol(var)
        t_sol = np.linspace(0, 1)
        u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] * np.linspace(0, 5)

        processed_var = pybamm.ProcessedVariable(var_sol,
                                                 t_sol,
                                                 u_sol,
                                                 mesh=disc.mesh)
        # 3 vectors
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=z_sol).shape, (15, 15, 50))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=z_sol),
            np.reshape(
                u_sol,
                [len(y_sol), len(z_sol), len(t_sol)]),
        )
        # 2 vectors, 1 scalar
        np.testing.assert_array_equal(
            processed_var(0.5, y=y_sol, z=z_sol).shape, (15, 15))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=0.2, z=z_sol).shape, (15, 50))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=y_sol, z=0.5).shape, (15, 50))
        # 1 vectors, 2 scalar
        np.testing.assert_array_equal(
            processed_var(0.5, y=0.2, z=z_sol).shape, (15, ))
        np.testing.assert_array_equal(
            processed_var(0.5, y=y_sol, z=0.5).shape, (15, ))
        np.testing.assert_array_equal(
            processed_var(t_sol, y=0.2, z=0.5).shape, (50, ))
        # 3 scalars
        np.testing.assert_array_equal(
            processed_var(0.2, y=0.2, z=0.2).shape, ())
Example #24
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)
Example #25
0
 def test_timescale_input_fail(self):
     # Make sure timescale can't depend on inputs
     model = pybamm.BaseModel()
     v = pybamm.Variable("v")
     model.rhs = {v: -1}
     model.initial_conditions = {v: 1}
     a = pybamm.InputParameter("a")
     model.timescale = a
     solver = pybamm.CasadiSolver()
     solver.set_up(model, inputs={"a": 10})
     sol = solver.step(old_solution=None,
                       model=model,
                       dt=1.0,
                       inputs={"a": 10})
     with self.assertRaisesRegex(pybamm.SolverError, "The model timescale"):
         sol = solver.step(old_solution=sol,
                           model=model,
                           dt=1.0,
                           inputs={"a": 20})
 def test_definite_integral(self):
     mesh = get_2p1d_mesh_for_testing()
     spatial_methods = {
         "macroscale": pybamm.FiniteVolume(),
         "current collector": pybamm.ScikitFiniteElement(),
     }
     disc = pybamm.Discretisation(mesh, spatial_methods)
     var = pybamm.Variable("var", domain="current collector")
     y = pybamm.SpatialVariable("y", ["current collector"])
     z = pybamm.SpatialVariable("z", ["current collector"])
     integral_eqn = pybamm.Integral(var, [y, z])
     disc.set_variable_slices([var])
     integral_eqn_disc = disc.process_symbol(integral_eqn)
     y_test = 6 * np.ones(mesh["current collector"][0].npts)
     fem_mesh = mesh["current collector"][0]
     ly = fem_mesh.coordinates[0, -1]
     lz = fem_mesh.coordinates[1, -1]
     np.testing.assert_array_almost_equal(
         integral_eqn_disc.evaluate(None, y_test), 6 * ly * lz)
    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)
Example #28
0
    def test_outer(self):
        var = pybamm.Variable("var", ["current collector"])
        x = pybamm.SpatialVariable("x_s", ["separator"])

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

        # process Outer variable
        disc.set_variable_slices([var])
        outer = pybamm.outer(var, x)
        outer_disc = disc.process_symbol(outer)
        self.assertIsInstance(outer_disc, pybamm.Outer)
        self.assertIsInstance(outer_disc.children[0], pybamm.StateVector)
        self.assertIsInstance(outer_disc.children[1], pybamm.Vector)
        self.assertEqual(
            outer_disc.shape,
            (mesh["separator"][0].npts * mesh["current collector"][0].npts, 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)
Example #30
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]),
        )