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 MultiFittingProblem class and example #364

Merged
merged 150 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
2a779e1
Add MultiFittingProblem, example and test
NicolaCourtier Jun 14, 2024
48d196e
Remove unused n_problems property
NicolaCourtier Jun 14, 2024
ac8e11a
Merge branch '358-passing-inputs' into 238b-multi-fitting
NicolaCourtier Jul 4, 2024
0b0c0e0
Update multi_fitting.py
NicolaCourtier Jul 4, 2024
2d84260
Merge branch '358-passing-inputs' into 238b-multi-fitting
NicolaCourtier Jul 4, 2024
c614542
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 4, 2024
12ab515
Update CHANGELOG.md
NicolaCourtier Jul 4, 2024
2a2eb29
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 5, 2024
cd1c7f8
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 5, 2024
851255c
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 8, 2024
abbeb84
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 11, 2024
dd01edb
Remove unused weights
NicolaCourtier Jul 12, 2024
1724cc5
Update problem_list to problem args
NicolaCourtier Jul 12, 2024
84b611d
Concatenate the whole list
NicolaCourtier Jul 12, 2024
c5f58ce
Apply suggestions from code review
NicolaCourtier Jul 12, 2024
6654b1b
Update description
NicolaCourtier Jul 12, 2024
e51decc
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 23, 2024
b66a52a
Update default init_soc
NicolaCourtier Jul 24, 2024
157c211
Update CHANGELOG.md
NicolaCourtier Jul 24, 2024
5e60cee
Update check_params
NicolaCourtier Jul 24, 2024
60ae64e
Add pybamm_model as default attribute
NicolaCourtier Jul 24, 2024
c682ace
Ensure predict uses unprocessed_model
NicolaCourtier Jul 25, 2024
914f307
Move rebuild check to model.simulate
NicolaCourtier Jul 25, 2024
367ab12
Align simulate output with predict
NicolaCourtier Jul 25, 2024
4e92192
Replace init_soc with init_ocv for FittingProblem
NicolaCourtier Jul 25, 2024
f799e06
Update notebooks
NicolaCourtier Jul 25, 2024
3f61322
Update test_observers.py
NicolaCourtier Jul 26, 2024
46f31fc
Update descriptions and simplify
NicolaCourtier Jul 26, 2024
28d56fb
Add test_set_initial_state
NicolaCourtier Jul 26, 2024
026fbbd
Copy each model into MultiFittingProblem
NicolaCourtier Jul 26, 2024
cba5c36
Update test_problem.py
NicolaCourtier Jul 26, 2024
781f889
Update ecm.py
NicolaCourtier Jul 26, 2024
61a6fc7
style: pre-commit fixes
pre-commit-ci[bot] Jul 26, 2024
6e6cb70
Break connection between parameter_sets
NicolaCourtier Jul 26, 2024
d17c728
Allow predict to update initial state
NicolaCourtier Jul 26, 2024
aeba8c4
Fix typo
NicolaCourtier Jul 26, 2024
5e65542
Add nbstripout pre-commit hook
NicolaCourtier Jul 28, 2024
c54ae5b
Add -q and re-run all notebooks
NicolaCourtier Jul 28, 2024
4cba081
Copy parameter sets and remove model.initial_state
NicolaCourtier Jul 28, 2024
01115df
Reset spm_NelderMead.py
NicolaCourtier Jul 28, 2024
08df450
Update CHANGELOG.md
NicolaCourtier Jul 28, 2024
0039d8a
Update CHANGELOG.md
NicolaCourtier Jul 29, 2024
e655ed7
Allow parameter_set is None
NicolaCourtier Jul 29, 2024
b2f389b
Merge branch '427-pre-commit-nbstripout' into 424-fitting-ocv
NicolaCourtier Jul 29, 2024
9a7282c
Re-run notebooks
NicolaCourtier Jul 29, 2024
6223251
Update bounds
NicolaCourtier Jul 30, 2024
a71350e
Update notebooks
NicolaCourtier Jul 30, 2024
37be820
Update notebooks
NicolaCourtier Jul 30, 2024
8591ba7
Set numpy random seed in notebooks
NicolaCourtier Jul 30, 2024
d2c7e68
Re-run with fixed seed
NicolaCourtier Jul 30, 2024
e2410cc
Merge branch '427-pre-commit-nbstripout' into 424-fitting-ocv
NicolaCourtier Jul 30, 2024
9b8d27a
Update bounds
NicolaCourtier Jul 30, 2024
2f73168
Update notebooks to initial_state
NicolaCourtier Jul 30, 2024
80df8fe
Add set_initial_state for ECMs
NicolaCourtier Jul 30, 2024
eda8096
Add init_ocv setter
NicolaCourtier Jul 30, 2024
285777b
Add init_ocv values
NicolaCourtier Jul 30, 2024
aebec3c
Re-run notebooks
NicolaCourtier Jul 30, 2024
8df8618
Add tests for ECM get_initial_state
NicolaCourtier Jul 30, 2024
6f05cda
Add ECM initial state error tests
NicolaCourtier Jul 30, 2024
d18b28f
Remove unused store_optimised_parameters
NicolaCourtier Jul 31, 2024
c0d0a24
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Jul 31, 2024
5d4f2ea
Update parameters.initial_value
NicolaCourtier Jul 31, 2024
eac17cc
Use any Initial SoC from parameter_set
NicolaCourtier Jul 31, 2024
24e40a0
Merge branch 'develop' into 424-fitting-ocv
NicolaCourtier Jul 31, 2024
b9650c3
Update bounds again
NicolaCourtier Jul 31, 2024
7a7d58a
Update init_soc in notebooks
NicolaCourtier Jul 31, 2024
d993632
Move dataset check within unscented_kalman
NicolaCourtier Jul 31, 2024
5059f71
Remove unnecessary lines from spm_UKF
NicolaCourtier Jul 31, 2024
9bc0c0e
Update all parameters for rebuild
NicolaCourtier Jul 31, 2024
daf3f05
Update init_ocv to _init_ocv
NicolaCourtier Jul 31, 2024
b285885
Ensure value updates alongside initial_value
NicolaCourtier Jul 31, 2024
48788af
Update multi_model_identification
NicolaCourtier Jul 31, 2024
7798af5
Merge branch 'develop' into 421-design_init_soc
NicolaCourtier Jul 31, 2024
07644d4
Update spm_electrode_design.ipynb
NicolaCourtier Jul 31, 2024
799329e
Update spm_electrode_design.ipynb
NicolaCourtier Jul 31, 2024
c7ee29b
Merge branch '421-design_init_soc' into 424-fitting-ocv
NicolaCourtier Jul 31, 2024
61d326f
Fix identation
NicolaCourtier Jul 31, 2024
5be532c
Fix test_plots design problem
NicolaCourtier Jul 31, 2024
29e7a67
Move Changelog entry to breaking changes
NicolaCourtier Aug 1, 2024
f999b96
Move Changelog entry
NicolaCourtier Aug 1, 2024
e4f8ce6
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Aug 1, 2024
d31ee54
Merge branch 'develop' into 421-design_init_soc
NicolaCourtier Aug 1, 2024
098e3e2
style: pre-commit fixes
pre-commit-ci[bot] Aug 1, 2024
86f1d17
Fix merge mistake
NicolaCourtier Aug 1, 2024
beac8bb
Merge branch '421-design_init_soc' into 424-fitting-ocv
NicolaCourtier Aug 1, 2024
d205dc9
style: pre-commit fixes
pre-commit-ci[bot] Aug 1, 2024
d1fbd8d
Allow kwargs in MultiFitting evaluate
NicolaCourtier Aug 1, 2024
eef8acf
Add tests
NicolaCourtier Aug 1, 2024
5f7761f
Merge branch '421-design_init_soc' into 424-fitting-ocv
NicolaCourtier Aug 1, 2024
cdd4d1c
Update integration tests
NicolaCourtier Aug 1, 2024
b6c8824
Update spm_weighted_cost.py
NicolaCourtier Aug 1, 2024
ebc5114
Fix tests
NicolaCourtier Aug 2, 2024
d4421fe
style: pre-commit fixes
pre-commit-ci[bot] Aug 2, 2024
043ae52
Merge branch 'develop' into 424-fitting-ocv
NicolaCourtier Aug 2, 2024
3bba9c7
Merge branch 'develop' into 424-fitting-ocv
NicolaCourtier Aug 5, 2024
ede6363
Fix model type check
NicolaCourtier Aug 5, 2024
ab68d15
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Aug 5, 2024
fe1397e
Update _parameter_set to parameter_set
NicolaCourtier Aug 5, 2024
3cb4c4d
style: pre-commit fixes
pre-commit-ci[bot] Aug 5, 2024
e3b1466
Update tests with parameter set
NicolaCourtier Aug 5, 2024
cbda2ef
Add model build description
NicolaCourtier Aug 5, 2024
b19035d
Revert to _parameter_set
NicolaCourtier Aug 5, 2024
4d4857b
Fix predict without pybamm test
NicolaCourtier Aug 5, 2024
eba9def
Apply suggestions from code review
NicolaCourtier Aug 5, 2024
892eb77
Apply suggestions from code review
NicolaCourtier Aug 5, 2024
bf01b7e
Fix syntax
NicolaCourtier Aug 5, 2024
71a95cf
Fix variable name
NicolaCourtier Aug 5, 2024
4484951
Update model type check
NicolaCourtier Aug 5, 2024
08cdc4c
Update parameter_set setter
NicolaCourtier Aug 5, 2024
cd87e83
style: pre-commit fixes
pre-commit-ci[bot] Aug 5, 2024
14e4223
Add parameters.reset_initial_value
NicolaCourtier Aug 5, 2024
8a19542
Add n_outputs property
NicolaCourtier Aug 6, 2024
a67c564
style: pre-commit fixes
pre-commit-ci[bot] Aug 6, 2024
5450f21
Remove public parameter_set setter
NicolaCourtier Aug 6, 2024
676e7ed
Correct integer to float
NicolaCourtier Aug 6, 2024
80ef44e
Convert initial_state to dict
NicolaCourtier Aug 6, 2024
8e67d89
Add guidance
NicolaCourtier Aug 6, 2024
d5f63d0
Remove empty dictionary defaults
NicolaCourtier Aug 6, 2024
d0d1bd2
style: pre-commit fixes
pre-commit-ci[bot] Aug 6, 2024
5c52712
Add warning stacklevels
NicolaCourtier Aug 6, 2024
dacafc3
Catch simulation errors in problem evaluation
NicolaCourtier Aug 6, 2024
bcc7acf
Add pybamm version comment
NicolaCourtier Aug 6, 2024
8607a6b
Add set initial ocv check
NicolaCourtier Aug 6, 2024
605f509
Add model.clear and remove setters
NicolaCourtier Aug 7, 2024
a5be6ee
Merge branch '445-remove-setters' into 424-fitting-ocv
NicolaCourtier Aug 7, 2024
a188c4b
Update unscented_kalman.py
NicolaCourtier Aug 7, 2024
ca2a5b1
Update unscented_kalman.py
NicolaCourtier Aug 7, 2024
a4c0030
Update test_models.py
NicolaCourtier Aug 7, 2024
742f9d4
Update test_set_initial_state
NicolaCourtier Aug 7, 2024
db09455
Merge branch 'develop' into 424-fitting-ocv
NicolaCourtier Aug 7, 2024
4f7c75f
Merge branch '424-fitting-ocv' into 238b-multi-fitting
NicolaCourtier Aug 7, 2024
bbece50
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Aug 7, 2024
453d618
Use clear in model.new_copy
NicolaCourtier Aug 7, 2024
36ddd93
Reference public attributes
NicolaCourtier Aug 7, 2024
1b8cdb1
Move MultiFittingProblem into separate file
NicolaCourtier Aug 7, 2024
60a6ca8
Update description
NicolaCourtier Aug 7, 2024
9e02d24
Add dataset property
NicolaCourtier Aug 7, 2024
e494be5
Fix changes due to linting
NicolaCourtier Aug 7, 2024
172b484
Add test_multi_fitting_problem
NicolaCourtier Aug 7, 2024
0cb2917
Add problem.set_initial_state
NicolaCourtier Aug 8, 2024
a865732
Merge rebuild into build
NicolaCourtier Aug 8, 2024
0e4754b
Update CHANGELOG.md
NicolaCourtier Aug 8, 2024
791a732
Update base_model.py
NicolaCourtier Aug 8, 2024
f675e2e
Fix notebooks
NicolaCourtier Aug 8, 2024
d55a624
Merge branch '444-merge-rebuild' into 238b-multi-fitting
NicolaCourtier Aug 8, 2024
9c2f0b3
Update multi_fitting with different initial SoC
NicolaCourtier Aug 8, 2024
25f0714
Update copying
NicolaCourtier Aug 8, 2024
2cf54a6
Add check for identical models
NicolaCourtier Aug 8, 2024
dbded27
Merge branch 'develop' into 238b-multi-fitting
NicolaCourtier Aug 9, 2024
7eeeb00
refactor: model.new_copy() args as dictionary and single construction
BradyPlanden Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- [#364](https://github.com/pybop-team/PyBOP/pull/364) - Adds the MultiFittingProblem class and the multi_fitting example script.
- [#444](https://github.com/pybop-team/PyBOP/issues/444) - Merge `BaseModel` `build()` and `rebuild()` functionality.
- [#435](https://github.com/pybop-team/PyBOP/pull/435) - Adds SLF001 linting for private members.
- [#418](https://github.com/pybop-team/PyBOP/issues/418) - Wraps the `get_parameter_info` method from PyBaMM to get a dictionary of parameter names and types.
Expand All @@ -28,6 +29,7 @@

## Bug Fixes


## Breaking Changes

# [v24.6](https://github.com/pybop-team/PyBOP/tree/v24.6) - 2024-07-08
Expand Down
79 changes: 79 additions & 0 deletions examples/scripts/multi_fitting.py
NicolaCourtier marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import numpy as np

import pybop

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

# Fitting parameters
parameters = pybop.Parameters(
pybop.Parameter(
"Negative electrode active material volume fraction",
prior=pybop.Gaussian(0.68, 0.05),
true_value=parameter_set["Negative electrode active material volume fraction"],
),
pybop.Parameter(
"Positive electrode active material volume fraction",
prior=pybop.Gaussian(0.58, 0.05),
true_value=parameter_set["Positive electrode active material volume fraction"],
),
)

# Generate a dataset and a fitting problem
sigma = 0.001
experiment = pybop.Experiment([("Discharge at 0.5C for 2 minutes (4 second period)")])
values = model.predict(initial_state={"Initial SoC": 0.8}, experiment=experiment)
dataset_1 = pybop.Dataset(
{
"Time [s]": values["Time [s]"].data,
"Current function [A]": values["Current [A]"].data,
"Voltage [V]": values["Voltage [V]"].data
+ np.random.normal(0, sigma, len(values["Voltage [V]"].data)),
}
)
problem_1 = pybop.FittingProblem(model, parameters, dataset_1)

# Generate a second dataset and problem
model = model.new_copy()
experiment = pybop.Experiment([("Discharge at 1C for 1 minutes (4 second period)")])
values = model.predict(initial_state={"Initial SoC": 0.8}, experiment=experiment)
dataset_2 = pybop.Dataset(
{
"Time [s]": values["Time [s]"].data,
"Current function [A]": values["Current [A]"].data,
"Voltage [V]": values["Voltage [V]"].data
+ np.random.normal(0, sigma, len(values["Voltage [V]"].data)),
}
)
problem_2 = pybop.FittingProblem(model, parameters, dataset_2)

# Combine the problems into one
problem = pybop.MultiFittingProblem(problem_1, problem_2)

# Generate the cost function and optimisation class
cost = pybop.SumSquaredError(problem)
optim = pybop.IRPropMin(
cost,
verbose=True,
max_iterations=100,
)

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

# Plot the timeseries output
pybop.quick_plot(problem_1, problem_inputs=x, title="Optimised Comparison")
pybop.quick_plot(problem_2, problem_inputs=x, title="Optimised Comparison")

# Plot convergence
pybop.plot_convergence(optim)

# Plot the parameter traces
pybop.plot_parameters(optim)

# Plot the cost landscape with optimisation path
bounds = np.array([[0.5, 0.8], [0.4, 0.7]])
pybop.plot2d(optim, bounds=bounds, steps=15)
1 change: 1 addition & 0 deletions pybop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
#
from .problems.base_problem import BaseProblem
from .problems.fitting_problem import FittingProblem
from .problems.multi_fitting_problem import MultiFittingProblem
from .problems.design_problem import DesignProblem

#
Expand Down
8 changes: 4 additions & 4 deletions pybop/costs/_weighted_cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ class WeightedCost(BaseCost):

Attributes
---------------------
costs : list[pybop.BaseCost]
A list of PyBOP cost objects.
costs : pybop.BaseCost
The individual PyBOP cost objects.
weights : list[float]
A list of values with which to weight the cost values.
_has_identical_problems : bool
has_identical_problems : bool
If True, the shared problem will be evaluated once and saved before the
self.compute() method of each cost is called (default: False).
_has_separable_problem: bool
has_separable_problem: bool
This attribute must be set to False for WeightedCost objects. If the
corresponding attribute of an individual cost is True, the problem is
separable from the cost function and will be evaluated before the
Expand Down
32 changes: 30 additions & 2 deletions pybop/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@
Parameters
----------
parameters : Parameters, optional
The optimisation parameters. Defaults to None, resulting in the internal `pybop.Parameters` object to be used.
The optimisation parameters. Defaults to None, resulting in the internal
`pybop.Parameters` object to be used.
inputs : Inputs, optional
The input parameters for the simulation (default: None).
"""
Expand Down Expand Up @@ -337,6 +338,7 @@
if requires_rebuild:
self.clear()
self._geometry = self.pybamm_model.default_geometry
# Update both the active and unprocessed parameter sets for consistency
self._parameter_set.update(rebuild_parameters)
self._unprocessed_parameter_set.update(rebuild_parameters)

Expand Down Expand Up @@ -530,7 +532,7 @@
if PyBaMM models are not supported by the current simulation method.

"""
if self._unprocessed_model is None:
if self.pybamm_model is None:
raise ValueError(
"The predict method currently only supports PyBaMM models."
)
Expand Down Expand Up @@ -644,6 +646,32 @@
"""
return copy.copy(self)

def new_copy(self):
"""
Return a new copy of the model, explicitly copying all the mutable attributes
to avoid issues with shared objects.

Returns
-------
BaseModel
A new copy of the model.
"""
model_class = type(self)
if self.pybamm_model is None:
model_args = {"parameter_set": self.parameter_set.copy()}

Check warning on line 661 in pybop/models/base_model.py

View check run for this annotation

Codecov / codecov/patch

pybop/models/base_model.py#L661

Added line #L661 was not covered by tests
else:
model_args = {
"options": self._unprocessed_model.options,
"parameter_set": self._unprocessed_parameter_set.copy(),
"geometry": self.pybamm_model.default_geometry.copy(),
"submesh_types": self.pybamm_model.default_submesh_types.copy(),
"var_pts": self.pybamm_model.default_var_pts.copy(),
"spatial_methods": self.pybamm_model.default_spatial_methods.copy(),
"solver": self.pybamm_model.default_solver.copy(),
}

return model_class(**model_args)

def get_parameter_info(self, print_info: bool = False):
"""
Extracts the parameter names and types and returns them as a dictionary.
Expand Down
6 changes: 5 additions & 1 deletion pybop/optimisers/base_optimiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ def set_allow_infeasible_solutions(self, allow=True):
self.physical_viability = allow
self.allow_infeasible_solutions = allow

if hasattr(self.cost, "problem") and hasattr(self.cost.problem, "_model"):
if (
hasattr(self.cost, "problem")
and hasattr(self.cost.problem, "model")
and self.cost.problem.model is not None
):
self.cost.problem.model.allow_infeasible_solutions = (
self.allow_infeasible_solutions
)
Expand Down
17 changes: 16 additions & 1 deletion pybop/problems/base_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,23 @@ def __init__(
self.check_model = check_model
self.signal = signal or ["Voltage [V]"]
self.additional_variables = additional_variables or []
self.initial_state = initial_state
self.set_initial_state(initial_state)
self._dataset = None
self._time_data = None
self._target = None
self.verbose = False

def set_initial_state(self, initial_state: Optional[dict] = None):
"""
Set the initial state to be applied to evaluations of the problem.

Parameters
----------
initial_state : dict, optional
A valid initial state (default: None).
"""
self.initial_state = initial_state

@property
def n_parameters(self):
return len(self.parameters)
Expand Down Expand Up @@ -156,3 +167,7 @@ def time_data(self):
@time_data.setter
def time_data(self, time_data):
self._time_data = time_data

@property
def dataset(self):
return self._dataset
43 changes: 27 additions & 16 deletions pybop/problems/design_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,6 @@ def __init__(
additional_variables.extend(["Time [s]", "Current [A]"])
additional_variables = list(set(additional_variables))

if initial_state is None:
if isinstance(model, ECircuitModel):
initial_state = {"Initial SoC": model.parameter_set["Initial SoC"]}
else:
initial_state = {"Initial SoC": 1.0} # default value
elif "Initial open-circuit voltage [V]" in initial_state.keys():
warnings.warn(
"It is usually better to define an initial state of charge as the "
"initial_state for a DesignProblem because this state will scale with "
"design properties such as the capacity of the battery, as opposed to the "
"initial open-circuit voltage which may correspond to a different state "
"of charge for each design.",
UserWarning,
stacklevel=1,
)

super().__init__(
parameters, model, check_model, signal, additional_variables, initial_state
)
Expand All @@ -78,6 +62,33 @@ def __init__(
"Non-physical point encountered",
]

def set_initial_state(self, initial_state):
"""
Set the initial state to be applied to evaluations of the problem.

Parameters
----------
initial_state : dict, optional
A valid initial state (default: None).
"""
if initial_state is None:
if isinstance(self.model, ECircuitModel):
initial_state = {"Initial SoC": self.model.parameter_set["Initial SoC"]}
else:
initial_state = {"Initial SoC": 1.0} # default value
elif "Initial open-circuit voltage [V]" in initial_state.keys():
warnings.warn(
"It is usually better to define an initial state of charge as the "
"initial_state for a DesignProblem because this state will scale with "
"design properties such as the capacity of the battery, as opposed to the "
"initial open-circuit voltage which may correspond to a different state "
"of charge for each design.",
UserWarning,
stacklevel=1,
)

self.initial_state = initial_state

def evaluate(self, inputs: Inputs, update_capacity=False):
"""
Evaluate the model with the given parameters and return the signal.
Expand Down
51 changes: 38 additions & 13 deletions pybop/problems/fitting_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,25 @@ class FittingProblem(BaseProblem):
An object or list of the parameters for the problem.
dataset : Dataset
Dataset object containing the data to fit the model to.
check_model : bool, optional
Flag to indicate if the model should be checked (default: True).
signal : str, optional
The variable used for fitting (default: "Voltage [V]").
additional_variables : list[str], optional
Additional variables to observe and store in the solution (default additions are: ["Time [s]"]).
initial_state : dict, optional
A valid initial state, e.g. the initial open-circuit voltage (default: None).

Additional Attributes
---------------------
dataset : dictionary
The dictionary from a Dataset object containing the signal keys and values to fit the model to.
time_data : np.ndarray
The time points in the dataset.
n_time_data : int
The number of time points.
target : np.ndarray
The target values of the signals.
"""

def __init__(
Expand All @@ -44,19 +57,6 @@ def __init__(
additional_variables.extend(["Time [s]"])
additional_variables = list(set(additional_variables))

if initial_state is not None and "Initial SoC" in initial_state.keys():
warnings.warn(
"It is usually better to define an initial open-circuit voltage as the "
"initial_state for a FittingProblem because this value can typically be "
"obtained from the data, unlike the intrinsic initial state of charge. "
"In the case where the fitting parameters do not change the OCV-SOC "
"relationship, the initial state of charge may be passed to the model "
'using, for example, `model.set_initial_state({"Initial SoC": 1.0})` '
"before constructing the FittingProblem.",
UserWarning,
stacklevel=1,
)

super().__init__(
parameters, model, check_model, signal, additional_variables, initial_state
)
Expand All @@ -82,6 +82,30 @@ def __init__(
initial_state=self.initial_state,
)

def set_initial_state(self, initial_state: Optional[dict] = None):
"""
Set the initial state to be applied to evaluations of the problem.

Parameters
----------
initial_state : dict, optional
A valid initial state (default: None).
"""
if initial_state is not None and "Initial SoC" in initial_state.keys():
warnings.warn(
"It is usually better to define an initial open-circuit voltage as the "
"initial_state for a FittingProblem because this value can typically be "
"obtained from the data, unlike the intrinsic initial state of charge. "
"In the case where the fitting parameters do not change the OCV-SOC "
"relationship, the initial state of charge may be passed to the model "
'using, for example, `model.set_initial_state({"Initial SoC": 1.0})` '
"before constructing the FittingProblem.",
UserWarning,
stacklevel=1,
)

self.initial_state = initial_state

def evaluate(
self, inputs: Inputs, update_capacity=False
) -> dict[str, np.ndarray[np.float64]]:
Expand Down Expand Up @@ -130,6 +154,7 @@ def evaluateS1(self, inputs: Inputs):
dy/dx(t) evaluated with given inputs.
"""
inputs = self.parameters.verify(inputs)
self.parameters.update(values=list(inputs.values()))

try:
sol = self._model.simulateS1(
Expand Down
Loading
Loading