-
Notifications
You must be signed in to change notification settings - Fork 28
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
Restructure for improved modularity #60
Changes from all commits
8b55b64
ccc6315
ab550d1
d900ed3
f9bfddc
9c64328
5160702
d07b4f7
5ba589b
0e7aa3b
f79fd25
b0ee269
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import pybop | ||
import pandas as pd | ||
import numpy as np | ||
from os import path | ||
|
||
# Load dataset | ||
data_path = path.join(pybop.script_path, "..", "examples/scripts/Chen_example.csv") | ||
measurements = pd.read_csv(data_path, comment="#").to_numpy() | ||
observations = [ | ||
pybop.Dataset("Time [s]", measurements[:, 0]), | ||
pybop.Dataset("Current function [A]", measurements[:, 1]), | ||
pybop.Dataset("Voltage [V]", measurements[:, 2]), | ||
] | ||
|
||
# Define model | ||
# parameter_set = pybop.ParameterSet("pybamm", "Chen2020") | ||
model = pybop.models.lithium_ion.SPM() | ||
|
||
# Define fitting parameters | ||
params = [ | ||
pybop.Parameter( | ||
"Negative electrode active material volume fraction", | ||
prior=pybop.Gaussian(0.75, 0.05), | ||
bounds=[0.65, 0.85], | ||
), | ||
pybop.Parameter( | ||
"Positive electrode active material volume fraction", | ||
prior=pybop.Gaussian(0.65, 0.05), | ||
bounds=[0.55, 0.75], | ||
), | ||
] | ||
|
||
# Define the cost to optimise | ||
cost = pybop.RMSE() | ||
signal = "Voltage [V]" | ||
|
||
# Select optimiser | ||
optimiser = pybop.NLoptOptimize(x0=params) | ||
|
||
# Build the optimisation problem | ||
parameterisation = pybop.Optimisation( | ||
cost=cost, | ||
dataset=observations, | ||
signal=signal, | ||
model=model, | ||
optimiser=optimiser, | ||
fit_parameters=params, | ||
) | ||
|
||
# Run the optimisation problem | ||
x, output, final_cost, num_evals = parameterisation.run() | ||
|
||
print("Estimated parameters:", x) | ||
print("Final cost:", final_cost) | ||
|
||
|
||
# get MAP estimate, starting at a random initial point in parameter space | ||
# optimisation.map(x0=[p.sample() for p in params]) | ||
|
||
# or sample from posterior | ||
# optimisation.sample(1000, n_chains=4, ....) | ||
|
||
# or SOBER | ||
# optimisation.sober() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import pybop | ||
import pybamm | ||
import pandas as pd | ||
import numpy as np | ||
|
||
|
||
def getdata(x0): | ||
# Define the "ground truth" model with the default parameter set | ||
model = pybamm.lithium_ion.SPM() | ||
params = model.default_parameter_values | ||
|
||
# Overwrite the uncertain parameters | ||
params.update( | ||
{ | ||
"Negative electrode active material volume fraction": x0[0], | ||
"Positive electrode active material volume fraction": x0[1], | ||
} | ||
) | ||
|
||
# Define the experimental protocol | ||
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 | ||
) | ||
|
||
# Run a forward simulation | ||
sim = pybamm.Simulation(model, experiment=experiment, parameter_values=params) | ||
|
||
# Return the simulation results | ||
return sim.solve() | ||
|
||
|
||
# Define the initial values of the uncertain parameters | ||
x0 = np.array([0.55, 0.63]) | ||
|
||
# Generate observations | ||
solution = getdata(x0) | ||
observations = [ | ||
pybop.Dataset("Time [s]", solution["Time [s]"].data), | ||
pybop.Dataset("Current function [A]", solution["Current [A]"].data), | ||
pybop.Dataset("Voltage [V]", solution["Terminal voltage [V]"].data), | ||
] | ||
|
||
# Define the model with the default parameter set | ||
model = pybop.models.lithium_ion.SPM() | ||
model.parameter_set = model.pybamm_model.default_parameter_values | ||
|
||
# Initialise the 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], | ||
), | ||
] | ||
|
||
# Define the cost to optimise | ||
cost = pybop.RMSE() | ||
signal = "Voltage [V]" | ||
|
||
# Select optimiser | ||
optimiser = pybop.NLoptOptimize(x0=params) | ||
|
||
# Build the optimisation problem | ||
parameterisation = pybop.Optimisation( | ||
cost=cost, | ||
dataset=observations, | ||
signal=signal, | ||
model=model, | ||
optimiser=optimiser, | ||
fit_parameters=params, | ||
) | ||
|
||
# Run the optimisation problem | ||
x, output, final_cost, num_evals = parameterisation.run() | ||
|
||
print("Estimated parameters:", x) # x = [0.54452026, 0.63064801] | ||
print("Final cost:", final_cost) |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
# | ||
# Root of the pybop module. | ||
# Root of the PyBOP module. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to sort out a standard for this, real low priority though. |
||
# Provides access to all shared functionality (models, solvers, etc.). | ||
# | ||
# This file is adapted from Pints | ||
# (see https://github.com/pints-team/pints) | ||
# | ||
|
||
import sys | ||
import os | ||
from os import path | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe don't need to change, depends on whether we need any functionality from os in the future. |
||
|
||
# | ||
# Version info | ||
|
@@ -21,31 +21,50 @@ | |
# loss of information | ||
FLOAT_FORMAT = "{: .17e}" | ||
# Absolute path to the PyBOP repo | ||
script_path = os.path.abspath(__file__) | ||
script_path = path.dirname(__file__) | ||
|
||
# | ||
# Model Classes | ||
# Cost function class | ||
# | ||
from .models import BaseModel, lithium_ion | ||
from .costs.error_costs import RMSE | ||
|
||
# | ||
# Parameterisation class | ||
# Dataset class | ||
# | ||
from .identification import Parameterisation, ParameterSet, Parameter, Observed | ||
from .datasets.base_dataset import Dataset | ||
|
||
# | ||
# Priors class | ||
# Model classes | ||
# | ||
from .priors import Gaussian, Uniform, Exponential | ||
from .models.base_model import BaseModel | ||
from .models import lithium_ion | ||
|
||
# | ||
# Optimisation class | ||
# Main optimisation class | ||
# | ||
from .optimisation import BaseOptimisation | ||
from .optimisation.NLoptOptimize import NLoptOptimize | ||
from .optimisation.SciPyMinimize import SciPyMinimize | ||
from .optimisation import Optimisation | ||
|
||
# | ||
# Remove any imported modules, so we don't expose them as part of pybop | ||
# Optimiser class | ||
# | ||
from .optimisers.base_optimiser import BaseOptimiser | ||
from .optimisers.nlopt_optimize import NLoptOptimize | ||
from .optimisers.scipy_minimize import SciPyMinimize | ||
from .optimisers.pints_optimiser import PintsOptimiser, PintsError, PintsBoundaries | ||
|
||
# | ||
# Parameter classes | ||
# | ||
from .parameters.base_parameter import Parameter | ||
from .parameters.base_parameter_set import ParameterSet | ||
from .parameters.priors import Gaussian, Uniform, Exponential | ||
|
||
# | ||
# Plotting class | ||
# | ||
from .plotting.quick_plot import QuickPlot | ||
|
||
# | ||
# Remove any imported modules, so we don't expose them as part of PyBOP | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good for the above. |
||
# | ||
del sys |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import pybop | ||
import numpy as np | ||
|
||
|
||
class RMSE: | ||
""" | ||
Defines the root mean square error cost function. | ||
""" | ||
|
||
def __init__(self): | ||
self.name = "RMSE" | ||
|
||
def compute(self, prediction, target): | ||
# Check compatibility | ||
if len(prediction) != len(target): | ||
print( | ||
"Length of vectors:", | ||
len(prediction), | ||
len(target), | ||
) | ||
raise ValueError( | ||
"Measurement and simulated data length mismatch, potentially due to reaching a voltage cut-off" | ||
) | ||
|
||
print("Last Values:", prediction[-1], target[-1]) | ||
|
||
# Compute the cost | ||
try: | ||
cost = np.sqrt(np.mean((prediction - target) ** 2)) | ||
except: | ||
print("Error in RMSE calculation") | ||
|
||
return cost | ||
|
||
|
||
class MLE: | ||
""" | ||
Defines the cost function for maximum likelihood estimation. | ||
""" | ||
|
||
def __init__(self): | ||
self.name = "MLE" | ||
|
||
def compute(self, prediction, target): | ||
# Compute the cost | ||
try: | ||
cost = 0 # update with MLE residual | ||
except: | ||
print("Error in MLE calculation") | ||
|
||
return cost | ||
|
||
|
||
class PEM: | ||
""" | ||
Defines the cost function for prediction error minimisation. | ||
""" | ||
|
||
def __init__(self): | ||
self.name = "PEM" | ||
|
||
def compute(self, prediction, target): | ||
# Compute the cost | ||
try: | ||
cost = 0 # update with MLE residual | ||
except: | ||
print("Error in PEM calculation") | ||
|
||
return cost | ||
|
||
|
||
class MAP: | ||
""" | ||
Defines the cost function for maximum a posteriori estimation. | ||
""" | ||
|
||
def __init__(self): | ||
self.name = "MAP" | ||
|
||
def compute(self, prediction, target): | ||
# Compute the cost | ||
try: | ||
cost = 0 # update with MLE residual | ||
except: | ||
print("Error in MAP calculation") | ||
|
||
return cost | ||
|
||
def sample(self, n_chains): | ||
""" | ||
Sample from the posterior distribution. | ||
""" | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep