Source code for ocgis.driver.nc_scrip

import numpy as np

from ocgis import DimensionMap, env, constants, vm
from ocgis.base import raise_if_empty
from ocgis.constants import DriverKey, DMK, Topology, DecompositionType
from ocgis.driver.base import AbstractUnstructuredDriver
from ocgis.driver.nc import DriverNetcdf
from ocgis.util.helpers import create_unique_global_array
from ocgis.vmachine.mpi import hgather


[docs]class DriverNetcdfSCRIP(AbstractUnstructuredDriver, DriverNetcdf): """ Driver for the SCRIP NetCDF structured and unstructured grid format. SCRIP is a legacy format that is the primary precursor to NetCDF-CF convention. By default, SCRIP grids are treated as unstructured data creating an unstructured grid. """ _esmf_fileformat = 'SCRIP' key = DriverKey.NETCDF_SCRIP _default_crs = env.DEFAULT_COORDSYS @staticmethod def array_resolution(value, axis): """See :meth:`ocgis.driver.base.AbstractDriver.array_resolution`""" if value.size == 1: return 0.0 else: resolution_limit = constants.RESOLUTION_LIMIT value = np.sort(np.unique(np.abs(value))) value = value[0:resolution_limit] value = np.diff(value) ret = np.mean(value) return ret def create_dimension_map(self, group_metadata, **kwargs): ret = DimensionMap() ret.set_driver(self) topo = ret.get_topology(Topology.POINT, create=True) topo.set_variable(DMK.X, 'grid_center_lon', dimension='grid_size') topo.set_variable(DMK.Y, 'grid_center_lat', dimension='grid_size') if 'grid_corner_lon' in group_metadata['variables']: topo = ret.get_topology(Topology.POLYGON, create=True) topo.set_variable(DMK.X, 'grid_corner_lon', dimension='grid_size') topo.set_variable(DMK.Y, 'grid_corner_lat', dimension='grid_size') if 'grid_imask' in group_metadata['variables']: # Use the intrinsic SCRIP default attributes associated with the variable. ret.set_spatial_mask('grid_imask', default_attrs={}) # The isomorphic property covers all possible mesh topologies. ret.set_property(DMK.IS_ISOMORPHIC, True) return ret def get_distributed_dimension_name(self, dimension_map, dimensions_metadata, decomp_type=DecompositionType.OCGIS): return 'grid_size' @staticmethod def get_or_create_spatial_mask(*args, **kwargs): """ Get or create the SCRIP spatial mask. Arguments and keyword arguments in signature are for driver compatibility only. """ sobj = args[0] raise_if_empty(sobj) ret = None if sobj.has_mask: maskvar = sobj.parent['grid_imask'] if not maskvar.has_allocated_value: v = maskvar.v() ret = v == 0 maskvar.set_mask(ret) else: ret = maskvar.m() return ret @staticmethod def validate_spatial_mask(mask_variable): if mask_variable.name != 'grid_imask': msg = 'For SCRIP data, the mask variable must be named "grid_imask".' raise ValueError(msg) @classmethod def _get_field_write_target_(cls, field): # Unstructured SCRIP has a value of 1 for the grid dimensions by default. Just leave it alone. if field.dimensions['grid_rank'].size > 1: # Update the grid size based on unique x/y values. In SCRIP, the coordinate values are duplicated in the # coordinate vector. ux = field.grid.x.shape[0] uy = field.grid.y.shape[0] field['grid_dims'].get_value()[:] = ux, uy return field @staticmethod def _gc_iter_dst_grid_slices_(grid_chunker): # TODO: This method uses some global gathers which is not ideal. # Destination splitting works off center coordinates only. pgc = grid_chunker.dst_grid.abstractions_available['point'] # Use the unique center values to break the grid into pieces. This ensures that nearby grid cell are close # spatially. If we just break the grid into pieces w/out using unique values, the points may be scattered which # does not optimize the spatial coverage of the source grid. center_lat = pgc.y.get_value() # ucenter_lat = np.unique(center_lat) ucenter_lat = create_unique_global_array(center_lat) ucenter_lat = vm.gather(ucenter_lat) if vm.rank == 0: ucenter_lat = hgather(ucenter_lat) ucenter_lat.sort() ucenter_splits = np.array_split(ucenter_lat, grid_chunker.nchunks_dst[0]) else: ucenter_splits = [None] * grid_chunker.nchunks_dst[0] for ucenter_split in ucenter_splits: ucenter_split = vm.bcast(ucenter_split) select = np.zeros_like(center_lat, dtype=bool) for v in ucenter_split.flat: select = np.logical_or(select, center_lat == v) yield select @staticmethod def _gc_nchunks_dst_(grid_chunker): g = grid_chunker.dst_grid size = g.y.size if size < 100: ret = int(size / 2) else: raise NotImplementedError return ret