Skip to content
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
16 changes: 13 additions & 3 deletions lib/iris/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -1658,9 +1658,14 @@ def fill_value(self):
@fill_value.setter
def fill_value(self, fill_value):
if fill_value is not None:
# Convert the given value to the dtype of the cube.
fill_value = np.asarray([fill_value])[0]
target_dtype = self.dtype
if fill_value.dtype.kind == 'f' and target_dtype.kind == 'i':
# Perform rounding when converting floats to ints.
fill_value = np.rint(fill_value)
try:
fill_value = np.asarray([fill_value],
dtype=self.dtype)[0]
fill_value = np.asarray([fill_value], dtype=target_dtype)[0]
except OverflowError:
emsg = 'Fill value of {!r} invalid for cube {!r}.'
raise ValueError(emsg.format(fill_value, self.dtype))
Expand Down Expand Up @@ -1763,12 +1768,17 @@ def data(self, value):
raise ValueError('Require cube data with shape %r, got '
'%r.' % (self.shape, value.shape))

# Set lazy or real data, and reset the other.
if is_lazy_data(value):
self._dask_array = value
self._numpy_array = None

else:
self._numpy_array = value
self._dask_array = None

# Cancel any 'realisation' datatype conversion, and fill value.
self._dtype = None
self.fill_value = None

def has_lazy_data(self):
return self._numpy_array is None
Expand Down
26 changes: 25 additions & 1 deletion lib/iris/tests/unit/cube/test_Cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,6 @@ def test_lazydata_dtype_change(self):
self.assertEqual(cube.core_data.dtype, np.dtype('f4'))
self.assertIsNone(cube.fill_value)


def test_lazydata_maskedints_dtype_change(self):
# Check that re-assigning dtype resets fill_value.
cube = self._sample_cube(dtype=np.int16, masked=True, lazy=True,
Expand All @@ -1384,6 +1383,31 @@ def test_fill_value__float_dtype_to_int(self):
self.assertEqual(cube.fill_value.dtype, np.dtype('i2'))
self.assertArrayAllClose(cube.fill_value, 1735)

def test_set_fill_value(self):
cube = self._sample_cube()
cube.fill_value = -74.6
self.assertEqual(cube.fill_value.dtype, np.dtype('f4'))
self.assertArrayAllClose(cube.fill_value, -74.6)

def test_set_fill_value__typecast(self):
cube = self._sample_cube(dtype=np.int16)
cube.fill_value = -74.6
self.assertEqual(cube.fill_value.dtype, np.dtype('i2'))
self.assertArrayAllClose(cube.fill_value, -75)

def test_set_fill_value__casterror(self):
cube = self._sample_cube(dtype=np.int16)
msg = "invalid for cube dtype\('int16'\)"
with self.assertRaisesRegexp(ValueError, msg):
# NOTE: this doesn't actually work properly in most cases.
# E.G. it will happily assign 1e12 to an int16 and gets 4096.
cube.fill_value = -1.0e23

def test_clear_fill_value(self):
cube = self._sample_cube(cube_fill_value=123.768)
cube.fill_value = None
self.assertIsNone(cube.fill_value)


class TestSubset(tests.IrisTest):
def test_scalar_coordinate(self):
Expand Down