Skip to content

Add parametric nowcasting and calibrated stochastic rainfall generation for v2 - #604

Open
alanseed wants to merge 9 commits into
pySTEPS:mainfrom
alanseed:parametric-nowcasting-v2
Open

alanseed wants to merge 9 commits into
pySTEPS:mainfrom
alanseed:parametric-nowcasting-v2

Conversation

@alanseed

@alanseed alanseed commented Nov 4, 2026

Copy link
Copy Markdown

Adds parametric ensemble nowcasting from three consecutive rain-rate observations, together with calibrated stochastic rainfall generation and marginal, spectral, and temporal estimators. This ports the contribution in #578 to the v2 main branch and its xarray dataset interface.

The forecast derives grid spacing and cadence from coordinates, accepts supplied motion or estimates Lucas–Kanade motion internally, and returns rain rates with ensemble identifiers, valid times, forecast reference time, and geographic metadata. It preserves the NumPy numerical engine, independent member random streams, temporal-only fallback, and the distinction between missing and dry input. Callbacks remain arrays and support generation without retaining the full ensemble.

The synthetic and Swiss examples use the dataset interface. The Swiss example includes lossless native NetCDF export and a GIF of three QPE observations followed by the first member's six forecasts.

Validation:

  • Full suite on Python 3.13: 987 passed, 1 skipped. The run used a writable temporary working directory and a test configuration with the packaged FMI GeoTIFF filename/importer settings.
  • The 253 focused parametric, ingestion, conversion, spatial-processing, and exporter tests also passed.
  • Saved reference forecast arrays and existing numerical tolerances are unchanged.
  • Three saved paper fields passed estimator comparison against the source implementation.
  • Three paper histories, each with two seeds, two members, and three leads, passed source comparisons. Fixed-parameter evolution was identical; maximum coupled forecast difference was below 0.000085 mm/h.
  • The actual Swiss run produced 20 members by 6 leads on a 150 by 150 grid at 2 km. NetCDF reopening preserved values, coordinates, timestamps, and attributes exactly.
  • The nine GIF frames were checked against their source fields, and the forecast was identical to the prior validated export.

Scope: the first interface supports complete regular grids with square pixels and integer lead counts. These checks establish implementation and serialization behaviour; they do not establish Swiss calibration or forecast skill. CF support for additional projections, including UTM and Swiss somerc, remains separate. Existing projection text and available latitude/longitude coordinates are preserved.

Cross-platform and Python 3.11 validation will run in upstream CI.

@alanseed
alanseed requested review from RubenImhoff and ladc November 4, 2026 23:39

@RubenImhoff RubenImhoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @alanseed, super great work! Still working on fully understanding all the steps that take place in this parametric approach (also to understand how to use and adapt this in the future). I have not given it a full review yet, but added some notes and suggestions based on my own quick reading through it to understand the method.
It may be good if @ladc, @dnerini and perhaps @pulkkins also have a look at it. :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very happy with all the documentation attached! This kind of documentation is not present / not the same for the other pysteps modules. How should we proceed? Does it make sense to merge this documentation into one or two docs in a sub folder under either pysteps_reference or user_guide? @dnerini and @ladc, what is your take on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that there is quite some documentation already - so then the question remains what we want to do with the documentation under proposals.

@@ -7,5 +7,6 @@ Implementation of deterministic and ensemble nowcasting methods.

.. automodule:: pysteps.noise.interface
.. automodule:: pysteps.noise.fftgenerators

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. automodule:: pysteps.noise.fftgenerators

@@ -7,5 +7,6 @@ Implementation of deterministic and ensemble nowcasting methods.

.. automodule:: pysteps.noise.interface

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. automodule:: pysteps.noise.interface
.. automodule:: pysteps.noise.fftgenerators
.. automodule:: pysteps.noise.interface

Good reason to directly make this alphabetical. :)


.. automodule:: pysteps.noise.interface
.. automodule:: pysteps.noise.fftgenerators
.. automodule:: pysteps.noise.parametric

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. automodule:: pysteps.noise.parametric

.. automodule:: pysteps.noise.fftgenerators
.. automodule:: pysteps.noise.parametric
.. automodule:: pysteps.noise.motion
.. automodule:: pysteps.noise.utils

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. automodule:: pysteps.noise.utils
.. automodule:: pysteps.noise.parametric
.. automodule:: pysteps.noise.utils

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! It might be good to add some docstrings to different parts in the functions to ensure all steps are clear for future developers and maintainers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also holds for the code in the functions under ./nowcasts

The time step of the output is taken from the inputs.
The ``parametric`` method requires exactly three complete, untransformed
rain-rate observations in mm/h on a regular grid with square pixels. Pixel
size and cadence are derived from coordinates; the time stepsize is in seconds.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size and cadence are derived from coordinates; the time stepsize is in seconds.
size and time step are derived from coordinates; the time step is in seconds.

or (ens_number, time, y, x) for ensemble methods. Method-specific forecast
functions document their input history and optional settings.

The output depends on the type of the method.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to still replace this with what the output is (an xarray DataSet)

return selected, audit


def _advance(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to add some docstrings to the function.

from ._cascade_advection import advect_field, prepare_advection_coordinates

# Evaluated nowcast_v2 settings, not the later plotting-only timescale fit.
_ESTIMATION_DEFAULTS = dict(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these all default settings that the user can change? And if so, does this happen in the main calling function? If that is the case, I would prefer having the defaults set in the function (as default) and then these dictionaries can be filled with the variable value (either the default or the user-assigned value).

@RubenImhoff RubenImhoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @alanseed, super great work! Still working on fully understanding all the steps that take place in this parametric approach (also to understand how to use and adapt this in the future). I have not given it a full review yet, but added some notes and suggestions based on my own quick reading through it to understand the method.
It may be good if @ladc, @dnerini and perhaps @pulkkins also have a look at it. :)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants