def test_extrapolate_on_nonuniform_grid(self): geometry = pybamm.Geometry("1D micro") submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.Exponential1DSubMesh), "positive particle": pybamm.MeshGenerator(pybamm.Exponential1DSubMesh), } var = pybamm.standard_spatial_vars rpts = 10 var_pts = {var.r_n: rpts, var.r_p: rpts} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) method_options = {"extrapolation": {"order": "linear", "use bcs": False}} spatial_methods = {"negative particle": pybamm.FiniteVolume(method_options)} disc = pybamm.Discretisation(mesh, spatial_methods) var = pybamm.Variable("var", domain="negative particle") surf_eqn = pybamm.surf(var) disc.set_variable_slices([var]) surf_eqn_disc = disc.process_symbol(surf_eqn) micro_submesh = mesh["negative particle"] # check constant extrapolates to constant constant_y = np.ones_like(micro_submesh[0].nodes[:, np.newaxis]) np.testing.assert_array_almost_equal( surf_eqn_disc.evaluate(None, constant_y), 1 ) # check linear variable extrapolates correctly linear_y = micro_submesh[0].nodes y_surf = micro_submesh[0].edges[-1] np.testing.assert_array_almost_equal( surf_eqn_disc.evaluate(None, linear_y), y_surf )
def default_submesh_types(self): if self.options["dimensionality"] == 1: return {"current collector": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh)} elif self.options["dimensionality"] == 2: return { "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh) }
def test_exceptions(self): edges = np.array([0, 0.3, 1]) submesh_params = {"edges": edges} mesh = pybamm.MeshGenerator(pybamm.UserSupplied1DSubMesh, submesh_params) x_n = pybamm.standard_spatial_vars.x_n # error if npts+1 != len(edges) lims = {x_n: {"min": 0, "max": 1}} npts = {x_n.id: 10} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[0] not equal to edges[0] lims = {x_n: {"min": 0.1, "max": 1}} npts = {x_n.id: len(edges) - 1} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[-1] not equal to edges[-1] lims = {x_n: {"min": 0, "max": 10}} npts = {x_n.id: len(edges) - 1} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # no user points mesh = pybamm.MeshGenerator(pybamm.UserSupplied1DSubMesh) with self.assertRaisesRegex(pybamm.GeometryError, "User mesh requires"): mesh(None, None)
def get_unit_2p1D_mesh_for_testing(ypts=15, zpts=15, include_particles=True): param = pybamm.ParameterValues( values={ "Electrode width [m]": 1, "Electrode height [m]": 1, "Negative tab width [m]": 1, "Negative tab centre y-coordinate [m]": 0.5, "Negative tab centre z-coordinate [m]": 0, "Positive tab width [m]": 1, "Positive tab centre y-coordinate [m]": 0.5, "Positive tab centre z-coordinate [m]": 1, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, } ) geometry = pybamm.battery_geometry( include_particles=include_particles, current_collector_dimension=2 ) param.process_geometry(geometry) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 3, var.x_s: 3, var.x_p: 3, var.y: ypts, var.z: zpts} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } return pybamm.Mesh(geometry, submesh_types, var_pts)
def test_mesh_creation_no_parameters(self): r = pybamm.SpatialVariable("r", domain=["negative particle"], coord_sys="spherical polar") geometry = { "negative particle": { r: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } } edges = np.array([0, 0.3, 1]) order = 3 submesh_params = {"edges": edges, "order": order} submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.SpectralVolume1DSubMesh, submesh_params) } var_pts = {r: len(edges) - 1} # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative particle"].edges[0], 0) self.assertEqual(mesh["negative particle"].edges[-1], 1) # check number of edges and nodes self.assertEqual(len(mesh["negative particle"].sv_nodes), var_pts[r]) self.assertEqual(len(mesh["negative particle"].nodes), order * var_pts[r]) self.assertEqual( len(mesh["negative particle"].edges), len(mesh["negative particle"].nodes) + 1, ) # check Chebyshev subdivision locations for (a, b) in zip(mesh["negative particle"].edges.tolist(), [0, 0.075, 0.225, 0.3, 0.475, 0.825, 1]): self.assertAlmostEqual(a, b) # test uniform submesh creation submesh_params = {"order": order} submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.SpectralVolume1DSubMesh, submesh_params) } var_pts = {r: 2} # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) for (a, b) in zip(mesh["negative particle"].edges.tolist(), [0.0, 0.125, 0.375, 0.5, 0.625, 0.875, 1.0]): self.assertAlmostEqual(a, b)
def test_mesh_creation(self): param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab width [m]": 0.1, "Negative tab centre y-coordinate [m]": 0.1, "Negative tab centre z-coordinate [m]": 0.5, "Positive tab width [m]": 0.1, "Positive tab centre y-coordinate [m]": 0.3, "Positive tab centre z-coordinate [m]": 0.5, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.Geometryxp1DMacro(cc_dimension=2) param.process_geometry(geometry) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 7, var.x_p: 12, var.y: 16, var.z: 24} y_edges = np.linspace(0, 0.8, 16) z_edges = np.linspace(0, 1, 24) submesh_params = {"y_edges": y_edges, "z_edges": z_edges} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.UserSupplied2DSubMesh, submesh_params), } mesh_type = pybamm.Mesh # create mesh mesh = mesh_type(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative electrode"][0].edges[0], 0) self.assertEqual(mesh["positive electrode"][0].edges[-1], 1) # check internal boundary locations self.assertEqual(mesh["negative electrode"][0].edges[-1], mesh["separator"][0].edges[0]) self.assertEqual(mesh["positive electrode"][0].edges[0], mesh["separator"][0].edges[-1]) for domain in mesh: if domain == "current collector": # NOTE: only for degree 1 npts = var_pts[var.y] * var_pts[var.z] self.assertEqual(mesh[domain][0].npts, npts) else: self.assertEqual(len(mesh[domain][0].edges), len(mesh[domain][0].nodes) + 1)
def test_init_failure(self): submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } geometry = pybamm.battery_geometry(include_particles=False, current_collector_dimension=2) with self.assertRaises(KeyError): pybamm.Mesh(geometry, submesh_types, {}) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.y: 10, var.z: 10} # there are parameters in the variables that need to be processed with self.assertRaisesRegex( pybamm.DiscretisationError, "Parameter values have not yet been set for geometry", ): pybamm.Mesh(geometry, submesh_types, var_pts) lims = {var.x_n: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}} with self.assertRaises(pybamm.GeometryError): pybamm.ScikitUniform2DSubMesh(lims, None) lims = { var.x_n: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, var.x_p: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, } with self.assertRaises(pybamm.DomainError): pybamm.ScikitUniform2DSubMesh(lims, None) lims = { var.y: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, var.z: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, } npts = {var.y.id: 10, var.z.id: 10} var.z.coord_sys = "not cartesian" with self.assertRaises(pybamm.DomainError): pybamm.ScikitUniform2DSubMesh(lims, npts) var.z.coord_sys = "cartesian"
def test_mesh_creation(self): param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab width [m]": 0.1, "Negative tab centre y-coordinate [m]": 0.1, "Negative tab centre z-coordinate [m]": 0.5, "Positive tab width [m]": 0.1, "Positive tab centre y-coordinate [m]": 0.3, "Positive tab centre z-coordinate [m]": 0.5, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.battery_geometry(include_particles=False, current_collector_dimension=2) param.process_geometry(geometry) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 7, var.x_p: 12, var.y: 16, var.z: 24} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative electrode"].edges[0], 0) self.assertEqual(mesh["positive electrode"].edges[-1], 1) # check internal boundary locations self.assertEqual(mesh["negative electrode"].edges[-1], mesh["separator"].edges[0]) self.assertEqual(mesh["positive electrode"].edges[0], mesh["separator"].edges[-1]) for domain in mesh: if domain == "current collector": # NOTE: only for degree 1 npts = var_pts[var.y] * var_pts[var.z] self.assertEqual(mesh[domain].npts, npts) else: self.assertEqual(len(mesh[domain].edges), len(mesh[domain].nodes) + 1)
def test_init_failure(self): submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } geometry = pybamm.Geometryxp1DMacro(cc_dimension=2) with self.assertRaises(KeyError): pybamm.Mesh(geometry, submesh_types, {}) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.y: 10, var.z: 10} # there are parameters in the variables that need to be processed with self.assertRaises(NotImplementedError): pybamm.Mesh(geometry, submesh_types, var_pts) lims = {var.x_n: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}} with self.assertRaises(pybamm.GeometryError): pybamm.ScikitUniform2DSubMesh(lims, None, None) lims = { var.x_n: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, var.x_p: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, } with self.assertRaises(pybamm.DomainError): pybamm.ScikitUniform2DSubMesh(lims, None, None) lims = { var.y: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, var.z: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) }, } npts = {var.y.id: 10, var.z.id: 10} var.z.coord_sys = "not cartesian" with self.assertRaises(pybamm.DomainError): pybamm.ScikitUniform2DSubMesh(lims, npts, None) var.z.coord_sys = "cartesian"
def test_mesh_creation_no_parameters(self): r = pybamm.SpatialVariable("r", domain=["negative particle"], coord_sys="spherical polar") geometry = { "negative particle": { "primary": { r: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } } } submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh) } var_pts = {r: 20} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative particle"][0].edges[0], 0) self.assertEqual(mesh["negative particle"][0].edges[-1], 1) # check number of edges and nodes self.assertEqual(len(mesh["negative particle"][0].nodes), var_pts[r]) self.assertEqual( len(mesh["negative particle"][0].edges), len(mesh["negative particle"][0].nodes) + 1, )
def get_1p1d_mesh_for_testing( xpts=None, zpts=15, cc_submesh=pybamm.MeshGenerator(pybamm.Uniform1DSubMesh) ): geometry = pybamm.battery_geometry(current_collector_dimension=1) return get_mesh_for_testing( xpts=xpts, zpts=zpts, geometry=geometry, cc_submesh=cc_submesh )
def test_symmetric_mesh_creation_no_parameters_odd(self): r = pybamm.SpatialVariable("r", domain=["negative particle"], coord_sys="spherical polar") geometry = { "negative particle": { r: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } } submesh_params = {"side": "symmetric", "stretch": 1.5} submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.Exponential1DSubMesh, submesh_params) } var_pts = {r: 21} # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative particle"].edges[0], 0) self.assertEqual(mesh["negative particle"].edges[-1], 1) # check number of edges and nodes self.assertEqual(len(mesh["negative particle"].nodes), var_pts[r]) self.assertEqual( len(mesh["negative particle"].edges), len(mesh["negative particle"].nodes) + 1, )
def test_mesh_creation_no_parameters(self): r = pybamm.SpatialVariable("r", domain=["negative particle"], coord_sys="spherical polar") geometry = { "negative particle": { r: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } } edges = np.array([0, 0.3, 1]) submesh_params = {"edges": edges} submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.UserSupplied1DSubMesh, submesh_params) } var_pts = {r: len(edges) - 1} # create mesh mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative particle"].edges[0], 0) self.assertEqual(mesh["negative particle"].edges[-1], 1) # check number of edges and nodes self.assertEqual(len(mesh["negative particle"].nodes), var_pts[r]) self.assertEqual( len(mesh["negative particle"].edges), len(mesh["negative particle"].nodes) + 1, )
def get_1p1d_mesh_for_testing( xpts=None, zpts=15, cc_submesh=pybamm.MeshGenerator(pybamm.Uniform1DSubMesh) ): geometry = pybamm.Geometry("1+1D macro") return get_mesh_for_testing( xpts=xpts, zpts=zpts, geometry=geometry, cc_submesh=cc_submesh )
def errors(pts, function, method_options, bcs=None): domain = "test" x = pybamm.SpatialVariable("x", domain=domain) geometry = { domain: {"primary": {x: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}}} } submesh_types = {domain: pybamm.MeshGenerator(pybamm.Uniform1DSubMesh)} var_pts = {x: pts} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) spatial_methods = {"test": pybamm.FiniteVolume(method_options)} disc = pybamm.Discretisation(mesh, spatial_methods) var = pybamm.Variable("var", domain="test") left_extrap = pybamm.BoundaryValue(var, "left") right_extrap = pybamm.BoundaryValue(var, "right") if bcs: model = pybamm.BaseBatteryModel() bc_dict = {var: bcs} model.boundary_conditions = bc_dict disc.bcs = disc.process_boundary_conditions(model) submesh = mesh["test"] y, l_true, r_true = function(submesh[0].nodes) disc.set_variable_slices([var]) left_extrap_processed = disc.process_symbol(left_extrap) right_extrap_processed = disc.process_symbol(right_extrap) l_error = np.abs(l_true - left_extrap_processed.evaluate(None, y)) r_error = np.abs(r_true - right_extrap_processed.evaluate(None, y)) return l_error, r_error
def test_adding_1D_external_variable(self): model = pybamm.BaseModel() a = pybamm.Variable("a", domain=["test"]) b = pybamm.Variable("b", domain=["test"]) model.rhs = {a: a * b} model.boundary_conditions = { a: { "left": (0, "Dirichlet"), "right": (0, "Dirichlet") } } model.initial_conditions = {a: 0} model.external_variables = [b] model.variables = { "a": a, "b": b, "c": a * b, "grad b": pybamm.grad(b), "div grad b": pybamm.div(pybamm.grad(b)), } x = pybamm.SpatialVariable("x", domain="test", coord_sys="cartesian") geometry = { "test": { "primary": { x: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } } } submesh_types = {"test": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh)} var_pts = {x: 10} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) spatial_methods = {"test": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) self.assertEqual(disc.y_slices[a.id][0], slice(0, 10, None)) self.assertEqual(model.y_slices[a][0], slice(0, 10, None)) b_test = np.ones((10, 1)) np.testing.assert_array_equal( model.variables["b"].evaluate(inputs={"b": b_test}), b_test) # check that b is added to the boundary conditions model.bcs[b.id]["left"] model.bcs[b.id]["right"] # check that grad and div(grad ) produce the correct shapes self.assertEqual(model.variables["b"].shape_for_testing, (10, 1)) self.assertEqual(model.variables["grad b"].shape_for_testing, (11, 1)) self.assertEqual(model.variables["div grad b"].shape_for_testing, (10, 1))
def test_exceptions(self): edges = np.array([0, 0.3, 1]) submesh_params = {"edges": edges} mesh = pybamm.MeshGenerator(pybamm.SpectralVolume1DSubMesh, submesh_params) x_n = pybamm.standard_spatial_vars.x_n # error if npts+1 != len(edges) lims = {x_n: {"min": 0, "max": 1}} npts = {x_n.id: 10} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[0] not equal to edges[0] lims = {x_n: {"min": 0.1, "max": 1}} npts = {x_n.id: len(edges) - 1} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[-1] not equal to edges[-1] lims = {x_n: {"min": 0, "max": 10}} npts = {x_n.id: len(edges) - 1} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts)
def test_exceptions(self): var = pybamm.standard_spatial_vars lims = {var.y: {"min": 0, "max": 1}} y_edges = np.array([0, 0.3, 1]) z_edges = np.array([0, 0.3, 1]) submesh_params = {"y_edges": y_edges, "z_edges": z_edges} mesh = pybamm.MeshGenerator(pybamm.UserSupplied2DSubMesh, submesh_params) # test not enough lims with self.assertRaises(pybamm.GeometryError): mesh(lims, None) lims = {var.y: {"min": 0, "max": 1}, var.z: {"min": 0, "max": 1}} # error if len(edges) != npts npts = {var.y.id: 10, var.z.id: 3} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[0] not equal to edges[0] lims = {var.y: {"min": 0.1, "max": 1}, var.z: {"min": 0, "max": 1}} npts = {var.y.id: 3, var.z.id: 3} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if lims[-1] not equal to edges[-1] lims = {var.y: {"min": 0, "max": 1}, var.z: {"min": 0, "max": 1.3}} npts = {var.y.id: 3, var.z.id: 3} with self.assertRaises(pybamm.GeometryError): mesh(lims, npts) # error if different coordinate system lims = {var.y: {"min": 0, "max": 1}, var.r_n: {"min": 0, "max": 1}} npts = {var.y.id: 3, var.r_n.id: 3} with self.assertRaises(pybamm.DomainError): mesh(lims, npts) mesh = pybamm.MeshGenerator(pybamm.UserSupplied2DSubMesh) with self.assertRaisesRegex(pybamm.GeometryError, "User mesh requires"): mesh(None, None) submesh_params = {"y_edges": np.array([0, 0.3, 1])} mesh = pybamm.MeshGenerator(pybamm.UserSupplied2DSubMesh, submesh_params) with self.assertRaisesRegex(pybamm.GeometryError, "User mesh requires"): mesh(None, None)
def test_multiple_meshes_macro(self): param = pybamm.ParameterValues( values={ "Negative electrode thickness [m]": 0.1, "Separator thickness [m]": 0.2, "Positive electrode thickness [m]": 0.3, "Electrode height [m]": 0.4, "Negative tab centre z-coordinate [m]": 0.0, "Positive tab centre z-coordinate [m]": 0.4, }) geometry = pybamm.Geometry("1+1D macro") param.process_geometry(geometry) # provide mesh properties var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 15, var.x_p: 20, var.z: 5} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.SubMesh0D), } mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check types self.assertIsInstance(mesh["negative electrode"], list) self.assertIsInstance(mesh["separator"], list) self.assertIsInstance(mesh["positive electrode"], list) self.assertEqual(len(mesh["negative electrode"]), 5) self.assertEqual(len(mesh["separator"]), 5) self.assertEqual(len(mesh["positive electrode"]), 5) for i in range(5): self.assertIsInstance(mesh["negative electrode"][i], pybamm.Uniform1DSubMesh) self.assertIsInstance(mesh["separator"][i], pybamm.Uniform1DSubMesh) self.assertIsInstance(mesh["positive electrode"][i], pybamm.Uniform1DSubMesh) self.assertEqual(mesh["negative electrode"][i].npts, 10) self.assertEqual(mesh["separator"][i].npts, 15) self.assertEqual(mesh["positive electrode"][i].npts, 20)
def get_2p1d_mesh_for_testing( xpts=None, ypts=15, zpts=15, cc_submesh=pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), ): geometry = pybamm.Geometry("2+1D macro") return get_mesh_for_testing( xpts=xpts, zpts=zpts, geometry=geometry, cc_submesh=cc_submesh )
def get_mesh_for_testing( xpts=None, rpts=10, ypts=15, zpts=15, geometry=None, cc_submesh=None, order=2 ): param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab width [m]": 0.1, "Negative tab centre y-coordinate [m]": 0.1, "Negative tab centre z-coordinate [m]": 0.0, "Positive tab width [m]": 0.1, "Positive tab centre y-coordinate [m]": 0.3, "Positive tab centre z-coordinate [m]": 0.5, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, } ) if geometry is None: geometry = pybamm.battery_geometry() param.process_geometry(geometry) submesh_types = { "negative electrode": pybamm.MeshGenerator( pybamm.SpectralVolume1DSubMesh, {"order": order} ), "separator": pybamm.MeshGenerator( pybamm.SpectralVolume1DSubMesh, {"order": order} ), "positive electrode": pybamm.MeshGenerator( pybamm.SpectralVolume1DSubMesh, {"order": order} ), "negative particle": pybamm.MeshGenerator( pybamm.SpectralVolume1DSubMesh, {"order": order} ), "positive particle": pybamm.MeshGenerator( pybamm.SpectralVolume1DSubMesh, {"order": order} ), "current collector": pybamm.MeshGenerator(pybamm.SubMesh0D), } if cc_submesh: submesh_types["current collector"] = cc_submesh if xpts is None: xn_pts, xs_pts, xp_pts = 40, 25, 35 else: xn_pts, xs_pts, xp_pts = xpts, xpts, xpts var = pybamm.standard_spatial_vars var_pts = { var.x_n: xn_pts, var.x_s: xs_pts, var.x_p: xp_pts, var.r_n: rpts, var.r_p: rpts, var.y: ypts, var.z: zpts, } return pybamm.Mesh(geometry, submesh_types, var_pts)
def get_2p1d_mesh_for_testing( xpts=None, ypts=15, zpts=15, include_particles=True, cc_submesh=pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), ): geometry = pybamm.battery_geometry( include_particles=include_particles, current_collector_dimension=2 ) return get_mesh_for_testing( xpts=xpts, zpts=zpts, geometry=geometry, cc_submesh=cc_submesh )
def test_tab_error(self): # set variables and submesh types var = pybamm.standard_spatial_vars var_pts = {var.x_n: 2, var.x_s: 2, var.x_p: 2, var.y: 64, var.z: 64} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } mesh_type = pybamm.Mesh # set base parameters param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab width [m]": 0.1, "Negative tab centre y-coordinate [m]": 0.1, "Negative tab centre z-coordinate [m]": 0.5, "Positive tab centre y-coordinate [m]": 10, "Positive tab centre z-coordinate [m]": 10, "Positive tab width [m]": 0.1, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, }) # check error raised if tab not on boundary geometry = pybamm.Geometryxp1DMacro(cc_dimension=2) param.process_geometry(geometry) with self.assertRaises(pybamm.GeometryError): mesh_type(geometry, submesh_types, var_pts)
def test_1plus1D_tabs_right_left(self): param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab centre z-coordinate [m]": 0.5, "Positive tab centre z-coordinate [m]": 0.0, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.Geometryxp1DMacro(cc_dimension=1) param.process_geometry(geometry) var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_s: 7, var.x_p: 12, var.z: 24} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), } mesh_type = pybamm.Mesh # create mesh mesh = mesh_type(geometry, submesh_types, var_pts) # negative tab should be "right" self.assertEqual(mesh["current collector"][0].tabs["negative tab"], "right") # positive tab should be "left" self.assertEqual(mesh["current collector"][0].tabs["positive tab"], "left")
def test_tab_left_right(self): # set variables and submesh types var = pybamm.standard_spatial_vars var_pts = {var.x_n: 2, var.x_s: 2, var.x_p: 2, var.y: 64, var.z: 64} submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.ScikitUniform2DSubMesh), } # set base parameters param = pybamm.ParameterValues( values={ "Electrode width [m]": 0.4, "Electrode height [m]": 0.5, "Negative tab width [m]": 0.1, "Negative tab centre y-coordinate [m]": 0.0, "Negative tab centre z-coordinate [m]": 0.25, "Positive tab centre y-coordinate [m]": 0.4, "Positive tab centre z-coordinate [m]": 0.25, "Positive tab width [m]": 0.1, "Negative electrode thickness [m]": 0.3, "Separator thickness [m]": 0.3, "Positive electrode thickness [m]": 0.3, }) # check mesh can be built geometry = pybamm.battery_geometry(include_particles=False, current_collector_dimension=2) param.process_geometry(geometry) pybamm.Mesh(geometry, submesh_types, var_pts)
def test_multiple_meshes(self): param = pybamm.ParameterValues( values={ "Negative electrode thickness [m]": 0.1, "Separator thickness [m]": 0.2, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.Geometry("1+1D micro") param.process_geometry(geometry) # provide mesh properties var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.x_p: 10, var.r_n: 5, var.r_p: 6} submesh_types = { "negative particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.SubMesh0D), } mesh = pybamm.Mesh(geometry, submesh_types, var_pts) # check types self.assertIsInstance(mesh["negative particle"], list) self.assertIsInstance(mesh["positive particle"], list) self.assertEqual(len(mesh["negative particle"]), 10) self.assertEqual(len(mesh["positive particle"]), 10) for i in range(10): self.assertIsInstance(mesh["negative particle"][i], pybamm.Uniform1DSubMesh) self.assertIsInstance(mesh["positive particle"][i], pybamm.Uniform1DSubMesh) self.assertEqual(mesh["negative particle"][i].npts, 5) self.assertEqual(mesh["positive particle"][i].npts, 6)
def default_submesh_types(self): base_submeshes = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "negative particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), } if self.options["dimensionality"] == 0: base_submeshes["current collector"] = pybamm.MeshGenerator(pybamm.SubMesh0D) elif self.options["dimensionality"] == 1: base_submeshes["current collector"] = pybamm.MeshGenerator( pybamm.Uniform1DSubMesh ) elif self.options["dimensionality"] == 2: base_submeshes["current collector"] = pybamm.MeshGenerator( pybamm.ScikitUniform2DSubMesh ) return base_submeshes
def test_mesh_sizes(self): param = pybamm.ParameterValues( values={ "Negative electrode thickness [m]": 0.1, "Separator thickness [m]": 0.2, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.Geometry1DMacro() param.process_geometry(geometry) # provide mesh properties var = pybamm.standard_spatial_vars var_pts = { var.x_n: 10, var.x_s: 10, var.x_p: 12, var.r_n: 5, var.r_p: 6 } submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "negative particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.SubMesh0D), } mesh_type = pybamm.Mesh # create mesh mesh = mesh_type(geometry, submesh_types, var_pts) var_id_pts = {var.id: pts for var, pts in var_pts.items()} self.assertEqual(mesh["negative electrode"][0].npts, var_id_pts[var.x_n.id]) self.assertEqual(mesh["separator"][0].npts, var_id_pts[var.x_s.id]) self.assertEqual(mesh["positive electrode"][0].npts, var_id_pts[var.x_p.id]) self.assertEqual( len(mesh["negative electrode"][0].edges) - 1, var_id_pts[var.x_n.id]) self.assertEqual( len(mesh["separator"][0].edges) - 1, var_id_pts[var.x_s.id]) self.assertEqual( len(mesh["positive electrode"][0].edges) - 1, var_id_pts[var.x_p.id])
def test_unimplemented_meshes(self): var = pybamm.standard_spatial_vars var_pts = {var.x_n: 10, var.y: 10} geometry = { "negative electrode": { var.x_n: { "min": 0, "max": 1 }, var.y: { "min": 0, "max": 1 }, } } submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh) } with self.assertRaises(pybamm.GeometryError): pybamm.Mesh(geometry, submesh_types, var_pts)
def test_mesh_creation(self): param = pybamm.ParameterValues( values={ "Negative electrode thickness [m]": 0.1, "Separator thickness [m]": 0.2, "Positive electrode thickness [m]": 0.3, }) geometry = pybamm.Geometry1DMacro() param.process_geometry(geometry) var = pybamm.standard_spatial_vars var_pts = { var.x_n: 10, var.x_s: 10, var.x_p: 12, var.r_n: 5, var.r_p: 6 } submesh_types = { "negative electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "separator": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive electrode": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "negative particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "positive particle": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), "current collector": pybamm.MeshGenerator(pybamm.SubMesh0D), } mesh_type = pybamm.Mesh # create mesh mesh = mesh_type(geometry, submesh_types, var_pts) # check boundary locations self.assertEqual(mesh["negative electrode"][0].edges[0], 0) self.assertEqual(mesh["positive electrode"][0].edges[-1], 1) # check internal boundary locations self.assertEqual(mesh["negative electrode"][0].edges[-1], mesh["separator"][0].edges[0]) self.assertEqual(mesh["positive electrode"][0].edges[0], mesh["separator"][0].edges[-1]) for domain in mesh: if domain != "current collector": self.assertEqual(len(mesh[domain][0].edges), len(mesh[domain][0].nodes) + 1)