From 257790c26d26dc998b2b14907d7c71d23d52459b Mon Sep 17 00:00:00 2001 From: Valeriu Predoi Date: Thu, 17 Nov 2022 15:26:10 +0000 Subject: [PATCH 1/5] add generalized mask prototype --- esmvalcore/preprocessor/_mask.py | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/esmvalcore/preprocessor/_mask.py b/esmvalcore/preprocessor/_mask.py index 05089da609..8ce962f887 100644 --- a/esmvalcore/preprocessor/_mask.py +++ b/esmvalcore/preprocessor/_mask.py @@ -701,3 +701,49 @@ def _get_fillvalues_mask(cube, threshold_fraction, min_value, time_window): mask = mask.data | mask.mask return mask + + +def mask_generalized(cube, mask_cube, mask_operation): + """Mask out either landsea (combined) or ice. + + Function that masks out data in a given cube, based on a + user-defined operation and a secondary cube (used as mask generator). + + Parameters + ---------- + cube: iris.cube.Cube + data cube to be masked. + + mask_cube: iris.cube.Cube + data cube to be used as mask. + + mask_operation: str + conditional operation that generates a mask on cube. + + Returns + ------- + iris.cube.Cube + Returns masked iris cube after applying mask_operation via mask_cube. + + Raises + ------ + ValueError + Error raised if landsea-ice mask not found as an ancillary variable. + """ + if not isinstance(mask_operation, dict): + raise ValueError(f"A valid masking operation dictionary is " + f"needed, got mask_operation {mask_operation}") + + # use case 1: mask mask_cube above a certain threshold + # then get its mask and add it to the input cube's one (if any) + if "above_threshold" in mask_operation: + if not "threshold" in mask_operation: + raise KeyError('A valid "threshold" parameter must be specified ' + 'for above_threshold mask_operation') + masked_above_th = mask_above_threshold(mask_cube, threshold) + + cubes = iris.cube.CubeList([cube, nasked_above_th]) + cube = _multimodel_mask_cubes(cubes, cube.shape)[0] + + return cube + From 40a2a87e403d07f3fc5aec2db671d88768b39f77 Mon Sep 17 00:00:00 2001 From: Valeriu Predoi Date: Thu, 17 Nov 2022 16:05:45 +0000 Subject: [PATCH 2/5] add test for use case --- tests/unit/preprocessor/_mask/test_mask.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/unit/preprocessor/_mask/test_mask.py b/tests/unit/preprocessor/_mask/test_mask.py index a6b28e2cae..0b7aa02e4a 100644 --- a/tests/unit/preprocessor/_mask/test_mask.py +++ b/tests/unit/preprocessor/_mask/test_mask.py @@ -5,6 +5,7 @@ import numpy as np import iris +import iris.fileformats import tests from cf_units import Unit from esmvalcore.preprocessor._mask import (_apply_fx_mask, @@ -12,7 +13,8 @@ mask_above_threshold, mask_below_threshold, mask_glaciated, mask_inside_range, - mask_outside_range) + mask_outside_range, + mask_generalized) class Test(tests.Test): @@ -126,6 +128,17 @@ def test_mask_outside_range(self): expected = np.ma.array(self.data2, mask=[[True, False], [False, True]]) self.assert_array_equal(result.data, expected) + def test_mask_generalized_above_threshold(self): + """Test mask generalized.""" + cube = self.time_cube + mask_cube = self.time_cube + mask_operation = {"above_threshold": True, "threshold": 2.0} + result = mask_generalized(cube, mask_cube, mask_operation) + self.assert_array_equal(result.shape, (24,)) + self.assert_array_equal(result.data, cube.data) + mask = np.ones((24), bool) + mask[0:2] = False + self.assert_array_equal(result.data.mask, mask) if __name__ == '__main__': unittest.main() From 17ecfb1240a580c72df2c304c1875d4a69e9a9b5 Mon Sep 17 00:00:00 2001 From: Valeriu Predoi Date: Thu, 17 Nov 2022 16:05:58 +0000 Subject: [PATCH 3/5] fix issues --- esmvalcore/preprocessor/_mask.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esmvalcore/preprocessor/_mask.py b/esmvalcore/preprocessor/_mask.py index 8ce962f887..e46f43e50c 100644 --- a/esmvalcore/preprocessor/_mask.py +++ b/esmvalcore/preprocessor/_mask.py @@ -740,9 +740,10 @@ def mask_generalized(cube, mask_cube, mask_operation): if not "threshold" in mask_operation: raise KeyError('A valid "threshold" parameter must be specified ' 'for above_threshold mask_operation') + threshold = mask_operation["threshold"] masked_above_th = mask_above_threshold(mask_cube, threshold) - cubes = iris.cube.CubeList([cube, nasked_above_th]) + cubes = iris.cube.CubeList([cube, masked_above_th]) cube = _multimodel_mask_cubes(cubes, cube.shape)[0] return cube From 80aafcb286ec9ba98f44536f82928fb64dff744f Mon Sep 17 00:00:00 2001 From: Valeriu Predoi Date: Thu, 17 Nov 2022 16:09:31 +0000 Subject: [PATCH 4/5] flake8 --- tests/unit/preprocessor/_mask/test_mask.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/preprocessor/_mask/test_mask.py b/tests/unit/preprocessor/_mask/test_mask.py index 0b7aa02e4a..f9e20114fb 100644 --- a/tests/unit/preprocessor/_mask/test_mask.py +++ b/tests/unit/preprocessor/_mask/test_mask.py @@ -140,5 +140,6 @@ def test_mask_generalized_above_threshold(self): mask[0:2] = False self.assert_array_equal(result.data.mask, mask) + if __name__ == '__main__': unittest.main() From 7e318cb6a1505c0cce662fa8d3693a8307a6a733 Mon Sep 17 00:00:00 2001 From: Valeriu Predoi Date: Thu, 17 Nov 2022 16:11:22 +0000 Subject: [PATCH 5/5] flake8 --- esmvalcore/preprocessor/_mask.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esmvalcore/preprocessor/_mask.py b/esmvalcore/preprocessor/_mask.py index e46f43e50c..0efde0c71e 100644 --- a/esmvalcore/preprocessor/_mask.py +++ b/esmvalcore/preprocessor/_mask.py @@ -737,7 +737,7 @@ def mask_generalized(cube, mask_cube, mask_operation): # use case 1: mask mask_cube above a certain threshold # then get its mask and add it to the input cube's one (if any) if "above_threshold" in mask_operation: - if not "threshold" in mask_operation: + if "threshold" not in mask_operation: raise KeyError('A valid "threshold" parameter must be specified ' 'for above_threshold mask_operation') threshold = mask_operation["threshold"] @@ -747,4 +747,3 @@ def mask_generalized(cube, mask_cube, mask_operation): cube = _multimodel_mask_cubes(cubes, cube.shape)[0] return cube -