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

add BatchStudy class #1455

Merged
merged 14 commits into from
Apr 9, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- Added Batch Study class ([#1455](https://github.com/pybamm-team/PyBaMM/pull/1455))
- Added `ConcatenationVariable`, which is automatically created when variables are concatenated ([#1453](https://github.com/pybamm-team/PyBaMM/pull/1453))
- Added "fast with events" mode for the CasADi solver, which solves a model and finds events more efficiently than "safe" mode. As of PR #1450 this feature is still being tested and "safe" mode remains the default ([#1450](https://github.com/pybamm-team/PyBaMM/pull/1450))

Expand Down
3 changes: 2 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ API documentation
source/util
source/citations
source/parameters_cli
source/batch_study

Examples
========

Detailed examples can be viewed on the
`GitHub examples page <https://github.com/pybamm-team/PyBaMM/tree/main/examples/notebooks>`_,
`GitHub examples page <https://github.com/pybamm-team/PyBaMM/tree/develop/examples/notebooks>`_,
and run locally using ``jupyter notebook``, or online through
`Google Colab <https://colab.research.google.com/github/pybamm-team/PyBaMM/blob/develop/>`_.

Expand Down
5 changes: 5 additions & 0 deletions docs/source/batch_study.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Batch Study
===========

.. autoclass:: pybamm.BatchStudy
:members:
3 changes: 3 additions & 0 deletions docs/source/models/base_models/base_battery_model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ Base Battery Model

.. autoclass:: pybamm.BaseBatteryModel
:members:

.. autoclass:: pybamm.models.full_battery_models.base_battery_model.Options
:members:
2 changes: 1 addition & 1 deletion docs/source/models/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Below is an overview of all the battery models included in PyBaMM.
Each of the pre-built models contains a reference to the paper in which it is derived.

The models can be customised using the `options` dictionary defined in the :class:`pybamm.BaseBatteryModel` (which also provides information on which options and models are compatible)
Visit our `examples page <https://github.com/pybamm-team/PyBaMM/tree/main/examples/notebooks/models>`_
Visit our `examples page <https://github.com/pybamm-team/PyBaMM/tree/develop/examples/notebooks/models>`_
to see how these models can be solved, and compared, using PyBaMM.

.. toctree::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"More details on each subset can be found [here](https://github.com/pybamm-team/PyBaMM/tree/main/pybamm/input/parameters).\n",
"More details on each subset can be found [here](https://github.com/pybamm-team/PyBaMM/tree/develop/pybamm/input/parameters).\n",
"\n",
"Now we can pass `'chemistry'` into `ParameterValues` to create the dictionary of parameter values"
]
Expand Down
6 changes: 6 additions & 0 deletions pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,17 @@ def version(formatted=False):
import matplotlib.pyplot as plt

plt.style.use(default_plot_style)

#
# Simulation
#
from .simulation import Simulation, load_sim, is_notebook

#
# Batch Study
#
from .batch_study import BatchStudy

#
# Remove any imported modules, so we don't expose them as part of pybamm
#
Expand Down
91 changes: 91 additions & 0 deletions pybamm/batch_study.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#
# BatchStudy class
#
import pybamm
from itertools import product


class BatchStudy:
"""
A BatchStudy class for comparison of different PyBaMM simulations.

Parameters
----------
models : dict
A dictionary of models to be simulated
solvers : dict (optional)
A dictionary of solvers to use to solve the model. Default is None
experiments : dict (optional)
A dictionary of experimental conditions under which to solve the model.
Default is None
repeats : int (optional)
The number of times `solve` should be called. Default is 1
permutations : bool (optional)
If False runs first model with first solver, first experiment
and second model with second solver, second experiment etc.
If True runs a cartesian product of models, solvers and experiments.
Default is False
"""

def __init__(
self, models, solvers=None, experiments=None, repeats=1, permutations=False
):
self.models = models
self.solvers = solvers
self.experiments = experiments
self.repeats = repeats
self.permutations = permutations

if not self.permutations:
if self.solvers and (len(self.models) != len(self.solvers)):
raise ValueError(
f"Either provide no solvers or an equal number of solvers as the"
f" models ({len(self.models)} models given) if permutations=False"
)
elif self.experiments and (len(self.models) != len(self.experiments)):
raise ValueError(
f"Either provide no experiments or an equal number of experiments"
f" as the models ({len(self.models)} models given)"
f" if permutations=False"
)

def solve(self, t_eval=None):
self.sims = []
iter_func = product if self.permutations else zip

# Instantiate values for solvers based on the value of self.permutations
if self.solvers:
solver_values = self.solvers.values()
elif self.permutations:
solver_values = [None]
else:
solver_values = [None] * len(self.models)

# Instantiate values for experminents based on the value of self.permutations
if self.experiments:
experiment_values = self.experiments.values()
elif self.permutations:
experiment_values = [None]
else:
experiment_values = [None] * len(self.models)

for model, solver, experiment in iter_func(
self.models.values(), solver_values, experiment_values
):
sim = pybamm.Simulation(model, solver=solver, experiment=experiment)
# repeat to get average solve time and integration time
solve_time = 0
integration_time = 0
for _ in range(self.repeats):
sol = sim.solve(t_eval)
solve_time += sol.solve_time
integration_time += sol.integration_time
sim.solution.solve_time = solve_time / self.repeats
sim.solution.integration_time = integration_time / self.repeats
self.sims.append(sim)

def plot(self, output_variables=None, **kwargs):
self.quick_plot = pybamm.dynamic_plot(
self.sims, output_variables=output_variables, **kwargs
)
return self.quick_plot
144 changes: 144 additions & 0 deletions tests/unit/test_batch_study.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"""
Tests for the batch_study.py
"""
import pybamm
import unittest

spm = pybamm.lithium_ion.SPM()
dfn = pybamm.lithium_ion.DFN()
casadi_safe = pybamm.CasadiSolver(mode="safe")
casadi_fast = pybamm.CasadiSolver(mode="fast")
cccv = pybamm.Experiment(
[
(
"Discharge at C/5 for 10 hours or until 3.3 V",
"Rest for 1 hour",
"Charge at 1 A until 4.1 V",
"Hold at 4.1 V until 10 mA",
"Rest for 1 hour",
),
]
* 3
)
gitt = pybamm.Experiment(
[("Discharge at C/20 for 1 hour", "Rest for 1 hour")] * 20,
)

batch_study_false_only_models = pybamm.BatchStudy(models={"SPM": spm, "DFN": dfn})
batch_study_true_only_models = pybamm.BatchStudy(
models={"SPM": spm, "DFN": dfn}, permutations=True
)
batch_study_false = pybamm.BatchStudy(
models={"SPM": spm, "DFN": dfn},
solvers={"casadi safe": casadi_safe, "casadi fast": casadi_fast},
experiments={"cccv": cccv, "gitt": gitt},
)
batch_study_true = pybamm.BatchStudy(
models={"SPM": spm, "DFN": dfn},
solvers={"casadi safe": casadi_safe, "casadi fast": casadi_fast},
experiments={"gitt": gitt},
permutations=True,
)


class TestBatchStudy(unittest.TestCase):
def test_solve(self):
# Tests for exceptions
with self.assertRaises(ValueError):
pybamm.BatchStudy(
models={"SPM": spm, "DFN": dfn}, experiments={"gitt": gitt}
)

with self.assertRaises(ValueError):
pybamm.BatchStudy(
models={"SPM": spm, "DFN": dfn},
solvers={"casadi fast": casadi_fast},
experiments={"cccv": cccv, "gitt": gitt},
)

# Tests for batch_study_false_only_models (Only models with permutations=False)
batch_study_false_only_models.solve(t_eval=[0, 3600])
output_len_false_only_models = len(batch_study_false_only_models.sims)
self.assertEqual(2, output_len_false_only_models)

# Tests for batch_study_true_only_models (Only models with permutations=True)
batch_study_true_only_models.solve(t_eval=[0, 3600])
output_len_true_only_models = len(batch_study_true_only_models.sims)
self.assertEqual(2, output_len_true_only_models)

# Tests for batch_study_false (permutations=False)
batch_study_false.solve()
output_len_false = len(batch_study_false.sims)
self.assertEqual(2, output_len_false)
for num in range(output_len_false):
output_model = batch_study_false.sims[num].model.name
models_list = [model.name for model in batch_study_false.models.values()]
self.assertIn(output_model, models_list)

output_solver = batch_study_false.sims[num].solver.name
solvers_list = [
solver.name for solver in batch_study_false.solvers.values()
]
self.assertIn(output_solver, solvers_list)

output_experiment = batch_study_false.sims[
num
].experiment.operating_conditions_strings
experiments_list = [
experiment.operating_conditions_strings
for experiment in batch_study_false.experiments.values()
]
self.assertIn(output_experiment, experiments_list)

# Tests for batch_study_true (permutations=True)
batch_study_true.solve()
output_len_true = len(batch_study_true.sims)
self.assertEqual(4, output_len_true)
for num in range(output_len_true):
output_model = batch_study_true.sims[num].model.name
models_list = [model.name for model in batch_study_true.models.values()]
self.assertIn(output_model, models_list)

output_solver = batch_study_true.sims[num].solver.name
solvers_list = [solver.name for solver in batch_study_true.solvers.values()]
self.assertIn(output_solver, solvers_list)

output_experiment = batch_study_true.sims[
num
].experiment.operating_conditions_strings
experiments_list = [
experiment.operating_conditions_strings
for experiment in batch_study_true.experiments.values()
]
self.assertIn(output_experiment, experiments_list)

def test_plot(self):
# Tests for batch_study_false (permutations=False)
batch_study_false.solve()
batch_study_false.plot(testing=True)
output_len_false = len(batch_study_false.sims)
self.assertEqual(2, output_len_false)
for num in range(output_len_false):
output_model = batch_study_false.sims[num].all_models[0].name
models_list = [model.name for model in batch_study_false.models.values()]
self.assertIn(output_model, models_list)

# Tests for batch_study_true (permutations=True)
batch_study_true.solve()
batch_study_true.plot(testing=True)
output_len_true = len(batch_study_true.sims)
self.assertEqual(4, output_len_true)
for num in range(output_len_true):
output_model = batch_study_true.sims[num].all_models[0].name
models_list = [model.name for model in batch_study_true.models.values()]
self.assertIn(output_model, models_list)


if __name__ == "__main__":
print("Add -v for more debug output")
import sys

if "-v" in sys.argv:
debug = True
pybamm.settings.debug_mode = True
unittest.main()