Ejemplo n.º 1
0
    def __init__(self, lims, npts, edges=None):

        # raise error if no edges passed
        if edges is None:
            raise pybamm.GeometryError("User mesh requires parameter 'edges'")

        spatial_var, spatial_lims, tabs = self.read_lims(lims)
        npts = npts[spatial_var.id]

        # check that npts + 1 equals number of user-supplied edges
        if (npts + 1) != len(edges):
            raise pybamm.GeometryError(
                """User-suppled edges has should have length (npts + 1) but has length {}.
                 Number of points (npts) for domain {} is {}.""".format(
                    len(edges), spatial_var.domain, npts))

        # check end points of edges agree with spatial_lims
        if edges[0] != spatial_lims["min"]:
            raise pybamm.GeometryError(
                """First entry of edges is {}, but should be equal to {}
                 for domain {}.""".format(edges[0], spatial_lims["min"],
                                          spatial_var.domain))
        if edges[-1] != spatial_lims["max"]:
            raise pybamm.GeometryError(
                """Last entry of edges is {}, but should be equal to {}
                for domain {}.""".format(edges[-1], spatial_lims["max"],
                                         spatial_var.domain))

        coord_sys = spatial_var.coord_sys

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 2
0
    def __init__(self, lims, npts, tabs, side="top", stretch=2.3):

        # check side is top
        if side != "top":
            raise pybamm.GeometryError(
                "At present, side can only be 'top', but is set to {}".format(side)
            )

        # check that two variables have been passed in
        if len(lims) != 2:
            raise pybamm.GeometryError(
                "lims should contain exactly two variables, not {}".format(len(lims))
            )

        # get spatial variables
        spatial_vars = list(lims.keys())

        # check coordinate system agrees
        if spatial_vars[0].coord_sys == spatial_vars[1].coord_sys:
            coord_sys = spatial_vars[0].coord_sys
        else:
            raise pybamm.DomainError(
                """spatial variables should have the same coordinate system,
                but have coordinate systems {} and {}""".format(
                    spatial_vars[0].coord_sys, spatial_vars[1].coord_sys
                )
            )

        # compute edges
        edges = {}
        for var in spatial_vars:
            if var.name not in ["y", "z"]:
                raise pybamm.DomainError(
                    "spatial variable must be y or z not {}".format(var.name)
                )
            elif var.name == "y":
                edges[var.name] = np.linspace(
                    lims[var]["min"], lims[var]["max"], npts[var.id]
                )
            elif var.name == "z":
                ii = np.array(range(0, npts[var.id]))
                a = lims[var]["min"]
                b = lims[var]["max"]
                edges[var.name] = (b - a) * (
                    np.exp(-stretch * ii / (npts[var.id] - 1)) - 1
                ) / (np.exp(-stretch) - 1) + a

        super().__init__(edges, coord_sys, tabs)
Ejemplo n.º 3
0
    def __init__(self, cc_dimension=1, custom_geometry={}):
        super().__init__()

        var = pybamm.standard_spatial_vars

        # Add secondary domains to x-domains
        if cc_dimension == 1:
            for domain in self.keys():
                self[domain]["secondary"] = {
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    }
                }
        elif cc_dimension == 2:
            for domain in self.keys():
                self[domain]["secondary"] = {
                    var.y: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_y,
                    },
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    },
                }
        else:
            raise pybamm.GeometryError(
                "current collector dimension must be 1 or 2, not {}".format(
                    cc_dimension
                )
            )

        # update with custom geometry if non empty
        self.update(custom_geometry)
Ejemplo n.º 4
0
    def __init__(self, lims, npts, side="top", stretch=2.3):

        # check side is top
        if side != "top":
            raise pybamm.GeometryError(
                "At present, side can only be 'top', but is set to {}".format(
                    side))

        spatial_vars, tabs = self.read_lims(lims)
        coord_sys = spatial_vars[0].coord_sys

        # compute edges
        edges = {}
        for var in spatial_vars:
            if var.name not in ["y", "z"]:
                raise pybamm.DomainError(
                    "spatial variable must be y or z not {}".format(var.name))
            elif var.name == "y":
                edges[var.name] = np.linspace(lims[var]["min"],
                                              lims[var]["max"], npts[var.id])
            elif var.name == "z":
                ii = np.array(range(0, npts[var.id]))
                a = lims[var]["min"]
                b = lims[var]["max"]
                edges[var.name] = (b - a) * (np.exp(-stretch * ii /
                                                    (npts[var.id] - 1)) -
                                             1) / (np.exp(-stretch) - 1) + a

        super().__init__(edges, coord_sys, tabs)
Ejemplo n.º 5
0
    def __init__(self, lims, npts, tabs):

        # check that two variables have been passed in
        if len(lims) != 2:
            raise pybamm.GeometryError(
                "lims should contain exactly two variables, not {}".format(len(lims))
            )

        # get spatial variables
        spatial_vars = list(lims.keys())

        # check coordinate system agrees
        if spatial_vars[0].coord_sys == spatial_vars[1].coord_sys:
            coord_sys = spatial_vars[0].coord_sys
        else:
            raise pybamm.DomainError(
                """spatial variables should have the same coordinate system,
                but have coordinate systems {} and {}""".format(
                    spatial_vars[0].coord_sys, spatial_vars[1].coord_sys
                )
            )

        # compute edges
        edges = {}
        for var in spatial_vars:
            if var.name not in ["y", "z"]:
                raise pybamm.DomainError(
                    "spatial variable must be y or z not {}".format(var.name)
                )
            else:
                edges[var.name] = np.linspace(
                    lims[var]["min"], lims[var]["max"], npts[var.id]
                )

        super().__init__(edges, coord_sys, tabs)
Ejemplo n.º 6
0
    def __init__(self, lims, npts, tabs=None):

        # check that only one variable passed in
        if len(lims) != 1:
            raise pybamm.GeometryError(
                "lims should only contain a single variable")

        spatial_var = list(lims.keys())[0]
        spatial_lims = lims[spatial_var]
        npts = npts[spatial_var.id]

        # Create N Chebyshev nodes in the interval (a,b)
        N = npts - 1
        ii = np.array(range(1, N + 1))
        a = spatial_lims["min"]
        b = spatial_lims["max"]
        x_cheb = (a + b) / 2 + (b - a) / 2 * np.cos(
            (2 * ii - 1) * np.pi / 2 / N)

        # Append the boundary nodes. Note: we need to flip the order the Chebyshev
        # nodes as they are created in descending order.
        edges = np.concatenate(([a], np.flip(x_cheb), [b]))
        coord_sys = spatial_var.coord_sys

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 7
0
    def __init__(self, edges, coord_sys, tabs=None):
        self.edges = edges
        self.nodes = (self.edges[1:] + self.edges[:-1]) / 2
        self.d_edges = np.diff(self.edges)
        self.d_nodes = np.diff(self.nodes)
        self.npts = self.nodes.size
        self.coord_sys = coord_sys
        self.internal_boundaries = []

        # Add tab locations in terms of "left" and "right"
        if tabs:
            self.tabs = {}
            l_z = self.edges[-1]

            def near(x, point, tol=3e-16):
                return abs(x - point) < tol

            for tab in ["negative", "positive"]:
                tab_location = tabs[tab]["z_centre"]
                if near(tab_location, 0):
                    self.tabs[tab + " tab"] = "left"
                elif near(tab_location, l_z):
                    self.tabs[tab + " tab"] = "right"
                else:
                    raise pybamm.GeometryError(
                        """{} tab located at {}, but must be at either 0 or {}
                        (in dimensionless coordinates).""".format(
                            tab, tab_location, l_z))
Ejemplo n.º 8
0
    def spatial_variable(self, symbol):
        """
        Creates a discretised spatial variable compatible with
        the FiniteElement method.

        Parameters
        -----------
        symbol : :class:`pybamm.SpatialVariable`
            The spatial variable to be discretised.

        Returns
        -------
        :class:`pybamm.Vector`
            Contains the discretised spatial variable
        """
        symbol_mesh = self.mesh
        if symbol.name == "y":
            vector = pybamm.Vector(symbol_mesh["current collector"]
                                   [0].coordinates[0, :][:, np.newaxis])
        elif symbol.name == "z":
            vector = pybamm.Vector(symbol_mesh["current collector"]
                                   [0].coordinates[1, :][:, np.newaxis])
        else:
            raise pybamm.GeometryError(
                "Spatial variable must be 'y' or 'z' not {}".format(
                    symbol.name))
        return vector
Ejemplo n.º 9
0
    def __init__(self, lims, npts, edges=None, order=2):

        spatial_var, spatial_lims, tabs = self.read_lims(lims)
        npts = npts[spatial_var.id]

        # default: Spectral Volumes of equal size
        if edges is None:
            edges = np.linspace(spatial_lims["min"], spatial_lims["max"],
                                npts + 1)
        # check that npts + 1 equals number of user-supplied edges
        elif (npts + 1) != len(edges):
            raise pybamm.GeometryError(
                "User-suppled edges should have length (npts + 1) but has len"
                "gth {}. Number of points (npts) for domain {} is {}.".format(
                    len(edges), spatial_var.domain, npts))

        # check end points of edges agree with spatial_lims
        if edges[0] != spatial_lims["min"]:
            raise pybamm.GeometryError(
                """First entry of edges is {}, but should be equal to {}
                 for domain {}.""".format(edges[0], spatial_lims["min"],
                                          spatial_var.domain))
        if edges[-1] != spatial_lims["max"]:
            raise pybamm.GeometryError(
                """Last entry of edges is {}, but should be equal to {}
                for domain {}.""".format(edges[-1], spatial_lims["max"],
                                         spatial_var.domain))

        coord_sys = spatial_var.coord_sys

        cv_edges = np.array([edges[0]] + [
            x for (a, b) in zip(edges[:-1], edges[1:])
            for x in np.flip(a + 0.5 * (b - a) * (
                1 + np.sin(np.pi * np.array([((order + 1) - 1 - 2 * i) /
                                             (2 * (order + 1) - 2)
                                             for i in range(order + 1)]))))[1:]
        ])

        self.sv_edges = edges
        self.sv_nodes = (edges[:-1] + edges[1:]) / 2
        self.d_sv_edges = np.diff(self.sv_edges)
        self.d_sv_nodes = np.diff(self.sv_nodes)
        self.order = 2
        # The Control Volume edges and nodes are assigned to the
        # "edges" and "nodes" properties. This makes some of the
        # code of FiniteVolume directly applicable.
        super().__init__(cv_edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 10
0
    def __init__(self, cc_dimension=1, custom_geometry={}):
        super().__init__()

        var = pybamm.standard_spatial_vars
        l_n = pybamm.geometric_parameters.l_n
        l_s = pybamm.geometric_parameters.l_s

        # Add secondary domains to x-domains
        if cc_dimension == 1:
            self["negative particle"]["secondary"] = {
                var.x_n: {"min": pybamm.Scalar(0), "max": l_n},
                var.z: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_z,
                },
            }
            self["positive particle"]["secondary"] = {
                var.x_p: {"min": l_n + l_s, "max": pybamm.Scalar(1)},
                var.z: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_z,
                },
            }
        elif cc_dimension == 2:
            self["negative particle"]["secondary"] = {
                var.x_n: {"min": pybamm.Scalar(0), "max": l_n},
                var.y: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_y,
                },
                var.z: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_z,
                },
            }
            self["positive particle"]["secondary"] = {
                var.x_p: {"min": l_n + l_s, "max": pybamm.Scalar(1)},
                var.y: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_y,
                },
                var.z: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.geometric_parameters.l_z,
                },
            }
        else:
            raise pybamm.GeometryError(
                "current collector dimension must be 1 or 2, not {}".format(
                    cc_dimension
                )
            )

        # update with custom geometry if non empty
        self.update(custom_geometry)
Ejemplo n.º 11
0
    def __init__(self, position, npts=None, tabs=None):
        # check that only one variable passed in
        if len(position) != 1:
            raise pybamm.GeometryError(
                "position should only contain a single variable")

        spatial_position = list(position.values())[0]
        self.nodes = np.array([spatial_position])
        self.edges = np.array([spatial_position])
        self.coord_sys = None
        self.npts = 1
Ejemplo n.º 12
0
    def __init__(self, lims, npts, tabs, side="symmetric", stretch=None):

        # check that only one variable passed in
        if len(lims) != 1:
            raise pybamm.GeometryError(
                "lims should only contain a single variable")

        spatial_var = list(lims.keys())[0]
        spatial_lims = lims[spatial_var]
        a = spatial_lims["min"]
        b = spatial_lims["max"]
        npts = npts[spatial_var.id]
        coord_sys = spatial_var.coord_sys

        # Set stretch if not provided
        if not stretch:
            if side == "symmetric":
                stretch = 1.15
            elif side in ["left", "right"]:
                stretch = 2.3

        # Create edges accoriding to "side"
        if side == "left":
            ii = np.array(range(0, npts + 1))
            edges = (b - a) * (np.exp(stretch * ii / npts) -
                               1) / (np.exp(stretch) - 1) + a

        elif side == "right":
            ii = np.array(range(0, npts + 1))
            edges = (b - a) * (np.exp(-stretch * ii / npts) -
                               1) / (np.exp(-stretch) - 1) + a

        elif side == "symmetric":
            # Mesh half-interval [a, b/2]
            if npts % 2 == 0:
                ii = np.array(range(0, int((npts) / 2)))
            else:
                ii = np.array(range(0, int((npts + 1) / 2)))
            x_exp_left = (b / 2 - a) * (np.exp(stretch * ii / npts) -
                                        1) / (np.exp(stretch) - 1) + a

            # Refelct mesh
            x_exp_right = b * np.ones_like(x_exp_left) - (x_exp_left[::-1] - a)

            # Combine left and right halves of the mesh, adding a node at the
            # centre if npts is even (odd number of edges)
            if npts % 2 == 0:
                edges = np.concatenate(
                    (x_exp_left, [(a + b) / 2], x_exp_right))
            else:
                edges = np.concatenate((x_exp_left, x_exp_right))

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 13
0
    def __init__(self, lims, npts, y_edges=None, z_edges=None):

        # raise error if no edges passed
        if y_edges is None:
            raise pybamm.GeometryError(
                "User mesh requires parameter 'y_edges'")
        if z_edges is None:
            raise pybamm.GeometryError(
                "User mesh requires parameter 'z_edges'")

        spatial_vars, tabs = self.read_lims(lims)
        coord_sys = spatial_vars[0].coord_sys

        # check and store edges
        edges = {"y": y_edges, "z": z_edges}
        for var in spatial_vars:

            # check that npts equals number of user-supplied edges
            if npts[var.id] != len(edges[var.name]):
                raise pybamm.GeometryError(
                    """User-suppled edges has should have length npts but has length {}.
                     Number of points (npts) for variable {} in
                     domain {} is {}.""".format(len(edges[var.name]), var.name,
                                                var.domain, npts[var.id]))

            # check end points of edges agree with spatial_lims
            if edges[var.name][0] != lims[var]["min"]:
                raise pybamm.GeometryError(
                    """First entry of edges is {}, but should be equal to {}
                     for variable {} in domain {}.""".format(
                        edges[var.name][0], lims[var]["min"], var.name,
                        var.domain))
            if edges[var.name][-1] != lims[var]["max"]:
                raise pybamm.GeometryError(
                    """Last entry of edges is {}, but should be equal to {}
                    for variable {} in domain {}.""".format(
                        edges[var.name][-1], lims[var]["max"], var.name,
                        var.domain))

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 14
0
    def __init__(self, lims, npts, tabs, y_edges=None, z_edges=None):

        # raise error if no edges passed
        if y_edges is None:
            raise pybamm.GeometryError("User mesh requires parameter 'y_edges'")
        if z_edges is None:
            raise pybamm.GeometryError("User mesh requires parameter 'z_edges'")

        # check that two variables have been passed in
        if len(lims) != 2:
            raise pybamm.GeometryError(
                "lims should contain exactly two variables, not {}".format(len(lims))
            )

        # get spatial variables
        spatial_vars = list(lims.keys())

        # check coordinate system agrees
        if spatial_vars[0].coord_sys == spatial_vars[1].coord_sys:
            coord_sys = spatial_vars[0].coord_sys
        else:
            raise pybamm.DomainError(
                """spatial variables should have the same coordinate system,
                but have coordinate systems {} and {}""".format(
                    spatial_vars[0].coord_sys, spatial_vars[1].coord_sys
                )
            )

        # check and store edges
        edges = {"y": y_edges, "z": z_edges}
        for var in spatial_vars:

            # check that npts equals number of user-supplied edges
            if npts[var.id] != len(edges[var.name]):
                raise pybamm.GeometryError(
                    """User-suppled edges has should have length npts but has length {}.
                     Number of points (npts) for variable {} in
                     domain {} is {}.""".format(
                        len(edges[var.name]), var.name, var.domain, npts[var.id]
                    )
                )

            # check end points of edges agree with spatial_lims
            if edges[var.name][0] != lims[var]["min"]:
                raise pybamm.GeometryError(
                    """First entry of edges is {}, but should be equal to {}
                     for variable {} in domain {}.""".format(
                        edges[var.name][0], lims[var]["min"], var.name, var.domain
                    )
                )
            if edges[var.name][-1] != lims[var]["max"]:
                raise pybamm.GeometryError(
                    """Last entry of edges is {}, but should be equal to {}
                    for variable {} in domain {}.""".format(
                        edges[var.name][-1], lims[var]["max"], var.name, var.domain
                    )
                )

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 15
0
    def read_lims(self, lims):
        # Separate limits and tabs
        # Read and remove tabs. If "tabs" is not a key in "lims", then tabs is set to
        # "None" and nothing is removed from lims
        tabs = lims.pop("tabs", None)

        # check that only one variable passed in
        if len(lims) != 1:
            raise pybamm.GeometryError(
                "lims should only contain a single variable")

        ((spatial_var, spatial_lims), ) = lims.items()
        return spatial_var, spatial_lims, tabs
Ejemplo n.º 16
0
    def on_boundary(self, y, z, tab):
        """
        A method to get the degrees of freedom corresponding to the subdomains
        for the tabs.
        """

        l_y = self.edges["y"][-1]
        l_z = self.edges["z"][-1]

        def near(x, point, tol=3e-16):
            return abs(x - point) < tol

        def between(x, interval, tol=3e-16):
            return x > interval[0] - tol and x < interval[1] + tol

        # Tab on top
        if near(tab["z_centre"], l_z):
            tab_left = tab["y_centre"] - tab["width"] / 2
            tab_right = tab["y_centre"] + tab["width"] / 2
            return [
                near(Z, l_z) and between(Y, [tab_left, tab_right])
                for Y, Z in zip(y, z)
            ]
        # Tab on bottom
        elif near(tab["z_centre"], 0):
            tab_left = tab["y_centre"] - tab["width"] / 2
            tab_right = tab["y_centre"] + tab["width"] / 2
            return [
                near(Z, 0) and between(Y, [tab_left, tab_right])
                for Y, Z in zip(y, z)
            ]
        # Tab on left
        elif near(tab["y_centre"], 0):
            tab_bottom = tab["z_centre"] - tab["width"] / 2
            tab_top = tab["z_centre"] + tab["width"] / 2
            return [
                near(Y, 0) and between(Z, [tab_bottom, tab_top])
                for Y, Z in zip(y, z)
            ]
        # Tab on right
        elif near(tab["y_centre"], l_y):
            tab_bottom = tab["z_centre"] - tab["width"] / 2
            tab_top = tab["z_centre"] + tab["width"] / 2
            return [
                near(Y, l_y) and between(Z, [tab_bottom, tab_top])
                for Y, Z in zip(y, z)
            ]
        else:
            raise pybamm.GeometryError("tab location not valid")
Ejemplo n.º 17
0
    def __init__(self, lims, npts, tabs=None):

        # check that only one variable passed in
        if len(lims) != 1:
            raise pybamm.GeometryError(
                "lims should only contain a single variable")

        spatial_var = list(lims.keys())[0]
        spatial_lims = lims[spatial_var]
        npts = npts[spatial_var.id]

        edges = np.linspace(spatial_lims["min"], spatial_lims["max"], npts + 1)

        coord_sys = spatial_var.coord_sys

        super().__init__(edges, coord_sys=coord_sys, tabs=tabs)
Ejemplo n.º 18
0
    def __init__(self, lims, npts, tabs):

        # check that two variables have been passed in
        if len(lims) != 2:
            raise pybamm.GeometryError(
                "lims should contain exactly two variables, not {}".format(len(lims))
            )

        # get spatial variables
        spatial_vars = list(lims.keys())

        # check coordinate system agrees
        if spatial_vars[0].coord_sys == spatial_vars[1].coord_sys:
            coord_sys = spatial_vars[0].coord_sys
        else:
            raise pybamm.DomainError(
                """spatial variables should have the same coordinate system,
                but have coordinate systems {} and {}""".format(
                    spatial_vars[0].coord_sys, spatial_vars[1].coord_sys
                )
            )

        # compute edges
        edges = {}
        for var in spatial_vars:
            if var.name not in ["y", "z"]:
                raise pybamm.DomainError(
                    "spatial variable must be y or z not {}".format(var.name)
                )
            else:
                # Create N Chebyshev nodes in the interval (a,b)
                N = npts[var.id] - 2
                ii = np.array(range(1, N + 1))
                a = lims[var]["min"]
                b = lims[var]["max"]
                x_cheb = (a + b) / 2 + (b - a) / 2 * np.cos(
                    (2 * ii - 1) * np.pi / 2 / N
                )

                # Append the boundary nodes. Note: we need to flip the order the
                # Chebyshev nodes as they are created in descending order.
                edges[var.name] = np.concatenate(([a], np.flip(x_cheb), [b]))

        super().__init__(edges, coord_sys, tabs)
Ejemplo n.º 19
0
    def read_lims(self, lims):
        # Separate limits and tabs
        # Read and remove tabs. If "tabs" is not a key in "lims", then tabs is set to
        # "None" and nothing is removed from lims
        tabs = lims.pop("tabs", None)

        # check that two variables have been passed in
        if len(lims) != 2:
            raise pybamm.GeometryError(
                "lims should contain exactly two variables, not {}".format(
                    len(lims)))

        # get spatial variables
        spatial_vars = list(lims.keys())

        # check coordinate system agrees
        if spatial_vars[0].coord_sys != spatial_vars[1].coord_sys:
            raise pybamm.DomainError(
                """spatial variables should have the same coordinate system,
                but have coordinate systems {} and {}""".format(
                    spatial_vars[0].coord_sys, spatial_vars[1].coord_sys))
        return spatial_vars, tabs
Ejemplo n.º 20
0
    def __init__(self, geometry, submesh_types, var_pts):
        super().__init__()
        # convert var_pts to an id dict
        var_id_pts = {var.id: pts for var, pts in var_pts.items()}

        # create submesh_pts from var_pts
        submesh_pts = {}
        for domain in geometry:
            # create mesh generator if just class is passed (will throw an error
            # later if the mesh needed parameters)
            if not isinstance(
                submesh_types[domain], pybamm.MeshGenerator
            ) and issubclass(submesh_types[domain], pybamm.SubMesh):
                submesh_types[domain] = pybamm.MeshGenerator(submesh_types[domain])
            # Zero dimensional submesh case (only one point)
            if issubclass(submesh_types[domain].submesh_type, pybamm.SubMesh0D):
                submesh_pts[domain] = 1
            # other cases
            else:
                submesh_pts[domain] = {}
                if len(list(geometry[domain].keys())) > 3:
                    raise pybamm.GeometryError("Too many keys provided")
                for var in list(geometry[domain].keys()):
                    if var in ["primary", "secondary"]:
                        raise pybamm.GeometryError(
                            "Geometry should no longer be given keys 'primary' or "
                            "'secondary'. See pybamm.battery_geometry() for example"
                        )
                    # skip over tabs key
                    if var != "tabs":
                        # Raise error if the number of points for a particular
                        # variable haven't been provided, unless that variable
                        # doesn't appear in the geometry
                        if (
                            var.id not in var_id_pts.keys()
                            and var.domain[0] in geometry.keys()
                        ):
                            raise KeyError(
                                "Points not given for a variable in domain {}".format(
                                    domain
                                )
                            )
                        # Otherwise add to the dictionary of submesh points
                        submesh_pts[domain][var.id] = var_id_pts[var.id]
        self.submesh_pts = submesh_pts

        # Input domain order manually
        self.domain_order = []
        # First the macroscale domains, whose order we care about
        for domain in ["negative electrode", "separator", "positive electrode"]:
            if domain in geometry:
                self.domain_order.append(domain)
        # Then the remaining domains
        for domain in geometry:
            if domain not in ["negative electrode", "separator", "positive electrode"]:
                self.domain_order.append(domain)

        # evaluate any expressions in geometry
        for domain in geometry:
            for spatial_variable, spatial_limits in geometry[domain].items():
                # process tab information if using 1 or 2D current collectors
                if spatial_variable == "tabs":
                    for tab, position_size in spatial_limits.items():
                        for position_size, sym in position_size.items():
                            if isinstance(sym, pybamm.Symbol):
                                sym_eval = sym.evaluate()
                                geometry[domain]["tabs"][tab][position_size] = sym_eval
                else:
                    for lim, sym in spatial_limits.items():
                        if isinstance(sym, pybamm.Symbol):
                            try:
                                sym_eval = sym.evaluate()
                            except NotImplementedError as error:
                                if sym.has_symbol_of_classes(pybamm.Parameter):
                                    raise pybamm.DiscretisationError(
                                        "Parameter values have not yet been set for "
                                        "geometry. Make sure that something like "
                                        "`param.process_geometry(geometry)` has been "
                                        "run."
                                    )
                                else:
                                    raise error
                        elif isinstance(sym, numbers.Number):
                            sym_eval = sym
                        geometry[domain][spatial_variable][lim] = sym_eval

        # Create submeshes
        for domain in geometry:
            self[domain] = submesh_types[domain](geometry[domain], submesh_pts[domain])

        # add ghost meshes
        self.add_ghost_meshes()
Ejemplo n.º 21
0
    def __init__(self, cc_dimension=1, custom_geometry={}):
        super().__init__()

        var = pybamm.standard_spatial_vars

        if cc_dimension == 1:
            # Add secondary domains to x-domains
            for geom in self.values():
                geom["secondary"] = {
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    }
                }
            # Add primary current collector domain
            self["current collector"] = {
                "primary": {
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    }
                },
                "tabs": {
                    "negative": {
                        "z_centre": pybamm.geometric_parameters.centre_z_tab_n
                    },
                    "positive": {
                        "z_centre": pybamm.geometric_parameters.centre_z_tab_p
                    },
                },
            }
        elif cc_dimension == 2:
            # Add secondary domains to x-domains
            for geom in self.values():
                geom["secondary"] = {
                    var.y: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_y,
                    },
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    },
                }
            # Add primary current collector domain
            self["current collector"] = {
                "primary": {
                    var.y: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_y,
                    },
                    var.z: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.geometric_parameters.l_z,
                    },
                },
                "tabs": {
                    "negative": {
                        "y_centre": pybamm.geometric_parameters.centre_y_tab_n,
                        "z_centre": pybamm.geometric_parameters.centre_z_tab_n,
                        "width": pybamm.geometric_parameters.l_tab_n,
                    },
                    "positive": {
                        "y_centre": pybamm.geometric_parameters.centre_y_tab_p,
                        "z_centre": pybamm.geometric_parameters.centre_z_tab_p,
                        "width": pybamm.geometric_parameters.l_tab_p,
                    },
                },
            }
        else:
            raise pybamm.GeometryError(
                "current collector dimension must be 1 or 2, not {}".format(
                    cc_dimension
                )
            )

        # update with custom geometry if non empty
        self.update(custom_geometry)
Ejemplo n.º 22
0
def battery_geometry(include_particles=True, current_collector_dimension=0):
    """
    A convenience function to create battery geometries.

    Parameters
    ----------
    include_particles : bool
        Whether to include particle domains
    current_collector_dimensions : int, default
        The dimensions of the current collector. Should be 0 (default), 1 or 2

    Returns
    -------
    :class:`pybamm.Geometry`
        A geometry class for the battery

    """
    var = pybamm.standard_spatial_vars
    geo = pybamm.geometric_parameters
    l_n = geo.l_n
    l_s = geo.l_s

    geometry = {
        "negative electrode": {
            var.x_n: {
                "min": 0,
                "max": l_n
            }
        },
        "separator": {
            var.x_s: {
                "min": l_n,
                "max": l_n + l_s
            }
        },
        "positive electrode": {
            var.x_p: {
                "min": l_n + l_s,
                "max": 1
            }
        },
    }
    # Add particle domains
    if include_particles is True:
        geometry.update({
            "negative particle": {
                var.r_n: {
                    "min": 0,
                    "max": 1
                }
            },
            "positive particle": {
                var.r_p: {
                    "min": 0,
                    "max": 1
                }
            },
        })

    if current_collector_dimension == 0:
        geometry["current collector"] = {var.z: {"position": 1}}
    elif current_collector_dimension == 1:
        geometry["current collector"] = {
            var.z: {
                "min": 0,
                "max": 1
            },
            "tabs": {
                "negative": {
                    "z_centre": geo.centre_z_tab_n
                },
                "positive": {
                    "z_centre": geo.centre_z_tab_p
                },
            },
        }
    elif current_collector_dimension == 2:
        geometry["current collector"] = {
            var.y: {
                "min": 0,
                "max": geo.l_y
            },
            var.z: {
                "min": 0,
                "max": geo.l_z
            },
            "tabs": {
                "negative": {
                    "y_centre": geo.centre_y_tab_n,
                    "z_centre": geo.centre_z_tab_n,
                    "width": geo.l_tab_n,
                },
                "positive": {
                    "y_centre": geo.centre_y_tab_p,
                    "z_centre": geo.centre_z_tab_p,
                    "width": geo.l_tab_p,
                },
            },
        }
    else:
        raise pybamm.GeometryError(
            "Invalid current collector dimension '{}' (should be 0, 1 or 2)".
            format(current_collector_dimension))

    return pybamm.Geometry(geometry)
Ejemplo n.º 23
0
def half_cell_geometry(
    include_particles=True, current_collector_dimension=0, working_electrode="positive"
):
    """
    A convenience function to create battery geometries.

    Parameters
    ----------
    include_particles : bool
        Whether to include particle domains
    current_collector_dimensions : int, default
        The dimensions of the current collector. Should be 0 (default), 1 or 2
    current_collector_dimensions : string
        The electrode taking as working electrode. Should be "positive" or "negative"

    Returns
    -------
    :class:`pybamm.Geometry`
        A geometry class for the battery

    """
    var = half_cell_spatial_vars
    geo = pybamm.geometric_parameters
    if working_electrode == "positive":
        l_w = geo.l_p
    elif working_electrode == "negative":
        l_w = geo.l_n
    else:
        raise ValueError(
            "The option 'working_electrode' should be either 'positive'"
            " or 'negative'"
        )
    l_Li = geo.l_Li
    l_s = geo.l_s

    geometry = {
        "lithium counter electrode": {var.x_Li: {"min": 0, "max": l_Li}},
        "separator": {var.x_s: {"min": l_Li, "max": l_Li + l_s}},
        "working electrode": {var.x_w: {"min": l_Li + l_s, "max": l_Li + l_s + l_w}},
    }
    # Add particle domains
    if include_particles is True:
        geometry.update({"working particle": {var.r_w: {"min": 0, "max": 1}}})

    if current_collector_dimension == 0:
        geometry["current collector"] = {var.z: {"position": 1}}
    elif current_collector_dimension == 1:
        geometry["current collector"] = {
            var.z: {"min": 0, "max": 1},
            "tabs": {
                "negative": {"z_centre": geo.centre_z_tab_n},
                "positive": {"z_centre": geo.centre_z_tab_p},
            },
        }
    elif current_collector_dimension == 2:
        geometry["current collector"] = {
            var.y: {"min": 0, "max": geo.l_y},
            var.z: {"min": 0, "max": geo.l_z},
            "tabs": {
                "negative": {
                    "y_centre": geo.centre_y_tab_n,
                    "z_centre": geo.centre_z_tab_n,
                    "width": geo.l_tab_n,
                },
                "positive": {
                    "y_centre": geo.centre_y_tab_p,
                    "z_centre": geo.centre_z_tab_p,
                    "width": geo.l_tab_p,
                },
            },
        }
    else:
        raise pybamm.GeometryError(
            "Invalid current collector dimension '{}' (should be 0, 1 or 2)".format(
                current_collector_dimension
            )
        )

    return pybamm.Geometry(geometry)