From 6c500699e2c691f75c3423213b96bbbe582ebf7e Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 17 Mar 2026 11:16:12 +0000 Subject: [PATCH 1/6] Adds violent rain diagnostic Fixes #1856 --- ...rain_presence_domain_mean_time_series.yaml | 38 +++++++++++++++++++ ...lent_rain_presence_spatial_difference.yaml | 37 ++++++++++++++++++ .../violent_rain_presence_spatial_plot.yaml | 33 ++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_domain_mean_time_series.yaml create mode 100644 src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml create mode 100644 src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_plot.yaml diff --git a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_domain_mean_time_series.yaml b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_domain_mean_time_series.yaml new file mode 100644 index 000000000..2ad1d5241 --- /dev/null +++ b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_domain_mean_time_series.yaml @@ -0,0 +1,38 @@ +category: Daily Weather +title: Violent rain presence domain mean timeseries +description: | + Generates a mask of whether violent rain is present in a grid box and provides a + timeseries representing the chance of violent rain anywhere within the domain. + Violent rain is defined in the Forecasting Handbook as rainfall exceeding 50 mm/h. + +steps: + - operator: read.read_cubes + file_paths: $INPUT_PATHS + model_names: $MODEL_NAME + constraint: + operator: constraints.combine_constraints + varname_constraint: + operator: constraints.generate_var_constraint + varname: 'surface_microphysical_rainfall_rate' + cell_methods_constraint: + operator: constraints.generate_cell_methods_constraint + cell_methods: [] + subarea_type: $SUBAREA_TYPE + subarea_extent: $SUBAREA_EXTENT + + - operator: filters.generate_mask + condition: 'ge' + value: 50.0 + + - operator: misc.rename_cube + name: "mask_for_violent_rainfall_rate" + + - operator: collapse.collapse + coordinate: [grid_latitude, grid_longitude] + method: MEAN + + # Plot the data. + - operator: plot.plot_line_series + + - operator: write.write_cube_to_nc + overwrite: True diff --git a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml new file mode 100644 index 000000000..49f4221c4 --- /dev/null +++ b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml @@ -0,0 +1,37 @@ +category: Daily Weather +title: "Violent rain presence\nDifference ($BASE_MODEL - $OTHER_MODEL)" +description: | + Generates a mask of whether violent rain is present in a grid box and provides a + the difference between two datasets. Values will be positive or negative one + depending on which model has violent rain present at that location if the other + does not. + Violent rain is defined in the Forecasting Handbook as rainfall exceeding 50 mm/h. + +steps: + - operator: read.read_cubes + file_paths: $INPUT_PATHS + model_names: [$BASE_MODEL, $OTHER_MODEL] + constraint: + operator: constraints.combine_constraints + varname_constraint: + operator: constraints.generate_var_constraint + varname: surface_microphysical_rainfall_rate + cell_methods_constraint: + operator: constraints.generate_cell_methods_constraint + cell_methods: [] + subarea_type: $SUBAREA_TYPE + subarea_extent: $SUBAREA_EXTENT + + - operator: filters.generate_mask + condition: 'ge' + value: 50.0 + + - operator: misc.rename_cube + name: "mask_for_violent_rainfall_rate" + + - operator: misc.difference + + - operator: plot.spatial_pcolormesh_plot + + - operator: write.write_cube_to_nc + overwrite: True diff --git a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_plot.yaml b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_plot.yaml new file mode 100644 index 000000000..74927bc76 --- /dev/null +++ b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_plot.yaml @@ -0,0 +1,33 @@ +category: Daily Weather +title: $MODEL_NAME Violent rain presence spatial plot +description: | + Generates a mask of whether violent rain is present in a grid box and provides a + spatial plot. A value of one implies violent rain is present; a value of zero + implies violent rain is not present. + Violent rain is defined in the Forecasting Handbook as rainfall exceeding 50 mm/h. +steps: + - operator: read.read_cubes + file_paths: $INPUT_PATHS + model_names: $MODEL_NAME + constraint: + operator: constraints.combine_constraints + varname_constraint: + operator: constraints.generate_var_constraint + varname: surface_microphysical_rainfall_rate + cell_methods_constraint: + operator: constraints.generate_cell_methods_constraint + cell_methods: [] + subarea_type: $SUBAREA_TYPE + subarea_extent: $SUBAREA_EXTENT + + - operator: filters.generate_mask + condition: 'ge' + value: 50.0 + + - operator: misc.rename_cube + name: "mask_for_violent_rainfall_rate" + + - operator: plot.spatial_pcolormesh_plot + + - operator: write.write_cube_to_nc + overwrite: True From df9cfeec09914acea34a131e85a40609c8c8b5be Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 17 Mar 2026 11:27:17 +0000 Subject: [PATCH 2/6] Adds loaders for violent rain diagnostics --- src/CSET/loaders/spatial_difference_field.py | 18 ++++++++++++++++++ src/CSET/loaders/spatial_field.py | 15 +++++++++++++++ src/CSET/loaders/timeseries.py | 13 +++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/CSET/loaders/spatial_difference_field.py b/src/CSET/loaders/spatial_difference_field.py index 1974f37ec..da4309ee1 100644 --- a/src/CSET/loaders/spatial_difference_field.py +++ b/src/CSET/loaders/spatial_difference_field.py @@ -192,6 +192,24 @@ def load(conf: Config): model_ids=[base_model["id"], model["id"]], aggregation=False, ) + + # Violent rain presence. + if conf.VIOLENT_RAIN_PRESENCE_SPATIAL_DIFFERENCE: + base_model = models[0] + for model in models[1:]: + yield RawRecipe( + recipe="violent_rain_presence_spatial_difference.yaml", + variables={ + "BASE_MODEL": base_model["name"], + "OTHER_MODEL": model["name"], + "SUBAREA_TYPE": conf.SUBAREA_TYPE if conf.SELECT_SUBAREA else None, + "SUBAREA_EXTENT": conf.SUBAREA_EXTENT + if conf.SELECT_SUBAREA + else None, + }, + model_ids=[base_model["id"], model["id"]], + aggregation=False, + ) # Heavy rain presence. if conf.HEAVY_RAIN_PRESENCE_SPATIAL_DIFFERENCE: diff --git a/src/CSET/loaders/spatial_field.py b/src/CSET/loaders/spatial_field.py index f0d77f4ad..b9d4a46ad 100644 --- a/src/CSET/loaders/spatial_field.py +++ b/src/CSET/loaders/spatial_field.py @@ -248,6 +248,21 @@ def load(conf: Config): }, aggregation=False, ) + + # Violent rain presence. + if conf.VIOLENT_RAIN_PRESENCE_SPATIAL_PLOT: + yield RawRecipe( + recipe="violent_rain_presence_spatial_plot.yaml", + model_ids=model["id"], + variables={ + "MODEL_NAME": model["name"], + "SUBAREA_TYPE": conf.SUBAREA_TYPE if conf.SELECT_SUBAREA else None, + "SUBAREA_EXTENT": conf.SUBAREA_EXTENT + if conf.SELECT_SUBAREA + else None, + }, + aggregation=False, + ) # Heavy rain presence. if conf.HEAVY_RAIN_PRESENCE_SPATIAL_PLOT: diff --git a/src/CSET/loaders/timeseries.py b/src/CSET/loaders/timeseries.py index b00a07ccc..7ce0277b4 100644 --- a/src/CSET/loaders/timeseries.py +++ b/src/CSET/loaders/timeseries.py @@ -250,6 +250,19 @@ def load(conf: Config): model_ids=[model["id"] for model in models], aggregation=False, ) + + # Violent rain presence + if conf.VIOLENT_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES: + yield RawRecipe( + recipe="violent_rain_presence_domain_mean_time_series.yaml", + variables={ + "MODEL_NAME": [model["name"] for model in models], + "SUBAREA_TYPE": conf.SUBAREA_TYPE if conf.SELECT_SUBAREA else None, + "SUBAREA_EXTENT": conf.SUBAREA_EXTENT if conf.SELECT_SUBAREA else None, + }, + model_ids=[model["id"] for model in models], + aggregation=False, + ) # Heavy rain presence if conf.HEAVY_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES: From af094f982042321f17f663005856d7e1d37d229b Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 17 Mar 2026 11:33:01 +0000 Subject: [PATCH 3/6] Adds violent rain diagnostics into the workflow --- .../meta/diagnostics/rose-meta.conf | 43 +++++++++++++++++++ .../cset_workflow/rose-suite.conf.example | 3 ++ 2 files changed, 46 insertions(+) diff --git a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf index 8c2d50bb0..6fe37541e 100644 --- a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf +++ b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf @@ -1089,6 +1089,20 @@ type=python_boolean compulsory=true sort-key=dw1c +[template variables=VIOLENT_RAIN_PRESENCE_SPATIAL_PLOT] +ns=Diagnostics/Derived/Daily +title=Violent rain presence: spatial plot +description=IMPACT-BASED DIAGNOSTIC. + Determines whether violent rain is located at a point. + Violent rain is defined as exceeding 50 mm/h. + Requires surface_microphysical_rainfall_rate. +help=This diagnostic identifies only whether there is violent rain or not, regardless + of the amount of rain that is falling. It can be particularly useful for + considering the model from the viewpoint of a customer. +type=python_boolean +compulsory=true +sort-key=dw1d + [template variables=MODERATE_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES] ns=Diagnostics/Derived/Daily title=Moderate rain presence: domain mean timeseries @@ -1119,6 +1133,21 @@ type=python_boolean compulsory=true sort-key=dw2c +[template variables=VIOLENT_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES] +ns=Diagnostics/Derived/Daily +title=Violent presence: domain mean timeseries +description=IMPACT-BASED DIAGNOSTIC. + Determines whether violent rain is located at a point and creates a + timeseries representing the chance of violent rain anywhere in the domain. + Violent rain is defined as exceeding 50 mm/h. + Requires surface_microphysical_rainfall_rate. +help=This diagnostic identifies only whether there is violent rain or not, regardless + of the amount of rain that is falling. It can be particularly useful for + considering the model from the viewpoint of a customer. +type=python_boolean +compulsory=true +sort-key=dw2d + [template variables=MODERATE_RAIN_PRESENCE_SPATIAL_DIFFERENCE] ns=Diagnostics/Derived/Daily title=Moderate rain presence: spatial difference @@ -1147,6 +1176,20 @@ type=python_boolean compulsory=true sort-key=dw3c +[template variables=VIOLENT_RAIN_PRESENCE_SPATIAL_DIFFERENCE] +ns=Diagnostics/Derived/Daily +title=Violent rain presence: spatial difference +description=IMPACT-BASED DIAGNOSTIC. + Determines the difference between two violent rain presence fields. + Violent rain is defined as 50 mm/h. + Requires surface_microphysical_rainfall_rate. +help=This diagnostic identifies only whether there is violent rain or not, regardless + of the amount of rain that is falling. It can be particularly useful for + considering the model from the viewpoint of a customer. +type=python_boolean +compulsory=true +sort-key=dw3d + [template variables=SFC_WIND_BEAUFORT_SCALE_SPATIAL] ns=Diagnostics/Derived/Daily title=Sfc wind (sustained) Beaufort Scale: spatial plot diff --git a/src/CSET/cset_workflow/rose-suite.conf.example b/src/CSET/cset_workflow/rose-suite.conf.example index a16d4c868..55a8e1bb2 100644 --- a/src/CSET/cset_workflow/rose-suite.conf.example +++ b/src/CSET/cset_workflow/rose-suite.conf.example @@ -190,6 +190,9 @@ TIMESERIES_SURFACE_FIELD_SEA_MASK=False !!USE_WMO_STATION_NUMBERS=False !!VERTICAL_COORDINATE_A=[] !!VERTICAL_COORDINATE_B=[] +VIOLENT_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES=False +VIOLENT_RAIN_PRESENCE_SPATIAL_DIFFERENCE=False +VIOLENT_RAIN_PRESENCE_SPATIAL_PLOT=False WEB_ADDR="" WEB_DIR="" !!WMO_BLOCK_STTN_NMBRS=[] From 66cd668b0638baa5e59dfe3c3f38b3b6840bcf9e Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 17 Mar 2026 13:56:46 +0000 Subject: [PATCH 4/6] Fixes pre-commit tests --- src/CSET/loaders/spatial_difference_field.py | 2 +- src/CSET/loaders/spatial_field.py | 2 +- src/CSET/loaders/timeseries.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CSET/loaders/spatial_difference_field.py b/src/CSET/loaders/spatial_difference_field.py index da4309ee1..9e7da79f1 100644 --- a/src/CSET/loaders/spatial_difference_field.py +++ b/src/CSET/loaders/spatial_difference_field.py @@ -192,7 +192,7 @@ def load(conf: Config): model_ids=[base_model["id"], model["id"]], aggregation=False, ) - + # Violent rain presence. if conf.VIOLENT_RAIN_PRESENCE_SPATIAL_DIFFERENCE: base_model = models[0] diff --git a/src/CSET/loaders/spatial_field.py b/src/CSET/loaders/spatial_field.py index b9d4a46ad..715cad8a9 100644 --- a/src/CSET/loaders/spatial_field.py +++ b/src/CSET/loaders/spatial_field.py @@ -248,7 +248,7 @@ def load(conf: Config): }, aggregation=False, ) - + # Violent rain presence. if conf.VIOLENT_RAIN_PRESENCE_SPATIAL_PLOT: yield RawRecipe( diff --git a/src/CSET/loaders/timeseries.py b/src/CSET/loaders/timeseries.py index 7ce0277b4..eced7fb7f 100644 --- a/src/CSET/loaders/timeseries.py +++ b/src/CSET/loaders/timeseries.py @@ -250,7 +250,7 @@ def load(conf: Config): model_ids=[model["id"] for model in models], aggregation=False, ) - + # Violent rain presence if conf.VIOLENT_RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES: yield RawRecipe( From 8fcf2af86219203015164f7963047d8dc13a53b0 Mon Sep 17 00:00:00 2001 From: David Flack <77390156+daflack@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:08:54 +0000 Subject: [PATCH 5/6] Update documentation in recipe Co-authored-by: Sylvia Bohnenstengel <62748926+Sylviabohnenstengel@users.noreply.github.com> --- .../daily_weather/violent_rain_presence_spatial_difference.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml index 49f4221c4..b614f1bf8 100644 --- a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml +++ b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml @@ -1,7 +1,7 @@ category: Daily Weather title: "Violent rain presence\nDifference ($BASE_MODEL - $OTHER_MODEL)" description: | - Generates a mask of whether violent rain is present in a grid box and provides a + Generates a mask of whether violent rain is present in a grid box and provides the difference between two datasets. Values will be positive or negative one depending on which model has violent rain present at that location if the other does not. From eb2e54a5fb098bf13a7a7a1bff162a0f126d074c Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 24 Mar 2026 16:15:36 +0000 Subject: [PATCH 6/6] Fixes PR checks --- .../daily_weather/violent_rain_presence_spatial_difference.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml index b614f1bf8..89a0089bd 100644 --- a/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml +++ b/src/CSET/recipes/derived_diagnostics/daily_weather/violent_rain_presence_spatial_difference.yaml @@ -1,7 +1,7 @@ category: Daily Weather title: "Violent rain presence\nDifference ($BASE_MODEL - $OTHER_MODEL)" description: | - Generates a mask of whether violent rain is present in a grid box and provides + Generates a mask of whether violent rain is present in a grid box and provides the difference between two datasets. Values will be positive or negative one depending on which model has violent rain present at that location if the other does not.