Example #1
0
    def set_rhs(self, variables):
        T = variables["Cell temperature"]
        T_n = variables["Negative electrode temperature"]
        T_s = variables["Separator temperature"]
        T_p = variables["Positive electrode temperature"]

        Q = variables["Total heating"]

        # Define volumetric heat capacity
        rho_k = pybamm.concatenation(
            self.param.rho_n(T_n),
            self.param.rho_s(T_s),
            self.param.rho_p(T_p),
        )

        # Devine thermal conductivity
        lambda_k = pybamm.concatenation(
            self.param.lambda_n(T_n),
            self.param.lambda_s(T_s),
            self.param.lambda_p(T_p),
        )

        # Fourier's law for heat flux
        q = -lambda_k * pybamm.grad(T)

        # N.B only y-z surface cooling is implemented for this model
        self.rhs = {
            T: (-pybamm.div(q) / self.param.delta**2 + self.param.B * Q) /
            (self.param.C_th * rho_k)
        }
    def test_concatenation_simplify(self):
        # Primary broadcast
        var = pybamm.Variable("var", "current collector")
        a = pybamm.PrimaryBroadcast(var, "negative electrode")
        b = pybamm.PrimaryBroadcast(var, "separator")
        c = pybamm.PrimaryBroadcast(var, "positive electrode")

        concat = pybamm.concatenation(a, b, c)
        self.assertIsInstance(concat, pybamm.PrimaryBroadcast)
        self.assertEqual(concat.orphans[0], var)
        self.assertEqual(
            concat.domain,
            ["negative electrode", "separator", "positive electrode"])

        # Full broadcast
        a = pybamm.FullBroadcast(0, "negative electrode", "current collector")
        b = pybamm.FullBroadcast(0, "separator", "current collector")
        c = pybamm.FullBroadcast(0, "positive electrode", "current collector")

        concat = pybamm.concatenation(a, b, c)
        self.assertIsInstance(concat, pybamm.FullBroadcast)
        self.assertEqual(concat.orphans[0].id, pybamm.Scalar(0).id)
        self.assertEqual(
            concat.domain,
            ["negative electrode", "separator", "positive electrode"])
        self.assertEqual(concat.auxiliary_domains,
                         {"secondary": ["current collector"]})
Example #3
0
    def set_rhs(self, variables):
        """Composite reaction-diffusion with source terms from leading order."""

        param = self.param

        eps_0_s = variables["Leading-order separator porosity"]
        eps_0_p = variables["Leading-order positive electrode porosity"]
        eps_0 = pybamm.concatenation(eps_0_s, eps_0_p)

        deps_0_dt_s = variables["Leading-order separator porosity change"]
        deps_0_dt_p = variables[
            "Leading-order positive electrode porosity change"]
        deps_0_dt = pybamm.concatenation(deps_0_dt_s, deps_0_dt_p)

        c_ox = variables[
            "Separator and positive electrode oxygen concentration"]
        N_ox = variables["Oxygen flux"].orphans[1]

        if self.extended is False:
            j_ox_0 = variables[
                "Leading-order positive electrode oxygen interfacial current density"]
            pos_reactions = param.s_ox_Ox * j_ox_0
        else:
            j_ox_0 = variables[
                "Positive electrode oxygen interfacial current density"]
            pos_reactions = param.s_ox_Ox * j_ox_0
        sep_reactions = pybamm.FullBroadcast(0, "separator",
                                             "current collector")
        source_terms_0 = (pybamm.concatenation(sep_reactions, pos_reactions) /
                          param.gamma_e)

        self.rhs = {
            c_ox: (1 / eps_0) *
            (-pybamm.div(N_ox) / param.C_e + source_terms_0 - c_ox * deps_0_dt)
        }
Example #4
0
    def test_symbol_replacements(self):
        a = pybamm.Parameter("a")
        b = pybamm.Parameter("b")
        c = pybamm.Parameter("c")
        d = pybamm.Parameter("d")
        replacer = pybamm.SymbolReplacer({a: b, c: d})

        for symbol_in, symbol_out in [
            (a, b),  # just the symbol
            (a + a, b + b),  # binary operator
            (2 * pybamm.sin(a), 2 * pybamm.sin(b)),  # function
            (3 * b, 3 * b),  # no replacement
            (a + c, b + d),  # two replacements
        ]:
            replaced_symbol = replacer.process_symbol(symbol_in)
            self.assertEqual(replaced_symbol.id, symbol_out.id)

        var1 = pybamm.Variable("var 1", domain="dom 1")
        var2 = pybamm.Variable("var 2", domain="dom 2")
        var3 = pybamm.Variable("var 3", domain="dom 1")
        conc = pybamm.concatenation(var1, var2)

        replacer = pybamm.SymbolReplacer({var1: var3})
        replaced_symbol = replacer.process_symbol(conc)
        self.assertEqual(replaced_symbol.id,
                         pybamm.concatenation(var3, var2).id)
Example #5
0
    def _get_standard_whole_cell_exchange_current_variables(self, variables):

        i_typ = self.param.i_typ
        L_x = self.param.L_x
        j_n_scale = i_typ / (self.param.a_n_typ * L_x)
        j_p_scale = i_typ / (self.param.a_p_typ * L_x)

        j0_n = variables["Negative electrode" + self.reaction_name +
                         " exchange current density"]
        j0_s = pybamm.FullBroadcast(0, "separator", "current collector")
        j0_p = variables["Positive electrode" + self.reaction_name +
                         " exchange current density"]
        j0 = pybamm.concatenation(j0_n, j0_s, j0_p)
        j0_dim = pybamm.concatenation(j_n_scale * j0_n, j0_s, j_p_scale * j0_p)

        if self.reaction_name == "":
            variables = {
                "Exchange current density": j0,
                "Exchange current density [A.m-2]": j0_dim,
                "Exchange current density per volume [A.m-3]":
                i_typ / L_x * j0,
            }
        else:
            reaction_name = self.reaction_name[1:].capitalize()
            variables = {
                reaction_name + " exchange current density":
                j0,
                reaction_name + " exchange current density [A.m-2]":
                j0_dim,
                reaction_name + " exchange current density per volume [A.m-3]":
                i_typ / L_x * j0,
            }

        return variables
Example #6
0
    def set_rhs(self, variables):

        param = self.param

        eps_s = variables["Separator porosity"]
        eps_p = variables["Positive electrode porosity"]
        eps = pybamm.concatenation(eps_s, eps_p)

        deps_dt_s = variables["Separator porosity change"]
        deps_dt_p = variables["Positive electrode porosity change"]
        deps_dt = pybamm.concatenation(deps_dt_s, deps_dt_p)

        c_ox = variables["Separator and positive electrode oxygen concentration"]
        N_ox = variables["Oxygen flux"].orphans[1]

        j_ox = variables["Positive electrode oxygen interfacial current density"]
        source_terms = pybamm.concatenation(
            pybamm.FullBroadcast(0, "separator", "current collector"),
            param.s_ox_Ox * j_ox,
        )

        self.rhs = {
            c_ox: (1 / eps)
            * (-pybamm.div(N_ox) / param.C_e + source_terms - c_ox * deps_dt)
        }
Example #7
0
    def test_symbol_visualise(self):

        param = pybamm.LithiumIonParameters()

        zero_n = pybamm.FullBroadcast(0, ["negative electrode"],
                                      "current collector")
        zero_s = pybamm.FullBroadcast(0, ["separator"], "current collector")
        zero_p = pybamm.FullBroadcast(0, ["positive electrode"],
                                      "current collector")

        zero_nsp = pybamm.concatenation(zero_n, zero_s, zero_p)

        v_box = pybamm.Scalar(0)

        variables = {
            "Porosity":
            param.epsilon,
            "Negative electrode porosity":
            param.epsilon_n,
            "Separator porosity":
            param.epsilon_s,
            "Positive electrode porosity":
            param.epsilon_p,
            "Electrolyte tortuosity":
            param.epsilon**1.5,
            "Porosity change":
            zero_nsp,
            "Electrolyte current density":
            zero_nsp,
            "Volume-averaged velocity":
            v_box,
            "Interfacial current density":
            zero_nsp,
            "Oxygen interfacial current density":
            zero_nsp,
            "Cell temperature":
            pybamm.concatenation(zero_n, zero_s, zero_p),
            "Transverse volume-averaged acceleration":
            pybamm.concatenation(zero_n, zero_s, zero_p),
            "Sum of electrolyte reaction source terms":
            zero_nsp,
        }
        model = pybamm.electrolyte_diffusion.Full(param)
        variables.update(model.get_fundamental_variables())
        variables.update(model.get_coupled_variables(variables))

        model.set_rhs(variables)

        rhs = list(model.rhs.values())[0]
        rhs.visualise("StefanMaxwell_test.png")
        self.assertTrue(os.path.exists("StefanMaxwell_test.png"))
        with self.assertRaises(ValueError):
            rhs.visualise("StefanMaxwell_test")
    def test_concatenation_domains(self):
        a = pybamm.Symbol("a", domain=["negative electrode"])
        b = pybamm.Symbol("b", domain=["separator", "positive electrode"])
        c = pybamm.Symbol("c", domain=["test"])
        conc = pybamm.concatenation(a, b, c)
        self.assertEqual(
            conc.domain,
            ["negative electrode", "separator", "positive electrode", "test"],
        )

        # Can't concatenate nodes with overlapping domains
        d = pybamm.Symbol("d", domain=["separator"])
        with self.assertRaises(pybamm.DomainError):
            pybamm.concatenation(a, b, d)
Example #9
0
    def test_set_initial_condition_errors(self):
        model = pybamm.BaseModel()
        var = pybamm.Scalar(1)
        model.rhs = {var: -var}
        model.initial_conditions = {var: 1}
        with self.assertRaisesRegex(NotImplementedError,
                                    "Variable must have type"):
            model.set_initial_conditions_from({})

        var = pybamm.Variable(
            "var",
            domain="negative particle",
            auxiliary_domains={
                "secondary": "negative electrode",
                "tertiary": "current collector",
            },
        )
        model.rhs = {var: -var}
        model.initial_conditions = {var: 1}
        with self.assertRaisesRegex(NotImplementedError,
                                    "Variable must be 0D, 1D, or 2D"):
            model.set_initial_conditions_from({"var": np.ones((5, 6, 7, 8))})

        var_concat_neg = pybamm.Variable("var concat neg",
                                         domain="negative electrode")
        var_concat_sep = pybamm.Variable("var concat sep", domain="separator")
        var_concat = pybamm.concatenation(var_concat_neg, var_concat_sep)
        model.algebraic = {var_concat: -var_concat}
        model.initial_conditions = {var_concat: 1}
        with self.assertRaisesRegex(NotImplementedError,
                                    "Variable in concatenation must be 1D"):
            model.set_initial_conditions_from(
                {"var concat neg": np.ones((5, 6, 7))})

        # Inconsistent model and variable names
        model = pybamm.BaseModel()
        var = pybamm.Variable("var")
        model.rhs = {var: -var}
        model.initial_conditions = {var: pybamm.Scalar(1)}
        with self.assertRaisesRegex(pybamm.ModelError,
                                    "must appear in the solution"):
            model.set_initial_conditions_from({"wrong var": 2})
        var = pybamm.concatenation(pybamm.Variable("var", "test"),
                                   pybamm.Variable("var2", "test2"))
        model.rhs = {var: -var}
        model.initial_conditions = {var: pybamm.Scalar(1)}
        with self.assertRaisesRegex(pybamm.ModelError,
                                    "must appear in the solution"):
            model.set_initial_conditions_from({"wrong var": 2})
Example #10
0
    def test_jac_of_domain_concatenation(self):
        # create mesh
        disc = get_1p1d_discretisation_for_testing()
        mesh = disc.mesh
        y = pybamm.StateVector(slice(0, 1500))

        # Jacobian of a DomainConcatenation of constants is a zero matrix of the
        # appropriate size
        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        c_dom = ["positive electrode"]
        cc_npts = mesh["current collector"].npts
        curr_coll_vector = pybamm.Vector(np.ones(cc_npts),
                                         domain="current collector")
        a = 2 * pybamm.PrimaryBroadcast(curr_coll_vector, a_dom)
        b = pybamm.PrimaryBroadcast(curr_coll_vector, b_dom)
        c = 3 * pybamm.PrimaryBroadcast(curr_coll_vector, c_dom)
        # Add bounds for compatibility with the discretisation
        a.bounds = (-np.inf, np.inf)
        b.bounds = (-np.inf, np.inf)
        c.bounds = (-np.inf, np.inf)

        conc = pybamm.concatenation(a, b, c)
        conc.bounds = (-np.inf, np.inf)
        disc.set_variable_slices([conc])
        conc_disc = disc.process_symbol(conc)
        jac = conc_disc.jac(y).evaluate().toarray()
        np.testing.assert_array_equal(jac, np.zeros((1500, 1500)))

        # Jacobian of a DomainConcatenation of StateVectors
        a = pybamm.Variable(
            "a",
            domain=a_dom,
            auxiliary_domains={"secondary": "current collector"})
        b = pybamm.Variable(
            "b",
            domain=b_dom,
            auxiliary_domains={"secondary": "current collector"})
        c = pybamm.Variable(
            "c",
            domain=c_dom,
            auxiliary_domains={"secondary": "current collector"})
        conc = pybamm.concatenation(a, b, c)
        disc.set_variable_slices([conc])
        conc_disc = disc.process_symbol(conc)
        y0 = np.ones(1500)
        jac = conc_disc.jac(y).evaluate(y=y0).toarray()
        np.testing.assert_array_equal(jac, np.eye(1500))
Example #11
0
    def test_shape_and_size_for_testing(self):
        scal = pybamm.Scalar(1)
        self.assertEqual(scal.shape_for_testing, scal.shape)
        self.assertEqual(scal.size_for_testing, scal.size)

        state = pybamm.StateVector(slice(10, 25), domain="test")
        state2 = pybamm.StateVector(slice(10, 25), domain="test 2")
        self.assertEqual(state.shape_for_testing, state.shape)

        param = pybamm.Parameter("a")
        self.assertEqual(param.shape_for_testing, ())

        func = pybamm.FunctionParameter("func", {"state": state})
        self.assertEqual(func.shape_for_testing, state.shape_for_testing)

        concat = pybamm.concatenation(state, state2)
        self.assertEqual(concat.shape_for_testing, (30, 1))
        self.assertEqual(concat.size_for_testing, 30)

        var = pybamm.Variable("var", domain="negative electrode")
        broadcast = pybamm.PrimaryBroadcast(0, "negative electrode")
        self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing)
        self.assertEqual((var + broadcast).shape_for_testing,
                         broadcast.shape_for_testing)

        var = pybamm.Variable("var", domain=["random domain", "other domain"])
        broadcast = pybamm.PrimaryBroadcast(0,
                                            ["random domain", "other domain"])
        self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing)
        self.assertEqual((var + broadcast).shape_for_testing,
                         broadcast.shape_for_testing)

        sym = pybamm.Symbol("sym")
        with self.assertRaises(NotImplementedError):
            sym.shape_for_testing
Example #12
0
    def _get_standard_porosity_variables(self,
                                         eps_n,
                                         eps_s,
                                         eps_p,
                                         set_leading_order=False):

        eps_n_av = pybamm.x_average(eps_n)
        eps_s_av = pybamm.x_average(eps_s)
        eps_p_av = pybamm.x_average(eps_p)
        eps = pybamm.concatenation(eps_n, eps_s, eps_p)

        variables = {
            "Porosity": eps,
            "Negative electrode porosity": eps_n,
            "Separator porosity": eps_s,
            "Positive electrode porosity": eps_p,
            "X-averaged negative electrode porosity": eps_n_av,
            "X-averaged separator porosity": eps_s_av,
            "X-averaged positive electrode porosity": eps_p_av,
        }

        if set_leading_order is True:
            leading_order_variables = {
                "Leading-order " + name.lower(): var
                for name, var in variables.items()
            }
            variables.update(leading_order_variables)

        return variables
Example #13
0
    def _get_standard_tortuosity_variables(self,
                                           tor_n,
                                           tor_s,
                                           tor_p,
                                           set_leading_order=False):
        tor = pybamm.concatenation(tor_n, tor_s, tor_p)

        variables = {
            self.phase + " tortuosity":
            tor,
            "Negative " + self.phase.lower() + " tortuosity":
            tor_n,
            "Positive " + self.phase.lower() + " tortuosity":
            tor_p,
            "X-averaged negative " + self.phase.lower() + " tortuosity":
            pybamm.x_average(tor_n),
            "X-averaged positive " + self.phase.lower() + " tortuosity":
            pybamm.x_average(tor_p),
        }
        if self.phase == "Electrolyte":
            variables.update({
                "Separator tortuosity":
                tor_s,
                "X-averaged separator tortuosity":
                pybamm.x_average(tor_s),
            })

        if set_leading_order is True:
            leading_order_variables = {
                "Leading-order " + name.lower(): var
                for name, var in variables.items()
            }
            variables.update(leading_order_variables)

        return variables
    def test_concatenations(self):
        y = np.linspace(0, 1, 10)[:, np.newaxis]
        a = pybamm.Vector(y)
        b = pybamm.Scalar(16)
        c = pybamm.Scalar(3)
        conc = pybamm.NumpyConcatenation(a, b, c)
        self.assert_casadi_equal(conc.to_casadi(),
                                 casadi.MX(conc.evaluate()),
                                 evalf=True)

        # Domain concatenation
        mesh = get_mesh_for_testing()
        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom)
        b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom)
        conc = pybamm.DomainConcatenation([b, a], mesh)
        self.assert_casadi_equal(conc.to_casadi(),
                                 casadi.MX(conc.evaluate()),
                                 evalf=True)

        # 2d
        disc = get_1p1d_discretisation_for_testing()
        a = pybamm.Variable("a", domain=a_dom)
        b = pybamm.Variable("b", domain=b_dom)
        conc = pybamm.concatenation(a, b)
        disc.set_variable_slices([conc])
        expr = disc.process_symbol(conc)
        y = casadi.SX.sym("y", expr.size)
        x = expr.to_casadi(None, y)
        f = casadi.Function("f", [x], [x])
        y_eval = np.linspace(0, 1, expr.size)
        self.assert_casadi_equal(f(y_eval), casadi.SX(expr.evaluate(y=y_eval)))
Example #15
0
    def _get_standard_porosity_change_variables(self,
                                                deps_n_dt,
                                                deps_s_dt,
                                                deps_p_dt,
                                                set_leading_order=False):

        deps_n_dt_av = pybamm.x_average(deps_n_dt)
        deps_s_dt_av = pybamm.x_average(deps_s_dt)
        deps_p_dt_av = pybamm.x_average(deps_p_dt)
        deps_dt = pybamm.concatenation(deps_n_dt, deps_s_dt, deps_p_dt)

        variables = {
            "Porosity change": deps_dt,
            "Negative electrode porosity change": deps_n_dt,
            "Separator porosity change": deps_s_dt,
            "Positive electrode porosity change": deps_p_dt,
            "X-averaged negative electrode porosity change": deps_n_dt_av,
            "X-averaged separator porosity change": deps_s_dt_av,
            "X-averaged positive electrode porosity change": deps_p_dt_av,
        }

        if set_leading_order is True:
            variables.update({
                "Leading-order x-averaged " + "negative electrode porosity change":
                deps_n_dt_av,
                "Leading-order x-averaged separator porosity change":
                deps_s_dt_av,
                "Leading-order x-averaged " + "positive electrode porosity change":
                deps_p_dt_av,
            })

        return variables
Example #16
0
    def test_binary_simplifications_concatenations(self):
        def conc_broad(x, y, z):
            return pybamm.concatenation(
                pybamm.PrimaryBroadcast(x, "negative electrode"),
                pybamm.PrimaryBroadcast(y, "separator"),
                pybamm.PrimaryBroadcast(z, "positive electrode"),
            )

        # Test that concatenations get simplified correctly
        a = conc_broad(1, 2, 3)
        b = conc_broad(11, 12, 13)
        c = conc_broad(
            pybamm.InputParameter("x"),
            pybamm.InputParameter("y"),
            pybamm.InputParameter("z"),
        )
        self.assertEqual((a + 4).id, conc_broad(5, 6, 7).id)
        self.assertEqual((4 + a).id, conc_broad(5, 6, 7).id)
        self.assertEqual((a + b).id, conc_broad(12, 14, 16).id)
        self.assertIsInstance((a + c), pybamm.Concatenation)

        # No simplifications if are Variable or StateVector objects
        v = pybamm.concatenation(
            pybamm.Variable("x", "negative electrode"),
            pybamm.Variable("y", "separator"),
            pybamm.Variable("z", "positive electrode"),
        )
        self.assertIsInstance((v * v), pybamm.Multiplication)
        self.assertIsInstance((a * v), pybamm.Multiplication)
Example #17
0
    def test_domain_concatenation_2D(self):
        disc = get_1p1d_discretisation_for_testing()

        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        a = pybamm.Variable("a", domain=a_dom)
        b = pybamm.Variable("b", domain=b_dom)
        conc = pybamm.concatenation(2 * a, 3 * b)
        disc.set_variable_slices([a, b])
        expr = disc.process_symbol(conc)
        self.assertIsInstance(expr, pybamm.DomainConcatenation)

        y = np.empty((expr._size, 1))
        for i in range(len(y)):
            y[i] = i

        constant_symbols = OrderedDict()
        variable_symbols = OrderedDict()
        pybamm.find_symbols(expr, constant_symbols, variable_symbols)

        self.assertEqual(len(constant_symbols), 0)

        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate(y=y)
        np.testing.assert_allclose(result, expr.evaluate(y=y))

        # check that concatenating a single domain is consistent
        expr = disc.process_symbol(pybamm.Concatenation(a))
        evaluator = pybamm.EvaluatorPython(expr)
        result = evaluator.evaluate(y=y)
        np.testing.assert_allclose(result, expr.evaluate(y=y))
    def set_rhs(self, variables):
        """Composite reaction-diffusion with source terms from leading order."""

        param = self.param

        eps_0 = variables["Leading-order porosity"]
        deps_0_dt = variables["Leading-order porosity change"]
        c_e = variables["Electrolyte concentration"]
        N_e = variables["Electrolyte flux"]
        if self.extended is False:
            sum_s_j = variables[
                "Leading-order sum of electrolyte reaction source terms"
            ]
        elif self.extended == "distributed":
            sum_s_j = variables["Sum of electrolyte reaction source terms"]
        elif self.extended == "average":
            sum_s_j_n_av = variables[
                "Sum of x-averaged negative electrode electrolyte reaction source terms"
            ]
            sum_s_j_p_av = variables[
                "Sum of x-averaged positive electrode electrolyte reaction source terms"
            ]
            sum_s_j = pybamm.concatenation(
                pybamm.PrimaryBroadcast(sum_s_j_n_av, "negative electrode"),
                pybamm.FullBroadcast(0, "separator", "current collector"),
                pybamm.PrimaryBroadcast(sum_s_j_p_av, "positive electrode"),
            )
        source_terms = sum_s_j / self.param.gamma_e

        self.rhs = {
            c_e: (1 / eps_0)
            * (-pybamm.div(N_e) / param.C_e + source_terms - c_e * deps_0_dt)
        }
    def test_discretisation(self):
        param = pybamm.LithiumIonParameters()
        model_n = pybamm.interface.ButlerVolmer(
            param,
            "Negative",
            "lithium-ion main",
            {
                "SEI film resistance": "none",
                "total interfacial current density as a state": "false",
            },
        )
        j_n = model_n.get_coupled_variables(
            self.variables)["Negative electrode interfacial current density"]
        model_p = pybamm.interface.ButlerVolmer(
            param,
            "Positive",
            "lithium-ion main",
            {
                "SEI film resistance": "none",
                "total interfacial current density as a state": "false",
            },
        )
        j_p = model_p.get_coupled_variables(
            self.variables)["Positive electrode interfacial current density"]
        j = pybamm.concatenation(j_n,
                                 pybamm.PrimaryBroadcast(0,
                                                         ["separator"]), j_p)

        # Process parameters and discretise
        parameter_values = pybamm.lithium_ion.BaseModel(
        ).default_parameter_values
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        disc.set_variable_slices([
            self.c_e_n,
            self.c_e_p,
            self.delta_phi_s_n,
            self.delta_phi_s_p,
            self.c_s_n_surf,
            self.c_s_p_surf,
        ])

        j_n = disc.process_symbol(parameter_values.process_symbol(j_n))
        j_p = disc.process_symbol(parameter_values.process_symbol(j_p))
        j = disc.process_symbol(parameter_values.process_symbol(j))

        # test butler-volmer in each electrode
        submesh = np.concatenate([
            mesh["negative electrode"].nodes, mesh["positive electrode"].nodes
        ])
        y = np.concatenate([submesh**2, submesh**3, submesh**4])
        self.assertEqual(
            j_n.evaluate(None, y).shape, (mesh["negative electrode"].npts, 1))
        self.assertEqual(
            j_p.evaluate(None, y).shape, (mesh["positive electrode"].npts, 1))

        # test concatenated butler-volmer
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        whole_cell_mesh = disc.mesh.combine_submeshes(*whole_cell)
        self.assertEqual(j.evaluate(None, y).shape, (whole_cell_mesh.npts, 1))
    def _get_coupled_variables_from_potential(self, variables, phi_e_av):
        i_boundary_cc = variables["Current collector current density"]

        param = self.param
        l_n = param.l_n
        l_p = param.l_p
        x_n = pybamm.standard_spatial_vars.x_n
        x_p = pybamm.standard_spatial_vars.x_p

        phi_e_n = pybamm.PrimaryBroadcast(phi_e_av, ["negative electrode"])
        phi_e_s = pybamm.PrimaryBroadcast(phi_e_av, ["separator"])
        phi_e_p = pybamm.PrimaryBroadcast(phi_e_av, ["positive electrode"])

        i_e_n = i_boundary_cc * x_n / l_n
        i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc, ["separator"])
        i_e_p = i_boundary_cc * (1 - x_p) / l_p
        i_e = pybamm.concatenation(i_e_n, i_e_s, i_e_p)

        variables.update(
            self._get_standard_potential_variables(phi_e_n, phi_e_s, phi_e_p)
        )
        variables.update(self._get_standard_current_variables(i_e))

        # concentration overpotential
        eta_c_av = pybamm.PrimaryBroadcast(0, "current collector")
        # ohmic losses
        delta_phi_e_av = pybamm.PrimaryBroadcast(0, "current collector")
        variables.update(self._get_split_overpotential(eta_c_av, delta_phi_e_av))

        return variables
    def test_concatenation_orphans(self):
        a = pybamm.Variable("a", domain=["negative electrode"])
        b = pybamm.Variable("b", domain=["separator"])
        c = pybamm.Variable("c", domain=["positive electrode"])
        conc = pybamm.concatenation(a, b, c)
        a_new, b_new, c_new = conc.orphans

        # We should be able to manipulate the children without TreeErrors
        self.assertIsInstance(2 * a_new, pybamm.Multiplication)
        self.assertIsInstance(3 + b_new, pybamm.Addition)
        self.assertIsInstance(4 - c_new, pybamm.Subtraction)

        # ids should stay the same
        self.assertEqual(a.id, a_new.id)
        self.assertEqual(b.id, b_new.id)
        self.assertEqual(c.id, c_new.id)
        self.assertEqual(conc.id, pybamm.concatenation(a_new, b_new, c_new).id)
    def test_base_concatenation(self):
        a = pybamm.Symbol("a", domain="test a")
        b = pybamm.Symbol("b", domain="test b")
        c = pybamm.Symbol("c", domain="test c")
        conc = pybamm.concatenation(a, b, c)
        self.assertEqual(conc.name, "concatenation")
        self.assertEqual(str(conc), "concatenation(a, b, c)")
        self.assertIsInstance(conc.children[0], pybamm.Symbol)
        self.assertEqual(conc.children[0].name, "a")
        self.assertEqual(conc.children[1].name, "b")
        self.assertEqual(conc.children[2].name, "c")
        d = pybamm.Vector([2], domain="test a")
        e = pybamm.Vector([1], domain="test b")
        f = pybamm.Vector([3], domain="test c")
        conc2 = pybamm.concatenation(d, e, f)
        with self.assertRaises(TypeError):
            conc2.evaluate()

        # trying to concatenate non-pybamm symbols
        with self.assertRaises(TypeError):
            pybamm.concatenation(1, 2)

        # concatenation of length 0
        with self.assertRaisesRegex(ValueError,
                                    "Cannot create empty concatenation"):
            pybamm.concatenation()

        # concatenation of lenght 1
        self.assertEqual(pybamm.concatenation(a), a)
Example #23
0
    def _get_standard_concentration_variables(self, c_e_n, c_e_s, c_e_p):
        """
        A private function to obtain the standard variables which
        can be derived from the concentration in the electrolyte.

        Parameters
        ----------
        c_e_n : :class:`pybamm.Symbol`
            The electrolyte concentration in the negative electrode.
        c_e_s : :class:`pybamm.Symbol`
            The electrolyte concentration in the separator.
        c_e_p : :class:`pybamm.Symbol`
            The electrolyte concentration in the positive electrode.

        Returns
        -------
        variables : dict
            The variables which can be derived from the concentration in the
            electrolyte.
        """

        c_e_typ = self.param.c_e_typ

        c_e = pybamm.concatenation(c_e_n, c_e_s, c_e_p)
        c_e_av = pybamm.x_average(c_e)
        c_e_n_av = pybamm.x_average(c_e_n)
        c_e_s_av = pybamm.x_average(c_e_s)
        c_e_p_av = pybamm.x_average(c_e_p)

        variables = {
            "Electrolyte concentration": c_e,
            "Electrolyte concentration [mol.m-3]": c_e_typ * c_e,
            "Electrolyte concentration [Molar]": c_e_typ * c_e / 1000,
            "X-averaged electrolyte concentration": c_e_av,
            "X-averaged electrolyte concentration [mol.m-3]": c_e_typ * c_e_av,
            "X-averaged electrolyte concentration [Molar]": c_e_typ * c_e_av / 1000,
            "Negative electrolyte concentration": c_e_n,
            "Negative electrolyte concentration [mol.m-3]": c_e_typ * c_e_n,
            "Negative electrolyte concentration [Molar]": c_e_typ * c_e_n / 1000,
            "Separator electrolyte concentration": c_e_s,
            "Separator electrolyte concentration [mol.m-3]": c_e_typ * c_e_s,
            "Separator electrolyte concentration [Molar]": c_e_typ * c_e_s / 1000,
            "Positive electrolyte concentration": c_e_p,
            "Positive electrolyte concentration [mol.m-3]": c_e_typ * c_e_p,
            "Positive electrolyte concentration [Molar]": c_e_typ * c_e_p / 1000,
            "X-averaged negative electrolyte concentration": c_e_n_av,
            "X-averaged negative electrolyte concentration [mol.m-3]": c_e_typ
            * c_e_n_av,
            "X-averaged separator electrolyte concentration": c_e_s_av,
            "X-averaged separator electrolyte concentration [mol.m-3]": c_e_typ
            * c_e_s_av,
            "X-averaged positive electrolyte concentration": c_e_p_av,
            "X-averaged positive electrolyte concentration [mol.m-3]": c_e_typ
            * c_e_p_av,
        }

        return variables
    def test_public_functions(self):
        param = pybamm.LithiumIonParameters()
        a = pybamm.Scalar(1)
        surf = "electrode surface area to volume ratio"
        a_n = pybamm.FullBroadcast(a, "negative electrode",
                                   "current collector")
        a_s = pybamm.FullBroadcast(a, "separator", "current collector")
        a_p = pybamm.FullBroadcast(a, "positive electrode",
                                   "current collector")

        variables = {
            "Electrolyte tortuosity":
            a,
            "Electrolyte concentration":
            pybamm.concatenation(a_n, a_s, a_p),
            "Negative electrolyte concentration":
            a_n,
            "Separator electrolyte concentration":
            a_s,
            "Positive electrolyte concentration":
            a_p,
            "Negative electrode temperature":
            a_n,
            "Separator temperature":
            a_s,
            "Positive electrode temperature":
            a_p,
            "Negative " + surf:
            pybamm.FullBroadcast(a, "negative electrode", "current collector"),
            "Positive " + surf:
            pybamm.FullBroadcast(a, "positive electrode", "current collector"),
            "Sum of interfacial current densities":
            pybamm.FullBroadcast(
                a,
                ["negative electrode", "separator", "positive electrode"],
                "current collector",
            ),
            "Cell temperature":
            pybamm.concatenation(a_n, a_s, a_p),
        }
        submodel = pybamm.electrolyte_conductivity.Full(param)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
Example #25
0
    def _get_standard_porosity_times_concentration_variables(
            self, eps_c_e_n, eps_c_e_s, eps_c_e_p):
        eps_c_e = pybamm.concatenation(eps_c_e_n, eps_c_e_s, eps_c_e_p)

        variables = {
            "Porosity times concentration": eps_c_e,
            "Negative electrode porosity times concentration": eps_c_e_n,
            "Separator porosity times concentration": eps_c_e_s,
            "Positive electrode porosity times concentration": eps_c_e_p,
        }
        return variables
Example #26
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)
    def test_concatenation_auxiliary_domains(self):
        a = pybamm.Symbol(
            "a",
            domain=["negative electrode"],
            auxiliary_domains={"secondary": "current collector"},
        )
        b = pybamm.Symbol(
            "b",
            domain=["separator", "positive electrode"],
            auxiliary_domains={"secondary": "current collector"},
        )
        conc = pybamm.concatenation(a, b)
        self.assertEqual(conc.auxiliary_domains,
                         {"secondary": ["current collector"]})

        # Can't concatenate nodes with overlapping domains
        c = pybamm.Symbol("c",
                          domain=["test"],
                          auxiliary_domains={"secondary": "something else"})
        with self.assertRaisesRegex(
                pybamm.DomainError,
                "children must have same or empty auxiliary domains"):
            pybamm.concatenation(a, b, c)
Example #28
0
    def get_coupled_variables(self, variables):

        tor_s = variables["Separator tortuosity"]
        tor_p = variables["Positive electrode tortuosity"]
        tor = pybamm.concatenation(tor_s, tor_p)

        c_ox = variables["Separator and positive electrode oxygen concentration"]
        # TODO: allow charge and convection?
        v_box = pybamm.Scalar(0)

        param = self.param

        N_ox_diffusion = -tor * param.curlyD_ox * pybamm.grad(c_ox)

        N_ox = N_ox_diffusion + param.C_e * c_ox * v_box
        # Flux in the negative electrode is zero
        N_ox = pybamm.concatenation(
            pybamm.FullBroadcast(0, "negative electrode", "current collector"), N_ox
        )

        variables.update(self._get_standard_flux_variables(N_ox))

        return variables
Example #29
0
    def get_fundamental_variables(self):
        T_n = pybamm.standard_variables.T_n
        T_s = pybamm.standard_variables.T_s
        T_p = pybamm.standard_variables.T_p
        T_cn = pybamm.BoundaryValue(T_n, "left")
        T_cp = pybamm.BoundaryValue(T_p, "right")

        T = pybamm.concatenation(T_n, T_s, T_p)
        T_x_av = self._x_average(T, T_cn, T_cp)
        T_vol_av = self._yz_average(T_x_av)

        variables = self._get_standard_fundamental_variables(
            T_cn, T_n, T_s, T_p, T_cp, T_x_av, T_vol_av)
        return variables
Example #30
0
 def __neg__(self):
     """return a :class:`Negate` object."""
     if isinstance(self, pybamm.Negate):
         # Double negative is a positive
         return self.orphans[0]
     elif isinstance(self, pybamm.Broadcast):
         # Move negation inside the broadcast
         # Apply recursively
         return self._unary_new_copy(-self.orphans[0])
     elif isinstance(self, pybamm.Concatenation) and all(
             child.is_constant() for child in self.children):
         return pybamm.concatenation(*[-child for child in self.orphans])
     else:
         return pybamm.simplify_if_constant(pybamm.Negate(self))