예제 #1
0
    def test_p2d_mass_matrix_shape(self):
        """
        Test mass matrix shape in the pseudo 2-dimensional case
        """
        c = pybamm.Variable("c", domain=["negative particle"])
        N = pybamm.grad(c)
        model = pybamm.BaseModel()
        model.rhs = {c: pybamm.div(N)}
        model.initial_conditions = {c: pybamm.Scalar(0)}
        model.boundary_conditions = {
            c: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            }
        }
        model.variables = {"c": c, "N": N}
        mesh = get_p2d_mesh_for_testing()
        spatial_methods = {"negative particle": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)
        disc.process_model(model)

        prim_pts = mesh["negative particle"][0].npts
        sec_pts = len(mesh["negative particle"])
        mass_local = eye(prim_pts)
        mass = kron(eye(sec_pts), mass_local)
        np.testing.assert_array_equal(mass.toarray(),
                                      model.mass_matrix.entries.toarray())
예제 #2
0
    def test_boundary_value_domain(self):
        mesh = get_p2d_mesh_for_testing()
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(),
            "negative particle": pybamm.FiniteVolume(),
            "positive particle": pybamm.FiniteVolume(),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        c_s_n = pybamm.Variable("c_s_n", domain=["negative particle"])
        c_s_p = pybamm.Variable("c_s_p", domain=["positive particle"])

        disc.set_variable_slices([c_s_n, c_s_p])

        # surface values
        c_s_n_surf = pybamm.surf(c_s_n)
        c_s_p_surf = pybamm.surf(c_s_p)

        # domain for boundary values must now be explicitly set
        c_s_n_surf_disc = disc.process_symbol(c_s_n_surf)
        c_s_p_surf_disc = disc.process_symbol(c_s_p_surf)
        self.assertEqual(c_s_n_surf_disc.domain, [])
        self.assertEqual(c_s_p_surf_disc.domain, [])
        c_s_n_surf.domain = ["negative electrode"]
        c_s_p_surf.domain = ["positive electrode"]
        c_s_n_surf_disc = disc.process_symbol(c_s_n_surf)
        c_s_p_surf_disc = disc.process_symbol(c_s_p_surf)
        self.assertEqual(c_s_n_surf_disc.domain, ["negative electrode"])
        self.assertEqual(c_s_p_surf_disc.domain, ["positive electrode"])
예제 #3
0
    def test_p2d_spherical_grad_div_shapes_Neumann_bcs(self):
        """
        Test grad and div with Dirichlet boundary conditions (applied by grad on var)
        in the pseudo 2-dimensional case
        """

        mesh = get_p2d_mesh_for_testing()
        spatial_methods = {"negative particle": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        n_mesh = mesh["negative particle"]

        mesh.add_ghost_meshes()
        disc.mesh.add_ghost_meshes()

        # test 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)

        prim_pts = n_mesh[0].npts
        sec_pts = len(n_mesh)
        constant_y = np.kron(np.ones(sec_pts), np.ones(prim_pts))

        grad_eval = grad_eqn_disc.evaluate(None, constant_y)
        grad_eval = np.reshape(grad_eval, [sec_pts, prim_pts - 1])

        np.testing.assert_array_equal(grad_eval,
                                      np.zeros([sec_pts, prim_pts - 1]))

        # 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)

        const = 6 * np.ones(sec_pts * prim_pts)
        div_eval = div_eqn_disc.evaluate(None, const)
        div_eval = np.reshape(div_eval, [sec_pts, prim_pts])
        np.testing.assert_array_almost_equal(div_eval,
                                             np.zeros([sec_pts, prim_pts]))
예제 #4
0
        def get_error(m):
            # create mesh and discretisation p2d, uniform in x
            mesh = get_p2d_mesh_for_testing(3, m)
            disc = pybamm.Discretisation(mesh, spatial_methods)
            submesh = mesh["negative particle"]
            r = submesh[0].nodes
            r_edge = pybamm.standard_spatial_vars.r_n_edge

            N = r_edge ** 2 * pybamm.sin(r_edge)
            div_eqn = pybamm.div(N)
            # Define exact solutions
            # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r)
            div_exact = 4 * r * np.sin(r) + r ** 2 * np.cos(r)
            div_exact = np.kron(np.ones(len(submesh)), div_exact)

            # Discretise and evaluate
            div_eqn_disc = disc.process_symbol(div_eqn)
            div_approx = div_eqn_disc.evaluate()

            return div_approx[:, 0] - div_exact
예제 #5
0
        def get_error(m):
            # create mesh and discretisation p2d, x-dependent
            mesh = get_p2d_mesh_for_testing(6, m)
            disc = pybamm.Discretisation(mesh, spatial_methods)
            submesh_r = mesh["negative particle"]
            r = submesh_r.nodes
            r_edge = pybamm.standard_spatial_vars.r_n_edge
            x = pybamm.standard_spatial_vars.x_n

            N = pybamm.PrimaryBroadcast(
                x, "negative particle") * (r_edge**2 * pybamm.sin(r_edge))
            div_eqn = pybamm.div(N)
            # Define exact solutions
            # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r)
            div_exact = 4 * r * np.sin(r) + r**2 * np.cos(r)
            div_exact = np.kron(mesh["negative electrode"].nodes, div_exact)

            # Discretise and evaluate
            div_eqn_disc = disc.process_symbol(div_eqn)
            div_approx = div_eqn_disc.evaluate()

            return div_approx[:, 0] - div_exact
예제 #6
0
        def get_error(m):
            # create mesh and discretisation p2d, x-dependent
            mesh = get_p2d_mesh_for_testing(6, m)
            disc = pybamm.Discretisation(mesh, spatial_methods)
            submesh_r = mesh["negative particle"]
            r = submesh_r[0].nodes
            r_edge = submesh_r[0].edges
            x = pybamm.Vector(mesh["negative electrode"][0].nodes)

            N = pybamm.Matrix(
                np.kron(x.entries[:, 0], r_edge**2 * np.sin(r_edge)),
                domain=["negative particle"],
            )
            div_eqn = pybamm.div(N)
            # Define exact solutions
            # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r)
            div_exact = 4 * r * np.sin(r) + r**2 * np.cos(r)
            div_exact = np.kron(x.entries[:, 0], div_exact)

            # Discretise and evaluate
            div_eqn_disc = disc.process_symbol(div_eqn)
            div_approx = div_eqn_disc.evaluate()

            return div_approx[:, 0] - div_exact
예제 #7
0
    def test_extrapolate_2d_models(self):
        # create discretisation
        mesh = get_p2d_mesh_for_testing()
        method_options = {
            "extrapolation": {
                "order": "linear",
                "use bcs": False
            }
        }
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(method_options),
            "negative particle": pybamm.FiniteVolume(method_options),
            "positive particle": pybamm.FiniteVolume(method_options),
            "current collector": pybamm.FiniteVolume(method_options),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        # Microscale
        var = pybamm.Variable("var", domain="negative particle")
        extrap_right = pybamm.BoundaryValue(var, "right")
        disc.set_variable_slices([var])
        extrap_right_disc = disc.process_symbol(extrap_right)
        self.assertEqual(extrap_right_disc.domain, [])
        # domain for boundary values must now be explicitly set
        extrap_right.domain = ["negative electrode"]
        disc.set_variable_slices([var])
        extrap_right_disc = disc.process_symbol(extrap_right)
        self.assertEqual(extrap_right_disc.domain, ["negative electrode"])
        # evaluate
        y_macro = mesh["negative electrode"][0].nodes
        y_micro = mesh["negative particle"][0].nodes
        y = np.outer(y_macro, y_micro).reshape(-1, 1)
        # extrapolate to r=1 --> should evaluate to y_macro
        np.testing.assert_array_almost_equal(
            extrap_right_disc.evaluate(y=y)[:, 0], y_macro)

        var = pybamm.Variable("var", domain="positive particle")
        extrap_right = pybamm.BoundaryValue(var, "right")
        disc.set_variable_slices([var])
        extrap_right_disc = disc.process_symbol(extrap_right)
        self.assertEqual(extrap_right_disc.domain, [])
        # domain for boundary values must now be explicitly set
        extrap_right.domain = ["positive electrode"]
        disc.set_variable_slices([var])
        extrap_right_disc = disc.process_symbol(extrap_right)
        self.assertEqual(extrap_right_disc.domain, ["positive electrode"])

        # 2d macroscale
        mesh = get_1p1d_mesh_for_testing()
        disc = pybamm.Discretisation(mesh, spatial_methods)
        var = pybamm.Variable("var", domain="negative electrode")
        extrap_right = pybamm.BoundaryValue(var, "right")
        disc.set_variable_slices([var])
        extrap_right_disc = disc.process_symbol(extrap_right)
        self.assertEqual(extrap_right_disc.domain, [])

        # test extrapolate to "negative tab" gives same as "left" and
        # "positive tab" gives same "right" (see get_mesh_for_testing)
        var = pybamm.Variable("var", domain="current collector")
        disc.set_variable_slices([var])
        submesh = mesh["current collector"]
        constant_y = np.ones_like(submesh[0].nodes[:, np.newaxis])

        extrap_neg = pybamm.BoundaryValue(var, "negative tab")
        extrap_neg_disc = disc.process_symbol(extrap_neg)
        extrap_left = pybamm.BoundaryValue(var, "left")
        extrap_left_disc = disc.process_symbol(extrap_left)
        np.testing.assert_array_equal(
            extrap_neg_disc.evaluate(None, constant_y),
            extrap_left_disc.evaluate(None, constant_y),
        )

        extrap_pos = pybamm.BoundaryValue(var, "positive tab")
        extrap_pos_disc = disc.process_symbol(extrap_pos)
        extrap_right = pybamm.BoundaryValue(var, "right")
        extrap_right_disc = disc.process_symbol(extrap_right)
        np.testing.assert_array_equal(
            extrap_pos_disc.evaluate(None, constant_y),
            extrap_right_disc.evaluate(None, constant_y),
        )
    def test_p2d_add_ghost_nodes(self):
        # create discretisation
        mesh = get_p2d_mesh_for_testing()
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(),
            "negative particle": pybamm.FiniteVolume(),
            "positive particle": pybamm.FiniteVolume(),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        # add ghost nodes
        c_s_n = pybamm.Variable("c_s_n", domain=["negative particle"])
        c_s_p = pybamm.Variable("c_s_p", domain=["positive particle"])

        disc.set_variable_slices([c_s_n])
        disc_c_s_n = pybamm.StateVector(*disc.y_slices[c_s_n.id])

        disc.set_variable_slices([c_s_p])
        disc_c_s_p = pybamm.StateVector(*disc.y_slices[c_s_p.id])
        bcs = {
            "left": (pybamm.Scalar(0), "Dirichlet"),
            "right": (pybamm.Scalar(3), "Dirichlet"),
        }
        sp_meth = pybamm.FiniteVolume()
        sp_meth.build(mesh)
        c_s_n_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_n, disc_c_s_n, bcs)
        c_s_p_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_p, disc_c_s_p, bcs)

        mesh_s_n = mesh["negative particle"]
        mesh_s_p = mesh["positive particle"]

        n_prim_pts = mesh_s_n[0].npts
        n_sec_pts = len(mesh_s_n)

        p_prim_pts = mesh_s_p[0].npts
        p_sec_pts = len(mesh_s_p)

        y_s_n_test = np.kron(np.ones(n_sec_pts), np.ones(n_prim_pts))
        y_s_p_test = np.kron(np.ones(p_sec_pts), np.ones(p_prim_pts))

        # evaluate with and without ghost points
        c_s_n_eval = disc_c_s_n.evaluate(None, y_s_n_test)
        c_s_n_ghost_eval = c_s_n_plus_ghost.evaluate(None, y_s_n_test)

        c_s_p_eval = disc_c_s_p.evaluate(None, y_s_p_test)
        c_s_p_ghost_eval = c_s_p_plus_ghost.evaluate(None, y_s_p_test)

        # reshape to make easy to deal with
        c_s_n_eval = np.reshape(c_s_n_eval, [n_sec_pts, n_prim_pts])
        c_s_n_ghost_eval = np.reshape(c_s_n_ghost_eval,
                                      [n_sec_pts, n_prim_pts + 2])

        c_s_p_eval = np.reshape(c_s_p_eval, [p_sec_pts, p_prim_pts])
        c_s_p_ghost_eval = np.reshape(c_s_p_ghost_eval,
                                      [p_sec_pts, p_prim_pts + 2])

        np.testing.assert_array_equal(c_s_n_ghost_eval[:, 1:-1], c_s_n_eval)
        np.testing.assert_array_equal(c_s_p_ghost_eval[:, 1:-1], c_s_p_eval)

        np.testing.assert_array_equal(
            (c_s_n_ghost_eval[:, 0] + c_s_n_ghost_eval[:, 1]) / 2, 0)
        np.testing.assert_array_equal(
            (c_s_p_ghost_eval[:, 0] + c_s_p_ghost_eval[:, 1]) / 2, 0)

        np.testing.assert_array_equal(
            (c_s_n_ghost_eval[:, -2] + c_s_n_ghost_eval[:, -1]) / 2, 3)
        np.testing.assert_array_equal(
            (c_s_p_ghost_eval[:, -2] + c_s_p_ghost_eval[:, -1]) / 2, 3)