def test_boundary_integral(self):
        mesh = get_2p1d_mesh_for_testing(include_particles=False)
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(),
            "current collector": pybamm.ScikitFiniteElement(),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)
        var = pybamm.Variable("var", domain="current collector")
        disc.set_variable_slices([var])

        full = pybamm.BoundaryIntegral(var)
        neg = pybamm.BoundaryIntegral(var, region="negative tab")
        pos = pybamm.BoundaryIntegral(var, region="positive tab")

        full_disc = disc.process_symbol(full)
        neg_disc = disc.process_symbol(neg)
        pos_disc = disc.process_symbol(pos)

        # check integrating 1 gives correct *dimensionless* region lengths
        perimeter = 2 * (1 + 0.8)
        l_tab_n = 0.1 / 0.5
        l_tab_p = 0.1 / 0.5
        constant_y = np.ones(mesh["current collector"].npts)
        # Integral around boundary is exact
        np.testing.assert_array_almost_equal(
            full_disc.evaluate(None, constant_y), perimeter
        )
        # Ideally mesh edges should line up with tab edges.... then we would get
        # better agreement between actual and numerical tab width
        np.testing.assert_array_almost_equal(
            neg_disc.evaluate(None, constant_y), l_tab_n, decimal=1
        )
        np.testing.assert_array_almost_equal(
            pos_disc.evaluate(None, constant_y), l_tab_p, decimal=1
        )
Exemple #2
0
 def test_evaluates_on_edges(self):
     a = pybamm.StateVector(slice(0, 10), domain="test")
     self.assertFalse(pybamm.Index(a, slice(1)).evaluates_on_edges("primary"))
     self.assertFalse(pybamm.Laplacian(a).evaluates_on_edges("primary"))
     self.assertFalse(pybamm.GradientSquared(a).evaluates_on_edges("primary"))
     self.assertFalse(pybamm.BoundaryIntegral(a).evaluates_on_edges("primary"))
     self.assertTrue(pybamm.Upwind(a).evaluates_on_edges("primary"))
     self.assertTrue(pybamm.Downwind(a).evaluates_on_edges("primary"))
    def set_boundary_conditions(self, variables):

        phi_s_cn = variables["Negative current collector potential"]
        phi_s_cp = variables["Positive current collector potential"]

        param = self.param
        applied_current = variables["Total current density"]
        cc_area = self._get_effective_current_collector_area()

        # Note: we divide by the *numerical* tab area so that the correct total
        # current is applied. That is, numerically integrating the current density
        # around the boundary gives the applied current exactly.

        positive_tab_area = pybamm.BoundaryIntegral(
            pybamm.PrimaryBroadcast(param.l_cp, "current collector"),
            region="positive tab",
        )

        # cc_area appears here due to choice of non-dimensionalisation
        pos_tab_bc = (
            -applied_current
            * cc_area
            / (param.sigma_cp * param.delta ** 2 * positive_tab_area)
        )

        # Boundary condition needs to be on the variables that go into the Laplacian,
        # even though phi_s_cp isn't a pybamm.Variable object
        # In the 2+1D model, the equations for the current collector potentials
        # are solved on a 2D domain and the regions "negative tab" and "positive tab"
        # are the projections of the tabs onto this 2D domain.
        # In the 2D formulation it is assumed that no flux boundary conditions
        # are applied everywhere apart from the tabs.
        # The reference potential is taken to be zero on the negative tab,
        # giving the zero Dirichlet condition on phi_s_cn. Elsewhere, the boundary
        # is insulated, giving no flux conditions on phi_s_cn. This is automatically
        # applied everywhere, apart from the region corresponding to the projection
        # of the positive tab, so we need to explitly apply a zero-flux boundary
        # condition on the region "positive tab" for phi_s_cn.
        # A current is drawn from the positive tab, giving the non-zero Neumann
        # boundary condition on phi_s_cp at "positive tab". Elsewhere, the boundary is
        # insulated, so, as with phi_s_cn, we need to explicitly give the zero-flux
        # condition on the region "negative tab" for phi_s_cp.
        self.boundary_conditions = {
            phi_s_cn: {
                "negative tab": (pybamm.Scalar(0), "Dirichlet"),
                "positive tab": (pybamm.Scalar(0), "Neumann"),
            },
            phi_s_cp: {
                "negative tab": (pybamm.Scalar(0), "Neumann"),
                "positive tab": (pos_tab_bc, "Neumann"),
            },
        }
Exemple #4
0
    def __init__(self):
        super().__init__()
        self.name = "Effective resistance in current collector model (2D)"
        self.param = pybamm.standard_parameters_lithium_ion

        # Get necessary parameters
        param = self.param
        l_cn = param.l_cn
        l_cp = param.l_cp
        l_tab_p = param.l_tab_p
        A_tab_p = l_cp * l_tab_p
        sigma_cn_dbl_prime = param.sigma_cn_dbl_prime
        sigma_cp_dbl_prime = param.sigma_cp_dbl_prime
        delta = param.delta

        # Set model variables -- we solve a auxilliary problem in each current collector
        # then relate this to the potentials and resistances later
        f_n = pybamm.Variable("Unit solution in negative current collector",
                              domain="current collector")
        f_p = pybamm.Variable("Unit solution in positive current collector",
                              domain="current collector")

        # Governing equations -- we impose that the average of f_p is zero
        # by introducing a Lagrange multiplier
        c = pybamm.Variable("Lagrange multiplier")

        self.algebraic = {
            f_n:
            pybamm.laplacian(f_n) + pybamm.source(1, f_n),
            c:
            pybamm.laplacian(f_p) - pybamm.source(1, f_p) +
            c * pybamm.DefiniteIntegralVector(f_p, vector_type="column"),
            f_p:
            pybamm.yz_average(f_p) + 0 * c,
        }

        # Boundary conditons
        pos_tab_bc = l_cp / A_tab_p
        self.boundary_conditions = {
            f_n: {
                "negative tab": (0, "Dirichlet"),
                "positive tab": (0, "Neumann")
            },
            f_p: {
                "negative tab": (0, "Neumann"),
                "positive tab": (pos_tab_bc, "Neumann"),
            },
        }

        # "Initial conditions" provides initial guess for solver
        self.initial_conditions = {
            f_n: pybamm.Scalar(0),
            f_p: pybamm.Scalar(0),
            c: pybamm.Scalar(0),
        }

        # Define effective current collector resistance
        R_cc_n = delta * pybamm.yz_average(f_n) / (l_cn * sigma_cn_dbl_prime)
        R_cc_p = (delta * pybamm.BoundaryIntegral(f_p, "positive tab") /
                  (l_cp * sigma_cp_dbl_prime))
        R_cc = R_cc_n + R_cc_p
        R_scale = param.potential_scale / param.I_typ

        self.variables = {
            "Unit solution in negative current collector":
            f_n,
            "Unit solution in positive current collector":
            f_p,
            "Effective current collector resistance":
            R_cc,
            "Effective current collector resistance [Ohm]":
            R_cc * R_scale,
            "Effective negative current collector resistance":
            R_cc_n,
            "Effective negative current collector resistance [Ohm]":
            R_cc_n * R_scale,
            "Effective positive current collector resistance":
            R_cc_p,
            "Effective positive current collector resistance [Ohm]":
            R_cc_p * R_scale,
        }

        # Add spatial variables
        var = pybamm.standard_spatial_vars
        L_y = pybamm.geometric_parameters.L_y
        L_z = pybamm.geometric_parameters.L_z
        self.variables.update({
            "y": var.y,
            "y [m]": var.y * L_y,
            "z": var.z,
            "z [m]": var.z * L_z
        })

        pybamm.citations.register("timms2020")