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()
def test_simple_ode_model(self): model = pybamm.lithium_ion.BaseModel(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"), } model.timescale = pybamm.Scalar(1) # 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"]) # 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"]) pybamm.close_plots()
def test_loqs_spme(self): t_eval = np.linspace(0, 10, 2) for model in [pybamm.lithium_ion.SPMe(), pybamm.lead_acid.LOQS()]: geometry = model.default_geometry param = model.default_parameter_values param.process_model(model) 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 } mesh = pybamm.Mesh(geometry, model.default_submesh_types, 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(solution) # check 1D (space) variables update properly for different time units t = solution["Time [s]"].entries c_e_var = solution["Electrolyte concentration [mol.m-3]"] # 1D variables should be evaluated on edges L_x = param.evaluate(model.param.L_x) c_e = c_e_var(t=t, x=mesh.combine_submeshes(*c_e_var.domain).edges * L_x) 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][0].get_ydata(), )[0] np.testing.assert_array_almost_equal(qp_data, c_e[:, 0]) quick_plot.slider_update(t_eval[-1] / scale) qp_data = ( quick_plot.plots[("Electrolyte concentration [mol.m-3]", )][0][0].get_ydata(), )[0][:, 0] np.testing.assert_array_almost_equal(qp_data, c_e[:, 1]) # test quick plot of particle for spme if model.name == "Single Particle Model with electrolyte": output_variables = [ "X-averaged negative particle concentration [mol.m-3]", "X-averaged positive particle concentration [mol.m-3]", "Negative particle concentration [mol.m-3]", "Positive particle concentration [mol.m-3]", ] pybamm.QuickPlot(solution, output_variables) # check 2D (space) variables update properly for different time units c_n = solution["Negative particle concentration [mol.m-3]"] for unit, scale in zip(["seconds", "minutes", "hours"], [1, 60, 3600]): quick_plot = pybamm.QuickPlot( solution, ["Negative particle concentration [mol.m-3]"], time_unit=unit, ) quick_plot.plot(0) qp_data = quick_plot.plots[( "Negative particle concentration [mol.m-3]", )][0][1] c_n_eval = c_n(t_eval[0], r=c_n.first_dim_pts, x=c_n.second_dim_pts) np.testing.assert_array_almost_equal(qp_data, c_n_eval) quick_plot.slider_update(t_eval[-1] / scale) qp_data = quick_plot.plots[( "Negative particle concentration [mol.m-3]", )][0][1] c_n_eval = c_n(t_eval[-1], r=c_n.first_dim_pts, x=c_n.second_dim_pts) np.testing.assert_array_almost_equal(qp_data, c_n_eval) pybamm.close_plots()