Skip to content

Commit f663de5

Browse files
markysparksbayliffe
authored andcommitted
Impro-1121: Rename probability cubes - 1 (#813)
* initial changes for probability_of.... names in wxcodes and tests * further changes to wxcodes stuff, threshold.py and unit tests for both * separate naming of cubes now required for above/below in threshold.py * applied naming changes to set_up_test_cubes.py and associated unit test * formatting fixes/reverts * formatting fixes * fix incorrect renaming of cloud diagnostic sfc-1000ft * correction to wxcode bats test, further minor updates * update diagnostic probability names * update lightning nowcast files * fix missed update for cloud diagnostic name * update wxcode bats tests to work with new inpute file/cube probability names * some formatting fixes * changes made in response to reviewers comments * remove uneccessary np.flipud from unit test * doc changes in response to reviewers comments
1 parent f840aba commit f663de5

20 files changed

+326
-231
lines changed

bin/improver-wxcode

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ def interrogate_decision_tree(wxtree):
9292
return_index=True)
9393
output.append('{}; thresholds: {}'.format(
9494
requirement, ', '.join([
95-
'{} {} ({})'.format(str(relation),
96-
str(threshold.points.item()),
97-
str(threshold.units))
95+
'{} ({})'.format(str(threshold.points.item()),
96+
str(threshold.units))
9897
for threshold, relation in
9998
zip(entries[thresholds], relations[thresholds])])))
10099
return output

lib/improver/nowcasting/lightning.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def _update_metadata(cube):
186186
Modify the meta data of input cube to resemble a Nowcast of lightning
187187
probability.
188188
189-
1. Rename to "probability_of_lightning"
189+
1. Rename to "probability_of_lightning_rate_above_threshold"
190190
191191
2. Remove "threshold" coord
192192
(or causes iris.exceptions.CoordinateNotFoundError)
@@ -204,7 +204,7 @@ def _update_metadata(cube):
204204
The data array will be a copy of the input cube.data
205205
"""
206206
new_cube = cube.copy()
207-
new_cube.rename("probability_of_lightning")
207+
new_cube.rename("probability_of_lightning_rate_above_threshold")
208208
new_cube.remove_coord('threshold')
209209
new_cube.cell_methods = None
210210
return new_cube
@@ -465,16 +465,16 @@ def process(self, cubelist):
465465
If cubelist does not contain the expected cubes.
466466
"""
467467
first_guess_lightning_cube = cubelist.extract(
468-
"probability_of_lightning", strict=True)
468+
"probability_of_lightning_rate_above_threshold", strict=True)
469469
lightning_rate_cube = cubelist.extract(
470470
"rate_of_lightning", strict=True)
471471
lightning_rate_cube.convert_units("min^-1") # Ensure units are correct
472472
prob_precip_cube = cubelist.extract(
473-
"probability_of_precipitation", strict=True)
473+
"probability_of_precipitation_rate_above_threshold", strict=True)
474474
# Now find prob_vii_cube. Can't use strict=True here as cube may not be
475475
# present, so will use a normal extract and then merge_cube if needed.
476476
prob_vii_cube = cubelist.extract(
477-
"probability_of_vertical_integral_of_ice")
477+
"probability_of_vertical_integral_of_ice_above_threshold")
478478
if prob_vii_cube:
479479
prob_vii_cube = prob_vii_cube.merge_cube()
480480
prob_precip_cube.coord('threshold').convert_units('mm hr-1')

lib/improver/tests/blending/weighted_blend/test_WeightedBlendAcrossWholeDimension.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ def setUp(self):
183183
data_threshold[1, 1, :, :] = 0.6
184184
data_threshold[1, 2, :, :] = 0.8
185185

186-
cube_threshold = Cube(data_threshold,
187-
long_name="probability_of_precipitation_amount")
186+
cube_threshold = Cube(
187+
data_threshold,
188+
long_name="probability_of_precipitation_amount_below_threshold")
188189
cube_threshold.add_dim_coord(threshold, 0)
189190
cube_threshold.add_dim_coord(frt_coord, 1)
190191
cube_threshold.add_dim_coord(lat_coord, 2)

lib/improver/tests/blending/weighted_blend/test_rationalise_blend_time_coords.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@ def setUp(self):
5454
# coming from the UKV
5555
data = np.full((3, 3), 0.6, dtype=np.float32)
5656
self.ukv_cube = set_up_variable_cube(
57-
data, name='probability_of_air_temperature', units='1',
58-
time=datetime(2017, 1, 10, 3, 0), frt=datetime(2017, 1, 9, 23, 0),
59-
standard_grid_metadata='uk_det')
57+
data, name='probability_of_air_temperature_above_threshold',
58+
units='1', time=datetime(2017, 1, 10, 3, 0),
59+
frt=datetime(2017, 1, 9, 23, 0), standard_grid_metadata='uk_det')
6060

6161
# make a cube labelled as coming from MOGREPS-UK, with a different
6262
# forecast reference time from the UKV cube
6363
self.enuk_cube = set_up_variable_cube(
64-
data, name='probability_of_air_temperature', units='1',
65-
time=datetime(2017, 1, 10, 3, 0), frt=datetime(2017, 1, 10, 0, 0),
66-
standard_grid_metadata='uk_ens')
64+
data, name='probability_of_air_temperature_above_threshold',
65+
units='1', time=datetime(2017, 1, 10, 3, 0),
66+
frt=datetime(2017, 1, 10, 0, 0), standard_grid_metadata='uk_ens')
6767

6868
# make a cube list and merged cube containing the two model cubes, for
6969
# use in defining reference coordinates for tests below

lib/improver/tests/nowcasting/lightning/test_NowcastLightning.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ def test_basic(self):
115115
cell methods to be discarded."""
116116
result = self.plugin._update_metadata(self.cube)
117117
self.assertIsInstance(result, Cube)
118-
self.assertEqual(result.name(), "probability_of_lightning")
118+
self.assertEqual(
119+
result.name(), "probability_of_lightning_rate_above_threshold")
119120
msg = ("Expected to find exactly 1 threshold coordinate, but found "
120121
"none.")
121122
with self.assertRaisesRegex(CoordinateNotFoundError, msg):
@@ -427,21 +428,21 @@ def setUp(self):
427428
self.fg_cube:
428429
forecast_period (on time coord): 0.0 hours (simulates nowcast data)
429430
All points contain float(1.)
430-
Cube name is "probability_of_lightning".
431+
Cube name is "probability_of_lightning_rate_above_threshold".
431432
self.precip_cube:
432433
With extra coordinate of length(3) "threshold" containing
433434
points [0.5, 7., 35.] mm hr-1.
434435
All points contain float(1.) except the
435436
zero point [0, 0, 1, 1] which is float(0.)
436437
and [1:, 0, ...] which are float(0.)
437-
Cube name is "probability_of_precipitation".
438+
Cube name is "probability_of_precipitation_rate_above_threshold".
438439
Cube has added attribute {'relative_to_threshold': 'above'}
439440
"""
440441
self.fg_cube = add_forecast_reference_time_and_forecast_period(
441442
set_up_cube_with_no_realizations(zero_point_indices=[],
442443
num_grid_points=3),
443444
fp_point=0.0)
444-
self.fg_cube.rename("probability_of_lightning")
445+
self.fg_cube.rename("probability_of_lightning_rate_above_threshold")
445446
self.precip_cube = (
446447
add_forecast_reference_time_and_forecast_period(
447448
set_up_cube(num_realization_points=3,
@@ -451,7 +452,8 @@ def setUp(self):
451452
threshold_coord.points = [0.5, 7.0, 35.0]
452453
threshold_coord.rename('threshold')
453454
threshold_coord.units = cf_units.Unit('mm hr-1')
454-
self.precip_cube.rename("probability_of_precipitation")
455+
self.precip_cube.rename(
456+
"probability_of_precipitation_rate_above_threshold")
455457
self.precip_cube.attributes.update({'relative_to_threshold': 'above'})
456458
self.precip_cube.data[1:, 0, ...] = 0.
457459
self.plugin = Plugin()
@@ -598,19 +600,20 @@ def setUp(self):
598600
Data:
599601
self.fg_cube:
600602
All points contain float(1.)
601-
Cube name is "probability_of_lightning".
603+
Cube name is "probability_of_lightning_rate_above_threshold".
602604
self.ice_cube:
603605
With extra coordinate of length(3) "threshold" containing
604606
points [0.5, 1., 2.] kg m^-2.
605607
Time and forecast_period dimensions "sqeezed" to be Scalar coords.
606608
All points contain float(0.)
607-
Cube name is "probability_of_vertical_integral_of_ice".
609+
Cube name is
610+
"probability_of_vertical_integral_of_ice_above_threshold".
608611
"""
609612
self.fg_cube = add_forecast_reference_time_and_forecast_period(
610613
set_up_cube_with_no_realizations(zero_point_indices=[],
611614
num_grid_points=3),
612615
fp_point=0.0)
613-
self.fg_cube.rename("probability_of_lightning")
616+
self.fg_cube.rename("probability_of_lightning_rate_above_threshold")
614617
self.ice_cube = squeeze(
615618
add_forecast_reference_time_and_forecast_period(
616619
set_up_cube(num_realization_points=3,
@@ -622,7 +625,8 @@ def setUp(self):
622625
threshold_coord.rename('threshold')
623626
threshold_coord.units = cf_units.Unit('kg m^-2')
624627
self.ice_cube.data = np.zeros_like(self.ice_cube.data)
625-
self.ice_cube.rename("probability_of_vertical_integral_of_ice")
628+
self.ice_cube.rename(
629+
"probability_of_vertical_integral_of_ice_above_threshold")
626630
self.plugin = Plugin()
627631

628632
def test_basic(self):
@@ -748,7 +752,7 @@ def setUp(self):
748752
Data:
749753
self.fg_cube:
750754
All points contain float(1.)
751-
Cube name is "probability_of_lightning".
755+
Cube name is "probability_of_lightning_rate_above_threshold".
752756
self.ltng_cube:
753757
forecast_period (on time coord): 0.0 hours (simulates nowcast data)
754758
All points contain float(1.)
@@ -760,7 +764,7 @@ def setUp(self):
760764
All points contain float(1.) except the
761765
zero point [0, 0, 7, 7] which is float(0.)
762766
and [1:, 0, ...] which are float(0.)
763-
Cube name is "probability_of_precipitation".
767+
Cube name is "probability_of_precipitation_rate_above_threshold".
764768
Cube has added attribute {'relative_to_threshold': 'above'}
765769
self.vii_cube:
766770
forecast_period (on time coord): 0.0 hours (simulates nowcast data)
@@ -769,11 +773,12 @@ def setUp(self):
769773
forecast_period (on time coord): 0.0 hours (simulates nowcast data)
770774
Time and forecast_period dimensions "sqeezed" to be Scalar coords.
771775
All points contain float(0.)
772-
Cube name is "probability_of_vertical_integral_of_ice".
776+
Cube name is
777+
"probability_of_vertical_integral_of_ice_above_threshold".
773778
"""
774779
self.fg_cube = add_forecast_reference_time_and_forecast_period(
775780
set_up_cube_with_no_realizations(zero_point_indices=[]))
776-
self.fg_cube.rename("probability_of_lightning")
781+
self.fg_cube.rename("probability_of_lightning_rate_above_threshold")
777782
self.ltng_cube = add_forecast_reference_time_and_forecast_period(
778783
set_up_cube_with_no_realizations(zero_point_indices=[]),
779784
fp_point=0.0)
@@ -786,7 +791,8 @@ def setUp(self):
786791
threshold_coord.points = [0.5, 7.0, 35.0]
787792
threshold_coord.rename('threshold')
788793
threshold_coord.units = cf_units.Unit('mm hr-1')
789-
self.precip_cube.rename("probability_of_precipitation")
794+
self.precip_cube.rename(
795+
"probability_of_precipitation_rate_above_threshold")
790796
self.precip_cube.attributes.update({'relative_to_threshold': 'above'})
791797
self.precip_cube.data[1:, 0, ...] = 0.
792798
self.vii_cube = squeeze(
@@ -799,7 +805,8 @@ def setUp(self):
799805
threshold_coord.rename('threshold')
800806
threshold_coord.units = cf_units.Unit('kg m^-2')
801807
self.vii_cube.data = np.zeros_like(self.vii_cube.data)
802-
self.vii_cube.rename("probability_of_vertical_integral_of_ice")
808+
self.vii_cube.rename(
809+
"probability_of_vertical_integral_of_ice_above_threshold")
803810
self.plugin = Plugin()
804811

805812
def set_up_vii_input_output(self):
@@ -860,7 +867,8 @@ def test_basic(self):
860867
# We expect the threshold coordinate to have been removed.
861868
self.assertCountEqual(find_dimension_coordinate_mismatch(
862869
result, self.precip_cube), ['threshold'])
863-
self.assertTrue(result.name() == 'probability_of_lightning')
870+
self.assertTrue(
871+
result.name() == 'probability_of_lightning_rate_above_threshold')
864872

865873
def test_basic_with_vii(self):
866874
"""Test that the method returns the expected cube type when vii is
@@ -874,13 +882,14 @@ def test_basic_with_vii(self):
874882
# We expect the threshold coordinate to have been removed.
875883
self.assertCountEqual(find_dimension_coordinate_mismatch(
876884
result, self.precip_cube), ['threshold'])
877-
self.assertTrue(result.name() == 'probability_of_lightning')
885+
self.assertTrue(
886+
result.name() == 'probability_of_lightning_rate_above_threshold')
878887

879888
def test_no_first_guess_cube(self):
880889
"""Test that the method raises an error if the first_guess cube is
881890
omitted from the cubelist"""
882891
msg = (r"Got 0 cubes for constraint Constraint\(name=\'probability_of_"
883-
r"lightning\'\), expecting 1.")
892+
r"lightning_rate_above_threshold\'\), expecting 1.")
884893
with self.assertRaisesRegex(ConstraintMismatchError, msg):
885894
self.plugin.process(CubeList([
886895
self.ltng_cube,
@@ -900,7 +909,7 @@ def test_no_precip_cube(self):
900909
"""Test that the method raises an error if the precip cube is
901910
omitted from the cubelist"""
902911
msg = (r"Got 0 cubes for constraint Constraint\(name=\'probability_of_"
903-
r"precipitation\'\), expecting 1.")
912+
r"precipitation_rate_above_threshold\'\), expecting 1.")
904913
with self.assertRaisesRegex(ConstraintMismatchError, msg):
905914
self.plugin.process(CubeList([
906915
self.fg_cube,

lib/improver/tests/set_up_test_cubes.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ def set_up_probability_cube(data, thresholds, variable_name='air_temperature',
323323
- option to specify additional scalar coordinates
324324
- "relative_to_threshold" attribute (default "above")
325325
- default or configurable attributes
326-
- configurable cube data, name conforms to "probability_of_X" convention
326+
- configurable cube data, name conforms to
327+
"probability_of_X_above(or below)_threshold" convention
327328
328329
Args:
329330
data (np.ndarray):
@@ -366,9 +367,19 @@ def set_up_probability_cube(data, thresholds, variable_name='air_temperature',
366367
attributes['relative_to_threshold'] = relative_to_threshold
367368

368369
if variable_name.startswith('probability_of_'):
369-
name = variable_name
370+
name_prefix = ''
370371
else:
371-
name = 'probability_of_{}'.format(variable_name)
372+
name_prefix = 'probability_of_'
373+
374+
if relative_to_threshold == 'above':
375+
name = name_prefix + '{}_above_threshold'.format(variable_name)
376+
elif relative_to_threshold == 'below':
377+
name = name_prefix + '{}_below_threshold'.format(variable_name)
378+
else:
379+
msg = 'The relative_to_threshold attribute MUST be set for ' \
380+
'IMPROVER probability cubes'
381+
raise ValueError(msg)
382+
372383
cube = set_up_variable_cube(
373384
data, name=name, units='1', spatial_grid=spatial_grid,
374385
time=time, frt=frt, time_bounds=time_bounds,

lib/improver/tests/test_set_up_test_cubes.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,8 @@ def test_defaults(self):
337337
and metadata"""
338338
result = set_up_probability_cube(self.data, self.thresholds)
339339
thresh_coord = result.coord("threshold")
340-
self.assertEqual(result.name(), 'probability_of_air_temperature')
340+
self.assertEqual(
341+
result.name(), 'probability_of_air_temperature_above_threshold')
341342
self.assertEqual(result.units, '1')
342343
self.assertArrayEqual(thresh_coord.points, self.thresholds)
343344
self.assertEqual(thresh_coord.units, 'K')
@@ -349,7 +350,8 @@ def test_probability_of_name(self):
349350
result = set_up_probability_cube(
350351
self.data, self.thresholds,
351352
variable_name='probability_of_air_temperature')
352-
self.assertEqual(result.name(), 'probability_of_air_temperature')
353+
self.assertEqual(
354+
result.name(), 'probability_of_air_temperature_above_threshold')
353355

354356
def test_relative_to_threshold(self):
355357
"""Test ability to reset the "relative_to_threshold" attribute"""
@@ -359,6 +361,14 @@ def test_relative_to_threshold(self):
359361
self.assertEqual(len(result.attributes), 1)
360362
self.assertEqual(result.attributes['relative_to_threshold'], 'below')
361363

364+
def test_relative_to_threshold_set(self):
365+
"""Test that an error is raised if the "relative_to_threshold"
366+
attribute has not been set when setting up a probability cube"""
367+
msg = 'The relative_to_threshold attribute MUST be set'
368+
with self.assertRaisesRegex(ValueError, msg):
369+
set_up_probability_cube(self.data, self.thresholds,
370+
relative_to_threshold=None)
371+
362372
def test_standard_grid_metadata(self):
363373
"""Test standard grid metadata"""
364374
result = set_up_probability_cube(self.data, self.thresholds,

lib/improver/tests/threshold/test_BasicThreshold.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def test_metadata_changes(self):
190190
plugin = Threshold(0.1)
191191
result = plugin.process(cube)
192192
# The single 0.5-valued point => 1.0, so cheat by * 2.0 vs orig data.
193-
name = "probability_of_{}"
193+
name = "probability_of_{}_above_threshold"
194194
expected_name = name.format(self.cube.name())
195195
expected_attribute = "above"
196196
expected_units = 1

lib/improver/tests/utilities/test_cube_extraction.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,16 @@ def set_up_precip_probability_cube():
5858
[0.02, 0.02, 0.00],
5959
[0.01, 0.00, 0.00]]])
6060

61-
MMH_TO_MS = 0.001/3600.
62-
threshold = DimCoord(MMH_TO_MS*np.array([0.03, 0.1, 1.0]),
61+
MMH_TO_MS = 0.001 / 3600.
62+
threshold = DimCoord(MMH_TO_MS * np.array([0.03, 0.1, 1.0]),
6363
long_name="threshold", units="m s-1")
6464
ycoord = DimCoord(np.arange(3), "projection_y_coordinate")
6565
xcoord = DimCoord(np.arange(3), "projection_x_coordinate")
6666

67-
cube = iris.cube.Cube(data, long_name="probability_of_precipitation",
68-
dim_coords_and_dims=[(threshold, 0), (ycoord, 1),
69-
(xcoord, 2)], units="1")
67+
cube = iris.cube.Cube(
68+
data, long_name="probability_of_precipitation_rate_above_threshold",
69+
dim_coords_and_dims=[(threshold, 0), (ycoord, 1),
70+
(xcoord, 2)], units="1")
7071
return cube
7172

7273

@@ -189,7 +190,8 @@ def setUp(self):
189190

190191
def test_basic_no_units(self):
191192
""" Test cube extraction for single constraint without units """
192-
constraint_dict = {"name": "probability_of_precipitation"}
193+
constraint_dict = {
194+
"name": "probability_of_precipitation_rate_above_threshold"}
193195
constr = iris.Constraint(**constraint_dict)
194196
cube = apply_extraction(self.precip_cube, constr)
195197
self.assertIsInstance(cube, iris.cube.Cube)
@@ -208,8 +210,9 @@ def test_basic_with_units(self):
208210

209211
def test_multiple_constraints_with_units(self):
210212
""" Test behaviour with a list of constraints and units """
211-
constraint_dict = {"name": "probability_of_precipitation",
212-
"threshold": 0.03}
213+
constraint_dict = {
214+
"name": "probability_of_precipitation_rate_above_threshold",
215+
"threshold": 0.03}
213216
constr = iris.Constraint(**constraint_dict)
214217
cube = apply_extraction(self.precip_cube, constr, self.units_dict)
215218
self.assertIsInstance(cube, iris.cube.Cube)
@@ -219,7 +222,8 @@ def test_multiple_constraints_with_units(self):
219222
def test_error_non_coord_units(self):
220223
""" Test error raised if units are provided for a non-coordinate
221224
constraint """
222-
constraint_dict = {"name": "probability_of_precipitation"}
225+
constraint_dict = {
226+
"name": "probability_of_precipitation_rate_above_threshold"}
223227
units_dict = {"name": "1"}
224228
with self.assertRaises(CoordinateNotFoundError):
225229
apply_extraction(self.precip_cube, constraint_dict, units_dict)
@@ -228,8 +232,9 @@ def test_return_none(self):
228232
""" Test function returns None rather than raising an error where
229233
no subcubes match the required constraints, when unit conversion is
230234
required """
231-
constraint_dict = {"name": "probability_of_precipitation",
232-
"threshold": 5}
235+
constraint_dict = {
236+
"name": "probability_of_precipitation_rate_above_threshold",
237+
"threshold": 5}
233238
constr = iris.Constraint(**constraint_dict)
234239
cube = apply_extraction(self.precip_cube, constr, self.units_dict)
235240
self.assertFalse(cube)

0 commit comments

Comments
 (0)