From f8038e8eb2fc091e426264f3dbc7dd03f13bc6a3 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Fri, 12 Jan 2024 02:07:40 +0100 Subject: [PATCH 1/5] Refactor get_initial_stoichiometries with custom and default tolerance This commit enhances the `get_initial_stoichiometries` method by introducing a new parameter, `solver_tolerance`. The primary change allows users to specify a custom tolerance level for the solver. The method now includes a default solver tolerance set at 1e-6, maintaining a balance between precision and computational efficiency for general use cases. Additionally, users can adjust this tolerance level as needed, catering to specific requirements of different battery modeling scenarios. Resolves: #3688 --- .../lithium_ion/electrode_soh.py | 29 ++++++++++++++----- pybamm/parameters/parameter_values.py | 8 ++++- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py index d975de859c..bc0674f639 100644 --- a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py +++ b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py @@ -664,7 +664,7 @@ def _check_esoh_feasible(self, inputs): ) ) - def get_initial_stoichiometries(self, initial_value): + def get_initial_stoichiometries(self, initial_value, solver_tolerance=1e-6): """ Calculate initial stoichiometries to start off the simulation at a particular state of charge, given voltage limits, open-circuit potentials, etc defined by @@ -677,6 +677,10 @@ def get_initial_stoichiometries(self, initial_value): If integer, interpreted as SOC, must be between 0 and 1. If string e.g. "4 V", interpreted as voltage, must be between V_min and V_max. + solver_tolerance : float, optional + The tolerance for the solver used to compute the initial stoichiometries. + A lower value results in higher precision but may increase computation time. + Default is 1e-6. Returns ------- @@ -723,7 +727,7 @@ def get_initial_stoichiometries(self, initial_value): soc_model.initial_conditions[soc] = (V_init - V_min) / (V_max - V_min) soc_model.variables["soc"] = soc parameter_values.process_model(soc_model) - initial_soc = pybamm.AlgebraicSolver().solve(soc_model, [0])["soc"].data[0] + initial_soc = pybamm.AlgebraicSolver(tol=solver_tolerance).solve(soc_model, [0])["soc"].data[0] elif isinstance(initial_value, (int, float)): initial_soc = initial_value if not 0 <= initial_soc <= 1: @@ -766,7 +770,7 @@ def get_min_max_stoichiometries(self): sol = self.solve(inputs) return [sol["x_0"], sol["x_100"], sol["y_100"], sol["y_0"]] - def get_initial_ocps(self, initial_value): + def get_initial_ocps(self, initial_value, solver_tolerance=1e-6): """ Calculate initial open-circuit potentials to start off the simulation at a particular state of charge, given voltage limits, open-circuit potentials, etc @@ -776,6 +780,8 @@ def get_initial_ocps(self, initial_value): ---------- initial_value : float Target SOC, must be between 0 and 1. + solver_tolerance: float, optional + Tolerance for the solver used in calculating initial stoichiometries. Returns ------- @@ -784,7 +790,7 @@ def get_initial_ocps(self, initial_value): """ parameter_values = self.parameter_values param = self.param - x, y = self.get_initial_stoichiometries(initial_value) + x, y = self.get_initial_stoichiometries(initial_value, solver_tolerance) if self.options["open-circuit potential"] == "MSMR": msmr_pot_model = _get_msmr_potential_model( self.parameter_values, self.param @@ -854,6 +860,7 @@ def get_initial_stoichiometries( param=None, known_value="cyclable lithium capacity", options=None, + solver_tolerance=1e-6, ): """ Calculate initial stoichiometries to start off the simulation at a particular @@ -878,6 +885,10 @@ def get_initial_stoichiometries( options : dict-like, optional A dictionary of options to be passed to the model, see :class:`pybamm.BatteryModelOptions`. + solver_tolerance : float, optional + The tolerance for the solver used to compute the initial stoichiometries. + A lower value results in higher precision but may increase computation time. + Default is 1e-6. Returns ------- @@ -885,7 +896,7 @@ def get_initial_stoichiometries( The initial stoichiometries that give the desired initial state of charge """ esoh_solver = ElectrodeSOHSolver(parameter_values, param, known_value, options) - return esoh_solver.get_initial_stoichiometries(initial_value) + return esoh_solver.get_initial_stoichiometries(initial_value, solver_tolerance) def get_min_max_stoichiometries( @@ -1014,7 +1025,7 @@ def theoretical_energy_integral(parameter_values, param, inputs, points=100): def calculate_theoretical_energy( - parameter_values, initial_soc=1.0, final_soc=0.0, points=100 + parameter_values, initial_soc=1.0, final_soc=0.0, points=100, solver_tolerance=1e-6 ): """ Calculate maximum energy possible from a cell given OCV, initial soc, and final soc @@ -1030,14 +1041,16 @@ def calculate_theoretical_energy( The soc at end of discharge, default 0.0 points : int The number of points at which to calculate voltage. + solver_tolerance: float + Tolerance for the solver used in calculating initial and final stoichiometries. Returns ------- E The total energy of the cell in Wh """ # Get initial and final stoichiometric values. - x_100, y_100 = get_initial_stoichiometries(initial_soc, parameter_values) - x_0, y_0 = get_initial_stoichiometries(final_soc, parameter_values) + x_100, y_100 = get_initial_stoichiometries(initial_soc, parameter_values, solver_tolerance=solver_tolerance) + x_0, y_0 = get_initial_stoichiometries(final_soc, parameter_values, solver_tolerance=solver_tolerance) Q_p = parameter_values.evaluate(pybamm.LithiumIonParameters().p.prim.Q_init) E = theoretical_energy_integral( parameter_values, diff --git a/pybamm/parameters/parameter_values.py b/pybamm/parameters/parameter_values.py index 5dcb3c950a..c63171edd0 100644 --- a/pybamm/parameters/parameter_values.py +++ b/pybamm/parameters/parameter_values.py @@ -307,6 +307,7 @@ def set_initial_stoichiometries( known_value="cyclable lithium capacity", inplace=True, options=None, + solver_tolerance=1e-6, ): """ Set the initial stoichiometry of each electrode, based on the initial @@ -314,7 +315,12 @@ def set_initial_stoichiometries( """ param = param or pybamm.LithiumIonParameters(options) x, y = pybamm.lithium_ion.get_initial_stoichiometries( - initial_value, self, param=param, known_value=known_value, options=options + initial_value, + self, + param=param, + known_value=known_value, + options=options, + solver_tolerance=solver_tolerance ) if inplace: parameter_values = self From 71c355560e822b4cc80a9fe0c5eaaa448beab3f9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 01:26:25 +0000 Subject: [PATCH 2/5] style: pre-commit fixes --- .../lithium_ion/electrode_soh.py | 14 +++++++++++--- pybamm/parameters/parameter_values.py | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py index bc0674f639..b07e63a7f4 100644 --- a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py +++ b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py @@ -727,7 +727,11 @@ def get_initial_stoichiometries(self, initial_value, solver_tolerance=1e-6): soc_model.initial_conditions[soc] = (V_init - V_min) / (V_max - V_min) soc_model.variables["soc"] = soc parameter_values.process_model(soc_model) - initial_soc = pybamm.AlgebraicSolver(tol=solver_tolerance).solve(soc_model, [0])["soc"].data[0] + initial_soc = ( + pybamm.AlgebraicSolver(tol=solver_tolerance) + .solve(soc_model, [0])["soc"] + .data[0] + ) elif isinstance(initial_value, (int, float)): initial_soc = initial_value if not 0 <= initial_soc <= 1: @@ -1049,8 +1053,12 @@ def calculate_theoretical_energy( The total energy of the cell in Wh """ # Get initial and final stoichiometric values. - x_100, y_100 = get_initial_stoichiometries(initial_soc, parameter_values, solver_tolerance=solver_tolerance) - x_0, y_0 = get_initial_stoichiometries(final_soc, parameter_values, solver_tolerance=solver_tolerance) + x_100, y_100 = get_initial_stoichiometries( + initial_soc, parameter_values, solver_tolerance=solver_tolerance + ) + x_0, y_0 = get_initial_stoichiometries( + final_soc, parameter_values, solver_tolerance=solver_tolerance + ) Q_p = parameter_values.evaluate(pybamm.LithiumIonParameters().p.prim.Q_init) E = theoretical_energy_integral( parameter_values, diff --git a/pybamm/parameters/parameter_values.py b/pybamm/parameters/parameter_values.py index c63171edd0..acb3ef5d86 100644 --- a/pybamm/parameters/parameter_values.py +++ b/pybamm/parameters/parameter_values.py @@ -320,7 +320,7 @@ def set_initial_stoichiometries( param=param, known_value=known_value, options=options, - solver_tolerance=solver_tolerance + solver_tolerance=solver_tolerance, ) if inplace: parameter_values = self From c3ec3b2392d008778b02bdf9761af3ea2ac5e46f Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Sat, 13 Jan 2024 00:41:27 +0100 Subject: [PATCH 3/5] Refactor argument name from 'solver_tolerance' to 'tol' for consistency with AlgebraicSolver class --- .../lithium_ion/electrode_soh.py | 32 ++++++++----------- pybamm/parameters/parameter_values.py | 4 +-- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py index b07e63a7f4..d698da5c2e 100644 --- a/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py +++ b/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py @@ -664,7 +664,7 @@ def _check_esoh_feasible(self, inputs): ) ) - def get_initial_stoichiometries(self, initial_value, solver_tolerance=1e-6): + def get_initial_stoichiometries(self, initial_value, tol=1e-6): """ Calculate initial stoichiometries to start off the simulation at a particular state of charge, given voltage limits, open-circuit potentials, etc defined by @@ -677,7 +677,7 @@ def get_initial_stoichiometries(self, initial_value, solver_tolerance=1e-6): If integer, interpreted as SOC, must be between 0 and 1. If string e.g. "4 V", interpreted as voltage, must be between V_min and V_max. - solver_tolerance : float, optional + tol : float, optional The tolerance for the solver used to compute the initial stoichiometries. A lower value results in higher precision but may increase computation time. Default is 1e-6. @@ -728,9 +728,7 @@ def get_initial_stoichiometries(self, initial_value, solver_tolerance=1e-6): soc_model.variables["soc"] = soc parameter_values.process_model(soc_model) initial_soc = ( - pybamm.AlgebraicSolver(tol=solver_tolerance) - .solve(soc_model, [0])["soc"] - .data[0] + pybamm.AlgebraicSolver(tol=tol).solve(soc_model, [0])["soc"].data[0] ) elif isinstance(initial_value, (int, float)): initial_soc = initial_value @@ -774,7 +772,7 @@ def get_min_max_stoichiometries(self): sol = self.solve(inputs) return [sol["x_0"], sol["x_100"], sol["y_100"], sol["y_0"]] - def get_initial_ocps(self, initial_value, solver_tolerance=1e-6): + def get_initial_ocps(self, initial_value, tol=1e-6): """ Calculate initial open-circuit potentials to start off the simulation at a particular state of charge, given voltage limits, open-circuit potentials, etc @@ -784,7 +782,7 @@ def get_initial_ocps(self, initial_value, solver_tolerance=1e-6): ---------- initial_value : float Target SOC, must be between 0 and 1. - solver_tolerance: float, optional + tol: float, optional Tolerance for the solver used in calculating initial stoichiometries. Returns @@ -794,7 +792,7 @@ def get_initial_ocps(self, initial_value, solver_tolerance=1e-6): """ parameter_values = self.parameter_values param = self.param - x, y = self.get_initial_stoichiometries(initial_value, solver_tolerance) + x, y = self.get_initial_stoichiometries(initial_value, tol) if self.options["open-circuit potential"] == "MSMR": msmr_pot_model = _get_msmr_potential_model( self.parameter_values, self.param @@ -864,7 +862,7 @@ def get_initial_stoichiometries( param=None, known_value="cyclable lithium capacity", options=None, - solver_tolerance=1e-6, + tol=1e-6, ): """ Calculate initial stoichiometries to start off the simulation at a particular @@ -889,7 +887,7 @@ def get_initial_stoichiometries( options : dict-like, optional A dictionary of options to be passed to the model, see :class:`pybamm.BatteryModelOptions`. - solver_tolerance : float, optional + tol : float, optional The tolerance for the solver used to compute the initial stoichiometries. A lower value results in higher precision but may increase computation time. Default is 1e-6. @@ -900,7 +898,7 @@ def get_initial_stoichiometries( The initial stoichiometries that give the desired initial state of charge """ esoh_solver = ElectrodeSOHSolver(parameter_values, param, known_value, options) - return esoh_solver.get_initial_stoichiometries(initial_value, solver_tolerance) + return esoh_solver.get_initial_stoichiometries(initial_value, tol) def get_min_max_stoichiometries( @@ -1029,7 +1027,7 @@ def theoretical_energy_integral(parameter_values, param, inputs, points=100): def calculate_theoretical_energy( - parameter_values, initial_soc=1.0, final_soc=0.0, points=100, solver_tolerance=1e-6 + parameter_values, initial_soc=1.0, final_soc=0.0, points=100, tol=1e-6 ): """ Calculate maximum energy possible from a cell given OCV, initial soc, and final soc @@ -1045,7 +1043,7 @@ def calculate_theoretical_energy( The soc at end of discharge, default 0.0 points : int The number of points at which to calculate voltage. - solver_tolerance: float + tol: float Tolerance for the solver used in calculating initial and final stoichiometries. Returns ------- @@ -1053,12 +1051,8 @@ def calculate_theoretical_energy( The total energy of the cell in Wh """ # Get initial and final stoichiometric values. - x_100, y_100 = get_initial_stoichiometries( - initial_soc, parameter_values, solver_tolerance=solver_tolerance - ) - x_0, y_0 = get_initial_stoichiometries( - final_soc, parameter_values, solver_tolerance=solver_tolerance - ) + x_100, y_100 = get_initial_stoichiometries(initial_soc, parameter_values, tol=tol) + x_0, y_0 = get_initial_stoichiometries(final_soc, parameter_values, tol=tol) Q_p = parameter_values.evaluate(pybamm.LithiumIonParameters().p.prim.Q_init) E = theoretical_energy_integral( parameter_values, diff --git a/pybamm/parameters/parameter_values.py b/pybamm/parameters/parameter_values.py index acb3ef5d86..4256561c96 100644 --- a/pybamm/parameters/parameter_values.py +++ b/pybamm/parameters/parameter_values.py @@ -307,7 +307,7 @@ def set_initial_stoichiometries( known_value="cyclable lithium capacity", inplace=True, options=None, - solver_tolerance=1e-6, + tol=1e-6, ): """ Set the initial stoichiometry of each electrode, based on the initial @@ -320,7 +320,7 @@ def set_initial_stoichiometries( param=param, known_value=known_value, options=options, - solver_tolerance=solver_tolerance, + tol=tol, ) if inplace: parameter_values = self From eb6e52a159ad5624c963decaa19db9744c7cbe76 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Wed, 24 Jan 2024 13:42:57 +0100 Subject: [PATCH 4/5] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1651ed889c..2ec6bc7efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ## Features +- Added the ability to specify a custom solver tolerance in `get_initial_stoichiometries` and related functions ([#3714](https://github.com/pybamm-team/PyBaMM/pull/3714)) - The `pybamm_install_odes` command now includes support for macOS systems and can be used to set up SUNDIALS and install the `scikits.odes` solver on macOS ([#3417](https://github.com/pybamm-team/PyBaMM/pull/3417), [#3706](https://github.com/pybamm-team/PyBaMM/3706])) - Added support for Python 3.12 ([#3531](https://github.com/pybamm-team/PyBaMM/pull/3531)) - Added method to get QuickPlot axes by variable ([#3596](https://github.com/pybamm-team/PyBaMM/pull/3596)) From 2bfcdd5a679e9b888de667d3e65684c4525cf231 Mon Sep 17 00:00:00 2001 From: AlessioBugetti Date: Wed, 24 Jan 2024 14:03:36 +0100 Subject: [PATCH 5/5] Fix CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec6bc7efe..2c02c6be84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,14 @@ - 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)). - The SEI thickness decreased at some intervals when the 'electron-migration limited' model was used. It has been corrected ([#3622](https://github.com/pybamm-team/PyBaMM/pull/3622)) +## Features + +- Added the ability to specify a custom solver tolerance in `get_initial_stoichiometries` and related functions ([#3714](https://github.com/pybamm-team/PyBaMM/pull/3714)) + # [v24.1rc1](https://github.com/pybamm-team/PyBaMM/tree/v24.1rc1) - 2024-01-17 ## Features -- Added the ability to specify a custom solver tolerance in `get_initial_stoichiometries` and related functions ([#3714](https://github.com/pybamm-team/PyBaMM/pull/3714)) - The `pybamm_install_odes` command now includes support for macOS systems and can be used to set up SUNDIALS and install the `scikits.odes` solver on macOS ([#3417](https://github.com/pybamm-team/PyBaMM/pull/3417), [#3706](https://github.com/pybamm-team/PyBaMM/3706])) - Added support for Python 3.12 ([#3531](https://github.com/pybamm-team/PyBaMM/pull/3531)) - Added method to get QuickPlot axes by variable ([#3596](https://github.com/pybamm-team/PyBaMM/pull/3596))