Skip to content

Commit

Permalink
Latest eccodes + workaround seg fault when saving (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
lbdreyer authored Jun 2, 2020
1 parent 12b989b commit 2193f52
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 12 deletions.
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ channels:
- conda-forge
dependencies:
- iris>=2.4
- python-eccodes>=0.9.1,<2
- python-eccodes
- pep8
7 changes: 7 additions & 0 deletions iris_grib/_save_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,13 @@ def data_section(cube, grib):
# Enable missing values in the grib message.
gribapi.grib_set(grib, "bitmapPresent", 1)
gribapi.grib_set_double(grib, "missingValue", fill_value)

# A segmentation fault is raised by `gribapi.grib_set_double_array` if it
# tries to cast large data to float64. As a temporary fix we cast the data
# upfront
# TODO: remove the `astype` command once eccodes (gribapi) has been fixed.
if data.dtype != np.float64:
data = data.astype(np.float64)
gribapi.grib_set_double_array(grib, "values", data.flatten())

# todo: check packing accuracy?
Expand Down
23 changes: 23 additions & 0 deletions iris_grib/tests/unit/save_rules/test_data_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from unittest import mock

import gribapi
import iris.cube
import numpy as np

Expand Down Expand Up @@ -151,5 +152,27 @@ def test_scaled_with_nan_fill_value(self):
self.assertValues(grib_api, [-1000, 2000, FILL, FILL])


class TestNonDoubleData(tests.IrisGribTest):
# When saving to GRIB, data that is not float64 is cast to float64. This
# test checks that non-float64 data is saved without raising a segmentation
# fault.
def check(self, dtype):
data = np.random.random(1920 * 2560).astype(dtype)
cube = iris.cube.Cube(data,
standard_name='geopotential_height', units='km')
grib_message = gribapi.grib_new_from_samples("GRIB2")
data_section(cube, grib_message)
gribapi.grib_release(grib_message)

def test_float32(self):
self.check(dtype=np.float32)

def test_int32(self):
self.check(dtype=np.int32)

def test_int64(self):
self.check(dtype=np.int64)


if __name__ == "__main__":
tests.main()
14 changes: 3 additions & 11 deletions iris_grib/tests/unit/test_save_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,14 @@ def setUp(self):
def test_save(self):
m = mock.mock_open()
with mock.patch('builtins.open', m, create=True):
# sending a MagicMock object to gribapi raises an AssertionError
# as the gribapi code does a type check
# this is deemed acceptable within the scope of this unit test
with self.assertRaises((AssertionError, TypeError)):
iris_grib.save_messages([self.grib_message], 'foo.grib2')
iris_grib.save_messages([self.grib_message], 'foo.grib2')
self.assertTrue(mock.call('foo.grib2', 'wb') in m.mock_calls)

def test_save_append(self):
m = mock.mock_open()
with mock.patch('builtins.open', m, create=True):
# sending a MagicMock object to gribapi raises an AssertionError
# as the gribapi code does a type check
# this is deemed acceptable within the scope of this unit test
with self.assertRaises((AssertionError, TypeError)):
iris_grib.save_messages([self.grib_message], 'foo.grib2',
append=True)
iris_grib.save_messages([self.grib_message], 'foo.grib2',
append=True)
self.assertTrue(mock.call('foo.grib2', 'ab') in m.mock_calls)


Expand Down

0 comments on commit 2193f52

Please sign in to comment.