Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
8b92e71
import directly to xarray and rewrite utils_transformation to work wi…
mats-knmi Jul 17, 2024
23825dd
convert to_rainrate function also
mats-knmi Jul 17, 2024
9952bd3
Rewrite more code to xarray
mats-knmi Jul 18, 2024
904bce8
conversion.py works on the xarray datamodel
mats-knmi Jul 18, 2024
b3aa009
fix converters.py
mats-knmi Jul 18, 2024
016d28f
make dimension.py xarray compatible (#397)
mats-knmi Aug 12, 2024
e7c081c
make all nowcast methods xarray compatible (#414)
mats-knmi Sep 2, 2024
c2ae9db
Added member and time dimension (#432)
gjm174 Sep 23, 2024
9ea07fc
Initial commit to branch
sidekock Oct 8, 2024
53a98a9
Working on _apply_noise_and_ar_model method
sidekock Oct 8, 2024
0c5185f
Only update function needs to be added
sidekock Oct 8, 2024
46bc44a
Fully refactored code
sidekock Oct 9, 2024
eb33c06
Possible solution for errors solved
sidekock Oct 9, 2024
a1ce4bc
Fixed small bug in _nowcast_main
sidekock Oct 9, 2024
b002354
Name changes from feedback Ruben
sidekock Oct 22, 2024
e105a24
Name changes from feedback Ruben v2
sidekock Oct 22, 2024
b6c47af
Name changes from feedback Ruben v3
sidekock Oct 22, 2024
5aaf24d
Added config dataclass to steps nowcast
sidekock Oct 31, 2024
fa9a1ef
Added config dataclass to steps nowcast, v2
sidekock Oct 31, 2024
3da1696
Added config dataclass to steps nowcast, v3
sidekock Oct 31, 2024
8c7982c
Added config dataclass to steps nowcast, v4
sidekock Oct 31, 2024
aa26517
Added config dataclass to steps nowcast, fixed some assignment issues…
sidekock Oct 31, 2024
ff2e2be
Fixed num_ensemble_workers bug
sidekock Oct 31, 2024
2543683
Added params and state dataclasses
sidekock Oct 31, 2024
37739de
Implemented a reset of the states and params to allow for multiple fo…
sidekock Oct 31, 2024
f123bde
Removed some redundant fields
sidekock Oct 31, 2024
1a71e61
Update pysteps/nowcasts/steps.py
sidekock Nov 4, 2024
db953fb
Added suggested changed by Mats regarding __ and typing
sidekock Nov 4, 2024
eff9248
Possible fix for static code analysis
sidekock Nov 4, 2024
663a9a2
Added the needed documentation to the class
sidekock Nov 4, 2024
3226042
Merge remote-tracking branch 'origin/refactoring_steps' into xarray/main
mats-knmi Nov 6, 2024
2066f14
Refactored all names in the steps blending code from old to new
sidekock Nov 18, 2024
72d0fbc
Made some name changes but test still do not pass
sidekock Nov 18, 2024
1ce563e
Fixed naming changes, now the tests pass
sidekock Nov 18, 2024
fbe551b
Built the rough scaffolding for the blending class
sidekock Nov 18, 2024
46a93e5
Refactored untill no rain case
sidekock Nov 27, 2024
1eede39
Added code to estimation of ar parameters of radar
sidekock Nov 28, 2024
a18f1f6
Next go, start with forecast loop #7
sidekock Nov 29, 2024
8d16c11
Added some uniformity between nowcast and blending steps. Now at # 8.…
sidekock Dec 2, 2024
88df97d
Small changes since prev commit
sidekock Dec 2, 2024
7ee0020
All code is tranfered. Last part of the main loop needs to be refactored
sidekock Dec 2, 2024
f387981
Everything is refactored, no test ran as of yet
sidekock Dec 5, 2024
760c185
Old forecast function is updated to fit newly refactored code
sidekock Dec 5, 2024
8d8905a
Removed old code which is no longer used
sidekock Dec 5, 2024
d6249f5
6 more tests that fail
sidekock Dec 5, 2024
38702b3
All tests pass, still need to fix TODOs
sidekock Dec 5, 2024
5ff1713
Updated gitignore
sidekock Dec 5, 2024
d999501
Cleanup of params and state dataclasses, next step: better typing
sidekock Dec 6, 2024
ed20ecc
Cleanup of params and state dataclasses, now all tests pass
sidekock Dec 6, 2024
701e726
Added correct typing to all parts of params and state
sidekock Dec 6, 2024
b9de511
Ready for pull request
sidekock Dec 6, 2024
38ed195
Made changes for Codacy review
sidekock Dec 6, 2024
32b656f
Added aditional tests which currently fail in master branch
sidekock Dec 16, 2024
4fe9f78
Update .gitignore
sidekock Dec 16, 2024
b31d55c
Used the __zero_precip_time in __zero_precipitation_forecast()
sidekock Dec 16, 2024
cc02593
Changed typing hints to python 3.10+ version
sidekock Dec 16, 2024
4e4a148
Added comments back to the State dataclass
sidekock Dec 16, 2024
0f4e037
Changed the self.__state.velocity_perturbations = [] to self.__params…
sidekock Dec 17, 2024
9f413aa
Added code changes as suggested by Ruben, comments and documentation …
sidekock Dec 18, 2024
c72d953
Added frozen functionality to dataclasses, removed reset_state and fi…
sidekock Dec 19, 2024
00f057b
Added frozen dataclass to nowcast
sidekock Dec 19, 2024
1b82512
The needed checks are done for this TODO so it can be removed
sidekock Dec 19, 2024
47ab6c3
Use the seed in all rng in blending code (#449)
mats-knmi Jan 2, 2025
48187c4
Removed deepcopy of worker_state. The state is now accessable to all …
sidekock Jan 3, 2025
9b216a7
Update to probmatching comments to keep in track with main
sidekock Jan 8, 2025
4e2d4b7
Merge remote-tracking branch 'origin/master' into xarray/main
mats-knmi Jan 14, 2025
2ff8e2c
Merge remote-tracking branch 'origin/refactor-steps-in-blending-code'…
mats-knmi Jan 14, 2025
561e7ac
Fix for multithreading issue, this produces exactly the same results …
sidekock Jan 20, 2025
4fb784e
New commit for new pr
sidekock Jan 20, 2025
9f48c66
Merge remote-tracking branch 'origin/refactor-steps-in-blending-code'…
mats-knmi Jan 29, 2025
153d52a
modified advection_correction.py example which supports xarray
AVI68 Sep 11, 2025
c55b143
modified adevection_correction.py markdown
AVI68 Sep 11, 2025
365668e
modified plot_optical_flow.py
AVI68 Sep 11, 2025
b06de1d
modified probability_forecast.py example
AVI68 Sep 11, 2025
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@ venv.bak/

# Mac OS Stuff
.DS_Store

# Running local tests
/tmp
/pysteps/tests/tmp/

/pysteps_data/
/examples/data_download.py

1 change: 1 addition & 0 deletions ci/ci_test_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies:
- pillow
- pyproj
- scipy
- xarray
# Optional dependencies
- dask
- pyfftw
Expand Down
140 changes: 102 additions & 38 deletions examples/advection_correction.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
"""
Advection correction
====================
Advection correction (xarray version)
=====================================

This tutorial shows how to use the optical flow routines of pysteps to implement
the advection correction procedure described in Anagnostou and Krajewski (1999).

Advection correction is a temporal interpolation procedure that is often used
when estimating rainfall accumulations to correct for the shift of rainfall patterns
between consecutive radar rainfall maps. This shift becomes particularly
between consecutive radar rainfall maps. This shift becomes particularly
significant for long radar scanning cycles and in presence of fast moving
precipitation features.

.. note:: The code for the advection correction using pysteps was originally
written by `Daniel Wolfensberger <https://github.com/wolfidan>`_.

Ported to xarray: the workflow now uses an xr.Dataset with a DataArray
'precip_intensity' on dims ('time','y','x'), with projection info in attrs.
"""

from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
import xarray as xr

from scipy.ndimage import map_coordinates

from pysteps import io, motion, rcparams
from pysteps.utils import conversion, dimension
from pysteps.visualization import plot_precip_field
from scipy.ndimage import map_coordinates

################################################################################
# Read the radar input images
Expand Down Expand Up @@ -51,26 +55,42 @@
fn_ext = data_source["fn_ext"]
importer_name = data_source["importer"]
importer_kwargs = data_source["importer_kwargs"]
timestep = data_source["timestep"]

# Find the input files from the archive
fns = io.archive.find_by_date(
date, root_path, path_fmt, fn_pattern, fn_ext, timestep=5, num_next_files=35
)

# Read the radar composites
# Read as xarray Dataset (expects variable 'precip_intensity' and coords x,y,time)
importer = io.get_method(importer_name, "importer")
R, __, metadata = io.read_timeseries(fns, importer, **importer_kwargs)
precip_ds = io.read_timeseries(fns, importer, **importer_kwargs)

# Convert to mm/h
R, metadata = conversion.to_rainrate(R, metadata)
precip_ds = conversion.to_rainrate(precip_ds)

# Upscale to 2 km (simply to reduce the memory demand)
R, metadata = dimension.aggregate_fields_space(R, metadata, 2000)
# Upscale to 2 km (to reduce memory)
precip_ds = dimension.aggregate_fields_space(precip_ds, 2000)

# Keep only one frame every 10 minutes (i.e., every 2 timesteps)
# (to highlight the need for advection correction)
R = R[::2]
precip_ds = precip_ds.isel(time=slice(None, None, 2))

# Ensure precip_var is present
precip_ds.attrs.setdefault("precip_var", "precip_intensity")
precip_var = precip_ds.attrs["precip_var"]

# Convenience handle to the intensity DataArray (time, y, x)
R = precip_ds[precip_var]

# Build geodata for plotting from xarray metadata/coordinates
geodata = {
"projection": precip_ds.attrs.get("projection", None),
"x1": float(R.x.values[0]),
"x2": float(R.x.values[-1]),
"y1": float(R.y.values[0]),
"y2": float(R.y.values[-1]),
"yorigin": "lower",
}

################################################################################
# Advection correction
Expand All @@ -84,43 +104,85 @@
# going to use the Lucas-Kanade optical flow routine available in pysteps.


def advection_correction(R, T=5, t=1):
def advection_correction_xr(pair_da: xr.DataArray, T: int = 10, t: int = 1) -> xr.DataArray:
"""
R = np.array([qpe_previous, qpe_current])
T = time between two observations (5 min)
t = interpolation timestep (1 min)
Advection correction for a pair of successive frames using optical flow motion.

Parameters
----------
pair_da : xr.DataArray
'precip_intensity' for two consecutive times with dims ('time','y','x').
pair_da.sizes['time'] must be 2.
T : int
Minutes between the two observations (here 10 after subsampling).
t : int
Interpolation timestep in minutes (1 minute).

Returns
-------
xr.DataArray
Time-averaged field from the temporally interpolated sequence between
the two frames (same dims as a single frame: ('y','x')).
"""


assert pair_da.sizes["time"] == 2, "pair_da must have exactly two time steps"

# Evaluate advection
oflow_method = motion.get_method("LK")
fd_kwargs = {"buffer_mask": 10} # avoid edge effects
V = oflow_method(np.log(R), fd_kwargs=fd_kwargs)
# Optical flow on log-intensity (avoid log(0))
eps = 1e-6
oflow = motion.get_method("LK")
fd_kwargs = {"buffer_mask": 10}

# Perform temporal interpolation
Rd = np.zeros((R[0].shape))
x, y = np.meshgrid(
np.arange(R[0].shape[1], dtype=float), np.arange(R[0].shape[0], dtype=float)
)
pair_np = pair_da.values # shape (2, y, x) — kept for interpolation step

# Build a two-frame Dataset that LK expects and set precip_var attr
pair_ds = pair_da.to_dataset(name=precip_var)
# carry over global attrs to satisfy decorators
pair_ds.attrs.update(precip_ds.attrs)
pair_ds.attrs.setdefault("precip_var", precip_var)

# log-transform
pair_ds[precip_var] = np.log(pair_ds[precip_var] + eps)

# Run optical flow motion algorithm (returns velocities as variables on the same grid)
V_ds = oflow(pair_ds)
u = V_ds["velocity_x"].values # x-component
v = V_ds["velocity_y"].values # y-component

# Temporal interpolation loop
ny, nx = pair_np.shape[-2], pair_np.shape[-1]
y, x = np.meshgrid(np.arange(ny, dtype=float), np.arange(nx, dtype=float), indexing="ij")

Rd = np.zeros((ny, nx), dtype=float)
for i in range(t, T + t, t):
pos1 = (y - i / T * V[1], x - i / T * V[0])
R1 = map_coordinates(R[0], pos1, order=1)
# Backward sample from the first frame
pos1 = (y - i / T * v, x - i / T * u)
R1 = map_coordinates(pair_np[0], pos1, order=1, mode="nearest")

pos2 = (y + (T - i) / T * V[1], x + (T - i) / T * V[0])
R2 = map_coordinates(R[1], pos2, order=1)
# Forward sample from the second frame
pos2 = (y + (T - i) / T * v, x + (T - i) / T * u)
R2 = map_coordinates(pair_np[1], pos2, order=1, mode="nearest")

Rd += (T - i) * R1 + i * R2

return t / T**2 * Rd
Rd = (t / (T ** 2)) * Rd # time-weighted mean over the interval

# Wrap back into a DataArray with original coords
return xr.DataArray(
Rd, dims=("y", "x"), coords={"y": pair_da.y, "x": pair_da.x}, attrs=pair_da.attrs
)

###############################################################################
# Finally, we apply the advection correction to the whole sequence of radar
# images and produce the rainfall accumulation map.

R_ac = R[0].copy()
for i in range(R.shape[0] - 1):
R_ac += advection_correction(R[i : (i + 2)], T=10, t=1)
R_ac /= R.shape[0]
R_mean = R.mean(dim="time")
R_ac = R.isel(time=0).copy(deep=True)
for k in range(R.sizes["time"] - 1):
pair = R.isel(time=slice(k, k + 2))
R_ac = R_ac + advection_correction_xr(pair, T=10, t=1)

R_ac = R_ac / R.sizes["time"]

###############################################################################
# Results
Expand All @@ -134,10 +196,10 @@ def advection_correction(R, T=5, t=1):
# rainfall accumulation map.

plt.figure(figsize=(9, 4))
plt.subplot(121)
plot_precip_field(R.mean(axis=0), title="3-h rainfall accumulation")
plt.subplot(122)
plot_precip_field(R_ac, title="Same with advection correction")
plt.subplot(1, 2, 1)
plot_precip_field(R_mean, geodata=geodata, title="3-h rainfall accumulation")
plt.subplot(1, 2, 2)
plot_precip_field(R_ac, geodata=geodata, title="Same with advection correction")
plt.tight_layout()
plt.show()

Expand All @@ -149,3 +211,5 @@ def advection_correction(R, T=5, t=1):
# Estimation. Part I: Algorithm Formulation." Journal of Atmospheric and
# Oceanic Technology 16: 189–97.
# https://doi.org/10.1175/1520-0426(1999)016<0189:RTRREP>2.0.CO;2

# sphinx_gallery_thumbnail_number = 2
6 changes: 3 additions & 3 deletions examples/my_first_nowcast.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"display_name": "pysteps_dev",
"language": "python",
"name": "python3"
},
Expand All @@ -819,7 +819,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.2"
"version": "3.13.5"
},
"pycharm": {
"stem_cell": {
Expand All @@ -833,4 +833,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
Loading