Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 3530 custom termination #3596

Merged
merged 17 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@

doctest_global_setup = """
from docs import *
import pybamm
"""

# Add any paths that contain templates here, relative to this directory.
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/simulation_and_experiments/callbacks.ipynb
notebooks/simulation_and_experiments/custom-experiments.ipynb
notebooks/simulation_and_experiments/experiments-start-time.ipynb
notebooks/simulation_and_experiments/rpt-experiment.ipynb
notebooks/simulation_and_experiments/simulating-long-experiments.ipynb
notebooks/simulation_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
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

Large diffs are not rendered by default.

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
1 change: 0 additions & 1 deletion pybamm/citations.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class Citations:

Examples
--------
>>> import pybamm
>>> pybamm.citations.register("Sulzer2021")
>>> pybamm.citations.register("@misc{Newton1687, title={Mathematical...}}")
>>> pybamm.print_citations("citations.txt")
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

Check warning on line 34 in pybamm/experiment/step/step_termination.py

View check run for this annotation

Codecov / codecov/patch

pybamm/experiment/step/step_termination.py#L34

Added line #L34 was not covered by tests

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

Check warning on line 41 in pybamm/experiment/step/step_termination.py

View check run for this annotation

Codecov / codecov/patch

pybamm/experiment/step/step_termination.py#L41

Added line #L41 was not covered by tests


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.
8 changes: 6 additions & 2 deletions pybamm/expression_tree/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,12 @@ def set_id(self):
need to hash once.
"""
self._id = hash(
(self.__class__, self.name, *tuple([child.id for child in self.children]), *tuple([(k, tuple(v)) for k, v in self.domains.items() if v != []]))
(
self.__class__,
self.name,
*tuple([child.id for child in self.children]),
*tuple([(k, tuple(v)) for k, v in self.domains.items() if v != []]),
)
)

@property
Expand Down Expand Up @@ -532,7 +537,6 @@ def pre_order(self):
Examples
--------

>>> import pybamm
>>> a = pybamm.Symbol('a')
>>> b = pybamm.Symbol('b')
>>> for node in (a*b).pre_order():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class Thevenin(pybamm.BaseModel):

Examples
--------
>>> import pybamm
>>> model = pybamm.equivalent_circuit.Thevenin()
>>> model.name
'Thevenin Equivalent Circuit Model'
Expand Down
1 change: 0 additions & 1 deletion pybamm/models/full_battery_models/lithium_ion/dfn.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class DFN(BaseModel):

Examples
--------
>>> import pybamm
>>> model = pybamm.lithium_ion.DFN()
>>> model.name
'Doyle-Fuller-Newman model'
Expand Down
1 change: 0 additions & 1 deletion pybamm/models/full_battery_models/lithium_ion/mpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class MPM(SPM):

Examples
--------
>>> import pybamm
>>> model = pybamm.lithium_ion.MPM()
>>> model.name
'Many-Particle Model'
Expand Down
1 change: 0 additions & 1 deletion pybamm/models/full_battery_models/lithium_ion/spm.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class SPM(BaseModel):

Examples
--------
>>> import pybamm
>>> model = pybamm.lithium_ion.SPM()
>>> model.name
'Single Particle Model'
Expand Down
1 change: 0 additions & 1 deletion pybamm/models/full_battery_models/lithium_ion/spme.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class SPMe(SPM):

Examples
--------
>>> import pybamm
>>> model = pybamm.lithium_ion.SPMe()
>>> model.name
'Single Particle Model with electrolyte'
Expand Down
Loading
Loading