def test_processed_variable_ode_pde_solution(self): # without space model = pybamm.BaseBatteryModel() c = pybamm.Variable("conc") model.rhs = {c: -c} model.initial_conditions = {c: 1} model.variables = {"c": c} modeltest = tests.StandardModelTest(model) modeltest.test_all() t_sol, y_sol = modeltest.solution.t, modeltest.solution.y processed_vars = pybamm.post_process_variables(model.variables, t_sol, y_sol) np.testing.assert_array_almost_equal(processed_vars["c"](t_sol), np.exp(-t_sol)) # with space # set up and solve model whole_cell = ["negative electrode", "separator", "positive electrode"] model = pybamm.BaseBatteryModel() c = pybamm.Variable("conc", domain=whole_cell) c_s = pybamm.Variable( "particle conc", domain="negative particle", auxiliary_domains={"secondary": ["negative electrode"]}, ) model.rhs = {c: -c, c_s: 1 - c_s} model.initial_conditions = {c: 1, c_s: 0.5} model.boundary_conditions = { c: { "left": (0, "Neumann"), "right": (0, "Neumann") }, c_s: { "left": (0, "Neumann"), "right": (0, "Neumann") }, } model.variables = { "c": c, "N": pybamm.grad(c), "c_s": c_s, "N_s": pybamm.grad(c_s), } modeltest = tests.StandardModelTest(model) modeltest.test_all() # set up testing t_sol, y_sol = modeltest.solution.t, modeltest.solution.y x = pybamm.SpatialVariable("x", domain=whole_cell) x_sol = modeltest.disc.process_symbol(x).entries[:, 0] processed_vars = pybamm.post_process_variables(model.variables, t_sol, y_sol, modeltest.disc.mesh) # test np.testing.assert_array_almost_equal( processed_vars["c"](t_sol, x_sol), np.ones_like(x_sol)[:, np.newaxis] * np.exp(-t_sol), )
def test_default_geometry(self): var = pybamm.standard_spatial_vars model = pybamm.BaseBatteryModel({"dimensionality": 0}) self.assertEqual( model.default_geometry["current collector"][var.z]["position"], 1 ) model = pybamm.BaseBatteryModel({"dimensionality": 1}) self.assertEqual(model.default_geometry["current collector"][var.z]["min"], 0) model = pybamm.BaseBatteryModel({"dimensionality": 2}) self.assertEqual(model.default_geometry["current collector"][var.y]["min"], 0)
def test_default_parameters(self): # check parameters are read in ok model = pybamm.BaseBatteryModel() self.assertEqual( model.default_parameter_values["Reference temperature [K]"], 298.15) # change path and try again cwd = os.getcwd() os.chdir("..") model = pybamm.BaseBatteryModel() self.assertEqual( model.default_parameter_values["Reference temperature [K]"], 298.15) os.chdir(cwd)
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_default_spatial_methods(self): model = pybamm.BaseBatteryModel({"dimensionality": 0}) self.assertTrue( isinstance( model.default_spatial_methods["current collector"], pybamm.ZeroDimensionalSpatialMethod, )) model = pybamm.BaseBatteryModel({"dimensionality": 1}) self.assertTrue( isinstance(model.default_spatial_methods["current collector"], pybamm.FiniteVolume)) model = pybamm.BaseBatteryModel({"dimensionality": 2}) self.assertTrue( isinstance( model.default_spatial_methods["current collector"], pybamm.ScikitFiniteElement, ))
def test_default_var_pts(self): 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, } model = pybamm.BaseBatteryModel({"dimensionality": 0}) self.assertDictEqual(var_pts, model.default_var_pts) var_pts.update({var.x_n: 10, var.x_s: 10, var.x_p: 10}) model = pybamm.BaseBatteryModel({"dimensionality": 2}) self.assertDictEqual(var_pts, model.default_var_pts)
def test_default_submesh_types(self): model = pybamm.BaseBatteryModel({"dimensionality": 0}) self.assertTrue( issubclass( model.default_submesh_types["current collector"].submesh_type, pybamm.SubMesh0D, )) model = pybamm.BaseBatteryModel({"dimensionality": 1}) self.assertTrue( issubclass( model.default_submesh_types["current collector"].submesh_type, pybamm.Uniform1DSubMesh, )) model = pybamm.BaseBatteryModel({"dimensionality": 2}) self.assertTrue( issubclass( model.default_submesh_types["current collector"].submesh_type, pybamm.ScikitUniform2DSubMesh, ))
def test_default_solver(self): model = pybamm.BaseBatteryModel() self.assertIsInstance( model.default_solver, (pybamm.ScipySolver, pybamm.ScikitsOdeSolver) ) # check that default_solver gives you a new solver, not an internal object solver = model.default_solver solver = pybamm.BaseModel() self.assertIsInstance( model.default_solver, (pybamm.ScipySolver, pybamm.ScikitsOdeSolver) ) self.assertIsInstance(solver, pybamm.BaseModel)
def test_default_solver(self): model = pybamm.BaseBatteryModel() self.assertIsInstance(model.default_solver, pybamm.CasadiSolver) # check that default_solver gives you a new solver, not an internal object solver = model.default_solver solver = pybamm.BaseModel() self.assertIsInstance(model.default_solver, pybamm.CasadiSolver) self.assertIsInstance(solver, pybamm.BaseModel) # check that adding algebraic variables gives algebraic solver a = pybamm.Variable("a") model.algebraic = {a: a - 1} self.assertIsInstance(model.default_solver, pybamm.CasadiAlgebraicSolver)
def test_bad_options(self): with self.assertRaisesRegex(pybamm.OptionError, "option"): pybamm.BaseBatteryModel({"bad option": "bad option"}) with self.assertRaisesRegex(pybamm.OptionError, "current collector model"): pybamm.BaseBatteryModel({"current collector": "bad current collector"}) with self.assertRaisesRegex(pybamm.OptionError, "thermal model"): pybamm.BaseBatteryModel({"thermal": "bad thermal"}) with self.assertRaisesRegex( pybamm.OptionError, "Dimension of current collectors" ): pybamm.BaseBatteryModel({"dimensionality": 5}) with self.assertRaisesRegex(pybamm.OptionError, "surface form"): pybamm.BaseBatteryModel({"surface form": "bad surface form"}) with self.assertRaisesRegex(pybamm.OptionError, "particle model"): pybamm.BaseBatteryModel({"particle": "bad particle"}) with self.assertRaisesRegex(pybamm.OptionError, "option set external"): pybamm.BaseBatteryModel({"current collector": "set external potential"}) with self.assertRaisesRegex(pybamm.OptionError, "operating mode"): pybamm.BaseBatteryModel({"operating mode": "bad operating mode"})
def test_bad_options(self): with self.assertRaisesRegex(pybamm.OptionError, "Option"): pybamm.BaseBatteryModel({"bad option": "bad option"}) with self.assertRaisesRegex(pybamm.OptionError, "current collector model"): pybamm.BaseBatteryModel({"current collector": "bad current collector"}) with self.assertRaisesRegex(pybamm.OptionError, "thermal model"): pybamm.BaseBatteryModel({"thermal": "bad thermal"}) with self.assertRaisesRegex( pybamm.OptionError, "Dimension of current collectors" ): pybamm.BaseBatteryModel({"dimensionality": 5}) with self.assertRaisesRegex(pybamm.OptionError, "surface form"): pybamm.BaseBatteryModel({"surface form": "bad surface form"}) with self.assertRaisesRegex(pybamm.OptionError, "convection option"): pybamm.BaseBatteryModel({"convection": "bad convection"}) with self.assertRaisesRegex( pybamm.OptionError, "cannot have transverse convection in 0D model" ): pybamm.BaseBatteryModel({"convection": "full transverse"}) with self.assertRaisesRegex(pybamm.OptionError, "particle model"): pybamm.BaseBatteryModel({"particle": "bad particle"}) with self.assertRaisesRegex(pybamm.OptionError, "operating mode"): pybamm.BaseBatteryModel({"operating mode": "bad operating mode"})
def test_default_solver(self): model = pybamm.BaseBatteryModel() self.assertIsInstance(model.default_solver, pybamm.CasadiSolver) # check that default_solver gives you a new solver, not an internal object solver = model.default_solver solver = pybamm.BaseModel() self.assertIsInstance(model.default_solver, pybamm.CasadiSolver) self.assertIsInstance(solver, pybamm.BaseModel) # check that adding algebraic variables gives DAE solver a = pybamm.Variable("a") model.algebraic = {a: a - 1} self.assertIsInstance( model.default_solver, (pybamm.IDAKLUSolver, pybamm.CasadiSolver) ) # Check that turning off jacobian gives casadi solver model.use_jacobian = False self.assertIsInstance(model.default_solver, pybamm.CasadiSolver)
def test_options(self): with self.assertRaisesRegex(pybamm.OptionError, "Option"): pybamm.BaseBatteryModel({"bad option": "bad option"}) with self.assertRaisesRegex(pybamm.OptionError, "current collector model"): pybamm.BaseBatteryModel( {"current collector": "bad current collector"}) with self.assertRaisesRegex(pybamm.OptionError, "thermal"): pybamm.BaseBatteryModel({"thermal": "bad thermal"}) with self.assertRaisesRegex(pybamm.OptionError, "cell geometry"): pybamm.BaseBatteryModel({"cell geometry": "bad geometry"}) with self.assertRaisesRegex(pybamm.OptionError, "dimensionality"): pybamm.BaseBatteryModel({"dimensionality": 5}) with self.assertRaisesRegex(pybamm.OptionError, "current collector"): pybamm.BaseBatteryModel({ "dimensionality": 1, "current collector": "bad option" }) with self.assertRaisesRegex(pybamm.OptionError, "surface form"): pybamm.BaseBatteryModel({"surface form": "bad surface form"}) with self.assertRaisesRegex(pybamm.OptionError, "convection"): pybamm.BaseBatteryModel({"convection": "bad convection"}) with self.assertRaisesRegex( pybamm.OptionError, "cannot have transverse convection in 0D model"): pybamm.BaseBatteryModel({"convection": "full transverse"}) with self.assertRaisesRegex(pybamm.OptionError, "particle"): pybamm.BaseBatteryModel({"particle": "bad particle"}) with self.assertRaisesRegex(NotImplementedError, "The 'fast diffusion'"): pybamm.BaseBatteryModel({"particle": "fast diffusion"}) with self.assertRaisesRegex(pybamm.OptionError, "particle shape"): pybamm.BaseBatteryModel({"particle shape": "bad particle shape"}) with self.assertRaisesRegex(pybamm.OptionError, "operating mode"): pybamm.BaseBatteryModel({"operating mode": "bad operating mode"}) with self.assertRaisesRegex(pybamm.OptionError, "electrolyte conductivity"): pybamm.BaseBatteryModel( {"electrolyte conductivity": "bad electrolyte conductivity"}) # SEI options with self.assertRaisesRegex(pybamm.OptionError, "SEI"): pybamm.BaseBatteryModel({"SEI": "bad sei"}) with self.assertRaisesRegex(pybamm.OptionError, "SEI film resistance"): pybamm.BaseBatteryModel( {"SEI film resistance": "bad SEI film resistance"}) with self.assertRaisesRegex(pybamm.OptionError, "SEI porosity change"): pybamm.BaseBatteryModel( {"SEI porosity change": "bad SEI porosity change"}) with self.assertRaisesRegex( pybamm.OptionError, "SEI porosity change must now be given in string format"): pybamm.BaseBatteryModel({"SEI porosity change": True}) # changing defaults based on other options model = pybamm.BaseBatteryModel() self.assertEqual(model.options["SEI film resistance"], "none") model = pybamm.BaseBatteryModel({"SEI": "constant"}) self.assertEqual(model.options["SEI film resistance"], "distributed") self.assertEqual( model.options["total interfacial current density as a state"], "true") with self.assertRaisesRegex(pybamm.OptionError, "must be 'true'"): model = pybamm.BaseBatteryModel({ "SEI film resistance": "distributed", "total interfacial current density as a state": "false", }) # loss of active material model with self.assertRaisesRegex(pybamm.OptionError, "loss of active material"): model = pybamm.BaseBatteryModel( {"loss of active material": "bad LAM model"}) # crack model with self.assertRaisesRegex(pybamm.OptionError, "particle cracking"): pybamm.BaseBatteryModel( {"particle cracking": "bad particle cracking"}) # plating model with self.assertRaisesRegex(pybamm.OptionError, "lithium plating"): pybamm.BaseBatteryModel({"lithium plating": "bad plating"}) with self.assertRaisesRegex(pybamm.OptionError, "lithium plating porosity change"): pybamm.BaseBatteryModel({ "lithium plating porosity change": "bad lithium " "plating porosity change" })
def test_simple_ode_model(self): model = pybamm.BaseBatteryModel(name="Simple ODE Model") whole_cell = ["negative electrode", "separator", "positive electrode"] # Create variables: domain is explicitly empty since these variables are only # functions of time a = pybamm.Variable("a", domain=[]) b = pybamm.Variable("b", domain=[]) c = pybamm.Variable("c", domain=[]) # Simple ODEs model.rhs = {a: pybamm.Scalar(2), b: pybamm.Scalar(0), c: -c} # Simple initial conditions model.initial_conditions = { a: pybamm.Scalar(0), b: pybamm.Scalar(1), c: pybamm.Scalar(1), } # no boundary conditions for an ODE model # Broadcast some of the variables model.variables = { "a": a, "b broadcasted": pybamm.FullBroadcast(b, whole_cell, "current collector"), "c broadcasted": pybamm.FullBroadcast( c, ["negative electrode", "separator"], "current collector" ), } # ODEs only (don't use jacobian) model.use_jacobian = False # Process and solve geometry = model.default_geometry param = model.default_parameter_values param.process_model(model) param.process_geometry(geometry) mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) disc = pybamm.Discretisation(mesh, model.default_spatial_methods) disc.process_model(model) solver = model.default_solver t_eval = np.linspace(0, 2, 100) solution = solver.solve(model, t_eval) quick_plot = pybamm.QuickPlot(model, mesh, solution) quick_plot.plot(0) # update the axis new_axis = [0, 0.5, 0, 1] quick_plot.axis.update({("a",): new_axis}) self.assertEqual(quick_plot.axis[("a",)], new_axis) # and now reset them quick_plot.reset_axis() self.assertNotEqual(quick_plot.axis[("a",)], new_axis) # check dynamic plot loads quick_plot.dynamic_plot(testing=True) quick_plot.update(0.01) # Test with different output variables quick_plot = pybamm.QuickPlot(model, mesh, solution, ["b broadcasted"]) self.assertEqual(len(quick_plot.axis), 1) quick_plot.plot(0) quick_plot = pybamm.QuickPlot( model, mesh, solution, [["a", "a"], ["b broadcasted", "b broadcasted"], "c broadcasted"], ) self.assertEqual(len(quick_plot.axis), 3) quick_plot.plot(0) # update the axis new_axis = [0, 0.5, 0, 1] var_key = ("c broadcasted",) quick_plot.axis.update({var_key: new_axis}) self.assertEqual(quick_plot.axis[var_key], new_axis) # and now reset them quick_plot.reset_axis() self.assertNotEqual(quick_plot.axis[var_key], new_axis) # check dynamic plot loads quick_plot.dynamic_plot(testing=True) quick_plot.update(0.01) # Test longer name model.variables["Variable with a very long name"] = model.variables["a"] quick_plot = pybamm.QuickPlot(model, mesh, solution) quick_plot.plot(0) # Test errors with self.assertRaisesRegex(ValueError, "mismatching variable domains"): pybamm.QuickPlot(model, mesh, solution, [["a", "b broadcasted"]]) model.variables["3D variable"] = disc.process_symbol( pybamm.Broadcast(1, ["negative particle"]) ) with self.assertRaisesRegex(NotImplementedError, "cannot plot 3D variables"): pybamm.QuickPlot(model, mesh, solution, ["3D variable"])
def test_simple_ode_model(self): model = pybamm.BaseBatteryModel(name="Simple ODE Model") whole_cell = ["negative electrode", "separator", "positive electrode"] # Create variables: domain is explicitly empty since these variables are only # functions of time a = pybamm.Variable("a", domain=[]) b = pybamm.Variable("b", domain=[]) c = pybamm.Variable("c", domain=[]) # Simple ODEs model.rhs = {a: pybamm.Scalar(2), b: pybamm.Scalar(0), c: -c} # Simple initial conditions model.initial_conditions = { a: pybamm.Scalar(0), b: pybamm.Scalar(1), c: pybamm.Scalar(1), } # no boundary conditions for an ODE model # Broadcast some of the variables model.variables = { "a": a, "b broadcasted": pybamm.FullBroadcast(b, whole_cell, "current collector"), "c broadcasted": pybamm.FullBroadcast(c, ["negative electrode", "separator"], "current collector"), "b broadcasted negative electrode": pybamm.PrimaryBroadcast(b, "negative particle"), "c broadcasted positive electrode": pybamm.PrimaryBroadcast(c, "positive particle"), "x [m]": pybamm.standard_spatial_vars.x, "x": pybamm.standard_spatial_vars.x, "r_n [m]": pybamm.standard_spatial_vars.r_n, "r_n": pybamm.standard_spatial_vars.r_n, "r_p [m]": pybamm.standard_spatial_vars.r_p, "r_p": pybamm.standard_spatial_vars.r_p, } # ODEs only (don't use jacobian) model.use_jacobian = False # Process and solve geometry = model.default_geometry param = model.default_parameter_values param.process_model(model) param.process_geometry(geometry) mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) disc = pybamm.Discretisation(mesh, model.default_spatial_methods) disc.process_model(model) solver = model.default_solver t_eval = np.linspace(0, 2, 100) solution = solver.solve(model, t_eval) quick_plot = pybamm.QuickPlot( solution, [ "a", "b broadcasted", "c broadcasted", "b broadcasted negative electrode", "c broadcasted positive electrode", ], ) quick_plot.plot(0) # update the axis new_axis = [0, 0.5, 0, 1] quick_plot.axis_limits.update({("a", ): new_axis}) self.assertEqual(quick_plot.axis_limits[("a", )], new_axis) # and now reset them quick_plot.reset_axis() self.assertNotEqual(quick_plot.axis_limits[("a", )], new_axis) # check dynamic plot loads quick_plot.dynamic_plot(testing=True) quick_plot.slider_update(0.01) # Test with different output variables quick_plot = pybamm.QuickPlot(solution, ["b broadcasted"]) self.assertEqual(len(quick_plot.axis_limits), 1) quick_plot.plot(0) quick_plot = pybamm.QuickPlot( solution, [ ["a", "a"], ["b broadcasted", "b broadcasted"], "c broadcasted", "b broadcasted negative electrode", "c broadcasted positive electrode", ], ) self.assertEqual(len(quick_plot.axis_limits), 5) quick_plot.plot(0) # update the axis new_axis = [0, 0.5, 0, 1] var_key = ("c broadcasted", ) quick_plot.axis_limits.update({var_key: new_axis}) self.assertEqual(quick_plot.axis_limits[var_key], new_axis) # and now reset them quick_plot.reset_axis() self.assertNotEqual(quick_plot.axis_limits[var_key], new_axis) # check dynamic plot loads quick_plot.dynamic_plot(testing=True) quick_plot.slider_update(0.01) # Test longer name model.variables["Variable with a very long name"] = model.variables[ "a"] quick_plot = pybamm.QuickPlot(solution, ["Variable with a very long name"]) quick_plot.plot(0) # Test different inputs quick_plot = pybamm.QuickPlot( [solution, solution], ["a"], colors=["r", "g", "b"], linestyles=["-", "--"], figsize=(1, 2), labels=["sol 1", "sol 2"], ) self.assertEqual(quick_plot.colors, ["r", "g", "b"]) self.assertEqual(quick_plot.linestyles, ["-", "--"]) self.assertEqual(quick_plot.figsize, (1, 2)) self.assertEqual(quick_plot.labels, ["sol 1", "sol 2"]) # Test different time units quick_plot = pybamm.QuickPlot(solution, ["a"]) self.assertEqual(quick_plot.time_scaling_factor, 1) quick_plot = pybamm.QuickPlot(solution, ["a"], time_unit="seconds") quick_plot.plot(0) self.assertEqual(quick_plot.time_scaling_factor, 1) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_xdata(), t_eval) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_ydata(), 2 * t_eval) quick_plot = pybamm.QuickPlot(solution, ["a"], time_unit="minutes") quick_plot.plot(0) self.assertEqual(quick_plot.time_scaling_factor, 60) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_xdata(), t_eval / 60) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_ydata(), 2 * t_eval) quick_plot = pybamm.QuickPlot(solution, ["a"], time_unit="hours") quick_plot.plot(0) self.assertEqual(quick_plot.time_scaling_factor, 3600) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_xdata(), t_eval / 3600) np.testing.assert_array_almost_equal( quick_plot.plots[("a", )][0][0].get_ydata(), 2 * t_eval) with self.assertRaisesRegex(ValueError, "time unit"): pybamm.QuickPlot(solution, ["a"], time_unit="bad unit") # long solution defaults to hours instead of seconds solution_long = solver.solve(model, np.linspace(0, 1e5)) quick_plot = pybamm.QuickPlot(solution_long, ["a"]) self.assertEqual(quick_plot.time_scaling_factor, 3600) # Test different spatial units quick_plot = pybamm.QuickPlot(solution, ["a"]) self.assertEqual(quick_plot.spatial_unit, "$\mu m$") quick_plot = pybamm.QuickPlot(solution, ["a"], spatial_unit="m") self.assertEqual(quick_plot.spatial_unit, "m") quick_plot = pybamm.QuickPlot(solution, ["a"], spatial_unit="mm") self.assertEqual(quick_plot.spatial_unit, "mm") quick_plot = pybamm.QuickPlot(solution, ["a"], spatial_unit="um") self.assertEqual(quick_plot.spatial_unit, "$\mu m$") with self.assertRaisesRegex(ValueError, "spatial unit"): pybamm.QuickPlot(solution, ["a"], spatial_unit="bad unit") # Test 2D variables model.variables["2D variable"] = disc.process_symbol( pybamm.FullBroadcast(1, "negative particle", {"secondary": "negative electrode"})) quick_plot = pybamm.QuickPlot(solution, ["2D variable"]) quick_plot.plot(0) quick_plot.dynamic_plot(testing=True) quick_plot.slider_update(0.01) with self.assertRaisesRegex(NotImplementedError, "Cannot plot 2D variables"): pybamm.QuickPlot([solution, solution], ["2D variable"]) # Test different variable limits quick_plot = pybamm.QuickPlot( solution, ["a", ["c broadcasted", "c broadcasted"]], variable_limits="tight") self.assertEqual(quick_plot.axis_limits[("a", )][2:], [None, None]) self.assertEqual( quick_plot.axis_limits[("c broadcasted", "c broadcasted")][2:], [None, None]) quick_plot.plot(0) quick_plot.slider_update(1) quick_plot = pybamm.QuickPlot(solution, ["2D variable"], variable_limits="tight") self.assertEqual(quick_plot.variable_limits[("2D variable", )], (None, None)) quick_plot.plot(0) quick_plot.slider_update(1) quick_plot = pybamm.QuickPlot( solution, ["a", ["c broadcasted", "c broadcasted"]], variable_limits={ "a": [1, 2], ("c broadcasted", "c broadcasted"): [3, 4] }, ) self.assertEqual(quick_plot.axis_limits[("a", )][2:], [1, 2]) self.assertEqual( quick_plot.axis_limits[("c broadcasted", "c broadcasted")][2:], [3, 4]) quick_plot.plot(0) quick_plot.slider_update(1) quick_plot = pybamm.QuickPlot(solution, ["a", "b broadcasted"], variable_limits={"a": "tight"}) self.assertEqual(quick_plot.axis_limits[("a", )][2:], [None, None]) self.assertNotEqual(quick_plot.axis_limits[("b broadcasted", )][2:], [None, None]) quick_plot.plot(0) quick_plot.slider_update(1) with self.assertRaisesRegex( TypeError, "variable_limits must be 'fixed', 'tight', or a dict"): pybamm.QuickPlot(solution, ["a", "b broadcasted"], variable_limits="bad variable limits") # Test errors with self.assertRaisesRegex(ValueError, "Mismatching variable domains"): pybamm.QuickPlot(solution, [["a", "b broadcasted"]]) with self.assertRaisesRegex(ValueError, "labels"): pybamm.QuickPlot([solution, solution], ["a"], labels=["sol 1", "sol 2", "sol 3"]) # Remove 'x [m]' from the variables and make sure a key error is raise del solution.model.variables["x [m]"] with self.assertRaisesRegex( KeyError, "Can't find spatial scale for 'negative electrode'"): pybamm.QuickPlot(solution, ["b broadcasted"]) # No variable can be NaN model.variables["NaN variable"] = disc.process_symbol( pybamm.Scalar(np.nan)) with self.assertRaisesRegex( ValueError, "All-NaN variable 'NaN variable' provided"): pybamm.QuickPlot(solution, ["NaN variable"])
def test_options(self): with self.assertRaisesRegex(pybamm.OptionError, "Option"): pybamm.BaseBatteryModel({"bad option": "bad option"}) with self.assertRaisesRegex(pybamm.OptionError, "current collector model"): pybamm.BaseBatteryModel( {"current collector": "bad current collector"}) with self.assertRaisesRegex(pybamm.OptionError, "thermal model"): pybamm.BaseBatteryModel({"thermal": "bad thermal"}) with self.assertRaisesRegex(pybamm.OptionError, "Unknown geometry"): pybamm.BaseBatteryModel({"cell geometry": "bad geometry"}) with self.assertRaisesRegex(pybamm.OptionError, "Dimension of current collectors"): pybamm.BaseBatteryModel({"dimensionality": 5}) with self.assertRaisesRegex(pybamm.OptionError, "current collector"): pybamm.BaseBatteryModel({ "dimensionality": 1, "current collector": "bad option" }) with self.assertRaisesRegex(pybamm.OptionError, "surface form"): pybamm.BaseBatteryModel({"surface form": "bad surface form"}) with self.assertRaisesRegex(pybamm.OptionError, "convection option"): pybamm.BaseBatteryModel({"convection": "bad convection"}) with self.assertRaisesRegex( pybamm.OptionError, "cannot have transverse convection in 0D model"): pybamm.BaseBatteryModel({"convection": "full transverse"}) with self.assertRaisesRegex(pybamm.OptionError, "particle model"): pybamm.BaseBatteryModel({"particle": "bad particle"}) with self.assertRaisesRegex(NotImplementedError, "The 'fast diffusion'"): pybamm.BaseBatteryModel({"particle": "fast diffusion"}) with self.assertRaisesRegex(pybamm.OptionError, "particle shape"): pybamm.BaseBatteryModel({"particle shape": "bad particle shape"}) with self.assertRaisesRegex(pybamm.OptionError, "operating mode"): pybamm.BaseBatteryModel({"operating mode": "bad operating mode"}) # SEI options with self.assertRaisesRegex(pybamm.OptionError, "sei"): pybamm.BaseBatteryModel({"sei": "bad sei"}) with self.assertRaisesRegex(pybamm.OptionError, "sei film resistance"): pybamm.BaseBatteryModel( {"sei film resistance": "bad sei film resistance"}) with self.assertRaisesRegex(pybamm.OptionError, "sei porosity change"): pybamm.BaseBatteryModel( {"sei porosity change": "bad sei porosity change"}) # variable defaults model = pybamm.BaseBatteryModel() self.assertEqual(model.options["sei film resistance"], None) model = pybamm.BaseBatteryModel({"sei": "constant"}) self.assertEqual(model.options["sei film resistance"], "distributed")