From bab82871b229e2d07984c4d383ef8e4cd6aa38ea Mon Sep 17 00:00:00 2001 From: gavinevans Date: Fri, 14 Feb 2025 10:28:58 +0000 Subject: [PATCH 01/11] Avoid cell method duplication in ApplyEMOS (#2095) * Modify ApplyEMOS to avoid the potential duplication of cell methods, if probability forecasts are input with an existing cell method e.g. to describe a period diagnostic. * Remove whitespace. --- improver/calibration/ensemble_calibration.py | 6 +++--- .../ensemble_calibration/test_ApplyEMOS.py | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/improver/calibration/ensemble_calibration.py b/improver/calibration/ensemble_calibration.py index 2856f74ed0..78e204f9ee 100644 --- a/improver/calibration/ensemble_calibration.py +++ b/improver/calibration/ensemble_calibration.py @@ -1912,9 +1912,9 @@ def _format_forecast( random_seed=random_seed, ) - # Preserve cell methods from template. - for cm in template.cell_methods: - result.add_cell_method(cm) + # Preserve cell methods from template. + for cm in template.cell_methods: + result.add_cell_method(cm) return result diff --git a/improver_tests/calibration/ensemble_calibration/test_ApplyEMOS.py b/improver_tests/calibration/ensemble_calibration/test_ApplyEMOS.py index dcb731ca05..5d155c2f7e 100644 --- a/improver_tests/calibration/ensemble_calibration/test_ApplyEMOS.py +++ b/improver_tests/calibration/ensemble_calibration/test_ApplyEMOS.py @@ -484,6 +484,26 @@ def test_period_percentiles(self): self.assertAlmostEqual( np.mean(result.data), self.null_percentiles_expected_mean ) + self.assertEqual(len(result.cell_methods), 1) + self.assertEqual(result.cell_methods[0], cell_methods) + + def test_period_probabilities(self): + """Test that the desired cell methods are present on the calibrated forecasts, + without duplication of the cell methods, if cell methods are present on the + input probability forecast.""" + self.probabilities.coord("time").bounds = [ + int(self.probabilities.coord("time").points - 3600), + int(self.probabilities.coord("time").points), + ] + + cell_methods = CellMethod("maximum", coords="time") + self.probabilities.add_cell_method(cell_methods) + + result = ApplyEMOS()( + self.probabilities, self.coefficients, realizations_count=3 + ) + self.assertIn("probability", result.name()) + self.assertEqual(len(result.cell_methods), 1) self.assertEqual(result.cell_methods[0], cell_methods) def test_invalid_attribute(self): From 73bfa3fba4de8a50b214d53ba708d66590ec1493 Mon Sep 17 00:00:00 2001 From: Belinda Trotta Date: Tue, 18 Feb 2025 03:45:37 +1100 Subject: [PATCH 02/11] Update beta calibration docs (#2097) * Use np.interp instead of scipy interp1d * Revert "Use np.interp instead of scipy interp1d" This reverts commit 3ea6cd633deb9c535b0f2b875f9b31029d41e53f. * Fix docstring so that the extended documentation appears. * add more detail to the docs * fix spacing * change word --------- Co-authored-by: Belinda Trotta --- .../beta_recalibration/beta_recalibration.rst | 13 ++++++++++++- improver/calibration/beta_recalibration.py | 13 ++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/doc/source/extended_documentation/calibration/beta_recalibration/beta_recalibration.rst b/doc/source/extended_documentation/calibration/beta_recalibration/beta_recalibration.rst index b6b183f648..e5d1514761 100644 --- a/doc/source/extended_documentation/calibration/beta_recalibration/beta_recalibration.rst +++ b/doc/source/extended_documentation/calibration/beta_recalibration/beta_recalibration.rst @@ -7,8 +7,19 @@ calibrated (for example, by reliability calibration or by the Rainforests calibr that, when blending probabilistic forecasts, even if each input is perfectly calibrated, the output is in general not perfectly calibrated. The authors also show that applying a recalibration to the blended output improves its reliability, sharpness, and score on proper scoring metrics. IMPROVER implements the recalibration method studied in the -article, namely a transformation given by the cumulative distribution function of the beta distribution. +article, namely a transformation given by the cumulative distribution function of the beta distribution. This is a natural choice of transformation +function for probabilities because it is a monotone-increasing function that maps the interval `[0, 1]` onto itself. The implementation here allows the `alpha` and `beta` parameters of the beta distribution to vary by forecast period. +It is recommended that the `alpha` and `beta` parameters be chosen to optimise the desired metric (for example, the continuous rank +probability score) over some training period. Specifically, this could be done as follows: + +1. Obtain blended forecast output, along with ground truth data from observations or analyses, for the training period. +2. Implement the loss function. The loss function should have arguments `alpha` and `beta` and return the loss over the training period when the probabilistic forecast is transformed by the CDF of the beta distribution function. A suggested loss function is the CRPS calculated from the thresholded probability forecast. In this case, the loss function should transform the input blended probabilities by the CDF of the beta distribution (which is available in `scipy.stats`), then calculate the CRPS of the transformed probability forecast against the ground truth. +3. Use `scipy.optimize.minimize` to find the parameters `alpha` and `beta` that minimise the loss. + +Alternatively, one could jointly optimise the blending weights and the parameters of the beta calibration. This may yield better results, but +is more complex. + .. _Ranjan & Gneiting, 2008: https://stat.uw.edu/sites/default/files/files/reports/2008/tr543.pdf diff --git a/improver/calibration/beta_recalibration.py b/improver/calibration/beta_recalibration.py index aecc2cab0a..06665c40b4 100644 --- a/improver/calibration/beta_recalibration.py +++ b/improver/calibration/beta_recalibration.py @@ -2,7 +2,12 @@ # # This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. # See LICENSE in the root of the repository for full licensing details. -"""Module containing class for recalibrating blended probabilities.""" +"""Module containing class for recalibrating blended probabilities. + +.. See the documentation for a more detailed discussion of this plugin. +.. include:: extended_documentation/calibration/beta_recalibration/ + beta_recalibration.rst +""" from typing import Any, Dict @@ -15,12 +20,6 @@ from improver import PostProcessingPlugin from improver.metadata.probabilistic import is_probability -""" -.. See the documentation for a more detailed discussion of this plugin. -.. include:: extended_documentation/calibration/beta_recalibration/ - beta_recalibration.rst -""" - class BetaRecalibrate(PostProcessingPlugin): """Recalibrate probabilities using the cumulative distribution function From 660f8f42995bb99baf6f2a6ea6c2cfac32a6647c Mon Sep 17 00:00:00 2001 From: bayliffe Date: Tue, 18 Feb 2025 17:28:27 +0000 Subject: [PATCH 03/11] Addresses metadata issues for duration subdivision plugin (#2094) * Modify duration subdivision plugin to correctly recalculate the forecast periods. Modify the netcdf save code to explicitly exclude the inclusion of any duplicate cell methods. * Belt and braces test to demonstrate that slight variations in cell method properties means they would not be removed as duplicates. * Update improver/utilities/save.py Co-authored-by: gavinevans --------- Co-authored-by: gavinevans --- improver/utilities/save.py | 6 ++-- improver/utilities/temporal_interpolation.py | 11 +++++-- improver_tests/acceptance/SHA256SUMS | 6 ++-- .../utilities/test_DurationSubdivision.py | 19 +++++++++++- improver_tests/utilities/test_save.py | 29 +++++++++++++++++++ 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/improver/utilities/save.py b/improver/utilities/save.py index 30d50d7b15..d670fd0c42 100644 --- a/improver/utilities/save.py +++ b/improver/utilities/save.py @@ -19,13 +19,15 @@ def _order_cell_methods(cube: Cube) -> None: """ Sorts the cell methods on a cube such that if there are multiple methods they are always written in a consistent order in the output cube. The - input cube is modified. + input cube is modified. Ensure that if there are any identical duplicate + cell methods, only one of these is included in the outputs. Args: cube: The cube on which the cell methods are to be sorted. """ - cell_methods = tuple(sorted(cube.cell_methods)) + cell_methods = set(cube.cell_methods) + cell_methods = tuple(sorted(cell_methods)) cube.cell_methods = cell_methods diff --git a/improver/utilities/temporal_interpolation.py b/improver/utilities/temporal_interpolation.py index 84be605e03..0e97247121 100644 --- a/improver/utilities/temporal_interpolation.py +++ b/improver/utilities/temporal_interpolation.py @@ -16,6 +16,8 @@ from improver import BasePlugin from improver.metadata.constants import FLOAT_DTYPE from improver.metadata.constants.time_types import TIME_COORDS +from improver.metadata.forecast_times import unify_cycletime +from improver.metadata.utilities import enforce_time_point_standard from improver.utilities.complex_conversion import complex_to_deg, deg_to_complex from improver.utilities.cube_manipulation import MergeCubes from improver.utilities.round import round_close @@ -884,12 +886,15 @@ def construct_target_periods(self, fidelity_period_cube: Cube) -> Cube: ) components = fidelity_period_cube.extract(period_constraint) component_cube = components.collapsed("time", iris.analysis.SUM) - component_cube.coord("time").points = component_cube.coord("time").bounds[ - 0 - ][-1] + enforce_time_point_standard(component_cube) new_period_cubes.append(component_cube) start_time += interval + # The cycle times are already the same. This code will recalculate + # the forecasts periods relative to the cycletime for each of our + # extracted shorter duration cubes. + cycle_time = fidelity_period_cube.coord("forecast_reference_time").cell(0).point + new_period_cubes = unify_cycletime(new_period_cubes, cycle_time) return new_period_cubes.merge_cube() def process(self, cube: Cube) -> Cube: diff --git a/improver_tests/acceptance/SHA256SUMS b/improver_tests/acceptance/SHA256SUMS index e63f46c504..4aede2dc91 100644 --- a/improver_tests/acceptance/SHA256SUMS +++ b/improver_tests/acceptance/SHA256SUMS @@ -337,9 +337,9 @@ bf7e42be7897606682c3ecdaeb27bf3d3b6ab13a9a88b46c88ae6e92801c6245 ./create-grid- 55ba8a8ca8b5eee667d37fe8ec4a653caddea27f19ea290397428a487eb13ca0 ./cubelist-extract/input_cubelist.nc 33c7e0cf46ac62ead74ffde502ee28076a59550474fb3872c3e22083c4bd3cc3 ./cubelist-extract/kgo.nc 368f3c0c658d1155399ad4bdbfe0f98e0c65f5c53a49ece105bba3758012c0e8 ./duration-subdivision/input.nc -2c8c4972ae2dca29a05ac62f982cdd5727546c19a1699f4366416a12640ed2f8 ./duration-subdivision/kgo_daymask.nc -fc87547220adc1326af0e484a826d8950a7acc6e3c2d76ce63b7beea6133bf73 ./duration-subdivision/kgo_nightmask.nc -f2fd4c7884e50ab90f0d085a66d7b3b41c9bf09508481ceaa409a9025fde8386 ./duration-subdivision/kgo_nomask.nc +f56f65dca4c6887c422c23e10b63e034ed9d5388081dd2030d690c5f5be73fa4 ./duration-subdivision/kgo_daymask.nc +19388549f7a5f1bc616bb353d5a1380a2f26763cd3a223c0d3eabbc5fc4b389d ./duration-subdivision/kgo_nightmask.nc +8dc93f63957a89eb027b8552c1e923586ac8804f63e1d452a0cab31a9ea5cfc9 ./duration-subdivision/kgo_nomask.nc fe00aadb6854f44d765b40bb6608c23f4eb4f10193c96f43f207db0590739dab ./enforce-consistent-forecasts/double_bound_percentile_kgo.nc 51f9ff2c8e6cad54d04d6323c654ce25b812bea3ba6b0d85df21c19731c580fc ./enforce-consistent-forecasts/percentile_forecast.nc e210bf956dd3574eda3a64fdc3371ab16f85048ca120a3823e90d751d2325c46 ./enforce-consistent-forecasts/percentile_reference.nc diff --git a/improver_tests/utilities/test_DurationSubdivision.py b/improver_tests/utilities/test_DurationSubdivision.py index b942d0fa6d..844eec56c5 100644 --- a/improver_tests/utilities/test_DurationSubdivision.py +++ b/improver_tests/utilities/test_DurationSubdivision.py @@ -12,6 +12,7 @@ import numpy as np import pytest from iris.cube import Cube, CubeList +from numpy.testing import assert_array_equal from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube from improver.utilities.temporal_interpolation import DurationSubdivision @@ -371,7 +372,7 @@ def test_allocate_data(data_cube, kwargs, data, time, period, realizations): if not any([kwargs[key] for key in kwargs.keys() if "mask" in key]): # Check that summing over the time dimension returns the original data # if we've applied no masking. - np.testing.assert_array_equal(collapsed_rslice, data) + assert_array_equal(collapsed_rslice, data) # Without masking we can test that all the shorter durations are the # expected fraction of the total. for cslice in rslice.slices_over("time"): @@ -514,6 +515,22 @@ def test_construct_target_periods(kwargs, data, input_period, expected): assert bounds == kwargs["target_period"] assert cslice.coord("time").bounds[0][-1] == cslice.coord("time").points[0] + # Check forecast periods have been recalculated relative to time + # coordinate as expected. + expected_fp_lower = ( + cslice[0].coord("time").cell(0).bound[0] + - cslice[0].coord("forecast_reference_time").cell(0).point + ).total_seconds() + expected_fp_upper = ( + cslice[0].coord("time").cell(0).bound[-1] + - cslice[0].coord("forecast_reference_time").cell(0).point + ).total_seconds() + assert cslice.coord("forecast_period").points[0] == expected_fp_upper + assert_array_equal( + cslice.coord("forecast_period").bounds, + [[expected_fp_lower, expected_fp_upper]], + ) + # Check subdivided data is as expected. Also checks that shape is as # expected. np.testing.assert_array_almost_equal(result.data, expected) diff --git a/improver_tests/utilities/test_save.py b/improver_tests/utilities/test_save.py index 0e9f584b9c..5a840bccbf 100644 --- a/improver_tests/utilities/test_save.py +++ b/improver_tests/utilities/test_save.py @@ -4,6 +4,7 @@ # See LICENSE in the root of the repository for full licensing details. """Unit tests for saving functionality.""" +import copy import os import unittest from tempfile import mkdtemp @@ -294,6 +295,34 @@ def test_reordering_cube(self): # Test that they do match once sorting has occured. self.assertEqual(self.cube.cell_methods, self.cell_methods) + def test_duplicates_removed(self): + """Test that only one of any exact duplicate cell method is included + in the output.""" + cell_methods = self.cell_methods + self.cell_methods + self.cube.cell_methods = cell_methods + _order_cell_methods(self.cube) + self.assertEqual(self.cube.cell_methods, self.cell_methods) + + def test_inexact_duplicates_retained(self): + """Test that if cell_methods are almost duplicated, but one has an + additional property, e.g comment, both are retained. This test is + overkill as we are effectively testing the ability of the set command + to differentiate the cell methods, but it is included for + completeness.""" + + extra = ( + CellMethod( + method="maximum", + coords="time", + intervals="1 hour", + comments="I am unique and special", + ), + ) + cell_methods = tuple(sorted(self.cell_methods + (extra))) + self.cube.cell_methods = copy.copy(cell_methods) + _order_cell_methods(self.cube) + self.assertEqual(self.cube.cell_methods, cell_methods) + if __name__ == "__main__": unittest.main() From ba049c3b6c801caf4d5f938b35d77f9efe11742b Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 21 Feb 2025 07:34:01 +0000 Subject: [PATCH 04/11] Mobt 2066 convert forecast to climatological anomaly (#2072) * Recreated checksums * Added the requested functionality to convert a forecast to climatological anomaly, and appropriate tests. **Main Script** The structure of the new CalculateClimateAnomalies class follows that agreed with @brhooper. - Initialisation - Verification of inputs - Matching standard names - Matching units - Matching grids - Matching time coordinates - Calculation of anomalies: both unstandardised and standardised - Creation of output cube with appropriate metadata * Removed whitespace from files. * Resolved test coverage issues Codecov highlighted. * Resolved remaining test coverage issues. * Resolved formatting issues. * Partial commit. Switching branches to work on another ticket. Resolved prior requests from Ben Hooper following review. Only now require implementation of further metadata adjustments. * Implemented points from previous review, including compliance with CF conventions for anomalies. Metadata added for outputed cube: - Standard names suffixed with "anomaly" if mean cube provided or "standard anomaly" if variance cube also provided - Units set to None if variance cube used and copied from the provided diagnostic cube if no variance cube provided - "Reference epoch" scalar coordinate with (1) timebound from mean cube for anomaly cube, and (2) validity time from diagnostic cube - Cell method of "reference epoch: anomaly" * Resolved failure of pre-commmit checks due to comparing to 'None' using '==' and not 'is' * Resolved warning from pre-commit checks that 'expected_attributes' variable assigned but never used * Added new pytest tests for site data and replaced a test using 'pytest.mark.xfail' with 'pytest.raises(AssertionError)' to ensure the test fails for the correct reason. * Added unit test for raising an error when the time bounds of mean cube and variance cube do not match. * Resolved accidental reuse of a test name * Added missing unit test highlighted by codecov * Ensured all redundant comments removed and added a new test for when the user incorrectly sets 'standardised_anomaly' to false when instantiating and yet provides a variance cube * Increased readability of unit tests and resolved new test failures previously hidden behind faulty fixture setup * Adding the missing test file * Adjusted unit tests following review * Implemented unit test changes suggested in the latest first review * Standardised check that a reference epoch coordinate is present, which was different for one check * Fixed double hashes for comments and simplified testing of reference epoch metadata with Ben's function from last review * Removed references to the diagnostic_cube in the _add_reference_epoch_metadata function as this was mistakenly added * Resolved reformatting requests in previous review * Aimed to fix checksums issue and remove whitespace * Reverted mistaken changes to test_mathematical_operations.py * Changed all instances of 'standard anomaly' with 'standardized anomaly' to reflect requested cf-convention changes in previous review + fixed incorrect type annotations * Removed whitespace --- improver/utilities/mathematical_operations.py | 245 ++++++++++++ .../test_CalculateClimateAnomalies.py | 350 ++++++++++++++++++ 2 files changed, 595 insertions(+) create mode 100644 improver_tests/utilities/test_CalculateClimateAnomalies.py diff --git a/improver/utilities/mathematical_operations.py b/improver/utilities/mathematical_operations.py index b9f2398607..fed81c83f3 100644 --- a/improver/utilities/mathematical_operations.py +++ b/improver/utilities/mathematical_operations.py @@ -9,6 +9,7 @@ import iris import numpy as np import numpy.ma as ma +from iris.coords import CellMethod from iris.cube import Cube from numpy import ndarray @@ -17,6 +18,7 @@ create_new_diagnostic_cube, generate_mandatory_attributes, ) +from improver.utilities.cube_checker import spatial_coords_match from improver.utilities.cube_manipulation import ( enforce_coordinate_ordering, get_dim_coord_names, @@ -404,3 +406,246 @@ def fast_linear_fit( intercept = y_mean - grad * x_mean return grad, intercept + + +class CalculateClimateAnomalies(BasePlugin): + """Utility functionality to convert an input cube of data to a cube containing + anomaly data. If a forecast and a climatological mean are supplied, the anomaly + calculated will be forecast - mean. If the climatological standard deviation is + also supplied, then a standardized anomaly is calculated as + (forecast - mean) / standard deviation""" + + def __init__( + self, + ignore_temporal_mismatch: bool = False, + ) -> None: + """ + Initialise class. + + Args: + ignore_temporal_mismatch: + If True, ignore mismatch in time coordinates between + the input cubes. Default is False. Set to True when interested + in the anomaly of a diagnostic cube relative to mean and + standard deviation cubes of a different period. + """ + self.ignore_temporal_mismatch = ignore_temporal_mismatch + + @staticmethod + def verify_units_match( + diagnostic_cube: Cube, mean_cube: Cube, std_cube: Optional[Cube] = None + ) -> None: + """Check that all cubes have the same units. E.g. to prevent accidental + use of cubes with rate data with cubes with accumulation data.""" + errors = [] + if mean_cube.units != diagnostic_cube.units: + errors.append( + f"The mean cube must have the same units as the diagnostic cube." + f"The following units were found: {mean_cube.units}," + f"{diagnostic_cube.units}" + ) + if std_cube and std_cube.units != str(diagnostic_cube.units): + errors.append( + f"The standard deviation cube must have the same units " + "as the diagnostic cube." + f"The following units were found: {std_cube.units}," + f"{diagnostic_cube.units}" + ) + + if errors: + raise ValueError("\n".join(errors)) + + @staticmethod + def verify_spatial_coords_match( + diagnostic_cube: Cube, mean_cube: Cube, std_cube: Optional[Cube] = None + ) -> None: + """Check that all cubes have the same spatial coordinates (i.e. the same grid + coordinates or spot index coordinates).""" + cubes_to_check = [ + cube for cube in [diagnostic_cube, mean_cube, std_cube] if cube is not None + ] + # Check if spot_index coordinate present for some but not all cubes + spot_index_present = list( + set(["spot_index" in get_dim_coord_names(cube) for cube in cubes_to_check]) + ) + if len(spot_index_present) > 1: + raise ValueError( + "The cubes must all have the same spatial coordinates. Some cubes" + "contain spot_index coordinates and some do not." + ) + elif spot_index_present[0]: + if any( + not np.array_equal( + cube.coord("spot_index").points, + diagnostic_cube.coord("spot_index").points, + ) + for cube in cubes_to_check + ): + raise ValueError( + "Mismatching spot_index coordinates were found on the input cubes." + ) + elif not spatial_coords_match(cubes_to_check): + raise ValueError("The spatial coordinates must match.") + + def verify_time_coords_match( + self, diagnostic_cube: Cube, mean_cube: Cube, std_cube: Optional[Cube] = None + ) -> None: + """Check that all cubes have compatible time coordinates.""" + errors = [] + + # Check if mean and standard deviation cubes have the same bounds + if std_cube and not np.array_equal( + mean_cube.coord("time").bounds, std_cube.coord("time").bounds + ): + errors.append( + "The mean and standard deviation cubes must have compatible bounds. " + "The following bounds were found: " + f"mean_cube bounds: {mean_cube.coord('time').bounds}," + f"std_cube bounds: {std_cube.coord('time').bounds}" + ) + + # Check if diagnostic cube's time point falls within the bounds of the + # mean cube. + # The verification of the standard deviation cube's bounds suitably matching the + # diagnostic cube's is covered implicitly due to the above code chunk. + if not self.ignore_temporal_mismatch: + diagnostic_max = diagnostic_cube.coord("time").cell(-1).point + diagnostic_min = diagnostic_cube.coord("time").cell(0).point + mean_time_bounds = mean_cube.coord("time").bounds[0] + mean_time_bounds = [ + mean_cube.coord("time").units.num2date(bound) + for bound in mean_time_bounds + ] + if not ( + diagnostic_max <= mean_time_bounds[1] + and diagnostic_min >= mean_time_bounds[0] + ): + errors.append( + "The diagnostic cube's time points must fall within the bounds " + "of the mean cube. The following was found: " + f"diagnostic cube maximum: {diagnostic_max}," + f"diagnostic cube minimum: {diagnostic_min}," + f"mean cube upper bound: {mean_time_bounds[1]}," + f"mean cube lower bound:{mean_time_bounds[0]}" + ) + + if errors: + raise ValueError("\n".join(errors)) + + @staticmethod + def calculate_anomalies( + diagnostic_cube: Cube, mean_cube: Cube, std_cube: Optional[Cube] = None + ) -> ndarray: + """Calculate climate anomalies from the input cubes.""" + anomalies_data = diagnostic_cube.data - mean_cube.data + if std_cube: + anomalies_data = anomalies_data / std_cube.data + return anomalies_data + + @staticmethod + def _update_cube_name_and_units( + output_cube: Cube, standardized_anomaly: bool = False + ) -> None: + """This method updates the name and units of the given output cube based on + whether it represents a standardized anomaly or not. The cube is modified + in place. + + Args: + output_cube: + The cube to be updated. + standardized_anomaly: + Flag indicating if the output is a standardized anomaly. If True, + a "_standardized_anomaly" suffix is added to the name. + If False, an "_anomaly" suffix is added to the name. + """ + + # If standardized_anomaly is true, the output is a standardized anomaly + # and units are changed + suffix = "_standardized_anomaly" if standardized_anomaly else "_anomaly" + + try: + output_cube.standard_name = output_cube.standard_name + suffix + except ValueError: + output_cube.long_name = output_cube.standard_name + suffix + + @staticmethod + def _add_reference_epoch_metadata(output_cube: Cube, mean_cube: Cube) -> None: + """Add epoch metadata to describe the creation of the anomaly. + 1. Add a scalar coordinate 'reference epoch' to describe the time period over + which the climatology was calculated: + - timebound inherited from mean cube, + 2. Add a cell method called 'anomaly' to describe the operation + that was performed. + + The output_cube is modified in place. + """ + reference_epoch = iris.coords.AuxCoord( + points=mean_cube.coord("time").points, + bounds=mean_cube.coord("time").bounds + if mean_cube.coord("time").has_bounds() + else None, + long_name="reference_epoch", + units=mean_cube.coord("time").units, + ) + + output_cube.add_aux_coord(reference_epoch) + + cell_method = CellMethod(method="anomaly", coords=["reference_epoch"]) + output_cube.add_cell_method(cell_method) + + def _create_output_cube( + self, + diagnostic_cube: Cube, + mean_cube: Cube, + std_cube: Optional[Cube] = None, + ) -> Cube: + """ + Create a final anomalies cube by copying the diagnostic cube, updating its data + with the anomalies data, and updating its metadata. + """ + # Use the diagnostic cube as a template for the output cube + output_cube = diagnostic_cube.copy() + + # Update the cube name and units + standardized_anomaly = True if std_cube else False + self._update_cube_name_and_units(output_cube, standardized_anomaly) + + # Create the reference epoch coordinate and cell method + self._add_reference_epoch_metadata(output_cube, mean_cube) + + anomalies_data = self.calculate_anomalies(diagnostic_cube, mean_cube, std_cube) + + output_cube.data = anomalies_data + + return output_cube + + def process( + self, + diagnostic_cube: Cube, + mean_cube: Cube, + std_cube: Cube = None, + ) -> Cube: + """Calculate anomalies from the input cubes. + + Args: + diagnostic_cube: + Cube containing the data to be converted to anomalies. + mean_cube: + Cube containing the mean data to be used for the + calculation of anomalies. + std_cube: + Cube containing the standard deviation data to be used for the + calculation of standardized anomalies. If not provided, + only anomalies (not standardized anomalies) will be + calculated. + Returns: + Cube containing the result of the calculation with metadata reflecting + the operation. + """ + self.verify_units_match(diagnostic_cube, mean_cube, std_cube) + self.verify_spatial_coords_match(diagnostic_cube, mean_cube, std_cube) + self.verify_time_coords_match(diagnostic_cube, mean_cube, std_cube) + + anomalies_cube = self._create_output_cube(diagnostic_cube, mean_cube, std_cube) + + return anomalies_cube diff --git a/improver_tests/utilities/test_CalculateClimateAnomalies.py b/improver_tests/utilities/test_CalculateClimateAnomalies.py new file mode 100644 index 0000000000..d0cc2ac2d0 --- /dev/null +++ b/improver_tests/utilities/test_CalculateClimateAnomalies.py @@ -0,0 +1,350 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for the CalculateClimateAnomalies plugin within +mathematical_operations.py""" + +from datetime import datetime + +import iris +import numpy as np +import pytest +from iris.cube import CubeList + +from improver.constants import SECONDS_IN_HOUR +from improver.synthetic_data.set_up_test_cubes import ( + set_up_spot_variable_cube, + set_up_variable_cube, +) +from improver.utilities.mathematical_operations import ( + CalculateClimateAnomalies, +) + + +@pytest.fixture +def time_bounds(): + """Fixture for creating time bounds for a cube.""" + return {"mean_and_std": (datetime(2024, 9, 16, 0, 0), datetime(2024, 10, 16, 1, 0))} + + +@pytest.fixture +def validity_time(): + """Fixture for creating validity times for a cube.""" + return { + "diagnostic_basic": datetime(2024, 10, 16, 0, 0), + "diagnostic_multiple": [ + datetime(2024, 10, 15, 23, 0), + datetime(2024, 10, 16, 0, 0), + datetime(2024, 10, 16, 1, 0), + ], + "mean_and_std": datetime(2024, 10, 16, 0, 0), + } + + +@pytest.fixture +def forecast_reference_time(): + """Fixture for creating forecast reference times for a cube.""" + return datetime(2024, 10, 15, 18, 0) + + +@pytest.fixture +def diagnostic_cube(validity_time, forecast_reference_time): + """Fixture for creating a diagnostic cube""" + data = np.full((1, 1, 1), 305, dtype=np.float32) + return set_up_variable_cube( + data=data, time=validity_time["diagnostic_basic"], frt=forecast_reference_time + ) + + +@pytest.fixture +def diagnostic_cube_multiple_time_points(validity_time, forecast_reference_time): + """Fixture for creating a diagnostic cube with multiple time points""" + cubes = CubeList() + for time, value in zip(validity_time["diagnostic_multiple"], [300, 298, 296]): + data = np.full((1, 1), value, dtype=np.float32) + cubes.append( + set_up_variable_cube(data=data, time=time, frt=forecast_reference_time) + ) + cube = cubes.merge_cube() + return cube + + +@pytest.fixture +def mean_cube(validity_time, time_bounds): + """Fixture for creating a mean cube.""" + data = np.array([298], dtype=np.float32).reshape(1, 1, 1) + cube = set_up_variable_cube( + data=data, + time=validity_time["mean_and_std"], + time_bounds=time_bounds["mean_and_std"], + ) + cell_method = iris.coords.CellMethod(method="mean", coords="time") + cube.add_cell_method(cell_method) + return cube + + +@pytest.fixture +def std_cube(validity_time, time_bounds): + """Fixture for creating a std cube""" + data = np.array([4], dtype=np.float32).reshape(1, 1, 1) + cube = set_up_variable_cube( + data=data, + time=validity_time["mean_and_std"], + time_bounds=time_bounds["mean_and_std"], + units="K", + ) + cell_method = iris.coords.CellMethod(method="standard_deviation", coords="time") + cube.add_cell_method(cell_method) + return cube + + +@pytest.fixture +def site_cubes(validity_time, time_bounds, forecast_reference_time): + """Fixture for creating site cubes.""" + site_cube_diagnostic_data = np.array([305], dtype=np.float32) + site_cube_diagnostic = set_up_spot_variable_cube( + site_cube_diagnostic_data, + time=validity_time["diagnostic_basic"], + frt=forecast_reference_time, + ) + + site_cube_mean_data = np.array([298], dtype=np.float32) + site_cube_mean = set_up_spot_variable_cube( + site_cube_mean_data, + time=validity_time["mean_and_std"], + time_bounds=time_bounds["mean_and_std"], + ) + cell_method = iris.coords.CellMethod(method="mean", coords="time") + site_cube_mean.add_cell_method(cell_method) + + site_cube_std_data = np.array([4], dtype=np.float32) + site_cube_std = set_up_spot_variable_cube( + site_cube_std_data, + time=validity_time["mean_and_std"], + time_bounds=time_bounds["mean_and_std"], + units="K", + ) + cell_method = iris.coords.CellMethod(method="std", coords="time") + site_cube_std.add_cell_method(cell_method) + return site_cube_diagnostic, site_cube_mean, site_cube_std + + +def check_reference_epoch_coord(result_cube, reference_cube): + """Check that the reference_epoch coordinate on the result cube is as expected.""" + if "reference_epoch" not in [coord.name() for coord in result_cube.coords()]: + return False + else: + results = [] + results.append( + result_cube.coord("reference_epoch").points + == reference_cube.coord("time").points + ) + results.append( + np.array_equal( + result_cube.coord("reference_epoch").bounds, + reference_cube.coord("time").bounds, + ) + ) + results.append( + result_cube.coord("reference_epoch").units + == reference_cube.coord("time").units + ) + return all(results) + + +# Testing the plugin's basic functionality +@pytest.mark.parametrize( + "fixture_name", ["diagnostic_cube", "diagnostic_cube_multiple_time_points"] +) +def test_calculate_unstandardized_anomalies_gridded_data( + request, fixture_name, mean_cube +): + """Test that the plugin calculates unstandardized anomalies correctly.""" + + diagnostic_cube = request.getfixturevalue(fixture_name) + plugin = CalculateClimateAnomalies() + if fixture_name == "diagnostic_cube": + expected_anomalies = np.array([7], dtype=np.float32).reshape(1, 1, 1) + elif fixture_name == "diagnostic_cube_multiple_time_points": + expected_anomalies = np.array([2, 0, -2], dtype=np.float32).reshape(3, 1, 1) + result = plugin.process(diagnostic_cube, mean_cube) + np.testing.assert_allclose(result.data, expected_anomalies, rtol=1e-5) + assert result.name() == diagnostic_cube.name() + "_anomaly" + assert result.units == "K" + assert check_reference_epoch_coord(result, mean_cube) + assert ( + "(CellMethod(method='anomaly', coord_names=('reference_epoch',), " + "intervals=(), comments=())" in str(result.cell_methods) + ) + + +@pytest.mark.parametrize( + "fixture_name", ["diagnostic_cube", "diagnostic_cube_multiple_time_points"] +) +def test_calculate_standardized_anomalies_gridded_data( + request, fixture_name, mean_cube, std_cube +): + """Test that the plugin returns a cube with expected data.""" + diagnostic_cube = request.getfixturevalue(fixture_name) + plugin = CalculateClimateAnomalies() + if fixture_name == "diagnostic_cube": + expected_anomalies = np.array([1.75], dtype=np.float32).reshape(1, 1, 1) + elif fixture_name == "diagnostic_cube_multiple_time_points": + expected_anomalies = np.array([0.5, 0.0, -0.5], dtype=np.float32).reshape( + 3, 1, 1 + ) + result = plugin.process(diagnostic_cube, mean_cube, std_cube) + np.testing.assert_allclose(result.data, expected_anomalies, rtol=1e-5) + assert result.long_name == diagnostic_cube.name() + "_standardized_anomaly" + assert result.units == "K" + assert check_reference_epoch_coord(result, mean_cube) + assert ( + "(CellMethod(method='anomaly', coord_names=('reference_epoch',), " + "intervals=(), comments=())" in str(result.cell_methods) + ) + + +def test_calculate_unstandardized_anomalies_site_data(site_cubes): + """Test that the plugin calculates unstandardized anomalies correctly + for site data.""" + plugin = CalculateClimateAnomalies() + site_cube_diagnostic, site_cube_mean, _ = site_cubes + result = plugin.process(site_cube_diagnostic, site_cube_mean) + expected_anomalies = np.array([7.0], dtype=np.float32) + np.testing.assert_allclose(result.data, expected_anomalies, rtol=1e-5) + assert result.name() == site_cube_diagnostic.name() + "_anomaly" + assert result.units == "K" + assert check_reference_epoch_coord(result, site_cube_mean) + assert ( + "(CellMethod(method='anomaly', coord_names=('reference_epoch',), " + "intervals=(), comments=())" in str(result.cell_methods) + ) + + +def test_calculate_standardized_anomalies_site_data(site_cubes): + """Test that the plugin calculates standardized anomalies correctly + for site data.""" + plugin = CalculateClimateAnomalies() + site_cube_diagnostic, site_cube_mean, site_cube_std = site_cubes + result = plugin.process(site_cube_diagnostic, site_cube_mean, site_cube_std) + expected_anomalies = np.array([1.75], dtype=np.float32) + np.testing.assert_allclose(result.data, expected_anomalies, rtol=1e-5) + assert result.long_name == site_cube_diagnostic.name() + "_standardized_anomaly" + assert result.units == "K" + assert check_reference_epoch_coord(result, site_cube_mean) + assert ( + "(CellMethod(method='anomaly', coord_names=('reference_epoch',), " + "intervals=(), comments=())" in str(result.cell_methods) + ) + + +def test_ignore_temporal_mismatch(diagnostic_cube, mean_cube, std_cube): + """Test that the plugin handles requests to ignore temporal mismatch + between diagnostic cube and mean/std cube. + """ + diagnostic_cube.coord("time").points = ( + diagnostic_cube.coord("time").points + 10 * SECONDS_IN_HOUR + ) # Moves diagnostic bounds outside mean bounds + + plugin = CalculateClimateAnomalies(ignore_temporal_mismatch=True) + result = plugin.process(diagnostic_cube, mean_cube, std_cube) + + assert result.long_name == diagnostic_cube.name() + "_standardized_anomaly" + assert result.units == "K" + assert check_reference_epoch_coord(result, mean_cube) + assert ( + "(CellMethod(method='anomaly', coord_names=('reference_epoch',), " + "intervals=(), comments=())" in str(result.cell_methods) + ) + + +# Testing the plugin's internal verification checks +@pytest.mark.parametrize("error_to_check", ["mean_check", "std_check"]) +def test_error_units_mismatch(diagnostic_cube, mean_cube, std_cube, error_to_check): + """Test that the plugin raises a ValueError if the units of the diagnostic and + another cube mismatch""" + plugin = CalculateClimateAnomalies() + if error_to_check == "mean_check": + mean_cube.units = "C" # The units should be K ordinarily + with pytest.raises( + ValueError, + match="The mean cube must have the same units as the diagnostic cube.", + ): + plugin.verify_units_match(diagnostic_cube, mean_cube, std_cube=None) + else: + std_cube.units = "C" # The units should be K ordinarily + with pytest.raises( + ValueError, match="The standard deviation cube must have the same units " + ): + plugin.verify_units_match(diagnostic_cube, mean_cube, std_cube) + + +def test_error_spatial_coords_mismatch_gridded_data( + diagnostic_cube, mean_cube, std_cube +): + """Test that the plugin raises a ValueError if the spatial coordinates of the + diagnostic cube and another cube mismatch""" + mean_cube.coord("latitude").points = mean_cube.coord("latitude").points + 20 + mean_cube.coord("longitude").points = mean_cube.coord("longitude").points + 20 + plugin = CalculateClimateAnomalies() + with pytest.raises(ValueError, match="The spatial coordinates must match."): + plugin.verify_spatial_coords_match(diagnostic_cube, mean_cube, std_cube) + + +@pytest.mark.parametrize( + "error_to_raise", ["more_than_one_spot_index", "mismatching_spot_index_points"] +) +def test_error_spatial_coords_mismatch_site_data(site_cubes, mean_cube, error_to_raise): + site_cube_diagnostic, site_cube_mean, site_cube_std = site_cubes + """Test that the plugin raises a ValueError if the spatial coordinates of the + mean and std cubes have different bounds.""" + plugin = CalculateClimateAnomalies() + if error_to_raise == "more_than_one_spot_index": + # Uses a cube without the spot_index coordinate (gridded mean_cube) + # to trigger the error + with pytest.raises( + ValueError, + match="The cubes must all have the same spatial coordinates. Some cubes" + "contain spot_index coordinates and some do not.", + ): + plugin.verify_spatial_coords_match( + site_cube_diagnostic, mean_cube, site_cube_std + ) + else: + site_cube_diagnostic.coord("spot_index").points = ( + site_cube_diagnostic.coord("spot_index").points + 1 + ) + with pytest.raises( + ValueError, + match="Mismatching spot_index coordinates were found on the input cubes.", + ): + plugin.verify_spatial_coords_match( + site_cube_diagnostic, site_cube_mean, site_cube_std + ) + + +@pytest.mark.parametrize("check", ["mean_to_std_check", "diagnostic_to_others_check"]) +def test_error_time_coords_mismatch(diagnostic_cube, mean_cube, std_cube, check): + plugin = CalculateClimateAnomalies(ignore_temporal_mismatch=False) + if check == "mean_to_std_check": + mean_cube.coord("time").bounds = mean_cube.coord("time").bounds + ( + 1 * SECONDS_IN_HOUR, + 1 * SECONDS_IN_HOUR, + ) # Moves mean bounds outside std bounds + with pytest.raises( + ValueError, + match="The mean and standard deviation cubes must have compatible bounds. " + "The following bounds were found: ", + ): + plugin.verify_time_coords_match(diagnostic_cube, mean_cube, std_cube) + else: + diagnostic_cube.coord("time").points = ( + diagnostic_cube.coord("time").points + 10 * SECONDS_IN_HOUR + ) # Moves diagnostic bounds outside mean bounds + with pytest.raises( + ValueError, + match="The diagnostic cube's time points must fall within the bounds " + "of the mean cube. The following was found:", + ): + plugin.verify_time_coords_match(diagnostic_cube, mean_cube, std_cube) From 9d11aa6764a57290ca2bde11fadd5aaba595eb73 Mon Sep 17 00:00:00 2001 From: lambert-p Date: Tue, 4 Mar 2025 09:12:33 +0000 Subject: [PATCH 05/11] Mobt 812 vera threshold interpolation (#2079) * Threshold interpolation plugin and cli * changes made after PR comments * add unit tests for threshold interpolation plugin * plugin changes and completed iris unit tests * Converted unit tests from iris test to pytest. * acceptance test * added acceptance tests and tidied up * formatting plugin * rewritten as a class * commit for rebase * commit to run suite * first review changes complete * pre-commit checks done * Update improver_tests/utilities/test_threshold_interpolation.py Co-authored-by: bayliffe * wip * second review changes * pre-commit tests done * kgos recreated, unit test added and small changes * test fixed and file renamed * check sums sorted * checksums updated --------- Co-authored-by: Katharine Hurst Co-authored-by: bayliffe --- improver/cli/threshold_interpolation.py | 36 +++ improver/utilities/threshold_interpolation.py | 213 ++++++++++++++++++ improver_tests/acceptance/SHA256SUMS | 6 + .../test_threshold_interpolation.py | 52 +++++ .../utilities/test_ThresholdInterpolation.py | 159 +++++++++++++ 5 files changed, 466 insertions(+) create mode 100755 improver/cli/threshold_interpolation.py create mode 100644 improver/utilities/threshold_interpolation.py create mode 100644 improver_tests/acceptance/test_threshold_interpolation.py create mode 100644 improver_tests/utilities/test_ThresholdInterpolation.py diff --git a/improver/cli/threshold_interpolation.py b/improver/cli/threshold_interpolation.py new file mode 100755 index 0000000000..5d012feb04 --- /dev/null +++ b/improver/cli/threshold_interpolation.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Script to run the threshold interpolation plugin.""" + +from improver import cli + + +@cli.clizefy +@cli.with_output +def process( + forecast_at_thresholds: cli.inputcube, + *, + thresholds: cli.comma_separated_list, +): + """ + Use this CLI to modify the probability thresholds in an existing probability + forecast cube by linearly interpolating between the existing thresholds. + + Args: + forecast_at_thresholds: + Cube expected to contain a threshold coordinate. + thresholds: + List of the desired output thresholds. + + Returns: + Cube with forecast values at the desired set of thresholds. + The threshold coordinate is always the zeroth dimension. + """ + from improver.utilities.threshold_interpolation import ThresholdInterpolation + + result = ThresholdInterpolation(thresholds)(forecast_at_thresholds) + + return result diff --git a/improver/utilities/threshold_interpolation.py b/improver/utilities/threshold_interpolation.py new file mode 100644 index 0000000000..3ffc6a1f34 --- /dev/null +++ b/improver/utilities/threshold_interpolation.py @@ -0,0 +1,213 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Script to linearly interpolate thresholds""" + +from typing import List, Optional + +import iris +import numpy as np +from iris.cube import Cube +from numpy import ndarray + +from improver import PostProcessingPlugin +from improver.calibration.utilities import convert_cube_data_to_2d +from improver.ensemble_copula_coupling.utilities import ( + interpolate_multiple_rows_same_x, + restore_non_percentile_dimensions, +) +from improver.metadata.probabilistic import ( + find_threshold_coordinate, +) +from improver.utilities.cube_manipulation import ( + collapse_realizations, + enforce_coordinate_ordering, +) + + +class ThresholdInterpolation(PostProcessingPlugin): + def __init__(self, thresholds: List[float]): + """ + Args: + thresholds: + List of the desired output thresholds. + + Raises: + ValueError: + If the thresholds list is empty. + """ + if not thresholds: + raise ValueError("The thresholds list cannot be empty.") + self.thresholds = thresholds + self.threshold_coord = None + + def mask_checking(self, forecast_at_thresholds: Cube) -> Optional[np.ndarray]: + """ + Check if the mask is consistent across different slices of the threshold coordinate. + + Args: + forecast_at_thresholds: + The input cube containing forecast data with a threshold coordinate. + + Returns: + original_mask: + The original mask if the data is masked and the mask is consistent across + different slices of the threshold coordinate, otherwise None. + + Raises: + ValueError: If the mask varies across different slices of the threshold coordinate. + """ + original_mask = None + if np.ma.is_masked(forecast_at_thresholds.data): + (crd_dim,) = forecast_at_thresholds.coord_dims(self.threshold_coord.name()) + if np.diff(forecast_at_thresholds.data.mask, axis=crd_dim).any(): + raise ValueError( + f"The mask is expected to be constant across different slices of the {self.threshold_coord.name()}" + f" dimension, however, in the dataset provided, the mask varies across the {self.threshold_coord.name()}" + f" dimension. This is not currently supported." + ) + else: + original_mask = next( + forecast_at_thresholds.slices_over(self.threshold_coord.name()) + ).data.mask + + return original_mask + + def _interpolate_thresholds( + self, + forecast_at_thresholds: Cube, + ) -> np.ndarray: + """ + Interpolate forecast data to a new set of thresholds. + + This method performs linear interpolation of forecast data from an initial + set of thresholds to a new set of thresholds. The interpolation is done + by converting the data to a 2D array, performing the interpolation, and + then restoring the original dimensions. + + Args: + forecast_at_thresholds: + Cube containing forecast data with a threshold coordinate. + + Returns: + ndarray: + Interpolated forecast data with the new set of thresholds. + """ + original_thresholds = self.threshold_coord.points + + # Ensure that the threshold dimension is first, so that the + # conversion to a 2d array produces data in the desired order. + enforce_coordinate_ordering(forecast_at_thresholds, self.threshold_coord.name()) + forecast_at_reshaped_thresholds = convert_cube_data_to_2d( + forecast_at_thresholds, coord=self.threshold_coord.name() + ) + + forecast_at_interpolated_thresholds = interpolate_multiple_rows_same_x( + np.array(self.thresholds, dtype=np.float64), + original_thresholds.astype(np.float64), + forecast_at_reshaped_thresholds.astype(np.float64), + ) + + forecast_at_interpolated_thresholds = np.transpose( + forecast_at_interpolated_thresholds + ) + + # Restore the original dimensions of the interpolated forecast data. + forecast_at_thresholds_data = restore_non_percentile_dimensions( + forecast_at_interpolated_thresholds, + next(forecast_at_thresholds.slices_over(self.threshold_coord.name())), + len(self.thresholds), + ) + + return forecast_at_thresholds_data + + def create_cube_with_thresholds( + self, + forecast_at_thresholds: Cube, + cube_data: ndarray, + ) -> Cube: + """ + Create a cube with a threshold coordinate based on a template cube extracted + by slicing over the threshold coordinate. + + The resulting cube will have an extra threshold coordinate compared with + the template cube. The shape of the cube_data should be the shape of the + desired output cube. + + Args: + forecast_at_thresholds: + Cube containing forecast data with a threshold coordinate. + cube_data: + Array containing the interpolated forecast data with the new thresholds. + + Returns: + Cube containing the new threshold coordinate and the interpolated data. + """ + template_cube = next( + forecast_at_thresholds.slices_over(self.threshold_coord.name()) + ) + template_cube.remove_coord(self.threshold_coord) + + # create cube with new threshold dimension + cubes = iris.cube.CubeList([]) + for point in self.thresholds: + cube = template_cube.copy() + coord = iris.coords.DimCoord( + np.array([point], dtype="float32"), units=self.threshold_coord.units + ) + coord.rename(self.threshold_coord.name()) + coord.var_name = "threshold" + coord.attributes = self.threshold_coord.attributes + cube.add_aux_coord(coord) + cubes.append(cube) + result = cubes.merge_cube() + # replace data + result.data = cube_data + return result + + def process( + self, + forecast_at_thresholds: Cube, + ) -> Cube: + """ + Process the input cube to interpolate forecast data to a new set of thresholds. + + This method performs the following steps: + 1. Identifies the threshold coordinate in the input cube. + 2. Checks if the mask is consistent across different slices of the threshold coordinate. + 3. Collapses the realizations if present. + 4. Interpolates the forecast data to the new set of thresholds. + 5. Creates a new cube with the interpolated threshold data. + 6. Applies the original mask to the new cube if it exists. + + Args: + forecast_at_thresholds: + Cube expected to contain a threshold coordinate. + + Returns: + Cube: + Cube with forecast values at the desired set of thresholds. + The threshold coordinate is always the zeroth dimension. + """ + self.threshold_coord = find_threshold_coordinate(forecast_at_thresholds) + + original_mask = self.mask_checking(forecast_at_thresholds) + + if forecast_at_thresholds.coords("realization"): + forecast_at_thresholds = collapse_realizations(forecast_at_thresholds) + + forecast_at_thresholds_data = self._interpolate_thresholds( + forecast_at_thresholds, + ) + threshold_cube = self.create_cube_with_thresholds( + forecast_at_thresholds, + forecast_at_thresholds_data, + ) + if original_mask is not None: + original_mask = np.broadcast_to(original_mask, threshold_cube.shape) + threshold_cube.data = np.ma.MaskedArray( + threshold_cube.data, mask=original_mask + ) + + return threshold_cube diff --git a/improver_tests/acceptance/SHA256SUMS b/improver_tests/acceptance/SHA256SUMS index 4aede2dc91..87a213a3cb 100644 --- a/improver_tests/acceptance/SHA256SUMS +++ b/improver_tests/acceptance/SHA256SUMS @@ -881,6 +881,12 @@ eb6f7c3f646c4c51a0964b9a19367f43d6e3762ff5523b982cfaf7bf2610f091 ./temporal-int e3b8f51a0be52c4fead55f95c0e3da29ee3d93f92deed26314e60ad43e8fd5ef ./temporal-interpolate/uv/20181220T1200Z-PT0024H00M-uv_index.nc b3fde693b3a8e144cb8f9ee9ff23c51ef92701858667cff850b2a49986bacaab ./temporal-interpolate/uv/kgo_t1.nc 1065ae1f25e6bc6df8d02e61c1f8ef92ab3dae679595d5165bd94d9c740adb2c ./temporal-interpolate/uv/kgo_t1_daynight.nc +3335761a3c15c0fd4336cb852970376abd6f6dac99907fe9b081e6a7672e530c ./threshold-interpolation/extra_thresholds_kgo.nc +022657626d7ae4608781c390ca9c30d9cbb949d71bedf74a2687228f5964b3e9 ./threshold-interpolation/input.nc +12acca08e123437e07ad4e3aab81cc2fc0a3cfb72b5cb2fd06343bd5beb13f00 ./threshold-interpolation/input_realization.nc +7b172ce0d98c0f7fbfea1cde23a126d7116871bb62a221348c7ddddc35c29a0a ./threshold-interpolation/masked_cube_kgo.nc +ec73679ff5e308a2bb4d21283262118f8d9fbb6a425309b76d5865a97a773c40 ./threshold-interpolation/masked_input.nc +6058009963941b539117ea44792277253d87c7a1c81318e4836406b5c0b88525 ./threshold-interpolation/realization_collapse_kgo.nc ac93ed67c9947547e5879af6faaa329fede18afd822c720ac3afcb18fa41077a ./threshold/basic/input.nc eb3fdc9400401ec47d95961553aed452abcbd91891d0fbca106b3a05131adaa9 ./threshold/basic/kgo.nc 6b50fa16b663869b3e3fbff36197603886ff7383b2df2a8ba92579bcc9461a16 ./threshold/below_threshold/kgo.nc diff --git a/improver_tests/acceptance/test_threshold_interpolation.py b/improver_tests/acceptance/test_threshold_interpolation.py new file mode 100644 index 0000000000..704dae9219 --- /dev/null +++ b/improver_tests/acceptance/test_threshold_interpolation.py @@ -0,0 +1,52 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Test for the threshold interpolation CLI.""" + +import pytest + +from . import acceptance as acc + +pytestmark = [pytest.mark.acc, acc.skip_if_kgo_missing] +CLI = acc.cli_name_with_dashes(__file__) +run_cli = acc.run_cli(CLI) + + +def test_basic(tmp_path): + """Test basic invocation with threshold argument""" + thresholds = "50.0,200.0,400.0,600.0,1000.0,2000.0,10000.0,25000.0,40000.0" + kgo_dir = acc.kgo_root() / "threshold-interpolation" + kgo_path = kgo_dir / "extra_thresholds_kgo.nc" + input_path = kgo_dir / "input.nc" + output_path = tmp_path / "output.nc" + args = [input_path, "--thresholds", thresholds, "--output", f"{output_path}"] + + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_realization_collapse(tmp_path): + """Test realization coordinate is collapsed""" + thresholds = "50.0,200.0,400.0,600.0,1000.0,2000.0,10000.0,25000.0,40000.0" + kgo_dir = acc.kgo_root() / "threshold-interpolation" + kgo_path = kgo_dir / "realization_collapse_kgo.nc" + input_path = kgo_dir / "input_realization.nc" + output_path = tmp_path / "output.nc" + args = [input_path, "--thresholds", thresholds, "--output", f"{output_path}"] + + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_masked_cube(tmp_path): + """Test masked cube""" + thresholds = "50.0,200.0,400.0,600.0,1000.0,2000.0,10000.0,25000.0,40000.0" + kgo_dir = acc.kgo_root() / "threshold-interpolation" + kgo_path = kgo_dir / "masked_cube_kgo.nc" + input_path = kgo_dir / "masked_input.nc" + output_path = tmp_path / "output.nc" + args = [input_path, "--thresholds", thresholds, "--output", f"{output_path}"] + + run_cli(args) + acc.compare(output_path, kgo_path) diff --git a/improver_tests/utilities/test_ThresholdInterpolation.py b/improver_tests/utilities/test_ThresholdInterpolation.py new file mode 100644 index 0000000000..a4edfef0bc --- /dev/null +++ b/improver_tests/utilities/test_ThresholdInterpolation.py @@ -0,0 +1,159 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +""" +Unit tests for the +`ensemble_copula_coupling.EnsembleCopulaCouplingUtilities` class. +""" + +import iris +import numpy as np +import pytest +from iris.cube import Cube + +from improver.synthetic_data.set_up_test_cubes import ( + set_up_probability_cube, +) +from improver.utilities.threshold_interpolation import ThresholdInterpolation + + +def basic_input_cube() -> Cube: + """Set up input cube with sparse thresholds.""" + data = np.array( + [ + [[1.0, 0.9, 1.0], [0.8, 0.9, 0.5], [0.5, 0.2, 0.0]], + [[1.0, 0.5, 1.0], [0.5, 0.5, 0.3], [0.2, 0.0, 0.0]], + [[1.0, 0.2, 0.5], [0.2, 0.0, 0.1], [0.0, 0.0, 0.0]], + ], + dtype=np.float32, + ) + + input_cube = set_up_probability_cube( + data, + thresholds=[100, 200, 300], + variable_name="visibility_in_air", + threshold_units="m", + spp__relative_to_threshold="less_than", + ) + return input_cube + + +@pytest.fixture +def input_cube() -> Cube: + """Return an input cube with sparse thresholds.""" + return basic_input_cube() + + +@pytest.fixture +def masked_cube() -> Cube: + """Set up a masked cube which is consistent for every threshold.""" + masked_cube = basic_input_cube() + + mask = np.array( + [ + [[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], + [[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], + [[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], + ], + dtype=np.int8, + ) + masked_cube.data = np.ma.masked_array(masked_cube.data, mask=mask) + return masked_cube + + +@pytest.mark.parametrize("input", ["input_cube", "masked_cube"]) +def test_cube_returned(request, input): + """ + Test that the plugin returns an Iris.cube.Cube with suitable units. + """ + cube = request.getfixturevalue(input) + thresholds = [100, 150, 200, 250, 300] + result = ThresholdInterpolation(thresholds)(cube) + assert isinstance(result, Cube) + assert result.units == cube.units + + +@pytest.mark.parametrize("input", ["input_cube", "masked_cube"]) +def test_interpolated_values(request, input): + """ + Test that the interpolated values are as expected. + """ + cube = request.getfixturevalue(input) + thresholds = [100, 150, 200, 250, 300] + result = ThresholdInterpolation(thresholds)(cube) + expected_interpolated_values = np.array( + [ + [[1.0, 0.9, 1.0], [0.8, 0.9, 0.5], [0.5, 0.2, 0.0]], + [[1.0, 0.7, 1.0], [0.65, 0.7, 0.4], [0.35, 0.1, 0.0]], + [[1.0, 0.5, 1.0], [0.5, 0.5, 0.3], [0.2, 0.0, 0.0]], + [[1.0, 0.35, 0.75], [0.35, 0.25, 0.2], [0.1, 0.0, 0.0]], + [[1.0, 0.2, 0.5], [0.2, 0.0, 0.1], [0.0, 0.0, 0.0]], + ], + dtype=np.float32, + ) + np.testing.assert_array_equal(result.data, expected_interpolated_values) + + +def test_empty_threshold_list(): + """ + Test that a ValueError is raised if the threshold list is empty. + """ + with pytest.raises(ValueError, match="The thresholds list cannot be empty."): + ThresholdInterpolation([]) + + +def test_metadata_copy(input_cube): + """ + Test that the metadata dictionaries within the input cube are + also present on the output cube. + """ + input_cube.attributes = {"source": "ukv"} + thresholds = [100, 150, 200, 250, 300] + result = ThresholdInterpolation(thresholds)(input_cube) + assert input_cube.metadata._asdict() == result.metadata._asdict() + + +def test_thresholds_different_mask(masked_cube): + """ + Testing that a value error message is raised if masks are different across thresholds. + """ + masked_cube.data.mask[0, 0, 0] = True + thresholds = [100, 150, 200, 250, 300] + error_msg = "The mask is expected to be constant across different slices of the" + + with pytest.raises(ValueError, match=error_msg): + ThresholdInterpolation(thresholds)(masked_cube) + + +def test_mask_consistency(masked_cube): + """ + Test that the mask is the same before and after ThresholdInterpolation. + """ + thresholds = [100, 150, 200, 250, 300] + original_mask = masked_cube.data.mask.copy() + result = ThresholdInterpolation(thresholds)(masked_cube).data.mask + np.testing.assert_array_equal(original_mask[0], result[0]) + + +def test_collapse_realizations(input_cube): + """ + Test that the realizations are collapsed if present in the input cube. + """ + cubes = iris.cube.CubeList([]) + for realization in range(2): + cube = input_cube.copy(data=input_cube.data / (realization + 1)) + cube.add_aux_coord( + iris.coords.AuxCoord( + realization, + long_name="realization", + units="1", + ) + ) + cubes.append(cube) + + cube = cubes.merge_cube() + + thresholds = [100, 150, 200, 250, 300] + result = ThresholdInterpolation(thresholds)(cube.copy())[0::2] + np.testing.assert_array_equal(result.data, 0.5 * (cube[0].data + cube[1].data)) From f3445d2f4e15fc9cb1e2bb07076ef37809cc33e1 Mon Sep 17 00:00:00 2001 From: bayliffe Date: Fri, 7 Mar 2025 10:08:58 +0000 Subject: [PATCH 06/11] MOBT-798: Multi-diagnostic classification for precipitation (#2092) * Start of a precip duration plugin. * Precipitation duration plugin fundamentally working. * Add generic time collapse function that sets the correct time and forecast period coordinate points as per the improver metadata. * Precipitation classification plugin in a working and tidied state. Still needs unit tests, a CLI, and associated acceptance tests. * Add a CLI for precipitation duration. * Add unit tests for collapse_time function. * Improve legibility of collapse_time unit tests. * Starting to add unit tests. * Tests and fixes. * More unit tests and tweaks to plugin. * Test for collapse_time function demonstrating behaviour when a cube with a single values time coordinate is passed in. * Add unit test covering multi-realization data. * Style fixes. * Add acceptance tests. * Additional acceptance test. * Update checksums. * Style fixes. * Modify one parameterised test and remove basic test. * Modify time collapse function to use enforce_time_point_standard utility from metadata/utilities. * Modify CLI to accept comma separated list of inputs to allow for multiple thresholds. Ensure string types, which may be supplied by the CLI are handled by the plugin. A new acceptance test and unit tests for multiple thresholds. * Remove var_names from threshold coordinates in final output. * Make probs into bool type before multiplying to reduce memory usage. May need to make add a test that inputs are bool otherwise this is a horrible potential bug for someone. * Add model id attribute option to the precipitation duration CLI and plugin. * Percentile generation matches numpy method. Still the output looks very different. * Use int type for hit count * Promote any scalar threshold dimensions to ensure that the indexing is working across the expected dimensions. * Lots of comments. * Remove bool type setting as this actually realized the data twice, once as float32 and once as bool. * Reorder loop and realize data to reduce number of lazy selects and deselects which were dominating the processing time. * More comments and start of longer documentation. Added mandatory percentile argument. Fixed up acceptance tests. * Add check for realization coordinate on inputs. * Simplify loop over possible values * Update checksums. * Extended documentation for the precipitation duration percentile generation method. * Improvements to doc strings and extended documentation. * Unit tests rewritten. * Style checks applied. * Add diagnostic name and update checksums for updated KGO. * Move precipitation_type directory to precipitation. Many files affected. * Code changes and acceptance test data addition. * Unit tests back to working but need restucturing and extension for spot data. * Unit tests extended to cover new methods. * Style fixes. * Metadata changes. * Add unit tests for processing of spot data. * Style fixes. * Remove threshold var_names. * Updated checksums for precipitation_duration acceptance tests with new metadata. * Fixed up time coordinate being set incorrectly. Typos addressed. Documentation moved to align with new directory structure. * Update image paths in documentation. * Int type change and doc-string improvements. --- .../precipitation/example_table.png | Bin 0 -> 71300 bytes .../precipitation/looking_up_indices.png | Bin 0 -> 198984 bytes .../precipitation/percentile_calculation.png | Bin 0 -> 102515 bytes .../precipitation/percentile_table.png | Bin 0 -> 30875 bytes .../precipitation/precipitation_duration.rst | 58 ++ improver/api/__init__.py | 12 +- improver/cli/convection_ratio.py | 2 +- improver/cli/freezing_rain.py | 2 +- improver/cli/hail_fraction.py | 2 +- improver/cli/precipitation_duration.py | 76 ++ improver/cli/remake_as_shower_condition.py | 2 +- improver/cli/shower_condition_probability.py | 2 +- improver/cli/sleet_probability.py | 2 +- improver/cli/snow_fraction.py | 2 +- improver/cli/snow_splitter.py | 2 +- .../__init__.py | 0 .../calculate_sleet_prob.py | 0 .../convection.py | 0 .../freezing_rain.py | 0 .../hail_fraction.py | 0 .../precipitation/precipitation_duration.py | 590 +++++++++++++ .../shower_condition_probability.py | 0 .../snow_fraction.py | 0 .../snow_splitter.py | 0 .../utilities.py | 0 improver/spotdata/neighbour_finding.py | 2 +- improver/utilities/cube_manipulation.py | 34 + improver_tests/acceptance/SHA256SUMS | 54 ++ .../acceptance/test_precipitation_duration.py | 138 +++ .../__init__.py | 0 .../calculate_sleet_prob/__init__.py | 0 .../test_calculate_sleet_probability.py | 2 +- .../convection/__init__.py | 0 .../test_ConvectionRatioFromComponents.py | 2 +- .../freezing_rain/__init__.py | 0 .../freezing_rain/conftest.py | 0 .../freezing_rain/test_FreezingRain.py | 4 +- .../hail_fraction/__init__.py | 0 .../hail_fraction/test_HailFraction.py | 4 +- .../precipitation_duration}/__init__.py | 0 .../test_PrecipitationDuration.py | 835 ++++++++++++++++++ .../shower_condition_probability}/__init__.py | 0 .../test_ShowerConditionProbability.py | 2 +- .../snow_fraction}/__init__.py | 0 .../snow_fraction/test_SnowFraction.py | 2 +- .../precipitation/snow_splitter/__init__.py | 0 .../snow_splitter/test_snow_splitter.py | 4 +- .../test_utilities.py | 2 +- .../cube_manipulation/test_collapse_time.py | 97 ++ 49 files changed, 1908 insertions(+), 26 deletions(-) create mode 100644 doc/source/extended_documentation/precipitation/example_table.png create mode 100644 doc/source/extended_documentation/precipitation/looking_up_indices.png create mode 100644 doc/source/extended_documentation/precipitation/percentile_calculation.png create mode 100644 doc/source/extended_documentation/precipitation/percentile_table.png create mode 100644 doc/source/extended_documentation/precipitation/precipitation_duration.rst create mode 100755 improver/cli/precipitation_duration.py rename improver/{precipitation_type => precipitation}/__init__.py (100%) rename improver/{precipitation_type => precipitation}/calculate_sleet_prob.py (100%) rename improver/{precipitation_type => precipitation}/convection.py (100%) rename improver/{precipitation_type => precipitation}/freezing_rain.py (100%) rename improver/{precipitation_type => precipitation}/hail_fraction.py (100%) create mode 100644 improver/precipitation/precipitation_duration.py rename improver/{precipitation_type => precipitation}/shower_condition_probability.py (100%) rename improver/{precipitation_type => precipitation}/snow_fraction.py (100%) rename improver/{precipitation_type => precipitation}/snow_splitter.py (100%) rename improver/{precipitation_type => precipitation}/utilities.py (100%) create mode 100644 improver_tests/acceptance/test_precipitation_duration.py rename improver_tests/{precipitation_type => precipitation}/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/calculate_sleet_prob/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/calculate_sleet_prob/test_calculate_sleet_probability.py (97%) rename improver_tests/{precipitation_type => precipitation}/convection/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/convection/test_ConvectionRatioFromComponents.py (98%) rename improver_tests/{precipitation_type => precipitation}/freezing_rain/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/freezing_rain/conftest.py (100%) rename improver_tests/{precipitation_type => precipitation}/freezing_rain/test_FreezingRain.py (98%) rename improver_tests/{precipitation_type => precipitation}/hail_fraction/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/hail_fraction/test_HailFraction.py (97%) rename improver_tests/{precipitation_type/shower_condition_probability => precipitation/precipitation_duration}/__init__.py (100%) create mode 100644 improver_tests/precipitation/precipitation_duration/test_PrecipitationDuration.py rename improver_tests/{precipitation_type/snow_fraction => precipitation/shower_condition_probability}/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/shower_condition_probability/test_ShowerConditionProbability.py (99%) rename improver_tests/{precipitation_type/snow_splitter => precipitation/snow_fraction}/__init__.py (100%) rename improver_tests/{precipitation_type => precipitation}/snow_fraction/test_SnowFraction.py (98%) create mode 100644 improver_tests/precipitation/snow_splitter/__init__.py rename improver_tests/{precipitation_type => precipitation}/snow_splitter/test_snow_splitter.py (97%) rename improver_tests/{precipitation_type => precipitation}/test_utilities.py (96%) create mode 100644 improver_tests/utilities/cube_manipulation/test_collapse_time.py diff --git a/doc/source/extended_documentation/precipitation/example_table.png b/doc/source/extended_documentation/precipitation/example_table.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd137c1e711409e1943b3bac5afa6b9cfe959d7 GIT binary patch literal 71300 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 KN%mudcmDxPccYB} literal 0 HcmV?d00001 diff --git a/doc/source/extended_documentation/precipitation/precipitation_duration.rst b/doc/source/extended_documentation/precipitation/precipitation_duration.rst new file mode 100644 index 0000000000..a05cfb6e9b --- /dev/null +++ b/doc/source/extended_documentation/precipitation/precipitation_duration.rst @@ -0,0 +1,58 @@ +**Percentile generation from a frequency table** + +Construction of the precipitation duration diagnostics involves a lot of input data. When working with a large ensemble, and constructing a long period from many short periods, the array sizes can become very memory hungry if operated on all at once. Every additional threshold that is included in the processing further exacerbates this issue. For example, input cubes something like this: + + +- realizations = 50 +- times = 8 x 3-hour periods to construct a 24-hour target. +- number of combined rate and accumulation thresholds = 3 +- y and x = 1000 + +means we are combining and collapsing two cubes of shape (50, 8, 3, 1000, 1000). + +We work around this using slicing, breaking the data up into smaller chunks that can be read into memory one at a time, their contribution assessed, and then the data freed. However, if we need to save out the result of this processing in its expanded form, or use it directly to generate percentiles, we still need to hold all of the resulting data in memory unless we implement some kind of piecemeal writing to disk. An alternative is to construct a frequency table that retains counts of where the data falls and generate our percentiles from this table. As we are calculating fractions of a day that are classified as wet with this plugin, we have a discrete set of potential fractions, which makes this frequency table approach possible without any loss of precision. + + +The method used in this plugin is as follows: + +1. Extract the requested rate and accumulation threshold cubes from the input cubes. +2. Combine the extracted cubes to form a single max rate and a single accumulation cube, each with a time dimension, as well a threshold dimension if multiple thresholds have been requested. +3. If we are working with scalar (single valued) threshold coordinates, escalate these to dimensions anyway so we can work with arrays with a consistent number of dimensions. +4. Arrange the dimensions into a specific order so we know that when we index these we are working with the expected dimensions. +5. Generate a tuple of tuples that describe all the different combinations of rate and accumulation thresholds to be combined together to classify the precipitation. +6. Scale the percentiles requested by the user to values in the range 0 to (realizations - 1). The table below shows an example, with 5 percentile values requested and these scaled to suitable lookup values for a 3-member ensemble (i.e. values of 0, 1 or 2). + +.. figure:: extended_documentation/precipitation/percentile_table.png + :align: center + :alt: A table showing the target percentiles, their scaled values, and the integer floor and ceiling of these values. + +7. Create a target array into which data will be placed. Note that if the number of percentiles being generated were as large as the number of realizations, we might not be doing much to reduce the memory with this approach. +8. Slice the data over the realization coordinate. We are therefore working with arrays with dimensions (threshold, time, y, x). +9. Realize the data by touching it, `cube.data`. This is done to reduce the amount of lazy loading of the data when we further subset it. Without this step the lazy loading and cleaning up of data takes much longer, making the plugin far too slow. +10. Loop over the different combinations of rate and accumulation thresholds. The inputs are binary probabilities, so we multiply them together to determine if a given location at a given time may be classified as precipitating by exceeding the rate and accumulation thresholds. +11. We sum over the time coordinate to get the total number of periods at each location that have been classified as precipitating. +12. As we are working with discrete periods we know all the values (number of periods) that could be returned by the sum over time. We use an array that has as one of its dimensions a length equal to this set of potential values. For each threshold combination and location we then add 1 into an array at an index in this dimension that corresponds to the value (number of periods) that was calculated. For example if a 24-hour period is being constructed from 3-hour period inputs, the possible values are between 0 (no periods classified) and 8 (all periods classified). + +This table demonstrates how the hit count and cumulative table is formed for our example 3-member ensemble with data covering a 24-hour period in 3-hour chunks. The no of periods that can be classified as wet (8) determines the length of this array (shown here for a single grid cell). For each realization a value of 1 is added into the array at the index corresponding to the number of periods classified as wet. The hit count total is formed by summing across all realizations. + +.. figure:: extended_documentation/precipitation/example_table.png + :align: center + :alt: An example of a hit count and cumulative table being formed. + + +13. Once we have looped over all realizations we have a `hit_count` array of shape (accumulation_thresholds, rate_thresholds, N, y, x) where `N` corresponds to the possible number of periods. This is effectively a frequency table, telling us how many realizations, for each location, classified 0, 1, 2 etc. periods as precipitating. We loop again over the rate and accumulation thresholds and sum cumulatively along the periods dimension. The sum that's returned is therefore a count of how many realizations classified each location as having fewer than or equal to `n` many periods classified as precipitating. This can be seen in the bottom row of the example table above. +14. We can find the index in this cumulative list at which the various requested percentiles would fall. This allows us to get to the percentile we are after without having all of the values in `hit_count` represented individually in some array. The lower and upper bounds are found, i.e. our target percentile value sits between these two values. + +This figure demonstrates the use of the cumulative table to lookup the bounding period fractions for our target percentile. + +.. figure:: extended_documentation/precipitation/looking_up_indices.png + :align: center + :alt: A diagram showing how the cumulative table is used to find the bounding period fractions for our target percentiles. + +15. We perform a linear interpolation between the values to obtain our target percentile value. The interpolation fraction is the difference between the lookup percentile and the floored version of the same. + +.. figure:: extended_documentation/precipitation/percentile_calculation.png + :align: center + :alt: A table showing the calculation of the percentile values from the frequency table. + +16. The calculated percentiles are stored for each combination of rate and accumulation thresholds and location, with these then written out to an output cube with suitable metadata. \ No newline at end of file diff --git a/improver/api/__init__.py b/improver/api/__init__.py index b999e185f8..1afb258c87 100644 --- a/improver/api/__init__.py +++ b/improver/api/__init__.py @@ -39,7 +39,7 @@ "Combine": "improver.cube_combiner", "ConstructReliabilityCalibrationTables": "improver.calibration.reliability_calibration", "ContinuousRankedProbabilityScoreMinimisers": "improver.calibration.ensemble_calibration", - "ConvectionRatioFromComponents": "improver.precipitation_type.convection", + "ConvectionRatioFromComponents": "improver.precipitation.convection", "ConvertProbabilitiesToPercentiles": "improver.ensemble_copula_coupling.ensemble_copula_coupling", "CopyMetadata": "improver.utilities.copy_metadata", "CorrectLandSeaMask": "improver.generate_ancillaries.generate_ancillary", @@ -58,7 +58,7 @@ "ExtractValueFromTable": "improver.utilities.extract_from_table", "FieldTexture": "improver.utilities.textural", "FillRadarHoles": "improver.nowcasting.utilities", - "FreezingRain": "improver.precipitation_type.freezing_rain", + "FreezingRain": "improver.precipitation.freezing_rain", "FrictionVelocity": "improver.wind_calculations.wind_downscaling", "GenerateClearskySolarRadiation": "improver.generate_ancillaries.generate_derived_solar_fields", "GenerateOrographyBandAncils": "improver.generate_ancillaries.generate_ancillary", @@ -67,7 +67,7 @@ "GenerateTopographicZoneWeights": "improver.generate_ancillaries.generate_topographic_zone_weights", "GradientBetweenAdjacentGridSquares": "improver.utilities.spatial", "GradientBetweenVerticalLevels": "improver.utilities.gradient_between_vertical_levels", - "HailFraction": "improver.precipitation_type.hail_fraction", + "HailFraction": "improver.precipitation.hail_fraction", "HailSize": "improver.psychrometric_calculations.hail_size", "height_of_maximum": "improver.utilities.cube_manipulation", "HumidityMixingRatio": "improver.psychrometric_calculations.psychrometric_calculations", @@ -106,10 +106,10 @@ "ResolveWindComponents": "improver.wind_calculations.wind_components", "RoughnessCorrection": "improver.wind_calculations.wind_downscaling", "SaturatedVapourPressureTable": "improver.generate_ancillaries.generate_svp_table", - "ShowerConditionProbability": "improver.precipitation_type.shower_condition_probability", + "ShowerConditionProbability": "improver.precipitation.shower_condition_probability", "SignificantPhaseMask": "improver.psychrometric_calculations.significant_phase_mask", - "SnowFraction": "improver.precipitation_type.snow_fraction", - "SnowSplitter": "improver.precipitation_type.snow_splitter", + "SnowFraction": "improver.precipitation.snow_fraction", + "SnowSplitter": "improver.precipitation.snow_splitter", "SpatiallyVaryingWeightsFromMask": "improver.blending.spatial_weights", "SpotExtraction": "improver.spotdata.spot_extraction", "SpotHeightAdjustment": "improver.spotdata.height_adjustment", diff --git a/improver/cli/convection_ratio.py b/improver/cli/convection_ratio.py index 4e1f566db9..b82fd36275 100755 --- a/improver/cli/convection_ratio.py +++ b/improver/cli/convection_ratio.py @@ -31,7 +31,7 @@ def process(*cubes: cli.inputcube, model_id_attr: str = None): A cube of convection_ratio of the same dimensions as the input cubes. """ - from improver.precipitation_type.convection import ConvectionRatioFromComponents + from improver.precipitation.convection import ConvectionRatioFromComponents if len(cubes) != 2: raise IOError(f"Expected 2 input cubes, received {len(cubes)}") diff --git a/improver/cli/freezing_rain.py b/improver/cli/freezing_rain.py index 4106d18cf3..1341f847e8 100644 --- a/improver/cli/freezing_rain.py +++ b/improver/cli/freezing_rain.py @@ -39,6 +39,6 @@ def process(*cubes: cli.inputcube, model_id_attr: str = None): A cube of freezing rain rate or accumulation probabilities. """ - from improver.precipitation_type.freezing_rain import FreezingRain + from improver.precipitation.freezing_rain import FreezingRain return FreezingRain(model_id_attr=model_id_attr)(*cubes) diff --git a/improver/cli/hail_fraction.py b/improver/cli/hail_fraction.py index b6f87d7801..b46daba876 100644 --- a/improver/cli/hail_fraction.py +++ b/improver/cli/hail_fraction.py @@ -27,6 +27,6 @@ def process(*cubes: cli.inputcubelist, model_id_attr: str = None): A single cube containing the hail fraction. """ - from improver.precipitation_type.hail_fraction import HailFraction + from improver.precipitation.hail_fraction import HailFraction return HailFraction(model_id_attr=model_id_attr)(*cubes) diff --git a/improver/cli/precipitation_duration.py b/improver/cli/precipitation_duration.py new file mode 100755 index 0000000000..8df74a168e --- /dev/null +++ b/improver/cli/precipitation_duration.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Script to calculate the fraction of a long period in which precipitation +reaches a given classification.""" + +from improver import cli + + +@cli.clizefy +@cli.with_output +def process( + *cubes: cli.inputcubelist, + min_accumulation_per_hour: cli.comma_separated_list, + critical_rate: cli.comma_separated_list, + target_period: float, + percentiles: cli.comma_separated_list, + accumulation_diagnostic: str = "probability_of_lwe_thickness_of_precipitation_amount_above_threshold", + rate_diagnostic: str = "probability_of_lwe_precipitation_rate_above_threshold", + model_id_attr: str = None, +): + r"""Classifies periods of precipitation intensity using both the maximum + precipitation rate in the period and the accumulation in the period. These + classified periods are then used to determine what fraction of a + constructed longer period would be classified as such. + + Args: + cubes (iris.cube.CubeList): + Cubes covering the expected period that include cubes of: + max_precip_rate: Maximum precipitation rate in a period. + precip_accumulation: Precipitation accumulation in a period. + The periods of these two diagnostics must be the same. + min_accumulation_per_hour: + The minimum accumulation per hour in the period, or a list + of several, used to classify the period. The accumulation is + used in conjuction with the critical rate. Units of mm. + critical_rate: + A rate threshold, or list of rate thresholds, which if the + maximum rate in the period is in excess of contributes to + classifying the period. Units of mm/hr. + target_period: + The period in hours that the final diagnostic represents. + This should be equivalent to the period covered by the inputs. + Specifying this explicitly here is entirely for the purpose of + checking that the returned diagnostic represents the period + that is expected. Without this a missing input file could + lead to a suddenly different overall period. + percentiles (list): + Definition of percentiles at which to calculate data. + accumulation_diagnostic: + The expected diagnostic name for the accumulation in period + diagnostic. Used to extract the cubes from the inputs. + rate_diagnostic: + The expected diagnostic name for the maximum rate in period + diagnostic. Used to extract the cubes from the inputs. + model_id_attr: + Name of the attribute used to identify the source model for + blending. + + Returns: + result (iris.cube.Cube): + Returns a cube with the combined data. + """ + from improver.precipitation.precipitation_duration import PrecipitationDuration + + return PrecipitationDuration( + min_accumulation_per_hour, + critical_rate, + target_period, + percentiles, + accumulation_diagnostic=accumulation_diagnostic, + rate_diagnostic=rate_diagnostic, + model_id_attr=model_id_attr, + )(*cubes) diff --git a/improver/cli/remake_as_shower_condition.py b/improver/cli/remake_as_shower_condition.py index 6dac8eeb72..0c6df0e409 100644 --- a/improver/cli/remake_as_shower_condition.py +++ b/improver/cli/remake_as_shower_condition.py @@ -31,6 +31,6 @@ def process(cube: cli.inputcube): Probability of any precipitation, if present, being classified as showery. """ - from improver.precipitation_type.utilities import make_shower_condition_cube + from improver.precipitation.utilities import make_shower_condition_cube return make_shower_condition_cube(cube) diff --git a/improver/cli/shower_condition_probability.py b/improver/cli/shower_condition_probability.py index 34fa72ca00..7e29db8c01 100644 --- a/improver/cli/shower_condition_probability.py +++ b/improver/cli/shower_condition_probability.py @@ -46,7 +46,7 @@ def process( """ from iris.cube import CubeList - from improver.precipitation_type.shower_condition_probability import ( + from improver.precipitation.shower_condition_probability import ( ShowerConditionProbability, ) diff --git a/improver/cli/sleet_probability.py b/improver/cli/sleet_probability.py index 992e5e2563..7492054c75 100644 --- a/improver/cli/sleet_probability.py +++ b/improver/cli/sleet_probability.py @@ -27,7 +27,7 @@ def process(snow: cli.inputcube, rain: cli.inputcube): Returns a cube with the probability of sleet. """ - from improver.precipitation_type.calculate_sleet_prob import ( + from improver.precipitation.calculate_sleet_prob import ( calculate_sleet_probability, ) diff --git a/improver/cli/snow_fraction.py b/improver/cli/snow_fraction.py index 8e6389a54a..f750f97d8c 100644 --- a/improver/cli/snow_fraction.py +++ b/improver/cli/snow_fraction.py @@ -31,6 +31,6 @@ def process(*cubes: cli.inputcube, model_id_attr: str = None): """ from iris.cube import CubeList - from improver.precipitation_type.snow_fraction import SnowFraction + from improver.precipitation.snow_fraction import SnowFraction return SnowFraction(model_id_attr=model_id_attr)(CubeList(cubes)) diff --git a/improver/cli/snow_splitter.py b/improver/cli/snow_splitter.py index 984bcd7514..9556eb9ca0 100644 --- a/improver/cli/snow_splitter.py +++ b/improver/cli/snow_splitter.py @@ -42,6 +42,6 @@ def process(*cubes: cli.inputcube, output_is_rain: bool): on precipitation cube) """ - from improver.precipitation_type.snow_splitter import SnowSplitter + from improver.precipitation.snow_splitter import SnowSplitter return SnowSplitter(output_is_rain=output_is_rain)(*cubes) diff --git a/improver/precipitation_type/__init__.py b/improver/precipitation/__init__.py similarity index 100% rename from improver/precipitation_type/__init__.py rename to improver/precipitation/__init__.py diff --git a/improver/precipitation_type/calculate_sleet_prob.py b/improver/precipitation/calculate_sleet_prob.py similarity index 100% rename from improver/precipitation_type/calculate_sleet_prob.py rename to improver/precipitation/calculate_sleet_prob.py diff --git a/improver/precipitation_type/convection.py b/improver/precipitation/convection.py similarity index 100% rename from improver/precipitation_type/convection.py rename to improver/precipitation/convection.py diff --git a/improver/precipitation_type/freezing_rain.py b/improver/precipitation/freezing_rain.py similarity index 100% rename from improver/precipitation_type/freezing_rain.py rename to improver/precipitation/freezing_rain.py diff --git a/improver/precipitation_type/hail_fraction.py b/improver/precipitation/hail_fraction.py similarity index 100% rename from improver/precipitation_type/hail_fraction.py rename to improver/precipitation/hail_fraction.py diff --git a/improver/precipitation/precipitation_duration.py b/improver/precipitation/precipitation_duration.py new file mode 100644 index 0000000000..72a6e7ed16 --- /dev/null +++ b/improver/precipitation/precipitation_duration.py @@ -0,0 +1,590 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Module containing the PrecipitationDuration class.""" + +import itertools +from numbers import Number +from typing import List, Optional, Tuple, Union + +import numpy as np +from iris import Constraint +from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube, CubeList +from iris.exceptions import CoordinateNotFoundError +from iris.util import new_axis, squeeze +from numpy import ndarray + +from improver import PostProcessingPlugin +from improver.metadata.probabilistic import ( + get_threshold_coord_name_from_probability_name, +) +from improver.metadata.utilities import ( + enforce_time_point_standard, + generate_mandatory_attributes, +) +from improver.utilities.common_input_handle import as_cubelist +from improver.utilities.cube_manipulation import ( + MergeCubes, + enforce_coordinate_ordering, +) + + +class PrecipitationDuration(PostProcessingPlugin): + """ + Classifies periods of precipitation intensity using a mix of the maximum + precipitation rate in the period and the accumulation in the period. These + classified periods are then used to determine what fraction of a + constructed longer period would be classified as such. Percentiles are + produced directly from these fractions via a frequency table. This is done + to reduce the memory requirements when potentially combining so much data. + The discrete nature of the fractions that are possible makes this table + approach possible. + + Note that we apply linear interpolation in the percentile generation, + meaning that fractions of the target period that are not multiples of the + input data period can be returned. + + .. See the documentation for a more detailed discussion of this plugin. + .. include:: extended_documentation/precipitation/ + precipitation_duration.rst + """ + + def __init__( + self, + min_accumulation_per_hour: float, + critical_rate: float, + target_period: float, + percentiles: Union[List[float], ndarray], + accumulation_diagnostic: str = "probability_of_lwe_thickness_of_precipitation_amount_above_threshold", + rate_diagnostic: str = "probability_of_lwe_precipitation_rate_above_threshold", + model_id_attr: Optional[str] = None, + ) -> None: + """ + Initialise the class. + + Args: + min_accumulation_per_hour: + The minimum accumulation per hour in the period, or a list + of several, used to classify the period. The accumulation is + used in conjunction with the critical rate. + Units of mm. + critical_rate: + A rate threshold, or list of rate thresholds, which if the + maximum rate in the period is in excess of contributes to + classifying the period. Units of mm/hr. + target_period: + The period in hours that the final diagnostic represents. + This should be equivalent to the period covered by the inputs. + Specifying this explicitly here is entirely for the purpose of + checking that the returned diagnostic represents the period + that is expected. Without this a missing input file could + lead to a suddenly different overall period. + percentiles: + A list of percentile values to be returned. + accumulation_diagnostic: + The expected diagnostic name for the accumulation in period + diagnostic. Used to extract the cubes from the inputs. + rate_diagnostic: + The expected diagnostic name for the maximum rate in period + diagnostic. Used to extract the cubes from the inputs. + model_id_attr: + The name of the dataset attribute to be used to identify the source + model when blending data from different models. + """ + if isinstance(min_accumulation_per_hour, Number): + min_accumulation_per_hour = [min_accumulation_per_hour] + min_accumulation_per_hour = [float(x) for x in min_accumulation_per_hour] + + if isinstance(critical_rate, Number): + critical_rate = [critical_rate] + critical_rate = [float(x) for x in critical_rate] + + self.min_accumulation_per_hour = min_accumulation_per_hour + self.critical_rate = critical_rate + self.target_period = target_period + self.rate_diagnostic = rate_diagnostic + self.accumulation_diagnostic = accumulation_diagnostic + self.model_id_attr = model_id_attr + self.period = None + self.acc_threshold = get_threshold_coord_name_from_probability_name( + self.accumulation_diagnostic + ) + self.rate_threshold = get_threshold_coord_name_from_probability_name( + self.rate_diagnostic + ) + self.percentiles = np.sort(np.array(percentiles, dtype=np.float32)) + + def _period_in_hours(self, cubes: CubeList) -> None: + """Get the periods of the input cubes, check they are equal and + return the period in hours. Sets the period as a class variable. + + Args: + cubes: + A list of cubes. + Raises: + ValueError: If periods of all cubes are not equal. + """ + periods = [] + for cube in cubes: + cube_periods = [ + np.diff(ctime.bound)[0].seconds for ctime in cube.coord("time").cells() + ] + periods.extend(set(cube_periods)) + + try: + (period,) = set(periods) + except ValueError as err: + raise ValueError( + "Cubes with inconsistent periods. Cannot return a single " + f"time period. Periods are: {periods}." + ) from err + + self.period = period / 3600 + + def _construct_thresholds(self) -> Tuple[ndarray, ndarray]: + """Converts the input threshold units to SI units to match the data. + The accumulation threshold specified by the user is also multiplied + up by the input cube period. So a 0.1 mm accumulation threshold + will become 0.3 mm for a 3-hour input cube, converted to metres. + + Returns: + Tuple of float values, one the accumulation threshold, the other + the rate threshold. + """ + + min_accumulation_per_hour = AuxCoord(self.min_accumulation_per_hour, units="mm") + critical_rate = AuxCoord(self.critical_rate, units="mm/hr") + min_accumulation_per_hour.convert_units("m") + critical_rate.convert_units("m/s") + + return min_accumulation_per_hour.points * self.period, critical_rate.points + + @staticmethod + def _construct_constraint( + diagnostic: str, threshold_name: str, threshold_values: ndarray + ) -> Constraint: + """Construct constraint to use in extracting the accumulation and + rate data, at relevant thresholds, from the input cubes. + + Args: + diagnostic: + The name of the diagnostic to be extracted. + threshold_name: + The name of the threshold from which to extract data. + threshold_values: + The threshold values to extract. + Returns: + Iris constraint to extract the target diagnostic and threshold + values. + """ + diagnostic_constraint = Constraint( + diagnostic, + coord_values={ + threshold_name: lambda cell: np.isclose( + cell.point, threshold_values + ).any() + }, + ) + return diagnostic_constraint + + def _extract_cubes( + self, + cubes: CubeList, + diagnostic: str, + threshold_name: str, + threshold_values: ndarray, + ) -> Cube: + """Extract target diagnostics and thresholds from the input cube list. + Merge these cubes into a single cube with a multi-valued time + dimension. + + Args: + cubes: + The input cubes from which to extract targets. + diagnostic: + The diagnostic name. + threshold_name: + Name of the threshold coordinate from which to extract specific + values. + threshold_values: + The threshold values to extract. + Raises: + ValueError: If the target diagnostic or thresholds are not found. + Returns: + cube: + The merged cube containing the target thresholds. + """ + diagnostic_constraint = self._construct_constraint( + diagnostic, threshold_name, threshold_values + ) + + try: + cube = MergeCubes()(cubes.extract(diagnostic_constraint)) + except IndexError: + msg = ( + "The requested diagnostic or threshold is not available. " + f"Requested diagnostic: {diagnostic}, threshold: {threshold_values}" + ) + raise ValueError(msg) + return cube + + @staticmethod + def _structure_inputs(cube: Cube) -> Tuple[Cube, DimCoord]: + """Ensure threshold and time coordinates are promoted to be dimensions + of the cube. Ensure the cube is ordered with realization, threshold, + and time as the three leading dimensions. + + Args: + cube: + The cube to be structured. + Returns: + Tuple containing: + The reordered cube and the threshold coordinate. + """ + # Pick out the threshold coordinates in the inputs. + thresh = cube.coord(var_name="threshold") + # Promote any scalar threshold coordinates to dimensions so that the + # array shapes are as expected. + if thresh not in cube.coords(dim_coords=True): + cube = new_axis(cube, thresh) + # Likewise, ensure the time coordinate is a dimension coordinate in + # case a period equal to the target period is passed in. + time_crd = cube.coord("time") + if time_crd not in cube.coords(dim_coords=True): + cube = new_axis(cube, time_crd) + # Enforce a coordinate order so we know which dimensions are which + # when indexing the data arrays. + enforce_coordinate_ordering(cube, ["realization", thresh.name(), "time"]) + return cube, thresh + + def _create_output_cube( + self, + data: ndarray, + cubes: Tuple[Cube, Cube], + acc_thresh: DimCoord, + rate_thresh: DimCoord, + spatial_dim_indices, + ) -> Cube: + """Create an output cube for the final diagnostic, which is the + set of percentiles describing the fraction of the constructed + period that has been classified as wet using the various accumulation + and peak rate thresholds. + + Args: + data: + The percentile data to be stored in the cube. + cubes: + Cubes from which to generate attributes, and the first of + which is used as a template from which to take time and + spatial coordinates. + acc_thresh: + The accumulation threshold to add to the cube. + rate_thresh: + The rate threshold to add to the cube. + spatial_dim_indices: + Indices of spatial coordinates on the the input cubes. + This allows us to select the correct dimensions when handling + either gridded or spot inputs. + Returns: + A Cube with a suitable name and metadata for the diagnostic data + stored within it. + """ + percentile_coord = DimCoord(self.percentiles, long_name="percentile", units="%") + + mandatory_attributes = generate_mandatory_attributes( + cubes, model_id_attr=self.model_id_attr + ) + mandatory_attributes["precipitation_sampling_period_in_hours"] = self.period + + # Add more descriptive long names to accumulation and rate thresholds. + acc_thresh.long_name = "precipitation_accumulation_threshold_for_wet" + rate_thresh.long_name = "precipitation_rate_threshold_for_wet" + # Remove var_names as working with dual threshold coordinates. + # Iris will otherwise suffix these, e.g. threshold_0. + acc_thresh.var_name = None + rate_thresh.var_name = None + + dim_coords = [ + (percentile_coord, 0), + (acc_thresh.copy(), 1), + (rate_thresh.copy(), 2), + ] + for index in spatial_dim_indices: + dim_coords.append( + (cubes[0].coords(dim_coords=True)[index], len(dim_coords)) + ) + # Add any aux coords associated with dim coords. + aux_coords = [ + (crd, index) + for index in spatial_dim_indices + for crd in cubes[0].aux_coords + if index in crd.cube_dims(cubes[0]) + ] + + classification_percentiles = Cube( + data.astype(np.float32), + long_name="fraction_of_time_classified_as_wet", + dim_coords_and_dims=dim_coords, + aux_coords_and_dims=aux_coords, + units="1", + attributes=mandatory_attributes, + ) + # The time dimension has been collapsed within the calculation. + # Here we collapse the coordinate from one of the inputs to apply to + # the output to match the data manipulation. + time_coords = [cubes[0].coord("forecast_reference_time").copy()] + for crd in ["time", "forecast_period"]: + time_crd = cubes[0].coord(crd).copy() + time_coords.append(time_crd.collapsed()) + + for crd in time_coords: + classification_percentiles.add_aux_coord(crd) + enforce_time_point_standard(classification_percentiles) + + return squeeze(classification_percentiles) + + def _calculate_fractions( + self, + precip_accumulation, + max_precip_rate, + acc_thresh, + rate_thresh, + realizations, + spatial_dims, + ): + """Calculate the fractions of the target period that are classified as + wet using the given accumulation and peak rate thresholds. This is + done by counting the number of periods in which the thresholds are + exceeded out of the total number possible. Frequency tables are + constructed and from these percentiles are calculated. + + Args: + precip_accumulation: + Precipitation accumulation in a period. + max_precip_rate: + Maximum preciptation rate in a period. + acc_thresh: + Accumulation threshold coordinate. + rate_thresh: + Max rate threshold coordinate. + realizations: + A list of realization indices to loop over. + spatial_dims: + A list containing the indices of the spatial coordinates on the + max_precip_rate cube, which are the same as for the + precip_accumulation cube. + Raises: + ValueError: If the input data is masked. + Returns: + An array containing the generated percentiles. + """ + # Get the lengths of the threshold arrays, after extraction, for use + # as looping counts and for creating a target array shape. + acc_len = acc_thresh.shape[0] + rate_len = rate_thresh.shape[0] + + # Generate the combinations of the threshold values. + combinations = list( + itertools.product(list(range(acc_len)), list(range(rate_len))) + ) + + # Determine the possible counts that can be achieved which is simply + # the length of the input time coordinates. + (n_periods,) = max_precip_rate.coord("time").shape + possible_period_counts = list(range(0, n_periods + 1)) + + # We've ensured that the periods combine to give the target_period. + # The fractions of that total period that can be returned are + # therefore simply 0-1 in increments of 1/n_periods. + fractions = np.arange(0, 1.0001, 1 / n_periods) + + # The percentile_rank_fractions are the target percentiles rescaled to match + # the number of realizations over which we are counting. + percentile_rank_fractions = self.percentiles * (len(realizations) - 1) / 100 + + # We create an empty array into which to put our resulting percentiles. + # We can index this with the accumulation and rate threshold indices + # to ensure we record the data where we expect. + generated_percentiles = np.empty( + (len(self.percentiles), acc_len, rate_len, *spatial_dims) + ) + + hit_count = np.zeros( + (acc_len, rate_len, n_periods + 1, *spatial_dims), + dtype=np.int8, + ) + for realization in realizations: + # Realize the data to reduce overhead of lazy loading smaller + # slices which comes to dominate the time with many small slices. + acc_realized = precip_accumulation[realization].data.astype(bool) + rate_realized = max_precip_rate[realization].data.astype(bool) + # Check for masked data and raise exception if present. + if np.ma.is_masked(acc_realized) or np.ma.is_masked(rate_realized): + raise ValueError( + "Precipitation duration plugin cannot handle masked data." + ) + + for acc_index, rate_index in combinations: + # Multiply the binary probabilities and then sum over the + # leading time dimension to count how many of the times + # precipitation is classified as exceeding both thresholds. + result = acc_realized[acc_index] * rate_realized[rate_index] + result = np.sum(result, axis=0) + + for value in possible_period_counts: + hit_count[acc_index, rate_index, value, result == value] += 1 + + for acc_index, rate_index in combinations: + # We accumulate the counts over the possible values. The resulting + # array contains monotonically increasing counts that we can use + # to determine where each target percentile falls in the possible + # values. + cumulated = np.cumsum(hit_count[acc_index, rate_index], axis=0) + resulting_percentiles = [] + for percentile_rank_fraction in percentile_rank_fractions: + # Find the value below and above the target percentile and + # apply linear interpolation to determine the percentile value + percentile_indices_lower = ( + cumulated <= np.floor(percentile_rank_fraction) + ).sum(axis=0) + percentile_indices_upper = ( + cumulated <= np.ceil(percentile_rank_fraction) + ).sum(axis=0) + + interp_fraction = percentile_rank_fraction - np.floor( + percentile_rank_fraction + ) + + percentile_values = fractions[ + percentile_indices_lower + ] + interp_fraction * ( + fractions[percentile_indices_upper] + - fractions[percentile_indices_lower] + ) + resulting_percentiles.append(percentile_values) + + resulting_percentiles = np.array(resulting_percentiles) + # Record the percentiles generated for this accumulation and rate + # threshold combination. + generated_percentiles[:, acc_index, rate_index] = resulting_percentiles + + return generated_percentiles + + def process(self, *cubes: Union[Cube, CubeList]) -> Cube: + """Produce a diagnostic that provides the fraction of the day that + can be classified as exceeding the given thresholds. The fidelity of + the returned product depends upon the period of the input cubes. + + This plugin can process an awful lot of data, particularly if + dealing with a large ensemble. To reduce the memory used, given the + discrete nature of the period fractions produced, frequency bins are + used to directly produce percentiles. This means that not all of the + values need to be held in memory at once, instead we count instances + of each value and then calculate the percentiles from the counts. + + Args: + cubes: + Cubes covering the expected period that include cubes of: + precip_accumulation: Precipitation accumulation in a period. + max_precip_rate: Maximum preciptation rate in a period. + Returns: + A cube of percentiles of the fraction of the target period that is + classified as exceeding the user specified thresholds. + Raises: + ValueError: If the input cubes have differing time coordinates. + ValueError: If the input cubes do not combine to create the expected + target period. + ValueError: If the input cubes lack a realization coordinate. + ValueError: If the input cubes have differing realization coordinates. + """ + cubes = as_cubelist(*cubes) + self._period_in_hours(cubes) + accumulation_threshold, rate_threshold = self._construct_thresholds() + + precip_accumulation = self._extract_cubes( + cubes, + self.accumulation_diagnostic, + self.acc_threshold, + accumulation_threshold, + ) + max_precip_rate = self._extract_cubes( + cubes, self.rate_diagnostic, self.rate_threshold, rate_threshold + ) + + if max_precip_rate.coord("time") != precip_accumulation.coord("time"): + raise ValueError( + "Precipitation accumulation and maximum rate in period cubes " + "have differing time coordinates and cannot be used together." + ) + + # Check input cubes combine to create the expected target period. + total_period = ( + max_precip_rate.coord("time").cell(-1).bound[-1] + - max_precip_rate.coord("time").cell(0).bound[0] + ).total_seconds() / 3600 + if total_period != self.target_period: + raise ValueError( + "Input cubes do not combine to create the expected target " + "period. The period covered by the cubes passed in is: " + f"{total_period} hours. Target is {self.target_period} hours." + ) + + # Ensure cubes have the expected dimensions and order. + precip_accumulation, acc_thresh = self._structure_inputs(precip_accumulation) + max_precip_rate, rate_thresh = self._structure_inputs(max_precip_rate) + + # Determine size of spatial dimensions so we can work with gridded + # or spot files. + spatial_dim_indices = sorted( + set( + [ + max_precip_rate.coord_dims(max_precip_rate.coord(axis=axis))[0] + for axis in "xy" + ] + ) + ) + spatial_dims = [max_precip_rate.shape[dim] for dim in spatial_dim_indices] + + # Check that a multi-valued realization coordinate is present on the inputs. + try: + realizations = list(range(max_precip_rate.coord("realization").shape[0])) + if len(realizations) == 1: + raise CoordinateNotFoundError + except CoordinateNotFoundError as err: + raise ValueError( + "This plugin requires input data from multiple realizations." + "Percentiles are generated by collapsing this coordinate. It " + "cannot therefore be used with deterministic data or input " + "with a realization coordinate of length 1." + ) from err + + # Check realizations are the same as we intend to loop over a counting + # index and must ensure we are matching the same realizations together. + if precip_accumulation.coord("realization") != max_precip_rate.coord( + "realization" + ): + raise ValueError( + "Mismatched realization coordinates between accumulation and " + "max rate inputs. These must be the same." + ) + + generated_percentiles = self._calculate_fractions( + precip_accumulation, + max_precip_rate, + acc_thresh, + rate_thresh, + realizations, + spatial_dims, + ) + + # Store the generated percentiles in a cube with suitable metadata. + classification_percentiles = self._create_output_cube( + generated_percentiles, + (max_precip_rate, precip_accumulation), + acc_thresh, + rate_thresh, + spatial_dim_indices, + ) + return classification_percentiles diff --git a/improver/precipitation_type/shower_condition_probability.py b/improver/precipitation/shower_condition_probability.py similarity index 100% rename from improver/precipitation_type/shower_condition_probability.py rename to improver/precipitation/shower_condition_probability.py diff --git a/improver/precipitation_type/snow_fraction.py b/improver/precipitation/snow_fraction.py similarity index 100% rename from improver/precipitation_type/snow_fraction.py rename to improver/precipitation/snow_fraction.py diff --git a/improver/precipitation_type/snow_splitter.py b/improver/precipitation/snow_splitter.py similarity index 100% rename from improver/precipitation_type/snow_splitter.py rename to improver/precipitation/snow_splitter.py diff --git a/improver/precipitation_type/utilities.py b/improver/precipitation/utilities.py similarity index 100% rename from improver/precipitation_type/utilities.py rename to improver/precipitation/utilities.py diff --git a/improver/spotdata/neighbour_finding.py b/improver/spotdata/neighbour_finding.py index 7cf2620ac9..c901769bd7 100644 --- a/improver/spotdata/neighbour_finding.py +++ b/improver/spotdata/neighbour_finding.py @@ -253,7 +253,7 @@ def get_nearest_indices(site_coords: ndarray, cube: Cube) -> ndarray: A list of shape (n_sites, 2) that contains the x and y indices of the nearest grid points to the sites. """ - nearest_indices = np.zeros((len(site_coords), 2)).astype(np.int) + nearest_indices = np.zeros((len(site_coords), 2)).astype(np.int32) for index, (x_point, y_point) in enumerate(site_coords): nearest_indices[index, 0] = cube.coord(axis="x").nearest_neighbour_index( x_point diff --git a/improver/utilities/cube_manipulation.py b/improver/utilities/cube_manipulation.py index 77e04b4b11..5edf65242b 100644 --- a/improver/utilities/cube_manipulation.py +++ b/improver/utilities/cube_manipulation.py @@ -16,6 +16,7 @@ from improver import BasePlugin from improver.metadata.constants import FLOAT_DTYPE, FLOAT_TYPES from improver.metadata.probabilistic import find_threshold_coordinate +from improver.metadata.utilities import enforce_time_point_standard from improver.utilities.common_input_handle import as_cube from improver.utilities.cube_checker import check_cube_coordinates @@ -56,6 +57,39 @@ def collapsed(cube: Cube, *args: Any, **kwargs: Any) -> Cube: return new_cube +def collapse_time(cube, *args: Any) -> Cube: + """Collapses a time / forecast period coord of a cube and modifies the + time coordinates to place the point at the end of the bound range. If the + coordinate being collapsed (time or forecast_period) is of length 1 the + cube is returned unchanged as there is nothing to collapse. + + Args: + cube: + A Cube to be collapsed. + args: + Takes the arguments that are provided to the iris.Cube.collapsed + method. These include the coordinate name and the collapse + method, e.g. iris.analysis.SUM. + Returns: + Cube with the specified coordinate collapsed and the time coordinate + updated. The points of the time-related coordinates have been updated + to be at the end of their bound range in line with IMPROVER's metadata + standards. + """ + if "time" not in args and "forecast_period" not in args: + raise ValueError( + "The collapse_time wrapper should only be used for collapsing " + "the time or forecast_period coordinates." + ) + if cube.coord("time").shape[0] == 1: + return cube + + collapsed_cube = collapsed(cube, *args) + enforce_time_point_standard(collapsed_cube) + + return collapsed_cube + + def collapse_realizations(cube: Cube, method="mean") -> Cube: """Collapses the realization coord of a cube and strips the coord from the cube. diff --git a/improver_tests/acceptance/SHA256SUMS b/improver_tests/acceptance/SHA256SUMS index 87a213a3cb..27eec10a8a 100644 --- a/improver_tests/acceptance/SHA256SUMS +++ b/improver_tests/acceptance/SHA256SUMS @@ -770,6 +770,60 @@ db89fbab53988556f708cbffaa7639b153ffa81eff4f5586398e7efa90af9db2 ./phase-probab 47fbacac3d831adb790680cb2c3d3133cd0bc33897e22c5e915f1ed09254a7b4 ./phase-probability/spot/snow_kgo_percentiles.nc ebac866c30e11898a4925b070d79077314ddf6f6685dfef5761e7f21a872bf89 ./phase-probability/spot/snow_sleet_deterministic.nc ed7a7806032f75669575cc37941d2d7570ebfcfe4784028a11a71ee72df35f87 ./phase-probability/spot/snow_sleet_percentiles.nc +b401e40639ab1139531524ad88b71425a66888c453d82acd7d11b72d031dc238 ./precipitation_duration/renamed/20250127T1200Z-precipitation_accumulation.nc +d561c2d83ad162dd1a0360a1a64c0998ffa09dc3bcb16d0970e2136d0035454d ./precipitation_duration/renamed/20250127T1200Z-precipitation_rate_max.nc +448af4b0d01e774d1d73d0a056215d58aa96b1caa1815ecbe7530bde2673bb69 ./precipitation_duration/renamed/20250127T1500Z-precipitation_accumulation.nc +e955375a3a13f716fff3ee6ef6db7a89fad4780629ddd03afe23a1c71d348621 ./precipitation_duration/renamed/20250127T1500Z-precipitation_rate_max.nc +f71a2b43ddec46db6fb778cf7760567d4111918f99666548981adec13669e64c ./precipitation_duration/renamed/20250127T1800Z-precipitation_accumulation.nc +6de27c3bf01abdd7bf05afb88d38bdd6c235d841d3e15078bf37edd51c0e00c6 ./precipitation_duration/renamed/20250127T1800Z-precipitation_rate_max.nc +fe6bf3df4dd19a80860042d10c8481849dc30cec6ae5d876694b3bcfc606c6f3 ./precipitation_duration/renamed/20250127T2100Z-precipitation_accumulation.nc +193f1e427451eb06afc141eb4ec6d9ba24ca2f802cf0c8520e3e5f27f2a03806 ./precipitation_duration/renamed/20250127T2100Z-precipitation_rate_max.nc +32e9cbbc86b6a25600c6b85d41bdae07560b44a50050889f558a3ee8cb9f0e44 ./precipitation_duration/renamed/20250128T0000Z-precipitation_accumulation.nc +9b61a9d047c3677b2361f87eb66e9aa8d6e1f2daa56aefdfbaeb2fa85f0b0e17 ./precipitation_duration/renamed/20250128T0000Z-precipitation_rate_max.nc +660843e74deaff009b0beda2d2355fc8e6aa6dc6e36581b13cc4a6c3b85e0847 ./precipitation_duration/renamed/20250128T0300Z-precipitation_accumulation.nc +c4c4fbef60200810dca850c72e3298ea5905e996ff35f54415121f212918bf73 ./precipitation_duration/renamed/20250128T0300Z-precipitation_rate_max.nc +5ab5da9f93d677c96b22f87e33ceb65897f29adee1ca7e653a1968ac1c4ca680 ./precipitation_duration/renamed/20250128T0600Z-precipitation_accumulation.nc +5d798a03f2160ec4293deddfb004605cfb92d9f34deef54ece9fc76cb70357c3 ./precipitation_duration/renamed/20250128T0600Z-precipitation_rate_max.nc +df1133cedfd5fe7d878edad9b6d0fa4cefe76705f647f5b5c0fbcda53575d7a3 ./precipitation_duration/renamed/20250128T0900Z-precipitation_accumulation.nc +b79764837a2fc67c8b9bc133d7c2fde373e78a0a54531d842b282cd3a05af401 ./precipitation_duration/renamed/20250128T0900Z-precipitation_rate_max.nc +d034f6fce3204c52e4d4087d36ca399b658e6b8174841478a697bbc59f5e2764 ./precipitation_duration/renamed/kgo.nc +4f5d4290a11b97eaf38ac567f92c915a198b505b03cb8c2919e24841289d795a ./precipitation_duration/spot_data/20250127T1200Z-precipitation_accumulation.nc +dcfa66035774604833f5bb963006a9b355945a2888f8c4793aae5d6588afd9ae ./precipitation_duration/spot_data/20250127T1200Z-precipitation_rate_max.nc +1da0e3aaf73ddedcd08443178bafc2f9c1f6b49ee0ac44f4ea7472eb34ba8d27 ./precipitation_duration/spot_data/20250127T1500Z-precipitation_accumulation.nc +9928399b692f6250c26a48aaafb2ea69fed49ea2b9376319cc24b52025e0af56 ./precipitation_duration/spot_data/20250127T1500Z-precipitation_rate_max.nc +b950ce0b491ecd53e700836b85c8be9dc314d967171ec7dee048e3ccfbf84d2e ./precipitation_duration/spot_data/20250127T1800Z-precipitation_accumulation.nc +22fdf6912d71f042bc6d6438c4ddfb491724281239f44b790f20ca035da54f6b ./precipitation_duration/spot_data/20250127T1800Z-precipitation_rate_max.nc +02cb00dd4c3076f31f7b309eb6c45ca0afe5206959d788a1d4a2b74db06e6e4e ./precipitation_duration/spot_data/20250127T2100Z-precipitation_accumulation.nc +6aad2440a82caf2b82f60369fcfb337385d3bc413be28503f274eca794fafa1d ./precipitation_duration/spot_data/20250127T2100Z-precipitation_rate_max.nc +e2be33eb15455cd2f49ae07320564184a4afbd610b728dc148dd154fc28d125e ./precipitation_duration/spot_data/20250128T0000Z-precipitation_accumulation.nc +972a05cb4fac1035a7493a78364df2af5eae1363b3bcec13ffc642a312b697f9 ./precipitation_duration/spot_data/20250128T0000Z-precipitation_rate_max.nc +3aec7dd67f16ca62004e582e55154c0b9f573b3e005fd0c038fefced2a3bf96c ./precipitation_duration/spot_data/20250128T0300Z-precipitation_accumulation.nc +ed18e7ebc2d0f8d7c3dc3b7b9525294bb1c8a6262c9d8a76c5dfc986d96674d6 ./precipitation_duration/spot_data/20250128T0300Z-precipitation_rate_max.nc +b2a96bac061acf70002838f1e683194f95617deb630faf034c7a7947e8e19377 ./precipitation_duration/spot_data/20250128T0600Z-precipitation_accumulation.nc +1c0068656a87cb039665805d0f4c8bf48b886e779c9066bd1fa66176a24a3499 ./precipitation_duration/spot_data/20250128T0600Z-precipitation_rate_max.nc +8ca0d1d7891b080e7af879e7ece0b4137536f9cd138007ab98eb2848b08a1d2f ./precipitation_duration/spot_data/20250128T0900Z-precipitation_accumulation.nc +752414ed48ce241caad82b2bab7d3ab13066d697c8c018b3507863f43745e390 ./precipitation_duration/spot_data/20250128T0900Z-precipitation_rate_max.nc +6029c623830e0844ef225cb34dc3626c787f2abee0a6938e72509d0e1a423dc2 ./precipitation_duration/spot_data/kgo.nc +3e7298a5c37d73e4faa2f3f640a27e8fe28fa705fc87806b82fe1163873b0a70 ./precipitation_duration/standard_names/20250127T1200Z-precipitation_accumulation.nc +487a50f8babf2fb37e0aa8263729fae38e640d6002941903a52e21b293f509bc ./precipitation_duration/standard_names/20250127T1200Z-precipitation_rate_max.nc +0b18ac8c7d1a4c6f0ee6c8952ab3f7dccba0aaa81631c59143200aa137ce5dee ./precipitation_duration/standard_names/20250127T1500Z-precipitation_accumulation.nc +6a881933afb383eacbfa262c0919054b8774ac6975e93530387e1ff1f63ed80a ./precipitation_duration/standard_names/20250127T1500Z-precipitation_rate_max.nc +338bf0caa4eeae13762489121fb833eefee04887931b0f74320f771e11fd8dc6 ./precipitation_duration/standard_names/20250127T1800Z-precipitation_accumulation.nc +4c6d95c3909aa6fbea46b568d336d40258ce8b910d240bc897e41eaf7f2e53ca ./precipitation_duration/standard_names/20250127T1800Z-precipitation_rate_max.nc +d1c18f5e1da3728f6963489370807dcb24fa1cd975f8e39987f29f6945765562 ./precipitation_duration/standard_names/20250127T2100Z-precipitation_accumulation.nc +911b28d3a72616c3cec4b11c02a490f0f2d4aeb908d74cbcb68d666513664e6c ./precipitation_duration/standard_names/20250127T2100Z-precipitation_rate_max.nc +f090d365cb7264654a8df937f95fbd2ca8026d9b3c6cc8f092dfdaf54441549d ./precipitation_duration/standard_names/20250128T0000Z-precipitation_accumulation.nc +e06f5b6876144b5a0f211bdecc9047340a9f53b0a89e0b2b83207dee5dad27b5 ./precipitation_duration/standard_names/20250128T0000Z-precipitation_rate_max.nc +7aeafa66500c6c9277176d883ad6837d40065a25c3dd33ce7bbc9cf32bdacc8b ./precipitation_duration/standard_names/20250128T0300Z-precipitation_accumulation.nc +6b6bcab08248c5e4d1b0d2e46d9fcdc9ad7ab2ce356a503ac8834c6be58c6e7d ./precipitation_duration/standard_names/20250128T0300Z-precipitation_rate_max.nc +84a263cc4f4a2233ebcdea57cfa39afb3c13bede245817256765186ff00cc09e ./precipitation_duration/standard_names/20250128T0600Z-precipitation_accumulation.nc +97dcccb976305aa1510eee6713b1346ce425b122058012c9034f47646df52ceb ./precipitation_duration/standard_names/20250128T0600Z-precipitation_rate_max.nc +a5666723c4b27f7f27b05e1c1dfca396bab7661e06b6ecc2458a36879c5949d1 ./precipitation_duration/standard_names/20250128T0900Z-precipitation_accumulation.nc +a89ba9668fd878ed5c5cc017e46a25ab1f9d205b1a6913457a8f3af770cc49e1 ./precipitation_duration/standard_names/20250128T0900Z-precipitation_rate_max.nc +3c5ef2bc343f94465468d29ede1382640f25b59911a70b08512a19991e29416f ./precipitation_duration/standard_names/kgo_acc_0.10_rate_4.0.nc +8443aba0c0467e5b93f44f636a5f35f56171230f8902cb93f897cc8e516635d5 ./precipitation_duration/standard_names/kgo_acc_1.00_rate_4.0.nc +2a6f100b57e377fbc0aa36e16aeebe7094df4929f5229488042084c0ebc5d6b0 ./precipitation_duration/standard_names/kgo_multi_threshold.nc +ef48c916f13f0935906d679019f6a239458d2d757056f2e8f3ded6eb413e02ff ./precipitation_duration/standard_names/kgo_short_period.nc ae048c636992e80b79c6cbb44b36339b30ea8d0ef1db72cd3f4de8766346fa1d ./recursive-filter/input.nc b6cdb8bf877bb0b3b78ad224b50b9272b65732bf9e39a88df704209e228bf4c0 ./recursive-filter/input_masked.nc 11c428f6fb0202ab0f975e58e52d17342c50f607aee4fd0e387a2a62c188790e ./recursive-filter/input_variable_masked.nc diff --git a/improver_tests/acceptance/test_precipitation_duration.py b/improver_tests/acceptance/test_precipitation_duration.py new file mode 100644 index 0000000000..089b6814f6 --- /dev/null +++ b/improver_tests/acceptance/test_precipitation_duration.py @@ -0,0 +1,138 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +"""Tests for the precipitation duration CLI""" + +import pytest + +from . import acceptance as acc + +pytestmark = [pytest.mark.acc, acc.skip_if_kgo_missing] +CLI = acc.cli_name_with_dashes(__file__) +run_cli = acc.run_cli(CLI) + +PERCENTILES = "10,25,50,75,90" + + +@pytest.mark.parametrize("min_accumulation, critical_rate", [(0.1, 4.0), (1, 4.0)]) +def test_different_threshold_parameters(tmp_path, min_accumulation, critical_rate): + """Test precipitation duration with different parameters""" + kgo_dir = acc.kgo_root() / "precipitation_duration/standard_names" + kgo_path = kgo_dir / f"kgo_acc_{min_accumulation:.2f}_rate_{critical_rate:.1f}.nc" + input_cubes = kgo_dir.glob("2025*.nc") + output_path = tmp_path / "output.nc" + args = [ + *input_cubes, + "--min-accumulation-per-hour", + f"{min_accumulation}", + "--critical-rate", + f"{critical_rate}", + "--target-period", + "24", + "--percentiles", + PERCENTILES, + "--model-id-attr", + "mosg__model_configuration", + "--output", + output_path, + ] + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_shorter_period(tmp_path): + """Test basic precipitation duration calculation works for a + shorter period than a full day.""" + kgo_dir = acc.kgo_root() / "precipitation_duration/standard_names" + kgo_path = kgo_dir / "kgo_short_period.nc" + input_cubes = kgo_dir.glob("20250128T0*.nc") + output_path = tmp_path / "output.nc" + args = [ + *input_cubes, + "--min-accumulation-per-hour", + "0.1", + "--critical-rate", + "4.0", + "--target-period", + "12", + "--percentiles", + PERCENTILES, + "--output", + output_path, + ] + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_renamed_diagnostics(tmp_path): + """Test precipitation duration with different diagnostic names.""" + kgo_dir = acc.kgo_root() / "precipitation_duration/renamed" + kgo_path = kgo_dir / "kgo.nc" + input_cubes = kgo_dir.glob("2025*.nc") + output_path = tmp_path / "output.nc" + args = [ + *input_cubes, + "--min-accumulation-per-hour", + "0.1", + "--critical-rate", + "4.0", + "--target-period", + "24", + "--percentiles", + PERCENTILES, + "--rate-diagnostic", + "probability_of_lwe_rate_of_kittens_above_threshold", + "--accumulation-diagnostic", + "probability_of_lwe_thickness_of_kittens_above_threshold", + "--output", + output_path, + ] + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_multiple_thresholds(tmp_path): + """Test precipitation duration with multiple threshold pairings.""" + kgo_dir = acc.kgo_root() / "precipitation_duration/standard_names" + kgo_path = kgo_dir / "kgo_multi_threshold.nc" + input_cubes = kgo_dir.glob("2025*.nc") + output_path = tmp_path / "output.nc" + args = [ + *input_cubes, + "--min-accumulation-per-hour", + "0.1,1.0", + "--critical-rate", + "4", + "--target-period", + "24", + "--percentiles", + PERCENTILES, + "--output", + output_path, + ] + run_cli(args) + acc.compare(output_path, kgo_path) + + +def test_spot_data(tmp_path): + """Test precipitation duration from spot data.""" + kgo_dir = acc.kgo_root() / "precipitation_duration/spot_data" + kgo_path = kgo_dir / "kgo.nc" + input_cubes = kgo_dir.glob("2025*.nc") + output_path = tmp_path / "output.nc" + args = [ + *input_cubes, + "--min-accumulation-per-hour", + "0.1,1.0", + "--critical-rate", + "4", + "--target-period", + "24", + "--percentiles", + PERCENTILES, + "--output", + output_path, + ] + run_cli(args) + acc.compare(output_path, kgo_path) diff --git a/improver_tests/precipitation_type/__init__.py b/improver_tests/precipitation/__init__.py similarity index 100% rename from improver_tests/precipitation_type/__init__.py rename to improver_tests/precipitation/__init__.py diff --git a/improver_tests/precipitation_type/calculate_sleet_prob/__init__.py b/improver_tests/precipitation/calculate_sleet_prob/__init__.py similarity index 100% rename from improver_tests/precipitation_type/calculate_sleet_prob/__init__.py rename to improver_tests/precipitation/calculate_sleet_prob/__init__.py diff --git a/improver_tests/precipitation_type/calculate_sleet_prob/test_calculate_sleet_probability.py b/improver_tests/precipitation/calculate_sleet_prob/test_calculate_sleet_probability.py similarity index 97% rename from improver_tests/precipitation_type/calculate_sleet_prob/test_calculate_sleet_probability.py rename to improver_tests/precipitation/calculate_sleet_prob/test_calculate_sleet_probability.py index 1a5ed7d242..bb95cf0cdd 100644 --- a/improver_tests/precipitation_type/calculate_sleet_prob/test_calculate_sleet_probability.py +++ b/improver_tests/precipitation/calculate_sleet_prob/test_calculate_sleet_probability.py @@ -9,7 +9,7 @@ import numpy as np from iris.tests import IrisTest -from improver.precipitation_type.calculate_sleet_prob import calculate_sleet_probability +from improver.precipitation.calculate_sleet_prob import calculate_sleet_probability from improver.synthetic_data.set_up_test_cubes import set_up_probability_cube diff --git a/improver_tests/precipitation_type/convection/__init__.py b/improver_tests/precipitation/convection/__init__.py similarity index 100% rename from improver_tests/precipitation_type/convection/__init__.py rename to improver_tests/precipitation/convection/__init__.py diff --git a/improver_tests/precipitation_type/convection/test_ConvectionRatioFromComponents.py b/improver_tests/precipitation/convection/test_ConvectionRatioFromComponents.py similarity index 98% rename from improver_tests/precipitation_type/convection/test_ConvectionRatioFromComponents.py rename to improver_tests/precipitation/convection/test_ConvectionRatioFromComponents.py index ef20af076d..ad98e82c40 100644 --- a/improver_tests/precipitation_type/convection/test_ConvectionRatioFromComponents.py +++ b/improver_tests/precipitation/convection/test_ConvectionRatioFromComponents.py @@ -10,7 +10,7 @@ from iris.cube import CubeList from numpy.testing import assert_allclose, assert_equal, assert_raises_regex -from improver.precipitation_type.convection import ConvectionRatioFromComponents +from improver.precipitation.convection import ConvectionRatioFromComponents from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube GLOBAL_ATTRIBUTES = { diff --git a/improver_tests/precipitation_type/freezing_rain/__init__.py b/improver_tests/precipitation/freezing_rain/__init__.py similarity index 100% rename from improver_tests/precipitation_type/freezing_rain/__init__.py rename to improver_tests/precipitation/freezing_rain/__init__.py diff --git a/improver_tests/precipitation_type/freezing_rain/conftest.py b/improver_tests/precipitation/freezing_rain/conftest.py similarity index 100% rename from improver_tests/precipitation_type/freezing_rain/conftest.py rename to improver_tests/precipitation/freezing_rain/conftest.py diff --git a/improver_tests/precipitation_type/freezing_rain/test_FreezingRain.py b/improver_tests/precipitation/freezing_rain/test_FreezingRain.py similarity index 98% rename from improver_tests/precipitation_type/freezing_rain/test_FreezingRain.py rename to improver_tests/precipitation/freezing_rain/test_FreezingRain.py index 8490559b8a..2cde617221 100644 --- a/improver_tests/precipitation_type/freezing_rain/test_FreezingRain.py +++ b/improver_tests/precipitation/freezing_rain/test_FreezingRain.py @@ -12,7 +12,7 @@ from iris.cube import Cube from numpy.testing import assert_almost_equal -from improver.precipitation_type.freezing_rain import FreezingRain +from improver.precipitation.freezing_rain import FreezingRain RATE_NAME = "lwe_freezing_rainrate" ACCUM_NAME = "thickness_of_lwe_freezing_rainfall_amount" @@ -24,7 +24,7 @@ class HaltExecution(Exception): pass -@patch("improver.precipitation_type.freezing_rain.as_cubelist") +@patch("improver.precipitation.freezing_rain.as_cubelist") def test_as_cubelist_called(mock_as_cubelist): mock_as_cubelist.side_effect = HaltExecution try: diff --git a/improver_tests/precipitation_type/hail_fraction/__init__.py b/improver_tests/precipitation/hail_fraction/__init__.py similarity index 100% rename from improver_tests/precipitation_type/hail_fraction/__init__.py rename to improver_tests/precipitation/hail_fraction/__init__.py diff --git a/improver_tests/precipitation_type/hail_fraction/test_HailFraction.py b/improver_tests/precipitation/hail_fraction/test_HailFraction.py similarity index 97% rename from improver_tests/precipitation_type/hail_fraction/test_HailFraction.py rename to improver_tests/precipitation/hail_fraction/test_HailFraction.py index 88612fe139..da9b534c80 100644 --- a/improver_tests/precipitation_type/hail_fraction/test_HailFraction.py +++ b/improver_tests/precipitation/hail_fraction/test_HailFraction.py @@ -10,7 +10,7 @@ import numpy as np import pytest -from improver.precipitation_type.hail_fraction import HailFraction +from improver.precipitation.hail_fraction import HailFraction from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube COMMON_ATTRS = { @@ -24,7 +24,7 @@ class HaltExecution(Exception): pass -@patch("improver.precipitation_type.hail_fraction.as_cubelist") +@patch("improver.precipitation.hail_fraction.as_cubelist") def test_as_cubelist_called(mock_as_cubelist): mock_as_cubelist.side_effect = HaltExecution try: diff --git a/improver_tests/precipitation_type/shower_condition_probability/__init__.py b/improver_tests/precipitation/precipitation_duration/__init__.py similarity index 100% rename from improver_tests/precipitation_type/shower_condition_probability/__init__.py rename to improver_tests/precipitation/precipitation_duration/__init__.py diff --git a/improver_tests/precipitation/precipitation_duration/test_PrecipitationDuration.py b/improver_tests/precipitation/precipitation_duration/test_PrecipitationDuration.py new file mode 100644 index 0000000000..560803c052 --- /dev/null +++ b/improver_tests/precipitation/precipitation_duration/test_PrecipitationDuration.py @@ -0,0 +1,835 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +""" +Unit tests for the PrecipitationDuration plugin. +""" + +from datetime import datetime, timedelta +from typing import List, Tuple, Union + +import iris +import iris.util +import numpy as np +import pytest +from iris.coords import AuxCoord +from iris.cube import Cube, CubeList +from numpy import ndarray +from numpy.testing import assert_array_almost_equal, assert_array_equal + +from improver.precipitation.precipitation_duration import PrecipitationDuration +from improver.synthetic_data.set_up_test_cubes import ( + set_up_probability_cube, + set_up_spot_probability_cube, +) + +DEFAULT_ACC_NAME = ( + "probability_of_lwe_thickness_of_precipitation_amount_above_threshold" +) +DEFAULT_ACC_THRESH_NAME = "lwe_thickness_of_precipitation_amount" +DEFAULT_RATE_NAME = "probability_of_lwe_precipitation_rate_above_threshold" +DEFAULT_RATE_THRESH_NAME = "lwe_precipitation_rate" +DEFAULT_PERCENTILES = [10, 50, 90] + + +def data_times( + start_time: datetime, end_time: datetime, period: timedelta +) -> Tuple[datetime, List[datetime], List[List[datetime]]]: + """Define the times for the input cubes.""" + frt = start_time - 2 * period + times = [] + bounds = [] + time = start_time + period + while time <= end_time: + times.append(time) + bounds.append([time - period, time]) + time += period + return frt, times, bounds + + +def multi_time_cube( + frt: datetime, + times: Tuple[List[datetime]], + bounds: Tuple[List[List[datetime]]], + data: ndarray, + thresh: List[float], + diagnostic_name: str, + units: str, + function=set_up_probability_cube, +) -> CubeList: + """Create diagnostic cubes describing period data for each input time. + If the input data has 5 dimensions the first is assumed to be time and + the second is assumed to be a realization dimension.""" + + cubes = CubeList() + for time, time_bounds, diagnostic_data in zip(times, bounds, data): + realization_cubes = CubeList() + for index, realization_data in enumerate(diagnostic_data): + cube = function( + realization_data.astype(np.float32), + thresh, + time=time, + time_bounds=time_bounds, + frt=frt, + variable_name=diagnostic_name, + threshold_units=units, + ) + realization_coord = AuxCoord([index], standard_name="realization", units=1) + cube.add_aux_coord(realization_coord) + realization_cubes.append(cube) + cubes.append(realization_cubes.merge_cube()) + return cubes + + +@pytest.fixture(params=[set_up_probability_cube, set_up_spot_probability_cube]) +def precip_cubes( + request, + start_time: datetime, + end_time: datetime, + period: timedelta, + acc_data: ndarray, + rate_data: ndarray, + acc_thresh: List[float], + rate_thresh: List[float], +) -> CubeList: + """Create precipitation rate and accumulation cubes valid at a range of + times. The thresholds and data must be provided. Thresholds are expected + in units of mm for accumulations and mm/hr for rates; these are converted + to SI units when creating the cubes. The accumulation threshold is + multiplied up by the period. This means the accumulation threshold + argument represents the accumulation per hour, which is what the user will + specify when using the plugin. Multi-realization cubes will be returned. + + Args: + start_time: The start time of the input cubes (the lower bound of the + first time coordinate). + end_time: The end time of the input cubes (the upper bound of the last + time coordinate). + period: The period of the input cubes. + acc_data: The accumulation probabilities for the input cubes. + rate_data: The rate probabilities for the input cubes. + acc_thresh: The accumulation threshold for the input cubes. + rate_thresh: The rate threshold for the input cubes. + Returns: + CubeList: A list of cubes representing the input data with bespoke + periods, times, thresholds, and data. + """ + + function = request.param + + if function == set_up_spot_probability_cube: + acc_sites = np.product(acc_data.shape[-2:]) + acc_data = acc_data.reshape(acc_data.shape[:-2] + (acc_sites,)) + rate_sites = np.product(rate_data.shape[-2:]) + rate_data = rate_data.reshape(rate_data.shape[:-2] + (rate_sites,)) + + frt, times, bounds = data_times(start_time, end_time, period) + period_hours = period.total_seconds() / 3600 + acc_thresh = [period_hours * item / 1000.0 for item in acc_thresh] + rate_thresh = [item / (3600.0 * 1000.0) for item in rate_thresh] + cubes = CubeList() + cubes.extend( + multi_time_cube( + frt, + times, + bounds, + acc_data, + acc_thresh, + DEFAULT_ACC_THRESH_NAME, + "m", + function=function, + ) + ) + cubes.extend( + multi_time_cube( + frt, + times, + bounds, + rate_data, + rate_thresh, + DEFAULT_RATE_THRESH_NAME, + "m/s", + function=function, + ) + ) + return cubes + + +@pytest.mark.parametrize( + "start_time,end_time,period,acc_data,rate_data,acc_thresh,rate_thresh", + [ + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 6), + timedelta(hours=3), + np.ones((2, 1, 1, 3, 3)), + np.ones((2, 1, 1, 3, 3)), + [0.1], + [4], + ), # Period of 3-hours + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 9), + timedelta(hours=9), + np.ones((1, 1, 1, 3, 3)), + np.ones((1, 1, 1, 3, 3)), + [0.1], + [4], + ), # Period of 9-hours + ], +) +def test__period_in_hours( + start_time: datetime, + end_time: datetime, + period: timedelta, + acc_data: ndarray, + rate_data: ndarray, + acc_thresh: List[float], + rate_thresh: List[float], + precip_cubes: CubeList, +): + """Test that the period is calculated correctly from the input cubes.""" + + plugin = PrecipitationDuration(0, 0, 0, DEFAULT_PERCENTILES) + plugin._period_in_hours(precip_cubes) + + assert plugin.period == period.total_seconds() / 3600 + + +def test__period_in_hours_exception(): + """Test that an exception is raised if the input cubes have different + periods.""" + + frt = datetime(2025, 1, 15, 0) + times = [frt + timedelta(hours=3), frt + timedelta(hours=7)] + bounds = [ + [frt, frt + timedelta(hours=3)], + [frt + timedelta(hours=3), frt + timedelta(hours=7)], + ] + data = np.ones((2, 1, 1, 3, 4)) + + precip_cubes = multi_time_cube( + frt, times, bounds, data, [4.0], DEFAULT_RATE_NAME, "m/s" + ) + + with pytest.raises(ValueError, match="Cubes with inconsistent periods"): + plugin = PrecipitationDuration(0, 0, 0, DEFAULT_PERCENTILES) + plugin._period_in_hours(precip_cubes) + + +@pytest.mark.parametrize( + "acc_thresh,rate_thresh,period,expected_acc,expected_rate", + [ + ( + 0.1, + 0.2, + timedelta(hours=1), + [0.0001], + [5.56e-8], + ), # 1-hour period so acc thresh is just converted to SI + ( + 0.1, + 0.2, + timedelta(hours=3), + [0.0003], + [5.56e-8], + ), # 3-hour period so acc thresh is multiplied by 3 and converted to SI + ( + 1, + 1, + timedelta(hours=6), + [0.006], + [2.778e-7], + ), # 6-hour period so acc thresh is multiplied by 6 and converted to SI + ( + [0.1, 1], + [0.2, 0.4], + timedelta(hours=1), + [0.0001, 0.001], + [5.56e-8, 1.112e-7], + ), # Check mulitple thresholds can be accepted and converted. + ( + ["0.1", "1"], + ["0.2", "0.4"], + timedelta(hours=1), + [0.0001, 0.001], + [5.56e-8, 1.112e-7], + ), # Check thresholds given as strings. + ], +) +def test__construct_thresholds( + acc_thresh: Union[float, str, List], + rate_thresh: Union[float, str, List], + period: timedelta, + expected_acc: List[float], + expected_rate: List[float], +): + """Test that the thresholds are constructed correctly. Inputs are in units + involving mm, but all outputs are in SI units. Accumulation thresholds, + which are provided as the accumulation per hour, are multiplied up by the + period to reflect the expected accumulation for a given period input.""" + + plugin = PrecipitationDuration(acc_thresh, rate_thresh, 24, DEFAULT_PERCENTILES) + plugin.period = period.total_seconds() / 3600 + + ( + acc_thresh, + rate_thresh, + ) = plugin._construct_thresholds() + + assert_array_almost_equal(acc_thresh, expected_acc) + assert_array_almost_equal(rate_thresh, expected_rate) + + +DEFAULT_ACC_NAME, DEFAULT_ACC_THRESH_NAME, DEFAULT_RATE_NAME, DEFAULT_RATE_THRESH_NAME + + +@pytest.mark.parametrize( + "diagnostic,threshold,threshold_name", + [ + (DEFAULT_ACC_NAME, 0.1, DEFAULT_ACC_THRESH_NAME), + (DEFAULT_RATE_NAME, 4, DEFAULT_RATE_THRESH_NAME), + ], +) +def test__construct_constraint(diagnostic: str, threshold: float, threshold_name: str): + """Test that the iris constraint for the given threshold is constructed and + returned correctly.""" + + plugin = PrecipitationDuration( + threshold, + threshold, + 24, + DEFAULT_PERCENTILES, + accumulation_diagnostic=diagnostic, + rate_diagnostic=diagnostic, + ) + constraint = plugin._construct_constraint(diagnostic, threshold, threshold_name) + + assert isinstance(constraint, iris.Constraint) + assert constraint._name == diagnostic + assert threshold in constraint._coord_values.keys() + + +@pytest.mark.parametrize( + "start_time,end_time,period,acc_data,rate_data,acc_thresh,rate_thresh,diagnostic,threshold_name,threshold_values", + [ + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 6), + timedelta(hours=3), + np.ones((2, 2, 2, 3, 3)), + np.ones((2, 1, 1, 3, 3)), + [0.1, 0.2], + [4], + DEFAULT_RATE_NAME, + DEFAULT_RATE_THRESH_NAME, + np.array([4.0 / (3600.0 * 1000.0)], dtype=np.float32), + ), # Rate cube extraction. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 2), + timedelta(hours=1), + np.ones((2, 2, 2, 3, 3)), + np.ones((2, 1, 1, 3, 3)), + [0.1, 0.2], + [4], + DEFAULT_ACC_NAME, + DEFAULT_ACC_THRESH_NAME, + np.array([0.0001], dtype=np.float32), + ), # 1-hour period accumulation extraction. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 6), + timedelta(hours=3), + np.ones((2, 2, 2, 3, 3)), + np.ones((2, 1, 1, 3, 3)), + [0.1, 0.2], + [4], + DEFAULT_ACC_NAME, + DEFAULT_ACC_THRESH_NAME, + np.array([0.0003], dtype=np.float32), + ), # 3-hour period accumulation extraction with a suitably scaled + # threshold value. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 2), + timedelta(hours=1), + np.ones((2, 2, 2, 3, 3)), + np.ones((2, 1, 1, 3, 3)), + [0.1, 0.2], + [4], + DEFAULT_ACC_NAME, + DEFAULT_ACC_THRESH_NAME, + np.array([0.0001, 0.0002], dtype=np.float32), + ), # 1-hour period accumulation extraction multiple thresholds. + ], +) +def test__extract_cubes( + start_time: datetime, + end_time: datetime, + period: timedelta, + diagnostic: str, + threshold_name: str, + threshold_values: ndarray, + precip_cubes: CubeList, +): + """Test the extraction of target diagnostics and thresholds and the + merging of resulting outputs to form a single cube.""" + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + result = plugin._extract_cubes( + precip_cubes, + diagnostic, + threshold_name, + threshold_values, + ) + assert isinstance(result, Cube) + assert result.name() == diagnostic + assert result.coord("time").shape[0] == ((end_time - start_time) / period) + assert result.coord(var_name="threshold").name() == threshold_name + assert_array_equal(result.coord(var_name="threshold").points, threshold_values) + + +@pytest.mark.parametrize( + "diagnostic,threshold_name,threshold_values", + [ + ("kittens", DEFAULT_ACC_THRESH_NAME, np.array([2], dtype=np.float32)), + (DEFAULT_ACC_NAME, "kittens", np.array([2], dtype=np.float32)), + (DEFAULT_ACC_NAME, DEFAULT_ACC_THRESH_NAME, np.array([4], dtype=np.float32)), + ], +) +def test__extract_cubes_exception_thresholds( + diagnostic: str, threshold_name: str, threshold_values: ndarray +): + """Test an exception is raised if the input cubes do not contain the + required diagnostic, threshold name, or threshold value. Three tests are + run to check these conditions individually by setting the target values + to those we know are not present in the cubes.""" + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 2, 1, 3, 4)) + cubes = multi_time_cube(*time_args, data, [2.0], DEFAULT_ACC_THRESH_NAME, "m") + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + msg = "The requested diagnostic or threshold is not available." + + with pytest.raises(ValueError, match=msg): + plugin._extract_cubes( + cubes, + diagnostic, + threshold_name, + threshold_values, + ) + + +@pytest.mark.parametrize( + "start_time,end_time,period,acc_data,rate_data,acc_thresh,rate_thresh", + [ + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 3), + timedelta(hours=3), + np.ones((1, 2, 1, 3, 3)), # 1 time, 2 realizations, 1 threshold, y, x. + np.ones((1, 2, 1, 3, 3)), # Rate cube not used. + [0.1], # 1 accumulation threshold. + [4], # Rate cube not used. + ), # Time and threshold scalars to be promoted + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 6), + timedelta(hours=3), + np.ones((2, 2, 1, 3, 3)), # 2 times, 2 realizations, 1 threshold, y, x. + np.ones((2, 2, 1, 3, 3)), # Rate cube not used. + [0.1], # 1 accumulation threshold. + [4], # Rate cube not used. + ), # Threshold scalar to be promoted + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 6), + timedelta(hours=3), + np.ones((1, 2, 2, 3, 3)), # 1 times, 2 realizations, 2 thresholds, y, x. + np.ones((1, 2, 1, 3, 3)), # Rate cube not used. + [0.1, 0.2], # 2 accumulation thresholds. + [4], # Rate cube not used. + ), # Time scalar to be promoted + ], +) +def test__structure_inputs( + start_time: datetime, + end_time: datetime, + period: timedelta, + precip_cubes: CubeList, +): + """Test that all expected dim coords are in place after this method has + been applied and ordered as expected. The threshold coordinate should also + be returned.""" + + # Squeeze length 1 dim coords to scalars so they can be restored. + cube = iris.util.squeeze(precip_cubes[0]) + + expected_dimcrd_order = [ + "realization", + "lwe_thickness_of_precipitation_amount", + "time", + ] + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + result_cube, result_thresh = plugin._structure_inputs(cube) + + dimcrds = [crd.name() for crd in result_cube.coords(dim_coords=True)] + assert dimcrds[0:3] == expected_dimcrd_order + assert result_thresh.name() == "lwe_thickness_of_precipitation_amount" + + +@pytest.mark.parametrize( + "start_time,end_time,period,acc_data,rate_data,acc_thresh,rate_thresh,expected", + [ + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 2), + timedelta(hours=1), + np.ones((2, 2, 1, 3, 4)), # 2 times, 2 realizations, 1 threshold, y, x + np.ones((2, 2, 1, 3, 4)), # 2 times, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.ones((3, 3, 4)), # 3 percentiles, y, x + ), # Accumulation and rate probabilities are 1 for both input hours + # and all realizations. The resulting percentiles are all 1 for the + # combined period at all points. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 9), + timedelta(hours=3), + np.ones((3, 2, 1, 3, 4)), # 3 times, 2 realizations, 1 threshold, y, x + np.ones((3, 2, 1, 3, 4)), # 3 times, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.ones((3, 3, 4)), # 3 percentiles, y, x + ), # As above but for 3 hour input periods, with 3 of them comprising + # the total period. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 2), + timedelta(hours=1), + np.stack( + [ + np.stack([[np.ones((3, 4))], [np.zeros((3, 4))]]), + np.stack([[np.ones((3, 4))], [np.zeros((3, 4))]]), + ] + ), # 2 times, 2 realizations, 1 threshold, y, x + np.ones((2, 2, 1, 3, 4)), # 2 times, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.stack( + [ + np.full((3, 4), 0.1), + np.full((3, 4), 0.5), + np.full((3, 4), 0.9), + ] + ).astype(np.float32), # 3 percentiles, y, x + ), # Accumulation probabilities are 1 in the first realization and 0 + # in the second for both input hours. Half of the ensemble is + # classified as satisfying the rate and accumulation thresholds + # for both input periods. Realization 0 = 100% of total period + # classified as wet. Realization 1 = 0% of total period classified + # as wet. This results in the 50th percentile being a fraction of + # 0.5. Likewise the 10th and 90th percentiles are 0.1 and 0.9 + # respectively. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 2), + timedelta(hours=1), + np.stack( + [ + np.stack([[np.ones((3, 4))], [np.ones((3, 4))]]), + np.stack([[np.zeros((3, 4))], [np.zeros((3, 4))]]), + ] + ), # 2 times, 2 realizations, 1 threshold, y, x + np.ones((2, 2, 1, 3, 4)), # 2 times, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.full((3, 3, 4), 0.5, dtype=np.float32), # 3 percentiles, y, x + ), # Accumulation probabilities are 1 in both ensemble members for the + # first time and both 0 for the second time. This means that the + # fraction of the target period classified as wet by each ensemble + # member is 0.5. Given that percentiles are generated from the + # possible values within the ensemble the only value that can be + # returned for all the requested percentiles is 0.5. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 9), + timedelta(hours=3), + np.ones((3, 2, 2, 3, 4)), # 3 times, 2 realizations, 2 thresholds, y, x + np.ones((3, 2, 2, 3, 4)), # 3 times, 2 realizations, 2 thresholds, y, x + [0.1, 1.0], + [4, 8], + np.ones( + (3, 2, 2, 3, 4) + ), # 3 percentiles, 2 acc thresholds, 2 rate thresholds, y, x + ), # Multiple thresholds for both the accumulation and maximum rate. + # Again the total period fraction percentiles are 1 in this case as all + # input probabilities are 1 and all periods are classified as wet. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 12), + timedelta(hours=3), + np.ones((4, 3, 2, 3, 4)), # 4 times, 3 realizations, 2 thresholds, y, x + np.r_[[[1] * 12, [0] * 12] * 12] + .reshape(4, 3, 2, 3, 4) # 4 times, 3 realizations, 2 thresholds, y, x + .astype(np.float32), # 4 times, 3 realizations, 2 thresholds, y, x + [0.1, 1.0], + [2, 4], + np.r_[[[1] * 12, [0] * 12] * 6] + .reshape(3, 2, 2, 3, 4) + .astype( + np.float32 + ), # 3 percentiles, 2 acc thresholds, 2 rate thresholds, y, x + ), # Maximum rate in period probabilities are 1 for the lower + # threshold and 0 for the higher threshold for every time and realization. + # The resulting percentiles are all 0 or 1 as a fraction of the total + # period. So the 10th, 50th, and 90th percentiles are all 1 for + # combinations that include the lower rate threshold, and are all + # 0 for combinations that include the upper rate threshold. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 3), + timedelta(hours=3), + np.r_[[1, 0] * 12] + .reshape(1, 2, 1, 3, 4) + .astype(np.float32), # 1 time, 2 realizations, 1 threshold, y, x + np.ones((1, 2, 1, 3, 4)), # 1 time, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.r_[[1, 0] * 18] + .reshape(3, 3, 4) + .astype(np.float32), # 3 percentiles, y, x + ), # A single threshold, 2 realizations, and a single time input. + # The accumulation input binary probabilities are set in stripes of + # 1 and 0. This resulting percentiles are either 0 or 1; either + # none of the period or the whole period are classified as wet. + ( + datetime(2025, 1, 15, 0), + datetime(2025, 1, 15, 5), + timedelta(hours=1), + np.ones((5, 2, 1, 5, 5)), # 5 times, 2 realizations, 1 threshold, y, x + np.repeat( + [ + np.r_[[1] * 5 * j, [0] * 5 * (5 - j)].reshape(5, 5) + for j in range(5, 0, -1) + ], + 2, + axis=0, + ) + .astype(np.float32) + .reshape(5, 2, 1, 5, 5), # 5 times, 2 realizations, 1 threshold, y, x + [0.1], + [4], + np.repeat( + np.stack( + [np.r_[np.full((5), i)] for i in np.arange(1, 0, -0.2)] + ).reshape(1, 5, 5), + 3, + axis=0, + ).astype(np.float32), # 3 percentiles, y, x + ), # Accumulation probabilities vary by row with time such that the + # total period fraction for the top row is 1, the second row is + # 0.8, etc. down to 0.2 for the bottom row. Duplicated across realizations + # to avoid any further complication. Each location ends up with only a + # single fraction of the total period across the ensemble, which again + # means that the percentiles returned are all the same for the 10th, 50th + # and 90th percentile for a given location, e.g 1 for the top row + # and 0.2 for the bottom row. + ], +) +def test_process( + start_time: datetime, + end_time: datetime, + period: timedelta, + acc_data: ndarray, + rate_data: ndarray, + acc_thresh: List[float], + rate_thresh: List[float], + expected: ndarray, + precip_cubes: CubeList, +): + """Test the plugin produces the expected output. The calculation method + _calculate_fractions is tested here as is the creation of the + output cube.""" + + total_period = (end_time - start_time).total_seconds() + period_hours = period.total_seconds() / 3600 + + plugin = PrecipitationDuration( + acc_thresh, rate_thresh, total_period / 3600, DEFAULT_PERCENTILES + ) + result = plugin.process(precip_cubes) + + if result.coords("wmo_id"): + n_sites = np.product(acc_data.shape[-2:]) + expected = expected.reshape(expected.shape[:-2] + (n_sites,)) + + assert_array_equal(result.data, expected) + assert_array_almost_equal( + result.coord("lwe_thickness_of_precipitation_amount").points, + period.total_seconds() / 3600 * np.array(acc_thresh) / 1000, + ) + assert_array_almost_equal( + result.coord("lwe_precipitation_rate").points, + np.array(rate_thresh) / (3600 * 1000), + ) + assert result.attributes["precipitation_sampling_period_in_hours"] == period_hours + assert np.diff(result.coord("time").bounds) == total_period + assert result.coord( + long_name="precipitation_accumulation_threshold_for_wet" + ) is result.coord("lwe_thickness_of_precipitation_amount") + assert result.coord( + long_name="precipitation_rate_threshold_for_wet" + ) is result.coord("lwe_precipitation_rate") + + var_names = [crd.var_name for crd in result.coords()] + assert "threshold" not in var_names + + +def test_process_exception_differing_time(): + """Test an exception is raised if the input cubes have differing time + dimensions, meaning they cannot be used together.""" + + cubes = CubeList() + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 2, 1, 3, 4)) + cubes.extend( + multi_time_cube(*time_args, data, [1.0 / 1000], DEFAULT_ACC_THRESH_NAME, "m") + ) + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 3), timedelta(hours=1) + ) + data = np.ones((3, 2, 1, 3, 4)) + cubes.extend( + multi_time_cube( + *time_args, data, [7.0 / (3600 * 1000)], DEFAULT_RATE_THRESH_NAME, "m/s" + ) + ) + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + msg = ( + "Precipitation accumulation and maximum rate in period cubes " + "have differing time coordinates and cannot be used together." + ) + with pytest.raises(ValueError, match=msg): + plugin.process(cubes) + + +def test_process_exception_total_period(): + """Test an exception is raised if the input cubes do not combine to cover + the specified target total period.""" + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 2, 1, 3, 4)) + + cubes = CubeList() + cubes.extend( + multi_time_cube(*time_args, data, [1.0 / 1000], DEFAULT_ACC_THRESH_NAME, "m") + ) + cubes.extend( + multi_time_cube( + *time_args, data, [7.0 / (3600 * 1000)], DEFAULT_RATE_THRESH_NAME, "m/s" + ) + ) + + target_period = 24 + plugin = PrecipitationDuration(1.0, 7.0, target_period, DEFAULT_PERCENTILES) + msg = ( + "Input cubes do not combine to create the expected target " + "period. The period covered by the cubes passed in is: " + f"2.0 hours. Target is {target_period} hours." + ) + with pytest.raises(ValueError, match=msg): + plugin.process(cubes) + + +def test_process_exception_no_realization(): + """Test an exception is raised if the input cubes do not have multi-valued + realization coordinates.""" + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 1, 1, 3, 4)) + + cubes = CubeList() + cubes.extend( + multi_time_cube(*time_args, data, [1.0 / 1000], DEFAULT_ACC_THRESH_NAME, "m") + ) + cubes.extend( + multi_time_cube( + *time_args, data, [7.0 / (3600 * 1000)], DEFAULT_RATE_THRESH_NAME, "m/s" + ) + ) + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + msg = "This plugin requires input data from multiple realizations." + with pytest.raises(ValueError, match=msg): + plugin.process(cubes) + + +def test_process_exception_mismatched_realization(): + """Test an exception is raised if the input cubes do not have matching + realization coordinates.""" + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 2, 1, 3, 4)) + + cubes = CubeList() + cubes.extend( + multi_time_cube(*time_args, data, [1.0 / 1000], DEFAULT_ACC_THRESH_NAME, "m") + ) + for cube in cubes: + cube.coord("realization").points = [1, 2] + cubes.extend( + multi_time_cube( + *time_args, data, [7.0 / (3600 * 1000)], DEFAULT_RATE_THRESH_NAME, "m/s" + ) + ) + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + msg = ( + "Mismatched realization coordinates between accumulation and " + "max rate inputs. These must be the same." + ) + with pytest.raises(ValueError, match=msg): + plugin.process(cubes) + + +def test__calculate_fractions_exception_masked_data(): + """Test an exception is raised if the input cubes contain masked data.""" + + time_args = data_times( + datetime(2025, 1, 15, 0), datetime(2025, 1, 15, 2), timedelta(hours=1) + ) + data = np.ones((2, 2, 1, 3, 4)) + data = np.ma.masked_where(data == 1, data) + data.mask[0, 0, 0, 0] = False + + cubes = CubeList() + cubes.extend( + multi_time_cube(*time_args, data, [1.0 / 1000], DEFAULT_ACC_THRESH_NAME, "m") + ) + cubes.extend( + multi_time_cube( + *time_args, data, [7.0 / (3600 * 1000)], DEFAULT_RATE_THRESH_NAME, "m/s" + ) + ) + + plugin = PrecipitationDuration(1.0, 7.0, 2, DEFAULT_PERCENTILES) + msg = "Precipitation duration plugin cannot handle masked data." + with pytest.raises(ValueError, match=msg): + plugin.process(cubes) diff --git a/improver_tests/precipitation_type/snow_fraction/__init__.py b/improver_tests/precipitation/shower_condition_probability/__init__.py similarity index 100% rename from improver_tests/precipitation_type/snow_fraction/__init__.py rename to improver_tests/precipitation/shower_condition_probability/__init__.py diff --git a/improver_tests/precipitation_type/shower_condition_probability/test_ShowerConditionProbability.py b/improver_tests/precipitation/shower_condition_probability/test_ShowerConditionProbability.py similarity index 99% rename from improver_tests/precipitation_type/shower_condition_probability/test_ShowerConditionProbability.py rename to improver_tests/precipitation/shower_condition_probability/test_ShowerConditionProbability.py index 8e0aa232bf..fb92531554 100644 --- a/improver_tests/precipitation_type/shower_condition_probability/test_ShowerConditionProbability.py +++ b/improver_tests/precipitation/shower_condition_probability/test_ShowerConditionProbability.py @@ -12,7 +12,7 @@ from numpy import ndarray from improver.metadata.constants import FLOAT_DTYPE -from improver.precipitation_type.shower_condition_probability import ( +from improver.precipitation.shower_condition_probability import ( ShowerConditionProbability, ) from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube diff --git a/improver_tests/precipitation_type/snow_splitter/__init__.py b/improver_tests/precipitation/snow_fraction/__init__.py similarity index 100% rename from improver_tests/precipitation_type/snow_splitter/__init__.py rename to improver_tests/precipitation/snow_fraction/__init__.py diff --git a/improver_tests/precipitation_type/snow_fraction/test_SnowFraction.py b/improver_tests/precipitation/snow_fraction/test_SnowFraction.py similarity index 98% rename from improver_tests/precipitation_type/snow_fraction/test_SnowFraction.py rename to improver_tests/precipitation/snow_fraction/test_SnowFraction.py index 8c7bd83056..179355f926 100644 --- a/improver_tests/precipitation_type/snow_fraction/test_SnowFraction.py +++ b/improver_tests/precipitation/snow_fraction/test_SnowFraction.py @@ -11,7 +11,7 @@ import pytest from cf_units import Unit -from improver.precipitation_type.snow_fraction import SnowFraction +from improver.precipitation.snow_fraction import SnowFraction from improver.synthetic_data.set_up_test_cubes import ( construct_scalar_time_coords, set_up_variable_cube, diff --git a/improver_tests/precipitation/snow_splitter/__init__.py b/improver_tests/precipitation/snow_splitter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/improver_tests/precipitation_type/snow_splitter/test_snow_splitter.py b/improver_tests/precipitation/snow_splitter/test_snow_splitter.py similarity index 97% rename from improver_tests/precipitation_type/snow_splitter/test_snow_splitter.py rename to improver_tests/precipitation/snow_splitter/test_snow_splitter.py index 9a1443cb8b..6790f81ee3 100644 --- a/improver_tests/precipitation_type/snow_splitter/test_snow_splitter.py +++ b/improver_tests/precipitation/snow_splitter/test_snow_splitter.py @@ -11,7 +11,7 @@ import pytest from iris.cube import Cube, CubeList -from improver.precipitation_type.snow_splitter import SnowSplitter +from improver.precipitation.snow_splitter import SnowSplitter from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube LOCAL_MANDATORY_ATTRIBUTES = { @@ -25,7 +25,7 @@ class HaltExecution(Exception): pass -@patch("improver.precipitation_type.snow_splitter.as_cubelist") +@patch("improver.precipitation.snow_splitter.as_cubelist") def test_as_cubelist_called(mock_as_cubelist): mock_as_cubelist.side_effect = HaltExecution try: diff --git a/improver_tests/precipitation_type/test_utilities.py b/improver_tests/precipitation/test_utilities.py similarity index 96% rename from improver_tests/precipitation_type/test_utilities.py rename to improver_tests/precipitation/test_utilities.py index 629f718a56..c135707aa8 100644 --- a/improver_tests/precipitation_type/test_utilities.py +++ b/improver_tests/precipitation/test_utilities.py @@ -9,7 +9,7 @@ from iris.exceptions import CoordinateNotFoundError from improver.metadata.constants import FLOAT_DTYPE -from improver.precipitation_type.utilities import make_shower_condition_cube +from improver.precipitation.utilities import make_shower_condition_cube from improver.synthetic_data.set_up_test_cubes import set_up_probability_cube diff --git a/improver_tests/utilities/cube_manipulation/test_collapse_time.py b/improver_tests/utilities/cube_manipulation/test_collapse_time.py new file mode 100644 index 0000000000..7983acd181 --- /dev/null +++ b/improver_tests/utilities/cube_manipulation/test_collapse_time.py @@ -0,0 +1,97 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license. +# See LICENSE in the root of the repository for full licensing details. +""" +Unit tests for the function collapse_time. +""" + +from datetime import datetime, timedelta + +import iris +import numpy as np +import pytest +from iris.cube import CubeList +from numpy.testing import assert_array_equal + +from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube +from improver.utilities.cube_manipulation import collapse_time + + +@pytest.fixture +def data_times(): + """Define the times for the input cubes. These are also used to test + the collapsed cube times against.""" + frt = datetime(2025, 1, 15, 3, 0) + times = [] + bounds = [] + for hour in range(3, 9 + 1, 3): + time = frt + timedelta(hours=hour) + times.append(time) + bounds.append([time - timedelta(hours=3), time]) + return frt, times, bounds + + +@pytest.fixture +def multi_time_cube(data_times): + """Create a cube that has a leadtime time coordinate with an entry for + each validity time passed in. This coordinate will be collapsed to test + the function.""" + frt, times, bounds = data_times + data = 281 * np.ones((3, 3)).astype(np.float32) + cubes = CubeList() + for time, time_bounds in zip(times, bounds): + cubes.append( + set_up_variable_cube(data, time=time, time_bounds=time_bounds, frt=frt) + ) + return cubes.merge_cube() + + +@pytest.mark.parametrize("collapse_crd", ["time", "forecast_period"]) +def test_basic(multi_time_cube, data_times, collapse_crd): + """Test that a collapsed cube is returned with the expected + time coordinates. The point should be at the end of the bounds + and the bounds should span the original bounds.""" + + frt, times, bounds = data_times + + expected_time = times[-1] + expected_time_bounds = (bounds[0][0], bounds[-1][-1]) + expected_fp = (times[-1] - frt).total_seconds() + expected_fp_bounds = ((bounds[0][0] - frt).total_seconds(), expected_fp) + expected_data = np.full((3, 3), 281 * 3) + + result = collapse_time(multi_time_cube, collapse_crd, iris.analysis.SUM) + + assert result.coord("time").cell(0).point == expected_time + assert_array_equal(result.coord("time").cell(0).bound, expected_time_bounds) + assert result.coord("forecast_period").points[0] == expected_fp + assert_array_equal(result.coord("forecast_period").bounds[0], expected_fp_bounds) + assert_array_equal(result.data, expected_data) + + +@pytest.mark.parametrize("collapse_crd", ["time", "forecast_period"]) +def test_single_valued(multi_time_cube, data_times, collapse_crd): + """Test that a cube with a single valued time coordinate is returned + unchanged.""" + + cube = multi_time_cube[0] + result = collapse_time(cube, collapse_crd, iris.analysis.SUM) + + assert result is cube + + +@pytest.mark.parametrize( + "collapse_crd", ["times_tables", "forecast_periodicity", "kittens"] +) +def test_exception(multi_time_cube, data_times, collapse_crd): + """Test that an exception is raised when attempting to collapse a + coordinate that is not time or forecast_period.""" + + msg = ( + "The collapse_time wrapper should only be used for collapsing " + "the time or forecast_period coordinates." + ) + + with pytest.raises(ValueError, match=msg): + collapse_time(multi_time_cube, collapse_crd, iris.analysis.SUM) From ebb86c79327eaac935f14afb0aeaa00801de1509 Mon Sep 17 00:00:00 2001 From: Ben Hooper <114418734+brhooper@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:28:27 +0000 Subject: [PATCH 07/11] MOBT-810: Fix SpotManipulation skip_ecc_bounds bug (#2101) * Fix bug which meant skip_ecc_bounds option was not passed through the SpotManipulation plugin in to the ResamplePercentiles plugin. Add test for bug fix. * Improved doc-strings --- improver/cli/spot_extract.py | 13 +++++++------ improver/spotdata/spot_manipulation.py | 15 ++++++++------- improver_tests/spotdata/test_SpotManipulation.py | 9 +++++++++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/improver/cli/spot_extract.py b/improver/cli/spot_extract.py index 6fe168940c..3eed7bece0 100755 --- a/improver/cli/spot_extract.py +++ b/improver/cli/spot_extract.py @@ -87,12 +87,13 @@ def process( Demotes exceptions where calculated percentiles are outside the ECC bounds range to warnings. skip_ecc_bounds (bool): - If True, ECC bounds are not included when probabilities - are converted to percentiles. This has the effect that percentiles - outside of the range given by the input percentiles will be computed - by nearest neighbour interpolation from the nearest available percentile, - rather than using linear interpolation between the nearest available - percentile and the ECC bound. + If True, ECC bounds are not included when converting probabilities to + percentiles or from one set of percentiles to another. This has the + effect that percentiles outside of the range given by the input + percentiles will be computed by nearest neighbour interpolation from + the nearest available percentile, rather than using linear + interpolation between the nearest available percentile and the ECC + bound. new_title (str): New title for the spot-extracted data. If None, this attribute is removed from the output cube since it has no prescribed standard diff --git a/improver/spotdata/spot_manipulation.py b/improver/spotdata/spot_manipulation.py index 70eea8cc98..b5df939363 100644 --- a/improver/spotdata/spot_manipulation.py +++ b/improver/spotdata/spot_manipulation.py @@ -100,12 +100,13 @@ def __init__( Demotes exceptions where calculated percentiles are outside the ECC bounds range to warnings. skip_ecc_bounds (bool): - If True, ECC bounds are not included when probabilities - are converted to percentiles. This has the effect that percentiles - outside of the range given by the input percentiles will be computed - by nearest neighbour interpolation from the nearest available percentile, - rather than using linear interpolation between the nearest available - percentile and the ECC bound. + If True, ECC bounds are not included when converting probabilities to + percentiles or from one set of percentiles to another. This has the + effect that percentiles outside of the range given by the input + percentiles will be computed by nearest neighbour interpolation from + the nearest available percentile, rather than using linear + interpolation between the nearest available percentile and the ECC + bound. new_title (str): New title for the spot-extracted data. If None, this attribute is removed from the output cube since it has no prescribed standard @@ -236,7 +237,7 @@ def process(self, cubes: CubeList) -> Cube: ] result = extract_subcube(result, constraint) else: - result = ResamplePercentiles()( + result = ResamplePercentiles(skip_ecc_bounds=self.skip_ecc_bounds)( result, percentiles=extract_percentiles ) diff --git a/improver_tests/spotdata/test_SpotManipulation.py b/improver_tests/spotdata/test_SpotManipulation.py index dd694c4367..7d4d060631 100755 --- a/improver_tests/spotdata/test_SpotManipulation.py +++ b/improver_tests/spotdata/test_SpotManipulation.py @@ -177,6 +177,15 @@ def add_grid_hash(target, source): {"extract_percentiles": [50]}, np.array([278.5, 279.5]), ), + # Resample existing percentiles to extract target percentiles. Use skip_ecc_bounds option so that the largest + # extracted percentile should be derived by nearest neighbour interpolation. + ( + gridded_percentiles, + np.arange(273, 291).reshape(2, 3, 3), + np.array([[[1, 2], [0, 0], [5, 10]]]), + {"extract_percentiles": [50, 90], "skip_ecc_bounds": True}, + np.array([[278.5, 279.5], [283, 284]]), + ), # Extract single percentile from a probability input cube. ( gridded_probabilities, From 03ff95bba05344e5956020626a56b0a4b58c185b Mon Sep 17 00:00:00 2001 From: Katherine Tomkins Date: Tue, 11 Mar 2025 10:31:56 +0000 Subject: [PATCH 08/11] add VirtualTemperature to PROCESSING_MODULES (#2102) * add VirtualTemperature to PROCESSING_MODULES * added to CONTRIBUTING.md --------- Co-authored-by: Katherine Tomkins Co-authored-by: Katherine Tomkins --- CONTRIBUTING.md | 1 + improver/api/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ce2c2139a..7fd22a3f14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,6 +77,7 @@ below: - Victoria Smart (Met Office, UK) - Eleanor Smith (Met Office, UK) - Marcus Spelman (Met Office, UK) + - Katherine Tomkins (Met Office, UK) - Belinda Trotta (Bureau of Meteorology, Australia) - Tomasz Trzeciak (Met Office, UK) - Max White (Met Office, UK) diff --git a/improver/api/__init__.py b/improver/api/__init__.py index 1afb258c87..54905a2877 100644 --- a/improver/api/__init__.py +++ b/improver/api/__init__.py @@ -120,6 +120,7 @@ "Threshold": "improver.threshold", "TriangularWeightedBlendAcrossAdjacentPoints": "improver.blending.blend_across_adjacent_points", "VerticalUpdraught": "improver.wind_calculations.vertical_updraught", + "VirtualTemperature": "improver.virtual_temperature", "VisibilityCombineCloudBase": "improver.visibility.visibility_combine_cloud_base", "WeightAndBlend": "improver.blending.calculate_weights_and_blend", "WeightedBlendAcrossWholeDimension": "improver.blending.weighted_blend", From cd09aae5924c330f32b81d88dfbc110e2c452a27 Mon Sep 17 00:00:00 2001 From: bayliffe Date: Tue, 11 Mar 2025 14:12:10 +0000 Subject: [PATCH 09/11] MOBT-797: Threshold modification to allow collapse of time coordinate (#2099) * Threshold plugin modified to be able to collapse multiple dimensions. In theory this could probably be generalised, but in this case the intended use case is time and one of realization or percentile. Work still required to fix up time coordinate if it is collapsed. * Add handling of the time coordinate when collapsing time to generate probabilities in the threshold plugin. * Remove print statements from conftest. * update CLI argument type for coord_collapse. * Ensure forecast period is updated suitably when collapsing time coordinate. Additional test coverage. * Style fixes. * Review doc-string and type hinting changes. * Add methods to apply a cell method when we collapse coordinates within the threshold plugin. * Update CLI doc-string. Add an explicit test for collapsing a scalar coordinate. Add a catch in the plugin that raised an exception if attempting to collapse a coordinate that is not present, allowing for the removal of a later try/except clause. --- improver/cli/threshold.py | 30 ++- improver/threshold.py | 120 +++++++-- improver_tests/acceptance/SHA256SUMS | 2 + improver_tests/acceptance/test_threshold.py | 11 + improver_tests/threshold/conftest.py | 76 ++++-- improver_tests/threshold/test_Threshold.py | 256 ++++++++++++++++++-- 6 files changed, 423 insertions(+), 72 deletions(-) diff --git a/improver/cli/threshold.py b/improver/cli/threshold.py index 6294d6cc8c..d1ad30a807 100755 --- a/improver/cli/threshold.py +++ b/improver/cli/threshold.py @@ -19,7 +19,8 @@ def process( threshold_units: str = None, comparison_operator=">", fuzzy_factor: float = None, - collapse_coord: str = None, + collapse_coord: cli.comma_separated_list = None, + collapse_cell_methods: cli.inputjson = None, vicinity: cli.comma_separated_list = None, fill_masked: float = None, ): @@ -77,14 +78,24 @@ def process( indicating a narrower fuzzy factor region / sharper threshold. A fuzzy factor cannot be used with a zero threshold or a threshold_config file. - collapse_coord (str): - An optional ability to set which coordinate we want to collapse - over. The only supported options are "realization" or "percentile". - If "percentile" is requested, the percentile coordinate will be - rebadged as a realization coordinate prior to collapse. The percentile - coordinate needs to be evenly spaced around the 50th percentile - to allow successful conversion from percentiles to realizations and - subsequent collapsing over the realization coordinate. + collapse_coord (list of str): + A coordinate or list of coordinates over which an average is + calculated by collapsing these coordinates. The only supported + options are combinations of "realization" and "percentile" with + "time"; realization and percentile cannot both be collapsed. + If "percentile" is included the percentile coordinate will be + rebadged as a realization coordinate prior to collapse. + The percentile coordinate needs to be evenly spaced around the + 50th percentile to allow successful conversion from percentiles + to realizations and subsequent collapsing over the realization + coordinate. + collapse_cell_methods (dict): + An optional dictionary that describes cell methods to apply in + relation to the collapsed coordinates. By default the threshold + method does not return a cell method for collapsed coordinates. + The dictionary should take the form: {"coord_name": "method"} + with an entry for every coordinate for which a method is + desired. vicinity (list of float / int): List of distances in metres used to define the vicinities within which to search for an occurrence. Each vicinity provided will @@ -106,6 +117,7 @@ def process( threshold_units=threshold_units, comparison_operator=comparison_operator, collapse_coord=collapse_coord, + collapse_cell_methods=collapse_cell_methods, vicinity=vicinity, fill_masked=fill_masked, )(cube, land_sea_mask) diff --git a/improver/threshold.py b/improver/threshold.py index 768692d01b..1a39041fbd 100644 --- a/improver/threshold.py +++ b/improver/threshold.py @@ -22,6 +22,7 @@ format_cell_methods_for_probability, probability_is_above_or_below, ) +from improver.metadata.utilities import enforce_time_point_standard from improver.utilities.cube_manipulation import enforce_coordinate_ordering from improver.utilities.probability_manipulation import comparison_operator_dict from improver.utilities.rescale import rescale @@ -50,7 +51,8 @@ def __init__( fuzzy_factor: Optional[float] = None, threshold_units: Optional[str] = None, comparison_operator: str = ">", - collapse_coord: str = None, + collapse_coord: Optional[Union[str, List[str]]] = None, + collapse_cell_methods: Optional[dict] = None, vicinity: Optional[Union[float, List[float]]] = None, fill_masked: Optional[float] = None, ) -> None: @@ -118,14 +120,23 @@ def __init__( is no difference between < and <= or > and >=. Valid choices: > >= < <= gt ge lt le. collapse_coord: - A coordinate over which an average is calculated, collapsing - this coordinate. The only supported options are "realization" or - "percentile". If "percentile" is requested, the percentile - coordinate will be rebadged as a realization coordinate prior to - collapse. The percentile coordinate needs to be evenly spaced - around the 50th percentile to allow successful conversion from - percentiles to realizations and subsequent collapsing over the - realization coordinate. + A coordinate or list of coordinates over which an average is + calculated by collapsing these coordinates. The only supported + options are combinations of "realization" and "percentile" with + "time"; realization and percentile cannot both be collapsed. + If "percentile" is included the percentile coordinate will be + rebadged as a realization coordinate prior to collapse. + The percentile coordinate needs to be evenly spaced around the + 50th percentile to allow successful conversion from percentiles + to realizations and subsequent collapsing over the realization + coordinate. + collapse_cell_methods: + An optional dictionary that describes cell methods to apply in + relation to the collapsed coordinates. By default the threshold + method does not return a cell method for collapsed coordinates. + The dictionary should take the form: {"coord_name": "method"} + with an entry for every coordinate for which a method is + desired. fill_masked: If provided all masked points in cube will be replaced with the provided value. @@ -140,8 +151,9 @@ def __init__( ValueError: If both fuzzy_factor and bounds within the threshold_config are set. ValueError: If the fuzzy_factor is not strictly between 0 and 1. ValueError: If using a fuzzy factor with a threshold of 0.0. - ValueError: Can only collapse over a realization coordinate or a percentile - coordinate that has been rebadged as a realization coordinate. + ValueError: Can only collapse over one or a combination of realization + and percentile coordinates with the time coordinate. + ValueError: If collapse_coord contains both percentile and realization. """ if threshold_config and threshold_values: raise ValueError( @@ -195,13 +207,39 @@ def __init__( self.comparison_operator_string = comparison_operator self._decode_comparison_operator_string() - if collapse_coord and collapse_coord not in ["percentile", "realization"]: + if collapse_coord and not isinstance(collapse_coord, list): + collapse_coord = [collapse_coord] + + if collapse_coord and not set(collapse_coord).issubset( + ["percentile", "realization", "time"] + ): + raise ValueError( + "Can only collapse over one or a combination of realization, " + "percentile, and time coordinates." + ) + if ( + collapse_coord + and "percentile" in collapse_coord + and "realization" in collapse_coord + ): raise ValueError( - "Can only collapse over a realization coordinate or a percentile " - "coordinate that has been rebadged as a realization coordinate." + "Cannot collapse over both percentile and realization coordinates." ) self.collapse_coord = collapse_coord + if collapse_cell_methods: + if not self.collapse_coord: + raise ValueError( + "Cannot apply cell methods without collapsing a coordinate." + ) + if not set(collapse_cell_methods.keys()).issubset(self.collapse_coord): + raise ValueError( + "Cell methods can only be defined for coordinates that " + "are being collapsed." + ) + + self.collapse_cell_methods = collapse_cell_methods + self.vicinity = None if vicinity: if isinstance(vicinity, Iterable): @@ -348,6 +386,13 @@ def _update_metadata(self, cube: Cube) -> None: threshold_coord.attributes.update( {"spp__relative_to_threshold": self.comparison_operator.spp_string} ) + + # Add any user defined cell_methods. + if self.collapse_cell_methods: + for coord, method in self.collapse_cell_methods.items(): + cell_method = iris.coords.CellMethod(method, coord) + cube.add_cell_method(cell_method) + if cube.cell_methods: format_cell_methods_for_probability(cube, self.threshold_coord_name) @@ -554,9 +599,20 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: * Cube units set to (1). Raises: + ValueError: if collapse coordinates are not found in input_cube. ValueError: Cannot apply land-mask cube without in-vicinity processing. ValueError: if a np.nan value is detected within the input cube. """ + input_cube_crds = [crd.name() for crd in input_cube.coords()] + if self.collapse_coord and not set(self.collapse_coord).issubset( + input_cube_crds + ): + raise ValueError( + "Cannot collapse over coordinates not present in the input_cube." + f"collapse_coords: {self.collapse_coord}\n" + f"input_cube_coords: {input_cube_crds}" + ) + if self.vicinity is None and landmask is not None: raise ValueError( "Cannot apply land-mask cube without in-vicinity processing" @@ -565,9 +621,10 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: if self.fill_masked is not None: input_cube.data = np.ma.filled(input_cube.data, self.fill_masked) - if self.collapse_coord == "percentile": + if self.collapse_coord and "percentile" in self.collapse_coord: input_cube = RebadgePercentilesAsRealizations()(input_cube) - self.collapse_coord = "realization" + self.collapse_coord.remove("percentile") + self.collapse_coord.append("realization") self.original_units = input_cube.units self.threshold_coord_name = input_cube.name() @@ -580,7 +637,7 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: for radius in self.vicinity ] - # Slice over realizations if required and create an empty threshold + # Slice over collapse coords if required and create an empty threshold # cube to store the resulting thresholded data. if self.collapse_coord is not None: input_slices = input_cube.slices_over(self.collapse_coord) @@ -611,10 +668,11 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: unmasked = np.ones(cube.shape, dtype=bool) # All unmasked points contribute 1 to the numerator for calculating - # a realization collapsed truth value. Note that if input_slices - # above includes the realization coordinate (i.e. we are not collapsing - # that coordinate) then contribution_total will include this extra - # dimension and our denominator will be 1 at all unmasked points. + # a coordinate collapsed truth value. Note that if input_slices + # above includes the realization/time coordinate (i.e. we are not + # collapsing that coordinate) then contribution_total will include + # this extra dimension and our denominator will be 1 at all unmasked + # points. contribution_total += unmasked for index, (threshold, bounds) in enumerate( @@ -655,13 +713,22 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: thresholded_cube.coord(self.threshold_coord_name).convert_units( self.original_units ) + + # If we've collapsed a time coordinate we need to add back suitable + # time coordinates that capture the range of the times collapsed. + if self.collapse_coord and "time" in self.collapse_coord: + for crd in ["time", "forecast_period"]: + time_crd = input_cube.coord(crd).copy().collapsed() + thresholded_cube.remove_coord(crd) + thresholded_cube.add_aux_coord(time_crd) + + enforce_time_point_standard(thresholded_cube) + # Squeeze any single value dimension coordinates to make them scalar. thresholded_cube = iris.util.squeeze(thresholded_cube) - if self.collapse_coord is not None and thresholded_cube.coords( - self.collapse_coord - ): - thresholded_cube.remove_coord(self.collapse_coord) + if self.collapse_coord is not None and "realization" in self.collapse_coord: + thresholded_cube.remove_coord("realization") # Re-cast to 32bit now that any unit conversion has already taken place. thresholded_cube.coord(var_name="threshold").points = thresholded_cube.coord( @@ -674,6 +741,7 @@ def process(self, input_cube: Cube, landmask: Cube = None) -> Cube: enforce_coordinate_ordering( thresholded_cube, [ + "time", "realization", "percentile", self.threshold_coord_name, @@ -803,6 +871,6 @@ def process(self, input_cube: Cube) -> Cube: cube.coord(var_name="threshold").convert_units(input_cube.units) self._update_metadata(cube) - enforce_coordinate_ordering(cube, ["realization", "percentile"]) + enforce_coordinate_ordering(cube, ["time", "realization", "percentile"]) return cube diff --git a/improver_tests/acceptance/SHA256SUMS b/improver_tests/acceptance/SHA256SUMS index 27eec10a8a..c599ac3fb5 100644 --- a/improver_tests/acceptance/SHA256SUMS +++ b/improver_tests/acceptance/SHA256SUMS @@ -944,6 +944,8 @@ ec73679ff5e308a2bb4d21283262118f8d9fbb6a425309b76d5865a97a773c40 ./threshold-in ac93ed67c9947547e5879af6faaa329fede18afd822c720ac3afcb18fa41077a ./threshold/basic/input.nc eb3fdc9400401ec47d95961553aed452abcbd91891d0fbca106b3a05131adaa9 ./threshold/basic/kgo.nc 6b50fa16b663869b3e3fbff36197603886ff7383b2df2a8ba92579bcc9461a16 ./threshold/below_threshold/kgo.nc +c5e7eaadc0fff747e42ce918eca13a9e95234c2f87f2e08881dc820e4bdd3913 ./threshold/cell_method/cell_method.json +312321fd7d8a2d625cfd61e6028f4e0ba3e256cdbdfe4a27360e30e5b48fcfea ./threshold/cell_method/kgo.nc d18607807f2c6aed39194bc2ddb830f1ea2be3c9ea4a2e2d25f7fcfe66d4a088 ./threshold/coord_collapse/kgo.nc 4b1f348db13fa8d85bc3679742bdbc01c8a118a45ef65a205c2d862f5add8281 ./threshold/fuzzy_bounds/threshold_config.json d00745d7911caf4968ec9befa9a1dc71fd29744bbe12b02649c4693bd5b0aecf ./threshold/fuzzy_factor/kgo.nc diff --git a/improver_tests/acceptance/test_threshold.py b/improver_tests/acceptance/test_threshold.py index 5f4e7e047a..2e83799567 100644 --- a/improver_tests/acceptance/test_threshold.py +++ b/improver_tests/acceptance/test_threshold.py @@ -54,6 +54,17 @@ ], "basic", ), + ( + [ + "--threshold-values", + "280", + "--collapse-coord", + "realization", + "--collapse-cell-methods", + acc.kgo_root() / "threshold" / "cell_method" / "cell_method.json", + ], + "cell_method", + ), ), ) def test_args(tmp_path, extra_args, kgo_subdir): diff --git a/improver_tests/threshold/conftest.py b/improver_tests/threshold/conftest.py index 40ccb360bd..7d88273454 100644 --- a/improver_tests/threshold/conftest.py +++ b/improver_tests/threshold/conftest.py @@ -9,7 +9,7 @@ import numpy as np import pytest from iris.coords import DimCoord -from iris.cube import Cube +from iris.cube import Cube, CubeList from iris.exceptions import CoordinateNotFoundError from improver.synthetic_data.set_up_test_cubes import ( @@ -25,34 +25,51 @@ } -def diagnostic_cube(n_realizations: int = 1, data: Optional[np.ndarray] = None) -> Cube: +def diagnostic_cube( + n_realizations: int = 1, + n_times: int = 1, + data: Optional[np.ndarray] = None, +) -> Cube: """Return a cube of precipitation rate in mm/hr with n_realizations containing the data provided by the data argument. Args: n_realizations: The number of realizations to be present in the returned cube. + n_times: + Number of times in the time coordinate. If equal to 1 the + returned cube contains only a scalar time coordinate. data: The data to be contained in the cube. If specified this must contain a leading dimension of length n_realizations. Returns: A diagnostic cube for use in testing. """ - if data is None: data = np.zeros((n_realizations, 5, 5), dtype=np.float32) data[..., 2, 2] = 0.5 data = np.squeeze(data) - return set_up_variable_cube( - data, - name="precipitation_rate", - units="mm/hr", - spatial_grid="equalarea", - realizations=range(n_realizations), - attributes=COMMON_ATTRS, - standard_grid_metadata="uk_ens", - ) + if n_times > 1 and data.ndim != (3 + (n_realizations > 1)): + data = np.ma.stack([data] * n_times) + elif n_times == 1: + data = [data] + + cubes = CubeList() + for index, dslice in enumerate(data): + cube = set_up_variable_cube( + dslice, + name="precipitation_rate", + units="mm/hr", + spatial_grid="equalarea", + realizations=range(n_realizations), + attributes=COMMON_ATTRS, + standard_grid_metadata="uk_ens", + ) + cube.coord("time").points = cube.coord("time").points + ((index - 1) * 3600) + cubes.append(cube) + + return cubes.merge_cube() def deterministic_cube() -> Cube: @@ -74,6 +91,16 @@ def multi_realization_cube() -> Cube: return diagnostic_cube(n_realizations=2, data=data) +def multi_realization_multi_time_cube() -> Cube: + """Return the diagnostic cube with multi-valued realization and time + coordinates. The data is duplicated for each time, meaning tests for + expected values should return the same data as the standard multi- + realization tests.""" + data = np.zeros((2, 5, 5), dtype=np.float32) + data[..., 2, 2] = [0.45, 0.55] + return diagnostic_cube(n_realizations=2, n_times=2, data=data) + + def expected_name_func(comparison_operator: str) -> str: """Return the text description of the comparison operator for incorporation in the cube name or coordinate attributes. @@ -88,13 +115,15 @@ def expected_name_func(comparison_operator: str) -> str: @pytest.fixture -def custom_cube(n_realizations: int, data: np.ndarray) -> Cube: +def custom_cube(n_realizations: int, n_times: int, data: np.ndarray) -> Cube: """Provide a diagnostic cube with a configurable number of realizations and custom data. Args: n_realizations: The number of realizations to be present in the returned cube. + n_times: + The length of the time dimension. data: The data to be contained in the cube. If specified this must contain a leading dimension of length n_realizations. @@ -104,7 +133,7 @@ def custom_cube(n_realizations: int, data: np.ndarray) -> Cube: if data.dtype == np.float64: data = data.astype(np.float32) - return diagnostic_cube(n_realizations, data) + return diagnostic_cube(n_realizations=n_realizations, n_times=n_times, data=data) @pytest.fixture @@ -137,7 +166,7 @@ def expected_cube_name( def expected_result( expected_single_value: float, expected_multi_value: List[float], - collapse: bool, + collapse: Optional[List[str]], comparator: str, default_cube: Cube, ) -> np.ndarray: @@ -168,8 +197,7 @@ def expected_result( input cube is being tested. If the collapse argument is true the expected result is the mean of these values. collapse: - Whether the test includes the collapsing of the realization - coordinate to calculate the final result. + A list of coordinates being collapsed, or None if not collapsing. comparator: The comparator being applied in the thresholding, either "gt", "ge", "lt", or "le". @@ -184,6 +212,8 @@ def expected_result( except CoordinateNotFoundError: n_realizations = 1 + n_times = default_cube.coord("time").shape[0] + if "l" in comparator: expected_single_value = 1.0 - expected_single_value expected_multi_value = [1.0 - value for value in expected_multi_value] @@ -198,14 +228,22 @@ def expected_result( else: expected_result_array[..., 2, 2] = expected_multi_value - if collapse and n_realizations > 1: + if collapse and "time" in collapse and n_times > 1: + expected_result_array = expected_result_array[0] + + if collapse and "realization" in collapse and n_realizations > 1: expected_result_array = expected_result_array[0] return expected_result_array @pytest.fixture( - params=[deterministic_cube, single_realization_cube, multi_realization_cube] + params=[ + deterministic_cube, + single_realization_cube, + multi_realization_cube, + multi_realization_multi_time_cube, + ] ) def default_cube(request) -> Cube: """Parameterised to provide a deterministic cube, scalar realization cube, diff --git a/improver_tests/threshold/test_Threshold.py b/improver_tests/threshold/test_Threshold.py index 515c939374..8fc970e3c5 100644 --- a/improver_tests/threshold/test_Threshold.py +++ b/improver_tests/threshold/test_Threshold.py @@ -73,7 +73,17 @@ # collapse coordinate is not percentile or realization ( {"threshold_values": 0.6, "collapse_coord": "kittens"}, - "Can only collapse over a realization coordinate or a percentile", + "Can only collapse over one or a combination of realization", + ), + # collapse coordinate is a list with invalid options + ( + {"threshold_values": 0.6, "collapse_coord": ["kittens", "puppies"]}, + "Can only collapse over one or a combination of realization", + ), + # collapse coordinate is a list containing both percentile and realization + ( + {"threshold_values": 0.6, "collapse_coord": ["percentile", "realization"]}, + "Cannot collapse over both percentile and realization coordinates.", ), # threshold values provided as argument and via config ( @@ -82,6 +92,20 @@ ), # at least one set means of defining the thresholds must be used. ({}, "One of threshold_config or threshold_values must be provided."), + # collapse_cell_method provided but no collapse_coords. + ( + {"threshold_values": 0.6, "collapse_cell_methods": {"realization": "mean"}}, + "Cannot apply cell methods without collapsing a coordinate.", + ), + # collapse_cell_method provided but does not correspond to collapse_coords. + ( + { + "threshold_values": 0.6, + "collapse_coord": ["realization"], + "collapse_cell_methods": {"kitten": "mean"}, + }, + "Cell methods can only be defined for coordinates that are being collapsed.", + ), ], ) def test_init(kwargs, exception): @@ -131,17 +155,31 @@ def test__add_threshold_coord(default_cube, diagnostic, units): @pytest.mark.parametrize( - "n_realizations,data", + "kwargs,n_realizations,n_times,data", [ # A typical case with float inputs - (1, np.zeros(25, dtype=np.float32).reshape(5, 5)), + ({}, 1, 1, np.zeros(25, dtype=np.float32).reshape(5, 5)), # A case with integer inputs, where the data is converted to # float32 type, allowing for non-integer thresholded values, # i.e. due to the application of fuzzy thresholds. - (1, np.zeros(25, dtype=np.int8).reshape(5, 5)), + ({}, 1, 1, np.zeros(25, dtype=np.int8).reshape(5, 5)), + # Test removal of realization coordinate if it is collapsed + ( + {"collapse_coord": "realization"}, + 2, + 1, + np.zeros(18, dtype=np.int8).reshape(2, 3, 3), + ), + # Test time coordinate remains even if it is collapsed + ( + {"collapse_coord": ["realization", "time"]}, + 2, + 2, + np.zeros(36, dtype=np.int8).reshape(2, 2, 3, 3), + ), ], ) -def test_attributes_and_types(custom_cube): +def test_attributes_and_types(kwargs, custom_cube): """Test that the returned cube has the expected type and attributes.""" expected_attributes = { @@ -149,15 +187,42 @@ def test_attributes_and_types(custom_cube): "institution": "Met Office", "title": "Post-Processed IMPROVER unit test", } - plugin = Threshold(threshold_values=12, fuzzy_factor=(5 / 6)) + default_kwargs = {"threshold_values": 12, "fuzzy_factor": (5 / 6)} + default_kwargs.update(kwargs) + plugin = Threshold(**default_kwargs) result = plugin(custom_cube) assert isinstance(result, Cube) assert result.dtype == np.float32 + assert not result.coords("realization") + assert result.coords("time") for key, attribute in expected_attributes.items(): assert result.attributes[key] == attribute +@pytest.mark.parametrize( + "kwargs,n_realizations,n_times,data", + [ + # No realization coordinate but attempting to collapse it + ( + {"collapse_coord": ["realization", "time"]}, + 1, + 2, + np.zeros(18, dtype=np.int8).reshape(2, 3, 3), + ), + ], +) +def test_exception_for_missing_collapse_coordinates(kwargs, custom_cube): + """Test that an exception is raised if the collapse_coordinates defined by + the user are not present on the input_cube.""" + + default_kwargs = {"threshold_values": 1} + default_kwargs.update(kwargs) + plugin = Threshold(**default_kwargs) + with pytest.raises(ValueError, match="Cannot collapse over"): + plugin(custom_cube) + + @pytest.mark.parametrize( "comparison_operator", ["gt", "lt", "ge", "le", ">", "<", ">=", "<=", "GT", "LT", "GE", "LE"], @@ -216,7 +281,7 @@ def test_vicinity_as_empty_list(default_cube): assert result.name() == expected_cube_name -@pytest.mark.parametrize("collapse", (False, True)) +@pytest.mark.parametrize("collapse", (None, ["realization"], ["realization", "time"])) @pytest.mark.parametrize("comparator", ("gt", "lt", "le", "ge")) @pytest.mark.parametrize( "kwargs,expected_single_value,expected_multi_value", @@ -255,7 +320,8 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re - Different threshold values relative to the diagnostic value(s) - Use of fuzzy thresholds, specified in different ways. - - Deterministic, single realization, and multi-realization inputs + - Deterministic, single realization, multi-realization, and multi- + realization/multi-time inputs - Different threshold comparators. Note that the tests have been engineered such that there are no cases where the difference between "ge" and "gt", or "le" and "lt" are signficant, these @@ -266,45 +332,64 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re local_kwargs = kwargs.copy() - if collapse and default_cube.coords("realization", dim_coords=True): - local_kwargs.update({"collapse_coord": "realization"}) + if collapse and set(collapse).issubset( + [crd.name() for crd in default_cube.coords(dim_coords=True)] + ): + local_kwargs.update({"collapse_coord": collapse}) elif collapse: - # No need to repeat tests in which collapse_coord is not used. + # Skip tests in which the coordinates to be collapsed are not + # dimension coordinates of the input cube. pytest.skip() + # Check the time coordinate returned is as expected even if the time + # coordinate is being collapsed. + expected_time_coord = default_cube.coord("time").copy() + expected_fp_coord = default_cube.coord("forecast_period").copy() + if collapse and "time" in collapse: + expected_time_coord = expected_time_coord.collapsed() + expected_time_coord.points = expected_time_coord.bounds[0][-1] + expected_fp_coord = expected_fp_coord.collapsed() + expected_fp_coord.points = expected_fp_coord.bounds[0][-1] + local_kwargs.update({"comparison_operator": comparator}) plugin = Threshold(**local_kwargs) result = plugin(default_cube) assert result.data.shape == expected_result.shape - assert np.allclose(result.data, expected_result) + np.testing.assert_array_almost_equal(result.data, expected_result) + assert result.coord("time") == expected_time_coord + assert result.coord("forecast_period") == expected_fp_coord @pytest.mark.parametrize( - "kwargs,n_realizations,data,expected_result", + "kwargs,n_realizations,n_times,data,expected_result", [ # diagnostic value at threshold value, no fuzziness, various comparators ( {"threshold_values": 0.0, "comparison_operator": "gt"}, 1, + 1, np.zeros((3, 3)), np.zeros((3, 3), dtype=np.float32), ), ( {"threshold_values": 0.0, "comparison_operator": "lt"}, 1, + 1, np.zeros((3, 3)), np.zeros((3, 3), dtype=np.float32), ), ( {"threshold_values": 0.0, "comparison_operator": "ge"}, 1, + 1, np.zeros((3, 3)), np.ones((3, 3), dtype=np.float32), ), ( {"threshold_values": 0.0, "comparison_operator": "le"}, 1, + 1, np.zeros((3, 3)), np.ones((3, 3), dtype=np.float32), ), @@ -313,6 +398,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_config": {"0.": [-1, 1]}, "comparison_operator": "gt"}, 1, + 1, np.zeros((3, 3)), np.full((3, 3), 0.5, dtype=np.float32), ), @@ -320,6 +406,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_config": {"-1.": [-2, 0]}, "comparison_operator": "gt"}, 1, + 1, np.ones((3, 3)) * -0.5, np.ones((3, 3), dtype=np.float32) * 0.75, ), @@ -327,6 +414,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": -1.0}, 1, + 1, np.zeros((3, 3)), np.ones((3, 3), dtype=np.float32), ), @@ -334,6 +422,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 1.0}, 1, + 1, np.ma.masked_array([[2.0, 0], [3.0, 3.0]], mask=[[0, 0], [1, 1]]), np.ma.masked_array( [[1.0, 0], [0, 0]], mask=[[0, 0], [1, 1]], dtype=np.float32 @@ -343,6 +432,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 1.0, "fill_masked": np.inf}, 1, + 1, np.ma.masked_array([[2.0, 0], [3, 3]], mask=[[0, 0], [1, 1]]), np.array([[1.0, 0], [1.0, 1.0]], dtype=np.float32), ), @@ -350,6 +440,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "fuzzy_factor": 0.5}, 1, + 1, np.ma.masked_array([[0.5, 0], [3.0, 3.0]], mask=[[0, 0], [1, 1]]), np.ma.masked_array( [[0.5, 0], [0, 0]], mask=[[0, 0], [1, 1]], dtype=np.float32 @@ -361,6 +452,40 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "collapse_coord": "realization"}, 2, + 1, + np.ma.masked_array( + [ + [[1.0, 0], [0, 0]], # 2x2 realization 0 + [[0, 0], [0, 0]], + ], # 2x2 realization 1 + mask=[[[0, 0], [0, 0]], [[1, 0], [0, 0]]], # 2x2 realization 0 + ), # 2x2 realization 1 + np.array([[1.0, 0], [0, 0]], dtype=np.float32), + ), + # as above but collapsing the scalar time coordinate as well. This + # has no impact but demonstrates that the scalar coordinate is handled + # succesfully. + ( + {"threshold_values": 0.5, "collapse_coord": ["realization", "time"]}, + 2, + 1, + np.ma.masked_array( + [ + [[1.0, 0], [0, 0]], # 2x2 realization 0 + [[0, 0], [0, 0]], + ], # 2x2 realization 1 + mask=[[[0, 0], [0, 0]], [[1, 0], [0, 0]]], # 2x2 realization 0 + ), # 2x2 realization 1 + np.array([[1.0, 0], [0, 0]], dtype=np.float32), + ), + # multi-realization, multi-time, masked in one realization, returns + # expected value when the realization and time coordinates are + # collapsed. This value is 1 as the second realization is excluded + # from the averaging due to masking. + ( + {"threshold_values": 0.5, "collapse_coord": ["realization", "time"]}, + 2, + 2, np.ma.masked_array( [ [[1.0, 0], [0, 0]], # 2x2 realization 0 @@ -375,6 +500,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "vicinity": 3000}, 1, + 1, np.array([[0, 0, 0], [0, 1.0, 0.0], [0, 0, 0]]), np.ones((3, 3), dtype=np.float32), ), @@ -384,6 +510,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "vicinity": 3000}, 1, + 1, np.array([[1.0, 0, 0], [0, 0, 0.0], [0, 0, 0]]), np.array([[1.0, 1.0, 0], [1.0, 1.0, 0.0], [0, 0, 0]], dtype=np.float32), ), @@ -394,6 +521,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "vicinity": 3000}, 1, + 1, np.ma.masked_array( [[0, 2.0, 0], [0, 0, 0.0], [0, 0, 0]], mask=[[0, 1, 0], [0, 0, 0], [0, 0, 0]], @@ -412,6 +540,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": [0.5, 1.5], "vicinity": [3000, 5000]}, 1, + 1, np.r_[[1], [0] * 24].reshape((5, 5)).astype(np.float32), np.stack( [ @@ -431,6 +560,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": [0.5, 1.5]}, 1, + 1, np.ones((3, 3)), np.stack( [np.ones((3, 3), dtype=np.float32), np.zeros((3, 3), dtype=np.float32)] @@ -442,6 +572,7 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 0.5, "threshold_units": "m/s"}, 1, + 1, np.ones((3, 3)), np.zeros((3, 3), dtype=np.float32), ), @@ -450,21 +581,79 @@ def test_expected_values(default_cube, kwargs, collapse, comparator, expected_re ( {"threshold_values": 1.0e6, "comparison_operator": "gt"}, 1, + 1, np.array([[np.inf, 0], [0, 0]]), np.array([[1.0, 0], [0, 0]], dtype=np.float32), ), ( {"threshold_values": 1.0e6, "comparison_operator": "lt"}, 1, + 1, np.array([[np.inf, 0], [0, 0]]), np.array([[0, 1.0], [1.0, 1.0]], dtype=np.float32), ), ( {"threshold_values": np.inf, "comparison_operator": "ge"}, 1, + 1, np.array([[np.inf, 0], [0, 0]]), np.array([[1.0, 0], [0, 0]], dtype=np.float32), ), + # Data varying across time and realization dimensions. For the first + # time, realization 0 (R0) contains all 2s, R1 contains all 1s. For the + # second time R0 contains all 4s, R1 contains all 5s. The three tests + # below demonstrate that result varies depending upon the dimension + # that is collapsed. Collapsing both leads to a 3x3 array of 0.5. + # Collapsing just the realizations leads to a 2x3x3 array with values + # 0 for the first time, 1 for the second. Collapsing just the time + # dimension leads to a 2x3x3 array with values 0.5 for both realizations. + ( + { + "threshold_values": [3], + "collapse_coord": ["realization", "time"], + "collapse_cell_methods": {"realization": "mean", "time": "mean"}, + }, + 2, + 2, + np.r_[[2] * 9, [1] * 9, [4] * 9, [5] * 9].reshape((2, 2, 3, 3)), + np.full((3, 3), 0.5).astype(np.float32), + ), + # As above but only adding a cell method for one of the collapse coordinates + ( + { + "threshold_values": [3], + "collapse_coord": ["realization", "time"], + "collapse_cell_methods": {"time": "mean"}, + }, + 2, + 2, + np.r_[[2] * 9, [1] * 9, [4] * 9, [5] * 9].reshape((2, 2, 3, 3)), + np.full((3, 3), 0.5).astype(np.float32), + ), + # As above but only collapsing realizations + ( + { + "threshold_values": [3], + "collapse_coord": ["realization"], + "collapse_cell_methods": {"realization": "mean"}, + }, + 2, + 2, + np.r_[[2] * 9, [1] * 9, [4] * 9, [5] * 9].reshape((2, 2, 3, 3)), + np.stack([np.full((3, 3), 0), np.full((3, 3), 1)]).astype(np.float32), + ), + # As above but only collapsing time + ( + { + "threshold_values": [3], + "collapse_coord": ["time"], + "collapse_cell_methods": {"time": "mean"}, + }, + 2, + 2, + np.r_[[2] * 9, [1] * 9, [4] * 9, [5] * 9].reshape((2, 2, 3, 3)), + np.full((2, 3, 3), 0.5).astype(np.float32), + ), ], ) def test_bespoke_expected_values(custom_cube, kwargs, expected_result): @@ -480,6 +669,9 @@ def test_bespoke_expected_values(custom_cube, kwargs, expected_result): are tested elsewhere. - Collapsing and not collapsing the realization coordinate when present. + - Adding cell methods associated with the coordinates being collapsed. + - Adding cell methods associated with one of the coordinates being + collapsed. """ plugin = Threshold(**kwargs) result = plugin(custom_cube) @@ -490,16 +682,26 @@ def test_bespoke_expected_values(custom_cube, kwargs, expected_result): assert result.data.dtype == expected_result.dtype if np.ma.is_masked(result.data): assert (result.data.mask == expected_result.mask).all() + if "collapse_cell_methods" in kwargs: + for ii, cell_method in enumerate(result.cell_methods): + assert ( + cell_method.method == list(kwargs["collapse_cell_methods"].values())[ii] + ) + assert ( + cell_method.coord_names[0] + == list(kwargs["collapse_cell_methods"].keys())[ii] + ) @pytest.mark.parametrize( - "kwargs,n_realizations,data,mask,expected_result", + "kwargs,n_realizations,n_times,data,mask,expected_result", [ # the land corner has a value of 1, this is not spread across the # other points within the vicinity as these are sea points. ( {"threshold_values": 0.5, "vicinity": 3000}, 1, + 1, np.array([[0, 0, 1.0], [0, 0, 0], [0, 0, 0]]), np.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]]), np.array([[0, 0, 1.0], [0, 0, 0], [0, 0, 0]], dtype=np.float32), @@ -510,6 +712,7 @@ def test_bespoke_expected_values(custom_cube, kwargs, expected_result): ( {"threshold_values": 0.5, "vicinity": 3000}, 1, + 1, np.array([[0, 0, 0], [0, 0, 0], [1.0, 0, 0]]), np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), np.array([[0, 0, 0], [1.0, 0, 0], [1.0, 1.0, 0]], dtype=np.float32), @@ -520,10 +723,25 @@ def test_bespoke_expected_values(custom_cube, kwargs, expected_result): ( {"threshold_values": 0.5, "vicinity": 5000}, 1, + 1, np.array([[0, 0, 0], [0, 0, 0], [1.0, 0, 0]]), np.array([[1, 0, 1], [0, 1, 0], [0, 1, 0]]), np.array([[0, 1.0, 0], [1.0, 0, 1.0], [1.0, 0, 1.0]], dtype=np.float32), ), + # as above but data duplicated along realization and time coordinates + # which are then collapsed. + ( + { + "threshold_values": 0.5, + "vicinity": 5000, + "collapse_coord": ["realization", "time"], + }, + 2, + 2, + np.r_[[0] * 4, [1] * 2, [0] * 12].reshape((2, 3, 3), order="F"), + np.array([[1, 0, 1], [0, 1, 0], [0, 1, 0]]), + np.array([[0, 1.0, 0], [1.0, 0, 1.0], [1.0, 0, 1.0]], dtype=np.float32), + ), ], ) def test_vicinity_with_landmask(custom_cube, landmask, kwargs, expected_result): @@ -541,7 +759,9 @@ def test_vicinity_with_landmask(custom_cube, landmask, kwargs, expected_result): assert result.data.dtype == expected_result.dtype -@pytest.mark.parametrize("n_realizations,data", [(1, np.array([[0, np.nan], [1, 1]]))]) +@pytest.mark.parametrize( + "n_realizations,n_times,data", [(1, 1, np.array([[0, np.nan], [1, 1]]))] +) def test_nan_handling(custom_cube): """Test that an exception is raised if the input data contains an unmasked NaN.""" @@ -552,7 +772,7 @@ def test_nan_handling(custom_cube): @pytest.mark.parametrize( - "n_realizations,data,mask", [(1, np.zeros((2, 2)), np.zeros((2, 2)))] + "n_realizations,n_times,data,mask", [(1, 1, np.zeros((2, 2)), np.zeros((2, 2)))] ) def test_landmask_no_vicinity(custom_cube, landmask): """Test that an exception is raised if a landmask is provided but @@ -564,7 +784,7 @@ def test_landmask_no_vicinity(custom_cube, landmask): plugin(custom_cube, landmask) -@pytest.mark.parametrize("n_realizations,data", [(1, np.zeros((2, 2)))]) +@pytest.mark.parametrize("n_realizations,n_times,data", [(1, 1, np.zeros((2, 2)))]) def test_cell_methods(custom_cube): """Test that cell methods are modified as expected when present on the input cube.""" @@ -579,7 +799,7 @@ def test_cell_methods(custom_cube): assert cell_method.comments[0] == "of precipitation_rate" -@pytest.mark.parametrize("n_realizations,data", [(4, np.zeros((4, 2, 2)))]) +@pytest.mark.parametrize("n_realizations,n_times,data", [(4, 1, np.zeros((4, 2, 2)))]) def test_percentile_collapse(custom_cube): """Test that a percentile coordinate can be collapsed to calculate an average. These percentiles must be equally spaced and centred on From 37ef5fd27f4c9617248d5624edd0850f45581527 Mon Sep 17 00:00:00 2001 From: Katherine Tomkins Date: Wed, 19 Mar 2025 11:36:52 +0000 Subject: [PATCH 10/11] Eppt 2354 Extend humidity mixing ratio and update standardise behaviour (#2105) * add VirtualTemperature to PROCESSING_MODULES * added to CONTRIBUTING.md * formatting * widen search to any pressure cube in HumidityMixingRatio * updated comment * updated code comments * debug comments for virtual temp run * removed debug! * enforce units for humidity_mixing_ratio in virtual_temperature * further units * enforce hmr units before calculation * hmr-temp units before calc * vt only * add units to virtual temp cube * addresses review comment * updated PR with latest findings * virt temp unit debug * DEBUG 2 * debug 3 * debug 4 * debug 5 * removed debug * debug 6 * removed debug * debug 7 * debug 8 * debug 9 * removed debug * add units manually * ensure virtual temperature calculation via copy * remove copy in favour of scheduler processes * calculation on cube not data * adresses review comment about status flag * adresses review comment about status flag * addressed review comment * addresses review comments * better use of whitespace * possibly more than 2 cubes handled * weird typo --------- Co-authored-by: Katherine Tomkins Co-authored-by: Katherine Tomkins --- .../psychrometric_calculations.py | 19 ++++- improver/standardise.py | 26 ++++--- .../test_HumidityMixingRatio.py | 77 ++++++++++++++++++- 3 files changed, 109 insertions(+), 13 deletions(-) diff --git a/improver/psychrometric_calculations/psychrometric_calculations.py b/improver/psychrometric_calculations/psychrometric_calculations.py index 6708c37edd..f4177cef63 100644 --- a/improver/psychrometric_calculations/psychrometric_calculations.py +++ b/improver/psychrometric_calculations/psychrometric_calculations.py @@ -7,6 +7,7 @@ import functools from typing import List, Tuple, Union +import iris._constraints import numpy as np from iris.cube import Cube, CubeList from iris.exceptions import ConstraintMismatchError @@ -355,8 +356,20 @@ def process(self, *cubes: Union[Cube, CubeList]) -> Cube: ) try: - self.pressure = cubes.extract_cube("surface_air_pressure") - except ConstraintMismatchError: + # Test if there is one, and only one, cube with pressure in the name + def test_pressure(cube): + return True if "pressure" in cube.name() else False + + self.pressure = cubes.extract_cube(iris.Constraint(cube_func=test_pressure)) + + except ConstraintMismatchError as err: + # If more than one pressure cube is provided, raise an error explaining this + import re + + more_than_one = re.search(r"Got\s([2-9]|\d\d\d*)\scubes", str(err)) + if more_than_one: + raise ValueError(f"{more_than_one.group()} with 'pressure' in name.") + # If no pressure cube is provided, check if pressure is a coordinate in the temperature and relative humidity cubes temp_coord_flag = any( coord.name() == "pressure" for coord in self.temperature.coords() @@ -368,7 +381,7 @@ def process(self, *cubes: Union[Cube, CubeList]) -> Cube: self.generate_pressure_cube() else: raise ValueError( - "No pressure cube called 'surface_air_pressure' found and no pressure coordinate found in temperature or relative humidity cubes" + "No pressure cube with name 'pressure' found and no pressure coordinate found in temperature or relative humidity cubes" ) self.mandatory_attributes = generate_mandatory_attributes( diff --git a/improver/standardise.py b/improver/standardise.py index 4937fea071..444b5550d3 100644 --- a/improver/standardise.py +++ b/improver/standardise.py @@ -69,7 +69,7 @@ def __init__( self._attributes_dict = attributes_dict @staticmethod - def _rm_air_temperature_status_flag(cube: Cube) -> Cube: + def _remove_air_temperature_status_flag(cube: Cube) -> Cube: """ Remove air_temperature status_flag coord by applying as NaN to cube data. @@ -247,8 +247,8 @@ def _remove_long_name_if_standard_name(cube: Cube) -> None: def process(self, cube: Cube) -> Cube: """ - Perform compulsory and user-configurable metadata adjustments. The - compulsory adjustments are: + Perform compulsory and user-configurable metadata adjustments. + The compulsory adjustments are: - to collapse any scalar dimensions apart from realization (which is expected always to be a dimension); @@ -256,6 +256,11 @@ def process(self, cube: Cube) -> Cube: - to convert time-related metadata into the required units - to remove cell method ("point": "time"). + If the air_temperature data is required, this can be retained by + removing the `air_temperature status_flag` as part of the standardise step + so that the process of masking this data with NaNs is bypassed. + See https://github.com/metoppv/improver/pull/1839 for further information. + Args: cube: Input cube to be standardised @@ -264,24 +269,27 @@ def process(self, cube: Cube) -> Cube: The processed cube """ cube = as_cube(cube) - cube = self._rm_air_temperature_status_flag(cube) + # It is necessary to have the `_coords_to_remove step` first + # so that it allows keeping the air temperature data for + # a future calculation. Removing the `air_temperature status_flag` + # means the air temperature data will then not be masked by NaNs, + # as happens in the `_remove_air_temperature_status_flag` step if + # the flag is not removed. + if self._coords_to_remove: + self._remove_scalar_coords(cube, self._coords_to_remove) + cube = self._remove_air_temperature_status_flag(cube) cube = self._collapse_scalar_dimensions(cube) - if self._new_name: cube.rename(self._new_name) if self._new_units: cube.convert_units(self._new_units) - if self._coords_to_remove: - self._remove_scalar_coords(cube, self._coords_to_remove) if self._coord_modification: self._modify_scalar_coord_value(cube, self._coord_modification) if self._attributes_dict: amend_attributes(cube, self._attributes_dict) self._discard_redundant_cell_methods(cube) self._remove_long_name_if_standard_name(cube) - # this must be done after unit conversion as if the input is an integer # field, unit conversion outputs the new data as float64 self._standardise_dtypes_and_units(cube) - return cube diff --git a/improver_tests/psychrometric_calculations/test_HumidityMixingRatio.py b/improver_tests/psychrometric_calculations/test_HumidityMixingRatio.py index 3bc7620b05..539b77a10f 100644 --- a/improver_tests/psychrometric_calculations/test_HumidityMixingRatio.py +++ b/improver_tests/psychrometric_calculations/test_HumidityMixingRatio.py @@ -168,6 +168,38 @@ def test_height_levels(): assert np.isclose(result.data, 1.459832e-2, atol=1e-7).all() +def test_height_levels_above_surface(): + """Check that the plugin works with height level data""" + + temperature = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=293, dtype=np.float32), + name="air_temperature", + units="K", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[100, 400], + height=True, + ) + pressure_cube = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=100000, dtype=np.float32), + name="some_random_pressure", + units="Pa", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[100, 400], + height=True, + ) + rel_humidity = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=1.0, dtype=np.float32), + name="relative_humidity", + units="1", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[100, 400], + height=True, + ) + result = HumidityMixingRatio()([temperature, pressure_cube, rel_humidity]) + metadata_ok(result, temperature) + assert np.isclose(result.data, 1.459832e-2, atol=1e-7).all() + + def test_pressure_levels(): """Check that the plugin works with pressure level data when pressure cube is not provided""" temperature = set_up_variable_cube( @@ -198,7 +230,8 @@ def test_error_raised_no_pressure_coordinate_or_pressure_cube( """Check that the plugin raises an error if there is no pressure coordinate and no pressure cube""" with pytest.raises( ValueError, - match="No pressure cube called 'surface_air_pressure' found and no pressure coordinate", + match="No pressure cube with name 'pressure' found and no pressure coordinate " + "found in temperature or relative humidity cubes", ): HumidityMixingRatio()([temperature, rel_humidity]) @@ -213,3 +246,45 @@ def test_model_id_attr(temperature, pressure, rel_humidity, model_id_attr): [temperature, pressure, rel_humidity] ) metadata_ok(result, temperature, model_id_attr=model_id_attr) + + +def test_correct_value_error_returned_when_more_than_one_named_pressure(): + temperature = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=293, dtype=np.float32), + name="air_temperature", + units="K", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[95000, 100000], + pressure=True, + ) + rel_humidity = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=1.0, dtype=np.float32), + name="relative_humidity", + units="1", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[95000, 100000], + pressure=True, + ) + some_pressure = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=293, dtype=np.float32), + name="some_random_pressure", + units="K", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[95000, 100000], + pressure=True, + ) + some_more_pressure = set_up_variable_cube( + np.full((1, 2, 2, 2), fill_value=1.0, dtype=np.float32), + name="another_random_pressure", + units="1", + attributes=LOCAL_MANDATORY_ATTRIBUTES, + vertical_levels=[95000, 100000], + pressure=True, + ) + with pytest.raises( + ValueError, + match="Got 2 cubes with 'pressure' in name.", + ): + HumidityMixingRatio()( + [temperature, rel_humidity, some_pressure, some_more_pressure] + ) From 4241612f766ea487e8249a8de7aa902399d9bff2 Mon Sep 17 00:00:00 2001 From: Katherine Tomkins Date: Tue, 25 Mar 2025 10:38:19 +0000 Subject: [PATCH 11/11] workaround in virtual temperature (#2111) --- improver/virtual_temperature.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/improver/virtual_temperature.py b/improver/virtual_temperature.py index a3c7a69766..a9d3423caa 100644 --- a/improver/virtual_temperature.py +++ b/improver/virtual_temperature.py @@ -31,6 +31,12 @@ def get_virtual_temperature(temperature: Cube, humidity_mixing_ratio: Cube) -> C """ # Calculate the virtual temperature virtual_temperature = temperature * (1 + 0.61 * humidity_mixing_ratio) + # Workaround as cf-units id not correctly pickleable: + # https://github.com/SciTools/iris/issues/6378 + # The units get lost when being calculated as part of running a graph + # using the multiprocessing scheduler in dagrunner and so need to be + # added back after the calculation on line 33. + virtual_temperature.units = str(virtual_temperature.units) # Update the cube metadata virtual_temperature.rename("virtual_temperature")