From 0137fa7ca5aa5522b62b2bf6d7ef9fcd03fa9b02 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:19:29 +0100 Subject: [PATCH 01/15] fixes: stricter ruff settings - 1 --- docs/conf.py | 2 +- pybop/costs/fitting_costs.py | 2 +- pybop/models/base_model.py | 2 +- pybop/observers/unscented_kalman.py | 4 ++-- pybop/parameters/parameter_set.py | 2 +- pyproject.toml | 24 +++++++++++++++++++++--- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index cfc37a90f..e8d1fdaa7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ from pathlib import Path sys.path.append(str(Path(".").resolve())) -from pybop._version import __version__ # noqa: E402 +from pybop._version import __version__ # -- Project information ----------------------------------------------------- project = "PyBOP" diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index eff56059c..aee6882c8 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -161,7 +161,7 @@ def _evaluate(self, x, grad=None): e = np.asarray( [ - np.sum(((prediction[signal] - self._target[signal]) ** 2)) + np.sum((prediction[signal] - self._target[signal]) ** 2) for signal in self.signal ] ) diff --git a/pybop/models/base_model.py b/pybop/models/base_model.py index a05062672..5e82455fb 100644 --- a/pybop/models/base_model.py +++ b/pybop/models/base_model.py @@ -12,7 +12,7 @@ @dataclass -class TimeSeriesState(object): +class TimeSeriesState: """ The current state of a time series model that is a pybamm model. """ diff --git a/pybop/observers/unscented_kalman.py b/pybop/observers/unscented_kalman.py index 0b6425db9..42e1e69b0 100644 --- a/pybop/observers/unscented_kalman.py +++ b/pybop/observers/unscented_kalman.py @@ -152,7 +152,7 @@ def get_current_covariance(self) -> Covariance: @dataclass -class SigmaPoint(object): +class SigmaPoint: """ A sigma point is a point in the state space that is used to estimate the mean and covariance of a random variable. """ @@ -162,7 +162,7 @@ class SigmaPoint(object): w_c: float -class SquareRootUKF(object): +class SquareRootUKF: """ van der Menve, R., & Wan, E. A. (2001). THE SQUARE-ROOT UNSCENTED KALMAN FILTER FOR STATE AND PARAMETER-ESTIMATION. https://doi.org/10.1109/ICASSP.2001.940586 diff --git a/pybop/parameters/parameter_set.py b/pybop/parameters/parameter_set.py index a1ab63cd3..ddac8d9ef 100644 --- a/pybop/parameters/parameter_set.py +++ b/pybop/parameters/parameter_set.py @@ -60,7 +60,7 @@ def import_parameters(self, json_path=None): # Read JSON file if not self.params and self.json_path: - with open(self.json_path, "r") as file: + with open(self.json_path) as file: self.params = json.load(file) else: raise ValueError( diff --git a/pyproject.toml b/pyproject.toml index 6d2e1b61c..096bab1c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,8 +83,26 @@ extend-include = ["*.ipynb"] extend-exclude = ["__init__.py"] [tool.ruff.lint] -extend-select = ["I"] +select = [ + "A", # Pyflake attribute and variable clashes with builtins + "B", # bugbear: security warnings + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # import order + "ISC", # implicit string concatenation + "UP", # Better syntax is available in your python version + "RUF", # the ruff developer's own rules +] + ignore = ["E501","E741"] +per-file-ignores = {"**.ipynb" = ["E402", "E703"]} + +[tool.ruff.lint.mccabe] +max-complexity = 10 + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" -[tool.ruff.lint.per-file-ignores] -"**.ipynb" = ["E402", "E703"] +[tool.ruff.lint.pydocstyle] +convention = "google" From 344fdd2898ac9c10291b216f7e8f6215410d49bb Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:30:26 +0100 Subject: [PATCH 02/15] fixes: stricter ruff settings - 2, --unsafe-fixes --- docs/_extension/gallery_directive.py | 6 +++--- examples/standalone/problem.py | 2 +- pybop/_dataset.py | 6 ++++-- pybop/costs/_likelihoods.py | 6 +++--- pybop/costs/design_costs.py | 6 +++--- pybop/costs/fitting_costs.py | 6 +++--- pybop/models/base_model.py | 26 +++++++++++++------------- pybop/observers/observer.py | 8 ++++++-- pybop/observers/unscented_kalman.py | 18 +++++++++++------- pybop/optimisers/base_optimiser.py | 5 +++-- pybop/optimisers/scipy_optimisers.py | 2 +- pybop/parameters/parameter.py | 17 ++++++++--------- pybop/parameters/parameter_set.py | 2 +- pybop/plotting/plot_dataset.py | 6 +++--- pybop/problems/base_problem.py | 8 ++++++-- pybop/problems/design_problem.py | 8 ++++++-- pybop/problems/fitting_problem.py | 10 +++++++--- tests/unit/test_optimisation.py | 4 ++-- 18 files changed, 84 insertions(+), 62 deletions(-) diff --git a/docs/_extension/gallery_directive.py b/docs/_extension/gallery_directive.py index 3579ffcd8..4ab88d996 100644 --- a/docs/_extension/gallery_directive.py +++ b/docs/_extension/gallery_directive.py @@ -12,7 +12,7 @@ """ from pathlib import Path -from typing import Any, Dict, List +from typing import Any from docutils import nodes from docutils.parsers.rst import directives @@ -68,7 +68,7 @@ class GalleryGridDirective(SphinxDirective): "class-card": directives.unchanged, } - def run(self) -> List[nodes.Node]: + def run(self) -> list[nodes.Node]: """Create the gallery grid.""" if self.arguments: # If an argument is given, assume it's a path to a YAML file @@ -129,7 +129,7 @@ def run(self) -> List[nodes.Node]: return [container.children[0]] -def setup(app: Sphinx) -> Dict[str, Any]: +def setup(app: Sphinx) -> dict[str, Any]: """Add custom configuration to sphinx app. Args: diff --git a/examples/standalone/problem.py b/examples/standalone/problem.py index d6d1f4b01..94eb4be8d 100644 --- a/examples/standalone/problem.py +++ b/examples/standalone/problem.py @@ -24,7 +24,7 @@ def __init__( self._dataset = dataset.data # Check that the dataset contains time and current - for name in ["Time [s]"] + self.signal: + for name in ["Time [s]", *self.signal]: if name not in self._dataset: raise ValueError(f"expected {name} in list of dataset") diff --git a/pybop/_dataset.py b/pybop/_dataset.py index 0da8be4be..fe8fd9a91 100644 --- a/pybop/_dataset.py +++ b/pybop/_dataset.py @@ -96,7 +96,7 @@ def Interpolant(self): else: NotImplementedError("Only time interpolation is supported") - def check(self, signal=["Voltage [V]"]): + def check(self, signal=None): """ Check the consistency of a PyBOP Dataset against the expected format. @@ -110,11 +110,13 @@ def check(self, signal=["Voltage [V]"]): ValueError If the time series and the data series are not consistent. """ + if signal is None: + signal = ["Voltage [V]"] if isinstance(signal, str): signal = [signal] # Check that the dataset contains time and chosen signal - for name in ["Time [s]"] + signal: + for name in ["Time [s]", *signal]: if name not in self.names: raise ValueError(f"expected {name} in list of dataset") diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index cce09f9bb..9c9c1eaeb 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -9,7 +9,7 @@ class BaseLikelihood(BaseCost): """ def __init__(self, problem): - super(BaseLikelihood, self).__init__(problem) + super().__init__(problem) self.n_time_data = problem.n_time_data @@ -28,7 +28,7 @@ class GaussianLogLikelihoodKnownSigma(BaseLikelihood): """ def __init__(self, problem, sigma): - super(GaussianLogLikelihoodKnownSigma, self).__init__(problem) + super().__init__(problem) self.sigma = None self.set_sigma(sigma) self._offset = -0.5 * self.n_time_data * np.log(2 * np.pi / self.sigma) @@ -121,7 +121,7 @@ class GaussianLogLikelihood(BaseLikelihood): """ def __init__(self, problem): - super(GaussianLogLikelihood, self).__init__(problem) + super().__init__(problem) self._logpi = -0.5 * self.n_time_data * np.log(2 * np.pi) self._dl = np.ones(self.n_parameters + self.n_outputs) diff --git a/pybop/costs/design_costs.py b/pybop/costs/design_costs.py index 60064c65c..077e3a030 100644 --- a/pybop/costs/design_costs.py +++ b/pybop/costs/design_costs.py @@ -31,7 +31,7 @@ def __init__(self, problem, update_capacity=False): problem : object The problem instance containing the model and data. """ - super(DesignCost, self).__init__(problem) + super().__init__(problem) self.problem = problem if update_capacity is True: nominal_capacity_warning = ( @@ -97,7 +97,7 @@ class GravimetricEnergyDensity(DesignCost): """ def __init__(self, problem, update_capacity=False): - super(GravimetricEnergyDensity, self).__init__(problem, update_capacity) + super().__init__(problem, update_capacity) def _evaluate(self, x, grad=None): """ @@ -156,7 +156,7 @@ class VolumetricEnergyDensity(DesignCost): """ def __init__(self, problem, update_capacity=False): - super(VolumetricEnergyDensity, self).__init__(problem, update_capacity) + super().__init__(problem, update_capacity) def _evaluate(self, x, grad=None): """ diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index aee6882c8..aa025e0c7 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -18,7 +18,7 @@ class RootMeanSquaredError(BaseCost): """ def __init__(self, problem): - super(RootMeanSquaredError, self).__init__(problem) + super().__init__(problem) # Default fail gradient self._de = 1.0 @@ -131,7 +131,7 @@ class SumSquaredError(BaseCost): """ def __init__(self, problem): - super(SumSquaredError, self).__init__(problem) + super().__init__(problem) # Default fail gradient self._de = 1.0 @@ -293,7 +293,7 @@ class MAP(BaseLikelihood): """ def __init__(self, problem, likelihood, sigma=None): - super(MAP, self).__init__(problem) + super().__init__(problem) self.sigma0 = sigma if self.sigma0 is None: self.sigma0 = [] diff --git a/pybop/models/base_model.py b/pybop/models/base_model.py index 5e82455fb..7c338b989 100644 --- a/pybop/models/base_model.py +++ b/pybop/models/base_model.py @@ -1,6 +1,6 @@ import copy from dataclasses import dataclass -from typing import Any, Dict, Optional, Union +from typing import Any, Optional, Union import casadi import numpy as np @@ -8,7 +8,7 @@ from pybop import Dataset, Experiment, Parameters, ParameterSet -Inputs = Dict[str, float] +Inputs = dict[str, float] @dataclass @@ -81,9 +81,9 @@ def n_parameters(self): def build( self, dataset: Dataset = None, - parameters: Union[Parameters, Dict] = None, + parameters: Union[Parameters, dict] = None, check_model: bool = True, - init_soc: float = None, + init_soc: Optional[float] = None, ) -> None: """ Construct the PyBaMM model if not already built, and set parameters. @@ -196,10 +196,10 @@ def set_params(self, rebuild=False): def rebuild( self, dataset: Dataset = None, - parameters: Union[Parameters, Dict] = None, + parameters: Union[Parameters, dict] = None, parameter_set: ParameterSet = None, check_model: bool = True, - init_soc: float = None, + init_soc: Optional[float] = None, ) -> None: """ Rebuild the PyBaMM model for a given parameter set. @@ -243,7 +243,7 @@ def rebuild( # Clear solver and setup model self._solver._model_set_up = {} - def classify_and_update_parameters(self, parameters: Union[Parameters, Dict]): + def classify_and_update_parameters(self, parameters: Union[Parameters, dict]): """ Update the parameter values according to their classification as either 'rebuild_parameters' which require a model rebuild and @@ -326,7 +326,7 @@ def step(self, state: TimeSeriesState, time: np.ndarray) -> TimeSeriesState: def simulate( self, inputs: Inputs, t_eval: np.array - ) -> Dict[str, np.ndarray[np.float64]]: + ) -> dict[str, np.ndarray[np.float64]]: """ Execute the forward model simulation and return the result. @@ -459,8 +459,8 @@ def predict( t_eval: np.array = None, parameter_set: ParameterSet = None, experiment: Experiment = None, - init_soc: float = None, - ) -> Dict[str, np.ndarray[np.float64]]: + init_soc: Optional[float] = None, + ) -> dict[str, np.ndarray[np.float64]]: """ Solve the model using PyBaMM's simulation framework and return the solution. @@ -689,7 +689,7 @@ def submesh_types(self): return self._submesh_types @submesh_types.setter - def submesh_types(self, submesh_types: Optional[Dict[str, Any]]): + def submesh_types(self, submesh_types: Optional[dict[str, Any]]): self._submesh_types = ( submesh_types.copy() if submesh_types is not None else None ) @@ -703,7 +703,7 @@ def var_pts(self): return self._var_pts @var_pts.setter - def var_pts(self, var_pts: Optional[Dict[str, int]]): + def var_pts(self, var_pts: Optional[dict[str, int]]): self._var_pts = var_pts.copy() if var_pts is not None else None @property @@ -711,7 +711,7 @@ def spatial_methods(self): return self._spatial_methods @spatial_methods.setter - def spatial_methods(self, spatial_methods: Optional[Dict[str, Any]]): + def spatial_methods(self, spatial_methods: Optional[dict[str, Any]]): self._spatial_methods = ( spatial_methods.copy() if spatial_methods is not None else None ) diff --git a/pybop/observers/observer.py b/pybop/observers/observer.py index 1b81c5ac7..6a43f5f39 100644 --- a/pybop/observers/observer.py +++ b/pybop/observers/observer.py @@ -38,10 +38,14 @@ def __init__( parameters: Parameters, model: BaseModel, check_model=True, - signal=["Voltage [V]"], - additional_variables=[], + signal=None, + additional_variables=None, init_soc=None, ) -> None: + if additional_variables is None: + additional_variables = [] + if signal is None: + signal = ["Voltage [V]"] super().__init__( parameters, model, check_model, signal, additional_variables, init_soc ) diff --git a/pybop/observers/unscented_kalman.py b/pybop/observers/unscented_kalman.py index 42e1e69b0..92df26422 100644 --- a/pybop/observers/unscented_kalman.py +++ b/pybop/observers/unscented_kalman.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Tuple, Union +from typing import Union import numpy as np import scipy.linalg as linalg @@ -41,17 +41,21 @@ class UnscentedKalmanFilterObserver(Observer): def __init__( self, - parameters: List[Parameter], + parameters: list[Parameter], model: BaseModel, sigma0: Union[Covariance, float], process: Union[Covariance, float], measure: Union[Covariance, float], dataset=None, check_model=True, - signal=["Voltage [V]"], - additional_variables=[], + signal=None, + additional_variables=None, init_soc=None, ) -> None: + if additional_variables is None: + additional_variables = [] + if signal is None: + signal = ["Voltage [V]"] super().__init__( parameters, model, check_model, signal, additional_variables, init_soc ) @@ -59,7 +63,7 @@ def __init__( self._dataset = dataset.data # Check that the dataset contains time and current - dataset.check(self.signal + ["Current function [A]"]) + dataset.check([*self.signal, "Current function [A]"]) self._time_data = self._dataset["Time [s]"] self.n_time_data = len(self._time_data) @@ -235,7 +239,7 @@ def reset(self, x: np.ndarray, S: np.ndarray) -> None: @staticmethod def gen_sigma_points( x: np.ndarray, S: np.ndarray, alpha: float, beta: float, states: np.ndarray - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Generates 2L+1 sigma points for the unscented transform, where L is the number of states. @@ -291,7 +295,7 @@ def unscented_transform( w_c: np.ndarray, sqrtR: np.ndarray, states: Union[np.ndarray, None] = None, - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: """ Performs the unscented transform diff --git a/pybop/optimisers/base_optimiser.py b/pybop/optimisers/base_optimiser.py index b283b9d8e..92001d9ae 100644 --- a/pybop/optimisers/base_optimiser.py +++ b/pybop/optimisers/base_optimiser.py @@ -1,4 +1,5 @@ import warnings +from typing import Optional import numpy as np @@ -253,8 +254,8 @@ class Result: def __init__( self, x: np.ndarray = None, - final_cost: float = None, - n_iterations: int = None, + final_cost: Optional[float] = None, + n_iterations: Optional[int] = None, scipy_result=None, ): self.x = x diff --git a/pybop/optimisers/scipy_optimisers.py b/pybop/optimisers/scipy_optimisers.py index 843423042..acd3470f4 100644 --- a/pybop/optimisers/scipy_optimisers.py +++ b/pybop/optimisers/scipy_optimisers.py @@ -161,7 +161,7 @@ def callback(intermediate_result: OptimizeResult): # Compute the absolute initial cost and resample if required self._cost0 = np.abs(self.cost(self.x0)) if np.isinf(self._cost0): - for i in range(1, self.num_resamples): + for _i in range(1, self.num_resamples): x0 = self.cost.parameters.rvs(1) self._cost0 = np.abs(self.cost(x0)) if not np.isinf(self._cost0): diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index 76754847d..ecffb336b 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -1,5 +1,4 @@ from collections import OrderedDict -from typing import Dict, List import numpy as np @@ -186,7 +185,7 @@ def __getitem__(self, key: str) -> Parameter: def __len__(self) -> int: return len(self.param) - def keys(self) -> List: + def keys(self) -> list: """ A list of parameter names """ @@ -240,7 +239,7 @@ def remove(self, parameter_name): # Remove the parameter self.param.pop(parameter_name) - def get_bounds(self) -> Dict: + def get_bounds(self) -> dict: """ Get bounds, for either all or no parameters. """ @@ -267,7 +266,7 @@ def update(self, values): for i, param in enumerate(self.param.values()): param.update(value=values[i]) - def rvs(self, n_samples: int) -> List: + def rvs(self, n_samples: int) -> list: """ Draw random samples from each parameter's prior distribution. @@ -300,7 +299,7 @@ def rvs(self, n_samples: int) -> List: return all_samples - def get_sigma0(self) -> List: + def get_sigma0(self) -> list: """ Get the standard deviation, for either all or no parameters. """ @@ -317,7 +316,7 @@ def get_sigma0(self) -> List: return sigma0 - def initial_value(self) -> List: + def initial_value(self) -> list: """ Return the initial value of each parameter. """ @@ -331,7 +330,7 @@ def initial_value(self) -> List: return initial_values - def current_value(self) -> List: + def current_value(self) -> list: """ Return the current value of each parameter. """ @@ -342,7 +341,7 @@ def current_value(self) -> List: return current_values - def true_value(self) -> List: + def true_value(self) -> list: """ Return the true value of each parameter. """ @@ -372,7 +371,7 @@ def get_bounds_for_plotly(self): return bounds - def as_dict(self, values=None) -> Dict: + def as_dict(self, values=None) -> dict: if values is None: values = self.current_value() return {key: values[i] for i, key in enumerate(self.param.keys())} diff --git a/pybop/parameters/parameter_set.py b/pybop/parameters/parameter_set.py index ddac8d9ef..4137af1c9 100644 --- a/pybop/parameters/parameter_set.py +++ b/pybop/parameters/parameter_set.py @@ -132,7 +132,7 @@ def export_parameters(self, output_json_path, fit_params=None): # Update parameter set if fit_params is not None: - for i, param in enumerate(fit_params): + for _i, param in enumerate(fit_params): exportable_params.update({param.name: param.value}) # Replace non-serializable values diff --git a/pybop/plotting/plot_dataset.py b/pybop/plotting/plot_dataset.py index 70573e476..ecc84aa6e 100644 --- a/pybop/plotting/plot_dataset.py +++ b/pybop/plotting/plot_dataset.py @@ -3,9 +3,7 @@ from pybop import StandardPlot, plot_trajectories -def plot_dataset( - dataset, signal=["Voltage [V]"], trace_names=None, show=True, **layout_kwargs -): +def plot_dataset(dataset, signal=None, trace_names=None, show=True, **layout_kwargs): """ Quickly plot a PyBOP Dataset using Plotly. @@ -31,6 +29,8 @@ def plot_dataset( """ # Get data dictionary + if signal is None: + signal = ["Voltage [V]"] dataset.check(signal) # Compile ydata and labels or legend diff --git a/pybop/problems/base_problem.py b/pybop/problems/base_problem.py index 48f53dab1..2e279ec87 100644 --- a/pybop/problems/base_problem.py +++ b/pybop/problems/base_problem.py @@ -26,11 +26,15 @@ def __init__( parameters, model=None, check_model=True, - signal=["Voltage [V]"], - additional_variables=[], + signal=None, + additional_variables=None, init_soc=None, ): # Check if parameters is a list of pybop.Parameter objects + if additional_variables is None: + additional_variables = [] + if signal is None: + signal = ["Voltage [V]"] if isinstance(parameters, list): if all(isinstance(param, Parameter) for param in parameters): parameters = Parameters(*parameters) diff --git a/pybop/problems/design_problem.py b/pybop/problems/design_problem.py index 3217ca95d..71a1f4bf9 100644 --- a/pybop/problems/design_problem.py +++ b/pybop/problems/design_problem.py @@ -33,11 +33,15 @@ def __init__( parameters, experiment, check_model=True, - signal=["Voltage [V]"], - additional_variables=[], + signal=None, + additional_variables=None, init_soc=None, ): # Add time and current and remove duplicates + if additional_variables is None: + additional_variables = [] + if signal is None: + signal = ["Voltage [V]"] additional_variables.extend(["Time [s]", "Current [A]"]) additional_variables = list(set(additional_variables)) diff --git a/pybop/problems/fitting_problem.py b/pybop/problems/fitting_problem.py index 15d1ed7e2..9754a055c 100644 --- a/pybop/problems/fitting_problem.py +++ b/pybop/problems/fitting_problem.py @@ -31,11 +31,15 @@ def __init__( parameters, dataset, check_model=True, - signal=["Voltage [V]"], - additional_variables=[], + signal=None, + additional_variables=None, init_soc=None, ): # Add time and remove duplicates + if additional_variables is None: + additional_variables = [] + if signal is None: + signal = ["Voltage [V]"] additional_variables.extend(["Time [s]"]) additional_variables = list(set(additional_variables)) @@ -46,7 +50,7 @@ def __init__( self.x = self.x0 # Check that the dataset contains time and current - dataset.check(self.signal + ["Current function [A]"]) + dataset.check([*self.signal, "Current function [A]"]) # Unpack time and target data self._time_data = self._dataset["Time [s]"] diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index 97fe12fc5..97df1d231 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -224,7 +224,7 @@ def test_optimiser_kwargs(self, cost, optimiser): assert optim.pints_optimiser._lambda == 0.1 # Incorrect values - for i, match in (("Value", -1),): + for i, _match in (("Value", -1),): with pytest.raises( Exception, match="must be a numeric value between 0 and 1." ): @@ -325,7 +325,7 @@ class RandomClass: @pytest.mark.unit def test_prior_sampling(self, cost): # Tests prior sampling - for i in range(50): + for _i in range(50): optim = pybop.Optimisation(cost=cost) assert optim.x0[0] < 0.62 and optim.x0[0] > 0.58 From a2ae5be6b612dbcee068262b672025de48d6ee02 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:39:55 +0100 Subject: [PATCH 03/15] fixes: stricter ruff settings - 3, ISC003 --- pybop/costs/_likelihoods.py | 2 +- pybop/models/base_model.py | 4 ++-- pybop/optimisers/base_optimiser.py | 4 ++-- pybop/optimisers/pints_optimisers.py | 2 +- pybop/parameters/parameter.py | 4 ++-- pyproject.toml | 1 - tests/unit/test_likelihoods.py | 2 +- tests/unit/test_parameters.py | 8 ++++---- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index 9c9c1eaeb..984bf48df 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -43,7 +43,7 @@ def set_sigma(self, sigma): if sigma is None: raise ValueError( "The GaussianLogLikelihoodKnownSigma cost requires sigma to be " - + "either a scalar value or an array with one entry per dimension." + "either a scalar value or an array with one entry per dimension." ) if not isinstance(sigma, np.ndarray): diff --git a/pybop/models/base_model.py b/pybop/models/base_model.py index 7c338b989..802d64cf4 100644 --- a/pybop/models/base_model.py +++ b/pybop/models/base_model.py @@ -561,8 +561,8 @@ def check_params( for entry in inputs: if not isinstance(entry, (int, float)): raise ValueError( - "Expecting inputs in the form of a dictionary, numeric list" - + f" or None, but received a list with type: {type(inputs)}" + f"Expecting inputs in the form of a dictionary, numeric list" + f" or None, but received a list with type: {type(inputs)}" ) else: inputs = self.parameters.as_dict(inputs) diff --git a/pybop/optimisers/base_optimiser.py b/pybop/optimisers/base_optimiser.py index 92001d9ae..3452456e3 100644 --- a/pybop/optimisers/base_optimiser.py +++ b/pybop/optimisers/base_optimiser.py @@ -80,7 +80,7 @@ def __init__( cost_test = cost(optimiser_kwargs.get("x0", [])) warnings.warn( "The cost is not an instance of pybop.BaseCost, but let's continue " - + "assuming that it is a callable function to be minimised.", + "assuming that it is a callable function to be minimised.", UserWarning, ) self.cost = cost @@ -196,7 +196,7 @@ def check_optimal_parameters(self, x): else: warnings.warn( "Optimised parameters are not physically viable! \nConsider retrying the optimisation" - + " with a non-gradient-based optimiser and the option allow_infeasible_solutions=False", + " with a non-gradient-based optimiser and the option allow_infeasible_solutions=False", UserWarning, stacklevel=2, ) diff --git a/pybop/optimisers/pints_optimisers.py b/pybop/optimisers/pints_optimisers.py index 4872973a8..16f2fe61a 100644 --- a/pybop/optimisers/pints_optimisers.py +++ b/pybop/optimisers/pints_optimisers.py @@ -272,6 +272,6 @@ def __init__(self, cost, **optimiser_kwargs): if x0 is not None and len(x0) == 1: raise ValueError( "CMAES requires optimisation of >= 2 parameters at once. " - + "Please choose another optimiser." + "Please choose another optimiser." ) super().__init__(cost, PintsCMAES, **optimiser_kwargs) diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index ecffb336b..797ebe554 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -211,7 +211,7 @@ def add(self, parameter): if parameter.name in self.param.keys(): raise ValueError( f"There is already a parameter with the name {parameter.name} " - + "in the Parameters object. Please remove the duplicate entry." + "in the Parameters object. Please remove the duplicate entry." ) self.param[parameter.name] = parameter elif isinstance(parameter, dict): @@ -221,7 +221,7 @@ def add(self, parameter): if name in self.param.keys(): raise ValueError( f"There is already a parameter with the name {name} " - + "in the Parameters object. Please remove the duplicate entry." + "in the Parameters object. Please remove the duplicate entry." ) self.param[name] = Parameter(**parameter) else: diff --git a/pyproject.toml b/pyproject.toml index 096bab1c3..51a89c0fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,6 @@ select = [ "I", # import order "ISC", # implicit string concatenation "UP", # Better syntax is available in your python version - "RUF", # the ruff developer's own rules ] ignore = ["E501","E741"] diff --git a/tests/unit/test_likelihoods.py b/tests/unit/test_likelihoods.py index 41ee36673..84669f7fb 100644 --- a/tests/unit/test_likelihoods.py +++ b/tests/unit/test_likelihoods.py @@ -97,7 +97,7 @@ def test_set_get_sigma(self, one_signal_problem): with pytest.raises( ValueError, match="The GaussianLogLikelihoodKnownSigma cost requires sigma to be " - + "either a scalar value or an array with one entry per dimension.", + "either a scalar value or an array with one entry per dimension.", ): pybop.GaussianLogLikelihoodKnownSigma(one_signal_problem, sigma=None) diff --git a/tests/unit/test_parameters.py b/tests/unit/test_parameters.py index 736684fef..87f7c7e6d 100644 --- a/tests/unit/test_parameters.py +++ b/tests/unit/test_parameters.py @@ -108,8 +108,8 @@ def test_parameters_construction(self, parameter): with pytest.raises( ValueError, match="There is already a parameter with the name " - + "Negative electrode active material volume fraction" - + " in the Parameters object. Please remove the duplicate entry.", + "Negative electrode active material volume fraction" + " in the Parameters object. Please remove the duplicate entry.", ): params.add(parameter) @@ -131,8 +131,8 @@ def test_parameters_construction(self, parameter): with pytest.raises( ValueError, match="There is already a parameter with the name " - + "Negative electrode active material volume fraction" - + " in the Parameters object. Please remove the duplicate entry.", + "Negative electrode active material volume fraction" + " in the Parameters object. Please remove the duplicate entry.", ): params.add( dict( From 2ce06c26df7ece3e9fc8c263e65f693bf425f515 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:47:33 +0100 Subject: [PATCH 04/15] fixes: stricter ruff settings - 4, B904 --- pybop/costs/base_cost.py | 4 ++-- pybop/costs/fitting_costs.py | 2 +- pybop/models/lithium_ion/base_echem.py | 2 +- pybop/optimisers/base_optimiser.py | 4 ++-- pybop/plotting/plotly_manager.py | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pybop/costs/base_cost.py b/pybop/costs/base_cost.py index 04d0a3934..0ac94b84f 100644 --- a/pybop/costs/base_cost.py +++ b/pybop/costs/base_cost.py @@ -73,7 +73,7 @@ def evaluate(self, x, grad=None): raise e except Exception as e: - raise ValueError(f"Error in cost calculation: {e}") + raise ValueError(f"Error in cost calculation: {e}") from e def _evaluate(self, x, grad=None): """ @@ -128,7 +128,7 @@ def evaluateS1(self, x): raise e except Exception as e: - raise ValueError(f"Error in cost calculation: {e}") + raise ValueError(f"Error in cost calculation: {e}") from e def _evaluateS1(self, x): """ diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index aa025e0c7..fe975e12e 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -305,7 +305,7 @@ def __init__(self, problem, likelihood, sigma=None): except Exception as e: raise ValueError( f"An error occurred when constructing the Likelihood class: {e}" - ) + ) from e if hasattr(self, "likelihood") and not isinstance( self.likelihood, BaseLikelihood diff --git a/pybop/models/lithium_ion/base_echem.py b/pybop/models/lithium_ion/base_echem.py index 6947774bf..9b4af998a 100644 --- a/pybop/models/lithium_ion/base_echem.py +++ b/pybop/models/lithium_ion/base_echem.py @@ -313,7 +313,7 @@ def approximate_capacity(self, x): mean_sto_pos ) - negative_electrode_ocp(mean_sto_neg) except Exception as e: - raise ValueError(f"Error in average voltage calculation: {e}") + raise ValueError(f"Error in average voltage calculation: {e}") from e # Calculate and update nominal capacity theoretical_capacity = theoretical_energy / average_voltage diff --git a/pybop/optimisers/base_optimiser.py b/pybop/optimisers/base_optimiser.py index 3452456e3..b8b897461 100644 --- a/pybop/optimisers/base_optimiser.py +++ b/pybop/optimisers/base_optimiser.py @@ -86,8 +86,8 @@ def __init__( self.cost = cost self.minimising = True - except Exception: - raise Exception("The cost is not a recognised cost object or function.") + except Exception as e: + raise Exception("The cost is not a recognised cost object or function.") from e if not np.isscalar(cost_test) or not np.isreal(cost_test): raise TypeError( diff --git a/pybop/plotting/plotly_manager.py b/pybop/plotting/plotly_manager.py index 7b4b079a4..5554a2af5 100644 --- a/pybop/plotting/plotly_manager.py +++ b/pybop/plotting/plotly_manager.py @@ -119,7 +119,7 @@ def check_browser_availability(self): if self.pio and self.pio.renderers.default == "browser": try: webbrowser.get() - except webbrowser.Error: + except webbrowser.Error as e: raise Exception( "\n **Browser Not Found** \nFor Windows users, in order to view figures in the browser using Plotly, " "you need to set the environment variable BROWSER equal to the " @@ -129,4 +129,4 @@ def check_browser_availability(self): "\n\nThen reactivate your virtual environment. Alternatively, you can use a " "different Plotly renderer. For more information see: " "https://plotly.com/python/renderers/#setting-the-default-renderer" - ) + ) from e From 104923adb105860c64fb9b14c2c8d6a7fdb6d424 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:54:04 +0100 Subject: [PATCH 05/15] fixes: stricter ruff settings - 5, B008 --- pybop/plotting/quick_plot.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pybop/plotting/quick_plot.py b/pybop/plotting/quick_plot.py index 5be353a62..1ef4e3ffe 100644 --- a/pybop/plotting/quick_plot.py +++ b/pybop/plotting/quick_plot.py @@ -57,16 +57,16 @@ def __init__( x, y, layout=None, - layout_options=DEFAULT_LAYOUT_OPTIONS.copy(), - trace_options=DEFAULT_TRACE_OPTIONS.copy(), + layout_options=DEFAULT_LAYOUT_OPTIONS, + trace_options=DEFAULT_TRACE_OPTIONS, trace_names=None, trace_name_width=40, ): self.x = x self.y = y self.layout = layout - self.layout_options = layout_options - self.trace_options = DEFAULT_TRACE_OPTIONS.copy() + self.layout_options = layout_options.copy() + self.trace_options = trace_options.copy() if trace_options is not None: for arg, value in trace_options.items(): self.trace_options[arg] = value @@ -246,9 +246,9 @@ def __init__( num_cols=None, axis_titles=None, layout=None, - layout_options=DEFAULT_LAYOUT_OPTIONS.copy(), - subplot_options=DEFAULT_SUBPLOT_OPTIONS.copy(), - trace_options=DEFAULT_SUBPLOT_TRACE_OPTIONS.copy(), + layout_options=DEFAULT_LAYOUT_OPTIONS, + subplot_options=DEFAULT_SUBPLOT_OPTIONS, + trace_options=DEFAULT_SUBPLOT_TRACE_OPTIONS, trace_names=None, trace_name_width=40, ): @@ -267,7 +267,7 @@ def __init__( elif self.num_cols is None: self.num_cols = int(math.ceil(self.num_traces / self.num_rows)) self.axis_titles = axis_titles - self.subplot_options = DEFAULT_SUBPLOT_OPTIONS.copy() + self.subplot_options = subplot_options.copy() if subplot_options is not None: for arg, value in subplot_options.items(): self.subplot_options[arg] = value From 1a257f976982823b26ce5718f4be0bd8e08f1ecb Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 12:59:10 +0100 Subject: [PATCH 06/15] fixes: stricter ruff settings - 6, B028 --- pybop/costs/design_costs.py | 2 +- pybop/models/lithium_ion/base_echem.py | 2 +- pybop/models/lithium_ion/weppner_huggins.py | 2 +- pybop/optimisers/base_optimiser.py | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pybop/costs/design_costs.py b/pybop/costs/design_costs.py index 077e3a030..c7f68cbe3 100644 --- a/pybop/costs/design_costs.py +++ b/pybop/costs/design_costs.py @@ -41,7 +41,7 @@ def __init__(self, problem, update_capacity=False): nominal_capacity_warning = ( "The nominal capacity is fixed at the initial model value." ) - warnings.warn(nominal_capacity_warning, UserWarning) + warnings.warn(nominal_capacity_warning, UserWarning, stacklevel=2) self.update_capacity = update_capacity self.parameter_set = problem.model.parameter_set self.update_simulation_data(self.x0) diff --git a/pybop/models/lithium_ion/base_echem.py b/pybop/models/lithium_ion/base_echem.py index 9b4af998a..0a68355aa 100644 --- a/pybop/models/lithium_ion/base_echem.py +++ b/pybop/models/lithium_ion/base_echem.py @@ -139,7 +139,7 @@ def _check_params( ): if self.param_check_counter <= len(electrode_params): infeasibility_warning = "Non-physical point encountered - [{material_vol_fraction} + {porosity}] > 1.0!" - warnings.warn(infeasibility_warning, UserWarning) + warnings.warn(infeasibility_warning, UserWarning, stacklevel=2) self.param_check_counter += 1 return allow_infeasible_solutions diff --git a/pybop/models/lithium_ion/weppner_huggins.py b/pybop/models/lithium_ion/weppner_huggins.py index 5d8d626a4..74c42c70e 100644 --- a/pybop/models/lithium_ion/weppner_huggins.py +++ b/pybop/models/lithium_ion/weppner_huggins.py @@ -36,7 +36,7 @@ def __init__(self, name="Weppner & Huggins model", **model_kwargs): # Model kwargs (build, options) are not implemented, keeping here for consistent interface if model_kwargs is not dict(build=True): unused_kwargs_warning = "The input model_kwargs are not currently used by the Weppner & Huggins model." - warnings.warn(unused_kwargs_warning, UserWarning) + warnings.warn(unused_kwargs_warning, UserWarning, stacklevel=2) super().__init__({}, name) diff --git a/pybop/optimisers/base_optimiser.py b/pybop/optimisers/base_optimiser.py index b8b897461..1a2732bfe 100644 --- a/pybop/optimisers/base_optimiser.py +++ b/pybop/optimisers/base_optimiser.py @@ -82,6 +82,7 @@ def __init__( "The cost is not an instance of pybop.BaseCost, but let's continue " "assuming that it is a callable function to be minimised.", UserWarning, + stacklevel=2, ) self.cost = cost self.minimising = True From aadef82ee035b66b5a18ab9e266b11d3c4c53d24 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 13:05:28 +0100 Subject: [PATCH 07/15] fixes: stricter ruff settings - 7, A001 --- docs/conf.py | 2 +- tests/examples/test_examples.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e8d1fdaa7..14dd72db0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,7 @@ # -- Project information ----------------------------------------------------- project = "PyBOP" -copyright = "2023, The PyBOP Team" +copyright = "2023, The PyBOP Team" # noqa A001 author = "The PyBOP Team" release = f"v{__version__}" diff --git a/tests/examples/test_examples.py b/tests/examples/test_examples.py index 1c45be840..2bebc6fc6 100644 --- a/tests/examples/test_examples.py +++ b/tests/examples/test_examples.py @@ -12,14 +12,14 @@ class TestExamples: """ def list_of_examples(): - list = [] + examples_list = [] path_to_example_scripts = os.path.join( pybop.script_path, "..", "examples", "scripts" ) for example in os.listdir(path_to_example_scripts): if example.endswith(".py"): - list.append(os.path.join(path_to_example_scripts, example)) - return list + examples_list.append(os.path.join(path_to_example_scripts, example)) + return examples_list @pytest.mark.parametrize("example", list_of_examples()) @pytest.mark.examples From 6e761114699feaaf0ca6dbe6153a5a1ac3a977ae Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 13:14:26 +0100 Subject: [PATCH 08/15] fixes: stricter ruff settings - 8, B017 --- pybop/optimisers/_adamw.py | 2 +- tests/unit/test_optimisation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pybop/optimisers/_adamw.py b/pybop/optimisers/_adamw.py index 24e5ec982..817d8f290 100644 --- a/pybop/optimisers/_adamw.py +++ b/pybop/optimisers/_adamw.py @@ -157,7 +157,7 @@ def tell(self, reply): # Check ask-tell pattern if not self._ready_for_tell: - raise Exception("ask() not called before tell()") + raise RuntimeError("ask() not called before tell()") self._ready_for_tell = False # Unpack reply diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index 97df1d231..b07af2a53 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -242,7 +242,7 @@ def test_optimiser_kwargs(self, cost, optimiser): assert optim.pints_optimiser.n_hyper_parameters() == 5 assert not optim.pints_optimiser.running() assert optim.pints_optimiser.x_guessed() == optim.pints_optimiser._x0 - with pytest.raises(Exception): + with pytest.raises(RuntimeError): optim.pints_optimiser.tell([0.1]) else: From 83e87b63d72ead10cf2cbe5c749813f0ec2b764f Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 13:26:53 +0100 Subject: [PATCH 09/15] fixes: stricter ruff settings - 9, B018 --- tests/unit/test_optimisation.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index b07af2a53..89b950dc7 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -298,12 +298,8 @@ def test_default_optimiser(self, cost): optim = pybop.Optimisation(cost=cost) assert optim.name() == "Exponential Natural Evolution Strategy (xNES)" - # Test incorrect setting attribute - with pytest.raises( - AttributeError, - match="'Optimisation' object has no attribute 'not_a_valid_attribute'", - ): - optim.not_a_valid_attribute + # Test getting incorrect attribute + assert not hasattr(optim, "not_a_valid_attribute") @pytest.mark.unit def test_incorrect_optimiser_class(self, cost): From 837d3cac23e1baaf4c4184937c703646865e0e92 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 13:38:32 +0100 Subject: [PATCH 10/15] fixes: stricter ruff settings - 10, TID252 --- benchmarks/benchmark_model.py | 3 +-- benchmarks/benchmark_optim_construction.py | 3 +-- benchmarks/benchmark_parameterisation.py | 3 +-- benchmarks/benchmark_track_parameterisation.py | 3 +-- pybop/models/lithium_ion/echem.py | 3 +-- pyproject.toml | 6 ++---- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/benchmarks/benchmark_model.py b/benchmarks/benchmark_model.py index 843b03bcc..5d496215b 100644 --- a/benchmarks/benchmark_model.py +++ b/benchmarks/benchmark_model.py @@ -1,8 +1,7 @@ import numpy as np import pybop - -from .benchmark_utils import set_random_seed +from benchmarks.benchmark_utils import set_random_seed class BenchmarkModel: diff --git a/benchmarks/benchmark_optim_construction.py b/benchmarks/benchmark_optim_construction.py index fee5f0789..75bb28b3c 100644 --- a/benchmarks/benchmark_optim_construction.py +++ b/benchmarks/benchmark_optim_construction.py @@ -1,8 +1,7 @@ import numpy as np import pybop - -from .benchmark_utils import set_random_seed +from benchmarks.benchmark_utils import set_random_seed class BenchmarkOptimisationConstruction: diff --git a/benchmarks/benchmark_parameterisation.py b/benchmarks/benchmark_parameterisation.py index a64116a48..681502387 100644 --- a/benchmarks/benchmark_parameterisation.py +++ b/benchmarks/benchmark_parameterisation.py @@ -1,8 +1,7 @@ import numpy as np import pybop - -from .benchmark_utils import set_random_seed +from benchmarks.benchmark_utils import set_random_seed class BenchmarkParameterisation: diff --git a/benchmarks/benchmark_track_parameterisation.py b/benchmarks/benchmark_track_parameterisation.py index 9180ffecb..a420dd3b9 100644 --- a/benchmarks/benchmark_track_parameterisation.py +++ b/benchmarks/benchmark_track_parameterisation.py @@ -1,8 +1,7 @@ import numpy as np import pybop - -from .benchmark_utils import set_random_seed +from benchmarks.benchmark_utils import set_random_seed class BenchmarkTrackParameterisation: diff --git a/pybop/models/lithium_ion/echem.py b/pybop/models/lithium_ion/echem.py index 8bd8ab636..9fdd308e3 100644 --- a/pybop/models/lithium_ion/echem.py +++ b/pybop/models/lithium_ion/echem.py @@ -1,8 +1,7 @@ from pybamm import lithium_ion as pybamm_lithium_ion from pybop.models.lithium_ion.base_echem import EChemBaseModel - -from .weppner_huggins import BaseWeppnerHuggins +from pybop.models.lithium_ion.weppner_huggins import BaseWeppnerHuggins class SPM(EChemBaseModel): diff --git a/pyproject.toml b/pyproject.toml index 51a89c0fa..23fa0ca78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,16 +90,14 @@ select = [ "W", # pycodestyle warnings "F", # pyflakes "I", # import order - "ISC", # implicit string concatenation + "ISC", # implicit string concatenation + "TID", # tidy-imports "UP", # Better syntax is available in your python version ] ignore = ["E501","E741"] per-file-ignores = {"**.ipynb" = ["E402", "E703"]} -[tool.ruff.lint.mccabe] -max-complexity = 10 - [tool.ruff.lint.flake8-tidy-imports] ban-relative-imports = "all" From b1823511a5134ff29b081af2bd6c162102e64358 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 13:40:05 +0100 Subject: [PATCH 11/15] fixes: stricter ruff settings - 11, remove doc style, updt comments --- pyproject.toml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 23fa0ca78..047f03c5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,15 +84,15 @@ extend-exclude = ["__init__.py"] [tool.ruff.lint] select = [ - "A", # Pyflake attribute and variable clashes with builtins - "B", # bugbear: security warnings - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # import order - "ISC", # implicit string concatenation - "TID", # tidy-imports - "UP", # Better syntax is available in your python version + "A", # flake8-builtins: Check for Python builtins being used as variables or parameters + "B", # flake8-bugbear: Find likely bugs and design problems in your program + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes: Detect various errors by parsing the source file + "I", # isort: Check and enforce import ordering + "ISC", # flake8-implicit-str-concat: Check for implicit string concatenation + "TID", # flake8-tidy-imports: Validate import hygiene + "UP", # pyupgrade: Automatically upgrade syntax for newer versions of Python ] ignore = ["E501","E741"] @@ -100,6 +100,3 @@ per-file-ignores = {"**.ipynb" = ["E402", "E703"]} [tool.ruff.lint.flake8-tidy-imports] ban-relative-imports = "all" - -[tool.ruff.lint.pydocstyle] -convention = "google" From 483ed51d4923290c2686467674c320793e97df7e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:35:39 +0000 Subject: [PATCH 12/15] style: pre-commit fixes --- docs/conf.py | 2 +- pybop/optimisers/base_optimiser.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 14dd72db0..dd7322083 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,7 @@ # -- Project information ----------------------------------------------------- project = "PyBOP" -copyright = "2023, The PyBOP Team" # noqa A001 +copyright = "2023, The PyBOP Team" # noqa A001 author = "The PyBOP Team" release = f"v{__version__}" diff --git a/pybop/optimisers/base_optimiser.py b/pybop/optimisers/base_optimiser.py index 1a2732bfe..fc27ea0a3 100644 --- a/pybop/optimisers/base_optimiser.py +++ b/pybop/optimisers/base_optimiser.py @@ -88,7 +88,9 @@ def __init__( self.minimising = True except Exception as e: - raise Exception("The cost is not a recognised cost object or function.") from e + raise Exception( + "The cost is not a recognised cost object or function." + ) from e if not np.isscalar(cost_test) or not np.isreal(cost_test): raise TypeError( From b4a5b905c2cb16397dd9c1e74b947406aae05393 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 16:20:00 +0100 Subject: [PATCH 13/15] refactor: remove non-used dataset.Interpolant(), fix coverage --- pybop/_dataset.py | 22 +--------------------- tests/unit/test_dataset.py | 4 ++-- tests/unit/test_plots.py | 2 +- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/pybop/_dataset.py b/pybop/_dataset.py index fe8fd9a91..66bcb1f10 100644 --- a/pybop/_dataset.py +++ b/pybop/_dataset.py @@ -1,6 +1,5 @@ import numpy as np -from pybamm import Interpolant, solvers -from pybamm import t as pybamm_t +from pybamm import solvers class Dataset: @@ -77,25 +76,6 @@ def __getitem__(self, key): return self.data[key] - def Interpolant(self): - """ - Create an interpolation function of the dataset based on the independent variable. - - Currently, only time-based interpolation is supported. This method modifies - the instance's Interpolant attribute to be an interpolation function that - can be evaluated at different points in time. - - Raises - ------ - NotImplementedError - If the independent variable for interpolation is not supported. - """ - - if self.variable == "time": - self.Interpolant = Interpolant(self.x, self.y, pybamm_t) - else: - NotImplementedError("Only time interpolation is supported") - def check(self, signal=None): """ Check the consistency of a PyBOP Dataset against the expected format. diff --git a/tests/unit/test_dataset.py b/tests/unit/test_dataset.py index 9c4eac13d..618b8ad53 100644 --- a/tests/unit/test_dataset.py +++ b/tests/unit/test_dataset.py @@ -20,7 +20,7 @@ def test_dataset(self): data_dictionary = { "Time [s]": solution["Time [s]"].data, "Current [A]": solution["Current [A]"].data, - "Terminal voltage [V]": solution["Terminal voltage [V]"].data, + "Voltage [V]": solution["Voltage [V]"].data, } dataset = pybop.Dataset(data_dictionary) @@ -55,4 +55,4 @@ def test_dataset(self): dataset["Time"] # Test conversion of single signal to list - assert dataset.check(signal="Terminal voltage [V]") + assert dataset.check() diff --git a/tests/unit/test_plots.py b/tests/unit/test_plots.py index b810e3f0e..1fd2def83 100644 --- a/tests/unit/test_plots.py +++ b/tests/unit/test_plots.py @@ -64,7 +64,7 @@ def test_dataset_plots(self, dataset): dataset["Voltage [V]"], trace_names=["Time [s]", "Voltage [V]"], ) - pybop.plot_dataset(dataset, signal=["Voltage [V]"]) + pybop.plot_dataset(dataset) @pytest.fixture def fitting_problem(self, model, parameters, dataset): From b44bc2558d4e41c61b28eca834cbe316f7a143f3 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 8 Jul 2024 18:55:32 +0100 Subject: [PATCH 14/15] tests: add coverage for default UKF signal --- tests/unit/test_observer_unscented_kalman.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/unit/test_observer_unscented_kalman.py b/tests/unit/test_observer_unscented_kalman.py index ce60abbc0..0b5d3067b 100644 --- a/tests/unit/test_observer_unscented_kalman.py +++ b/tests/unit/test_observer_unscented_kalman.py @@ -156,3 +156,22 @@ def test_wrong_input_shapes(self, model, parameters): pybop.UnscentedKalmanFilterObserver( parameters, model, sigma0, process, measure, signal=signal ) + + @pytest.mark.unit + def test_without_signal(self): + model = pybop.lithium_ion.SPM() + parameters = pybop.Parameters( + pybop.Parameter( + "Negative electrode active material volume fraction", + prior=pybop.Gaussian(0.5, 0.05), + ) + ) + model.build(parameters=parameters) + n = model.n_states + sigma0 = np.diag([1e-4] * n) + process = np.diag([1e-4] * n) + measure = np.diag([1e-4]) + observer = pybop.UnscentedKalmanFilterObserver( + parameters, model, sigma0, process, measure + ) + assert observer.signal == ["Voltage [V]"] From bd3d59bb81c2a24253429034c169dab518d3cadc Mon Sep 17 00:00:00 2001 From: Brady Planden <55357039+BradyPlanden@users.noreply.github.com> Date: Thu, 11 Jul 2024 08:57:23 +0100 Subject: [PATCH 15/15] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 193aa2a68..5c365da61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,7 +86,7 @@ fix = true [tool.ruff.lint] select = [ "A", # flake8-builtins: Check for Python builtins being used as variables or parameters - "B", # flake8-bugbear: Find likely bugs and design problems in your program + "B", # flake8-bugbear: Find likely bugs and design problems "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes: Detect various errors by parsing the source file