Skip to content

Commit

Permalink
Merge pull request pybamm-team#3596 from pybamm-team/issue-3530-custo…
Browse files Browse the repository at this point in the history
…m-termination

Issue 3530 custom termination
  • Loading branch information
valentinsulzer authored Dec 8, 2023
2 parents fe3eb65 + 3468e08 commit 4ad2853
Show file tree
Hide file tree
Showing 27 changed files with 570 additions and 81 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

## Features

- Added method to get QuickPlot axes by variable ([#3596](https://github.com/pybamm-team/PyBaMM/pull/3596))
- Added custom experiment terminations ([#3596](https://github.com/pybamm-team/PyBaMM/pull/3596))
- Mechanical parameters are now a function of stoichiometry and temperature ([#3576](https://github.com/pybamm-team/PyBaMM/pull/3576))
- Added a new unary operator, `EvaluateAt`, that evaluates a spatial variable at a given position ([#3573](https://github.com/pybamm-team/PyBaMM/pull/3573))
- Added a method, `insert_reference_electrode`, to `pybamm.lithium_ion.BaseModel` that insert a reference electrode to measure the electrolyte potential at a given position in space and adds new variables that mimic a 3E cell setup. ([#3573](https://github.com/pybamm-team/PyBaMM/pull/3573))
- Serialisation added so models can be written to/read from JSON ([#3397](https://github.com/pybamm-team/PyBaMM/pull/3397))
- Mechanical parameters are now a function of stoichiometry and temperature ([#3576](https://github.com/pybamm-team/PyBaMM/pull/3576))

## Bug fixes

Expand Down
25 changes: 25 additions & 0 deletions docs/source/api/experiment/experiment_steps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,28 @@ directly:

.. autoclass:: pybamm.step._Step
:members:

Step terminations
-----------------

Standard step termination events are implemented by the following classes, which are
called when the termination is specified by a specific string. These classes can be
either be called directly or via the string format specified in the class docstring

.. autoclass:: pybamm.step.CrateTermination
:members:

.. autoclass:: pybamm.step.CurrentTermination
:members:

.. autoclass:: pybamm.step.VoltageTermination
:members:

The following classes can be used to define custom terminations for an experiment
step:

.. autoclass:: pybamm.step.BaseTermination
:members:

.. autoclass:: pybamm.step.CustomTermination
:members:
3 changes: 3 additions & 0 deletions docs/source/api/plotting/quick_plot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ Quick Plot
:members:

.. autofunction:: pybamm.dynamic_plot

.. autoclass:: pybamm.QuickPlotAxes
:members:
16 changes: 11 additions & 5 deletions docs/source/examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ The notebooks are organised into subfolders, and can be viewed in the galleries
notebooks/parameterization/parameter-values.ipynb
notebooks/parameterization/parameterization.ipynb

.. nbgallery::
:caption: Simulations and Experiments
:glob:

notebooks/simulations_and_experiments/callbacks.ipynb
notebooks/simulations_and_experiments/custom-experiments.ipynb
notebooks/simulations_and_experiments/experiments-start-time.ipynb
notebooks/simulations_and_experiments/rpt-experiment.ipynb
notebooks/simulations_and_experiments/simulating-long-experiments.ipynb
notebooks/simulations_and_experiments/simulation-class.ipynb

.. nbgallery::
:caption: Plotting
:glob:
Expand All @@ -111,11 +122,6 @@ The notebooks are organised into subfolders, and can be viewed in the galleries
:glob:

notebooks/batch_study.ipynb
notebooks/callbacks.ipynb
notebooks/change-settings.ipynb
notebooks/initialize-model-with-solution.ipynb
notebooks/rpt-experiment.ipynb
notebooks/simulating-long-experiments.ipynb
notebooks/simulation-class.ipynb
notebooks/solution-data-and-processed-variables.ipynb
notebooks/experiments-start-time.ipynb
2 changes: 1 addition & 1 deletion docs/source/examples/notebooks/batch_study.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The difference in the individual plots is not very well visible in the above slider plot, but we can access all the simulations created by `BatchStudy` (`batch_study.sims`) and pass it to `pybamm.plot_summary_variables` to plot the summary variables (more details on \"summary variables\" are available in the [`simulating-long-experiments`](https://github.com/pybamm-team/PyBaMM/blob/develop/docs/source/examples/notebooks/simulating-long-experiments.ipynb) notebook).\n",
"The difference in the individual plots is not very well visible in the above slider plot, but we can access all the simulations created by `BatchStudy` (`batch_study.sims`) and pass it to `pybamm.plot_summary_variables` to plot the summary variables (more details on \"summary variables\" are available in the [`simulating-long-experiments`](./simulations_and_experiments/simulating-long-experiments.ipynb) notebook).\n",
"\n",
"## Comparing summary variables"
]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/notebooks/change-settings.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"source": [
"# Changing settings when solving PyBaMM models\n",
"\n",
"[This](https://github.com/pybamm-team/PyBaMM/blob/develop/docs/source/examples/notebooks/models/SPM.ipynb) example notebook showed how to run an SPM battery model, using the default parameters, discretisation and solvers that were defined for that particular model. Naturally we would like the ability to alter these options on a case by case basis, and this notebook gives an example of how to do this, again using the SPM model. In this notebook we explicitly handle all the stages of setting up, processing and solving the model in order to explain them in detail. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](https://github.com/pybamm-team/PyBaMM/blob/develop/docs/source/examples/notebooks/simulation-class.ipynb).\n",
"[This](./models/SPM.ipynb) example notebook showed how to run an SPM battery model, using the default parameters, discretisation and solvers that were defined for that particular model. Naturally we would like the ability to alter these options on a case by case basis, and this notebook gives an example of how to do this, again using the SPM model. In this notebook we explicitly handle all the stages of setting up, processing and solving the model in order to explain them in detail. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](./simulations_and_experiments/simulation-class.ipynb).\n",
"\n",
"\n",
"### Table of Contents\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.0"
"version": "3.11.3"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/notebooks/models/DFN.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"source": [
"Below we show how to solve the DFN model, using the default geometry, mesh, parameters, discretisation and solver provided with PyBaMM. For a more detailed example, see the notebook on the [SPM](https://github.com/pybamm-team/PyBaMM/blob/develop/docs/source/examples/notebooks/models/SPM.ipynb).\n",
"\n",
"In order to show off all the different points at which the process of setting up and solving a model in PyBaMM can be customised we explicitly handle the stages of choosing a geometry, setting parameters, discretising the model and solving the model. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](../simulation-class.ipynb).\n",
"In order to show off all the different points at which the process of setting up and solving a model in PyBaMM can be customised we explicitly handle the stages of choosing a geometry, setting parameters, discretising the model and solving the model. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](../simulations_and_experiments/simulation-class.ipynb).\n",
"\n",
"First we need to import pybamm, along with numpy which we will use in this notebook."
]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/notebooks/models/SPM.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"source": [
"## Example solving SPM using PyBaMM\n",
"\n",
"Below we show how to solve the Single Particle Model, using the default geometry, mesh, parameters, discretisation and solver provided with PyBaMM. In this notebook we explicitly handle all the stages of setting up, processing and solving the model in order to explain them in detail. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](../simulation-class.ipynb).\n",
"Below we show how to solve the Single Particle Model, using the default geometry, mesh, parameters, discretisation and solver provided with PyBaMM. In this notebook we explicitly handle all the stages of setting up, processing and solving the model in order to explain them in detail. However, it is often simpler in practice to use the `Simulation` class, which handles many of the stages automatically, as shown [here](../simulations_and_experiments/simulation-class.ipynb).\n",
"\n",
"First we need to import `pybamm`, and then change our working directory to the root of the pybamm folder. "
]
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"metadata": {},
"source": [
"# A step-by-step look at the Simulation class\n",
"The simplest way to solve a model is to use the `Simulation` class. This automatically processes the model (setting of parameters, setting up the mesh and discretisation, etc.) for you, and provides built-in functionality for solving and plotting. Changing things such as parameters in handled by passing options to the `Simulation`, as shown in the [Getting Started](getting_started/tutorial-1-how-to-run-a-model.ipynb) guides, [example notebooks](../index.rst) and [documentation](https://docs.pybamm.org/en/latest/source/api/simulation.html).\n",
"The simplest way to solve a model is to use the `Simulation` class. This automatically processes the model (setting of parameters, setting up the mesh and discretisation, etc.) for you, and provides built-in functionality for solving and plotting. Changing things such as parameters in handled by passing options to the `Simulation`, as shown in the [Getting Started](../getting_started/tutorial-1-how-to-run-a-model.ipynb) guides, [example notebooks](../../index.rst) and [documentation](https://docs.pybamm.org/en/latest/source/api/simulation.html).\n",
"\n",
"In this notebook we show how to solve a model using a `Simulation` and compare this to manually handling the different stages of the process, such as setting parameters, ourselves step-by-step."
]
Expand Down Expand Up @@ -152,7 +152,7 @@
"metadata": {},
"source": [
"## Processing the model step-by-step\n",
"One way of gaining more control over the simulation processing is by passing options, as outlined in the [documentation](https://docs.pybamm.org/en/latest/source/api/simulation.html). However, you can also process the model step-by-step yourself. A detailed example of this can be found in the [SPM notebook](./models/SPM.ipynb), but here we outline the basic steps.\n",
"One way of gaining more control over the simulation processing is by passing options, as outlined in the [documentation](https://docs.pybamm.org/en/latest/source/api/simulation.html). However, you can also process the model step-by-step yourself. A detailed example of this can be found in the [SPM notebook](../models/SPM.ipynb), but here we outline the basic steps.\n",
"\n",
"First we pick a model"
]
Expand All @@ -171,7 +171,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we must set up the geometry. We'll use the default geometry for the SPM. In all of the following steps we will also use the default settings provided by the model. For a look at changing these options, see the [change settings](./change-settings.ipynb) notebook."
"Next we must set up the geometry. We'll use the default geometry for the SPM. In all of the following steps we will also use the default settings provided by the model. For a look at changing these options, see the [change settings](../change-settings.ipynb) notebook."
]
},
{
Expand Down
3 changes: 2 additions & 1 deletion pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,13 @@
#
from .experiment.experiment import Experiment
from . import experiment
from .experiment import step


#
# Plotting
#
from .plotting.quick_plot import QuickPlot, close_plots
from .plotting.quick_plot import QuickPlot, close_plots, QuickPlotAxes
from .plotting.plot import plot
from .plotting.plot2D import plot2D
from .plotting.plot_voltage_components import plot_voltage_components
Expand Down
2 changes: 1 addition & 1 deletion pybamm/experiment/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#

import pybamm
from pybamm.step._steps_util import (
from .step._steps_util import (
_convert_time_to_seconds,
_convert_temperature_to_kelvin,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .steps import *
from .steps import _Step
from .step_termination import *
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pybamm
import numpy as np
from datetime import datetime
from .step_termination import _read_termination

_examples = """
Expand Down Expand Up @@ -136,8 +137,10 @@ def __init__(
termination = [termination]
self.termination = []
for term in termination:
typ, value = _convert_electric(term)
self.termination.append({"type": typ, "value": value})
if isinstance(term, str):
term = _convert_electric(term)
term = _read_termination(term)
self.termination.append(term)

self.temperature = _convert_temperature_to_kelvin(temperature)

Expand Down Expand Up @@ -193,10 +196,7 @@ def to_dict(self):
}

def __eq__(self, other):
return (
isinstance(other, _Step)
and self.hash_args == other.hash_args
)
return isinstance(other, _Step) and self.hash_args == other.hash_args

def __hash__(self):
return hash(self.basic_repr())
Expand Down
164 changes: 164 additions & 0 deletions pybamm/experiment/step/step_termination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import pybamm
import numpy as np


class BaseTermination:
"""
Base class for a termination event for an experiment step. To create a custom
termination, a class must implement `get_event` to return a :class:`pybamm.Event`
corresponding to the desired termination. In most cases the class
:class:`pybamm.step.CustomTermination` can be used to assist with this.
Parameters
----------
value : float
The value at which the event is triggered
"""

def __init__(self, value):
self.value = value

def get_event(self, variables, step_value):
"""
Return a :class:`pybamm.Event` object corresponding to the termination event
Parameters
----------
variables : dict
Dictionary of model variables, to be used for selecting the variable(s) that
determine the event
step_value : float or :class:`pybamm.Symbol`
Value of the step for which this is a termination event, to be used in some
cases to determine the sign of the event.
"""
raise NotImplementedError

def __eq__(self, other):
# objects are equal if they have the same type and value
if isinstance(other, self.__class__):
return self.value == other.value
else:
return False


class CrateTermination(BaseTermination):
"""
Termination based on C-rate, created when a string termination of the C-rate type
(e.g. "C/10") is provided
"""

def get_event(self, variables, step_value):
"""
See :meth:`BaseTermination.get_event`
"""
event = pybamm.Event(
"C-rate cut-off [experiment]",
abs(variables["C-rate"]) - self.value,
)
return event


class CurrentTermination(BaseTermination):
"""
Termination based on current, created when a string termination of the current type
(e.g. "1A") is provided
"""

def get_event(self, variables, step_value):
"""
See :meth:`BaseTermination.get_event`
"""
event = pybamm.Event(
"Current cut-off [A] [experiment]",
abs(variables["Current [A]"]) - self.value,
)
return event


class VoltageTermination(BaseTermination):
"""
Termination based on voltage, created when a string termination of the voltage type
(e.g. "4.2V") is provided
"""

def get_event(self, variables, step_value):
"""
See :meth:`BaseTermination.get_event`
"""
# The voltage event should be positive at the start of charge/
# discharge. We use the sign of the current or power input to
# figure out whether the voltage event is greater than the starting
# voltage (charge) or less (discharge) and set the sign of the
# event accordingly
if isinstance(step_value, pybamm.Symbol):
inpt = {"start time": 0}
init_curr = step_value.evaluate(t=0, inputs=inpt).flatten()[0]
else:
init_curr = step_value
sign = np.sign(init_curr)
if sign > 0:
name = "Discharge"
else:
name = "Charge"
if sign != 0:
# Event should be positive at initial conditions for both
# charge and discharge
event = pybamm.Event(
f"{name} voltage cut-off [V] [experiment]",
sign * (variables["Battery voltage [V]"] - self.value),
)
return event


class CustomTermination(BaseTermination):
"""
Define a custom termination event using a function. This can be used to create an
event based on any variable in the model.
Parameters
----------
name : str
Name of the event
event_function : callable
A function that takes in a dictionary of variables and evaluates the event
value. Must be positive before the event is triggered and zero when the
event is triggered.
Example
-------
Add a cut-off based on negative electrode stoichiometry. The event will trigger
when the negative electrode stoichiometry reaches 10%.
>>> def neg_stoich_cutoff(variables):
... return variables["Negative electrode stoichiometry"] - 0.1
>>> neg_stoich_termination = pybamm.step.CustomTermination(
... name="Negative stoichiometry cut-off", event_function=neg_stoich_cutoff
... )
"""

def __init__(self, name, event_function):
if not name.endswith(" [experiment]"):
name += " [experiment]"
self.name = name
self.event_function = event_function

def get_event(self, variables, step_value):
"""
See :meth:`BaseTermination.get_event`
"""
return pybamm.Event(self.name, self.event_function(variables))


def _read_termination(termination):
if isinstance(termination, tuple):
typ, value = termination
else:
return termination

termination_class = {
"current": CurrentTermination,
"voltage": VoltageTermination,
"C-rate": CrateTermination,
}[typ]
return termination_class(value)
File renamed without changes.
Loading

0 comments on commit 4ad2853

Please sign in to comment.