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)
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)
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)
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)
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)
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)
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))
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
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)
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)
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
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)
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)
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)
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
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")
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)
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)
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
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()
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)
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)
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)