Skip to content

Commit

Permalink
Summary variables calculated only when called (pybamm-team#4621)
Browse files Browse the repository at this point in the history
* Create & use SummaryVariables class

* Stop looping back to recalculate eSOH vars

* tests pass

* Cleanup SummaryVariables class

* Add SummaryVariables to API docs

* style: pre-commit fixes

* rename solution.set_summary_variables to update_summary_variables

* Add typing, move some stuff around

* edit typing

* Pass through calc_esoh when creating SummaryVariables with sub-cycles

* Add tests

* Document 'cycle_number'

* Update changelog

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric G. Kratz <[email protected]>
  • Loading branch information
3 people authored Dec 19, 2024
1 parent f851d5d commit 735ffbd
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

## Breaking changes

- Summary variables now calculated only when called, accessed via a class in the same manner as other variables rather than a dictionary. ([#4621](https://github.com/pybamm-team/PyBaMM/pull/4621))
- The conda distribution (`pybamm`) now installs all optional dependencies available on conda-forge. Use the new `pybamm-base` conda
package to install PyBaMM with only the required dependencies. ([conda-forge/pybamm-feedstock#70](https://github.com/conda-forge/pybamm-feedstock/pull/70))
- Separated extrapolation options for `pybamm.BoundaryValue` and `pybamm.BoundaryGradient`, and updated the default to be "linear" for the value and "quadratic" for the gradient. ([#4614](https://github.com/pybamm-team/PyBaMM/pull/4614))
Expand Down
1 change: 1 addition & 0 deletions docs/source/api/solvers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ Solvers
algebraic_solvers
solution
processed_variable
summary_variables
5 changes: 5 additions & 0 deletions docs/source/api/solvers/summary_variables.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Summary Variables
======================

.. autoclass:: pybamm.SummaryVariables
:members:
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": null,
"id": "right-skiing",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -638,7 +638,7 @@
}
],
"source": [
"sorted(sol.summary_variables.keys())"
"sorted(sol.summary_variables.all_variables)"
]
},
{
Expand Down Expand Up @@ -1936,7 +1936,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "venv",
"language": "python",
"name": "python3"
},
Expand All @@ -1950,7 +1950,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
"version": "3.11.10"
},
"toc": {
"base_numbering": 1,
Expand All @@ -1964,11 +1964,6 @@
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
},
"vscode": {
"interpreter": {
"hash": "612adcc456652826e82b485a1edaef831aa6d5abc680d008e93d513dd8724f14"
}
}
},
"nbformat": 4,
Expand Down
1 change: 1 addition & 0 deletions src/pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
from .solvers.processed_variable_time_integral import ProcessedVariableTimeIntegral
from .solvers.processed_variable import ProcessedVariable, process_variable
from .solvers.processed_variable_computed import ProcessedVariableComputed
from .solvers.summary_variable import SummaryVariables
from .solvers.base_solver import BaseSolver
from .solvers.dummy_solver import DummySolver
from .solvers.algebraic_solver import AlgebraicSolver
Expand Down
2 changes: 1 addition & 1 deletion src/pybamm/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def on_cycle_end(self, logs):

voltage_stop = logs["stopping conditions"]["voltage"]
if voltage_stop is not None:
min_voltage = logs["summary variables"]["Minimum voltage [V]"]
min_voltage = logs["Minimum voltage [V]"]
if min_voltage > voltage_stop[0]:
self.logger.notice(
f"Minimum voltage is now {min_voltage:.3f} V "
Expand Down
21 changes: 21 additions & 0 deletions src/pybamm/models/full_battery_models/lithium_ion/electrode_soh.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,27 @@ def __init__(
self.__get_electrode_soh_sims_split
)

def __getstate__(self):
"""
Return dictionary of picklable items
"""
result = self.__dict__.copy()
result["_get_electrode_soh_sims_full"] = None # Exclude LRU cache
result["_get_electrode_soh_sims_split"] = None # Exclude LRU cache
return result

def __setstate__(self, state):
"""
Unpickle, restoring unpicklable relationships
"""
self.__dict__ = state
self._get_electrode_soh_sims_full = lru_cache()(
self.__get_electrode_soh_sims_full
)
self._get_electrode_soh_sims_split = lru_cache()(
self.__get_electrode_soh_sims_split
)

def _get_lims_ocp(self):
parameter_values = self.parameter_values

Expand Down
4 changes: 2 additions & 2 deletions src/pybamm/plotting/plot_summary_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ def plot_summary_variables(
for solution in solutions:
# plot summary variable v/s cycle number
ax.plot(
solution.summary_variables["Cycle number"],
solution.summary_variables.cycle_number,
solution.summary_variables[var],
)
# label the axes
ax.set_xlabel("Cycle number")
ax.set_ylabel(var)
ax.set_xlim([1, solution.summary_variables["Cycle number"][-1]])
ax.set_xlim([1, solution.summary_variables.cycle_number[-1]])

fig.tight_layout()

Expand Down
4 changes: 2 additions & 2 deletions src/pybamm/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ def solve(
# See PR #3995
if voltage_stop is not None:
min_voltage = np.min(cycle_solution["Battery voltage [V]"].data)
logs["summary variables"]["Minimum voltage [V]"] = min_voltage
logs["Minimum voltage [V]"] = min_voltage

callbacks.on_cycle_end(logs)

Expand All @@ -941,7 +941,7 @@ def solve(

if self._solution is not None and len(all_cycle_solutions) > 0:
self._solution.cycles = all_cycle_solutions
self._solution.set_summary_variables(all_summary_variables)
self._solution.update_summary_variables(all_summary_variables)
self._solution.all_first_states = all_first_states

callbacks.on_experiment_end(logs)
Expand Down
59 changes: 5 additions & 54 deletions src/pybamm/solvers/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,16 +563,10 @@ def initial_start_time(self, value):
"""Updates the initial start time of the experiment"""
self._initial_start_time = value

def set_summary_variables(self, all_summary_variables):
summary_variables = {var: [] for var in all_summary_variables[0]}
for sum_vars in all_summary_variables:
for name, value in sum_vars.items():
summary_variables[name].append(value)

summary_variables["Cycle number"] = range(1, len(all_summary_variables) + 1)
def update_summary_variables(self, all_summary_variables):
self.all_summary_variables = all_summary_variables
self._summary_variables = pybamm.FuzzyDict(
{name: np.array(value) for name, value in summary_variables.items()}
self._summary_variables = pybamm.SummaryVariables(
self, cycle_summary_variables=all_summary_variables
)

def update(self, variables):
Expand Down Expand Up @@ -1142,8 +1136,8 @@ def make_cycle_solution(

cycle_solution.steps = step_solutions

cycle_summary_variables = _get_cycle_summary_variables(
cycle_solution, esoh_solver, user_inputs=inputs
cycle_summary_variables = pybamm.SummaryVariables(
cycle_solution, esoh_solver=esoh_solver, user_inputs=inputs
)

cycle_first_state = cycle_solution.first_state
Expand All @@ -1154,46 +1148,3 @@ def make_cycle_solution(
cycle_solution = None

return cycle_solution, cycle_summary_variables, cycle_first_state


def _get_cycle_summary_variables(cycle_solution, esoh_solver, user_inputs=None):
user_inputs = user_inputs or {}
model = cycle_solution.all_models[0]
cycle_summary_variables = pybamm.FuzzyDict({})

# Summary variables
summary_variables = model.summary_variables
first_state = cycle_solution.first_state
last_state = cycle_solution.last_state
for var in summary_variables:
data_first = first_state[var].data
data_last = last_state[var].data
cycle_summary_variables[var] = data_last[0]
var_lowercase = var[0].lower() + var[1:]
cycle_summary_variables["Change in " + var_lowercase] = (
data_last[0] - data_first[0]
)

# eSOH variables (full-cell lithium-ion model only, for now)
if (
esoh_solver is not None
and isinstance(model, pybamm.lithium_ion.BaseModel)
and model.options.electrode_types["negative"] == "porous"
and "Negative electrode capacity [A.h]" in model.variables
and "Positive electrode capacity [A.h]" in model.variables
):
Q_n = last_state["Negative electrode capacity [A.h]"].data[0]
Q_p = last_state["Positive electrode capacity [A.h]"].data[0]
Q_Li = last_state["Total lithium capacity in particles [A.h]"].data[0]
all_inputs = {**user_inputs, "Q_n": Q_n, "Q_p": Q_p, "Q_Li": Q_Li}
try:
esoh_sol = esoh_solver.solve(inputs=all_inputs)
except pybamm.SolverError as error: # pragma: no cover
raise pybamm.SolverError(
"Could not solve for summary variables, run "
"`sim.solve(calc_esoh=False)` to skip this step"
) from error

cycle_summary_variables.update(esoh_sol)

return cycle_summary_variables
Loading

0 comments on commit 735ffbd

Please sign in to comment.