def test_with_bounds(self): self.delta.guess_bounds(0) self.sigma.guess_bounds(0.5) # Determine expected coord by manually broadcasting coord points # and bounds based on the dimension mapping. delta_pts = self.delta.points[..., np.newaxis, np.newaxis] sigma_pts = self.sigma.points[..., np.newaxis, np.newaxis] surf_pts = self.surface_air_pressure.points[np.newaxis, ...] expected_points = delta_pts + sigma_pts * surf_pts delta_vals = self.delta.bounds.reshape(3, 1, 1, 2) sigma_vals = self.sigma.bounds.reshape(3, 1, 1, 2) surf_vals = self.surface_air_pressure.points.reshape(1, 2, 2, 1) expected_bounds = delta_vals + sigma_vals * surf_vals expected_coord = iris.coords.AuxCoord( expected_points, standard_name="air_pressure", units="Pa", bounds=expected_bounds, ) factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure, ) derived_coord = factory.make_coord(self.coords_dims_func) self.assertEqual(expected_coord, derived_coord)
def setUp(self): self.delta = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.sigma = mock.Mock(units=cf_units.Unit('1'), nbounds=0) self.surface_air_pressure = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure)
def test_none_surface_air_pressure(self): # Note absence of broadcasting as multidimensional coord # is not present. expected_points = self.delta.points expected_coord = iris.coords.AuxCoord(expected_points, standard_name='air_pressure', units='Pa') factory = HybridPressureFactory(delta=self.delta, sigma=self.sigma) derived_coord = factory.make_coord(self.coords_dims_func) self.assertEqual(expected_coord, derived_coord)
def test_insufficient_coords(self): with self.assertRaises(ValueError): HybridPressureFactory() with self.assertRaises(ValueError): HybridPressureFactory( delta=None, sigma=self.sigma, surface_air_pressure=None) with self.assertRaises(ValueError): HybridPressureFactory( delta=None, sigma=None, surface_air_pressure=self.surface_air_pressure)
def test_none_sigma(self): delta_pts = self.delta.points[..., np.newaxis, np.newaxis] sigma_pts = 0 surf_pts = self.surface_air_pressure.points[np.newaxis, ...] expected_points = delta_pts + sigma_pts * surf_pts expected_coord = iris.coords.AuxCoord(expected_points, standard_name='air_pressure', units='Pa') factory = HybridPressureFactory( delta=self.delta, surface_air_pressure=self.surface_air_pressure) derived_coord = factory.make_coord(self.coords_dims_func) self.assertEqual(expected_coord, derived_coord)
def test_invalid_dependencies(self): # Must have either delta or surface_air_pressure with self.assertRaises(ValueError): factory = HybridPressureFactory() sigma = self.cube.coord('sigma') with self.assertRaises(ValueError): factory = HybridPressureFactory(sigma=sigma) # Surface pressure must not have bounds with warnings.catch_warnings(): # Cause all warnings to raise Exceptions warnings.simplefilter("error") with self.assertRaises(UserWarning): factory = HybridPressureFactory(surface_pressure=sigma)
def test_points_only(self): # Determine expected coord by manually broadcasting coord points # knowing the dimension mapping. delta_pts = self.delta.points[..., np.newaxis, np.newaxis] sigma_pts = self.sigma.points[..., np.newaxis, np.newaxis] surf_pts = self.surface_air_pressure.points[np.newaxis, ...] expected_points = delta_pts + sigma_pts * surf_pts expected_coord = iris.coords.AuxCoord(expected_points, standard_name='air_pressure', units='Pa') factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure) derived_coord = factory.make_coord(self.coords_dims_func) self.assertEqual(expected_coord, derived_coord)
def mock_hybrid_pressure_cube(): """Return mocked cube with hybrid pressure coordinate.""" cube = unittest.mock.create_autospec(Cube, spec_set=True, instance=True) ap_coord = AuxCoord([1.0], bounds=[[0.0, 2.0]], var_name='ap', units='Pa') b_coord = AuxCoord([0.0], bounds=[[-0.5, 1.5]], var_name='b', units=Unit('1')) ps_coord = AuxCoord([[[100000]]], var_name='ps', units='Pa') cube.coord.side_effect = [ ap_coord, b_coord, ps_coord, ap_coord, b_coord, ps_coord ] cube.coords.return_value = [ ap_coord, b_coord, ps_coord, AuxCoord(0.0, standard_name='atmosphere_hybrid_sigma_pressure_coordinate'), ] aux_factory = HybridPressureFactory( delta=ap_coord, sigma=b_coord, surface_air_pressure=ps_coord, ) cube.aux_factories = ['dummy', aux_factory] return cube
def test_incompatible_sigma_units(self): self.sigma.units = iris.unit.Unit('Pa') with self.assertRaises(ValueError): HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure)
def test_hybrid_pressure_with_non_standard_coords(self): # Check the save rules are using the AuxFactory to find the # hybrid pressure coordinates and not relying on their names. ny, nx = 30, 40 sigma_lower, sigma, sigma_upper = 0.75, 0.8, 0.75 delta_lower, delta, delta_upper = 0.15, 0.2, 0.25 cube = Cube(np.zeros((ny, nx)), 'air_temperature') level_coord = AuxCoord(0, 'model_level_number') cube.add_aux_coord(level_coord) delta_coord = AuxCoord(delta, bounds=[[delta_lower, delta_upper]], long_name='moog', units='Pa') sigma_coord = AuxCoord(sigma, bounds=[[sigma_lower, sigma_upper]], long_name='mavis') surface_air_pressure_coord = AuxCoord(np.zeros((ny, nx)), 'surface_air_pressure', units='Pa') cube.add_aux_coord(delta_coord) cube.add_aux_coord(sigma_coord) cube.add_aux_coord(surface_air_pressure_coord, (0, 1)) cube.add_aux_factory(HybridPressureFactory( delta_coord, sigma_coord, surface_air_pressure_coord)) field = iris.fileformats.pp.PPField3() field.lbfc = 0 field.lbvc = 0 field.brsvd = [None, None] field.lbuser = [None] * 7 field = verify(cube, field) self.assertEqual(field.bhlev, delta) self.assertEqual(field.bhrlev, delta_lower) self.assertEqual(field.blev, sigma) self.assertEqual(field.brlev, sigma_lower) self.assertEqual(field.brsvd, [sigma_upper, delta_upper])
def test_too_many_sigma_bounds(self): self.sigma.nbounds = 4 with self.assertRaises(ValueError): HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure)
def test_different_pressure_units(self): self.delta.units = cf_units.Unit('hPa') self.surface_air_pressure.units = cf_units.Unit('Pa') with self.assertRaises(ValueError): HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure)
def test_incompatible_surface_air_pressure_units(self): self.surface_air_pressure.units = cf_units.Unit('unknown') with self.assertRaises(ValueError): HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure)
def test_promote_sigma_units_unknown_to_dimensionless(self): sigma = mock.Mock(units=cf_units.Unit("unknown"), nbounds=0) factory = HybridPressureFactory( delta=self.delta, sigma=sigma, surface_air_pressure=self.surface_air_pressure, ) self.assertEqual("1", factory.dependencies["sigma"].units)
def test_value(self): kwargs = dict( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure, ) factory = HybridPressureFactory(**kwargs) self.assertEqual(factory.dependencies, kwargs)
def test_factory_metadata(self): factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure) self.assertEqual(factory.standard_name, 'air_pressure') self.assertIsNone(factory.long_name) self.assertIsNone(factory.var_name) self.assertEqual(factory.units, self.delta.units) self.assertEqual(factory.units, self.surface_air_pressure.units) self.assertIsNone(factory.coord_system) self.assertEqual(factory.attributes, {})
def test_with_bounds(self): self.delta.guess_bounds(0) self.sigma.guess_bounds(0.5) # Determine expected coord by manually broadcasting coord points # and bounds based on the dimension mapping. delta_pts = self.delta.points[..., np.newaxis, np.newaxis] sigma_pts = self.sigma.points[..., np.newaxis, np.newaxis] surf_pts = self.surface_air_pressure.points[np.newaxis, ...] expected_points = delta_pts + sigma_pts * surf_pts delta_vals = self.delta.bounds.reshape(3, 1, 1, 2) sigma_vals = self.sigma.bounds.reshape(3, 1, 1, 2) surf_vals = self.surface_air_pressure.points.reshape(1, 2, 2, 1) expected_bounds = delta_vals + sigma_vals * surf_vals expected_coord = iris.coords.AuxCoord(expected_points, standard_name='air_pressure', units='Pa', bounds=expected_bounds) factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure) derived_coord = factory.make_coord(self.coords_dims_func) self.assertEqual(expected_coord, derived_coord)
def get_hybrid_pressure_cube_list(): """Return list of cubes including hybrid pressure coordinate.""" cube_0 = get_hybrid_pressure_cube() cube_1 = get_hybrid_pressure_cube() cube_0.add_dim_coord(get_time_coord(0), 0) cube_1.add_dim_coord(get_time_coord(1), 0) cubes = CubeList([cube_0, cube_1]) for cube in cubes: aux_factory = HybridPressureFactory( delta=cube.coord(var_name='ap'), sigma=cube.coord(var_name='b'), surface_air_pressure=cube.coord(var_name='ps'), ) cube.add_aux_factory(aux_factory) return cubes
def create_data_object(self, filenames, variable): """Reads the data for a variable. :param filenames: list of names of files from which to read data :param variable: (optional) name of variable; if None, the file(s) must contain data for only one cube :return: iris.cube.Cube """ from iris.aux_factory import HybridPressureFactory # Add the derived coordinates back (https://github.com/SciTools/iris/issues/2478)... cube = super().create_data_object(filenames, variable) if cube.coords('atmosphere_hybrid_sigma_pressure_coordinate' ) and not cube.coords('air_pressure'): cube.add_aux_factory( HybridPressureFactory( cube.coord('atmosphere_hybrid_sigma_pressure_coordinate'), cube.coord('hybrid B coefficient at layer midpoints'), cube.coord('surface pressure'))) return cube
def _add_available_aux_coords(self, cube, filenames): from iris.aux_factory import HybridPressureFactory from iris.coords import AuxCoord from cis.data_io.netcdf import read ps_filenames = [ f.replace('concbc', 'ps_TL95L80_192x48NH_3hr') for f in filenames ] # These will be the same for all files hybrid_a = read(ps_filenames[0], 'a')['a'] hybrid_b = read(ps_filenames[0], 'b')['b'] hybrid_a_coord = AuxCoord( points=hybrid_a[:], long_name='vertical coordinate formula term: a(k)', units='Pa') hybrid_b_coord = AuxCoord( points=hybrid_b[:], long_name='vertical coordinate formula term: b(k)', units='1') # This needs to be from each file and then merged surface_pressure_cube = _get_cubes( ps_filenames, 'ps', callback=self.load_multiple_files_callback).concatenate_cube() surface_pressure = AuxCoord(points=surface_pressure_cube.data, standard_name='surface_air_pressure', long_name='surface pressure', units='Pa') # First convert the hybrid coefficients to hPa, so that air pressure will be in hPa hybrid_a_coord.convert_units('hPa') surface_pressure.convert_units('hPa') cube.add_aux_coord(surface_pressure, (0, 2, 3)) cube.add_aux_coord(hybrid_a_coord, (1, )) cube.add_aux_coord(hybrid_b_coord, (1, )) cube.add_aux_factory( HybridPressureFactory(delta=hybrid_a_coord, sigma=hybrid_b_coord, surface_air_pressure=surface_pressure))
def _add_available_aux_coords(self, cube, filenames): from iris.aux_factory import HybridPressureFactory from iris.coords import AuxCoord from iris.exceptions import CoordinateNotFoundError import iris try: surface_pressure = cube.coord('surface pressure') except iris.exceptions.CoordinateNotFoundError as e: # If there isn't a surface pressure coordinate we can try and pull out the lowest pressure level with demote_warnings(): surface_pressure_cubes = iris.load(filenames, 'atmospheric pressure at interfaces', callback=self.load_multiple_files_callback) surface_pressure_cube = surface_pressure_cubes.concatenate_cube()[:, -1, :, :] surface_pressure = AuxCoord(points=surface_pressure_cube.data, long_name='surface pressure', units='Pa') cube.add_aux_coord(surface_pressure, (0, 2, 3)) if len(cube.coords(long_name='hybrid level at layer midpoints')) > 0: cube.add_aux_factory(HybridPressureFactory(delta=cube.coord('hybrid A coefficient at layer midpoints'), sigma=cube.coord('hybrid B coefficient at layer midpoints'), surface_air_pressure=surface_pressure))
def setUp(self): # Convert the hybrid-height into hybrid-pressure... cube = iris.tests.stock.realistic_4d() # Get rid of the normal hybrid-height factory. factory = cube.aux_factory(name='altitude') cube.remove_aux_factory(factory) # Mangle the height coords into pressure coords. delta = cube.coord('level_height') delta.rename('level_pressure') delta.units = 'Pa' sigma = cube.coord('sigma') ref = cube.coord('surface_altitude') ref.rename('surface_air_pressure') ref.units = 'Pa' factory = HybridPressureFactory(delta, sigma, ref) cube.add_aux_factory(factory) self.cube = cube self.air_pressure = self.cube.coord('air_pressure')
class Test_update(tests.IrisTest): def setUp(self): self.delta = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.sigma = mock.Mock(units=cf_units.Unit('1'), nbounds=0) self.surface_air_pressure = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.factory = HybridPressureFactory( delta=self.delta, sigma=self.sigma, surface_air_pressure=self.surface_air_pressure) def test_good_delta(self): new_delta_coord = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.factory.update(self.delta, new_delta_coord) self.assertIs(self.factory.delta, new_delta_coord) def test_bad_delta(self): new_delta_coord = mock.Mock(units=cf_units.Unit('1'), nbounds=0) with self.assertRaises(ValueError): self.factory.update(self.delta, new_delta_coord) def test_alternative_bad_delta(self): new_delta_coord = mock.Mock(units=cf_units.Unit('Pa'), nbounds=4) with self.assertRaises(ValueError): self.factory.update(self.delta, new_delta_coord) def test_good_surface_air_pressure(self): new_surface_p_coord = mock.Mock(units=cf_units.Unit('Pa'), nbounds=0) self.factory.update(self.surface_air_pressure, new_surface_p_coord) self.assertIs(self.factory.surface_air_pressure, new_surface_p_coord) def test_bad_surface_air_pressure(self): new_surface_p_coord = mock.Mock(units=cf_units.Unit('km'), nbounds=0) with self.assertRaises(ValueError): self.factory.update(self.surface_air_pressure, new_surface_p_coord) def test_non_dependency(self): old_coord = mock.Mock() new_coord = mock.Mock() orig_dependencies = self.factory.dependencies self.factory.update(old_coord, new_coord) self.assertEqual(orig_dependencies, self.factory.dependencies) def test_none_delta(self): self.factory.update(self.delta, None) self.assertIsNone(self.factory.delta) def test_none_sigma(self): self.factory.update(self.sigma, None) self.assertIsNone(self.factory.sigma) def test_insufficient_coords(self): self.factory.update(self.delta, None) with self.assertRaises(ValueError): self.factory.update(self.surface_air_pressure, None)
def _add_available_aux_coords(self, cube, filenames): import iris from iris.aux_factory import HybridPressureFactory from iris.coords import AuxCoord from iris.exceptions import CoordinateNotFoundError # Only do this for fields with a vertical component - this check is a bit hacky though (doesn't consider 3D with no time...) if cube.ndim == 4: # Only read the first file for these coefficients as they are time-independant and iris won't merge them hybrid_a = _get_cubes(filenames, 'hybrid A coefficient at layer midpoints') hybrid_b = _get_cubes(filenames, 'hybrid B coefficient at layer midpoints') hybrid_a_coord = AuxCoord( points=hybrid_a[0].data, long_name='hybrid A coefficient at layer midpoints', units='Pa', var_name='hyam') hybrid_b_coord = AuxCoord( points=hybrid_b[0].data, long_name='hybrid B coefficient at layer midpoints', units='1', var_name='hybm') if cube.coords('surface pressure'): surface_pressure = cube.coord('surface pressure') elif cube.coords('surface_air_pressure'): surface_pressure = cube.coord('surface_air_pressure') else: try: # If there isn't a surface pressure coordinate we can try loading it manually surface_pressure_cube = _get_cubes( filenames, 'surface_air_pressure', callback=self.load_multiple_files_callback ).concatenate_cube() surface_pressure = AuxCoord( points=surface_pressure_cube.data, standard_name='surface_air_pressure', long_name='surface pressure', units='Pa') cube.add_aux_coord(surface_pressure, (0, 2, 3)) except ValueError: try: # If there isn't a surface pressure coordinate we can try and pull out the lowest pressure level surface_pressure_cubes = _get_cubes( filenames, 'atmospheric pressure at interfaces', callback=self.load_multiple_files_callback) surface_pressure_cube = surface_pressure_cubes.concatenate_cube( )[:, -1, :, :] surface_pressure = AuxCoord( points=surface_pressure_cube.data, long_name='surface pressure', units='Pa') cube.add_aux_coord(surface_pressure, (0, 2, 3)) except ValueError: # Try and get it from the vphysc stream v_files = [ '_'.join(f.split('_')[:-1]) + '_vphyscm.nc' for f in filenames ] try: surface_pressure_cubes = _get_cubes( v_files, 'atmospheric pressure at interfaces', callback=self.load_multiple_files_callback) except: # If we can't do that then just exit - there must be a cleaner way to do this... return surface_pressure_cube = surface_pressure_cubes.concatenate_cube( )[:, -1, :, :] surface_pressure = AuxCoord( points=surface_pressure_cube.data, long_name='surface pressure', units='Pa') cube.add_aux_coord(surface_pressure, (0, 2, 3)) # First convert the hybrid coefficients to hPa, so that air pressure will be in hPa hybrid_a_coord.convert_units('hPa') surface_pressure.convert_units('hPa') cube.add_aux_coord(hybrid_a_coord, (1, )) cube.add_aux_coord(hybrid_b_coord, (1, )) if len(cube.coords( long_name='hybrid level at layer midpoints')) > 0: cube.add_aux_factory( HybridPressureFactory( delta=hybrid_a_coord, sigma=hybrid_b_coord, surface_air_pressure=surface_pressure))