示例#1
0
    def test_public_functions(self):
        param = pybamm.standard_parameters_lead_acid

        a = pybamm.Scalar(0)
        a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"])
        a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"])
        variables = {
            "Current collector current density": a,
            "Negative electrode potential": a_n,
            "Negative electrolyte potential": a_n,
            "Negative electrode open circuit potential": a_n,
            "Negative electrolyte concentration": a_n,
        }
        submodel = pybamm.interface.lead_acid.ButlerVolmer(param, "Negative")
        std_tests = tests.StandardSubModelTests(submodel, variables)

        std_tests.test_all()

        variables = {
            "Current collector current density": a,
            "Positive electrode potential": a_p,
            "Positive electrolyte potential": a_p,
            "Positive electrode open circuit potential": a_p,
            "Positive electrolyte concentration": a_p,
            "Negative electrode interfacial current density": a_n,
            "Negative electrode exchange current density": a_n,
        }
        submodel = pybamm.interface.lead_acid.ButlerVolmer(param, "Positive")
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
示例#2
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))
        self.assertEqual(state.shape_for_testing, state.shape)

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

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

        concat = pybamm.Concatenation()
        self.assertEqual(concat.shape_for_testing, (0, ))
        concat = pybamm.Concatenation(state, state)
        self.assertEqual(concat.shape_for_testing, (30, 1))
        self.assertEqual(concat.size_for_testing, 30)

        var = pybamm.Variable("var", domain="negative electrode")
        broadcast = pybamm.Broadcast(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.Broadcast(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
示例#3
0
    def test_public_functions(self):
        param = pybamm.standard_parameters_lithium_ion
        a = pybamm.Scalar(0)
        a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"])
        a_s = pybamm.Broadcast(pybamm.Scalar(0), ["separator"])
        a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"])
        variables = {
            "Current collector current density": a,
            "Negative electrode porosity": a_n,
            "Negative electrolyte concentration": a_n,
            "Negative electrode interfacial current density": a_n,
            "X-averaged negative electrode interfacial current density": a,
            "X-averaged negative electrode total interfacial current density":
            a,
        }
        icd = " interfacial current density"
        reactions = {
            "main": {
                "Negative": {
                    "s": 1,
                    "aj": "Negative electrode" + icd
                },
                "Positive": {
                    "s": 1,
                    "aj": "Positive electrode" + icd
                },
            }
        }
        spf = pybamm.electrolyte.stefan_maxwell.conductivity.surface_potential_form
        submodel = spf.LeadingOrderAlgebraic(param, "Negative", reactions)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
        submodel = spf.LeadingOrderDifferential(param, "Negative", reactions)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()

        variables = {
            "Current collector current density": a,
            "Negative electrolyte potential": a_n,
            "Negative electrolyte current density": a_n,
            "Separator electrolyte potential": a_s,
            "Separator electrolyte current density": a_s,
            "Positive electrode porosity": a_p,
            "Positive electrolyte concentration": a_p,
            "X-averaged positive electrode interfacial current density": a,
            "X-averaged positive electrode total interfacial current density":
            a,
        }
        submodel = spf.LeadingOrderAlgebraic(param, "Positive", reactions)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
        submodel = spf.LeadingOrderDifferential(param, "Positive", reactions)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
示例#4
0
 def test_public_functions(self):
     param = pybamm.standard_parameters_lead_acid
     a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"])
     a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"])
     variables = {
         "Negative electrode interfacial current density": a_n,
         "Positive electrode interfacial current density": a_p,
     }
     submodel = pybamm.porosity.Full(param)
     std_tests = tests.StandardSubModelTests(submodel, variables)
     std_tests.test_all()
示例#5
0
    def process_dict(self, var_eqn_dict):
        """Discretise a dictionary of {variable: equation}, broadcasting if necessary
        (can be model.rhs, model.algebraic, model.initial_conditions or
        model.variables).

        Parameters
        ----------
        var_eqn_dict : dict
            Equations ({variable: equation} dict) to dicretise
            (can be model.rhs, model.algebraic, model.initial_conditions or
            model.variables)

        Returns
        -------
        new_var_eqn_dict : dict
            Discretised equations

        """
        new_var_eqn_dict = {}
        for eqn_key, eqn in var_eqn_dict.items():
            # Broadcast if the equation evaluates to a number(e.g. Scalar)
            if eqn.evaluates_to_number() and not isinstance(eqn_key, str):
                eqn = pybamm.Broadcast(eqn, eqn_key.domain)

            # note we are sending in the key.id here so we don't have to
            # keep calling .id
            pybamm.logger.debug("Discretise {!r}".format(eqn_key))

            new_var_eqn_dict[eqn_key] = self.process_symbol(eqn)

        return new_var_eqn_dict
示例#6
0
    def test_broadcast(self):
        whole_cell = ["negative electrode", "separator", "positive electrode"]

        a = pybamm.Scalar(7)
        var = pybamm.Variable("var")

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        combined_submesh = mesh.combine_submeshes(*whole_cell)

        # scalar
        broad = disc._spatial_methods[whole_cell[0]].broadcast(
            a, whole_cell, {}, broadcast_type="full")
        np.testing.assert_array_equal(
            broad.evaluate(),
            7 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis]))
        self.assertEqual(broad.domain, whole_cell)

        broad_disc = disc.process_symbol(broad)
        self.assertIsInstance(broad_disc, pybamm.Multiplication)
        self.assertIsInstance(broad_disc.children[0], pybamm.Scalar)
        self.assertIsInstance(broad_disc.children[1], pybamm.Vector)

        # process Broadcast variable
        disc.y_slices = {var.id: [slice(1)]}
        broad1 = pybamm.Broadcast(var, ["negative electrode"])
        broad1_disc = disc.process_symbol(broad1)
        self.assertIsInstance(broad1_disc, pybamm.Multiplication)
        self.assertIsInstance(broad1_disc.children[0], pybamm.StateVector)
        self.assertIsInstance(broad1_disc.children[1], pybamm.Vector)
示例#7
0
def r_average(symbol):
    """convenience function for creating an average in the r-direction

    Parameters
    ----------
    symbol : :class:`pybamm.Symbol`
        The function to be averaged

    Returns
    -------
    :class:`Symbol`
        the new averaged symbol
    """
    # If symbol doesn't have a particle domain, its r-averaged value is itself
    if symbol.domain not in [["positive particle"], ["negative particle"]]:
        new_symbol = symbol.new_copy()
        new_symbol.parent = None
        return new_symbol
    # If symbol is a Broadcast, its average value is its child
    elif isinstance(symbol, pybamm.Broadcast):
        return symbol.orphans[0]
    else:
        r = pybamm.SpatialVariable("r", symbol.domain)
        v = pybamm.Broadcast(pybamm.Scalar(1), symbol.domain)
        return Integral(symbol, r) / Integral(v, r)
示例#8
0
    def test_public_functions(self):
        param = pybamm.standard_parameters_lead_acid

        a = pybamm.Scalar(0)
        variables = {
            "Current collector current density": a,
            "Negative electrode interfacial current density": pybamm.Broadcast(
                a, ["negative electrode"]
            ),
            "X-averaged negative electrode interfacial current density": a,
            "Positive electrode interfacial current density": pybamm.Broadcast(
                a, ["positive electrode"]
            ),
            "X-averaged positive electrode interfacial current density": a,
        }
        submodel = pybamm.convection.Composite(param)
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
示例#9
0
    def test_concatenation_of_scalars(self):
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        a = pybamm.Broadcast(5, ["negative electrode"])
        b = pybamm.Broadcast(4, ["positive electrode"])

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        variables = [pybamm.Variable("var", domain=whole_cell)]
        disc.set_variable_slices(variables)

        eqn = pybamm.Concatenation(a, b)
        eqn_disc = disc.process_symbol(eqn)
        expected_vector = np.concatenate([
            5 * np.ones_like(mesh["negative electrode"][0].nodes),
            4 * np.ones_like(mesh["positive electrode"][0].nodes),
        ])[:, np.newaxis]
        np.testing.assert_allclose(eqn_disc.evaluate(), expected_vector)
示例#10
0
    def test_add_internal_boundary_conditions(self):
        model = pybamm.BaseModel()
        c_e_n = pybamm.Broadcast(0, ["negative electrode"])
        c_e_s = pybamm.Broadcast(0, ["separator"])
        c_e_p = pybamm.Broadcast(0, ["positive electrode"])
        c_e = pybamm.Concatenation(c_e_n, c_e_s, c_e_p)
        lbc = (pybamm.Scalar(0), "Neumann")
        rbc = (pybamm.Scalar(0), "Neumann")
        model.boundary_conditions = {c_e: {"left": lbc, "right": rbc}}

        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": SpatialMethodForTesting}

        disc = pybamm.Discretisation(mesh, spatial_methods)
        disc.bcs = disc.process_boundary_conditions(model)
        disc.set_internal_boundary_conditions(model)

        for child in c_e.children:
            self.assertTrue(child.id in disc.bcs.keys())
示例#11
0
 def test_public_functions(self):
     param = pybamm.standard_parameters_lead_acid
     a = pybamm.Broadcast(pybamm.Scalar(0), "test")
     variables = {
         "X-averaged negative electrode interfacial current density": a,
         "X-averaged positive electrode interfacial current density": a,
     }
     submodel = pybamm.porosity.LeadingOrder(param)
     std_tests = tests.StandardSubModelTests(submodel, variables)
     std_tests.test_all()
示例#12
0
    def test_yz_average(self):
        a = pybamm.Scalar(1)
        z_average_a = pybamm.z_average(a)
        yz_average_a = pybamm.yz_average(a)
        self.assertEqual(z_average_a.id, a.id)
        self.assertEqual(yz_average_a.id, a.id)

        z_average_broad_a = pybamm.z_average(
            pybamm.Broadcast(a, ["current collector"]))
        yz_average_broad_a = pybamm.yz_average(
            pybamm.Broadcast(a, ["current collector"]))
        self.assertEqual(z_average_broad_a.evaluate(), np.array([1]))
        self.assertEqual(yz_average_broad_a.evaluate(), np.array([1]))

        a = pybamm.Symbol("a", domain=["current collector"])
        y = pybamm.SpatialVariable("y", ["current collector"])
        z = pybamm.SpatialVariable("z", ["current collector"])
        z_av_a = pybamm.z_average(a)
        yz_av_a = pybamm.yz_average(a)

        self.assertIsInstance(z_av_a, pybamm.Division)
        self.assertIsInstance(yz_av_a, pybamm.Division)
        self.assertIsInstance(z_av_a.children[0], pybamm.Integral)
        self.assertIsInstance(yz_av_a.children[0], pybamm.Integral)
        self.assertEqual(z_av_a.children[0].integration_variable[0].domain,
                         z.domain)
        self.assertEqual(yz_av_a.children[0].integration_variable[0].domain,
                         y.domain)
        self.assertEqual(yz_av_a.children[0].integration_variable[1].domain,
                         z.domain)
        self.assertEqual(z_av_a.domain, [])
        self.assertEqual(yz_av_a.domain, [])

        a = pybamm.Symbol("a", domain="bad domain")
        with self.assertRaises(pybamm.DomainError):
            pybamm.z_average(a)
        with self.assertRaises(pybamm.DomainError):
            pybamm.yz_average(a)
示例#13
0
    def test_average(self):
        a = pybamm.Scalar(1)
        average_a = pybamm.x_average(a)
        self.assertEqual(average_a.id, a.id)

        average_broad_a = pybamm.x_average(
            pybamm.Broadcast(a, ["negative electrode"]))
        self.assertEqual(average_broad_a.evaluate(), np.array([1]))

        conc_broad = pybamm.Concatenation(
            pybamm.Broadcast(1, ["negative electrode"]),
            pybamm.Broadcast(2, ["separator"]),
            pybamm.Broadcast(3, ["positive electrode"]),
        )
        average_conc_broad = pybamm.x_average(conc_broad)
        self.assertIsInstance(average_conc_broad, pybamm.Division)

        for domain in [
            ["negative electrode"],
            ["separator"],
            ["positive electrode"],
            ["negative electrode", "separator", "positive electrode"],
        ]:
            a = pybamm.Symbol("a", domain=domain)
            x = pybamm.SpatialVariable("x", domain)
            av_a = pybamm.x_average(a)
            self.assertIsInstance(av_a, pybamm.Division)
            self.assertIsInstance(av_a.children[0], pybamm.Integral)
            self.assertEqual(av_a.children[0].integration_variable[0].domain,
                             x.domain)
            # electrode domains go to current collector when averaged
            self.assertEqual(av_a.domain, [])

        a = pybamm.Symbol("a", domain="bad domain")
        with self.assertRaises(pybamm.DomainError):
            pybamm.x_average(a)
示例#14
0
    def test_public_functions(self):
        param = pybamm.standard_parameters_lead_acid

        a = pybamm.Scalar(0)
        variables = {
            "Current collector current density": a,
            "Negative electrolyte current density": pybamm.Broadcast(
                a, ["negative electrode"]
            ),
            "Positive electrolyte current density": pybamm.Broadcast(
                a, ["positive electrode"]
            ),
            "Negative electrode porosity": a,
            "Positive electrode porosity": a,
            "Separator electrolyte potential": a,
            "Positive electrode surface potential difference": a,
        }
        submodel = pybamm.electrode.ohm.SurfaceForm(param, "Negative")
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()

        submodel = pybamm.electrode.ohm.SurfaceForm(param, "Positive")
        std_tests = tests.StandardSubModelTests(submodel, variables)
        std_tests.test_all()
示例#15
0
    def test_broadcast_2D(self):
        # broadcast in 2D --> Outer symbol
        var = pybamm.Variable("var", ["current collector"])
        disc = get_1p1d_discretisation_for_testing()
        mesh = disc.mesh
        broad = pybamm.Broadcast(var, "separator", broadcast_type="primary")

        disc.set_variable_slices([var])
        broad_disc = disc.process_symbol(broad)
        self.assertIsInstance(broad_disc, pybamm.Outer)
        self.assertIsInstance(broad_disc.children[0], pybamm.StateVector)
        self.assertIsInstance(broad_disc.children[1], pybamm.Vector)
        self.assertEqual(
            broad_disc.shape,
            (mesh["separator"][0].npts * mesh["current collector"][0].npts, 1),
        )
示例#16
0
    def test_discretisation(self):
        param = pybamm.standard_parameters_lithium_ion
        model_n = pybamm.interface.lithium_ion.ButlerVolmer(param, "Negative")
        j_n = model_n.get_coupled_variables(
            self.variables)["Negative electrode interfacial current density"]
        model_p = pybamm.interface.lithium_ion.ButlerVolmer(param, "Positive")
        j_p = model_p.get_coupled_variables(
            self.variables)["Positive electrode interfacial current density"]
        j = pybamm.Concatenation(j_n, pybamm.Broadcast(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"][0].nodes,
            mesh["positive electrode"][0].nodes
        ])
        y = np.concatenate([submesh**2, submesh**3, submesh**4])
        self.assertEqual(
            j_n.evaluate(None, y).shape,
            (mesh["negative electrode"][0].npts, 1))
        self.assertEqual(
            j_p.evaluate(None, y).shape,
            (mesh["positive electrode"][0].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[0].npts, 1))
示例#17
0
    def test_boundary_value(self):
        a = pybamm.Scalar(1)
        boundary_a = pybamm.boundary_value(a, "right")
        self.assertEqual(boundary_a.id, a.id)

        boundary_broad_a = pybamm.boundary_value(
            pybamm.Broadcast(a, ["negative electrode"]), "left")
        self.assertEqual(boundary_broad_a.evaluate(), np.array([1]))

        a = pybamm.Symbol("a", domain=["separator"])
        boundary_a = pybamm.boundary_value(a, "right")
        self.assertIsInstance(boundary_a, pybamm.BoundaryValue)
        self.assertEqual(boundary_a.side, "right")
        self.assertEqual(boundary_a.domain, [])
        self.assertEqual(boundary_a.auxiliary_domains, {})
        # test with secondary domain
        a_sec = pybamm.Symbol(
            "a",
            domain=["separator"],
            auxiliary_domains={"secondary": "current collector"},
        )
        boundary_a_sec = pybamm.boundary_value(a_sec, "right")
        self.assertEqual(boundary_a_sec.domain, ["current collector"])
        self.assertEqual(boundary_a_sec.auxiliary_domains, {})
        # test with secondary domain and tertiary domain
        a_tert = pybamm.Symbol(
            "a",
            domain=["separator"],
            auxiliary_domains={
                "secondary": "current collector",
                "tertiary": "bla"
            },
        )
        boundary_a_tert = pybamm.boundary_value(a_tert, "right")
        self.assertEqual(boundary_a_tert.domain, ["current collector"])
        self.assertEqual(boundary_a_tert.auxiliary_domains,
                         {"secondary": ["bla"]})

        # error if boundary value on tabs and domain is not "current collector"
        var = pybamm.Variable("var", domain=["negative electrode"])
        with self.assertRaisesRegex(pybamm.ModelError,
                                    "Can only take boundary"):
            pybamm.boundary_value(var, "negative tab")
            pybamm.boundary_value(var, "positive tab")
示例#18
0
def source(left, right, boundary=False):
    """A convinience function for creating (part of) an expression tree representing
    a source term. This is necessary for spatial methods where the mass matrix
    is not the identity (e.g. finite element formulation with piecwise linear
    basis functions). The left child is the symbol representing the source term
    and the right child is the symbol of the equation variable (currently, the
    finite element formulation in PyBaMM assumes all functions are constructed
    using the same basis, and the matrix here is constructed accoutning for the
    boundary conditions of the right child). The method returns the matrix-vector
    product of the mass matrix (adjusted to account for any Dirichlet boundary
    conditions imposed the the right symbol) and the discretised left symbol.

    Parameters
    ----------

    left : :class:`Symbol`
        The left child node, which represents the expression for the source term.
    right : :class:`Symbol`
        The right child node. This is the symbol whose boundary conditions are
        accounted for in the construction of the mass matrix.
    boundary : bool, optional
        If True, then the mass matrix should is assembled over the boundary,
        corresponding to a source term which only acts on the boundary of the
        domain. If False (default), the matrix is assembled over the entire domain,
        corresponding to a source term in the bulk.

    """
    # Broadcast if left is number
    if isinstance(left, numbers.Number):
        left = pybamm.Broadcast(left, "current collector")

    if left.domain != ["current collector"
                       ] or right.domain != ["current collector"]:
        raise pybamm.DomainError(
            """'source' only implemented in the 'current collector' domain,
            but symbols have domains {} and {}""".format(
                left.domain, right.domain))
    if boundary:
        return pybamm.BoundaryMass(right) @ left
    else:
        return pybamm.Mass(right) @ left
示例#19
0
    def test_exceptions(self):
        c_n = pybamm.Variable("c", domain=["negative electrode"])
        N_n = pybamm.grad(c_n)
        c_s = pybamm.Variable("c", domain=["separator"])
        N_s = pybamm.grad(c_s)
        model = pybamm.BaseModel()
        model.rhs = {c_n: pybamm.div(N_n), c_s: pybamm.div(N_s)}
        model.initial_conditions = {
            c_n: pybamm.Scalar(3),
            c_s: pybamm.Scalar(1)
        }
        model.boundary_conditions = {
            c_n: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
            c_s: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
        }

        disc = get_discretisation_for_testing()

        # check raises error if different sized key and output var
        model.variables = {c_n.name: c_s}
        with self.assertRaisesRegex(pybamm.ModelError, "variable and its eqn"):
            disc.process_model(model)

        # check doesn't raise if concatenation
        model.variables = {c_n.name: pybamm.Concatenation(c_n, c_s)}
        disc.process_model(model)

        # check doesn't raise if broadcast
        model.variables = {
            c_n.name: pybamm.Broadcast(pybamm.Scalar(2),
                                       ["negative electrode"])
        }
        disc.process_model(model)
示例#20
0
    def test_r_average(self):
        a = pybamm.Scalar(1)
        average_a = pybamm.r_average(a)
        self.assertEqual(average_a.id, a.id)

        average_broad_a = pybamm.r_average(
            pybamm.Broadcast(a, ["negative particle"]))
        self.assertEqual(average_broad_a.evaluate(), np.array([1]))

        for domain in [["negative particle"], ["positive particle"]]:
            a = pybamm.Symbol("a", domain=domain)
            r = pybamm.SpatialVariable("r", domain)
            av_a = pybamm.r_average(a)
            self.assertIsInstance(av_a, pybamm.Division)
            self.assertIsInstance(av_a.children[0], pybamm.Integral)
            self.assertEqual(av_a.children[0].integration_variable[0].domain,
                             r.domain)
            # electrode domains go to current collector when averaged
            self.assertEqual(av_a.domain, [])

        a = pybamm.Symbol("a", domain="bad domain")
        with self.assertRaises(pybamm.DomainError):
            pybamm.x_average(a)
示例#21
0
    def test_discretise_equations(self):
        # get mesh
        mesh = get_2p1d_mesh_for_testing()
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume,
            "current collector": pybamm.ScikitFiniteElement,
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)
        # discretise some equations
        var = pybamm.Variable("var", domain="current collector")
        y = pybamm.SpatialVariable("y", ["current collector"])
        z = pybamm.SpatialVariable("z", ["current collector"])
        disc.set_variable_slices([var])
        y_test = np.ones(mesh["current collector"][0].npts)
        unit_source = pybamm.Broadcast(1, "current collector")
        disc.bcs = {
            var.id: {
                "negative tab": (pybamm.Scalar(0), "Neumann"),
                "positive tab": (pybamm.Scalar(0), "Neumann"),
            }
        }

        for eqn in [
                pybamm.laplacian(var),
                pybamm.source(unit_source, var),
                pybamm.laplacian(var) - pybamm.source(unit_source, var),
                pybamm.source(var, var),
                pybamm.laplacian(var) - pybamm.source(2 * var, var),
                pybamm.laplacian(var) -
                pybamm.source(unit_source**2 + 1 / var, var),
                pybamm.Integral(var, [y, z]) - 1,
                pybamm.source(var, var, boundary=True),
                pybamm.laplacian(var) -
                pybamm.source(unit_source, var, boundary=True),
                pybamm.laplacian(var) -
                pybamm.source(unit_source**2 + 1 / var, var, boundary=True),
                pybamm.grad_squared(var),
        ]:
            # Check that equation can be evaluated in each case
            # Dirichlet
            disc.bcs = {
                var.id: {
                    "negative tab": (pybamm.Scalar(0), "Dirichlet"),
                    "positive tab": (pybamm.Scalar(1), "Dirichlet"),
                }
            }
            eqn_disc = disc.process_symbol(eqn)
            eqn_disc.evaluate(None, y_test)
            # Neumann
            disc.bcs = {
                var.id: {
                    "negative tab": (pybamm.Scalar(0), "Neumann"),
                    "positive tab": (pybamm.Scalar(1), "Neumann"),
                }
            }
            eqn_disc = disc.process_symbol(eqn)
            eqn_disc.evaluate(None, y_test)
            # One of each
            disc.bcs = {
                var.id: {
                    "negative tab": (pybamm.Scalar(0), "Neumann"),
                    "positive tab": (pybamm.Scalar(1), "Dirichlet"),
                }
            }
            eqn_disc = disc.process_symbol(eqn)
            eqn_disc.evaluate(None, y_test)
            # One of each
            disc.bcs = {
                var.id: {
                    "negative tab": (pybamm.Scalar(0), "Dirichlet"),
                    "positive tab": (pybamm.Scalar(1), "Neumann"),
                }
            }
            eqn_disc = disc.process_symbol(eqn)
            eqn_disc.evaluate(None, y_test)

        # check  ValueError raised for non Dirichlet or Neumann BCs
        eqn = pybamm.laplacian(var) - pybamm.source(unit_source, var)
        disc.bcs = {
            var.id: {
                "negative tab": (pybamm.Scalar(0), "Dirichlet"),
                "positive tab": (pybamm.Scalar(1), "Other BC"),
            }
        }
        with self.assertRaises(ValueError):
            eqn_disc = disc.process_symbol(eqn)
        disc.bcs = {
            var.id: {
                "negative tab": (pybamm.Scalar(0), "Other BC"),
                "positive tab": (pybamm.Scalar(1), "Neumann"),
            }
        }
        with self.assertRaises(ValueError):
            eqn_disc = disc.process_symbol(eqn)

        # raise ModelError if no BCs provided
        new_var = pybamm.Variable("new_var", domain="current collector")
        disc.set_variable_slices([new_var])
        eqn = pybamm.laplacian(new_var)
        with self.assertRaises(pybamm.ModelError):
            eqn_disc = disc.process_symbol(eqn)

        # check GeometryError if using scikit-fem not in y or z
        x = pybamm.SpatialVariable("x", ["current collector"])
        with self.assertRaises(pybamm.GeometryError):
            disc.process_symbol(x)
示例#22
0
 def test_broadcast_number(self):
     broad_a = pybamm.Broadcast(1, ["negative electrode"])
     self.assertEqual(broad_a.name, "broadcast")
     self.assertIsInstance(broad_a.children[0], pybamm.Symbol)
     self.assertEqual(broad_a.children[0].evaluate(), np.array([1]))
     self.assertEqual(broad_a.domain, ["negative electrode"])
示例#23
0
 def test_broadcast_type(self):
     a = pybamm.Symbol("a", domain="current collector")
     with self.assertRaisesRegex(ValueError, "Variables on the current collector"):
         pybamm.Broadcast(a, "electrode")
示例#24
0
    def test_process_symbol(self):
        parameter_values = pybamm.ParameterValues({"a": 1, "b": 2, "c": 3})
        # process parameter
        a = pybamm.Parameter("a")
        processed_a = parameter_values.process_symbol(a)
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 1)

        # process binary operation
        b = pybamm.Parameter("b")
        add = a + b
        processed_add = parameter_values.process_symbol(add)
        self.assertIsInstance(processed_add, pybamm.Addition)
        self.assertIsInstance(processed_add.children[0], pybamm.Scalar)
        self.assertIsInstance(processed_add.children[1], pybamm.Scalar)
        self.assertEqual(processed_add.children[0].value, 1)
        self.assertEqual(processed_add.children[1].value, 2)

        scal = pybamm.Scalar(34)
        mul = a * scal
        processed_mul = parameter_values.process_symbol(mul)
        self.assertIsInstance(processed_mul, pybamm.Multiplication)
        self.assertIsInstance(processed_mul.children[0], pybamm.Scalar)
        self.assertIsInstance(processed_mul.children[1], pybamm.Scalar)
        self.assertEqual(processed_mul.children[0].value, 1)
        self.assertEqual(processed_mul.children[1].value, 34)

        # process integral
        aa = pybamm.Parameter("a", domain=["negative electrode"])
        x = pybamm.SpatialVariable("x", domain=["negative electrode"])
        integ = pybamm.Integral(aa, x)
        processed_integ = parameter_values.process_symbol(integ)
        self.assertIsInstance(processed_integ, pybamm.Integral)
        self.assertIsInstance(processed_integ.children[0], pybamm.Scalar)
        self.assertEqual(processed_integ.children[0].value, 1)
        self.assertEqual(processed_integ.integration_variable[0].id, x.id)

        # process unary operation
        grad = pybamm.Gradient(a)
        processed_grad = parameter_values.process_symbol(grad)
        self.assertIsInstance(processed_grad, pybamm.Gradient)
        self.assertIsInstance(processed_grad.children[0], pybamm.Scalar)
        self.assertEqual(processed_grad.children[0].value, 1)

        # process delta function
        aa = pybamm.Parameter("a")
        delta_aa = pybamm.DeltaFunction(aa, "left", "some domain")
        processed_delta_aa = parameter_values.process_symbol(delta_aa)
        self.assertIsInstance(processed_delta_aa, pybamm.DeltaFunction)
        self.assertEqual(processed_delta_aa.side, "left")
        processed_a = processed_delta_aa.children[0]
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 1)

        # process boundary operator (test for BoundaryValue)
        aa = pybamm.Parameter("a", domain=["negative electrode"])
        x = pybamm.SpatialVariable("x", domain=["negative electrode"])
        boundary_op = pybamm.BoundaryValue(aa * x, "left")
        processed_boundary_op = parameter_values.process_symbol(boundary_op)
        self.assertIsInstance(processed_boundary_op, pybamm.BoundaryOperator)
        processed_a = processed_boundary_op.children[0].children[0]
        processed_x = processed_boundary_op.children[0].children[1]
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 1)
        self.assertEqual(processed_x.id, x.id)

        # process broadcast
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        broad = pybamm.Broadcast(a, whole_cell)
        processed_broad = parameter_values.process_symbol(broad)
        self.assertIsInstance(processed_broad, pybamm.Broadcast)
        self.assertEqual(processed_broad.domain, whole_cell)
        self.assertIsInstance(processed_broad.children[0], pybamm.Scalar)
        self.assertEqual(processed_broad.children[0].evaluate(), np.array([1]))

        # process concatenation
        conc = pybamm.Concatenation(pybamm.Vector(np.ones(10)),
                                    pybamm.Vector(2 * np.ones(15)))
        processed_conc = parameter_values.process_symbol(conc)
        self.assertIsInstance(processed_conc.children[0], pybamm.Vector)
        self.assertIsInstance(processed_conc.children[1], pybamm.Vector)
        np.testing.assert_array_equal(processed_conc.children[0].entries, 1)
        np.testing.assert_array_equal(processed_conc.children[1].entries, 2)

        # process domain concatenation
        c_e_n = pybamm.Variable("c_e_n", ["negative electrode"])
        c_e_s = pybamm.Variable("c_e_p", ["separator"])
        test_mesh = shared.get_mesh_for_testing()
        dom_con = pybamm.DomainConcatenation([a * c_e_n, b * c_e_s], test_mesh)
        processed_dom_con = parameter_values.process_symbol(dom_con)
        a_proc = processed_dom_con.children[0].children[0]
        b_proc = processed_dom_con.children[1].children[0]
        self.assertIsInstance(a_proc, pybamm.Scalar)
        self.assertIsInstance(b_proc, pybamm.Scalar)
        self.assertEqual(a_proc.value, 1)
        self.assertEqual(b_proc.value, 2)

        # process variable
        c = pybamm.Variable("c")
        processed_c = parameter_values.process_symbol(c)
        self.assertIsInstance(processed_c, pybamm.Variable)
        self.assertEqual(processed_c.name, "c")

        # process scalar
        d = pybamm.Scalar(14)
        processed_d = parameter_values.process_symbol(d)
        self.assertIsInstance(processed_d, pybamm.Scalar)
        self.assertEqual(processed_d.value, 14)

        # process array types
        e = pybamm.Vector(np.ones(4))
        processed_e = parameter_values.process_symbol(e)
        self.assertIsInstance(processed_e, pybamm.Vector)
        np.testing.assert_array_equal(processed_e.evaluate(), np.ones((4, 1)))

        f = pybamm.Matrix(np.ones((5, 6)))
        processed_f = parameter_values.process_symbol(f)
        self.assertIsInstance(processed_f, pybamm.Matrix)
        np.testing.assert_array_equal(processed_f.evaluate(), np.ones((5, 6)))

        # process statevector
        g = pybamm.StateVector(slice(0, 10))
        processed_g = parameter_values.process_symbol(g)
        self.assertIsInstance(processed_g, pybamm.StateVector)
        np.testing.assert_array_equal(processed_g.evaluate(y=np.ones(10)),
                                      np.ones((10, 1)))

        # process outer
        c = pybamm.Parameter("c", domain="current collector")
        outer = pybamm.Outer(c, b)
        processed_outer = parameter_values.process_symbol(outer)
        self.assertIsInstance(processed_outer, pybamm.Outer)

        # not implemented
        sym = pybamm.Symbol("sym")
        with self.assertRaises(NotImplementedError):
            parameter_values.process_symbol(sym)
示例#25
0
 def test_broadcast(self):
     a = pybamm.Symbol("a")
     broad_a = pybamm.Broadcast(a, ["negative electrode"])
     self.assertEqual(broad_a.name, "broadcast")
     self.assertEqual(broad_a.children[0].name, a.name)
     self.assertEqual(broad_a.domain, ["negative electrode"])
示例#26
0
    def test_simple_ode_model(self):
        model = pybamm.BaseBatteryModel(name="Simple ODE Model")

        whole_cell = ["negative electrode", "separator", "positive electrode"]
        # Create variables: domain is explicitly empty since these variables are only
        # functions of time
        a = pybamm.Variable("a", domain=[])
        b = pybamm.Variable("b", domain=[])
        c = pybamm.Variable("c", domain=[])

        # Simple ODEs
        model.rhs = {a: pybamm.Scalar(2), b: pybamm.Scalar(0), c: -c}

        # Simple initial conditions
        model.initial_conditions = {
            a: pybamm.Scalar(0),
            b: pybamm.Scalar(1),
            c: pybamm.Scalar(1),
        }
        # no boundary conditions for an ODE model
        # Broadcast some of the variables
        model.variables = {
            "a": a,
            "b broadcasted": pybamm.FullBroadcast(b, whole_cell, "current collector"),
            "c broadcasted": pybamm.FullBroadcast(
                c, ["negative electrode", "separator"], "current collector"
            ),
        }

        # ODEs only (don't use jacobian)
        model.use_jacobian = False

        # Process and solve
        geometry = model.default_geometry
        param = model.default_parameter_values
        param.process_model(model)
        param.process_geometry(geometry)
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)
        solver = model.default_solver
        t_eval = np.linspace(0, 2, 100)
        solution = solver.solve(model, t_eval)
        quick_plot = pybamm.QuickPlot(model, mesh, solution)
        quick_plot.plot(0)

        # update the axis
        new_axis = [0, 0.5, 0, 1]
        quick_plot.axis.update({("a",): new_axis})
        self.assertEqual(quick_plot.axis[("a",)], new_axis)

        # and now reset them
        quick_plot.reset_axis()
        self.assertNotEqual(quick_plot.axis[("a",)], new_axis)

        # check dynamic plot loads
        quick_plot.dynamic_plot(testing=True)

        quick_plot.update(0.01)

        # Test with different output variables
        quick_plot = pybamm.QuickPlot(model, mesh, solution, ["b broadcasted"])
        self.assertEqual(len(quick_plot.axis), 1)
        quick_plot.plot(0)

        quick_plot = pybamm.QuickPlot(
            model,
            mesh,
            solution,
            [["a", "a"], ["b broadcasted", "b broadcasted"], "c broadcasted"],
        )
        self.assertEqual(len(quick_plot.axis), 3)
        quick_plot.plot(0)

        # update the axis
        new_axis = [0, 0.5, 0, 1]
        var_key = ("c broadcasted",)
        quick_plot.axis.update({var_key: new_axis})
        self.assertEqual(quick_plot.axis[var_key], new_axis)

        # and now reset them
        quick_plot.reset_axis()
        self.assertNotEqual(quick_plot.axis[var_key], new_axis)

        # check dynamic plot loads
        quick_plot.dynamic_plot(testing=True)

        quick_plot.update(0.01)

        # Test longer name
        model.variables["Variable with a very long name"] = model.variables["a"]
        quick_plot = pybamm.QuickPlot(model, mesh, solution)
        quick_plot.plot(0)

        # Test errors
        with self.assertRaisesRegex(ValueError, "mismatching variable domains"):
            pybamm.QuickPlot(model, mesh, solution, [["a", "b broadcasted"]])
        model.variables["3D variable"] = disc.process_symbol(
            pybamm.Broadcast(1, ["negative particle"])
        )
        with self.assertRaisesRegex(NotImplementedError, "cannot plot 3D variables"):
            pybamm.QuickPlot(model, mesh, solution, ["3D variable"])
示例#27
0
    def test_broadcast_and_concatenate(self):
        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh

        # Piecewise constant scalars
        a = pybamm.Broadcast(1, ["negative electrode"])
        b = pybamm.Broadcast(2, ["separator"])
        c = pybamm.Broadcast(3, ["positive electrode"])
        conc = pybamm.Concatenation(a, b, c)

        self.assertEqual(
            conc.domain,
            ["negative electrode", "separator", "positive electrode"])
        self.assertEqual(conc.children[0].domain, ["negative electrode"])
        self.assertEqual(conc.children[1].domain, ["separator"])
        self.assertEqual(conc.children[2].domain, ["positive electrode"])
        processed_conc = disc.process_symbol(conc)
        np.testing.assert_array_equal(
            processed_conc.evaluate(),
            np.concatenate([
                np.ones(mesh["negative electrode"][0].npts),
                2 * np.ones(mesh["separator"][0].npts),
                3 * np.ones(mesh["positive electrode"][0].npts),
            ])[:, np.newaxis],
        )

        # Piecewise constant functions of time
        a_t = pybamm.Broadcast(pybamm.t, ["negative electrode"])
        b_t = pybamm.Broadcast(2 * pybamm.t, ["separator"])
        c_t = pybamm.Broadcast(3 * pybamm.t, ["positive electrode"])
        conc = pybamm.Concatenation(a_t, b_t, c_t)

        self.assertEqual(
            conc.domain,
            ["negative electrode", "separator", "positive electrode"])
        self.assertEqual(conc.children[0].domain, ["negative electrode"])
        self.assertEqual(conc.children[1].domain, ["separator"])
        self.assertEqual(conc.children[2].domain, ["positive electrode"])

        processed_conc = disc.process_symbol(conc)
        np.testing.assert_array_equal(
            processed_conc.evaluate(t=2),
            np.concatenate([
                2 * np.ones(mesh["negative electrode"][0].npts),
                4 * np.ones(mesh["separator"][0].npts),
                6 * np.ones(mesh["positive electrode"][0].npts),
            ])[:, np.newaxis],
        )

        # Piecewise constant state vectors
        a_sv = pybamm.Broadcast(pybamm.StateVector(slice(0, 1)),
                                ["negative electrode"])
        b_sv = pybamm.Broadcast(pybamm.StateVector(slice(1, 2)), ["separator"])
        c_sv = pybamm.Broadcast(pybamm.StateVector(slice(2, 3)),
                                ["positive electrode"])
        conc = pybamm.Concatenation(a_sv, b_sv, c_sv)

        self.assertEqual(
            conc.domain,
            ["negative electrode", "separator", "positive electrode"])
        self.assertEqual(conc.children[0].domain, ["negative electrode"])
        self.assertEqual(conc.children[1].domain, ["separator"])
        self.assertEqual(conc.children[2].domain, ["positive electrode"])

        processed_conc = disc.process_symbol(conc)
        y = np.array([1, 2, 3])
        np.testing.assert_array_equal(
            processed_conc.evaluate(y=y),
            np.concatenate([
                np.ones(mesh["negative electrode"][0].npts),
                2 * np.ones(mesh["separator"][0].npts),
                3 * np.ones(mesh["positive electrode"][0].npts),
            ])[:, np.newaxis],
        )

        # Mixed
        conc = pybamm.Concatenation(a, b_t, c_sv)

        self.assertEqual(
            conc.domain,
            ["negative electrode", "separator", "positive electrode"])
        self.assertEqual(conc.children[0].domain, ["negative electrode"])
        self.assertEqual(conc.children[1].domain, ["separator"])
        self.assertEqual(conc.children[2].domain, ["positive electrode"])

        processed_conc = disc.process_symbol(conc)
        np.testing.assert_array_equal(
            processed_conc.evaluate(t=2, y=y),
            np.concatenate([
                np.ones(mesh["negative electrode"][0].npts),
                4 * np.ones(mesh["separator"][0].npts),
                3 * np.ones(mesh["positive electrode"][0].npts),
            ])[:, np.newaxis],
        )