From 0e6ac2472caca70bb6822aca7542a6a0a8d979d7 Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 14:00:56 +0100 Subject: [PATCH 01/10] Adds saturation fraction Fixes #1965 --- src/CSET/operators/humidity.py | 108 +++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/CSET/operators/humidity.py b/src/CSET/operators/humidity.py index 0854397f1..ab1b72635 100644 --- a/src/CSET/operators/humidity.py +++ b/src/CSET/operators/humidity.py @@ -15,9 +15,11 @@ """Operators for humidity conversions.""" import iris.cube +import numpy as np from CSET._common import iter_maybe from CSET.operators._atmospheric_constants import EPSILON +from CSET.operators._utils import get_cube_coordindex from CSET.operators.misc import convert_units from CSET.operators.pressure import vapour_pressure @@ -443,3 +445,109 @@ def relative_humidity_from_specific_humidity( return RH[0] else: return RH + + +def precipitable_water( + mixing_ratio: iris.cube.Cube | iris.cube.CubeList, +) -> iris.cube.Cube | iris.cube.CubeList: + """Calculate the precipitable water.""" + precipitable_water = iris.cube.CubeList([]) + for w in iter_maybe(mixing_ratio): + # Integrate the data in the vertical using np.trapz + # (following trapezoid rule). + pw = np.trapz( + w.data, + x=w.coord("level_height").points[:], + axis=get_cube_coordindex(w, "level_height"), + ) + # Determine array information of input cube to get + # correct cube to copy across to. + if len(w.coord("realization").points) != 1 and len(w.coord("time").points) != 1: + pwat = w[:, :, 0, :, :].copy() + elif ( + len(w.coord("realization").points) != 1 and len(w.coord("time").points) == 1 + ): + pwat = w[:, 0, :, :].copy() + elif ( + len(w.coord("time").points) != 1 and len(w.coord("realization").points) == 1 + ): + pwat = w[:, 0, :, :].copy() + else: + pwat = w[0, :, :].copy() + # Create the data array, rename, and correct units. + pwat.data = pw + pwat.rename("precipitable_water") + pwat.units = "m" + precipitable_water.append(pwat) + # Output the data. + if len(precipitable_water) == 1: + return precipitable_water[0] + else: + return precipitable_water + + +def saturation_precipitable_water( + mixing_ratio: iris.cube.Cube | iris.cube.CubeList, + relative_humidity: iris.cube.Cube | iris.cube.CubeList, +) -> iris.cube.Cube | iris.cube.CubeList: + """Calculate saturation precipitable water.""" + saturation_precipitable_water = iris.cube.CubeList([]) + for w, rh in zip( + iter_maybe(mixing_ratio), iter_maybe(relative_humidity), strict=True + ): + # Integrate the data in the vertical using np.trapz + # (following trapezoid rule). + spw = np.trapz( + (w / rh).data, + x=w.coord("level_height").points[:], + axis=get_cube_coordindex(w, "level_height"), + ) + # Determine array information of input cube to get + # correct cube to copy across to. + if len(w.coord("realization").points) != 1 and len(w.coord("time").points) != 1: + satpw = w[:, :, 0, :, :].copy() + elif ( + len(w.coord("realization").points) != 1 and len(w.coord("time").points) == 1 + ): + satpw = w[:, 0, :, :].copy() + elif ( + len(w.coord("time").points) != 1 and len(w.coord("realization").points) == 1 + ): + satpw = w[:, 0, :, :].copy() + else: + satpw = w[0, :, :].copy() + # Store the data for output, rename cube, and correct units. + satpw.data = spw + satpw.rename("saturation_precipitable_water") + satpw.units = "m" + saturation_precipitable_water.append(satpw) + # Output cube/cubelist. + if len(saturation_precipitable_water) == 1: + return saturation_precipitable_water[0] + else: + return saturation_precipitable_water + + +def saturation_fraction( + mixing_ratio: iris.cube.Cube | iris.cube.CubeList, + relative_humidity: iris.cube.Cube | iris.cube.CubeList, +) -> iris.cube.Cube | iris.cube.CubeList: + """Calculate saturation fraction.""" + saturation_fraction = iris.cube.CubeList([]) + for w, rh in zip( + iter_maybe(mixing_ratio), iter_maybe(relative_humidity), strict=True + ): + # Calculate both precipitable water and saturation + # precipitable water. + pw = precipitable_water(w) + spw = saturation_precipitable_water(w, rh) + # Calculate the saturation fraction by taking the ratio. + sf = pw / spw + # Rename the cube and append to cubelist. + sf.rename("saturation_fraction") + saturation_fraction.append(sf) + # Output the cube/cubelist. + if len(saturation_fraction) == 1: + return saturation_fraction[0] + else: + return saturation_fraction From 6f03f77ffcd981a565b810f98a9c42dcae8e8699 Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 14:13:56 +0100 Subject: [PATCH 02/10] Ensure correct RH units for calcuation --- src/CSET/operators/humidity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CSET/operators/humidity.py b/src/CSET/operators/humidity.py index ab1b72635..dee0850ad 100644 --- a/src/CSET/operators/humidity.py +++ b/src/CSET/operators/humidity.py @@ -497,6 +497,7 @@ def saturation_precipitable_water( ): # Integrate the data in the vertical using np.trapz # (following trapezoid rule). + rh = convert_units(rh, "1") spw = np.trapz( (w / rh).data, x=w.coord("level_height").points[:], From 7848d455dc2fd534a51e3731c599a51ca42d0bab Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 14:32:16 +0100 Subject: [PATCH 03/10] Initial holders for recipe, loader, and plumbing into workflow --- .../meta/diagnostics/rose-meta.conf | 12 +++++++++++ .../cset_workflow/rose-suite.conf.example | 1 + src/CSET/loaders/spatial_field.py | 16 ++++++++++++++ .../saturation_fraction_spatial_plot.yaml | 21 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml diff --git a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf index 77483988b..fa18ca850 100644 --- a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf +++ b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf @@ -1866,6 +1866,18 @@ type=python_boolean compulsory=true sort-key=xp-maulp3 +[template variables=SATURATION_FRACTION] +ns = Diagnostics/Derived/XPPN +title= Saturation fraction spatial plot +description=PROCESS-BASED DIAGNOSTIC. + Determines the saturation fraction of a column in the atmosphere. + Requires TBD on model levels. +help=This diagnostic identifies the moisture throughout the column. The larger + the saturation fraction, the more moisture there is in depth throughout the column. + +type=python_boolean +compulsory=true +sort-key=xp-satfrac ################################### # Ensembles [Diagnostics/Ensembles] diff --git a/src/CSET/cset_workflow/rose-suite.conf.example b/src/CSET/cset_workflow/rose-suite.conf.example index 5a8612e3f..bde0a6585 100644 --- a/src/CSET/cset_workflow/rose-suite.conf.example +++ b/src/CSET/cset_workflow/rose-suite.conf.example @@ -125,6 +125,7 @@ PROFILE_PLEVEL_AGGREGATION=False,False,False,False RAIN_PRESENCE_DOMAIN_MEAN_TIMESERIES=False RAIN_PRESENCE_SPATIAL_DIFFERENCE=False RAIN_PRESENCE_SPATIAL_PLOT=False +SATURATION_FRACTION=False SCREEN_LEVEL_TEMPERATURE_PROBABILITIES=False !!SCREEN_LEVEL_TEMPERATURE_SPATIAL_PROBABILITY_WITHOUT_CONTROL_MEMBER=False SELECT_SUBAREA=False diff --git a/src/CSET/loaders/spatial_field.py b/src/CSET/loaders/spatial_field.py index 1517c6816..87ca867ec 100644 --- a/src/CSET/loaders/spatial_field.py +++ b/src/CSET/loaders/spatial_field.py @@ -651,6 +651,22 @@ def load(conf: Config): aggregation=False, ) + # Saturation fraction + if conf.SATURATION_FRACTION: + for model in models: + yield RawRecipe( + recipe="saturation_fraction_spatial_plot.yaml", + 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, + }, + model_ids=model["id"], + aggregation=False, + ) + # Screen-level temperature probabilities for model, condition, threshold in itertools.product( models, diff --git a/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml b/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml new file mode 100644 index 000000000..e5fef874c --- /dev/null +++ b/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml @@ -0,0 +1,21 @@ +category: Extreme precipitation +title: $MODEL_NAME Saturation fraction spatial plot +description: | + To be updated + +steps: + - operator: read.read_cubes + file_paths: $INPUT_PATHS + model_names: $MODEL_NAME + constraints: ["air_temperature", "air_pressure", "specific_humidity"] + subarea_type: $SUBAREA_TYPE + subarea_extent: $SUBAREA_EXTENT + + - operator: humidity.saturation_fraction + mixing_ratio: + relative_humidity: + + - operator: plot.spatial_pcolormesh_plot + + - operator: write.write_cube_to_nc + overwrite: True From 753bd53adb23610b3b993b3d08dde242c2a54363 Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 15:11:36 +0100 Subject: [PATCH 04/10] Update recipe and documentation in rose gui --- .../meta/diagnostics/rose-meta.conf | 2 +- .../saturation_fraction_spatial_plot.yaml | 52 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf index fa18ca850..9dbf34512 100644 --- a/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf +++ b/src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf @@ -1871,7 +1871,7 @@ ns = Diagnostics/Derived/XPPN title= Saturation fraction spatial plot description=PROCESS-BASED DIAGNOSTIC. Determines the saturation fraction of a column in the atmosphere. - Requires TBD on model levels. + Requires "air temperature", "air_pressure" and "specific_humidity" on model levels. help=This diagnostic identifies the moisture throughout the column. The larger the saturation fraction, the more moisture there is in depth throughout the column. diff --git a/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml b/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml index e5fef874c..4006c0d8a 100644 --- a/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml +++ b/src/CSET/recipes/derived_diagnostics/precipitation/extreme_precipitation/saturation_fraction_spatial_plot.yaml @@ -1,7 +1,17 @@ category: Extreme precipitation title: $MODEL_NAME Saturation fraction spatial plot description: | - To be updated + Generates a spatial plot of the saturation fraction. The saturation fraction + is calculated from the specific humidity converted to a mixing ratio, and the + specific humidity, temperature, and air pressure to calculate the relative + humidity. From these diagnostics the precipitable water and saturation + precipitable water are derived, before their ratios are taken for the + saturation fraction. + + A saturation fraction close to one implies that the atmopshere is fully + saturated throughout the column. Smaller values imply the atmosphere is dry. + Traditionally for precipitation the saturation fraction is greater than 0.6. + The saturation fraction is applicable throughout the globe. steps: - operator: read.read_cubes @@ -13,7 +23,47 @@ steps: - operator: humidity.saturation_fraction mixing_ratio: + operator: humidity.mixing_ratio_from_specific_humidity + specific_humidity: + operator: filters.filter_multiple_cubes + constraint: + operator: constraints.combine_constraints + constraint_1: + operator: constraints.generate_var_constraint + varname: "specific_humidity" + constraint_2: + operator: constraints.generate_remove_single_level_constraint + coord: "model_level_number" + level: 0 relative_humidity: + operator: humidity.relative_humidity_from_specific_humidity + specific_humidity: + operator: filters.filter_multiple_cubes + constraint: + operator: constraints.combine_constraints + constraint_1: + operator: constraints.generate_var_constraint + varname: "specific_humidity" + constraint_2: + operator: constraints.generate_remove_single_level_constraint + coord: "model_level_number" + level: 0 + temperature: + operator: filters.filter_multiple_cubes + constraint: + operator: constraints.generate_var_constraint + varname: "air_temperature" + pressure: + operator: filters.filter_multiple_cubes + constraint: + operator: constraints.combine_constraints + constraint_1: + operator: constraints.generate_var_constraint + varname: "m01s00i408" + constraint_2: + operator: constraints.generate_remove_single_level_constraint + coord: "model_level_number" + level: 0 - operator: plot.spatial_pcolormesh_plot From c3bee4108e5433aebbb2a3d8d6589e0e88361567 Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 15:56:55 +0100 Subject: [PATCH 05/10] Adds documentation --- src/CSET/operators/humidity.py | 139 +++++++++++++++++++++++++++- src/CSET/operators/precipitation.py | 14 +++ 2 files changed, 150 insertions(+), 3 deletions(-) diff --git a/src/CSET/operators/humidity.py b/src/CSET/operators/humidity.py index dee0850ad..f44438efd 100644 --- a/src/CSET/operators/humidity.py +++ b/src/CSET/operators/humidity.py @@ -450,7 +450,55 @@ def relative_humidity_from_specific_humidity( def precipitable_water( mixing_ratio: iris.cube.Cube | iris.cube.CubeList, ) -> iris.cube.Cube | iris.cube.CubeList: - """Calculate the precipitable water.""" + r"""Calculate the precipitable water. + + Arguments + --------- + mixing_ratio: iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the mixing ratio. It can be + calculated within a recipe or a direct model output. + + Returns + ------- + iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the precipitable water. + + Notes + ----- + The precipitable water is the total depth of liquid water produced by + condensing all of the moisture in a column of the atmopshere. + + It can be calculated as + + .. math:: pw = \int w dz + + for pw the precipitable water, w the mixing ratio, and z the height. It is + integrated from the surface to the top of the atmosphere. + + Generally, the precipitable water is widely applicable across the globe. + It is likely that larger precipitation totals are associated with greater + precipitable water. However, this is not strictly the case and you can get + lower precipitable water values with large precipitaiton amounts + (e.g. [Daviesetal24]_). Therefore, caution is needed with its interpretation. + A diagnostic such as saturation fraction maybe more beneficial (e.g. [Daviesetal26]_). + + Examples + -------- + >>> pwat = humidity.precipitable_water(mixing_ratio) + + References + ---------- + .. [Daviesetal24] Davies, P.A., Fowler, H.J, Villalobos-Herrera, R., + Slingo, J., Flack, D.L.A., and Taszarek, M (2024) + "A New Conceptual Model for Understanding and Predicting Life-Threatening + Rainfall Extremes." Weather and Climate Extremes, vol. 45, 100696, + doi: 10.1016/j.wace.2024.100696 + .. [Daviesetal26] Davies, P. A., Flack, D. L. A., Pirret, J., Fowler, H. J. + (2026) "Application of the Davies Four-Stage Conceptual Model for + Life-Threatening Rainfall Extremes on the April 2024 United Arab Emirates + and Oman Floods." Weather and Climate Extremes, vol. 51, 100846. + doi:10.1016/j.wace.2025.100846 + """ precipitable_water = iris.cube.CubeList([]) for w in iter_maybe(mixing_ratio): # Integrate the data in the vertical using np.trapz @@ -490,7 +538,43 @@ def saturation_precipitable_water( mixing_ratio: iris.cube.Cube | iris.cube.CubeList, relative_humidity: iris.cube.Cube | iris.cube.CubeList, ) -> iris.cube.Cube | iris.cube.CubeList: - """Calculate saturation precipitable water.""" + r"""Calculate saturation precipitable water. + + Arguments + --------- + mixing_ratio: iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the mixing ratio. It can be + calculated within a recipe or a direct model output. + relative_humidity: iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the relative humidity. It can + either be calculated or used as model output. + + Returns + ------- + iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the saturation precipitable water. + + Notes + ----- + The saturation precipitable water is equivalent to the precipitable + water assuming that the atmosphere was fully saturated. + + It can be calculated as + + .. math:: spw = \int \frac{w}{RH} dz + + for spw the saturated precipitable water, w the mixing ratio, + RH the relative humidity (as a decimal) and z the height. It is + integrated from the surface to the top of the atmosphere. + + It is applicable throughout the globe and is, perhaps, best considered + in relation to the precipitable water. A useful way to do this is + via the saturation fraction. + + Examples + -------- + >>> sat_pwat = humidity.saturated_precipitable_water(mixing_ratio, RH) + """ saturation_precipitable_water = iris.cube.CubeList([]) for w, rh in zip( iter_maybe(mixing_ratio), iter_maybe(relative_humidity), strict=True @@ -533,7 +617,56 @@ def saturation_fraction( mixing_ratio: iris.cube.Cube | iris.cube.CubeList, relative_humidity: iris.cube.Cube | iris.cube.CubeList, ) -> iris.cube.Cube | iris.cube.CubeList: - """Calculate saturation fraction.""" + r"""Calculate saturation fraction. + + Arguments + --------- + mixing_ratio: iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the mixing ratio. It can be + calculated within a recipe or a direct model output. + relative_humidity: iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the relative humidity. It can be + calculated within a recipe or used as a direct model output. + + Returns + ------- + iris.cube.Cube | iris.cube.CubeList + A cube or cubelist of the saturation fraction. + + Notes + ----- + The saturation fraction indicates how moist a column of the atmosphere + is. A value close to one implies that the atmosphere is fully saturated + throughout the entire column. Smaller values imply the atmosphere is + drier throughout the column. It is based around ideas of specific entropy + ([Zengetal05]_) but can be simplified to an approximation following [Daviesetal2026]_. + + It can be approximated as + + .. math:: saturation_fraction = \frac{precipitable_water}{saturation_precipitable_water} + + and can be used throughout the globe with the same interpretation. + + For a recent, example, [Daviesetal2026]_ have applied the concept to their + conceptual model for extreme rainfall. Thus it is a potentially useful diagnostic + to consider for extreme events, and is thought of as more reliable than + using precipitable water on its own. + + Examples + -------- + >>> sf = humidity.saturation_fraction(mixing_ratio, relative_humidity) + + References + ---------- + .. [Daviesetal2026] Davies, P. A., Flack, D. L. A., Pirret, J., Fowler, H. J. + (2026) "Application of the Davies Four-Stage Conceptual Model for + Life-Threatening Rainfall Extremes on the April 2024 United Arab Emirates + and Oman Floods." Weather and Climate Extremes, vol. 51, 100846. + doi:10.1016/j.wace.2025.100846 + .. [Zengetal05] Zeng, X., Tao, W-K, and Simpson, J. (2005) "An Equation for Moist + Entropy in a Precipitating and Icy Atmosphere" Journal of the Atmospheric Sciences, + vol. 2, 4293-4309, doi: 10.1175/JAS3570.1 + """ saturation_fraction = iris.cube.CubeList([]) for w, rh in zip( iter_maybe(mixing_ratio), iter_maybe(relative_humidity), strict=True diff --git a/src/CSET/operators/precipitation.py b/src/CSET/operators/precipitation.py index a7e23b513..7f4316a2a 100644 --- a/src/CSET/operators/precipitation.py +++ b/src/CSET/operators/precipitation.py @@ -68,6 +68,20 @@ def MAUL_properties( If number of MAULs is the desired output it will be set to zero. The MAUL diagnostic is applicable anywhere in the globe and across all scales. + The properties used here are based upon [Daviesetal24]_ and [Daviesetal26]_. + + References + ---------- + .. [Daviesetal24] Davies, P.A., Fowler, H.J, Villalobos-Herrera, R., + Slingo, J., Flack, D.L.A., and Taszarek, M (2024) + "A New Conceptual Model for Understanding and Predicting Life-Threatening + Rainfall Extremes." Weather and Climate Extremes, vol. 45, 100696, + doi: 10.1016/j.wace.2024.100696 + .. [Daviesetal26] Davies, P. A., Flack, D. L. A., Pirret, J., Fowler, H. J. + (2026) "Application of the Davies Four-Stage Conceptual Model for + Life-Threatening Rainfall Extremes on the April 2024 United Arab Emirates + and Oman Floods." Weather and Climate Extremes, vol. 51, 100846. + doi:10.1016/j.wace.2025.100846 """ num_MAULs = iris.cube.CubeList([]) maul_d = iris.cube.CubeList([]) From 0ec54677b53d8cda88e498c641461e6a70246f40 Mon Sep 17 00:00:00 2001 From: daflack Date: Tue, 19 May 2026 17:04:17 +0100 Subject: [PATCH 06/10] Update operator and add some basic test data --- src/CSET/operators/humidity.py | 12 ++++++------ tests/test_data/humidity/mr_basic.nc | Bin 0 -> 19225 bytes tests/test_data/humidity/rh_basic.nc | Bin 0 -> 19263 bytes 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 tests/test_data/humidity/mr_basic.nc create mode 100644 tests/test_data/humidity/rh_basic.nc diff --git a/src/CSET/operators/humidity.py b/src/CSET/operators/humidity.py index f44438efd..235d8ca05 100644 --- a/src/CSET/operators/humidity.py +++ b/src/CSET/operators/humidity.py @@ -501,9 +501,9 @@ def precipitable_water( """ precipitable_water = iris.cube.CubeList([]) for w in iter_maybe(mixing_ratio): - # Integrate the data in the vertical using np.trapz + # Integrate the data in the vertical using np.trapezoid # (following trapezoid rule). - pw = np.trapz( + pw = np.trapezoid( w.data, x=w.coord("level_height").points[:], axis=get_cube_coordindex(w, "level_height"), @@ -525,7 +525,7 @@ def precipitable_water( # Create the data array, rename, and correct units. pwat.data = pw pwat.rename("precipitable_water") - pwat.units = "m" + pwat.units = "mm" precipitable_water.append(pwat) # Output the data. if len(precipitable_water) == 1: @@ -579,10 +579,10 @@ def saturation_precipitable_water( for w, rh in zip( iter_maybe(mixing_ratio), iter_maybe(relative_humidity), strict=True ): - # Integrate the data in the vertical using np.trapz + # Integrate the data in the vertical using np.trapezoid # (following trapezoid rule). rh = convert_units(rh, "1") - spw = np.trapz( + spw = np.trapezoid( (w / rh).data, x=w.coord("level_height").points[:], axis=get_cube_coordindex(w, "level_height"), @@ -604,7 +604,7 @@ def saturation_precipitable_water( # Store the data for output, rename cube, and correct units. satpw.data = spw satpw.rename("saturation_precipitable_water") - satpw.units = "m" + satpw.units = "mm" saturation_precipitable_water.append(satpw) # Output cube/cubelist. if len(saturation_precipitable_water) == 1: diff --git a/tests/test_data/humidity/mr_basic.nc b/tests/test_data/humidity/mr_basic.nc new file mode 100644 index 0000000000000000000000000000000000000000..504de1007a7b86b45120026c8e309dfb6e0cdc3b GIT binary patch literal 19225 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 literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/rh_basic.nc b/tests/test_data/humidity/rh_basic.nc new file mode 100644 index 0000000000000000000000000000000000000000..7b490395f801cd23f5a36611e68e3b0074a4e36d GIT binary patch literal 19263 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 Date: Wed, 20 May 2026 08:57:03 +0100 Subject: [PATCH 07/10] Add test data --- tests/test_data/humidity/mr_all.nc | Bin 0 -> 24830 bytes tests/test_data/humidity/mr_member.nc | Bin 0 -> 20048 bytes tests/test_data/humidity/mr_time.nc | Bin 0 -> 20058 bytes tests/test_data/humidity/pw_all.nc | Bin 0 -> 20908 bytes tests/test_data/humidity/pw_basic.nc | Bin 0 -> 18743 bytes tests/test_data/humidity/pw_member.nc | Bin 0 -> 19377 bytes tests/test_data/humidity/pw_time.nc | Bin 0 -> 19693 bytes tests/test_data/humidity/rh_all.nc | Bin 0 -> 24630 bytes tests/test_data/humidity/rh_member.nc | Bin 0 -> 20122 bytes tests/test_data/humidity/rh_time.nc | Bin 0 -> 20025 bytes tests/test_data/humidity/sf_basic.nc | Bin 0 -> 18743 bytes tests/test_data/humidity/spw_all.nc | Bin 0 -> 20892 bytes tests/test_data/humidity/spw_basic.nc | Bin 0 -> 18743 bytes tests/test_data/humidity/spw_member.nc | Bin 0 -> 19501 bytes tests/test_data/humidity/spw_time.nc | Bin 0 -> 19551 bytes 15 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/test_data/humidity/mr_all.nc create mode 100644 tests/test_data/humidity/mr_member.nc create mode 100644 tests/test_data/humidity/mr_time.nc create mode 100644 tests/test_data/humidity/pw_all.nc create mode 100644 tests/test_data/humidity/pw_basic.nc create mode 100644 tests/test_data/humidity/pw_member.nc create mode 100644 tests/test_data/humidity/pw_time.nc create mode 100644 tests/test_data/humidity/rh_all.nc create mode 100644 tests/test_data/humidity/rh_member.nc create mode 100644 tests/test_data/humidity/rh_time.nc create mode 100644 tests/test_data/humidity/sf_basic.nc create mode 100644 tests/test_data/humidity/spw_all.nc create mode 100644 tests/test_data/humidity/spw_basic.nc create mode 100644 tests/test_data/humidity/spw_member.nc create mode 100644 tests/test_data/humidity/spw_time.nc diff --git a/tests/test_data/humidity/mr_all.nc b/tests/test_data/humidity/mr_all.nc new file mode 100644 index 0000000000000000000000000000000000000000..0062b576bde1029089cf559cac22f625aec07610 GIT binary patch literal 24830 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 Jk!nBue*l}I2K4{{ literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/mr_member.nc b/tests/test_data/humidity/mr_member.nc new file mode 100644 index 0000000000000000000000000000000000000000..c91835823168f689ccdc512ac6eb2b61bec01f95 GIT binary patch literal 20048 zcmeG^d0dr6*K^^bh!@;7w?uNo1p(0v%>%L;pa>dR#`VIzz*V@wy`X?7n`xGPE}5Ep zpPIRrS+82I`C2Y5YNn-@`nsfLYPPuBH|Lo-4;S3vQ~SMtFs0|2ne&`EbLPyMGv_(i ziQ&Pat}e}79GsmQmA_?)?k`%rWk>2x?;QiegJT?YA3As_ZH^6OSNqQ5VS|LnBy@3L z8tiG*f@wK$bYMihBGiQ-o+EQ(&WNh-9~5oP$TV5A%_bYeVQ@qT{%54gYBOh8p7rf) zF=Zzilb`kT^6~ca?wo2&?*6Q=myefs-MSo&#rfdhjCB`)v0mdF{5l{gJVJE)_Y&=+ zWw$PrF(*ELBc@{w8B4O6vh_(B>6r$r*_L6^CmL)fR+r02h$(%l3h5u)Z$N*2WWU%!tSNsRXW4}RL5`3cq=%w*U;0z{B}D^VgESh>l^ECsA9x|?sKc@Bh2`XiEsh-( zRt0jAScZ6<2`^CssEXopZ$$}2R7WlpWuf%_1=~Rx8CIj&V#wy|f)q8%rRU9NnBLD#gJ}N z$m%3knrWmdO`mEqr=(`5^>!F6#2IEMPDhT3MTM0TC+)D>?3z^k_RR$yQ%wP#QSF$c zCc#70pAdkt&ruSzAE{L=51|mm)*4h%p^&9ot<;0=DIv8=Ag>E`%C7{Pg0gI`)+j^@ z8ciE)a^QP|L`hrYj(S7X&wB2lY$nD6m|tz?_cuqg*IdEF{4f&aNGV8f(V^<@gN829RTtkNP0{hcD!d6XQBc8Rm9@1*wx z#d~Yzp7}w(S0!7iyhF44{@ZukSCpdvGlFcH6zi5=^iZ&zQP%Z%HXz7{h}O3$^>oeS-%c7HTJa`I_2ZEZ42Q zKv&xdALoUI=ny~B-%;CXT%0Es!YBGTUo6yqMV^CXODeWYo37#$KfZ#D+D`PlRcI&r z+%r)M(I-A-Gke92o~mKB1ga%aErDtYR7;>*0@V_zmO!-xswGe@focg{;UL0*ijI@Z6y-HZ&F^(64AL9k1*=&86bvv zO-9aI$uKcV^&Jn98?{y?>MSVB$yFNYcmRncc>k9U_si88ZH&2jPuyEE zHs}Aec(*Kqr<-Ao42_KqVNNpSqv79X&f!Y54qIMw#>@N7CngtsQuDimzqqA?Z3**l zv7-=fH4QAW>^H-Ewf^e&cXBqYt&!Sp@XQ!EuKTp{sNu!1Udty2YmmX7^tB+sogLffgNqIWzFRO!zV|W7Zs22$8S8w!1}mE=(ISr|*h2 z*$`H+al>@?ad3R@vdam^5^ym_#1&i`1?Iy&zJFn_4fOTbcG~A_1h*w0T|1IB5-$AG zCGq__9Dmp8F*`mQ107?#|FJthA0}-Ld}ZVZHduVXP$Q-q->= zr)xI3<`zJ*htC>AU>nd6x)tB&YytfKVUKS#zN4XIUcbJUlX(#6H+5wHrDNgH#ef|X z;|bx%*{3kls zSaQn>+q$`LId&!w(h@)3btJg}x`tgT*f-1y-Hv?_*43B*$zD(HJUz<*W6tL|ZPXWn z=k(b>?t7~(bgtun{Lc=BklQ_SQiIDQ;QGeT8~>>
cM{cDvJ!7IhnGW_}#Ll>XV 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 DUOCa- literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/pw_all.nc b/tests/test_data/humidity/pw_all.nc new file mode 100644 index 0000000000000000000000000000000000000000..17b099456bb9ae081df0a0cbdf7268d45ad54d41 GIT binary patch literal 20908 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 literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/pw_basic.nc b/tests/test_data/humidity/pw_basic.nc new file mode 100644 index 0000000000000000000000000000000000000000..c9a13724af9dca137aae738baa51f9af7ef6095d GIT binary patch literal 18743 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 XXrd-r(9ADMDJgq6aN=79+gtw&sgRBx literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/pw_member.nc b/tests/test_data/humidity/pw_member.nc new file mode 100644 index 0000000000000000000000000000000000000000..1bc7703acb85555f01fb6be4c6ad5615f0b013a3 GIT binary patch literal 19377 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 L>Ae2dsxSTz_ghbv literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/rh_all.nc b/tests/test_data/humidity/rh_all.nc new file mode 100644 index 0000000000000000000000000000000000000000..bfeab4ebb0a91591e0864b1fc50d04f352177195 GIT binary patch literal 24630 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 GM*bgwav;Y5 literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/rh_time.nc b/tests/test_data/humidity/rh_time.nc new file mode 100644 index 0000000000000000000000000000000000000000..bc0ef933fe856406b7b92e4f0888117be0819c64 GIT binary patch literal 20025 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 O2dVyj$hw<7@BaYQG!1zG literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/sf_basic.nc b/tests/test_data/humidity/sf_basic.nc new file mode 100644 index 0000000000000000000000000000000000000000..2057bdc6e94d909535c5348696c6463dd0d5da0e GIT binary patch literal 18743 zcmeGkZE#b^ai1l-#(w6*7)*fy8L%M)97710G{hgWjYw=mF-=Huqso@B)!34eWFRyp zkS3*}WRlW`VFpSkp+GxHnMv~jwE3VNGEK>tk{`5`FbxSUv?&Y>kQvByLU-?OpPp>V zwj#TePTzvP@7?X&celHDxA%6>Pr9mVatoYGo!7flXsMk@yIxooRx19<|fw2}N4nA&=MJ8HN##GNXNSvSH3m)JD8^BAfE_ zb;~Pqp7`WMeP{% z2IK`f)HMW!3@l3lwYvn?!Zf^-brftK2%ILxoPr&O5dEEg|yH99} zdQ0>|g{-b&MU}4#fuvJiHnE#lyZ{lxQ2Zz18a$eV#v=9R5L{$`pDTLzs2D~$M>TB< z5vQS&)WE^UNa%s{NL=wWoIaFlY}2J>($l*ZN*lkVcgv|G3ocgWwR??nME#k&j`P=>8*k&Rzfj(QRJ4>1cjk>6+X^x>c zhdq(b5DrWn^eaQ2X1dVFQi0tx5Ga#zQN>IZNz#;GXMO@Ch9V<=52nBP)ca+B_{TN-@y@@Njm{Weg$FkL>2Oqk4_EZ_1|LS)StpBJ?it3EQ*SleRxvXcbmy2%>SSY@2Am-1V zk}h>ViTEcfm+!s&mc*Gx`-P z>yxA2N3RbMXBI}Ugj}hQGezdO(2R8qhH#+8T%O>1-_>vCNIjLB^u(SMW19>R7fe)9 zK?me#LjI%3GhHNM*s>@bWCu^Re9f=a+gNiMh7MR4j7b0_0u}yr)>bq$i0C-QjOg{z z*lvTfqP80Juahop*hdjBt*@x77WCzcep#zO?7;sX2c~d1t7;rxjGzT5tHaUM8F2)H zk!aOwOu+CowfTx246~C9{7U&uYCCt?h&N8bcEkkT4H;4ef^nZz(48&ZrHr^lGV4!t zQs~5np+%8=!jbG`!|v2<8S~pLx(KjCH32B`f#F!;j&;3xgn zd2l}!sA=)Y`E7T>L3$nwC^-Ah+S``GzlcIjMzhcDei$C30yQzZf3R^g?4bgDgeE|r ze)o^y8R9Wb1!VoZUV^h!umvCs(dcUze*>fO0xIYircrer|L4?=6I57$zcgq7m8MJ;`jQb}6xvuRp`35vv2@IU14|g4>0D=`N zA!Vb&yuuys?!!%>Nr3$Y_TeP8VAQ`~$@_4)%&DcSk2eg06NlL|w8Hcko`3grp43!X zoU}jg1#8T>|6Cr(GYLG9zSD10@Ic4}sM!OX_?L|wi8E*P=y+Klc<2RlW*eyPx`+-z z4*)tmMrvBKDSMH!{Oa(B8XB|kI%2cj9BsbUzBYHO&)?D-akK}$cq5Afo$XD&kR#;t zwE5SGJV8eU-Pgv0pDa}^Mb_Kwlkcl>RaF|hAuIzpn0^1hlXCJz$MHeN#3Ttnyr&0F zD?fFe)5VZmzq2O|Zl1M%nd8SAf53*~7Os9%uoLsoVp3=MRU$Ku(m_iogKlYcP4&|1 zdS|sv2c3(JqTPho6Tw95NHj0h*cm`3)l2`=5jS0Q4UZk9Bj7AJd1n$_G=G!RdsRBH zI}pTd>R!@;$$K-4Zy>wFt!LaH1xvaNc+uBsz8F-m6zqYA#KADLHR6Sqkrik7D%Kdy{Wg zUt@cCZ_DF(Z+!M6deVUo77V1n!;2SV_IIeU1OZ$?%wUKv>b~h%Pmi2mA`77(i9Ach6lEy=My{XHX%aSz<5&5 z1rFT*7FX~C-~04fSUAlYL`TPY@bsn1H;SIVT(e=!Xz4U_`*>bq%>1O&?7&-D^HcTq zv5J84@!Tv+Rxs+8~3&VMX>Aouu6ChOY>pV!^d23 zAC?xvo-^++2a39lhn){Dc@R$;ljr@n&NI#MZM@8f{khIjopjc&6h7F#f25)k^&Fw9eG zn`eBbX2Z3W`ZYcgN*z-SN`O=&jVb}sF2w$b?xi|u!4e=Xf!2Qk()Q@fm;h-8v=|DI zw!#PJlyw@TPudp^8v)Wv7wjo%;SvicmVodI`EMN%+18TBwrh4)c_x4B!eJ{wv$L&? zjB~8ma1rcmLb=Q!`WFHC#HVIwOR|$E#^)OQIs&TM*~Eqzt{k70)a-0;LybD|m5yC6 z&CV7WKS-(B*(8U?em!O9Mzgb#>gMmM#ZDFv8|?^lwP?egRZPQ{oZIs&?8q7>V*AcE zeV3iMkZal8?ewMgR+lXP9KQ{(KxmwF8$GcsWf?y^hT|q<=-4{~%bopzUd}DgB>H$B z`Uoti7W9xDomQvpi-f1u+c^jx_Z&Ps)n>lpI}J5NL8EOaNLh?CZ6Mj!8LNzxIDeGF zoIxa6?pnw)%2YC9SA%8LOYEY8c%-y9qb3jRR+3Ea#Nlf#<~RkrOssx=3x$vda~Hzj zz|m0uA=NY zM*us3NRbqDEj!tosw}1rhAfKvkOj@TBsJ4i2zMsc^&Z6ACv1K_Rauk_yez`}M<^27 X1x?f>3!3;jDWym6k}Eiwzpv##Z`Y4M literal 0 HcmV?d00001 diff --git a/tests/test_data/humidity/spw_all.nc b/tests/test_data/humidity/spw_all.nc new file mode 100644 index 0000000000000000000000000000000000000000..06225db94e91dfef6dc1c92b2b752d4913c13d86 GIT binary patch literal 20892 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 BU=IKQ literal 0 HcmV?d00001 From 31555f5b1c771bd0cc54c1cd811ac9021eb464fa Mon Sep 17 00:00:00 2001 From: daflack Date: Wed, 20 May 2026 09:07:53 +0100 Subject: [PATCH 08/10] Adds configurations for test data --- tests/conftest.py | 204 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 4daba7627..b374f167b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -585,3 +585,207 @@ def precalc_maul_depth_5d_read_only(): def precalc_maul_depth_5d(precalc_maul_depth_5d_read_only): """Get precalculated depth for 5D data. It is safe to modify.""" return precalc_maul_depth_5d_read_only.copy() + + +@pytest.fixture() +def mr_3d_read_only(): + """Get mixing ratio 3D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/mr_basic.nc") + + +@pytest.fixture() +def mr_3d(mr_3d_read_only): + """Get mixing ratio 3D data. It is safe to modify.""" + return mr_3d_read_only.copy() + + +@pytest.fixture() +def mr_time_read_only(): + """Get mixing ratio 4D data varying in time. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/mr_time.nc") + + +@pytest.fixture() +def mr_time(mr_time_read_only): + """Get mixing ratio 4D data varying in time. It is safe to modify.""" + return mr_time_read_only.copy() + + +@pytest.fixture() +def mr_member_read_only(): + """Get mixing ratio 4D data varying in member. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/mr_member.nc") + + +@pytest.fixture() +def mr_member(mr_member_read_only): + """Get mixing ratio 4D data varying in member. It is safe to modify.""" + return mr_member_read_only.copy() + + +@pytest.fixture() +def mr_5d_read_only(): + """Get mixing ratio 5D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/mr_all.nc") + + +@pytest.fixture() +def mr_5d(mr_5d_read_only): + """Get mixing ratio 5D data. It is safe to modify.""" + return mr_5d_read_only.copy() + + +@pytest.fixture() +def rh_3d_read_only(): + """Get relative humidity 3D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/rh_basic.nc") + + +@pytest.fixture() +def rh_3d(rh_3d_read_only): + """Get relative humidity 3D data. It is safe to modify.""" + return rh_3d_read_only.copy() + + +@pytest.fixture() +def rh_time_read_only(): + """Get relative humidity 4D data varying in time. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/rh_time.nc") + + +@pytest.fixture() +def rh_time(rh_time_read_only): + """Get relative humidity 4D data varying in time. It is safe to modify.""" + return rh_time_read_only.copy() + + +@pytest.fixture() +def rh_member_read_only(): + """Get relative humidity 4D data varying in member. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/rh_member.nc") + + +@pytest.fixture() +def rh_member(rh_member_read_only): + """Get relative humidity 4D data varying in member. It is safe to modify.""" + return rh_member_read_only.copy() + + +@pytest.fixture() +def rh_5d_read_only(): + """Get relative humidity 5D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/rh_all.nc") + + +@pytest.fixture() +def rh_5d(rh_5d_read_only): + """Get relative humidity 5D data. It is safe to modify.""" + return rh_5d_read_only.copy() + + +@pytest.fixture() +def pw_3d_read_only(): + """Get precipitable water 3D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/pw_basic.nc") + + +@pytest.fixture() +def pw_3d(pw_3d_read_only): + """Get precipitable water 3D data. It is safe to modify.""" + return pw_3d_read_only.copy() + + +@pytest.fixture() +def pw_time_read_only(): + """Get precipitable water 4D data varying in time. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/pw_time.nc") + + +@pytest.fixture() +def pw_time(pw_time_read_only): + """Get precipitable water 4D data varying in time. It is safe to modify.""" + return pw_time_read_only.copy() + + +@pytest.fixture() +def pw_member_read_only(): + """Get precipitable water 4D data varying in member. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/pw_member.nc") + + +@pytest.fixture() +def pw_member(pw_member_read_only): + """Get precipitable water 4D data varying in member. It is safe to modify.""" + return pw_member_read_only.copy() + + +@pytest.fixture() +def pw_5d_read_only(): + """Get precipitable water 5D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/pw_all.nc") + + +@pytest.fixture() +def pw_5d(pw_5d_read_only): + """Get precipitable water 5D data. It is safe to modify.""" + return pw_5d_read_only.copy() + + +@pytest.fixture() +def spw_3d_read_only(): + """Get saturation precipitable water 3D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/spw_basic.nc") + + +@pytest.fixture() +def spw_3d(spw_3d_read_only): + """Get saturation precipitable water 3D data. It is safe to modify.""" + return spw_3d_read_only.copy() + + +@pytest.fixture() +def spw_time_read_only(): + """Get saturation precipitable water 4D data varying in time. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/spw_time.nc") + + +@pytest.fixture() +def spw_time(spw_time_read_only): + """Get saturation precipitable water 4D data varying in time. It is safe to modify.""" + return spw_time_read_only.copy() + + +@pytest.fixture() +def spw_member_read_only(): + """Get saturation precipitable water 4D data varying in member. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/spw_member.nc") + + +@pytest.fixture() +def spw_member(spw_member_read_only): + """Get saturation precipitable water 4D data varying in member. It is safe to modify.""" + return spw_member_read_only.copy() + + +@pytest.fixture() +def spw_5d_read_only(): + """Get saturation precipitable water 5D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/spw_all.nc") + + +@pytest.fixture() +def spw_5d(spw_5d_read_only): + """Get saturation precipitable water 5D data. It is safe to modify.""" + return spw_5d_read_only.copy() + + +@pytest.fixture() +def sf_3d_read_only(): + """Get saturation fraction 3D data. It is NOT safe to modify.""" + return read.read_cube("tests/test_data/humidity/sf_basic.nc") + + +@pytest.fixture() +def sf_3d(sf_3d_read_only): + """Get saturation fraction 3D data. It is safe to modify.""" + return sf_3d_read_only.copy() From 161d0481d52af42945a73a51877402c004aaea31 Mon Sep 17 00:00:00 2001 From: daflack Date: Wed, 20 May 2026 09:28:29 +0100 Subject: [PATCH 09/10] Adds tests for saturation fraction, precipitable water and saturaiton precipitable water --- tests/operators/test_humidity.py | 145 +++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/tests/operators/test_humidity.py b/tests/operators/test_humidity.py index 9ff94d14d..c7e20eb49 100644 --- a/tests/operators/test_humidity.py +++ b/tests/operators/test_humidity.py @@ -722,3 +722,148 @@ def test_relative_humidity_from_specific_humidity_cubelist( ) for cube_a, cube_b in zip(expected_list, actual_cubelist, strict=True): assert np.allclose(cube_a.data, cube_b.data, rtol=1e-6, atol=1e-2) + + +def test_precipitable_water(mr_3d, pw_3d): + """Test calculation of precipitable water for 3D data.""" + assert np.allclose( + pw_3d.data, humidity.precipitable_water(mr_3d).data, rtol=1e-6, atol=1e-2 + ) + + +def test_precipitable_water_cubelist(mr_3d, pw_3d): + """Test calculation of precipitable water in a cubelist.""" + input_cube = iris.cube.CubeList([mr_3d, mr_3d]) + actual_cubelist = humidity.precipitable_water(input_cube) + expected_cubelist = iris.cube.CubeList([pw_3d, pw_3d]) + for cube_a, cube_b in zip(expected_cubelist, actual_cubelist, strict=True): + assert np.allclose(cube_a.data, cube_b.data, rtol=1e-6, atol=1e-2) + + +def test_precipitable_water_name(mr_3d): + """Test name of precipitable water cube.""" + assert humidity.precipitable_water(mr_3d).name() == "precipitable_water" + + +def test_precipitable_water_units(mr_3d): + """Test units of precipitable water cube.""" + assert humidity.precipitable_water(mr_3d).units == cf_units.Unit("mm") + + +def test_precipitable_water_time(mr_time, pw_time): + """Test precipitable water for cube varying in time.""" + assert np.allclose( + pw_time.data, humidity.precipitable_water(mr_time).data, rtol=1e-6, atol=1e-2 + ) + + +def test_precipitable_water_member(mr_member, pw_member): + """Test precipitable water for cube varying in member.""" + assert np.allclose( + pw_member.data, + humidity.precipitable_water(mr_member).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_precipitable_water_5d(mr_5d, pw_5d): + """Test precipitable water for 5D data.""" + assert np.allclose( + pw_5d.data, humidity.precipitable_water(mr_5d).data, rtol=1e-6, atol=1e-2 + ) + + +def test_saturation_precipitable_water(mr_3d, rh_3d, spw_3d): + """Test calculation of saturation precipitable water for 3D data.""" + assert np.allclose( + spw_3d.data, + humidity.saturation_precipitable_water(mr_3d, rh_3d).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_saturation_precipitable_water_cubelist(mr_3d, rh_3d, spw_3d): + """Test calculation of saturation precipitable water in a cubelist.""" + input_cube = iris.cube.CubeList([mr_3d, mr_3d]) + input_rh = iris.cube.CubeList([rh_3d, rh_3d]) + actual_cubelist = humidity.saturation_precipitable_water(input_cube, input_rh) + expected_cubelist = iris.cube.CubeList([spw_3d, spw_3d]) + for cube_a, cube_b in zip(expected_cubelist, actual_cubelist, strict=True): + assert np.allclose(cube_a.data, cube_b.data, rtol=1e-6, atol=1e-2) + + +def test_saturation_precipitable_water_name(mr_3d, rh_3d): + """Test name of saturation precipitable water cube.""" + assert ( + humidity.saturation_precipitable_water(mr_3d, rh_3d).name() + == "saturation_precipitable_water" + ) + + +def test_saturation_precipitable_water_units(mr_3d, rh_3d): + """Test units of saturation precipitable water cube.""" + assert humidity.saturation_precipitable_water(mr_3d, rh_3d).units == cf_units.Unit( + "mm" + ) + + +def test_saturation_precipitable_water_time(mr_time, rh_time, spw_time): + """Test saturation precipitable water for cube varying in time.""" + assert np.allclose( + spw_time.data, + humidity.saturation_precipitable_water(mr_time, rh_time).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_saturation_precipitable_water_member(mr_member, rh_member, spw_member): + """Test saturation precipitable water for cube varying in member.""" + assert np.allclose( + spw_member.data, + humidity.saturation_precipitable_water(mr_member, rh_member).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_saturation_precipitable_water_5d(mr_5d, rh_5d, spw_5d): + """Test saturation precipitable water for 5D data.""" + assert np.allclose( + spw_5d.data, + humidity.saturation_precipitable_water(mr_5d, rh_5d).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_saturation_fraction(mr_3d, rh_3d, sf_3d): + """Test calculation of saturation fraction for 3D data.""" + assert np.allclose( + sf_3d.data, + humidity.saturation_fraction(mr_3d, rh_3d).data, + rtol=1e-6, + atol=1e-2, + ) + + +def test_saturation_fraction_cubelist(mr_3d, rh_3d, sf_3d): + """Test calculation of saturation fraction in a cubelist.""" + input_cube = iris.cube.CubeList([mr_3d, mr_3d]) + input_rh = iris.cube.CubeList([rh_3d, rh_3d]) + actual_cubelist = humidity.saturation_fraction(input_cube, input_rh) + expected_cubelist = iris.cube.CubeList([sf_3d, sf_3d]) + for cube_a, cube_b in zip(expected_cubelist, actual_cubelist, strict=True): + assert np.allclose(cube_a.data, cube_b.data, rtol=1e-6, atol=1e-2) + + +def test_saturation_fraction_name(mr_3d, rh_3d): + """Test name of saturation fraction cube.""" + assert humidity.saturation_fraction(mr_3d, rh_3d).name() == "saturation_fraction" + + +def test_saturation_fraction_units(mr_3d, rh_3d): + """Test units of saturation fraction cube.""" + assert humidity.saturation_fraction(mr_3d, rh_3d).units == cf_units.Unit("1") From 3cf5bfebb2ee6b471d082479c7ef8125b82eed3e Mon Sep 17 00:00:00 2001 From: daflack Date: Wed, 20 May 2026 09:49:29 +0100 Subject: [PATCH 10/10] Update documentation around units --- src/CSET/operators/humidity.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/CSET/operators/humidity.py b/src/CSET/operators/humidity.py index 235d8ca05..ebd572b60 100644 --- a/src/CSET/operators/humidity.py +++ b/src/CSET/operators/humidity.py @@ -470,10 +470,11 @@ def precipitable_water( It can be calculated as - .. math:: pw = \int w dz + .. math:: pw = frac{1}{\rho_w} \int w dz - for pw the precipitable water, w the mixing ratio, and z the height. It is - integrated from the surface to the top of the atmosphere. + for pw the precipitable water, ..math::\rho_{w} the density of water, + w the mixing ratio, and z the height. It is integrated from the surface + to the top of the atmosphere. Generally, the precipitable water is widely applicable across the globe. It is likely that larger precipitation totals are associated with greater @@ -525,6 +526,7 @@ def precipitable_water( # Create the data array, rename, and correct units. pwat.data = pw pwat.rename("precipitable_water") + # Setting units to mm to account for normalization by density of water. pwat.units = "mm" precipitable_water.append(pwat) # Output the data. @@ -561,11 +563,11 @@ def saturation_precipitable_water( It can be calculated as - .. math:: spw = \int \frac{w}{RH} dz + .. math:: spw = frac{1}{\rho_w} \int \frac{w}{RH} dz - for spw the saturated precipitable water, w the mixing ratio, - RH the relative humidity (as a decimal) and z the height. It is - integrated from the surface to the top of the atmosphere. + for spw the saturated precipitable water, ..math::\rho_{w} the density of water, + w the mixing ratio, RH the relative humidity (as a decimal) and z the height. + It is integrated from the surface to the top of the atmosphere. It is applicable throughout the globe and is, perhaps, best considered in relation to the precipitable water. A useful way to do this is @@ -604,6 +606,7 @@ def saturation_precipitable_water( # Store the data for output, rename cube, and correct units. satpw.data = spw satpw.rename("saturation_precipitable_water") + # Setting units to mm to account for normalization by density of water. satpw.units = "mm" saturation_precipitable_water.append(satpw) # Output cube/cubelist.