Skip to content

Commit

Permalink
Merge pull request pybamm-team#2616 from pybamm-team/issue-1839-time-…
Browse files Browse the repository at this point in the history
…based-experiments

Timestamped experiments
  • Loading branch information
valentinsulzer authored Jun 16, 2023
2 parents e8300b6 + f641985 commit c2ca937
Show file tree
Hide file tree
Showing 9 changed files with 532 additions and 19 deletions.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- Enable multithreading in IDAKLU solver ([#2947](https://github.com/pybamm-team/PyBaMM/pull/2947))
- If a solution contains cycles and steps, the cycle number and step number are now saved when `solution.save_data()` is called ([#2931](https://github.com/pybamm-team/PyBaMM/pull/2931))
- Experiments can now be given a `start_time` to define when each step should be triggered ([#2616](https://github.com/pybamm-team/PyBaMM/pull/2616))

## Optimizations

Expand Down Expand Up @@ -38,20 +39,21 @@
## Features

- Added verbose logging to `pybamm.print_citations()` and citation tags for the `pybamm.Citations` class so that users can now see where the citations were registered when running simulations ([#2862](https://github.com/pybamm-team/PyBaMM/pull/2862))
- Updated to casadi 3.6, which required some changes to the casadi integrator ([#2859](https://github.com/pybamm-team/PyBaMM/pull/2859))
- PyBaMM is now natively supported on Apple silicon chips (`M1/M2`) ([#2435](https://github.com/pybamm-team/PyBaMM/pull/2435))
- PyBaMM is now supported on Python `3.10` and `3.11` ([#2435](https://github.com/pybamm-team/PyBaMM/pull/2435))
- Updated to casadi 3.6, which required some changes to the casadi integrator. ([#2859](https://github.com/pybamm-team/PyBaMM/pull/2859))


## Optimizations

- Fixed deprecated `interp2d` method by switching to `xarray.DataArray` as the backend for `ProcessedVariable` ([#2907](https://github.com/pybamm-team/PyBaMM/pull/2907))

## Bug fixes

- Initial conditions for sensitivity equations calculated correctly ([#2920](https://github.com/pybamm-team/PyBaMM/pull/2920))
- Parameter sets can now contain the key "chemistry", and will ignore its value (this previously would give errors in some cases) ([#2901](https://github.com/pybamm-team/PyBaMM/pull/2901))
- Fixed a bug in the discretisation of initial conditions of a scaled variable ([#2856](https://github.com/pybamm-team/PyBaMM/pull/2856))
- Fixed keyerror on "all" when getting sensitivities from IDAKLU solver([#2883](https://github.com/pybamm-team/PyBaMM/pull/2883))
- Initial conditions for sensitivity equations calculated correctly ([#2920](https://github.com/pybamm-team/PyBaMM/pull/2920))
- Fixed a bug in the discretisation of initial conditions of a scaled variable ([#2856](https://github.com/pybamm-team/PyBaMM/pull/2856))

## Breaking changes

Expand All @@ -73,7 +75,7 @@

- Fix non-deteministic outcome of some tests in the test suite ([#2844](https://github.com/pybamm-team/PyBaMM/pull/2844))
- Fixed excessive RAM consumption when running multiple simulations ([#2823](https://github.com/pybamm-team/PyBaMM/pull/2823))
- Fixed use of last_state as starting_solution in Simulation.solve() ([#2822](https://github.com/pybamm-team/PyBaMM/pull/2822))
- Fixed use of `last_state` as `starting_solution` in `Simulation.solve()` ([#2822](https://github.com/pybamm-team/PyBaMM/pull/2822))
- Fixed a bug where variable bounds could not contain `InputParameters` ([#2795](https://github.com/pybamm-team/PyBaMM/pull/2795))
- Improved `model.latexify()` to have a cleaner and more readable output ([#2764](https://github.com/pybamm-team/PyBaMM/pull/2764))
- Fixed electrolyte conservation in the case of concentration-dependent transference number ([#2758](https://github.com/pybamm-team/PyBaMM/pull/2758))
Expand Down
247 changes: 247 additions & 0 deletions examples/notebooks/experiments-start-time.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "regional-bedroom",
"metadata": {},
"source": [
"# Experiments with `start_time`"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "quantitative-radar",
"metadata": {},
"source": [
"This notebook introduces functionality for simulating user case in which the experiment steps are triggered at a certain point in time."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "novel-spectacular",
"metadata": {},
"outputs": [
{
"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.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.1.2\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"
]
}
],
"source": [
"%pip install pybamm -q # install PyBaMM if it is not installed\n",
"import pybamm"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "11c87da1",
"metadata": {},
"source": [
"Let's start defining a model to illustrate this functionality, in this case we choose the SPM"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "96b62a7f",
"metadata": {},
"outputs": [],
"source": [
"model = pybamm.lithium_ion.SPM()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "f388d538",
"metadata": {},
"source": [
"Usually we define an experiment such that each step is triggered when the previous step is completed. For example, in this case we do a 1C discharge for 20 minutes and then a C/3 charge for 10 minutes. The charge step starts after 20 minutes, i.e. once the discharge step is finished."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "eba027c8",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "5516f708a0264190a51d1811064afd82",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(FloatSlider(value=0.0, description='t', max=1800.0, step=18.0), Output()), _dom_classes=…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<pybamm.plotting.quick_plot.QuickPlot at 0x7f3194e00eb0>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"experiment = pybamm.Experiment([\"Discharge at 1C for 20 minutes\", \"Charge at C/3 for 10 minutes\"])\n",
"sim = pybamm.Simulation(model, experiment=experiment)\n",
"sim.solve()\n",
"sim.plot()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "0fdfced4",
"metadata": {},
"source": [
"However, if we want to represent a realistic user case we might certain experiments to be run at a certain time instead, even if that means cutting short the previous step. In this case we can pass a starting time as a keyword argument in the `pybamm.step.string` method. The `start_time` should be passed as a `datetime.datetime` object."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "171550ac",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4832134ba802437e957ccdd5072d6d93",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(FloatSlider(value=0.0, description='t', max=2.5, step=0.025), Output()), _dom_classes=('…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<pybamm.plotting.quick_plot.QuickPlot at 0x7f31943d3760>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s = pybamm.step.string\n",
"\n",
"experiment = pybamm.Experiment(\n",
" [\n",
" s(\"Discharge at 1C for 1 hour\", start_time=\"Day 1 08:00:00\"),\n",
" s(\"Charge at C/3 for 10 minutes\", start_time=\"Day 1 08:30:00\"),\n",
" s(\"Discharge at C/2 for 30 minutes\", start_time=\"Day 1 09:00:00\"),\n",
" s(\"Rest for 1 hour\"),\n",
" ]\n",
")\n",
"sim = pybamm.Simulation(model, experiment=experiment)\n",
"sim.solve()\n",
"sim.plot()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "edfa4c9f",
"metadata": {},
"source": [
"In the example above, we note that the first step (1C discharge) is cut short as the second step (C/3 charge) start time occurs before the end of the first step. On the other hand, an additional resting period is added after the second step as the third step (C/2 discharge) start time is 20 minutes later than the end of the second step. The final step does not have a start time so it is triggered immediately after the previous step. Note that if the argument `start_time` is used in an experiment, the first step should always have a `start_time`, otherwise the solver will throw an error."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "driven-sensitivity",
"metadata": {},
"outputs": [
{
"name": "stdout",
"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] 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",
"[3] 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",
"[4] 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",
"[5] 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",
"[6] 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",
"[7] Andrew Weng, Jason B Siegel, and Anna Stefanopoulou. Differential voltage analysis for battery manufacturing process control. arXiv preprint arXiv:2303.07088, 2023.\n",
"\n"
]
}
],
"source": [
"pybamm.print_citations()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "593ae90b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.17"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
},
"vscode": {
"interpreter": {
"hash": "612adcc456652826e82b485a1edaef831aa6d5abc680d008e93d513dd8724f14"
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}
52 changes: 44 additions & 8 deletions pybamm/experiment/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def __init__(
termination,
)

self.datetime_formats = [
"Day %j %H:%M:%S",
"%Y-%m-%d %H:%M:%S",
]

operating_conditions_cycles = []
for cycle in operating_conditions:
# Check types and convert to list
Expand All @@ -78,9 +83,9 @@ def __init__(
self.operating_conditions_cycles = operating_conditions_cycles
self.cycle_lengths = [len(cycle) for cycle in operating_conditions_cycles]

operating_conditions_steps_unprocessed = [
cond for cycle in operating_conditions_cycles for cond in cycle
]
operating_conditions_steps_unprocessed = self._set_next_start_time(
[cond for cycle in operating_conditions_cycles for cond in cycle]
)

# Convert strings to pybamm.step._Step objects
# We only do this once per unique step, do avoid unnecessary conversions
Expand All @@ -89,11 +94,7 @@ def __init__(
for step in unique_steps_unprocessed:
if isinstance(step, str):
processed_steps[step] = pybamm.step.string(step)
elif not isinstance(step, pybamm.step._Step):
raise TypeError(
"Operating conditions should be strings or _Step objects"
)
else:
elif isinstance(step, pybamm.step._Step):
processed_steps[step] = step

# Save the processed unique steps and the processed operating conditions
Expand All @@ -103,6 +104,17 @@ def __init__(
processed_steps[step] for step in operating_conditions_steps_unprocessed
]

self.initial_start_time = self.operating_conditions_steps[0].start_time

if (
self.operating_conditions_steps[0].end_time is not None
and self.initial_start_time is None
):
raise ValueError(
"When using experiments with `start_time`, the first step must have a "
"`start_time`."
)

self.termination_string = termination
self.termination = self.read_termination(termination)

Expand Down Expand Up @@ -182,3 +194,27 @@ def search_tag(self, tag):
break

return cycles

def _set_next_start_time(self, operating_conditions):
if all(isinstance(i, str) for i in operating_conditions):
return operating_conditions

end_time = None
next_start_time = None

for op in reversed(operating_conditions):
if isinstance(op, str):
op = pybamm.step.string(op)
elif not isinstance(op, pybamm.step._Step):
raise TypeError(
"Operating conditions should be strings or _Step objects"
)

op.next_start_time = next_start_time
op.end_time = end_time

next_start_time = op.start_time
if next_start_time:
end_time = next_start_time

return operating_conditions
Loading

0 comments on commit c2ca937

Please sign in to comment.