Skip to content

Add forecast classes#1115

Open
peanutfun wants to merge 47 commits intodevelopfrom
forecast-class
Open

Add forecast classes#1115
peanutfun wants to merge 47 commits intodevelopfrom
forecast-class

Conversation

@peanutfun
Copy link
Copy Markdown
Member

@peanutfun peanutfun commented Nov 21, 2025

Changes proposed in this PR:

  • Add forecast variants for Hazard and Impact.
  • Adapt ImpactCalc to return ImpactForecast when needed.
  • Add tests.
  • Add tutorial for forecast data.

This PR resolves a major portion of #1118. Note that sub-tasks have been marked as completed when merged into this branch.

PR Author Checklist

PR Reviewer Checklist

peanutfun and others added 21 commits November 21, 2025 18:23
* define base class
* add init test
* Fix #1127

---------

Co-authored-by: Valentin Gebhart <valentin.gebhart@usys.ethz.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add ImpactForecast
* Add unit tests
* Create hazardForecast base class
* Update datetime to timedelta for lead_time
* Add tests

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* change leadtime to timedelta
* adapt impactforecast docstrings and tests
* Remove stray comment

---------

Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add HazardForecast.select test

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add test for hazardForecast.concat
* Skip HazardForecast.concat test

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add test for ImpactForecast.select
* minor test modification

---------

Co-authored-by: Evelyn Mühlhofer <evelyn.muehlhofer@meteoswiss.ch>
* Add test stub for ImpactForecast.concat
* Use correct command for skipping a test
* Fix merge issue
* Add data shape check for HazardForecast, ImpactForecast
Adapt Hazard from_hdf5 and write_hdf5 to forecast attributes
* Add extended select impact forecast test
* Add extended hazard forecast test

---------

Co-authored-by: Chahan Kropf <chahan.kropf@posteo.com>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Return impactForecast object in _return_impact
* Return impactForecast object in _return_empty
* Add full  impactcalc test for impactForecast
* Raise value error when computing impact with impact forecast without saving impact matrix
* Cosmetics: Improve error message, move test to own class
* Write nans for eai_exp and aai_agg when forecast is used

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Chahan Kropf <chahan.kropf@posteo.com>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* add idx boolean selection for member and leadtime
* Add test for weird data types

---------

Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add ImpactForecast.select

---------

Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* add HazardForecast.select and extent test

---------

Co-authored-by: Eliane Kobler <ekobler@student.ethz.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
peanutfun and others added 8 commits December 10, 2025 14:32
* Add HazardForecast.concat
Add min/mean/max methods

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Chahan Kropf <chahan.kropf@posteo.com>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Make Impact.concat support ImpactForecast
* Add tests
* implement changes in Impact IO and ImpactForecast IO
* Update tests
* Fix small issue when reading H5 for ImpactForecast

---------

Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add quantile and median for hazard and impact forecast
* Review

---------

Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Chahan Kropf <chahan.kropf@posteo.com>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
* Add impact forecast tutorial
* Add 'dim' argument to reductions
* Update tests

---------

Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Co-authored-by: Evelyn Mühlhofer <evelyn.muehlhofer@meteoswiss.ch>
Co-authored-by: Chahan Kropf <chahan.kropf@posteo.com>
@peanutfun
Copy link
Copy Markdown
Member Author

So it had been reprojected? Does MCH store the data differently than OGD? I am just wondering because it might be important to highlight the caveats when importing datasets with varying CRS and coordinate specifications.

@Evelyn-M
Copy link
Copy Markdown
Collaborator

Evelyn-M commented Feb 5, 2026

I suppose so, yes. One is the MCH grid commonly used internally for national gridded forecast products, and the OGD data has a different grid and a much larger rectangular extent around Switzerland.

@chahank
Copy link
Copy Markdown
Member

chahank commented Feb 16, 2026

Can you please let me know once this is final for review?

Copy link
Copy Markdown
Collaborator

@ValentinGebhart ValentinGebhart left a comment

Choose a reason for hiding this comment

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

Looks very good to me, all comments are minor I think.

I have not yet reviewed the tutorial, will do a separate review for that when i have time.

Comment thread climada/util/forecast.py Outdated
Comment thread climada/util/forecast.py
Comment thread climada/util/test/test_forecast.py
Comment thread climada/util/forecast.py
return reduced_attrs


def reduce_unique_selection(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

on first reading I found the back on forth confusing. e.g. HazardForecast.min() calls reduce_unqiue_slection with again calls HazardForecast.min().

I see why this works, just to comment on readability

Comment thread climada/hazard/forecast.py Outdated
expected_n_events = (
forecast_netcdf_file["n_eps"] * forecast_netcdf_file["n_lead_time"]
)
npt.assert_array_equal(haz_fc.date, np.zeros(expected_n_events, dtype=int))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what about setting the date to the date of the reference time? Maybe it makes sense not to, just thinking

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See comment above

Comment thread climada/engine/impact_forecast.py
Comment thread climada/engine/test/test_impact_forecast.py Outdated
Comment thread climada/engine/test/test_impact_forecast.py
@pytest.fixture
def reduction_results_dim(self):
return {
"lead_time": {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think at_event should always return the sum of the impact matrix over the locations (i.e., axis=1), independent of if the eventdimension corresponds to lead_time or member.

In the test, this is tested for "member" but not for "lead_time". The values look like they would give the same sum(axis=1) and sum(axis=0). Maybe we could adapt a value such that we know the right axis is summed? If too compilated, maybe the test for "member" is enough.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't understand. at_event is always exactly np.sum(imp_mat, axis=1), both for member and for lead_time.

I think at_event should always return the sum of the impact matrix over the locations (i.e., axis=1), ...

I agree! The test checks this explicitly, in my perception?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

sorry, my comment was not clear. You are right, at_event should always be exactly np.sum(imp_mat, axis=1).

My point was that in the test of "lead_time", the test values seem to be chosen such that the impact matrix is always symmetric (off diagonals are the same), which is why np.sum(imp_mat, axis=1) is the same as np.sum(imp_mat, axis=0). So we actually do not test if the correct axis is summed here. However, we test this in the test of "member", so that should suffice. For me we can leave as is.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@ValentinGebhart I updated the test. The resulting matrices are now not symmetric anymore, making the test more clear.

@peanutfun
Copy link
Copy Markdown
Member Author

@chahank This is final for review 🙂

@chahank
Copy link
Copy Markdown
Member

chahank commented Mar 6, 2026

Excellent! I will try to do it swiftly.

"aai_agg": 14.4,
"unit": "USD",
"frequency_unit": "1/month",
"imp_mat": sparse.csr_matrix(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it worth to test for negative impact values too?

Comment thread doc/user-guide/climada_engine_ImpactForecast.ipynb
"haz_type": "TC",
"pool": None,
"intensity": sparse.csr_matrix(
[[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please include negative value intensity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The change to this file only wraps the previous dummy_hazard function into a fixture. I think changing the intensity values is out of scope, because changes here might have effects on all other hazard tests. We can pick this up in a discussion about the conftest.py fixtures, see #1278

@chahank
Copy link
Copy Markdown
Member

chahank commented Mar 26, 2026

Is there a plan already to work on the comments by @ValentinGebhart?

@chahank
Copy link
Copy Markdown
Member

chahank commented Mar 26, 2026

The tutorial is now titled only impact forecast, but it is mostly about hazard forecast. This is quite confusing. Can this maybe be split into two tutorials? Or give it a different name?

@chahank
Copy link
Copy Markdown
Member

chahank commented Mar 26, 2026

A few comments on the tutorial:

  • Can the tutorial of the impact forecast please add a section a the top that shows the whole pipeline within one cell for a fast introduction?

  • I think the cells with ?ImpactForecast and ?HazardForecast do not serve a clear purpose.

  • OVerall, the tutorial could need a bit of improvements in clarity. Some of the cells are just there without explanations.

  • There are a lot of of Logging that indicate something should be improved in the code I think. It is unclear why imp_fct_all_mem_1lt = impact_forecast.max(dim="lead_time") triggers many lines of info and warning logging. Or in some cell there is several repetitions of 2026-01-08 14:55:43,321 - climada.engine.impact - INFO - The eai_exp and aai_agg are computed for the selected subset of events WITHOUT modification of the frequencies..

Copy link
Copy Markdown
Member

@chahank chahank left a comment

Choose a reason for hiding this comment

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

Excellent addition to the CLIMADA platform!

Please find a few detailled comments by @ValentinGebhart to address, and some user friendliness requests for the tutorial.

@peanutfun
Copy link
Copy Markdown
Member Author

@chahank Thanks for the review!

Can the tutorial of the impact forecast please add a section a the top that shows the whole pipeline within one cell for a fast introduction?

I added that.

I think the cells with ?ImpactForecast and ?HazardForecast do not serve a clear purpose.

Removed the cells.

OVerall, the tutorial could need a bit of improvements in clarity. Some of the cells are just there without explanations.

Can you be more specific? What is missing apart from some "flavor text"?

There are a lot of of Logging that indicate something should be improved in the code I think. It is unclear why imp_fct_all_mem_1lt = impact_forecast.max(dim="lead_time") triggers many lines of info and warning logging. Or in some cell there is several repetitions of 2026-01-08 14:55:43,321 - climada.engine.impact - INFO - The eai_exp and aai_agg are computed for the selected subset of events WITHOUT modification of the frequencies..

I suppressed more warnings where it seemed appropriate. The particular warning you mention is now suppressed when computing an aggregate, but not when calling .select directly (where the warning originates from)

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.

6 participants