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

Update Readme & contributing #95

Merged
merged 14 commits into from
Nov 17, 2023
35 changes: 20 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ If you'd like to contribute to PyBOP, please have a look at the [pre-commit](#pr

Before you commit any code, please perform the following checks:

- [All tests pass](#testing): `$ nox -s unit_test`
- [All tests pass](#testing): `$ nox -s unit`

### Installing and using pre-commit

Expand Down Expand Up @@ -35,7 +35,7 @@ We use [GIT](https://en.wikipedia.org/wiki/Git) and [GitHub](https://en.wikipedi
2. Create a [branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/) of this repo (ideally on your own [fork](https://help.github.com/articles/fork-a-repo/)), where all changes will be made
3. Download the source code onto your local system, by [cloning](https://help.github.com/articles/cloning-a-repository/) the repository (or your fork of the repository).
4. [Install](Developer-Install) PyBOP with the developer options.
5. [Test](#testing) if your installation worked, using the test script: `$ python run-tests.py --unit`.
5. [Test](#testing) if your installation worked: `$ pytest --unit -v`.

You now have everything you need to start making changes!

Expand Down Expand Up @@ -120,15 +120,28 @@ All code requires testing. We use the [pytest](https://docs.pytest.org/en/) pack
If you have nox installed, to run unit tests, type

```bash
nox -s unit_test
nox -s unit
```

else, type

```bash
python run-tests.py
pytest --unit -v
```

To run individual test files, you can use

```bash
pytest tests/unit/path/to/test --unit -v
```

And for individual tests,

```bash
pytest tests/unit/path/to/test.py::TestClass:test_name --unit -v
```
where `--unit` is a flag to run only unit tests and `-v` is a flag to display verbose output.

### Writing tests

Every new feature should have its own test. To create ones, have a look at the `test` directory and see if there's a test for a similar method. Copy-pasting is a good way to start.
Expand All @@ -146,24 +159,16 @@ This also means that, if you can't fix the bug yourself, it will be much easier
1. Run individual test scripts instead of the whole test suite:

```bash
python tests/unit/path/to/test
pytest tests/unit/path/to/test --unit -v
```

You can also run an individual test from a particular script, e.g.

```bash
python tests/unit/test_quick_plot.py TestQuickPlot.test_failure
```

If you want to run several, but not all, the tests from a script, you can restrict which tests are run from a particular script by using the skipping decorator:

```python
@unittest.skip("")
def test_bit_of_code(self):
...
pytest tests/unit/path/to/test.py::TestClass:test_name --unit -v
```
where `--unit` is a flag to run only unit tests and `-v` is a flag to display verbose output.

or by just commenting out all the tests you don't want to run.
2. Set break-points, either in your IDE or using the Python debugging module. To use the latter, add the following line where you want to set the break point

```python
Expand Down
135 changes: 29 additions & 106 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@
<a href="https://github.com/pybop-team/PyBOP/blob/develop/LICENSE">
<img src="https://img.shields.io/github/license/pybop-team/PyBOP" alt="license" />
</a>
<a href="https://colab.research.google.com/github/pybop-team/PyBOP/blob/develop/">
<img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" />
</p>

</div>

<!-- Software Specification -->
## PyBOP
PyBOP provides a comprehensive suite of tools for parameterisation and optimisation of battery models. It aims to implement Bayesian and frequentist techniques with example workflows to guide the user. PyBOP can be applied to parameterise a wide range of battery models, including the electrochemical and equivalent circuit models available in [PyBaMM](https://pybamm.org/). A major emphasis in PyBOP is understandable and actionable diagnostics for the user, while still providing extensibility for advanced probabilistic methods. By building on the state-of-the-art battery models and leveraging Python's accessibility, PyBOP enables agile and robust parameterisation and optimisation.

The figure below gives PyBOP's current conceptual structure. The living software specification of PyBOP can be found [here](https://github.com/pybop-team/software-spec). This package is under active development, expect API (Application Programming Interface) evolution with releases.
PyBOP offers a full range of tools for the parameterisation and optimisation of battery models, utilising both Bayesian and frequentist approaches with example workflows to assist the user. PyBOP can be used to parameterise various battery models, which include electrochemical and equivalent circuit models that are present in [PyBaMM](https://pybamm.org/). PyBOP prioritises clear and informative diagnostics for users, while also allowing for advanced probabilistic methods.

The diagram below presents PyBOP's conceptual framework. The PyBOP software specification is available at [this link](https://github.com/pybop-team/software-spec). This product is currently undergoing development, and users can expect the API to evolve with future releases.

<p align="center">
<img src="https://raw.githubusercontent.com/pybop-team/PyBOP/develop/assets/PyBOP_Architecture.png" alt="Data flows from battery cycling machines to Galv Harvesters, then to the Galv server and REST API. Metadata can be updated and data read using the web client, and data can be downloaded by the Python client." width="600" />
Expand All @@ -48,7 +49,7 @@ The figure below gives PyBOP's current conceptual structure. The living software

<!-- Installation -->
### Prerequisites
To use and/or contribute to PyBOP, you must first install Python 3 (specifically, 3.8-3.11). For example, on a Debian-based distribution (Debian, Ubuntu - including via WSL, Linux Mint), open a terminal and enter:
To use and/or contribute to PyBOP, first install Python (3.8-3.11). On a Debian-based distribution, this looks like:

```bash
sudo apt update
Expand All @@ -59,38 +60,24 @@ For further information, please refer to the similar [installation instructions

### Installation

Create a virtual environment called `pybop-env` within your current directory using:
Create a virtual environment called `pybop-env` within your current directory:

```bash
virtualenv pybop-env
```

Activate the environment with:
Activate the environment:

```bash
source pybop-env/bin/activate
```

You can check which version of python is installed within the virtual environment by typing:

```bash
python --version
```

Later, you can deactivate the environment and go back to your original system using:
Later, you can deactivate the environment:

```bash
deactivate
```

Note that there are alternative packages that can be used to create and manage [virtual environments](https://realpython.com/python-virtual-environments-a-primer/), for example [pyenv](https://github.com/pyenv/pyenv#installation) and [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv#installation). In this case, follow the instructions to install these packages and then to create, activate and deactivate a virtual environment, use:

```bash
pyenv virtualenv pybop-env
pyenv activate pybop-env
pyenv deactivate
```

Within your virtual environment, install the `develop` branch of PyBOP:

```bash
Expand All @@ -103,103 +90,39 @@ To alternatively install PyBOP from a local directory, use the following templat
pip install -e "PATH_TO_PYBOP"
```

Now, with PyBOP installed in your virtual environment, you can run Python scripts that import and use the functionality of this package.
To check whether PyBOP has been installed correctly, run one of the examples in the following section or the full set of unit tests:

<!-- Example Usage -->
### Usage
PyBOP has two classes of intended use cases:
```bash
pytest --unit -v
```

### Using PyBOP
PyBOP has two general types of intended use cases:
1. parameter estimation from battery test data
2. design optimisation subject to battery manufacturing/usage constraints

These classes encompass a wide variety of optimisation problems, which depend on the choice of battery model, the available data and/or the choice of design parameters.

### Parameter estimation
The example below shows a simple fitting routine that starts by generating synthetic data from a single particle model with modified parameter values. An RMSE cost function using the terminal voltage as the optimised signal is completed to determine the unknown parameter values. First, the synthetic data is generated:

```python
import pybop
import pybamm
import pandas as pd
import numpy as np

def getdata(x0):
model = pybamm.lithium_ion.SPM()
params = model.default_parameter_values

params.update(
{
"Negative electrode active material volume fraction": x0[0],
"Positive electrode active material volume fraction": x0[1],
}
)
experiment = pybamm.Experiment(
[
(
"Discharge at 2C for 5 minutes (1 second period)",
"Rest for 2 minutes (1 second period)",
"Charge at 1C for 5 minutes (1 second period)",
"Rest for 2 minutes (1 second period)",
),
]
* 2
)
sim = pybamm.Simulation(model, experiment=experiment, parameter_values=params)
return sim.solve()


# Form observations
x0 = np.array([0.55, 0.63])
solution = getdata(x0)
```
Next, the observed variables are defined, with the model construction and parameter definitions following. Finally, the parameterisation class is constructed and parameter fitting is completed.
```python
observations = [
pybop.Observed("Time [s]", solution["Time [s]"].data),
pybop.Observed("Current function [A]", solution["Current [A]"].data),
pybop.Observed("Voltage [V]", solution["Terminal voltage [V]"].data),
]

# Define model
model = pybop.models.lithium_ion.SPM()
model.parameter_set = model.pybamm_model.default_parameter_values

# Fitting parameters
params = [
pybop.Parameter(
"Negative electrode active material volume fraction",
prior=pybop.Gaussian(0.5, 0.05),
bounds=[0.35, 0.75],
),
pybop.Parameter(
"Positive electrode active material volume fraction",
prior=pybop.Gaussian(0.65, 0.05),
bounds=[0.45, 0.85],
),
]

parameterisation = pybop.Parameterisation(
model, observations=observations, fit_parameters=params
)

# get RMSE estimate using NLOpt
results, last_optim, num_evals = parameterisation.rmse(
signal="Voltage [V]", method="nlopt" # results = [0.54452026, 0.63064801]
)
These general cases encompass a wide variety of optimisation problems that require careful consideration based on the choice of battery model, the available data and/or the choice of design parameters.

PyBOP comes with a number of [example notebooks and scripts](https://github.com/pybop-team/PyBOP/blob/develop/examples) which can be found in the examples folder.

The [spm_descent.py](https://github.com/pybop-team/PyBOP/blob/develop/examples/scripts/spm_descent.py) script illustrates a straightforward example that starts by generating artificial data from a single particle model (SPM). The unknown parameter values are identified by implementing a sum-of-square error cost function using the terminal voltage as the observed signal and a gradient descent optimiser. To run this example:

```bash
python examples/scripts/spm_descent.py
```

In addition, [spm_nlopt.ipynb](https://github.com/pybop-team/PyBOP/blob/develop/examples/notebooks/spm_nlopt.ipynb) provides a second example in notebook form. This example estimates the SPM parameters based on an RMSE cost function and a BOBYQA optimiser.

<!-- Code of Conduct -->
## Code of Conduct

PyBOP aims to foster a broad consortium of developers and users, building on and
learning from the success of the [PyBaMM](https://pybamm.org/) community. Our values are:

- Open-source (code and ideas should be shared)
PyBOP aims to foster a broad consortium of developers and users, building on and learning from the success of the [PyBaMM](https://pybamm.org/) community. Our values are:

- Inclusivity and fairness (those who want to contribute may do so, and their input is appropriately recognised)
- Inclusivity and fairness (those who wish to contribute may do so, and their input is appropriately recognised)

- Interoperability (aiming for modularity to enable maximum impact and inclusivity)
- Interoperability (modularity for maximum impact and inclusivity)

- User-friendliness (putting user requirements first, thinking about user-assistance & workflows)
- User-friendliness (putting user requirements first via user-assistance & workflows)


<!-- Contributing -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import numpy as np
import matplotlib.pyplot as plt

# Parameter set and model definition
parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPMe(parameter_set=parameter_set)

Expand All @@ -19,13 +20,15 @@
),
]

# Generate data
sigma = 0.001
t_eval = np.arange(0, 900, 2)
values = model.predict(t_eval=t_eval)
corrupt_values = values["Terminal voltage [V]"].data + np.random.normal(
0, sigma, len(t_eval)
)

# Dataset definition
dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
Expand All @@ -39,6 +42,7 @@
optim.optimiser.set_learning_rate(0.025)
optim.set_max_iterations(100)

# Run optimisation
x, final_cost = optim.run()
print("Estimated parameters:", x)

Expand Down
File renamed without changes.