Esempio n. 1
0
    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 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)
Esempio n. 3
0
    def test_init_failure(self):
        geometry = pybamm.battery_geometry()
        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),
        }
        with self.assertRaisesRegex(KeyError, "Points not given"):
            pybamm.Mesh(geometry, submesh_types, {})

        var = pybamm.standard_spatial_vars
        var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 12}
        geometry = pybamm.battery_geometry(current_collector_dimension=1)
        with self.assertRaisesRegex(KeyError, "Points not given"):
            pybamm.Mesh(geometry, submesh_types, var_pts)

        # Not processing geometry parameters
        geometry = pybamm.battery_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),
        }

        with self.assertRaisesRegex(pybamm.DiscretisationError,
                                    "Parameter values"):
            pybamm.Mesh(geometry, submesh_types, var_pts)

        # Geometry has an unrecognized variable type
        var = pybamm.standard_spatial_vars
        geometry["negative electrode"] = {
            var.x_n: {
                "min": 0,
                "max": pybamm.Variable("var")
            }
        }
        with self.assertRaisesRegex(NotImplementedError, "for symbol var"):
            pybamm.Mesh(geometry, submesh_types, var_pts)
Esempio n. 4
0
    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"
Esempio n. 5
0
    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"
Esempio n. 6
0
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
Esempio n. 7
0
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 test_processed_variable_1D_unknown_domain(self):
        x = pybamm.SpatialVariable("x",
                                   domain="SEI layer",
                                   coord_sys="cartesian")
        geometry = pybamm.Geometry({
            "SEI layer": {
                x: {
                    "min": pybamm.Scalar(0),
                    "max": pybamm.Scalar(1)
                }
            }
        })

        submesh_types = {"SEI layer": pybamm.Uniform1DSubMesh}
        var_pts = {x: 100}
        mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

        nt = 100

        y_sol = np.zeros((var_pts[x], nt))
        solution = pybamm.Solution(
            np.linspace(0, 1, nt),
            y_sol,
            pybamm.BaseModel(),
            {},
            np.linspace(0, 1, 1),
            np.zeros((var_pts[x])),
            "test",
        )

        c = pybamm.StateVector(slice(0, var_pts[x]), domain=["SEI layer"])
        c.mesh = mesh["SEI layer"]
        c_casadi = to_casadi(c, y_sol)
        pybamm.ProcessedVariable([c], [c_casadi], solution, warn=False)
Esempio n. 9
0
    def test_parameters_defaults_lead_acid(self):
        # Tests on how the parameters interact
        param = pybamm.Parameters(chemistry="lead-acid")
        mesh = pybamm.Mesh(param, 10)
        param.set_mesh(mesh)

        # Dimensionless lengths sum to 1
        self.assertAlmostEqual(
            param.geometric.ln + param.geometric.ls + param.geometric.lp, 1, places=10
        )
        # Diffusional C-rate should be smaller than C-rate
        self.assertLess(param.electrolyte.Cd, param.electrical.Crate)

        # # Dimensionless electrode conductivities should be large
        self.assertGreater(param.neg_electrode.iota_s, 10)
        self.assertGreater(param.pos_electrode.iota_s, 10)
        # # Dimensionless double-layer capacity should be small
        self.assertLess(param.neg_reactions.gamma_dl, 1e-3)
        self.assertLess(param.pos_reactions.gamma_dl, 1e-3)
        # # Volume change positive in negative electrode and negative in positive
        # # electrode
        self.assertGreater(param.neg_volume_changes.DeltaVsurf, 0)
        self.assertLess(param.pos_volume_changes.DeltaVsurf, 0)
        # # Excluded volume fraction should be less than 1
        self.assertLess(param.lead_acid_misc.pi_os, 1e-4)
Esempio n. 10
0
    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
        )
Esempio n. 11
0
    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,
        )
Esempio n. 12
0
    def test_changing_grid(self):
        model = pybamm.lithium_ion.SPM()
        solver = pybamm.IDAKLUSolver()

        # load parameter values and geometry
        geometry = model.default_geometry
        param = model.default_parameter_values

        # Process parameters
        param.process_model(model)
        param.process_geometry(geometry)

        # Calculate time for each solver and each number of grid points
        var = pybamm.standard_spatial_vars
        t_eval = np.linspace(0, 3600, 100)
        for npts in [100, 200]:
            # discretise
            var_pts = {
                spatial_var: npts
                for spatial_var in [var.x_n, var.x_s, var.x_p, var.r_n, var.r_p]
            }
            mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts)
            disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
            model_disc = disc.process_model(model, inplace=False)

            # solve
            solver.solve(model_disc, t_eval)
Esempio n. 13
0
    def test_loqs_spm_base(self):
        t_eval = np.linspace(0, 0.01, 2)

        # SPM
        for model in [pybamm.lithium_ion.SPM(), pybamm.lead_acid.LOQS()]:
            geometry = model.default_geometry
            param = model.default_parameter_values
            param.process_model(model)
            param.process_geometry(geometry)
            mesh = pybamm.Mesh(
                geometry, model.default_submesh_types, model.default_var_pts
            )
            disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
            disc.process_model(model)
            solver = model.default_solver
            solution = solver.solve(model, t_eval)
            pybamm.QuickPlot(model, mesh, solution)

            # test quick plot of particle for spm
            if model.name == "Single Particle Model":
                output_variables = [
                    "X-averaged negative particle concentration [mol.m-3]",
                    "X-averaged positive particle concentration [mol.m-3]",
                ]
                pybamm.QuickPlot(model, mesh, solution, output_variables)
Esempio n. 14
0
    def __init__(self,
                 model,
                 param=None,
                 mesh=None,
                 solver=None,
                 name="unnamed"):
        # Defaults
        if param is None:
            param = pybamm.Parameters()
        if mesh is None:
            mesh = pybamm.Mesh(param)
        if solver is None:
            solver = pybamm.Solver()

        # Assign attributes
        self.model = model
        self.param = param
        self.mesh = mesh
        self.solver = solver
        self.name = name

        # Initialise simulation to prepare for solving
        # Set mesh dependent parameters
        self.param.set_mesh(self.mesh)

        # Create operators from solver
        self.operators = self.solver.operators(self.mesh)

        # Assign param, operators and mesh as model attributes
        self.model.set_simulation(self.param, self.operators, self.mesh)
Esempio n. 15
0
    def test_processed_variable_1D_unknown_domain(self):
        x = pybamm.SpatialVariable("x",
                                   domain="SEI layer",
                                   coord_sys="cartesian")
        geometry = pybamm.Geometry()
        geometry.add_domain(
            "SEI layer",
            {
                "primary": {
                    x: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.Scalar(1)
                    }
                }
            },
        )

        submesh_types = {"SEI layer": pybamm.Uniform1DSubMesh}
        var_pts = {x: 100}
        mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

        nt = 100

        solution = pybamm.Solution(
            np.linspace(0, 1, nt),
            np.zeros((var_pts[x], nt)),
            np.linspace(0, 1, 1),
            np.zeros((var_pts[x])),
            "test",
        )

        c = pybamm.StateVector(slice(0, var_pts[x]), domain=["SEI layer"])
        c.mesh = mesh["SEI layer"]
        pybamm.ProcessedVariable(c, solution)
Esempio n. 16
0
def prepare_model(model):
    geometry = model.default_geometry

    # load parameter values and process model and geometry
    chemistry = pybamm.parameter_sets.Marquis2019
    param = pybamm.ParameterValues(chemistry=chemistry)
    param.process_model(model)
    param.process_geometry(geometry)

    # set mesh
    var = pybamm.standard_spatial_vars
    var_pts = {
        var.x_n: 20,
        var.x_s: 20,
        var.x_p: 20,
        var.r_n: 30,
        var.r_p: 30,
        var.y: 10,
        var.z: 10,
    }
    mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts)

    # discretise model
    disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
    disc.process_model(model)
Esempio n. 17
0
    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))
Esempio n. 18
0
    def test_process_parameters_and_discretise(self):
        model = pybamm.lithium_ion.SPM()
        # Set up geometry and parameters
        geometry = model.default_geometry
        parameter_values = model.default_parameter_values
        parameter_values.process_geometry(geometry)
        # Set up discretisation
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        # Process expression
        c = pybamm.Parameter("Negative electrode thickness [m]") * pybamm.Variable(
            "X-averaged negative particle concentration",
            domain="negative particle",
            auxiliary_domains={"secondary": "current collector"},
        )
        processed_c = model.process_parameters_and_discretise(c, parameter_values, disc)
        self.assertIsInstance(processed_c, pybamm.Multiplication)
        self.assertIsInstance(processed_c.left, pybamm.Scalar)
        self.assertIsInstance(processed_c.right, pybamm.StateVector)
        # Process flux manually and check result against flux computed in particle
        # submodel
        c_n = model.variables["X-averaged negative particle concentration"]
        T = pybamm.PrimaryBroadcast(
            model.variables["X-averaged negative electrode temperature"],
            ["negative particle"],
        )
        D = model.param.D_n(c_n, T)
        N = -D * pybamm.grad(c_n)

        flux_1 = model.process_parameters_and_discretise(N, parameter_values, disc)
        flux_2 = model.variables["X-averaged negative particle flux"]
        param_flux_2 = parameter_values.process_symbol(flux_2)
        disc_flux_2 = disc.process_symbol(param_flux_2)
        self.assertEqual(flux_1.id, disc_flux_2.id)
Esempio n. 19
0
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)
Esempio n. 20
0
    def test_grad_div_1D_FV_basic(self):
        param = pybamm.Parameters()
        mesh = pybamm.Mesh(param, target_npts=50)

        y = np.ones_like(mesh.x.centres)
        N = np.ones_like(mesh.x.edges)
        yn = np.ones_like(mesh.xn.centres)
        Nn = np.ones_like(mesh.xn.edges)
        yp = np.ones_like(mesh.xp.centres)
        Np = np.ones_like(mesh.xp.edges)

        # Get all operators
        operators = pybamm.Operators("Finite Volumes", mesh)

        # Check output shape
        self.assertEqual(operators.x.grad(y).shape[0], y.shape[0] - 1)
        self.assertEqual(operators.x.div(N).shape[0], N.shape[0] - 1)
        self.assertEqual(operators.xn.grad(yn).shape[0], yn.shape[0] - 1)
        self.assertEqual(operators.xn.div(Nn).shape[0], Nn.shape[0] - 1)
        self.assertEqual(operators.xp.grad(yp).shape[0], yp.shape[0] - 1)
        self.assertEqual(operators.xp.div(Np).shape[0], Np.shape[0] - 1)

        # Check grad and div are both zero
        self.assertEqual(np.linalg.norm(operators.x.grad(y)), 0)
        self.assertEqual(np.linalg.norm(operators.x.div(N)), 0)
        self.assertEqual(np.linalg.norm(operators.xn.grad(yn)), 0)
        self.assertEqual(np.linalg.norm(operators.xn.div(Nn)), 0)
        self.assertEqual(np.linalg.norm(operators.xp.grad(yp)), 0)
        self.assertEqual(np.linalg.norm(operators.xp.div(Np)), 0)
Esempio n. 21
0
    def build(self, check_model=True):
        """
        A method to build the model into a system of matrices and vectors suitable for
        performing numerical computations. If the model has already been built or
        solved then this function will have no effect. If you want to rebuild,
        first use "reset()". This method will automatically set the parameters
        if they have not already been set.

        Parameters
        ----------
        check_model : bool, optional
            If True, model checks are performed after discretisation (see
            :meth:`pybamm.Discretisation.process_model`). Default is True.
        """

        if self.built_model:
            return None
        elif self.model.is_discretised:
            self._model_with_set_params = self.model
            self._built_model = self.model
        else:
            self.set_parameters()
            self._mesh = pybamm.Mesh(self._geometry, self._submesh_types, self._var_pts)
            self._disc = pybamm.Discretisation(self._mesh, self._spatial_methods)
            self._built_model = self._disc.process_model(
                self._model_with_set_params, inplace=False, check_model=check_model
            )
Esempio n. 22
0
 def __init__(
     self,
     model,
     parameter_values=None,
     geometry=None,
     submesh_types=None,
     var_pts=None,
     spatial_methods=None,
     solver=None,
 ):
     self.model = model
     # Set parameters, geometry, spatial methods etc
     # The code below is equivalent to:
     #    if parameter_values is None:
     #       self.parameter_values = model.default_parameter_values
     #    else:
     #       self.parameter_values = parameter_values
     self.parameter_values = parameter_values or model.default_parameter_values
     geometry = geometry or model.default_geometry
     submesh_types = submesh_types or model.default_submesh_types
     var_pts = var_pts or model.default_var_pts
     spatial_methods = spatial_methods or model.default_spatial_methods
     self.solver = solver or model.default_solver
     # Process geometry
     self.parameter_values.process_geometry(geometry)
     # Set discretisation
     mesh = pybamm.Mesh(geometry, submesh_types, var_pts)
     self.disc = pybamm.Discretisation(mesh, spatial_methods)
Esempio n. 23
0
    def test_plot_1plus1D_spme(self):
        spm = pybamm.lithium_ion.SPMe(
            {"current collector": "potential pair", "dimensionality": 1}
        )
        geometry = spm.default_geometry
        param = spm.default_parameter_values
        param.process_model(spm)
        param.process_geometry(geometry)
        var = pybamm.standard_spatial_vars
        var_pts = {var.x_n: 5, var.x_s: 5, var.x_p: 5, var.r_n: 5, var.r_p: 5, var.z: 5}
        mesh = pybamm.Mesh(geometry, spm.default_submesh_types, var_pts)
        disc_spm = pybamm.Discretisation(mesh, spm.default_spatial_methods)
        disc_spm.process_model(spm)
        t_eval = np.linspace(0, 100, 10)
        solution = spm.default_solver.solve(spm, t_eval)

        # check 2D (x,z space) variables update properly for different time units
        # Note: these should be the transpose of the entries in the processed variable
        c_e = solution["Electrolyte concentration [mol.m-3]"]

        for unit, scale in zip(["seconds", "minutes", "hours"], [1, 60, 3600]):
            quick_plot = pybamm.QuickPlot(
                solution, ["Electrolyte concentration [mol.m-3]"], time_unit=unit
            )
            quick_plot.plot(0)
            qp_data = quick_plot.plots[("Electrolyte concentration [mol.m-3]",)][0][1]
            c_e_eval = c_e(t_eval[0], x=c_e.first_dim_pts, z=c_e.second_dim_pts)
            np.testing.assert_array_almost_equal(qp_data.T, c_e_eval)
            quick_plot.slider_update(t_eval[-1] / scale)
            qp_data = quick_plot.plots[("Electrolyte concentration [mol.m-3]",)][0][1]
            c_e_eval = c_e(t_eval[-1], x=c_e.first_dim_pts, z=c_e.second_dim_pts)
            np.testing.assert_array_almost_equal(qp_data.T, c_e_eval)

        pybamm.close_plots()
Esempio n. 24
0
    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,
        )
Esempio n. 25
0
    def test_inner(self):
        model = pybamm.lithium_ion.BaseModel()

        phi_s = pybamm.standard_variables.phi_s_n
        i = pybamm.grad(phi_s)

        model.rhs = {phi_s: pybamm.inner(i, i)}
        model.boundary_conditions = {
            phi_s: {
                "left": (pybamm.Scalar(0), "Neumann"),
                "right": (pybamm.Scalar(0), "Neumann"),
            }
        }
        model.initial_conditions = {phi_s: pybamm.Scalar(0)}

        model.variables = {"inner": pybamm.inner(i, i)}

        # load parameter values and process model and geometry
        param = model.default_parameter_values
        geometry = model.default_geometry
        param.process_model(model)
        param.process_geometry(geometry)

        # set mesh
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)

        # discretise model
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)

        # check doesn't evaluate on edges anymore
        self.assertEqual(model.variables["inner"].evaluates_on_edges("primary"), False)
Esempio n. 26
0
def init_model():
    # load model
    model = pb.lithium_ion.SPMe()

    # create geometry
    geometry = model.default_geometry

    # load parameter values and process model and geometry
    param = model.default_parameter_values

    param.update({
        "Current function [A]": current_function,
    })
    param.update({"Current": "[input]"}, check_already_exists=False)
    param.process_model(model)
    param.process_geometry(geometry)

    # set mesh
    mesh = pb.Mesh(geometry, model.default_submesh_types,
                   model.default_var_pts)

    # discretise model
    disc = pb.Discretisation(mesh, model.default_spatial_methods)
    disc.process_model(model)

    return model
Esempio n. 27
0
def simulation(models, t_eval, extra_parameter_values=None, disc_only=False):

    # create geometry
    geometry = models[-1].default_geometry

    # load parameter values and process models and geometry
    param = models[0].default_parameter_values
    extra_parameter_values = extra_parameter_values or {}
    param.update(extra_parameter_values)
    for model in models:
        param.process_model(model)
    param.process_geometry(geometry)

    # set mesh
    var = pybamm.standard_spatial_vars
    var_pts = {var.x_n: 25, var.x_s: 41, var.x_p: 34}
    mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts)

    # discretise models
    for model in models:
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)

    if disc_only:
        return model, mesh

    # solve model
    solutions = [None] * len(models)
    for i, model in enumerate(models):
        solution = model.default_solver.solve(model, t_eval)
        solutions[i] = solution

    return models, mesh, solutions
Esempio n. 28
0
    def test_append_external_variables(self):
        model = pybamm.lithium_ion.SPM({
            "thermal":
            "lumped",
            "external submodels": ["thermal", "negative particle"],
        })
        # create geometry
        geometry = model.default_geometry

        # load parameter values and process model and geometry
        param = model.default_parameter_values
        param.process_model(model)
        param.process_geometry(geometry)

        # set mesh
        mesh = pybamm.Mesh(geometry, model.default_submesh_types,
                           model.default_var_pts)

        # discretise model
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)

        # solve model
        solver = model.default_solver

        var = pybamm.standard_spatial_vars
        Nr = model.default_var_pts[var.r_n]

        T_av = 0
        c_s_n_av = np.ones((Nr, 1)) * 0.6
        external_variables = {
            "Volume-averaged cell temperature": T_av,
            "X-averaged negative particle concentration": c_s_n_av,
        }

        # Step
        dt = 0.1
        sol_step = None
        for _ in range(5):
            sol_step = solver.step(sol_step,
                                   model,
                                   dt,
                                   external_variables=external_variables)
        np.testing.assert_array_equal(
            sol_step.all_inputs[0]["Volume-averaged cell temperature"], 0)
        np.testing.assert_array_equal(
            sol_step.all_inputs[0]
            ["X-averaged negative particle concentration"], 0.6)

        # Solve
        t_eval = np.linspace(0, 3600)
        sol = solver.solve(model,
                           t_eval,
                           external_variables=external_variables)
        np.testing.assert_array_equal(
            sol.all_inputs[0]["Volume-averaged cell temperature"], 0)
        np.testing.assert_array_equal(
            sol.all_inputs[0]["X-averaged negative particle concentration"],
            0.6)
Esempio n. 29
0
 def setUp(self):
     self.model = pybamm.ElectrolyteCurrentModel()
     self.param = pybamm.Parameters()
     target_npts = 3
     tsteps = 10
     tend = 1
     self.mesh = pybamm.Mesh(self.param, target_npts, tsteps=tsteps, tend=tend)
     self.param.set_mesh(self.mesh)
Esempio n. 30
0
    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}

        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),
        }

        # 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)