Skip to content

Commit

Permalink
Issue 4224 duration bug (#4239)
Browse files Browse the repository at this point in the history
* make longer default duration and calculate it for C-rate

* add tests

* typo

* #4224 add warning for time termination and add abs

* fix tests

* #4224 keep non-C-rate default at 24h for performance reasons

* trying to fix experiment

* fix example

* #4224 eric comments

* fix bug
  • Loading branch information
valentinsulzer authored and kratman committed Jul 11, 2024
1 parent 7b61e65 commit 41d0ef2
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
"Note: you may need to restart the kernel to use updated packages.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
]
}
],
"source": [
Expand Down Expand Up @@ -163,18 +153,19 @@
"name": "stderr",
"output_type": "stream",
"text": [
"At t = 522.66 and h = 1.1556e-13, the corrector convergence failed repeatedly or with |h| = hmin.\n"
"At t = 339.952 and h = 1.4337e-18, the corrector convergence failed repeatedly or with |h| = hmin.\n",
"At t = 522.687 and h = 4.04917e-14, the corrector convergence failed repeatedly or with |h| = hmin.\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "5ab1f22de6af4878b6ca43d27ffc01c5",
"model_id": "93feca98298f4111909ae487e2a1e273",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(FloatSlider(value=0.0, description='t', max=40.132949019384355, step=0.40132949019384356"
"interactive(children=(FloatSlider(value=0.0, description='t', max=40.13268704803602, step=0.4013268704803602),"
]
},
"metadata": {},
Expand All @@ -183,7 +174,7 @@
{
"data": {
"text/plain": [
"<pybamm.plotting.quick_plot.QuickPlot at 0x7f1a11f76690>"
"<pybamm.plotting.quick_plot.QuickPlot at 0x16520e690>"
]
},
"execution_count": 5,
Expand Down Expand Up @@ -211,12 +202,12 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "7cdac234d74241a2814918053454f6a6",
"model_id": "4d6e43032f4e4aa6be5843c4916b4b50",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(FloatSlider(value=0.0, description='t', max=13.076977041121545, step=0.13076977041121546"
"interactive(children=(FloatSlider(value=0.0, description='t', max=13.076887099589111, step=0.1307688709958911)"
]
},
"metadata": {},
Expand All @@ -225,7 +216,7 @@
{
"data": {
"text/plain": [
"<pybamm.plotting.quick_plot.QuickPlot at 0x7f1a105ecb50>"
"<pybamm.plotting.quick_plot.QuickPlot at 0x1696cf290>"
]
},
"execution_count": 6,
Expand Down Expand Up @@ -255,7 +246,7 @@
{
"data": {
"text/plain": [
"_Step(C-rate, 1.0, duration=1 hour, period=1 minute, temperature=25oC, tags=['tag1'], description=Discharge at 1C for 1 hour)"
"Step(1.0, duration=1 hour, period=1 minute, temperature=25oC, tags=['tag1'], description=Discharge at 1C for 1 hour, direction=Discharge)"
]
},
"execution_count": 7,
Expand Down Expand Up @@ -293,7 +284,7 @@
{
"data": {
"text/plain": [
"_Step(current, 1, duration=1 hour, termination=2.5 V)"
"Step(1, duration=1 hour, termination=2.5 V, direction=Discharge)"
]
},
"execution_count": 8,
Expand Down Expand Up @@ -321,7 +312,7 @@
{
"data": {
"text/plain": [
"_Step(current, 1.0, duration=1 hour, termination=2.5V, description=Discharge at 1A for 1 hour or until 2.5V)"
"Step(1.0, duration=1 hour, termination=2.5V, description=Discharge at 1A for 1 hour or until 2.5V, direction=Discharge)"
]
},
"execution_count": 9,
Expand All @@ -348,10 +339,78 @@
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-07-10 14:41:02.625 - [WARNING] callbacks.on_experiment_infeasible_time(240): \n",
"\n",
"\tExperiment is infeasible: default duration (1.0 seconds) was reached during 'Step([[ 0.00000000e+00 0.00000000e+00]\n",
" [ 1.69491525e-02 5.31467428e-02]\n",
" [ 3.38983051e-02 1.05691312e-01]\n",
" [ 5.08474576e-02 1.57038356e-01]\n",
" [ 6.77966102e-02 2.06606093e-01]\n",
" [ 8.47457627e-02 2.53832900e-01]\n",
" [ 1.01694915e-01 2.98183679e-01]\n",
" [ 1.18644068e-01 3.39155918e-01]\n",
" [ 1.35593220e-01 3.76285385e-01]\n",
" [ 1.52542373e-01 4.09151388e-01]\n",
" [ 1.69491525e-01 4.37381542e-01]\n",
" [ 1.86440678e-01 4.60655989e-01]\n",
" [ 2.03389831e-01 4.78711019e-01]\n",
" [ 2.20338983e-01 4.91342062e-01]\n",
" [ 2.37288136e-01 4.98406004e-01]\n",
" [ 2.54237288e-01 4.99822806e-01]\n",
" [ 2.71186441e-01 4.95576416e-01]\n",
" [ 2.88135593e-01 4.85714947e-01]\n",
" [ 3.05084746e-01 4.70350133e-01]\n",
" [ 3.22033898e-01 4.49656065e-01]\n",
" [ 3.38983051e-01 4.23867214e-01]\n",
" [ 3.55932203e-01 3.93275778e-01]\n",
" [ 3.72881356e-01 3.58228370e-01]\n",
" [ 3.89830508e-01 3.19122092e-01]\n",
" [ 4.06779661e-01 2.76400033e-01]\n",
" [ 4.23728814e-01 2.30546251e-01]\n",
" [ 4.40677966e-01 1.82080288e-01]\n",
" [ 4.57627119e-01 1.31551282e-01]\n",
" [ 4.74576271e-01 7.95317480e-02]\n",
" [ 4.91525424e-01 2.66110874e-02]\n",
" [ 5.08474576e-01 -2.66110874e-02]\n",
" [ 5.25423729e-01 -7.95317480e-02]\n",
" [ 5.42372881e-01 -1.31551282e-01]\n",
" [ 5.59322034e-01 -1.82080288e-01]\n",
" [ 5.76271186e-01 -2.30546251e-01]\n",
" [ 5.93220339e-01 -2.76400033e-01]\n",
" [ 6.10169492e-01 -3.19122092e-01]\n",
" [ 6.27118644e-01 -3.58228370e-01]\n",
" [ 6.44067797e-01 -3.93275778e-01]\n",
" [ 6.61016949e-01 -4.23867214e-01]\n",
" [ 6.77966102e-01 -4.49656065e-01]\n",
" [ 6.94915254e-01 -4.70350133e-01]\n",
" [ 7.11864407e-01 -4.85714947e-01]\n",
" [ 7.28813559e-01 -4.95576416e-01]\n",
" [ 7.45762712e-01 -4.99822806e-01]\n",
" [ 7.62711864e-01 -4.98406004e-01]\n",
" [ 7.79661017e-01 -4.91342062e-01]\n",
" [ 7.96610169e-01 -4.78711019e-01]\n",
" [ 8.13559322e-01 -4.60655989e-01]\n",
" [ 8.30508475e-01 -4.37381542e-01]\n",
" [ 8.47457627e-01 -4.09151388e-01]\n",
" [ 8.64406780e-01 -3.76285385e-01]\n",
" [ 8.81355932e-01 -3.39155918e-01]\n",
" [ 8.98305085e-01 -2.98183679e-01]\n",
" [ 9.15254237e-01 -2.53832900e-01]\n",
" [ 9.32203390e-01 -2.06606093e-01]\n",
" [ 9.49152542e-01 -1.57038356e-01]\n",
" [ 9.66101695e-01 -1.05691312e-01]\n",
" [ 9.83050847e-01 -5.31467428e-02]\n",
" [ 1.00000000e+00 -1.22464680e-16]], duration=1.0, period=0.016949152542372836, direction=Rest)'. The returned solution only contains up to step 1 of cycle 1. Please specify a duration in the step instructions.\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "730d5e19b17e447ebde5679de68c46ef",
"model_id": "6364b4579fc447e2a607f2f8414172ba",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -365,7 +424,7 @@
{
"data": {
"text/plain": [
"<pybamm.plotting.quick_plot.QuickPlot at 0x7f1a1a4c2ed0>"
"<pybamm.plotting.quick_plot.QuickPlot at 0x16a1d8d90>"
]
},
"execution_count": 10,
Expand Down Expand Up @@ -419,13 +478,14 @@
"output_type": "stream",
"text": [
"[1] Joel A. E. Andersson, Joris Gillis, Greg Horn, James B. Rawlings, and Moritz Diehl. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.\n",
"[2] 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",
"[3] 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",
"[4] 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",
"[5] 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",
"[6] 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",
"[7] 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",
"[8] Andrew Weng, Jason B Siegel, and Anna Stefanopoulou. Differential voltage analysis for battery manufacturing process control. arXiv preprint arXiv:2303.07088, 2023.\n",
"[2] Von DAG Bruggeman. Berechnung verschiedener physikalischer konstanten von heterogenen substanzen. i. dielektrizitätskonstanten und leitfähigkeiten der mischkörper aus isotropen substanzen. Annalen der physik, 416(7):636–664, 1935.\n",
"[3] 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",
"[4] 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",
"[5] 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",
"[6] 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",
"[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",
"[8] 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",
"[9] Andrew Weng, Jason B Siegel, and Anna Stefanopoulou. Differential voltage analysis for battery manufacturing process control. arXiv preprint arXiv:2303.07088, 2023.\n",
"\n"
]
}
Expand Down
40 changes: 29 additions & 11 deletions pybamm/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,37 @@ def on_experiment_start(self, logs):
"""
Called at the start of an experiment simulation.
"""
pass
pass # pragma: no cover

def on_cycle_start(self, logs):
"""
Called at the start of each cycle in an experiment simulation.
"""
pass
pass # pragma: no cover

def on_step_start(self, logs):
"""
Called at the start of each step in an experiment simulation.
"""
pass
pass # pragma: no cover

def on_step_end(self, logs):
"""
Called at the end of each step in an experiment simulation.
"""
pass
pass # pragma: no cover

def on_cycle_end(self, logs):
"""
Called at the end of each cycle in an experiment simulation.
"""
pass
pass # pragma: no cover

def on_experiment_end(self, logs):
"""
Called at the end of an experiment simulation.
"""
pass
pass # pragma: no cover

def on_experiment_error(self, logs):
"""
Expand All @@ -75,13 +75,19 @@ def on_experiment_error(self, logs):
For example, this could be used to send an error alert with a bug report when
running batch simulations in the cloud.
"""
pass
pass # pragma: no cover

def on_experiment_infeasible(self, logs):
def on_experiment_infeasible_time(self, logs):
"""
Called when an experiment simulation is infeasible.
Called when an experiment simulation is infeasible due to reaching maximum time.
"""
pass
pass # pragma: no cover

def on_experiment_infeasible_event(self, logs):
"""
Called when an experiment simulation is infeasible due to an event.
"""
pass # pragma: no cover


########################################################################################
Expand Down Expand Up @@ -226,7 +232,19 @@ def on_experiment_error(self, logs):
error = logs["error"]
pybamm.logger.error(f"Simulation error: {error}")

def on_experiment_infeasible(self, logs):
def on_experiment_infeasible_time(self, logs):
duration = logs["step duration"]
cycle_num = logs["cycle number"][0]
step_num = logs["step number"][0]
operating_conditions = logs["step operating conditions"]
self.logger.warning(
f"\n\n\tExperiment is infeasible: default duration ({duration} seconds) "
f"was reached during '{operating_conditions}'. The returned solution only "
f"contains up to step {step_num} of cycle {cycle_num}. "
"Please specify a duration in the step instructions."
)

def on_experiment_infeasible_event(self, logs):
termination = logs["termination"]
cycle_num = logs["cycle number"][0]
step_num = logs["step number"][0]
Expand Down
23 changes: 17 additions & 6 deletions pybamm/experiment/step/base_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def __init__(
description=None,
direction=None,
):
self.input_duration = duration
self.input_value = value
# Check if drive cycle
is_drive_cycle = isinstance(value, np.ndarray)
is_python_function = callable(value)
Expand Down Expand Up @@ -100,8 +102,11 @@ def __init__(
f"Input function must return a real number output at t = {t0}"
)

# Record whether the step uses the default duration
# This will be used by the experiment to check whether the step is feasible
self.uses_default_duration = duration is None
# Set duration
if duration is None:
if self.uses_default_duration:
duration = self.default_duration(value)
self.duration = _convert_time_to_seconds(duration)

Expand Down Expand Up @@ -195,8 +200,8 @@ def copy(self):
A copy of the step.
"""
return self.__class__(
self.value,
duration=self.duration,
self.input_value,
duration=self.input_duration,
termination=self.termination,
period=self.period,
temperature=self.temperature,
Expand Down Expand Up @@ -259,7 +264,7 @@ def default_duration(self, value):
t = value[:, 0]
return t[-1]
else:
return 24 * 3600 # 24 hours in seconds
return 24 * 3600 # one day in seconds

def process_model(self, model, parameter_values):
new_model = model.new_copy()
Expand Down Expand Up @@ -411,10 +416,16 @@ def set_up(self, new_model, new_parameter_values):

def _convert_time_to_seconds(time_and_units):
"""Convert a time in seconds, minutes or hours to a time in seconds"""
# If the time is a number, assume it is in seconds
if isinstance(time_and_units, numbers.Number) or time_and_units is None:
if time_and_units is None:
return time_and_units

# If the time is a number, assume it is in seconds
if isinstance(time_and_units, numbers.Number):
if time_and_units <= 0:
raise ValueError("time must be positive")
else:
return time_and_units

# Split number and units
units = time_and_units.lstrip("0123456789.- ")
time = time_and_units[: -len(units)]
Expand Down
5 changes: 5 additions & 0 deletions pybamm/experiment/step/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ def __init__(self, value, **kwargs):
def current_value(self, variables):
return self.value * pybamm.Parameter("Nominal cell capacity [A.h]")

def default_duration(self, value):
# "value" is C-rate, so duration is "1 / value" hours in seconds
# with a 2x safety factor
return 1 / abs(value) * 3600 * 2


def c_rate(value, **kwargs):
"""
Expand Down
Loading

0 comments on commit 41d0ef2

Please sign in to comment.