From 24d093827db878c06cd30c5bf85712bc5772fada Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 16:26:24 +0100 Subject: [PATCH 1/7] Enhance plot_voltage_components for Simulation and Solution classes This commit extends the functionality of the `plot_voltage_components` method in PyBaMM, enhancing its integration within the `Simulation` and `Solution` classes. Key Changes: - Modified the standalone `plot_voltage_components` function in `plot_voltage_components.py` to handle both `Simulation` and `Solution` objects. - `Simulation` Class: Added a new method `plot_voltage_components`. This method calls the standalone `plot_voltage_components` function and utilizes the solution attribute of the `Simulation` instance to generate the plot. - `Solution` Class: Introduced a similar `plot_voltage_components` method to the `Solution` class, enabling direct plotting of voltage components from `Solution` instances. - Modified `test_plot_voltage_components.py` --- pybamm/plotting/plot_voltage_components.py | 15 ++++++-- pybamm/simulation.py | 38 +++++++++++++++++++ pybamm/solvers/solution.py | 36 ++++++++++++++++++ .../test_plot_voltage_components.py | 24 +++++++++--- 4 files changed, 105 insertions(+), 8 deletions(-) diff --git a/pybamm/plotting/plot_voltage_components.py b/pybamm/plotting/plot_voltage_components.py index ef95f7016f..694a01868a 100644 --- a/pybamm/plotting/plot_voltage_components.py +++ b/pybamm/plotting/plot_voltage_components.py @@ -4,10 +4,12 @@ import numpy as np from pybamm.util import have_optional_dependency +from pybamm.simulation import Simulation +from pybamm.solvers.solution import Solution def plot_voltage_components( - solution, + input_data, ax=None, show_legend=True, split_by_electrode=False, @@ -19,8 +21,8 @@ def plot_voltage_components( Parameters ---------- - solution : :class:`pybamm.Solution` - Solution object from which to extract voltage components + input_data : :class:`pybamm.Solution` or :class:`pybamm.Simulation` + Solution or Simulation object from which to extract voltage components. ax : matplotlib Axis, optional The axis on which to put the plot. If None, a new figure and axis is created. show_legend : bool, optional @@ -34,6 +36,13 @@ def plot_voltage_components( Keyword arguments, passed to ax.fill_between """ + # Check if the input is a Simulation and extract Solution + if isinstance(input_data, Simulation): + solution = input_data.solution + elif isinstance(input_data, Solution): + solution = input_data + else: + raise ValueError("Input must be a Solution or Simulation object") plt = have_optional_dependency("matplotlib.pyplot") # Set a default value for alpha, the opacity diff --git a/pybamm/simulation.py b/pybamm/simulation.py index c95ab3039c..be5f2a34c7 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -1194,6 +1194,44 @@ def save_model( """ ) + def plot_voltage_components( + self, + ax=None, + show_legend=True, + split_by_electrode=False, + testing=False, + **kwargs_fill, + ): + """ + Generate a plot showing the component overpotentials that make up the voltage + + Parameters + ---------- + ax : matplotlib Axis, optional + The axis on which to put the plot. If None, a new figure and axis is created. + show_legend : bool, optional + Whether to display the legend. Default is True. + split_by_electrode : bool, optional + Whether to show the overpotentials for the negative and positive electrodes + separately. Default is False. + testing : bool, optional + Whether to actually make the plot (turned off for unit tests). + kwargs_fill + Keyword arguments, passed to ax.fill_between. + + """ + if self.solution is None: + raise ValueError("The simulation has not been solved yet.") + + return pybamm.plot_voltage_components( + self.solution, + ax=ax, + show_legend=show_legend, + split_by_electrode=split_by_electrode, + testing=testing, + **kwargs_fill, + ) + def load_sim(filename): """Load a saved simulation""" diff --git a/pybamm/solvers/solution.py b/pybamm/solvers/solution.py index 90712960cc..d481768713 100644 --- a/pybamm/solvers/solution.py +++ b/pybamm/solvers/solution.py @@ -801,6 +801,42 @@ def copy(self): return new_sol + def plot_voltage_components( + self, + ax=None, + show_legend=True, + split_by_electrode=False, + testing=False, + **kwargs_fill, + ): + """ + Generate a plot showing the component overpotentials that make up the voltage + + Parameters + ---------- + ax : matplotlib Axis, optional + The axis on which to put the plot. If None, a new figure and axis is created. + show_legend : bool, optional + Whether to display the legend. Default is True. + split_by_electrode : bool, optional + Whether to show the overpotentials for the negative and positive electrodes + separately. Default is False. + testing : bool, optional + Whether to actually make the plot (turned off for unit tests). + kwargs_fill + Keyword arguments, passed to ax.fill_between. + + """ + # Use 'self' here as the solution object + return pybamm.plot_voltage_components( + self, + ax=ax, + show_legend=show_legend, + split_by_electrode=split_by_electrode, + testing=testing, + **kwargs_fill, + ) + class EmptySolution: def __init__(self, termination=None, t=None): diff --git a/tests/unit/test_plotting/test_plot_voltage_components.py b/tests/unit/test_plotting/test_plot_voltage_components.py index b2fc201cec..4058e63fee 100644 --- a/tests/unit/test_plotting/test_plot_voltage_components.py +++ b/tests/unit/test_plotting/test_plot_voltage_components.py @@ -6,20 +6,34 @@ class TestPlotVoltageComponents(TestCase): - def test_plot(self): + def test_plot_with_solution(self): model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model) sol = sim.solve([0, 3600]) for split in [True, False]: - _, ax = pybamm.plot_voltage_components( - sol, testing=True, split_by_electrode=split - ) + _, ax = sol.plot_voltage_components(testing=True, split_by_electrode=split) t, V = ax.get_lines()[0].get_data() np.testing.assert_array_equal(t, sol["Time [h]"].data) np.testing.assert_array_equal(V, sol["Battery voltage [V]"].data) _, ax = plt.subplots() - _, ax_out = pybamm.plot_voltage_components(sol, ax=ax, show_legend=True) + _, ax_out = sol.plot_voltage_components(ax=ax, show_legend=True) + self.assertEqual(ax_out, ax) + + def test_plot_with_simulation(self): + model = pybamm.lithium_ion.SPM() + sim = pybamm.Simulation(model) + sim.solve([0, 3600]) + + for split in [True, False]: + # Use the method from the Simulation instance + _, ax = sim.plot_voltage_components(testing=True, split_by_electrode=split) + t, V = ax.get_lines()[0].get_data() + np.testing.assert_array_equal(t, sim.solution["Time [h]"].data) + np.testing.assert_array_equal(V, sim.solution["Battery voltage [V]"].data) + + _, ax = plt.subplots() + _, ax_out = sim.plot_voltage_components(ax=ax, show_legend=True) self.assertEqual(ax_out, ax) From aebcef7422edf28f73d9f3d2edf18dd882734cdd Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 17:31:44 +0100 Subject: [PATCH 2/7] Improved code readability by removing explicit variable type checking --- pybamm/plotting/plot_voltage_components.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pybamm/plotting/plot_voltage_components.py b/pybamm/plotting/plot_voltage_components.py index 694a01868a..a24da57932 100644 --- a/pybamm/plotting/plot_voltage_components.py +++ b/pybamm/plotting/plot_voltage_components.py @@ -41,8 +41,6 @@ def plot_voltage_components( solution = input_data.solution elif isinstance(input_data, Solution): solution = input_data - else: - raise ValueError("Input must be a Solution or Simulation object") plt = have_optional_dependency("matplotlib.pyplot") # Set a default value for alpha, the opacity From fafe80068ef34d9e4068a8795852f50085ca5bd4 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 17:52:07 +0100 Subject: [PATCH 3/7] Update example notebooks --- .../getting_started/tutorial-3-basic-plotting.ipynb | 9 ++++----- .../notebooks/plotting/plot-voltage-components.ipynb | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/source/examples/notebooks/getting_started/tutorial-3-basic-plotting.ipynb b/docs/source/examples/notebooks/getting_started/tutorial-3-basic-plotting.ipynb index 022a11e48a..7fee5435c9 100644 --- a/docs/source/examples/notebooks/getting_started/tutorial-3-basic-plotting.ipynb +++ b/docs/source/examples/notebooks/getting_started/tutorial-3-basic-plotting.ipynb @@ -828,7 +828,7 @@ } ], "source": [ - "pybamm.plot_voltage_components(sim_dfn.solution)" + "sim_dfn.plot_voltage_components()" ] }, { @@ -864,8 +864,8 @@ "# comparing voltage components for Doyle-Fuller-Newman model and Single Particle Model\n", "fig, axes = plt.subplots(1, 2, figsize=(15, 6), sharey=True)\n", "\n", - "pybamm.plot_voltage_components(sim_dfn.solution, ax=axes.flat[0])\n", - "pybamm.plot_voltage_components(sim_spm.solution, ax=axes.flat[1])\n", + "sim_dfn.plot_voltage_components(ax=axes.flat[0])\n", + "sim_spm.plot_voltage_components(ax=axes.flat[1])\n", "\n", "axes.flat[0].set_title(\"Doyle-Fuller-Newman Model\")\n", "axes.flat[1].set_title(\"Single Particle Model\")\n", @@ -908,8 +908,7 @@ "[4] Marc Doyle, Thomas F. Fuller, and John Newman. Modeling of galvanostatic charge and discharge of the lithium/polymer/insertion cell. Journal of the Electrochemical society, 140(6):1526–1533, 1993. doi:10.1149/1.2221597.\n", "[5] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.\n", "[6] Scott G. Marquis, Valentin Sulzer, Robert Timms, Colin P. Please, and S. Jon Chapman. An asymptotic derivation of a single particle model with electrolyte. Journal of The Electrochemical Society, 166(15):A3693–A3706, 2019. doi:10.1149/2.0341915jes.\n", - "[7] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.\n", - "\n" + "[7] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.\n" ] } ], diff --git a/docs/source/examples/notebooks/plotting/plot-voltage-components.ipynb b/docs/source/examples/notebooks/plotting/plot-voltage-components.ipynb index bab1b8093e..be0706bbfe 100644 --- a/docs/source/examples/notebooks/plotting/plot-voltage-components.ipynb +++ b/docs/source/examples/notebooks/plotting/plot-voltage-components.ipynb @@ -179,7 +179,7 @@ } ], "source": [ - "pybamm.plot_voltage_components(sol, split_by_electrode=True)" + "sol.plot_voltage_components(split_by_electrode=True)" ] }, { @@ -217,7 +217,7 @@ } ], "source": [ - "pybamm.plot_voltage_components(sol)" + "sol.plot_voltage_components()" ] }, { @@ -254,8 +254,7 @@ "[6] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.\n", "[7] Peyman Mohtat, Suhak Lee, Jason B Siegel, and Anna G Stefanopoulou. Towards better estimability of electrode-specific state of health: decoding the cell expansion. Journal of Power Sources, 427:101–111, 2019.\n", "[8] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.\n", - "[9] Pauli Virtanen, Ralf Gommers, Travis E. Oliphant, Matt Haberland, Tyler Reddy, David Cournapeau, Evgeni Burovski, Pearu Peterson, Warren Weckesser, Jonathan Bright, and others. SciPy 1.0: fundamental algorithms for scientific computing in Python. Nature Methods, 17(3):261–272, 2020. doi:10.1038/s41592-019-0686-2.\n", - "\n" + "[9] Pauli Virtanen, Ralf Gommers, Travis E. Oliphant, Matt Haberland, Tyler Reddy, David Cournapeau, Evgeni Burovski, Pearu Peterson, Warren Weckesser, Jonathan Bright, and others. SciPy 1.0: fundamental algorithms for scientific computing in Python. Nature Methods, 17(3):261–272, 2020. doi:10.1038/s41592-019-0686-2.\n" ] } ], From 7f578f14aea3b9a66d7915631b9031c05adfde08 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 21:00:49 +0100 Subject: [PATCH 4/7] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ad65f9a0..48503b40b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Bug Fixes - Fixed a bug where if the first step(s) in a cycle are skipped then the cycle solution started from the model's initial conditions instead of from the last state of the previous cycle ([#3708](https://github.com/pybamm-team/PyBaMM/pull/3708)) +- Updated `plot_voltage_components.py` to support both `Simulation` and `Solution` objects. Added new methods in both `Simulation` and `Solution` classes for allow the syntax `simulation.plot_voltage_components` and `solution.plot_voltage_components`. Updated `test_plot_voltage_components.py` to reflect these changes ([#3723](https://github.com/pybamm-team/PyBaMM/pull/3723)). # [v24.1rc0](https://github.com/pybamm-team/PyBaMM/tree/v24.1rc0) - 2024-01-31 ## Features From a309b1be359e3744c96adfcf0c226e36d6ed5f1f Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 21:57:33 +0100 Subject: [PATCH 5/7] Fix test_plot_voltage_components.py --- .../test_plot_voltage_components.py | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_plotting/test_plot_voltage_components.py b/tests/unit/test_plotting/test_plot_voltage_components.py index 4058e63fee..412af2aeec 100644 --- a/tests/unit/test_plotting/test_plot_voltage_components.py +++ b/tests/unit/test_plotting/test_plot_voltage_components.py @@ -7,6 +7,39 @@ class TestPlotVoltageComponents(TestCase): def test_plot_with_solution(self): + model = pybamm.lithium_ion.SPM() + sim = pybamm.Simulation(model) + sol = sim.solve([0, 3600]) + for split in [True, False]: + _, ax = pybamm.plot_voltage_components( + sol, testing=True, split_by_electrode=split + ) + t, V = ax.get_lines()[0].get_data() + np.testing.assert_array_equal(t, sol["Time [h]"].data) + np.testing.assert_array_equal(V, sol["Battery voltage [V]"].data) + + _, ax = plt.subplots() + _, ax_out = pybamm.plot_voltage_components(sol, ax=ax, show_legend=True) + self.assertEqual(ax_out, ax) + + def test_plot_with_simulation(self): + model = pybamm.lithium_ion.SPM() + sim = pybamm.Simulation(model) + sim.solve([0, 3600]) + + for split in [True, False]: + _, ax = pybamm.plot_voltage_components( + sim, testing=True, split_by_electrode=split + ) + t, V = ax.get_lines()[0].get_data() + np.testing.assert_array_equal(t, sim.solution["Time [h]"].data) + np.testing.assert_array_equal(V, sim.solution["Battery voltage [V]"].data) + + _, ax = plt.subplots() + _, ax_out = pybamm.plot_voltage_components(sim, ax=ax, show_legend=True) + self.assertEqual(ax_out, ax) + + def test_plot_from_solution(self): model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model) sol = sim.solve([0, 3600]) @@ -20,13 +53,12 @@ def test_plot_with_solution(self): _, ax_out = sol.plot_voltage_components(ax=ax, show_legend=True) self.assertEqual(ax_out, ax) - def test_plot_with_simulation(self): + def test_plot_from_simulation(self): model = pybamm.lithium_ion.SPM() sim = pybamm.Simulation(model) sim.solve([0, 3600]) for split in [True, False]: - # Use the method from the Simulation instance _, ax = sim.plot_voltage_components(testing=True, split_by_electrode=split) t, V = ax.get_lines()[0].get_data() np.testing.assert_array_equal(t, sim.solution["Time [h]"].data) From 7ef99a0f3a5782799d1649a0ea7268fe138b2416 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Mon, 15 Jan 2024 22:45:24 +0100 Subject: [PATCH 6/7] Add test to ensure ValueError is raised when plot_voltage_components is called on an unsolved simulation --- .../test_plotting/test_plot_voltage_components.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/test_plotting/test_plot_voltage_components.py b/tests/unit/test_plotting/test_plot_voltage_components.py index 412af2aeec..eb0a84fe3a 100644 --- a/tests/unit/test_plotting/test_plot_voltage_components.py +++ b/tests/unit/test_plotting/test_plot_voltage_components.py @@ -68,6 +68,17 @@ def test_plot_from_simulation(self): _, ax_out = sim.plot_voltage_components(ax=ax, show_legend=True) self.assertEqual(ax_out, ax) + def test_plot_without_solution(self): + model = pybamm.lithium_ion.SPM() + sim = pybamm.Simulation(model) + + with self.assertRaises(ValueError) as error: + sim.plot_voltage_components() + + self.assertEqual( + str(error.exception), "The simulation has not been solved yet." + ) + if __name__ == "__main__": print("Add -v for more debug output") From 86cddb62fc1242bbac634f25c2a16ffb2e74393a Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Tue, 16 Jan 2024 09:37:31 +0100 Subject: [PATCH 7/7] Sort contributions by decreasing PR number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48503b40b0..20a41de516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## Bug Fixes -- Fixed a bug where if the first step(s) in a cycle are skipped then the cycle solution started from the model's initial conditions instead of from the last state of the previous cycle ([#3708](https://github.com/pybamm-team/PyBaMM/pull/3708)) - Updated `plot_voltage_components.py` to support both `Simulation` and `Solution` objects. Added new methods in both `Simulation` and `Solution` classes for allow the syntax `simulation.plot_voltage_components` and `solution.plot_voltage_components`. Updated `test_plot_voltage_components.py` to reflect these changes ([#3723](https://github.com/pybamm-team/PyBaMM/pull/3723)). +- Fixed a bug where if the first step(s) in a cycle are skipped then the cycle solution started from the model's initial conditions instead of from the last state of the previous cycle ([#3708](https://github.com/pybamm-team/PyBaMM/pull/3708)) # [v24.1rc0](https://github.com/pybamm-team/PyBaMM/tree/v24.1rc0) - 2024-01-31 ## Features