Ejemplo n.º 1
0
    def test_sign(self):
        b = pybamm.Scalar(-4)
        signb = pybamm.sign(b)
        self.assertEqual(signb.evaluate(), -1)

        A = diags(np.linspace(-1, 1, 5))
        b = pybamm.Matrix(A)
        signb = pybamm.sign(b)
        np.testing.assert_array_equal(np.diag(signb.evaluate().toarray()),
                                      [-1, -1, 0, 1, 1])
Ejemplo n.º 2
0
 def test_jac_of_sign(self):
     y = pybamm.StateVector(slice(0, 10))
     func = pybamm.sign(y) * y
     jac = func.jac(y)
     y_test = np.linspace(-2, 2, 10)
     np.testing.assert_array_equal(np.diag(jac.evaluate(y=y_test)),
                                   np.sign(y_test))
Ejemplo n.º 3
0
    def test_diff(self):
        a = pybamm.StateVector(slice(0, 1))
        y = np.array([5])

        # negation
        self.assertEqual((-a).diff(a).evaluate(y=y), -1)
        self.assertEqual((-a).diff(-a).evaluate(), 1)

        # absolute value
        self.assertEqual((a**3).diff(a).evaluate(y=y), 3 * 5**2)
        self.assertEqual((abs(a**3)).diff(a).evaluate(y=y), 3 * 5**2)
        self.assertEqual((a**3).diff(a).evaluate(y=-y), 3 * 5**2)
        self.assertEqual((abs(a**3)).diff(a).evaluate(y=-y), -3 * 5**2)

        # sign
        self.assertEqual((pybamm.sign(a)).diff(a).evaluate(y=y), 0)

        # floor
        self.assertEqual((pybamm.Floor(a)).diff(a).evaluate(y=y), 0)

        # ceil
        self.assertEqual((pybamm.Ceiling(a)).diff(a).evaluate(y=y), 0)

        # spatial operator (not implemented)
        spatial_a = pybamm.SpatialOperator("name", a)
        with self.assertRaises(NotImplementedError):
            spatial_a.diff(a)
Ejemplo n.º 4
0
    def set_initial_conditions(self, variables):
        if (self.options["total interfacial current density as a state"]
                == "true" and "main" in self.reaction):
            param = self.param
            j_tot_var = variables[
                "Total " + self.domain.lower() +
                " electrode interfacial current density variable"]
            current_at_0 = (pybamm.FunctionParameter(
                "Current function [A]", {"Time [s]": 0}) / param.I_typ *
                            pybamm.sign(param.I_typ))
            if self.domain == "Negative":
                j_tot_av_init = current_at_0 / param.l_n
            elif self.domain == "Positive":
                j_tot_av_init = -current_at_0 / param.l_p

            self.initial_conditions[j_tot_var] = j_tot_av_init
Ejemplo n.º 5
0
    def set_voltage_variables(self):

        ocp_n = self.variables["Negative electrode open circuit potential"]
        ocp_p = self.variables["Positive electrode open circuit potential"]
        ocp_n_av = self.variables[
            "X-averaged negative electrode open circuit potential"]
        ocp_p_av = self.variables[
            "X-averaged positive electrode open circuit potential"]

        ocp_n_dim = self.variables[
            "Negative electrode open circuit potential [V]"]
        ocp_p_dim = self.variables[
            "Positive electrode open circuit potential [V]"]
        ocp_n_av_dim = self.variables[
            "X-averaged negative electrode open circuit potential [V]"]
        ocp_p_av_dim = self.variables[
            "X-averaged positive electrode open circuit potential [V]"]

        ocp_n_left = pybamm.boundary_value(ocp_n, "left")
        ocp_n_left_dim = pybamm.boundary_value(ocp_n_dim, "left")
        ocp_p_right = pybamm.boundary_value(ocp_p, "right")
        ocp_p_right_dim = pybamm.boundary_value(ocp_p_dim, "right")

        ocv_av = ocp_p_av - ocp_n_av
        ocv_av_dim = ocp_p_av_dim - ocp_n_av_dim
        ocv = ocp_p_right - ocp_n_left
        ocv_dim = ocp_p_right_dim - ocp_n_left_dim

        # overpotentials
        eta_r_n_av = self.variables[
            "X-averaged negative electrode reaction overpotential"]
        eta_r_n_av_dim = self.variables[
            "X-averaged negative electrode reaction overpotential [V]"]
        eta_r_p_av = self.variables[
            "X-averaged positive electrode reaction overpotential"]
        eta_r_p_av_dim = self.variables[
            "X-averaged positive electrode reaction overpotential [V]"]

        delta_phi_s_n_av = self.variables[
            "X-averaged negative electrode ohmic losses"]
        delta_phi_s_n_av_dim = self.variables[
            "X-averaged negative electrode ohmic losses [V]"]
        delta_phi_s_p_av = self.variables[
            "X-averaged positive electrode ohmic losses"]
        delta_phi_s_p_av_dim = self.variables[
            "X-averaged positive electrode ohmic losses [V]"]

        delta_phi_s_av = delta_phi_s_p_av - delta_phi_s_n_av
        delta_phi_s_av_dim = delta_phi_s_p_av_dim - delta_phi_s_n_av_dim

        eta_r_av = eta_r_p_av - eta_r_n_av
        eta_r_av_dim = eta_r_p_av_dim - eta_r_n_av_dim

        # SEI film overpotential
        eta_sei_n_av = self.variables[
            "X-averaged negative electrode SEI film overpotential"]
        eta_sei_p_av = self.variables[
            "X-averaged positive electrode SEI film overpotential"]
        eta_sei_n_av_dim = self.variables[
            "X-averaged negative electrode SEI film overpotential [V]"]
        eta_sei_p_av_dim = self.variables[
            "X-averaged positive electrode SEI film overpotential [V]"]
        eta_sei_av = eta_sei_n_av + eta_sei_p_av
        eta_sei_av_dim = eta_sei_n_av_dim + eta_sei_p_av_dim

        # TODO: add current collector losses to the voltage in 3D

        self.variables.update({
            "X-averaged open circuit voltage":
            ocv_av,
            "Measured open circuit voltage":
            ocv,
            "X-averaged open circuit voltage [V]":
            ocv_av_dim,
            "Measured open circuit voltage [V]":
            ocv_dim,
            "X-averaged reaction overpotential":
            eta_r_av,
            "X-averaged reaction overpotential [V]":
            eta_r_av_dim,
            "X-averaged SEI film overpotential":
            eta_sei_av,
            "X-averaged SEI film overpotential [V]":
            eta_sei_av_dim,
            "X-averaged solid phase ohmic losses":
            delta_phi_s_av,
            "X-averaged solid phase ohmic losses [V]":
            delta_phi_s_av_dim,
        })

        # Battery-wide variables
        V = self.variables["Terminal voltage"]
        V_dim = self.variables["Terminal voltage [V]"]
        eta_e_av_dim = self.variables[
            "X-averaged electrolyte ohmic losses [V]"]
        eta_c_av_dim = self.variables[
            "X-averaged concentration overpotential [V]"]
        num_cells = pybamm.Parameter(
            "Number of cells connected in series to make a battery")
        self.variables.update({
            "X-averaged battery open circuit voltage [V]":
            ocv_av_dim * num_cells,
            "Measured battery open circuit voltage [V]":
            ocv_dim * num_cells,
            "X-averaged battery reaction overpotential [V]":
            eta_r_av_dim * num_cells,
            "X-averaged battery solid phase ohmic losses [V]":
            delta_phi_s_av_dim * num_cells,
            "X-averaged battery electrolyte ohmic losses [V]":
            eta_e_av_dim * num_cells,
            "X-averaged battery concentration overpotential [V]":
            eta_c_av_dim * num_cells,
            "Battery voltage [V]":
            V_dim * num_cells,
        })
        # Variables for calculating the equivalent circuit model (ECM) resistance
        # Need to compare OCV to initial value to capture this as an overpotential
        ocv_init = self.param.U_p(
            self.param.c_p_init(1), self.param.T_init) - self.param.U_n(
                self.param.c_n_init(0), self.param.T_init)
        ocv_init_dim = (self.param.U_p_ref - self.param.U_n_ref +
                        self.param.potential_scale * ocv_init)
        eta_ocv = ocv - ocv_init
        eta_ocv_dim = ocv_dim - ocv_init_dim
        # Current collector current density for working out euiqvalent resistance
        # based on Ohm's Law
        i_cc = self.variables["Current collector current density"]
        i_cc_dim = self.variables["Current collector current density [A.m-2]"]
        # ECM overvoltage is OCV minus terminal voltage
        v_ecm = ocv - V
        v_ecm_dim = ocv_dim - V_dim
        # Current collector area for turning resistivity into resistance
        A_cc = self.param.A_cc

        # Hack to avoid division by zero if i_cc is exactly zero
        # If i_cc is zero, i_cc_not_zero becomes 1. But multiplying by sign(i_cc) makes
        # the local resistance 'zero' (really, it's not defined when i_cc is zero)
        i_cc_not_zero = ((i_cc > 0) +
                         (i_cc < 0)) * i_cc + (i_cc >= 0) * (i_cc <= 0)
        i_cc_dim_not_zero = (
            (i_cc_dim > 0) +
            (i_cc_dim < 0)) * i_cc_dim + (i_cc_dim >= 0) * (i_cc_dim <= 0)

        self.variables.update({
            "Change in measured open circuit voltage":
            eta_ocv,
            "Change in measured open circuit voltage [V]":
            eta_ocv_dim,
            "Local ECM resistance":
            pybamm.sign(i_cc) * v_ecm / (i_cc_not_zero * A_cc),
            "Local ECM resistance [Ohm]":
            pybamm.sign(i_cc) * v_ecm_dim / (i_cc_dim_not_zero * A_cc),
        })

        # Cut-off voltage
        self.events.append(
            pybamm.Event(
                "Minimum voltage",
                V - self.param.voltage_low_cut,
                pybamm.EventType.TERMINATION,
            ))
        self.events.append(
            pybamm.Event(
                "Maximum voltage",
                V - self.param.voltage_high_cut,
                pybamm.EventType.TERMINATION,
            ))

        # Cut-off open-circuit voltage (for event switch with casadi 'fast with events'
        # mode)
        # A tolerance of 1 is sufficiently small since the dimensionless voltage is
        # scaled with the thermal voltage (0.025V) and hence has a range of around 60
        tol = 1
        self.events.append(
            pybamm.Event(
                "Minimum voltage switch",
                V - (self.param.voltage_low_cut - tol),
                pybamm.EventType.SWITCH,
            ))
        self.events.append(
            pybamm.Event(
                "Maximum voltage switch",
                V - (self.param.voltage_high_cut + tol),
                pybamm.EventType.SWITCH,
            ))

        # Power
        I_dim = self.variables["Current [A]"]
        self.variables.update({"Terminal power [W]": I_dim * V_dim})