Source code for pybop.problems.multi_fitting_problem

from typing import Optional

import numpy as np

from pybop import BaseProblem, Dataset
from pybop.parameters.parameter import Inputs, Parameters


[docs] class MultiFittingProblem(BaseProblem): """ Problem class for joining mulitple fitting problems into one combined fitting problem. Extends `BaseProblem` in a similar way to FittingProblem but for multiple parameter estimation problems, which must first be defined individually. Additional Attributes --------------------- problems : pybop.FittingProblem The individual PyBOP fitting problems. """ def __init__(self, *args):
[docs] self.problems = []
models_to_check = [] for problem in args: self.problems.append(problem) if problem.model is not None: models_to_check.append(problem.model) # Check that there are no copies of the same model if len(set(models_to_check)) < len(models_to_check): raise ValueError("Make a new_copy of the model for each problem.") # Compile the set of parameters, ignoring duplicates combined_parameters = Parameters() for problem in self.problems: combined_parameters.join(problem.parameters) # Combine the target datasets domain = self.problems[0].domain combined_domain_data = [] combined_signal = [] for problem in self.problems: domain = problem.domain if problem.domain == domain else "Mixed domain" for signal in problem.signal: combined_domain_data.extend(problem.domain_data) combined_signal.extend(problem.target[signal]) super().__init__( parameters=combined_parameters, model=None, signal=["Combined signal"], )
[docs] self.domain = domain
combined_dataset = Dataset( { self.domain: np.asarray(combined_domain_data), "Combined signal": np.asarray(combined_signal), } )
[docs] self._dataset = combined_dataset.data
self.parameters.initial_value() # Unpack domain and target data
[docs] self._domain_data = self._dataset[self.domain]
[docs] self.n_domain_data = len(self._domain_data)
self.set_target(combined_dataset)
[docs] 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). """ for problem in self.problems: problem.set_initial_state(initial_state)
[docs] def evaluate(self, inputs: Inputs): """ Evaluate the model with the given parameters and return the signal. Parameters ---------- inputs : Inputs Parameters for evaluation of the model. Returns ------- y : np.ndarray The model output y(t) simulated with given inputs. """ inputs = self.parameters.verify(inputs) self.parameters.update(values=list(inputs.values())) combined_domain = [] combined_signal = [] for problem in self.problems: problem_inputs = problem.parameters.as_dict() problem_output = problem.evaluate(problem_inputs) domain_data = ( problem_output[problem.domain] if problem.domain in problem_output.keys() else problem.domain_data[: len(problem_output[problem.signal[0]])] ) # Collect signals for signal in problem.signal: combined_domain.extend(domain_data) combined_signal.extend(problem_output[signal]) return { self.domain: np.asarray(combined_domain), "Combined signal": np.asarray(combined_signal), }
[docs] def evaluateS1(self, inputs: Inputs): """ Evaluate the model with the given parameters and return the signal and its derivatives. Parameters ---------- inputs : Inputs Parameters for evaluation of the model. Returns ------- tuple[dict, np.ndarray] A tuple containing the simulation result y(t) as a dictionary and the sensitivities dy/dx(t) evaluated with given inputs. """ inputs = self.parameters.verify(inputs) self.parameters.update(values=list(inputs.values())) combined_domain = [] combined_signal = [] dy = dict.fromkeys(self.parameters.keys()) for key in self.parameters.keys(): dy[key] = {"Combined signal": []} for problem in self.problems: problem_inputs = problem.parameters.as_dict() problem_output, dyi = problem.evaluateS1(problem_inputs) domain_data = ( problem_output[problem.domain] if problem.domain in problem_output.keys() else problem.domain_data[: len(problem_output[problem.signal[0]])] ) # Collect signals and derivatives for signal in problem.signal: combined_domain.extend(domain_data) combined_signal.extend(problem_output[signal]) for key in self.parameters.keys(): dy[key]["Combined signal"].extend(dyi[key][signal]) y = { self.domain: np.asarray(combined_domain), "Combined signal": np.asarray(combined_signal), } return (y, dy)
@property
[docs] def pybamm_solution(self): solution_list = [] for problem in self.problems: solution_list.append(problem.pybamm_solution) return solution_list if any(solution_list) else None