Skip to content

Commit 627dc84

Browse files
Add ruff and codespell to pre-commit hooks + perform associated fixes
1 parent afc6588 commit 627dc84

18 files changed

+198
-78
lines changed

.pre-commit-config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,20 @@ repos:
2222
^docs
2323
)
2424
args: [--strict, --ignore-missing-imports, --no-warn-unused-ignores]
25+
26+
- repo: https://github.com/codespell-project/codespell
27+
rev: v2.1.0
28+
hooks:
29+
- id: codespell
30+
files: ^.*\.(py|c|h|md|rst|yml)$
31+
args: [
32+
"docs tests",
33+
"*.py *.rst *.md",
34+
]
35+
36+
- repo: https://github.com/astral-sh/ruff-pre-commit
37+
# Ruff version.
38+
rev: v0.0.289
39+
hooks:
40+
- id: ruff
41+
args: [--fix, --exit-non-zero-on-fix]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Iterative Ensemble Smoother
22
===========================
33

4+
[![Precommit: enabled](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
5+
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
46
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
57
[![docs](https://readthedocs.org/projects/iterative_ensemble_smoother/badge/?version=latest&style=plastic)](https://iterative-ensemble-smoother.readthedocs.io/)
68

docs/source/Oscillator.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
# language: python
1313
# name: python3
1414
# ---
15-
15+
# ruff: noqa: E402
1616
# %% [markdown]
1717
# # Estimating parameters of an anharmonic oscillator
1818
#
1919
# The anharnomic oscillator can be modelled by a non-linear partial differential
2020
# equation as described in section 6.3.4 of the book [Fundamentals of Algorithms
21-
# and Data Assimilation](https://www.amazon.com/Data-Assimilation-Methods-Algorithms-Applications/dp/1611974534) by Mark Asch, Marc Bocquet and Maëlle Nodet.
21+
# and Data Assimilation](https://www.amazon.com/Data-Assimilation-Methods-Algorithms-Applications/dp/1611974534)
22+
# by Mark Asch, Marc Bocquet and Maëlle Nodet.
2223
#
2324
# -------------
2425
#
@@ -53,11 +54,11 @@
5354
#
5455
#
5556

56-
# %%
57-
from matplotlib import pyplot as plt
57+
#%%
5858
import numpy as np
59+
from matplotlib import pyplot as plt
5960
from scipy import stats
60-
from scipy.special import erf
61+
6162
import iterative_ensemble_smoother as ies
6263

6364
rng = np.random.default_rng(12345)
@@ -168,7 +169,7 @@ def forward_model(A, response_x_axis):
168169
# The trick is to sample $x \sim \mathcal{N}(0, 1)$,
169170
# then define a function $f$ that maps from standard normal to the
170171
# exponential distribution.
171-
# This funciton can be constructed by first mapping from standard normal to
172+
# This function can be constructed by first mapping from standard normal to
172173
# the interval $[0, 1)$ using the CDF, then mapping to exponential using the
173174
# quantile function (inverse CDF) of the exponential distribution.
174175
#

docs/source/Polynomial.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@
1212
# language: python
1313
# name: python3
1414
# ---
15+
# ruff: noqa: E402
16+
# ruff: noqa: E501
1517

1618
# %% [markdown]
1719
# # Fitting a polynomial with Gaussian priors
1820
#
19-
# We fit a simple polynomial with Gaussian priors, which is an example of a Gauss-linear problem for which the results obtained using Subspace Iterative Ensemble Smoother (SIES) tend to those obtained using Ensemble Smoother (ES).
21+
# We fit a simple polynomial with Gaussian priors, which is an example of a Gauss-linear
22+
# problem for which the results obtained using Subspace Iterative Ensemble Smoother
23+
# (SIES) tend to those obtained using Ensemble Smoother (ES).
2024
# This notebook illustrated this property.
2125
# %%
26+
import itertools
27+
2228
import numpy as np
2329
import pandas as pd
24-
import itertools
2530

2631
np.set_printoptions(suppress=True)
2732
rng = np.random.default_rng(12345)
@@ -32,9 +37,8 @@
3237

3338
plt.rcParams["figure.figsize"] = (6, 6)
3439
plt.rcParams.update({"font.size": 10})
35-
from ipywidgets import interact
36-
import ipywidgets as widgets
37-
40+
from ipywidgets import interact # noqa # isort:skip
41+
import ipywidgets as widgets # noqa # isort:skip
3842
from p_tqdm import p_map
3943

4044
import iterative_ensemble_smoother as ies
@@ -112,7 +116,7 @@ def poly(a, b, c, x):
112116
coeff_b,
113117
coeff_c,
114118
[np.arange(max(x_observations) + 1)] * ensemble_size,
115-
desc=f"Running forward model.",
119+
desc="Running forward model.",
116120
)
117121

118122
# %% [markdown]
@@ -228,7 +232,7 @@ def plot_posterior(ax, posterior, method):
228232
)
229233

230234
# Plot the posterior
231-
ax2.set_title(f"ES ert posterior")
235+
ax2.set_title("ES ert posterior")
232236
ax2.plot(x_plot, poly(a_t, b_t, c_t, x_plot), zorder=10, lw=4, color="black")
233237
for parameter_posterior in X_ES_ert.T:
234238
ax2.plot(

pyproject.toml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,56 @@ write_to = "src/iterative_ensemble_smoother/_version.py"
7575
testpaths = [
7676
"tests"
7777
]
78+
79+
[tool.ruff]
80+
select = [
81+
# Pyflakes
82+
"F",
83+
# Pycodestyle
84+
"E",
85+
"W",
86+
# isort
87+
"I"
88+
]
89+
src = ["src", "tests", "docs"]
90+
91+
# Allow autofix for all enabled rules (when `--fix`) is provided.
92+
fixable = ["A", "B", "C", "D", "E", "F", "I"]
93+
unfixable = []
94+
95+
# Exclude a variety of commonly ignored directories.
96+
exclude = [
97+
".bzr",
98+
".direnv",
99+
".eggs",
100+
".git",
101+
".hg",
102+
".mypy_cache",
103+
".nox",
104+
".pants.d",
105+
".pytype",
106+
".ruff_cache",
107+
".svn",
108+
".tox",
109+
".venv",
110+
"__pypackages__",
111+
"_build",
112+
"buck-out",
113+
"build",
114+
"dist",
115+
"node_modules",
116+
"venv",
117+
]
118+
119+
# Same as Black.
120+
line-length = 88
121+
122+
# Allow unused variables when underscore-prefixed.
123+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
124+
125+
# Assume Python 3.10.
126+
target-version = "py310"
127+
128+
[tool.ruff.mccabe]
129+
# Unlike Flake8, default to a complexity level of 10.
130+
max-complexity = 10

setup.cfg

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
[flake8]
2-
ignore = E302, W503, E501, E741, E203, F405
3-
max-line-length = 88
1+
[codespell]
2+
skip = *.pyc,*.gif,*.png,*.jpg,*.ply, ./bibliography.bib,*.ipynb
3+
ignore-words-list = lod,byteorder,flem,parm,doubleclick,revered,inout,fro,nd,sies,hist,ans
4+
quiet-level = 3
5+
6+
[pylint.LOGGING]
7+
# Format style used to check logging format string. `old` means using %
8+
# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings.
9+
logging-format-style=fstr

src/iterative_ensemble_smoother/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
except ImportError:
1010
__version__ = "unknown version"
1111
version_tuple = (0, 0, "unknown version", "unknown commit")
12+
1213
from iterative_ensemble_smoother._iterative_ensemble_smoother import ES, SIES
1314
from iterative_ensemble_smoother.esmda import ESMDA
1415

src/iterative_ensemble_smoother/_iterative_ensemble_smoother.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
from __future__ import annotations
2-
from typing import Optional, TYPE_CHECKING, Callable
2+
3+
from typing import TYPE_CHECKING, Callable, Optional
34

45
import numpy as np
56

67
if TYPE_CHECKING:
78
import numpy.typing as npt
89

10+
from iterative_ensemble_smoother.ies import create_coefficient_matrix
911
from iterative_ensemble_smoother.utils import (
1012
_validate_inputs,
1113
covariance_to_correlation,
12-
steplength_exponential,
1314
response_projection,
15+
steplength_exponential,
1416
)
1517

16-
from iterative_ensemble_smoother.ies import create_coefficient_matrix
17-
1818

1919
class SIES:
2020
"""
2121
Initialize a Subspace Iterative Ensemble Smoother (SIES) instance.
2222
2323
This is an implementation of the algorithm described in the paper:
24-
Efficient Implementation of an Iterative Ensemble Smoother for Data Assimilation and Reservoir History Matching
25-
written by Evensen et al (2019), URL: https://www.frontiersin.org/articles/10.3389/fams.2019.00047/full
24+
Efficient Implementation of an Iterative Ensemble Smoother for Data Assimilation
25+
and Reservoir History Matching written by Evensen et al (2019),
26+
URL: https://www.frontiersin.org/articles/10.3389/fams.2019.00047/full
2627
2728
The default step length is described in equation (49) in the paper
2829
Formulating the history matching problem with consistent error statistics
29-
written by Geir Evensen (2021), URL: https://link.springer.com/article/10.1007/s10596-021-10032-7
30+
written by Geir Evensen (2021),
31+
URL: https://link.springer.com/article/10.1007/s10596-021-10032-7
3032
3133
Parameters
3234
----------
@@ -110,7 +112,8 @@ def fit(
110112
Parameters
111113
----------
112114
response_ensemble : npt.NDArray[np.double]
113-
A 2D array of reponses from the model g(X) of shape (observations, ensemble_size).
115+
A 2D array of responses from the model g(X) of shape
116+
(observations, ensemble_size).
114117
This matrix is Y in Evensen (2019).
115118
observation_errors : npt.NDArray[np.double]
116119
Either a 1D array of standard deviations, or a 2D covariance matrix.
@@ -254,7 +257,8 @@ def fit(
254257

255258
if np.isnan(W).sum() != 0:
256259
raise ValueError(
257-
"Fit produces NaNs. Check your response matrix for outliers or use an inversion type with truncation."
260+
"Fit produces NaNs. Check your response matrix for outliers "
261+
"or use an inversion type with truncation."
258262
)
259263

260264
self.iteration += 1

src/iterative_ensemble_smoother/esmda.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
https://helper.ipam.ucla.edu/publications/oilws3/oilws3_14147.pdf
2222
2323
"""
24-
2524
import numbers
2625
from typing import Optional, Union
2726

@@ -106,16 +105,19 @@ def __init__(
106105
isinstance(seed, (int, np.random._generator.Generator)) or seed is None
107106
):
108107
raise TypeError(
109-
"Argument `seed` must be an integer or numpy.random._generator.Generator."
108+
"Argument `seed` must be an integer "
109+
"or numpy.random._generator.Generator."
110110
)
111111

112112
if not isinstance(inversion, str):
113113
raise TypeError(
114-
f"Argument `inversion` must be a string in {tuple(self._inversion_methods.keys())}"
114+
"Argument `inversion` must be a string in "
115+
f"{tuple(self._inversion_methods.keys())}"
115116
)
116117
if inversion not in self._inversion_methods.keys():
117118
raise ValueError(
118-
f"Argument `inversion` must be a string in {tuple(self._inversion_methods.keys())}"
119+
"Argument `inversion` must be a string in "
120+
f"{tuple(self._inversion_methods.keys())}"
119121
)
120122

121123
# Store data

src/iterative_ensemble_smoother/esmda_inversion.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,10 @@ def singular_values_to_keep(
135135
#
136136
# C_MD @ inv(C_DD + alpha * C_D) @ (D - Y)
137137
#
138-
# where C_MD = empirical_cross_covariance(X, Y) = center(X) @ center(Y).T / (X.shape[1] - 1)
139-
# C_DD = empirical_cross_covariance(Y, Y) = center(Y) @ center(Y).T / (Y.shape[1] - 1)
138+
# where C_MD = empirical_cross_covariance(X, Y) =
139+
# center(X) @ center(Y).T / (X.shape[1] - 1)
140+
# C_DD = empirical_cross_covariance(Y, Y) =
141+
# center(Y) @ center(Y).T / (Y.shape[1] - 1)
140142
#
141143
# The methods can be classified as
142144
# - exact : with truncation=1.0, these methods compute the exact solution
@@ -312,7 +314,9 @@ def inversion_exact_rescaled(
312314
X_shift = (X - np.mean(X, axis=1, keepdims=True)) / (N_e - 1)
313315
Y_shift = Y - np.mean(Y, axis=1, keepdims=True)
314316

315-
return np.linalg.multi_dot([X_shift, Y_shift.T, term / s_r, term.T, (D - Y)]) # type: ignore
317+
return np.linalg.multi_dot( # type: ignore
318+
[X_shift, Y_shift.T, term / s_r, term.T, (D - Y)]
319+
)
316320

317321

318322
def inversion_exact_subspace_woodbury(
@@ -339,7 +343,8 @@ def inversion_exact_subspace_woodbury(
339343
340344
"""
341345

342-
# Woodbury: (A + U @ U.T)^-1 = A^-1 - A^-1 @ U @ (1 + U.T @ A^-1 @ U )^-1 @ U.T @ A^-1
346+
# Woodbury:
347+
# (A + U @ U.T)^-1 = A^-1 - A^-1 @ U @ (1 + U.T @ A^-1 @ U )^-1 @ U.T @ A^-1
343348

344349
# Compute D_delta. N_n = number of outputs, N_e = number of ensemble members
345350
N_n, N_e = Y.shape
@@ -364,7 +369,9 @@ def inversion_exact_subspace_woodbury(
364369

365370
# Compute the woodbury inversion, then return
366371
inverted = C_D_inv - np.linalg.multi_dot([term, sp.linalg.inv(center), term.T])
367-
return np.linalg.multi_dot([X_shift, D_delta.T, inverted, (D - Y)]) # type: ignore
372+
return np.linalg.multi_dot( # type: ignore
373+
[X_shift, D_delta.T, inverted, (D - Y)]
374+
)
368375

369376
# A diagonal covariance matrix was given as a 1D array.
370377
# Same computation as above, but exploit the diagonal structure
@@ -376,7 +383,9 @@ def inversion_exact_subspace_woodbury(
376383
inverted = np.diag(C_D_inv) - np.linalg.multi_dot(
377384
[UT_D.T, sp.linalg.inv(center), UT_D]
378385
)
379-
return np.linalg.multi_dot([X_shift, D_delta.T, inverted, (D - Y)]) # type: ignore
386+
return np.linalg.multi_dot( # type: ignore
387+
[X_shift, D_delta.T, inverted, (D - Y)]
388+
)
380389

381390

382391
def inversion_subspace(
@@ -461,7 +470,9 @@ def inversion_subspace(
461470

462471
# Compute C_MD = center(X) @ center(Y).T / (num_ensemble - 1)
463472
X_shift = X - np.mean(X, axis=1, keepdims=True)
464-
return np.linalg.multi_dot([X_shift, D_delta.T, (term / (1 + T)), term.T, (D - Y)]) # type: ignore
473+
return np.linalg.multi_dot( # type: ignore
474+
[X_shift, D_delta.T, (term / (1 + T)), term.T, (D - Y)]
475+
)
465476

466477

467478
def inversion_rescaled_subspace(
@@ -520,7 +531,9 @@ def inversion_rescaled_subspace(
520531

521532
# Compute C_MD
522533
X_shift = X - np.mean(X, axis=1, keepdims=True)
523-
return np.linalg.multi_dot([X_shift, D_delta.T, (term * diag), term.T, (D - Y)]) # type: ignore
534+
return np.linalg.multi_dot( # type: ignore
535+
[X_shift, D_delta.T, (term * diag), term.T, (D - Y)]
536+
)
524537

525538

526539
if __name__ == "__main__":

0 commit comments

Comments
 (0)