Пример #1
0
    def test_softminus_softplus(self):
        a = pybamm.Scalar(1)
        b = pybamm.StateVector(slice(0, 1))

        minimum = pybamm.softminus(a, b, 50)
        self.assertAlmostEqual(minimum.evaluate(y=np.array([2]))[0, 0], 1)
        self.assertAlmostEqual(minimum.evaluate(y=np.array([0]))[0, 0], 0)
        self.assertEqual(
            str(minimum), "log(1.9287498479639178e-22 + exp(-50.0 * y[0:1])) / -50.0"
        )

        maximum = pybamm.softplus(a, b, 50)
        self.assertAlmostEqual(maximum.evaluate(y=np.array([2]))[0, 0], 2)
        self.assertAlmostEqual(maximum.evaluate(y=np.array([0]))[0, 0], 1)
        self.assertEqual(
            str(maximum), "log(5.184705528587072e+21 + exp(50.0 * y[0:1])) / 50.0"
        )

        # Test that smooth min/max are used when the setting is changed
        pybamm.settings.min_smoothing = 10
        pybamm.settings.max_smoothing = 10

        self.assertEqual(str(pybamm.minimum(a, b)), str(pybamm.softminus(a, b, 10)))
        self.assertEqual(str(pybamm.maximum(a, b)), str(pybamm.softplus(a, b, 10)))

        # But exact min/max should still be used if both variables are constant
        a = pybamm.Scalar(1)
        b = pybamm.Scalar(2)
        self.assertEqual(str(pybamm.minimum(a, b)), str(a))
        self.assertEqual(str(pybamm.maximum(a, b)), str(b))

        # Change setting back for other tests
        pybamm.settings.min_smoothing = "exact"
        pybamm.settings.max_smoothing = "exact"
Пример #2
0
    def __init__(self, name="Electrode-specific SOH model"):
        pybamm.citations.register("Mohtat2019")
        super().__init__(name)
        param = pybamm.LithiumIonParameters()

        Un = param.U_n_dimensional
        Up = param.U_p_dimensional
        T_ref = param.T_ref

        x_100 = pybamm.Variable("x_100", bounds=(0, 1))
        C = pybamm.Variable("C", bounds=(0, np.inf))

        V_max = pybamm.InputParameter("V_max")
        V_min = pybamm.InputParameter("V_min")
        C_n = pybamm.InputParameter("C_n")
        C_p = pybamm.InputParameter("C_p")
        n_Li = pybamm.InputParameter("n_Li")

        y_100 = (n_Li * param.F / 3600 - x_100 * C_n) / C_p
        x_0 = x_100 - C / C_n
        y_0 = y_100 + C / C_p

        self.algebraic = {
            x_100: Up(y_100, T_ref) - Un(x_100, T_ref) - V_max,
            C: Up(y_0, T_ref) - Un(x_0, T_ref) - V_min,
        }

        # initial guess must be chosen such that 0 < x_0, y_0, x_100, y_100 < 1
        # First guess for x_100
        x_100_init = 0.85
        # Make sure x_0 = x_100 - C/C_n > 0
        C_init = param.Q
        C_init = pybamm.minimum(C_n * x_100_init - 0.1, C_init)
        # Make sure y_100 > 0
        # x_100_init = pybamm.minimum(n_Li * param.F / 3600 / C_n - 0.01, x_100_init)
        self.initial_conditions = {x_100: x_100_init, C: C_init}

        self.variables = {
            "x_100": x_100,
            "y_100": y_100,
            "C": C,
            "x_0": x_0,
            "y_0": y_0,
            "Un(x_100)": Un(x_100, T_ref),
            "Un(x_0)": Un(x_0, T_ref),
            "Up(y_100)": Up(y_100, T_ref),
            "Up(y_0)": Up(y_0, T_ref),
            "Up(y_100) - Un(x_100)": Up(y_100, T_ref) - Un(x_100, T_ref),
            "Up(y_0) - Un(x_0)": Up(y_0, T_ref) - Un(x_0, T_ref),
            "n_Li_100": 3600 / param.F * (y_100 * C_p + x_100 * C_n),
            "n_Li_0": 3600 / param.F * (y_0 * C_p + x_0 * C_n),
            "n_Li": n_Li,
            "C_n": C_n,
            "C_p": C_p,
            "C_n * (x_100 - x_0)": C_n * (x_100 - x_0),
            "C_p * (y_100 - y_0)": C_p * (y_0 - y_100),
        }
Пример #3
0
 def test_jac_of_minimum_maximum(self):
     y = pybamm.StateVector(slice(0, 10))
     y_test = np.linspace(0, 2, 10)
     np.testing.assert_array_equal(
         np.diag(pybamm.minimum(1, y**2).jac(y).evaluate(y=y_test)),
         2 * y_test * (y_test < 1),
     )
     np.testing.assert_array_equal(
         np.diag(pybamm.maximum(1, y**2).jac(y).evaluate(y=y_test)),
         2 * y_test * (y_test > 1),
     )
Пример #4
0
    def test_symbol_new_copy(self):
        a = pybamm.Parameter("a")
        b = pybamm.Parameter("b")
        v_n = pybamm.Variable("v", "negative electrode")
        x_n = pybamm.standard_spatial_vars.x_n
        v_s = pybamm.Variable("v", "separator")
        vec = pybamm.Vector([1, 2, 3, 4, 5])
        mat = pybamm.Matrix([[1, 2], [3, 4]])
        mesh = get_mesh_for_testing()

        for symbol in [
                a + b,
                a - b,
                a * b,
                a / b,
                a**b,
                -a,
                abs(a),
                pybamm.Function(np.sin, a),
                pybamm.FunctionParameter("function", {"a": a}),
                pybamm.grad(v_n),
                pybamm.div(pybamm.grad(v_n)),
                pybamm.upwind(v_n),
                pybamm.IndefiniteIntegral(v_n, x_n),
                pybamm.BackwardIndefiniteIntegral(v_n, x_n),
                pybamm.BoundaryValue(v_n, "right"),
                pybamm.BoundaryGradient(v_n, "right"),
                pybamm.PrimaryBroadcast(a, "domain"),
                pybamm.SecondaryBroadcast(v_n, "current collector"),
                pybamm.FullBroadcast(a, "domain",
                                     {"secondary": "other domain"}),
                pybamm.concatenation(v_n, v_s),
                pybamm.NumpyConcatenation(a, b, v_s),
                pybamm.DomainConcatenation([v_n, v_s], mesh),
                pybamm.Parameter("param"),
                pybamm.InputParameter("param"),
                pybamm.StateVector(slice(0, 56)),
                pybamm.Matrix(np.ones((50, 40))),
                pybamm.SpatialVariable("x", ["negative electrode"]),
                pybamm.t,
                pybamm.Index(vec, 1),
                pybamm.NotConstant(a),
                pybamm.ExternalVariable(
                    "external variable",
                    20,
                    domain="test",
                    auxiliary_domains={"secondary": "test2"},
                ),
                pybamm.minimum(a, b),
                pybamm.maximum(a, b),
                pybamm.SparseStack(mat, mat),
        ]:
            self.assertEqual(symbol.id, symbol.new_copy().id)
Пример #5
0
    def test_diff_maximum_minimum(self):
        a = pybamm.Scalar(1)
        b = pybamm.StateVector(slice(0, 1))

        func = pybamm.minimum(a, b ** 3)
        self.assertEqual(func.diff(b).evaluate(y=np.array([10])), 0)
        self.assertEqual(func.diff(b).evaluate(y=np.array([2])), 0)
        self.assertEqual(func.diff(b).evaluate(y=np.array([-2])), 3 * (-2) ** 2)

        func = pybamm.maximum(a, b ** 3)
        self.assertEqual(func.diff(b).evaluate(y=np.array([10])), 3 * 10 ** 2)
        self.assertEqual(func.diff(b).evaluate(y=np.array([2])), 3 * 2 ** 2)
        self.assertEqual(func.diff(b).evaluate(y=np.array([-2])), 0)
Пример #6
0
    def test_minimum_maximum(self):
        a = pybamm.Scalar(1)
        b = pybamm.StateVector(slice(0, 1))
        minimum = pybamm.minimum(a, b)
        self.assertEqual(minimum.evaluate(y=np.array([2])), 1)
        self.assertEqual(minimum.evaluate(y=np.array([1])), 1)
        self.assertEqual(minimum.evaluate(y=np.array([0])), 0)
        self.assertEqual(str(minimum), "minimum(1.0, y[0:1])")

        maximum = pybamm.maximum(a, b)
        self.assertEqual(maximum.evaluate(y=np.array([2])), 2)
        self.assertEqual(maximum.evaluate(y=np.array([1])), 1)
        self.assertEqual(maximum.evaluate(y=np.array([0])), 1)
        self.assertEqual(str(maximum), "maximum(1.0, y[0:1])")
Пример #7
0
    def __init__(self, working_electrode, name="Electrode-specific SOH model"):
        self.working_electrode = working_electrode
        pybamm.citations.register("Mohtat2019")
        super().__init__(name)
        param = pybamm.LithiumIonParameters(
            {"working electrode": working_electrode})

        x_100 = pybamm.Variable("x_100", bounds=(0, 1))
        C = pybamm.Variable("C", bounds=(0, np.inf))
        Cw = pybamm.InputParameter("C_w")
        T_ref = param.T_ref
        if working_electrode == "negative":  # pragma: no cover
            raise NotImplementedError
        elif working_electrode == "positive":
            Uw = param.U_p_dimensional
            x_0 = x_100 + C / Cw

        V_max = pybamm.InputParameter("V_max")
        V_min = pybamm.InputParameter("V_min")

        self.algebraic = {
            x_100: Uw(x_100, T_ref) - V_max,
            C: Uw(x_0, T_ref) - V_min,
        }

        # initial guess must be chosen such that 0 < x_0, x_100 < 1
        # First guess for x_100
        x_100_init = 0.85
        # Make sure x_0 = x_100 - C/C_w > 0
        C_init = param.Q
        C_init = pybamm.minimum(Cw * x_100_init - 0.1, C_init)
        self.initial_conditions = {x_100: x_100_init, C: C_init}

        self.variables = {
            "x_100": x_100,
            "C": C,
            "x_0": x_0,
            "Uw(x_100)": Uw(x_100, T_ref),
            "Uw(x_0)": Uw(x_0, T_ref),
            "C_w": Cw,
            "C_w * (x_100 - x_0)": Cw * (x_100 - x_0),
        }
Пример #8
0
    def test_evaluator_python(self):
        a = pybamm.StateVector(slice(0, 1))
        b = pybamm.StateVector(slice(1, 2))

        y_tests = [np.array([[2], [3]]), np.array([[1], [3]])]
        t_tests = [1, 2]

        # test a * b
        expr = a * b
        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate(t=None, y=np.array([[2], [3]]))
        self.assertEqual(result, 6)
        result = evaluator.evaluate(t=None, y=np.array([[1], [3]]))
        self.assertEqual(result, 3)

        # test function(a*b)
        expr = pybamm.Function(test_function, a * b)
        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate(t=None, y=np.array([[2], [3]]))
        self.assertEqual(result, 12)

        # test a constant expression
        expr = pybamm.Scalar(2) * pybamm.Scalar(3)
        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate()
        self.assertEqual(result, 6)

        # test a larger expression
        expr = a * b + b + a**2 / b + 2 * a + b / 2 + 4
        evaluator = pybamm.EvaluatorPython(expr)
        for y in y_tests:
            result = evaluator.evaluate(t=None, y=y)
            self.assertEqual(result, expr.evaluate(t=None, y=y))

        # test something with time
        expr = a * pybamm.t
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            self.assertEqual(result, expr.evaluate(t=t, y=y))

        # test something with a matrix multiplication
        A = pybamm.Matrix(np.array([[1, 2], [3, 4]]))
        expr = A @ pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with a heaviside
        a = pybamm.Vector(np.array([1, 2]))
        expr = a <= pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        expr = a > pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with a minimum or maximum
        a = pybamm.Vector(np.array([1, 2]))
        expr = pybamm.minimum(a, pybamm.StateVector(slice(0, 2)))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        expr = pybamm.maximum(a, pybamm.StateVector(slice(0, 2)))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with an index
        expr = pybamm.Index(A @ pybamm.StateVector(slice(0, 2)), 0)
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            self.assertEqual(result, expr.evaluate(t=t, y=y))

        # test something with a sparse matrix multiplication
        A = pybamm.Matrix(np.array([[1, 2], [3, 4]]))
        B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        C = pybamm.Matrix(scipy.sparse.coo_matrix(np.array([[1, 0], [0, 4]])))
        expr = A @ B @ C @ pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test numpy concatenation
        a = pybamm.Vector(np.array([[1], [2]]))
        b = pybamm.Vector(np.array([[3]]))
        expr = pybamm.NumpyConcatenation(a, b)
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test sparse stack
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[2, 0], [5, 0]])))
        expr = pybamm.SparseStack(A, B)
        evaluator = pybamm.EvaluatorPython(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y).toarray()
            np.testing.assert_allclose(result,
                                       expr.evaluate(t=t, y=y).toarray())

        # test Inner
        v = pybamm.Vector(np.ones(5), domain="test")
        w = pybamm.Vector(2 * np.ones(5), domain="test")
        expr = pybamm.Inner(v, w)
        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate()
        np.testing.assert_allclose(result, expr.evaluate())
Пример #9
0
    def test_evaluator_jax(self):
        a = pybamm.StateVector(slice(0, 1))
        b = pybamm.StateVector(slice(1, 2))

        y_tests = [
            np.array([[2.0], [3.0]]),
            np.array([[1.0], [3.0]]),
            np.array([1.0, 3.0]),
        ]
        t_tests = [1.0, 2.0]

        # test a * b
        expr = a * b
        evaluator = pybamm.EvaluatorJax(expr)
        result = evaluator.evaluate(t=None, y=np.array([[2], [3]]))
        self.assertEqual(result, 6)
        result = evaluator.evaluate(t=None, y=np.array([[1], [3]]))
        self.assertEqual(result, 3)

        # test function(a*b)
        expr = pybamm.Function(test_function, a * b)
        evaluator = pybamm.EvaluatorJax(expr)
        result = evaluator.evaluate(t=None, y=np.array([[2], [3]]))
        self.assertEqual(result, 12)

        # test exp
        expr = pybamm.exp(a * b)
        evaluator = pybamm.EvaluatorJax(expr)
        result = evaluator.evaluate(t=None, y=np.array([[2], [3]]))
        self.assertEqual(result, np.exp(6))

        # test a constant expression
        expr = pybamm.Scalar(2) * pybamm.Scalar(3)
        evaluator = pybamm.EvaluatorJax(expr)
        result = evaluator.evaluate()
        self.assertEqual(result, 6)

        # test a larger expression
        expr = a * b + b + a**2 / b + 2 * a + b / 2 + 4
        evaluator = pybamm.EvaluatorJax(expr)
        for y in y_tests:
            result = evaluator.evaluate(t=None, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=None, y=y))

        # test something with time
        expr = a * pybamm.t
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            self.assertEqual(result, expr.evaluate(t=t, y=y))

        # test something with a matrix multiplication
        A = pybamm.Matrix(np.array([[1, 2], [3, 4]]))
        expr = A @ pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with a heaviside
        a = pybamm.Vector(np.array([1, 2]))
        expr = a <= pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        expr = a > pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with a minimum or maximum
        a = pybamm.Vector(np.array([1, 2]))
        expr = pybamm.minimum(a, pybamm.StateVector(slice(0, 2)))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        expr = pybamm.maximum(a, pybamm.StateVector(slice(0, 2)))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test something with an index
        expr = pybamm.Index(A @ pybamm.StateVector(slice(0, 2)), 0)
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            self.assertEqual(result, expr.evaluate(t=t, y=y))

        # test something with a sparse matrix-vector multiplication
        A = pybamm.Matrix(np.array([[1, 2], [3, 4]]))
        B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        C = pybamm.Matrix(scipy.sparse.coo_matrix(np.array([[1, 0], [0, 4]])))
        expr = A @ B @ C @ pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test the sparse-scalar multiplication
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        for expr in [
                A * pybamm.t @ pybamm.StateVector(slice(0, 2)),
                pybamm.t * A @ pybamm.StateVector(slice(0, 2)),
        ]:
            evaluator = pybamm.EvaluatorJax(expr)
            for t, y in zip(t_tests, y_tests):
                result = evaluator.evaluate(t=t, y=y)
                np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test the sparse-scalar division
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        expr = A / (1.0 + pybamm.t) @ pybamm.StateVector(slice(0, 2))
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test sparse stack
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[2, 0], [5, 0]])))
        a = pybamm.StateVector(slice(0, 1))
        expr = pybamm.SparseStack(A, a * B)
        with self.assertRaises(NotImplementedError):
            evaluator = pybamm.EvaluatorJax(expr)

        # test sparse mat-mat mult
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]])))
        B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[2, 0], [5, 0]])))
        a = pybamm.StateVector(slice(0, 1))
        expr = A @ (a * B)
        with self.assertRaises(NotImplementedError):
            evaluator = pybamm.EvaluatorJax(expr)

        # test numpy concatenation
        a = pybamm.Vector(np.array([[1], [2]]))
        b = pybamm.Vector(np.array([[3]]))
        expr = pybamm.NumpyConcatenation(a, b)
        evaluator = pybamm.EvaluatorJax(expr)
        for t, y in zip(t_tests, y_tests):
            result = evaluator.evaluate(t=t, y=y)
            np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))

        # test Inner
        A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1]])))
        v = pybamm.StateVector(slice(0, 1))
        for expr in [
                pybamm.Inner(A, v) @ v,
                pybamm.Inner(v, A) @ v,
                pybamm.Inner(v, v) @ v
        ]:
            evaluator = pybamm.EvaluatorJax(expr)
            for t, y in zip(t_tests, y_tests):
                result = evaluator.evaluate(t=t, y=y)
                np.testing.assert_allclose(result, expr.evaluate(t=t, y=y))
Пример #10
0
 def _binary_new_copy(self, left, right):
     "See :meth:`pybamm.BinaryOperator._binary_new_copy()`. "
     return pybamm.minimum(left, right)
Пример #11
0
    def _get_exchange_current_density(self, variables):
        """
        A private function to obtain the exchange current density

        Parameters
        ----------
        variables: dict
            The variables in the full model.

        Returns
        -------
        j0 : :class: `pybamm.Symbol`
            The exchange current density.
        """
        c_e = variables[self.domain + " electrolyte concentration"]
        T = variables[self.domain + " electrode temperature"]

        if self.reaction == "lithium-ion main":
            c_s_surf = variables[self.domain +
                                 " particle surface concentration"]

            # If variable was broadcast, take only the orphan
            if (isinstance(c_s_surf, pybamm.Broadcast)
                    and isinstance(c_e, pybamm.Broadcast)
                    and isinstance(T, pybamm.Broadcast)):
                c_s_surf = c_s_surf.orphans[0]
                c_e = c_e.orphans[0]
                T = T.orphans[0]

            tol = 1e-8
            c_e = pybamm.maximum(tol, c_e)
            c_s_surf = pybamm.maximum(tol, pybamm.minimum(c_s_surf, 1 - tol))

            if self.domain == "Negative":
                j0 = self.param.j0_n(c_e, c_s_surf, T) / self.param.C_r_n
            elif self.domain == "Positive":
                j0 = (self.param.gamma_p * self.param.j0_p(c_e, c_s_surf, T) /
                      self.param.C_r_p)

        elif self.reaction == "lead-acid main":
            # If variable was broadcast, take only the orphan
            if isinstance(c_e, pybamm.Broadcast) and isinstance(
                    T, pybamm.Broadcast):
                c_e = c_e.orphans[0]
                T = T.orphans[0]
            if self.domain == "Negative":
                j0 = self.param.j0_n(c_e, T)
            elif self.domain == "Positive":
                j0 = self.param.j0_p(c_e, T)

        elif self.reaction == "lead-acid oxygen":
            # If variable was broadcast, take only the orphan
            if isinstance(c_e, pybamm.Broadcast) and isinstance(
                    T, pybamm.Broadcast):
                c_e = c_e.orphans[0]
                T = T.orphans[0]
            if self.domain == "Negative":
                j0 = pybamm.Scalar(0)
            elif self.domain == "Positive":
                j0 = self.param.j0_p_Ox(c_e, T)
        else:
            j0 = pybamm.Scalar(0)

        return j0