Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle fill value on netCDF save #2747

Merged
merged 18 commits into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* When saving a cube or list of cubes in NetCDF format, a fill value or list of fill values can be specified via a new `fill_value` argument. If a list is supplied, each fill value will be applied to each cube in turn. If a `fill_value` argument is not specified, the default fill value for the file format and the cube's data type will be used. Fill values are no longer taken from the cube's `data` attribute when it is a masked array.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* A 'fill_value' key can no longer be specified as part of the `packing` argument to `iris.save` when saving in netCDF format. Instead, a fill value or list of fill values should be specified as a separate `fill_value` argument if required.

* If the `packing` argument to `iris.save` is a dictionary, an error is raised if it contains any keys other than 'dtype', 'scale_factor' and 'add_offset'.
33 changes: 0 additions & 33 deletions lib/iris/_lazy_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,39 +131,6 @@ def as_concrete_data(data):
return data


def get_fill_value(data):
"""
Return the fill value of a concrete or lazy masked array.

Args:

* data:
A dask array, NumPy `ndarray` or masked array.

Returns:
The fill value of `data` if `data` represents a masked array, or None.

"""
# If lazy, get the smallest slice of the data from which we can retrieve
# the fill_value.
if is_lazy_data(data):
inds = [0] * data.ndim
if inds:
inds[-1] = slice(0, 1)
data = data[tuple(inds)]
data = as_concrete_data(data)
else:
if isinstance(data, ma.core.MaskedConstant):
data = ma.array(data.data, mask=data.mask)

# Now get the fill value.
fill_value = None
if ma.isMaskedArray(data):
fill_value = data.fill_value

return fill_value


def multidim_lazy_stack(stack):
"""
Recursively build a multidimensional stacked dask array.
Expand Down
251 changes: 160 additions & 91 deletions lib/iris/fileformats/netcdf.py

Large diffs are not rendered by default.

16 changes: 0 additions & 16 deletions lib/iris/tests/integration/test_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ def test_shared_primary(self):
self.assertRaisesRegexp(ValueError, 'multiple aux factories'):
iris.save(cube, filename)

@tests.skip_dask_mask
def test_hybrid_height_cubes(self):
hh1 = stock.simple_4d_with_hybrid_height()
hh1.attributes['cube'] = 'hh1'
Expand Down Expand Up @@ -211,20 +210,6 @@ def test_lazy_preserved_save(self):
saver.write(acube)
self.assertTrue(acube.has_lazy_data())

@tests.skip_dask_mask
def test_lazy_mask_preserve_fill_value(self):
data = ma.array([0, 1], mask=[False, True])
cube = iris.cube.Cube(data, fill_value=-1)
with self.temp_filename(suffix='.nc') as filename, \
self.temp_filename(suffix='.nc') as other_filename:
iris.save(cube, filename, unlimited_dimensions=[])
ncube = iris.load_cube(filename)
# Lazy save of the masked cube
iris.save(ncube, other_filename, unlimited_dimensions=[])
ds = nc.Dataset(other_filename, 'r')
avar = ds['unknown']
self.assertEqual(avar._FillValue, -1)


@tests.skip_data
class TestCellMeasures(tests.IrisTest):
Expand Down Expand Up @@ -428,7 +413,6 @@ def test_multi_packed_single_dtype(self):
# Read PP input file.
self._multi_test('multi_packed_single_dtype.cdl')

@tests.skip_dask_mask
def test_multi_packed_multi_dtype(self):
"""Test saving multiple packed cubes with pack_dtype list."""
# Read PP input file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ variables:
height:standard_name = "height" ;
height:positive = "up" ;
float precipitation_flux(time, latitude, longitude) ;
precipitation_flux:_FillValue = -1.e+30f ;
precipitation_flux:standard_name = "precipitation_flux" ;
precipitation_flux:units = "kg m-2 s-1" ;
precipitation_flux:um_stash_source = "m01s05i216" ;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<cubes xmlns="urn:x-iris:cubeml-0.2">
<cube core-dtype="float64" dtype="int64" standard_name="air_temperature" units="K" var_name="air_temperature">
<cube core-dtype="int64" dtype="int64" standard_name="air_temperature" units="K" var_name="air_temperature">
<attributes>
<attribute name="Conventions" value="CF-1.5"/>
<attribute name="cube" value="hh1"/>
Expand Down Expand Up @@ -64,7 +64,7 @@
<cellMethods/>
<data checksum="0x2acdccca" dtype="int64" shape="(3, 4, 5, 6)"/>
</cube>
<cube core-dtype="float64" dtype="int64" standard_name="air_temperature" units="K" var_name="air_temperature_0">
<cube core-dtype="int64" dtype="int64" standard_name="air_temperature" units="K" var_name="air_temperature_0">
<attributes>
<attribute name="Conventions" value="CF-1.5"/>
<attribute name="cube" value="hh2"/>
Expand Down
6 changes: 3 additions & 3 deletions lib/iris/tests/results/netcdf/netcdf_monotonic.cml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<cubes xmlns="urn:x-iris:cubeml-0.2">
<cube core-dtype="float64" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind1">
<cube core-dtype="int32" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind1">
<attributes>
<attribute name="test" value="weak-monotonic time coordinate"/>
</attributes>
Expand All @@ -18,7 +18,7 @@
<cellMethods/>
<data checksum="0xd4e7a32f" dtype="int32" shape="(3, 3, 3)"/>
</cube>
<cube core-dtype="float64" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind2">
<cube core-dtype="int32" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind2">
<attributes>
<attribute name="test" value="masked monotonic time coordinate"/>
</attributes>
Expand All @@ -36,7 +36,7 @@
<cellMethods/>
<data checksum="0xd4e7a32f" dtype="int32" shape="(3, 3, 3)"/>
</cube>
<cube core-dtype="float64" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind3">
<cube core-dtype="int32" dtype="int32" standard_name="eastward_wind" units="m s-1" var_name="wind3">
<attributes>
<attribute name="test" value="masked non-monotonic time coordinate"/>
</attributes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ dimensions:
model_level_number = 10 ;
variables:
float air_potential_temperature(time, model_level_number, grid_latitude, grid_longitude) ;
air_potential_temperature:_FillValue = -1.073742e+09f ;
air_potential_temperature:standard_name = "air_potential_temperature" ;
air_potential_temperature:units = "K" ;
air_potential_temperature:um_stash_source = "m01s00i004" ;
Expand Down
1 change: 0 additions & 1 deletion lib/iris/tests/results/netcdf/netcdf_save_multi_0.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dimensions:
longitude = 96 ;
variables:
float air_temperature(time, latitude, longitude) ;
air_temperature:_FillValue = -1.e+30f ;
air_temperature:standard_name = "air_temperature" ;
air_temperature:units = "K" ;
air_temperature:um_stash_source = "m01s03i236" ;
Expand Down
1 change: 0 additions & 1 deletion lib/iris/tests/results/netcdf/netcdf_save_multi_1.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dimensions:
longitude = 96 ;
variables:
float air_temperature(time, latitude, longitude) ;
air_temperature:_FillValue = -1.e+30f ;
air_temperature:standard_name = "air_temperature" ;
air_temperature:units = "K" ;
air_temperature:um_stash_source = "m01s03i236" ;
Expand Down
1 change: 0 additions & 1 deletion lib/iris/tests/results/netcdf/netcdf_save_multi_2.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dimensions:
longitude = 96 ;
variables:
float precipitation_flux(time, latitude, longitude) ;
precipitation_flux:_FillValue = -1.e+30f ;
precipitation_flux:standard_name = "precipitation_flux" ;
precipitation_flux:units = "kg m-2 s-1" ;
precipitation_flux:um_stash_source = "m01s05i216" ;
Expand Down
3 changes: 0 additions & 3 deletions lib/iris/tests/results/netcdf/netcdf_save_multiple.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dimensions:
longitude = 96 ;
variables:
float air_temperature(time, latitude, longitude) ;
air_temperature:_FillValue = -1.e+30f ;
air_temperature:standard_name = "air_temperature" ;
air_temperature:units = "K" ;
air_temperature:um_stash_source = "m01s03i236" ;
Expand Down Expand Up @@ -45,15 +44,13 @@ variables:
height:standard_name = "height" ;
height:positive = "up" ;
float air_temperature_0(time, latitude, longitude) ;
air_temperature_0:_FillValue = -1.e+30f ;
air_temperature_0:standard_name = "air_temperature" ;
air_temperature_0:units = "K" ;
air_temperature_0:um_stash_source = "m01s03i236" ;
air_temperature_0:cell_methods = "time: minimum (interval: 1 hour)" ;
air_temperature_0:grid_mapping = "latitude_longitude" ;
air_temperature_0:coordinates = "forecast_period forecast_reference_time height" ;
float precipitation_flux(time, latitude, longitude) ;
precipitation_flux:_FillValue = -1.e+30f ;
precipitation_flux:standard_name = "precipitation_flux" ;
precipitation_flux:units = "kg m-2 s-1" ;
precipitation_flux:um_stash_source = "m01s05i216" ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dimensions:
rlon = 174 ;
variables:
float pr(time, rlat, rlon) ;
pr:_FillValue = 1.e+30f ;
pr:standard_name = "precipitation_flux" ;
pr:long_name = "Precipitation" ;
pr:units = "kg m-2 s-1" ;
Expand Down
1 change: 0 additions & 1 deletion lib/iris/tests/results/netcdf/netcdf_save_single.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ dimensions:
longitude = 96 ;
variables:
float air_temperature(latitude, longitude) ;
air_temperature:_FillValue = -1.e+30f ;
air_temperature:standard_name = "air_temperature" ;
air_temperature:units = "K" ;
air_temperature:um_stash_source = "m01s03i236" ;
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/netcdf/netcdf_units_1.cml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<cubes xmlns="urn:x-iris:cubeml-0.2">
<cube core-dtype="float64" dtype="int32" standard_name="air_temperature" units="unknown" var_name="cube_1">
<cube core-dtype="int32" dtype="int32" standard_name="air_temperature" units="unknown" var_name="cube_1">
<attributes>
<attribute name="invalid_units" value="kevin"/>
</attributes>
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/netcdf/uint32_data_netcdf3.cml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<cubes xmlns="urn:x-iris:cubeml-0.2">
<cube core-dtype="int64" dtype="int32" standard_name="surface_temperature" units="K" var_name="temp">
<cube core-dtype="int32" dtype="int32" standard_name="surface_temperature" units="K" var_name="temp">
<attributes>
<attribute name="Conventions" value="CF-1.5"/>
</attributes>
Expand Down
11 changes: 1 addition & 10 deletions lib/iris/tests/test_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@

@tests.skip_data
class TestNetCDFLoad(tests.IrisTest):
@tests.skip_dask_mask
def test_monotonic(self):
cubes = iris.load(tests.get_data_path(
('NetCDF', 'testing', 'test_monotonic_coordinate.nc')))
Expand Down Expand Up @@ -84,7 +83,6 @@ def test_missing_time_bounds(self):
dataset.close()
cube = iris.load_cube(filename, 'eastward_wind')

@tests.skip_dask_mask
def test_load_global_xyzt_gems(self):
# Test loading single xyzt CF-netCDF file (multi-cube).
cubes = iris.load(tests.get_data_path(('NetCDF', 'global', 'xyz_t',
Expand All @@ -96,7 +94,7 @@ def test_load_global_xyzt_gems(self):
# manager loading.
lnsp = cubes[1]
self.assertTrue(ma.isMaskedArray(lnsp.data))
self.assertEqual(-32767.0, lnsp.fill_value)
self.assertEqual(-32767.0, lnsp.data.fill_value)

def test_load_global_xyzt_gems_iter(self):
# Test loading stepped single xyzt CF-netCDF file (multi-cube).
Expand Down Expand Up @@ -245,7 +243,6 @@ def test_deferred_loading(self):
self.assertCML(cube[0][(0, 2), (1, 3)],
('netcdf', 'netcdf_deferred_mix_1.cml'))

@tests.skip_dask_mask
def test_units(self):
# Test exercising graceful cube and coordinate units loading.
cube0, cube1 = sorted(iris.load(tests.get_data_path(('NetCDF',
Expand Down Expand Up @@ -426,7 +423,6 @@ def test_netcdf_save_format(self):
with self.assertRaises(ValueError):
iris.save(cube, file_out, netcdf_format='WIBBLE')

@tests.skip_dask_mask
@tests.skip_data
def test_netcdf_save_single(self):
# Test saving a single CF-netCDF file.
Expand All @@ -445,7 +441,6 @@ def test_netcdf_save_single(self):

# TODO investigate why merge now make time an AuxCoord rather than a
# DimCoord and why forecast_period is 'preferred'.
@tests.skip_dask_mask
@tests.skip_data
def test_netcdf_save_multi2multi(self):
# Test saving multiple CF-netCDF files.
Expand All @@ -464,7 +459,6 @@ def test_netcdf_save_multi2multi(self):
self.assertCDL(file_out, ('netcdf',
'netcdf_save_multi_%d.cdl' % index))

@tests.skip_dask_mask
@tests.skip_data
def test_netcdf_save_multi2single(self):
# Test saving multiple cubes to a single CF-netCDF file.
Expand Down Expand Up @@ -565,7 +559,6 @@ def test_netcdf_multi_conflict_name_dup_coord(self):
self.assertCDL(
file_out, ('netcdf', 'multi_dim_coord_slightly_different.cdl'))

@tests.skip_dask_mask
@tests.skip_data
def test_netcdf_hybrid_height(self):
# Test saving a CF-netCDF file which contains a hybrid height
Expand All @@ -590,7 +583,6 @@ def test_netcdf_hybrid_height(self):
self.assertCML(cube,
('netcdf', 'netcdf_save_load_hybrid_height.cml'))

@tests.skip_dask_mask
@tests.skip_data
def test_netcdf_save_ndim_auxiliary(self):
# Test saving CF-netCDF with multi-dimensional auxiliary coordinates.
Expand Down Expand Up @@ -864,7 +856,6 @@ def test_uint32_auxiliary_coord_netcdf3(self):
'uint32_auxiliary_coord_netcdf3.cml'),
checksum=False)

@tests.skip_dask_mask
def test_uint32_data_netcdf3(self):
self.cube.data = self.cube.data.astype(np.uint32)
with self.temp_filename(suffix='.nc') as filename:
Expand Down
Loading