def get_field(self, ntime=2, variable_name='foo', nrow=2, ncol=2): """Create random field where mean varies with radius and std with the angle around the center of the grid. """ np.random.seed(1) row = Variable(value=np.arange(nrow) - nrow / 2., name='row', dimensions='row') col = Variable(value=np.arange(ncol) - ncol / 2., name='col', dimensions='col') grid = Grid(col, row) x, y = grid.get_value_stacked() start = dt.datetime(2000, 1, 1) delta = dt.timedelta(days=1) value_temporal = [start + i * delta for i in range(ntime)] temporal = TemporalVariable(value=value_temporal, dimensions='time', name='time') nlevel = 1 level = None nrlz = 1 realization = None value = np.random.rand(nrlz, ntime, nlevel, nrow, ncol) * np.arctan2(x, y).clip(.1) + np.hypot(x, y) variable = Variable(name=variable_name, value=value, dimensions=['realization', 'time', 'level', 'row', 'col']) field = Field(grid=grid, time=temporal, is_data=variable, level=level, realization=realization) return field
def test_get_intersects_masking(self): """Test no mask is created if no geometries are masked.""" x = Variable('x', [1, 2], 'x') y = Variable('y', [1, 2], 'y') grid = Grid(x, y) self.assertIsNone(grid.get_mask()) sub = grid.get_intersects(Point(1, 1)) self.assertIsNone(grid.get_mask()) self.assertIsNone(sub.get_mask())
def test_init(self): row = Variable(value=[2, 3], name='row', dimensions='y') col = Variable(value=[4, 5], name='col', dimensions='x') grid = Grid(col, row) self.assertIsNone(grid.archetype.bounds) row = Variable(value=[2, 3], name='row', dimensions='y') row.set_extrapolated_bounds('row_bounds', 'bounds') col = Variable(value=[4, 5], name='col', dimensions='x') col.set_extrapolated_bounds('col_bounds', 'bounds') grid = Grid(y=row, x=col) self.assertEqual(grid.abstraction, 'polygon') poly = get_geometry_variable(grid) self.assertEqual(poly.geom_type, 'Polygon')
def test_get_esmf_grid_with_mask(self): """Test with masked data.""" from ocgis.regrid.base import get_esmf_grid x = Variable(name='x', value=[1, 2, 3], dimensions='x') y = Variable(name='y', value=[4, 5, 6], dimensions='y') grid = Grid(x, y, crs=Spherical()) gmask = grid.get_mask(create=True) gmask[1, 1] = True grid.set_mask(gmask) self.assertEqual(grid.get_mask().sum(), 1) egrid = get_esmf_grid(grid) egrid_mask_inverted = np.invert(np.array(egrid.mask[0], dtype=bool)) self.assertNumpyAll(grid.get_mask(), egrid_mask_inverted) # Test with a value mask. value_mask = np.zeros(grid.shape, dtype=bool) value_mask[-1, -1] = True egrid = get_esmf_grid(grid, value_mask=value_mask) egrid_mask_inverted = np.invert(np.array(egrid.mask[0], dtype=bool)) self.assertNumpyAll(egrid_mask_inverted, np.logical_or(grid.get_mask(), value_mask))
def test_system_dataset_identifiers_on_variables(self): """Test dataset identifiers make it to output variables for iteration.""" paths = [] variables = [] for suffix in [1, 2]: path = self.get_temporary_file_path('foo{}.nc'.format(suffix)) paths.append(path) x = Variable(name='x{}'.format(suffix), value=[2, 3], dimensions='x') y = Variable(name='y{}'.format(suffix), value=[4, 5, 6], dimensions='y') data_variable_name = 'data{}'.format(suffix) variables.append(data_variable_name) data = Variable(name=data_variable_name, value=np.arange(6).reshape(2, 3) + suffix, dimensions=['x', 'y']) grid = Grid(x, y) field = Field(grid=grid, is_data=data) field.write(path) rds = [RequestDataset(uri=p, variable=dv) for p, dv in zip(paths, variables)] ops = OcgOperations(dataset=rds) rds_uids = [ds.uid for ds in ops.dataset] self.assertEqual(rds_uids, [1, 2]) ret = ops.execute() for field in ret.iter_fields(): self.assertFalse(field.grid.has_allocated_abstraction_geometry) for variable in list(field.values()): if isinstance(variable, CoordinateReferenceSystem): continue self.assertIsNotNone(variable._request_dataset.uid) for row in variable.get_iter(): self.assertIsNotNone(row[HeaderName.DATASET_IDENTIFER])
def test_system_process_geometries(self): """Test multiple geometries with coordinate system update.""" a = 'POLYGON((-105.21347987288135073 40.21514830508475313,-104.39928495762711691 40.21514830508475313,-104.3192002118643984 39.5677966101694949,-102.37047139830508513 39.61451271186440692,-102.12354343220337682 37.51896186440677639,-105.16009004237288593 37.51896186440677639,-105.21347987288135073 40.21514830508475313))' b = 'POLYGON((-104.15235699152542281 39.02722457627118757,-103.71189088983049942 39.44099576271186436,-102.71750529661017026 39.28082627118644155,-102.35712394067796538 37.63908898305084705,-104.13900953389830306 37.63241525423728717,-104.15235699152542281 39.02722457627118757))' geom = [{'geom': wkt.loads(xx), 'properties': {'UGID': ugid}} for ugid, xx in enumerate([a, b])] grid_value = [ [[37.0, 37.0, 37.0, 37.0], [38.0, 38.0, 38.0, 38.0], [39.0, 39.0, 39.0, 39.0], [40.0, 40.0, 40.0, 40.0]], [[-105.0, -104.0, -103.0, -102.0], [-105.0, -104.0, -103.0, -102.0], [-105.0, -104.0, -103.0, -102.0], [-105.0, -104.0, -103.0, -102.0]]] output_crs = CoordinateReferenceSystem( value={'a': 6370997, 'lon_0': -100, 'y_0': 0, 'no_defs': True, 'proj': 'laea', 'x_0': 0, 'units': 'm', 'b': 6370997, 'lat_0': 45}) x = Variable('x', grid_value[1], dimensions=['lat', 'lon']) y = Variable('y', grid_value[0], dimensions=['lat', 'lon']) grid = Grid(x, y) field = Field(grid=grid, crs=Spherical()) ops = OcgOperations(dataset=field, geom=geom, output_crs=output_crs) ret = ops.execute() expected = {0: -502052.79407259845, 1: -510391.37909706926} for field, container in ret.iter_fields(yield_container=True): self.assertAlmostEqual(field.grid.get_value_stacked().mean(), expected[container.geom.ugid.get_value()[0]])
def test_system_get_field_dimensioned_variables(self): """Test data is appropriately tagged to identify dimensioned variables.""" path = self.get_temporary_file_path('foo.nc') time = TemporalVariable(value=[1, 2, 3], dimensions='time') x = Variable(name='x', value=[10, 20], dimensions='x') y = Variable(name='y', value=[30, 40, 50, 60], dimensions='y') data1 = Variable(name='data1', value=np.random.rand(3, 4, 2), dimensions=['time', 'y', 'x']) data2 = Variable(name='data2', value=np.random.rand(3, 4, 2), dimensions=['time', 'y', 'x']) data3 = Variable(name='data3', value=[11, 12, 13], dimensions=['time']) field = Field(time=time, grid=Grid(x, y), variables=[data1, data2, data3]) field.write(path) # Test dimensioned variables are read from a file with appropriate metadata. rd = RequestDataset(path) self.assertEqual(rd.variable, ('data1', 'data2')) read_field = rd.get() actual = get_variable_names(read_field.data_variables) self.assertEqual(actual, ('data1', 'data2')) # Test dimensioned variables are overloaded. rd = RequestDataset(path, variable='data2') read_field = rd.get() actual = get_variable_names(read_field.data_variables) self.assertEqual(actual, ('data2', ))
def test_system_spatial_averaging_through_operations(self): data_name = 'data' with vm.scoped('write', [0]): if not vm.is_null: x = Variable('x', range(5), 'x', float) y = Variable('y', range(7), 'y', float) grid = Grid(x, y) data_value = np.arange(x.size * y.size).reshape(grid.shape) data = Variable(data_name, data_value, grid.dimensions, float) data_value = data.get_value() field = Field(grid=grid, is_data=data) path = self.get_temporary_file_path('data.nc') field.write(path) else: data_value, path = None, None data_value = MPI_COMM.bcast(data_value) path = MPI_COMM.bcast(path) rd = RequestDataset(path, variable=data_name) ops = OcgOperations(dataset=rd, aggregate=True) ret = ops.execute() if ret is None: self.assertNotEqual(vm.rank, vm.root) else: out_field = ret.get_element() if MPI_RANK == 0: desired = data_value.mean() actual = out_field.data_variables[0].get_value()[0] self.assertEqual(actual, desired)
def test_get_ocgis_field_from_esmf_spatial_only(self): """Test with spatial information only.""" from ocgis.regrid.base import get_esmf_field_from_ocgis_field from ocgis.regrid.base import get_ocgis_field_from_esmf_field row = Variable(name='row', value=[5, 6], dimensions='row') col = Variable(name='col', value=[7, 8], dimensions='col') grid = Grid(col, row) ofield = Field(grid=grid, crs=Spherical()) efield = get_esmf_field_from_ocgis_field(ofield) ofield_actual = get_ocgis_field_from_esmf_field(efield) self.assertEqual(len(ofield_actual.data_variables), 0) self.assertNumpyAll(grid.get_value_stacked(), ofield_actual.grid.get_value_stacked())
def get_wrap_field(crs=None, unwrapped=True): ompi = OcgDist() ompi.create_dimension('x', 5, dist=False) ompi.create_dimension('y', 7, dist=True) ompi.create_dimension('time', size_current=4, dist=False) ompi.update_dimension_bounds() if MPI_RANK == 0: row = Variable(value=[-60, -40, -20, 0, 20, 40, 60], name='y', dimensions='y') if unwrapped: col_value = [1, 90, 180, 225, 270] else: col_value = [-170, -85, 0, 85, 170] col = Variable(value=col_value, name='x', dimensions='x') grid = Grid(col, row) value = np.zeros((4, 7, 5)) for col_idx in range(value.shape[-1]): value[:, :, col_idx] = col_idx time = TemporalVariable(name='time', value=[1, 2, 3, 4], dimensions='time') var = Variable(name='foo', value=value, dimensions=['time', 'y', 'x']) field = Field(grid=grid, is_data=var, crs=crs, time=time) else: field = None field = variable_collection_scatter(field, ompi) return field
def test_set_extrapolated_bounds(self): value_grid = [[[40.0, 40.0, 40.0, 40.0], [39.0, 39.0, 39.0, 39.0], [38.0, 38.0, 38.0, 38.0]], [[-100.0, -99.0, -98.0, -97.0], [-100.0, -99.0, -98.0, -97.0], [-100.0, -99.0, -98.0, -97.0]]] actual_corners = [[[[40.5, 40.5, 39.5, 39.5], [40.5, 40.5, 39.5, 39.5], [40.5, 40.5, 39.5, 39.5], [40.5, 40.5, 39.5, 39.5]], [[39.5, 39.5, 38.5, 38.5], [39.5, 39.5, 38.5, 38.5], [39.5, 39.5, 38.5, 38.5], [39.5, 39.5, 38.5, 38.5]], [[38.5, 38.5, 37.5, 37.5], [38.5, 38.5, 37.5, 37.5], [38.5, 38.5, 37.5, 37.5], [38.5, 38.5, 37.5, 37.5]]], [[[-100.5, -99.5, -99.5, -100.5], [-99.5, -98.5, -98.5, -99.5], [-98.5, -97.5, -97.5, -98.5], [-97.5, -96.5, -96.5, -97.5]], [[-100.5, -99.5, -99.5, -100.5], [-99.5, -98.5, -98.5, -99.5], [-98.5, -97.5, -97.5, -98.5], [-97.5, -96.5, -96.5, -97.5]], [[-100.5, -99.5, -99.5, -100.5], [-99.5, -98.5, -98.5, -99.5], [-98.5, -97.5, -97.5, -98.5], [-97.5, -96.5, -96.5, -97.5]]]] for should_extrapolate in [False, True]: y = Variable(name='y', value=value_grid[0], dimensions=['ydim', 'xdim']) x = Variable(name='x', value=value_grid[1], dimensions=['ydim', 'xdim']) if should_extrapolate: y.set_extrapolated_bounds('ybounds', 'bounds') x.set_extrapolated_bounds('xbounds', 'bounds') grid = Grid(x, y) try: grid.set_extrapolated_bounds('ybounds', 'xbounds', 'bounds') except BoundsAlreadyAvailableError: self.assertTrue(should_extrapolate) else: np.testing.assert_equal(grid.y.bounds.get_value(), actual_corners[0]) np.testing.assert_equal(grid.x.bounds.get_value(), actual_corners[1]) # Test vectorized. y = Variable(name='y', value=[1., 2., 3.], dimensions='yy') x = Variable(name='x', value=[10., 20., 30.], dimensions='xx') grid = Grid(x, y) grid.set_extrapolated_bounds('ybounds', 'xbounds', 'bounds') self.assertEqual(grid.x.bounds.ndim, 2) self.assertTrue(grid.is_vectorized)
def test_get_mask_from_intersects(self): poly = wkt.loads( 'POLYGON((-98.26574367088608142 40.19952531645570559,-98.71764240506330168 39.54825949367089066,-99.26257911392406186 39.16281645569620906,-99.43536392405064817 38.64446202531645724,-98.78409810126584034 38.33876582278481493,-98.23916139240508016 37.71408227848101546,-97.77397151898735217 37.67420886075949937,-97.62776898734178133 38.15268987341772799,-98.39865506329114453 38.52484177215190186,-98.23916139240508016 39.33560126582278826,-97.73409810126582897 39.58813291139241386,-97.52143987341773368 40.27927215189873777,-97.52143987341773368 40.27927215189873777,-98.26574367088608142 40.19952531645570559))' ) desired_mask = np.array([[True, True, False, True], [True, False, True, True], [True, True, False, True]]) dist = OcgDist() xdim = dist.create_dimension('x', 4, dist=True) ydim = dist.create_dimension('y', 3) dist.create_dimension('bounds', 2) dist.update_dimension_bounds() if MPI_RANK == 0: x = self.get_variable_x() y = self.get_variable_y() grid = Grid(x=x, y=y, abstraction='point', crs=WGS84()) pa = get_geometry_variable(grid) else: pa = None pa = variable_scatter(pa, dist) vm.create_subcomm_by_emptyable('test_get_mask_from_intersects', pa, is_current=True) if vm.is_null: self.assertTrue(pa.is_empty) return usi = [False] if env.USE_SPATIAL_INDEX: usi.append(True) keywords = dict(use_spatial_index=usi) for k in self.iter_product_keywords(keywords): ret = pa.get_mask_from_intersects( poly, use_spatial_index=k.use_spatial_index) desired_mask_local = desired_mask[slice(*ydim.bounds_local), slice(*xdim.bounds_local)] if MPI_RANK > 1: self.assertIsNone(ret) else: self.assertNumpyAll(desired_mask_local, ret) # This does not test a parallel operation. if MPI_RANK == 0: # Test pre-masked values in geometry are okay for intersects operation. value = [Point(1, 1), Point(2, 2), Point(3, 3)] value = np.ma.array(value, mask=[False, True, False], dtype=object) pa2 = GeometryVariable(value=value, dimensions='ngeom') b = box(0, 0, 5, 5) res = pa2.get_mask_from_intersects( b, use_spatial_index=k.use_spatial_index) self.assertNumpyAll(res, value.mask)
def test_set_extrapolated_bounds_empty(self): """Test bounds extrapolation works on empty objects.""" dimx = Dimension('x', 2, is_empty=True, dist=True) x = Variable('x', dimensions=dimx) y = Variable('3', dimensions=Dimension('y', 3)) grid = Grid(x, y) self.assertTrue(dimx.is_empty) self.assertTrue(x.is_empty) self.assertTrue(grid.is_empty) self.assertFalse(grid.has_bounds) self.assertEqual(grid.abstraction, 'point') grid.set_extrapolated_bounds('xbnds', 'ybnds', 'bounds') self.assertEqual(grid.abstraction, 'polygon') self.assertTrue(grid.has_bounds) self.assertTrue(grid.is_empty)
def test_set_mask(self): grid = self.get_gridxy() grid.parent['coordinate_system'] = Variable(name='coordinate_system') self.assertFalse(np.any(grid.get_mask())) mask = np.zeros(grid.shape, dtype=bool) mask[1, 1] = True self.assertTrue(grid.is_vectorized) grid.set_mask(mask) self.assertTrue(np.all(grid.get_mask()[1, 1])) self.assertIn('coordinate_system', grid.parent) self.assertTrue(grid.is_vectorized) for mvar in grid.get_member_variables(): if mvar.name != grid._mask_name: self.assertIsNone(mvar.get_mask()) path = self.get_temporary_file_path('foo.nc') grid.write(path) nvc = RequestDataset(path).get() self.assertIsInstance(nvc, Field) ngrid = Grid(nvc['x'], nvc['y'], parent=nvc) # Mask is not written to coordinate variables. for mvar in ngrid.get_member_variables(): if mvar.name == grid.mask_variable.name: self.assertTrue(mvar.get_mask()[1, 1]) else: self.assertIsNone(mvar.get_mask()) # Test with a parent. grid = self.get_gridxy(with_parent=True) for k in ['tas', 'rhs']: self.assertIsNone(grid.parent[k].get_mask()) new_mask = grid.get_mask(create=True) self.assertFalse(new_mask.any()) new_mask[1:3, 1] = True grid.set_mask(new_mask, cascade=True) for k in ['tas', 'rhs']: backref_var = grid.parent[k] mask = backref_var.get_mask() self.assertTrue(mask.any()) if k == 'tas': self.assertTrue(mask[:, 1, 1:3].all()) if k == 'rhs': self.assertTrue(mask[1:3, 1, :].all()) self.assertEqual(mask.sum(), 20)
def test_get_field_write_target(self): # Test coordinate system names are added to attributes of dimensioned variables. x = Variable('x', dimensions='x', value=[1]) y = Variable('y', dimensions='y', value=[2]) t = Variable('t', dimensions='t', value=[3]) crs = WGS84() d = Variable('data', dimensions=['t', 'y', 'x'], value=[[[1]]]) grid = Grid(x, y) field = Field(grid=grid, time=t, crs=crs) field.add_variable(d, is_data=True) target = DriverNetcdfCF._get_field_write_target_(field) self.assertEqual(target[d.name].attrs['grid_mapping'], crs.name) self.assertEqual(field.x.units, 'degrees_east') # Test bounds units are removed when writing. x = Variable(name='x', value=[1, 2, 3], dtype=float, dimensions='xdim', units='hours') y = Variable(name='y', value=[1, 2, 3], dtype=float, dimensions='ydim', units='hours') grid = Grid(x, y) grid.set_extrapolated_bounds('x_bounds', 'y_bounds', 'bounds') self.assertEqual(x.bounds.units, x.units) self.assertEqual(y.bounds.units, y.units) field = Field(grid=grid) actual = DriverNetcdfCF._get_field_write_target_(field) self.assertEqual(x.bounds.units, x.units) self.assertNumpyMayShareMemory(actual[x.name].get_value(), field[x.name].get_value()) self.assertIsNone(actual[x.name].bounds.units) self.assertIsNone(actual[y.name].bounds.units) self.assertEqual(x.bounds.units, x.units) self.assertEqual(y.bounds.units, y.units) # Test actual coordinate system is triggered. field = Field() src = CFSpherical() dst = WGS84() field.set_crs(src) self.assertEqual(field.crs, src) self.assertIsNone(env.COORDSYS_ACTUAL) env.COORDSYS_ACTUAL = dst actual = DriverNetcdfCF._get_field_write_target_(field) self.assertEqual(actual.crs, dst) self.assertEqual(field.crs, src) self.assertNotIn(src.name, actual) self.assertIn(dst.name, actual)
def test_init(self): # Test a field is always the parent of a grid. grid = self.get_gridxy() self.assertIsInstance(grid.parent, Field) crs = WGS84() grid = self.get_gridxy(crs=crs) self.assertIsInstance(grid, Grid) self.assertIn('x', grid.parent) self.assertIn('y', grid.parent) self.assertEqual(grid.crs, crs) self.assertEqual([dim.name for dim in grid.dimensions], ['ydim', 'xdim']) self.assertEqual(grid.shape, (4, 3)) self.assertTrue(grid.is_vectorized) self.assertEqual(grid.x.ndim, 1) self.assertEqual(grid.y.ndim, 1) # Test with different variable names. x = Variable(name='col', value=[1], dimensions='col') y = Variable(name='row', value=[2], dimensions='row') grid = Grid(x, y) assert_equal(grid.x.get_value(), [1]) assert_equal(grid.y.get_value(), [2]) # Test point and polygon representations. grid = self.get_gridxy(crs=WGS84()) grid.set_extrapolated_bounds('x_bounds', 'y_bounds', 'bounds') targets = ['get_point', 'get_polygon'] targets = [getattr(grid, t)() for t in targets] for t in targets: self.assertIsInstance(t, GeometryVariable) self.assertTrue(grid.is_vectorized) sub = grid[1, 1] targets = ['get_point', 'get_polygon'] targets = [getattr(sub, t)() for t in targets] for t in targets: self.assertEqual(t.shape, (1, 1)) self.assertIsInstance(t, GeometryVariable) self.assertTrue(grid.is_vectorized)
def test_get_wrapped_state(self): if sys.version_info.major == 3 and sys.version_info.minor == 5: raise SkipTest('undefined behavior with Python 3.5') ompi = OcgDist() ompi.create_dimension('x', 5, dist=True) ompi.create_dimension('y', 1) ompi.update_dimension_bounds() values = [{ 'value': [-179, -90, 0, 90, 180], 'desired': WrappedState.WRAPPED }, { 'value': [0, 90, 180, 270, 360], 'desired': WrappedState.UNWRAPPED }, { 'value': [1, 2, 3, 4, 5], 'desired': WrappedState.UNKNOWN }] kwds = {'values': values, 'crs': [Spherical(), None]} for k in self.iter_product_keywords(kwds): ompi = deepcopy(ompi) if MPI_RANK == 0: vx = Variable(name='x', value=k.values['value'], dimensions='x') vy = Variable(name='y', value=[0], dimensions='y') else: vx, vy = [None] * 2 vx = variable_scatter(vx, ompi) vy = variable_scatter(vy, ompi) grid = Grid(vx, vy) field = Field(grid=grid, crs=k.crs) with vm.scoped_by_emptyable('wrap', field): if not vm.is_null: wrapped_state = field.wrapped_state else: wrapped_state = None if not field.is_empty: if k.crs is None: self.assertIsNone(wrapped_state) else: self.assertIsNotNone(wrapped_state) if k.crs is None or field.is_empty: self.assertIsNone(wrapped_state) else: self.assertEqual(wrapped_state, k.values['desired'])
def test_get_intersects_no_slice(self): """Test an intersects operations with no slice.""" x = Variable(name='x', value=[1, 2, 3, 4, 5], dtype=float, dimensions='x') y = Variable(name='y', value=[1, 2, 3, 4, 5, 6, 7], dtype=float, dimensions='y') grid = Grid(x, y) subset_geom = Point(3, 4) sub, the_slice = grid.get_intersects(subset_geom, return_slice=True, apply_slice=False) self.assertEqual(np.sum(np.invert(sub.get_mask())), 1) self.assertEqual(grid.shape, sub.shape) self.assertEqual(the_slice, (slice(3, 4, None), slice(2, 3, None))) sub2 = sub[the_slice] self.assertEqual(subset_geom, sub2.get_point().get_value().flatten()[0])
def test_resolution(self): for grid in self.get_iter_gridxy(): self.assertEqual(grid.resolution, 1.) # Test resolution with a singleton dimension. x = Variable(name='x', value=[[1, 2, 3, 4]], dimensions=['first', 'second']) y = Variable(name='y', value=[[5, 6, 7, 8]], dimensions=['first', 'second']) grid = Grid(x, y) self.assertEqual(grid.resolution, 1)
def test_system(self): from ocgis.regrid.base import create_esmf_grid, iter_esmf_fields, RegridOperation, destroy_esmf_objects import ESMF yc = Variable(name='yc', value=np.arange(-90 + (45 / 2.), 90, 45), dimensions='ydim', dtype=float) xc = Variable(name='xc', value=np.arange(15, 360, 30), dimensions='xdim', dtype=float) ogrid = Grid(y=yc, x=xc, crs=Spherical()) ogrid.set_extrapolated_bounds('xc_bounds', 'yc_bounds', 'bounds') np.random.seed(1) mask = np.random.rand(*ogrid.shape) mask = mask > 0.5 self.assertTrue(mask.sum() > 3) ogrid.set_mask(mask) egrid = create_esmf_grid(ogrid) actual_shape = egrid.size[0].tolist() desired_shape = np.flipud(ogrid.shape).tolist() self.assertEqual(actual_shape, desired_shape) desired = ogrid.get_value_stacked() desired = np.ma.array(desired, mask=False) desired.mask[0, :, :] = ogrid.get_mask() desired.mask[1, :, :] = ogrid.get_mask() desired = desired.sum() actual_col = egrid.get_coords(0) actual_row = egrid.get_coords(1) actual_mask = np.invert(egrid.mask[0].astype(bool)) actual = np.ma.array(actual_row, mask=actual_mask).sum() + np.ma.array(actual_col, mask=actual_mask).sum() self.assertEqual(actual, desired) desired = 9900.0 corners = egrid.coords[ESMF.StaggerLoc.CORNER] actual = corners[0].sum() + corners[1].sum() self.assertEqual(actual, desired) ofield = create_exact_field(ogrid, 'data', ntime=3, crs=Spherical()) variable_name, efield, tidx = list(iter_esmf_fields(ofield, split=False))[0] desired_value = ofield['data'].get_value() self.assertAlmostEqual(efield.data.sum(), desired_value.sum(), places=3) destroy_esmf_objects([egrid, efield]) ofield.grid.set_mask(ofield.grid.get_mask(), cascade=True) desired_value = ofield['data'].get_masked_value() keywords = dict(split=[False, True]) for k in self.iter_product_keywords(keywords): opts = {'split': k.split} dofield = ofield.deepcopy() dofield['data'].get_value().fill(0) ro = RegridOperation(ofield, dofield, regrid_options=opts) actual_field = ro.execute() actual_value = actual_field['data'].get_masked_value() self.assertAlmostEqual(0.0, np.abs(desired_value - actual_value).max())
def test_system_line_subsetting(self): """Test subsetting with a line.""" line = LineString([(-0.4, 0.2), (1.35, 0.3), (1.38, -0.716)]) geom = [{'geom': line, 'crs': None}] x = Variable('x', [-1, -0.5, 0.5, 1.5, 2], 'x') y = Variable('y', [-0.5, 0.5, 1.5], 'y') grid = Grid(x, y) grid.set_extrapolated_bounds('x_bounds', 'y_bounds', 'bounds') field = Field(grid=grid) ops = OcgOperations(dataset=field, geom=geom) ret = ops.execute() field = ret.get_element() desired = [[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]], [[-0.5, 0.5, 1.5], [-0.5, 0.5, 1.5]]] actual = field.grid.get_value_stacked().tolist() self.assertEqual(actual, desired) desired = [[True, True, False], [False, False, False]] actual = field.grid.get_mask().tolist() self.assertEqual(actual, desired)
def test_get_distributed_slice(self): with vm.scoped('grid write', [0]): if MPI_RANK == 0: x = Variable('x', list(range(768)), 'x', float) y = Variable('y', list(range(768)), 'y', float) grid = Grid(x, y) field = Field(grid=grid) path = self.get_temporary_file_path('grid.nc') field.write(path) else: path = None path = vm.bcast(path) rd = RequestDataset(path) grid = rd.get().grid bounds_global = deepcopy([d.bounds_global for d in grid.dimensions]) for _ in range(10): _ = grid.get_distributed_slice([slice(73, 157), slice(305, 386)]) bounds_global_grid_after_slice = [ d.bounds_global for d in grid.dimensions ] self.assertEqual(bounds_global, bounds_global_grid_after_slice)
def test_get_intersects_one_rank_with_mask(self): """Test mask is created if one rank has a spatial mask.""" if MPI_SIZE != 2: raise SkipTest('MPI_SIZE != 2') if MPI_RANK == 0: value = [1, 2] else: value = [3, 4] ompi = OcgDist() xdim = ompi.create_dimension('x', 4, dist=True) ydim = ompi.create_dimension('y', 5, dist=False) ompi.update_dimension_bounds() x = Variable('x', value=value, dimensions=xdim) y = Variable('y', value=[1, 2, 3, 4, 5], dimensions=ydim) grid = Grid(x, y) wkt_geom = 'Polygon ((0.72993630573248502 5.22484076433120936, 0.70318471337579691 0.67707006369426814, 2.70063694267515952 0.69490445859872629, 2.59363057324840796 2.54076433121019107, 4.52866242038216527 2.51401273885350296, 4.40382165605095466 5.34968152866241908, 0.72993630573248502 5.22484076433120936))' subset_geom = wkt.loads(wkt_geom) sub = grid.get_intersects(subset_geom) path = self.get_temporary_file_path('foo.nc') field = Field(grid=sub) field.write(path) with vm.scoped('mask count', [0]): if not vm.is_null: rd = RequestDataset(path) out_field = rd.get() target = out_field[out_field.grid._mask_name].get_value() select = target != 0 self.assertEqual(select.sum(), 4)
def test_expand_grid(self): x = [101, 102, 103] y = [40, 41, 42, 43] vx = Variable('x', value=x, dtype=float, dimensions='xdim') vx.set_extrapolated_bounds('x_bnds', 'bounds') vy = Variable('y', value=y, dtype=float, dimensions='ydim') vy.set_extrapolated_bounds('y_bnds', 'bounds') grid = Grid(vx, vy) for variable in [vx, vy]: self.assertEqual(grid.parent[variable.name].ndim, 1) expand_grid(grid) for variable in [vx, vy]: self.assertEqual(grid.parent[variable.name].ndim, 2)
def test_get_esmf_grid_periodicity(self): """Test periodicity parameters generate reasonable output.""" from ocgis.regrid.base import get_esmf_grid lon_in = np.arange(-180, 180, 10) lat_in = np.arange(-90, 90.1, 4) lon = Variable('lon', lon_in, 'dlon') lat = Variable('lat', lat_in, 'dlat') ogrid = Grid(x=lon, y=lat, crs=Spherical()) egrid = get_esmf_grid(ogrid) self.assertEqual(egrid.periodic_dim, 0) self.assertEqual(egrid.num_peri_dims, 1) self.assertEqual(egrid.pole_dim, 1)
def test_system_spherical_unwrapped_grid_negative_values(self): """Test with negative values in an unwrapped spherical dataset.""" xc = np.arange(-10, 350, step=10, dtype=float) yc = np.arange(-90, 100, step=10, dtype=float) xv = Variable("lon", xc, dimensions=["lon"]) yv = Variable("lat", yc, dimensions=["lat"]) grid = Grid(x=xv, y=yv, crs=Spherical()) self.assertEqual(grid.wrapped_state, WrappedState.UNWRAPPED) self.assertEqual(grid._wrapped_state, "auto") subbox = box(240, -10, 360, 10) subbox = GeometryVariable(name='subset', value=subbox, is_bbox=True, dimensions="ngeom", crs=Spherical()) so = SpatialSubsetOperation(grid.parent) subfield = so.get_spatial_subset("intersects", subbox) self.assertTrue(grid.is_vectorized) self.assertFalse(subfield.grid.get_mask()[0, 0]) self.assertEqual(grid.x.v()[0], -10.0) self.assertIsNone(so._transformed_unwrapped_select)
def test_init_from_file(self): """Test loading from file.""" grid = self.get_gridxy() path = self.get_temporary_file_path('foo.nc') grid.write(path) rd = RequestDataset(uri=path) x = SourcedVariable(name=grid.x.name, request_dataset=rd, protected=True) y = SourcedVariable(name=grid.y.name, request_dataset=rd, protected=True) self.assertIsNone(x._value) self.assertIsNone(y._value) fgrid = Grid(x, y) self.assertEqual(len(fgrid.dimensions), 2) for target in [fgrid._y_name, fgrid._x_name]: fgrid.parent[target].protected = False actual = np.mean( [fgrid.x.get_value().mean(), fgrid.y.get_value().mean()]) self.assertEqual(actual, 71.75)
def test_system_through_operations(self): """Test calculation through operations.""" row = Variable(name='y', value=[1, 2, 3, 4], dimensions='y') col = Variable(name='x', value=[10, 11, 12], dimensions='x') grid = Grid(col, row) time = TemporalVariable(name='time', value=[1, 2], dimensions='time') data = Variable(name='data', dimensions=[time.dimensions[0]] + list(grid.dimensions)) data.get_value()[0, :] = 1 data.get_value()[1, :] = 2 field = Field(grid=grid, time=time, is_data=data) calc = [{'func': 'sum', 'name': 'sum'}] ops = OcgOperations(dataset=field, calc=calc, calc_grouping='day', calc_raw=True, aggregate=True) ret = ops.execute() actual = ret.get_element( variable_name='sum').get_masked_value().flatten() self.assertNumpyAll(actual, np.ma.array([12.0, 24.0]))
def test_reorder(self): x = np.linspace(1, 360, num=7, dtype=float) x = Variable('lon', value=x, dimensions='dimx') y = Variable('lat', value=[-40, -20, 0, 20, 40], dimensions='dimy') t = Variable('time', value=[1, 2, 3], dimensions='dimt') data = Variable(name='data', value=np.zeros((3, 5, 7)), dimensions=['dimt', 'dimy', 'dimx'], dtype=float) data.get_value()[:] = 10. data.get_value()[:, :, 3:x.shape[0]] = 20. data_mask = data.get_mask(create=True) data_mask[:, :, 3:x.shape[0]] = 1 data.set_mask(data_mask) parent = Field(variables=[x, y, t, data]) grid = Grid(parent['lon'], parent['lat'], crs=Spherical(), parent=parent) desired_y = grid.y.get_value().copy() grid.wrap() self.assertFalse(np.any(data[:, :, 0:3].get_value() == 20.)) self.assertFalse(np.any(data.get_mask()[:, :, 0:3])) grid.reorder() actual = grid.x.get_value().tolist() desired = [ -179.5, -119.66666666666666, -59.833333333333314, 0.0, 1.0, 60.833333333333336, 120.66666666666667 ] self.assertEqual(actual, desired) self.assertNumpyAll(desired_y, grid.y.get_value()) rdata = grid.parent['data'] self.assertTrue(np.all(rdata[:, :, 0:3].get_value() == 20.)) self.assertTrue(np.all(rdata.get_mask()[:, :, 0:4])) self.assertFalse(np.any(rdata.get_mask()[:, :, 4:])) self.assertNumpyMayShareMemory(rdata.get_value(), data.get_value())
def test_get_esmf_grid_with_mask(self): """Test with masked data.""" from ocgis.regrid.base import create_esmf_grid x = Variable(name='x', value=[1, 2, 3], dimensions='x') y = Variable(name='y', value=[4, 5, 6], dimensions='y') grid = Grid(x, y, crs=Spherical()) gmask = grid.get_mask(create=True) gmask[1, 1] = True grid.set_mask(gmask) self.assertEqual(grid.get_mask().sum(), 1) egrid = create_esmf_grid(grid) egrid_mask_inverted = np.invert(np.array(egrid.mask[0], dtype=bool)) self.assertNumpyAll(grid.get_mask(), egrid_mask_inverted) # Test with a value mask. value_mask = np.zeros(grid.shape, dtype=bool) value_mask[-1, -1] = True egrid = create_esmf_grid(grid, value_mask=value_mask) egrid_mask_inverted = np.invert(np.array(egrid.mask[0], dtype=bool)) self.assertNumpyAll(egrid_mask_inverted, np.logical_or(grid.get_mask(), value_mask))
def test_get_field_write_target(self): # Test coordinate system names are added to attributes of dimensioned variables. x = Variable('x', dimensions='x', value=[1]) y = Variable('y', dimensions='y', value=[2]) t = Variable('t', dimensions='t', value=[3]) crs = WGS84() d = Variable('data', dimensions=['t', 'y', 'x'], value=[[[1]]]) grid = Grid(x, y) field = Field(grid=grid, time=t, crs=crs) field.add_variable(d, is_data=True) target = DriverNetcdfCF._get_field_write_target_(field) self.assertEqual(target[d.name].attrs['grid_mapping'], crs.name) self.assertEqual(field.x.units, 'degrees_east') # Test bounds units are removed when writing. x = Variable(name='x', value=[1, 2, 3], dtype=float, dimensions='x', units='hours') y = Variable(name='y', value=[1, 2, 3], dtype=float, dimensions='x', units='hours') grid = Grid(x, y) grid.set_extrapolated_bounds('x_bounds', 'y_bounds', 'bounds') self.assertEqual(x.bounds.units, x.units) self.assertEqual(y.bounds.units, y.units) field = Field(grid=grid) actual = DriverNetcdfCF._get_field_write_target_(field) self.assertEqual(x.bounds.units, x.units) self.assertNumpyMayShareMemory(actual[x.name].get_value(), field[x.name].get_value()) self.assertIsNone(actual[x.name].bounds.units) self.assertIsNone(actual[y.name].bounds.units) self.assertEqual(x.bounds.units, x.units) self.assertEqual(y.bounds.units, y.units)
def get_ocgis_grid_from_esmf_grid(egrid, crs=None, dimension_map=None): """ Create an OCGIS :class:`~ocgis.interface.base.dimension.spatial.SpatialDimension` object from an ESMF :class:`~ESMF.driver.grid.Grid`. :type egrid: :class:`ESMF.driver.grid.Grid` :param crs: The coordinate system to attach to the output spatial dimension. :type crs: :class:`ocgis.interface.base.crs.CoordinateReferenceSystem` :param dimension_map: Dimension map for the outgoing OCGIS field/grid. :type dimension_map: :class:`ocgis.DimensionMap` :rtype: :class:`~ocgis.Grid` """ if dimension_map is None: dimension_map = { DimensionMapKey.X: { 'variable': 'x', 'bounds': 'x_bounds', DimensionMapKey.DIMENSION: ['x'] }, DimensionMapKey.Y: { 'variable': 'y', 'bounds': 'y_bounds', DimensionMapKey.DIMENSION: ['y'] } } dimension_map = DimensionMap.from_dict(dimension_map) else: assert isinstance(dimension_map, DimensionMap) # OCGIS grid values are built on centers. coords = egrid.coords[ESMF.StaggerLoc.CENTER] shape_coords_list = list(coords[0].shape) dtype_coords = coords[0].dtype # construct the ocgis grid array and fill grid_value = np.zeros([2] + shape_coords_list, dtype=dtype_coords) grid_value[0, ...] = coords[1] grid_value[1, ...] = coords[0] # Build OCGIS corners array if corners are present on the ESMF grid object. has_corners = get_esmf_grid_has_corners(egrid) if has_corners: corner = egrid.coords[ESMF.StaggerLoc.CORNER] grid_corners = np.zeros([2] + shape_coords_list + [4], dtype=dtype_coords) slices = [(0, 0), (0, 1), (1, 1), (1, 0)] for ii, jj in iter_array(coords[0], use_mask=False): row_slice = slice(ii, ii + 2) col_slice = slice(jj, jj + 2) row_corners = corner[1][row_slice, col_slice] col_corners = corner[0][row_slice, col_slice] for kk, slc in enumerate(slices): grid_corners[:, ii, jj, kk] = row_corners[slc], col_corners[slc] else: grid_corners = None # Does the grid have a mask? has_mask = False if egrid.mask is not None: if egrid.mask[ESMF.StaggerLoc.CENTER] is not None: has_mask = True if has_mask: # if there is a mask, update the grid values egrid_mask = egrid.mask[ESMF.StaggerLoc.CENTER] egrid_mask = np.invert(egrid_mask.astype(bool)) # actually construct the masked arrays grid_value = np.ma.array(grid_value) if grid_corners is not None: grid_corners = np.ma.array(grid_corners) grid_dimensions = [ dimension_map.get_dimension(DimensionMapKey.Y)[0], dimension_map.get_dimension(DimensionMapKey.X)[0] ] if grid_corners is not None: grid_bounds_dimensions = deepcopy(grid_dimensions) grid_bounds_dimensions.append(constants.DEFAULT_NAME_CORNERS_DIMENSION) name = dimension_map.get_bounds(DimensionMapKey.X) x_bounds = Variable( name=name, value=grid_corners[1, ...], dimensions=['y', 'x', constants.DEFAULT_NAME_CORNERS_DIMENSION]) name = dimension_map.get_bounds(DimensionMapKey.Y) y_bounds = Variable( name=name, value=grid_corners[0, ...], dimensions=['y', 'x', constants.DEFAULT_NAME_CORNERS_DIMENSION]) else: x_bounds, y_bounds = [None] * 2 name = dimension_map.get_variable(DimensionMapKey.X) x = Variable(name=name, dimensions=grid_dimensions, value=grid_value[1, ...], bounds=x_bounds) name = dimension_map.get_variable(DimensionMapKey.Y) y = Variable(name=name, dimensions=grid_dimensions, value=grid_value[0, ...], bounds=y_bounds) ogrid = Grid(x, y, crs=crs) if has_mask: ogrid.set_mask(egrid_mask) return ogrid
def iter_src_grid_subsets(self, yield_dst=False): """ Yield source grid subsets using the extent of its associated destination grid subset. :param bool yield_dst: If ``True``, yield the destination subset as well as the source grid subset. :return: The source grid if ``yield_dst`` is ``False``, otherwise a three-element tuple in the form ``(<source grid subset>, <destination grid subset>, <destination grid slice>)``. :rtype: :class:`ocgis.Grid` or (:class:`ocgis.Grid`, :class:`ocgis.Grid`, dict) """ if yield_dst: yield_slice = True else: yield_slice = False dst_grid_resolution = self.dst_grid.resolution src_grid_resolution = self.src_grid.resolution if dst_grid_resolution <= src_grid_resolution: target_resolution = dst_grid_resolution else: target_resolution = src_grid_resolution buffer_value = 2 * target_resolution for yld in self.iter_dst_grid_subsets(yield_slice=yield_slice): if yield_slice: dst_grid_subset, dst_slice = yld else: dst_grid_subset = yld dst_box = None with vm.scoped_by_emptyable('extent_global', dst_grid_subset): if not vm.is_null: if self.check_contains: dst_box = box(*dst_grid_subset.extent_global) # Use the envelope! A buffer returns "fancy" borders. We just want to expand the bounding box. sub_box = box(*dst_grid_subset.extent_global).buffer(buffer_value).envelope ocgis_lh(msg=str(sub_box.bounds), level=logging.DEBUG) else: sub_box, dst_box = [None, None] live_ranks = vm.get_live_ranks_from_object(dst_grid_subset) sub_box = vm.bcast(sub_box, root=live_ranks[0]) if self.check_contains: dst_box = vm.bcast(dst_box, root=live_ranks[0]) src_grid_subset = self.src_grid.get_intersects(sub_box, keep_touches=False, cascade=False, optimized_bbox_subset=True) if not self.allow_masked: gmask = self.src_grid.get_mask() if not self.allow_masked: if gmask is not None and gmask.any(): raise ValueError('Masked values in source grid subset.') with vm.scoped_by_emptyable('src_grid_subset', src_grid_subset): if not vm.is_null: if self.check_contains: src_box = box(*src_grid_subset.extent_global) if not does_contain(src_box, dst_box): raise ValueError('Contains check failed.') else: src_grid_subset = Grid(Variable('x', is_empty=True), Variable('y', is_empty=True)) if yield_dst: yld = (src_grid_subset, dst_grid_subset, dst_slice) else: yld = src_grid_subset yield yld
def get_ugrid_data_structure(): x = Variable(name='node_x', value=[10, 20, 30], dtype=float, dimensions='x') y = Variable(name='node_y', value=[-60, -55, -50, -45, -40], dimensions='y') grid = Grid(x, y) grid.set_extrapolated_bounds('x_bounds', 'y_bounds', 'bounds') grid.expand() cindex = np.zeros((grid.archetype.size, 4), dtype=int) xc = grid.x.bounds.get_value().flatten() yc = grid.y.bounds.get_value().flatten() for eidx, (ridx, cidx) in enumerate(itertools.product(*[range(ii) for ii in grid.shape])): curr_element = grid[ridx, cidx] curr_xc = curr_element.x.bounds.get_value().flatten() curr_yc = curr_element.y.bounds.get_value().flatten() for element_node_idx in range(curr_xc.shape[0]): found_idx = find_index([xc, yc], [curr_xc[element_node_idx], curr_yc[element_node_idx]]) cindex[eidx, element_node_idx] = found_idx new_cindex, uindices = reduce_reindex_coordinate_index(cindex.flatten(), start_index=0) new_cindex = new_cindex.reshape(*cindex.shape) xc = xc[uindices] yc = yc[uindices] centers = grid.get_value_stacked() center_xc = centers[1].flatten() center_yc = centers[0].flatten() longitude_attrs = {'standard_name': 'longitude', 'units': 'degrees_east'} latitude_attrs = {'standard_name': 'latitude', 'units': 'degrees_north'} vc = VariableCollection(attrs={'conventions': 'CF-1.6, UGRID-1.0'}) face_center_x = Variable(name='face_center_x', value=center_xc, dimensions='n_face', parent=vc, attrs=longitude_attrs, dtype=float) face_center_y = Variable(name='face_center_y', value=center_yc, dimensions='n_face', parent=vc, attrs=latitude_attrs, dtype=float) face_node_index = Variable(name='face_node_index', value=new_cindex, dimensions=['n_face', 'max_nodes'], parent=vc, attrs={'standard_name': 'face_node_connectivity', 'order': 'counterclockwise'}) face_node_x = Variable(name='face_node_x', value=xc, dimensions='n_node', parent=vc, attrs=longitude_attrs, dtype=float) face_node_y = Variable(name='face_node_y', value=yc, dimensions='n_node', parent=vc, attrs=latitude_attrs, dtype=float) mesh = Variable(name='mesh', attrs={'standard_name': 'mesh_topology', 'cf_role': 'mesh_topology', 'dimension': 2, 'locations': 'face node', 'node_coordinates': 'face_node_x face_node_y', 'face_coordinates': 'face_center_x face_center_y', 'face_node_connectivity': 'face_node_index'}, parent=vc) # path = self.get_temporary_file_path('foo.nc') # vc.write(path) # self.ncdump(path) # # ============================================================================================================== # import matplotlib.pyplot as plt # from descartes import PolygonPatch # from shapely.geometry import Polygon, MultiPolygon # # BLUE = '#6699cc' # GRAY = '#999999' # # fig = plt.figure(num=1) # ax = fig.add_subplot(111) # # polys = [] # # for face_idx in range(face_node_index.shape[0]): # sub = face_node_index[face_idx, :].parent # curr_cindex = sub[face_node_index.name].get_value().flatten() # fcx = sub[face_node_x.name].get_value()[curr_cindex] # fcy = sub[face_node_y.name].get_value()[curr_cindex] # # coords = np.zeros((4, 2)) # coords[:, 0] = fcx # coords[:, 1] = fcy # # poly = Polygon(coords) # polys.append(poly) # patch = PolygonPatch(poly, fc=BLUE, ec=GRAY, alpha=0.5, zorder=2) # ax.add_patch(patch) # # minx, miny, maxx, maxy = MultiPolygon(polys).bounds # w, h = maxx - minx, maxy - miny # ax.set_xlim(minx - 0.2 * w, maxx + 0.2 * w) # ax.set_ylim(miny - 0.2 * h, maxy + 0.2 * h) # ax.set_aspect(1) # # plt.scatter(center_xc, center_yc, zorder=1) # # plt.show() # =============================================================================================================== return vc
def get_ocgis_grid_from_esmf_grid(egrid): """ Create an OCGIS grid from an ESMF grid. :param egrid: The input ESMF grid to convert to an OCGIS grid. :type egrid: :class:`ESMF.Grid` :return: :class:`~ocgis.Grid` """ dmap = egrid._ocgis['dimension_map'] edims = list(egrid._ocgis['dimnames']) odims = egrid._ocgis['dimnames_backref'] coords = egrid.coords[ESMF.StaggerLoc.CENTER] var_x = Variable(name=dmap.get_variable(DMK.X), value=coords[0], dimensions=edims) var_y = Variable(name=dmap.get_variable(DMK.Y), value=coords[1], dimensions=edims) # Build OCGIS corners array if corners are present on the ESMF grid object. has_corners = esmf_grid_has_corners(egrid) if has_corners: corner = egrid.coords[ESMF.StaggerLoc.CORNER] if egrid.periodic_dim == 0: xcorner = np.zeros([corner[0].shape[0] + 1, corner[0].shape[1]], dtype=corner[0].dtype) xcorner[0:corner[0].shape[0], :] = corner[0] xcorner[-1, :] = corner[0][0, :] ycorner = np.zeros([corner[1].shape[0] + 1, corner[1].shape[1]], dtype=corner[1].dtype) ycorner[0:corner[1].shape[0], :] = corner[1] ycorner[-1, :] = corner[1][0, :] else: xcorner = corner[0] ycorner = corner[1] ocorner_x = create_ocgis_corners_from_esmf_corners(xcorner) ocorner_y = create_ocgis_corners_from_esmf_corners(ycorner) cdims = deepcopy(edims) cdims.append(constants.DEFAULT_NAME_CORNERS_DIMENSION) vocorner_x = Variable(name=dmap.get_bounds(DMK.X), value=ocorner_x, dimensions=cdims) vocorner_y = Variable(name=dmap.get_bounds(DMK.Y), value=ocorner_y, dimensions=cdims) crs = get_crs_from_esmf(egrid) ogrid = Grid(x=var_x, y=var_y, crs=crs) # Does the grid have a mask? has_mask = False if egrid.mask is not None: if egrid.mask[ESMF.StaggerLoc.CENTER] is not None: has_mask = True if has_mask: # if there is a mask, update the grid values egrid_mask = egrid.mask[ESMF.StaggerLoc.CENTER] egrid_mask = np.invert(egrid_mask.astype(bool)) ogrid.set_mask(egrid_mask) ogrid.parent.dimension_map = dmap if tuple(odims) != tuple(edims): broadcast_variable(var_x, odims) broadcast_variable(var_y, odims) if has_corners: broadcast_variable(vocorner_x, list(odims) + [constants.DEFAULT_NAME_CORNERS_DIMENSION]) broadcast_variable(vocorner_y, list(odims) + [constants.DEFAULT_NAME_CORNERS_DIMENSION]) if has_corners: var_x.set_bounds(vocorner_x) var_y.set_bounds(vocorner_y) return ogrid