Esempio n. 1
0
    def test_inner(self):
        model = pybamm.lithium_ion.BaseModel()

        phi_s = pybamm.standard_variables.phi_s_n
        i = pybamm.grad(phi_s)

        model.rhs = {phi_s: pybamm.inner(i, i)}
        model.boundary_conditions = {
            phi_s: {
                "left": (pybamm.Scalar(0), "Neumann"),
                "right": (pybamm.Scalar(0), "Neumann"),
            }
        }
        model.initial_conditions = {phi_s: pybamm.Scalar(0)}

        model.variables = {"inner": pybamm.inner(i, i)}

        # load parameter values and process model and geometry
        param = model.default_parameter_values
        geometry = model.default_geometry
        param.process_model(model)
        param.process_geometry(geometry)

        # set mesh
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)

        # discretise model
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)

        # check doesn't evaluate on edges anymore
        self.assertEqual(model.variables["inner"].evaluates_on_edges("primary"), False)
Esempio n. 2
0
 def _current_collector_heating(self, variables):
     "Compute Ohmic heating in current collectors"
     # TODO: implement grad in 0D to return a scalar zero
     # TODO: implement grad_squared in other spatial methods so that the if
     # statement can be removed
     # In the limit of infinitely large current collector conductivity (i.e.
     # 0D current collectors), the Ohmic heating in the current collectors is
     # zero
     if self.cc_dimension == 0:
         Q_s_cn = pybamm.Scalar(0)
         Q_s_cp = pybamm.Scalar(0)
     # Otherwise we compute the Ohmic heating for 1 or 2D current collectors
     elif self.cc_dimension in [1, 2]:
         phi_s_cn = variables["Negative current collector potential"]
         phi_s_cp = variables["Positive current collector potential"]
         if self.cc_dimension == 1:
             Q_s_cn = self.param.sigma_cn_prime * pybamm.inner(
                 pybamm.grad(phi_s_cn), pybamm.grad(phi_s_cn))
             Q_s_cp = self.param.sigma_cp_prime * pybamm.inner(
                 pybamm.grad(phi_s_cp), pybamm.grad(phi_s_cp))
         elif self.cc_dimension == 2:
             # Inner not implemented in 2D -- have to call grad_squared directly
             Q_s_cn = self.param.sigma_cn_prime * pybamm.grad_squared(
                 phi_s_cn)
             Q_s_cp = self.param.sigma_cp_prime * pybamm.grad_squared(
                 phi_s_cp)
     return Q_s_cn, Q_s_cp
Esempio n. 3
0
 def _current_collector_heating(self, variables):
     """Returns the heat source terms in the 1D current collector"""
     phi_s_cn = variables["Negative current collector potential"]
     phi_s_cp = variables["Positive current collector potential"]
     Q_s_cn = self.param.sigma_cn_prime * pybamm.inner(
         pybamm.grad(phi_s_cn), pybamm.grad(phi_s_cn))
     Q_s_cp = self.param.sigma_cp_prime * pybamm.inner(
         pybamm.grad(phi_s_cp), pybamm.grad(phi_s_cp))
     return Q_s_cn, Q_s_cp
Esempio n. 4
0
 def test_jac_of_inner(self):
     a = pybamm.Scalar(1)
     b = pybamm.Scalar(2)
     y = pybamm.StateVector(slice(0, 1))
     self.assertEqual(pybamm.inner(a, b).jac(y).evaluate(), 0)
     self.assertEqual(pybamm.inner(a, y).jac(y).evaluate(), 1)
     self.assertEqual(pybamm.inner(y, b).jac(y).evaluate(), 2)
     vec = pybamm.StateVector(slice(0, 2))
     jac = pybamm.inner(a * vec, b * vec).jac(vec).evaluate(y=np.ones(2)).toarray()
     np.testing.assert_array_equal(jac, 4 * np.eye(2))
Esempio n. 5
0
    def test_simplify_inner(self):
        a1 = pybamm.Scalar(0)
        M1 = pybamm.Matrix(np.zeros((10, 10)))
        v1 = pybamm.Vector(np.ones(10))
        a2 = pybamm.Scalar(1)
        M2 = pybamm.Matrix(np.ones((10, 10)))
        a3 = pybamm.Scalar(3)

        np.testing.assert_array_equal(
            pybamm.inner(a1, M2).simplify().evaluate().toarray(), M1.entries
        )
        self.assertEqual(pybamm.inner(a1, a2).simplify().evaluate(), 0)
        np.testing.assert_array_equal(
            pybamm.inner(M2, a1).simplify().evaluate().toarray(), M1.entries
        )
        self.assertEqual(pybamm.inner(a2, a1).simplify().evaluate(), 0)
        np.testing.assert_array_equal(
            pybamm.inner(M1, a3).simplify().evaluate().toarray(), M1.entries
        )
        np.testing.assert_array_equal(
            pybamm.inner(v1, a3).simplify().evaluate(), 3 * v1.entries
        )
        self.assertEqual(pybamm.inner(a2, a3).simplify().evaluate(), 3)
        self.assertEqual(pybamm.inner(a3, a2).simplify().evaluate(), 3)
        self.assertEqual(pybamm.inner(a3, a3).simplify().evaluate(), 9)
Esempio n. 6
0
    def test_diff(self):
        a = pybamm.StateVector(slice(0, 1))
        b = pybamm.StateVector(slice(1, 2))
        y = np.array([5, 3])

        # power
        self.assertEqual((a ** b).diff(b).evaluate(y=y), 5 ** 3 * np.log(5))
        self.assertEqual((a ** b).diff(a).evaluate(y=y), 3 * 5 ** 2)
        self.assertEqual((a ** b).diff(a ** b).evaluate(), 1)
        self.assertEqual(
            (a ** a).diff(a).evaluate(y=y), 5 ** 5 * np.log(5) + 5 * 5 ** 4
        )
        self.assertEqual((a ** a).diff(b).evaluate(y=y), 0)

        # addition
        self.assertEqual((a + b).diff(a).evaluate(), 1)
        self.assertEqual((a + b).diff(b).evaluate(), 1)
        self.assertEqual((a + b).diff(a + b).evaluate(), 1)
        self.assertEqual((a + a).diff(a).evaluate(), 2)
        self.assertEqual((a + a).diff(b).evaluate(), 0)

        # subtraction
        self.assertEqual((a - b).diff(a).evaluate(), 1)
        self.assertEqual((a - b).diff(b).evaluate(), -1)
        self.assertEqual((a - b).diff(a - b).evaluate(), 1)
        self.assertEqual((a - a).diff(a).evaluate(), 0)
        self.assertEqual((a + a).diff(b).evaluate(), 0)

        # multiplication
        self.assertEqual((a * b).diff(a).evaluate(y=y), 3)
        self.assertEqual((a * b).diff(b).evaluate(y=y), 5)
        self.assertEqual((a * b).diff(a * b).evaluate(y=y), 1)
        self.assertEqual((a * a).diff(a).evaluate(y=y), 10)
        self.assertEqual((a * a).diff(b).evaluate(y=y), 0)

        # matrix multiplication (not implemented)
        matmul = a @ b
        with self.assertRaises(NotImplementedError):
            matmul.diff(a)

        # inner
        self.assertEqual(pybamm.inner(a, b).diff(a).evaluate(y=y), 3)
        self.assertEqual(pybamm.inner(a, b).diff(b).evaluate(y=y), 5)
        self.assertEqual(pybamm.inner(a, b).diff(pybamm.inner(a, b)).evaluate(y=y), 1)
        self.assertEqual(pybamm.inner(a, a).diff(a).evaluate(y=y), 10)
        self.assertEqual(pybamm.inner(a, a).diff(b).evaluate(y=y), 0)

        # division
        self.assertEqual((a / b).diff(a).evaluate(y=y), 1 / 3)
        self.assertEqual((a / b).diff(b).evaluate(y=y), -5 / 9)
        self.assertEqual((a / b).diff(a / b).evaluate(y=y), 1)
        self.assertEqual((a / a).diff(a).evaluate(y=y), 0)
        self.assertEqual((a / a).diff(b).evaluate(y=y), 0)
Esempio n. 7
0
    def _get_standard_coupled_variables(self, variables):

        param = self.param

        T = variables["Cell temperature"]
        T_n, _, T_p = T.orphans

        j_n = variables["Negative electrode interfacial current density"]
        j_p = variables["Positive electrode interfacial current density"]

        eta_r_n = variables["Negative electrode reaction overpotential"]
        eta_r_p = variables["Positive electrode reaction overpotential"]

        dUdT_n = variables["Negative electrode entropic change"]
        dUdT_p = variables["Positive electrode entropic change"]

        i_e = variables["Electrolyte current density"]
        phi_e = variables["Electrolyte potential"]

        i_s_n = variables["Negative electrode current density"]
        i_s_p = variables["Positive electrode current density"]
        phi_s_n = variables["Negative electrode potential"]
        phi_s_p = variables["Positive electrode potential"]

        # Ohmic heating in solid
        Q_ohm_s_cn, Q_ohm_s_cp = self._current_collector_heating(variables)
        Q_ohm_s_n = -pybamm.inner(i_s_n, pybamm.grad(phi_s_n))
        Q_ohm_s_s = pybamm.FullBroadcast(0, ["separator"], "current collector")
        Q_ohm_s_p = -pybamm.inner(i_s_p, pybamm.grad(phi_s_p))
        Q_ohm_s = pybamm.Concatenation(Q_ohm_s_n, Q_ohm_s_s, Q_ohm_s_p)

        # Ohmic heating in electrolyte
        # TODO: change full stefan-maxwell conductivity so that i_e is always
        # a Concatenation
        if isinstance(i_e, pybamm.Concatenation):
            # compute by domain if possible
            i_e_n, i_e_s, i_e_p = i_e.orphans
            phi_e_n, phi_e_s, phi_e_p = phi_e.orphans
            Q_ohm_e_n = -pybamm.inner(i_e_n, pybamm.grad(phi_e_n))
            Q_ohm_e_s = -pybamm.inner(i_e_s, pybamm.grad(phi_e_s))
            Q_ohm_e_p = -pybamm.inner(i_e_p, pybamm.grad(phi_e_p))
            Q_ohm_e = pybamm.Concatenation(Q_ohm_e_n, Q_ohm_e_s, Q_ohm_e_p)
        else:
            Q_ohm_e = -pybamm.inner(i_e, pybamm.grad(phi_e))

        # Total Ohmic heating
        Q_ohm = Q_ohm_s + Q_ohm_e

        # Irreversible electrochemical heating
        Q_rxn_n = j_n * eta_r_n
        Q_rxn_p = j_p * eta_r_p
        Q_rxn = pybamm.Concatenation(*[
            Q_rxn_n,
            pybamm.FullBroadcast(0, ["separator"], "current collector"),
            Q_rxn_p,
        ])

        # Reversible electrochemical heating
        Q_rev_n = j_n * (param.Theta**(-1) + T_n) * dUdT_n
        Q_rev_p = j_p * (param.Theta**(-1) + T_p) * dUdT_p
        Q_rev = pybamm.Concatenation(*[
            Q_rev_n,
            pybamm.FullBroadcast(0, ["separator"], "current collector"),
            Q_rev_p,
        ])

        # Total heating
        Q = Q_ohm + Q_rxn + Q_rev

        # Compute the X-average over the entire cell, including current collectors
        Q_ohm_av = self._x_average(Q_ohm, Q_ohm_s_cn, Q_ohm_s_cp)
        Q_rxn_av = self._x_average(Q_rxn, 0, 0)
        Q_rev_av = self._x_average(Q_rev, 0, 0)
        Q_av = self._x_average(Q, Q_ohm_s_cn, Q_ohm_s_cp)

        # Compute volume-averaged heat source terms
        Q_ohm_vol_av = self._yz_average(Q_ohm_av)
        Q_rxn_vol_av = self._yz_average(Q_rxn_av)
        Q_rev_vol_av = self._yz_average(Q_rev_av)
        Q_vol_av = self._yz_average(Q_av)

        # Dimensional scaling for heat source terms
        Q_scale = param.i_typ * param.potential_scale / param.L_x

        variables.update({
            "Ohmic heating":
            Q_ohm,
            "Ohmic heating [W.m-3]":
            Q_ohm * Q_scale,
            "X-averaged Ohmic heating":
            Q_ohm_av,
            "X-averaged Ohmic heating [W.m-3]":
            Q_ohm_av * Q_scale,
            "Volume-averaged Ohmic heating":
            Q_ohm_vol_av,
            "Volume-averaged Ohmic heating [W.m-3]":
            Q_ohm_vol_av * Q_scale,
            "Irreversible electrochemical heating":
            Q_rxn,
            "Irreversible electrochemical heating [W.m-3]":
            Q_rxn * Q_scale,
            "X-averaged irreversible electrochemical heating":
            Q_rxn_av,
            "X-averaged irreversible electrochemical heating [W.m-3]":
            Q_rxn_av * Q_scale,
            "Volume-averaged irreversible electrochemical heating":
            Q_rxn_vol_av,
            "Volume-averaged irreversible electrochemical heating " + "[W.m-3]":
            Q_rxn_vol_av * Q_scale,
            "Reversible heating":
            Q_rev,
            "Reversible heating [W.m-3]":
            Q_rev * Q_scale,
            "X-averaged reversible heating":
            Q_rev_av,
            "X-averaged reversible heating [W.m-3]":
            Q_rev_av * Q_scale,
            "Volume-averaged reversible heating":
            Q_rev_vol_av,
            "Volume-averaged reversible heating [W.m-3]":
            Q_rev_vol_av * Q_scale,
            "Total heating":
            Q,
            "Total heating [W.m-3]":
            Q * Q_scale,
            "X-averaged total heating":
            Q_av,
            "X-averaged total heating [W.m-3]":
            Q_av * Q_scale,
            "Volume-averaged total heating":
            Q_vol_av,
            "Volume-averaged total heating [W.m-3]":
            Q_vol_av * Q_scale,
        })
        return variables
Esempio n. 8
0

def D(cc):
    c_dim = c_inf_dim * cc
    return D_dim(c_dim) / D_dim(c_inf_dim)


# variables
x = pybamm.SpatialVariable("x", domain="SEI layer", coord_sys="cartesian")
c = pybamm.Variable("Solvent concentration", domain="SEI layer")
L = pybamm.Variable("SEI thickness")

# 3. State governing equations ---------------------------------------------------------
R = k * pybamm.BoundaryValue(c, "left")  # SEI reaction flux
N = -(1 / L) * D(c) * pybamm.grad(c)  # solvent flux
dcdt = (V_hat * R) * pybamm.inner(x / L, pybamm.grad(c)) - (
    1 / L) * pybamm.div(N)  # solvent concentration governing equation
dLdt = V_hat * R  # SEI thickness governing equation

model.rhs = {c: dcdt, L: dLdt}  # add to model

# 4. State boundary conditions ---------------------------------------------------------
D_left = pybamm.BoundaryValue(
    D(c),
    "left")  # pybamm requires BoundaryValue(D(c)) and not D(BoundaryValue(c))
grad_c_left = L * R / D_left  # left bc
c_right = pybamm.Scalar(1)  # right bc

# add to model
model.boundary_conditions = {
    c: {
Esempio n. 9
0
    def _get_standard_coupled_variables(self, variables):

        param = self.param

        T = variables["Cell temperature"]
        T_n, _, T_p = T.orphans

        j_n = variables["Negative electrode interfacial current density"]
        j_p = variables["Positive electrode interfacial current density"]

        eta_r_n = variables["Negative electrode reaction overpotential"]
        eta_r_p = variables["Positive electrode reaction overpotential"]

        dUdT_n = variables["Negative electrode entropic change"]
        dUdT_p = variables["Positive electrode entropic change"]

        i_e = variables["Electrolyte current density"]
        phi_e = variables["Electrolyte potential"]

        i_s_n = variables["Negative electrode current density"]
        i_s_p = variables["Positive electrode current density"]
        phi_s_n = variables["Negative electrode potential"]
        phi_s_p = variables["Positive electrode potential"]

        Q_ohm_s_cn, Q_ohm_s_cp = self._current_collector_heating(variables)
        Q_ohm_s_n = -pybamm.inner(i_s_n, pybamm.grad(phi_s_n))
        Q_ohm_s_s = pybamm.FullBroadcast(0, ["separator"], "current collector")
        Q_ohm_s_p = -pybamm.inner(i_s_p, pybamm.grad(phi_s_p))
        Q_ohm_s = pybamm.Concatenation(Q_ohm_s_n, Q_ohm_s_s, Q_ohm_s_p)

        Q_ohm_e = -pybamm.inner(i_e, pybamm.grad(phi_e))

        Q_ohm = Q_ohm_s + Q_ohm_e

        Q_rxn_n = j_n * eta_r_n
        Q_rxn_p = j_p * eta_r_p
        Q_rxn = pybamm.Concatenation(
            *[
                Q_rxn_n,
                pybamm.FullBroadcast(0, ["separator"], "current collector"),
                Q_rxn_p,
            ]
        )

        Q_rev_n = j_n * (param.Theta ** (-1) + T_n) * dUdT_n
        Q_rev_p = j_p * (param.Theta ** (-1) + T_p) * dUdT_p
        Q_rev = pybamm.Concatenation(
            *[
                Q_rev_n,
                pybamm.FullBroadcast(0, ["separator"], "current collector"),
                Q_rev_p,
            ]
        )

        Q = Q_ohm + Q_rxn + Q_rev

        # Compute the X-average over the current collectors by default.
        # Note: the method 'self._x_average' is overwritten by models which do
        # not include current collector effects, so that the average is just taken
        # over the negative electrode, separator and positive electrode.
        Q_ohm_av = self._x_average(Q_ohm, Q_ohm_s_cn, Q_ohm_s_cp)
        Q_rxn_av = self._x_average(Q_rxn, 0, 0)
        Q_rev_av = self._x_average(Q_rev, 0, 0)
        Q_av = self._x_average(Q, Q_ohm_s_cn, Q_ohm_s_cp)

        Q_ohm_vol_av = self._yz_average(Q_ohm_av)
        Q_rxn_vol_av = self._yz_average(Q_rxn_av)
        Q_rev_vol_av = self._yz_average(Q_rev_av)
        Q_vol_av = self._yz_average(Q_av)

        variables.update(
            {
                "Ohmic heating": Q_ohm,
                "Ohmic heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_ohm
                / param.L_x,
                "X-averaged Ohmic heating": Q_ohm_av,
                "X-averaged Ohmic heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_ohm_av
                / param.L_x,
                "Volume-averaged Ohmic heating": Q_ohm_vol_av,
                "Volume-averaged Ohmic heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_ohm_vol_av
                / param.L_x,
                "Irreversible electrochemical heating": Q_rxn,
                "Irreversible electrochemical heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rxn
                / param.L_x,
                "X-averaged electrochemical heating": Q_rxn_av,
                "X-averaged electrochemical heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rxn_av
                / param.L_x,
                "Volume-averaged electrochemical heating": Q_rxn_vol_av,
                "Volume-averaged electrochemical heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rxn_vol_av
                / param.L_x,
                "Reversible heating": Q_rev,
                "Reversible heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rev
                / param.L_x,
                "X-averaged reversible heating": Q_rev_av,
                "X-averaged reversible heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rev_av
                / param.L_x,
                "Volume-averaged reversible heating": Q_rev_vol_av,
                "Volume-averaged reversible heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_rev_vol_av
                / param.L_x,
                "Total heating": Q,
                "Total heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q
                / param.L_x,
                "X-averaged total heating": Q_av,
                "X-averaged total heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_av
                / param.L_x,
                "Volume-averaged total heating": Q_vol_av,
                "Volume-averaged total heating [A.V.m-3]": param.i_typ
                * param.potential_scale
                * Q_vol_av
                / param.L_x,
            }
        )
        return variables
Esempio n. 10
0
 def _binary_new_copy(self, left, right):
     """ See :meth:`pybamm.BinaryOperator._binary_new_copy()`. """
     return pybamm.inner(left, right)