Source code for cdms2.hgrid

# Automatically adapted for numpy.oldnumeric Aug 01, 2007 by
# Further modified to be pure new numpy June 24th 2008

"""CDMS HorizontalGrid objects"""

from __future__ import print_function
import numpy
import cdms2
import os
import os.path
# import PropertiedClasses
from .error import CDMSError
from .grid import AbstractGrid, LongitudeType, LatitudeType, CoordTypeToLoc
from .axis import TransientVirtualAxis
from .axis import getAutoBounds, allclose
from cdms2 import bindex
from cdms2 import _bindex
from functools import reduce
import copy

MethodNotImplemented = "Method not yet implemented"


[docs]def _flatten(boundsar): boundsshape = boundsar.shape if len(boundsshape) > 2: newshape = (reduce((lambda x, y: x * y), boundsshape[:-1], 1), boundsshape[-1]) boundsar.shape = newshape return boundsar
class AbstractHorizontalGrid(AbstractGrid): """ Create an horizontal grid Parameters ---------- latAxis lonAxis id - Default None maskvar - Default None tempmask - Default None node - Default None """
[docs] def __init__(self, latAxis, lonAxis, id=None, maskvar=None, tempmask=None, node=None): """Create a horizontal grid. """ AbstractGrid.__init__(self, node) if id is None: self.id = "<None>" else: self.id = id self._lataxis_ = latAxis self._lonaxis_ = lonAxis self._maskVar_ = maskvar self._tempMask_ = tempmask
# Generate default bounds
[docs] def genBounds(self): """ Not documented """ raise CDMSError(MethodNotImplemented)
# Get the n-th axis. naxis is 0 or 1.
[docs] def getAxis(self, naxis): """ Not documented """ raise CDMSError(MethodNotImplemented)
[docs] def getBounds(self): """Get the grid cell boundaries, as a tuple (latitudeBounds, longitudeBounds) """ latbnds, lonbnds = (self._lataxis_.getExplicitBounds(), self._lonaxis_.getExplicitBounds()) if (latbnds is None or lonbnds is None) and getAutoBounds() in [1, 2]: nlatbnds, nlonbnds = self.genBounds() if latbnds is None: latbnds = nlatbnds if lonbnds is None: lonbnds = nlonbnds return (latbnds, lonbnds)
[docs] def getLatitude(self): """Get the latitude coordinates.""" return self._lataxis_
[docs] def getLongitude(self): """Get the longitude coordinates.""" return self._lonaxis_
[docs] def getMask(self): """Get the mask array, if any, otherwise None is returned.""" if self._maskVar_ is not None: return self._maskVar_ else: return self._tempMask_
[docs] def getMesh(self): """Get the mesh array used by the meshfill plot.""" raise CDMSError(MethodNotImplemented)
[docs] def getWeightsArray(self): """ Get Weights Array Returns ------- normalized area weights, as an array of the same shape as the grid. """ raise CDMSError(MethodNotImplemented)
[docs] def listall(self, all=None): """ Not documented """ result = [] result.append('Grid has Python id %s.' % hex(id(self))) return result
[docs] def setMask(self, mask, permanent=0): """ Not documented """ self._maskVar_ = mask
[docs] def subGridRegion(self, latRegion, lonRegion): """ Not documented """ raise CDMSError(MethodNotImplemented)
[docs] def hasCoordType(self, coordType): """ Not documented """ return ((coordType == LatitudeType) or (coordType == LongitudeType))
[docs] def checkConvex(self): """Check that each cell of the grid is convex in lon-lat space, with nodes defined counter-clockwise. Returns ------- a 1D numpy array of cells that fail the cross-product test. """ from numpy import zeros, where, less, logical_or, compress latb, lonb = self.getBounds() saveshape = lonb.shape lonb = _flatten(lonb) latb = _flatten(latb) ncell, nnode = lonb.shape badmask = zeros((ncell,)) for n0 in range(nnode): n1 = (n0 + 1) % nnode n2 = (n1 + 1) % nnode vec0lon = lonb[:, n1] - lonb[:, n0] vec0lat = latb[:, n1] - latb[:, n0] vec1lon = lonb[:, n2] - lonb[:, n1] vec1lat = latb[:, n2] - latb[:, n1] cross = vec0lon * vec1lat - vec0lat * vec1lon mask = where(less(cross, 0.0), 1, 0) badmask = logical_or(mask, badmask) badcells = compress(badmask, list(range(len(badmask)))) lonb.shape = saveshape latb.shape = saveshape return badcells
[docs] def fixCutCells(self, nonConvexCells, threshold=270.0): """ For any mapping from a spherical to a planar surface, there is a linear cut. Grid cells that span the cut may appear to be nonconvex, which causes problems with meshfill graphics. This routine attempts to 'repair' the cut cell boundaries so that meshfill recognizes they are convex. Parameters ---------- nonConvexCells : 1D numpy array of indices of nonconvex cells, as returned from checkConvex. threshold : positive floating-point value in degrees. If the difference in longitude values of consecutive boundaries nodes exceeds the threshold, the cell is considered a cut cell. On return, the grid boundaries are modified. Returns ------- value is a 1D array of indices of cells that cannot be repaired. """ from numpy import take, array latb, lonb = self.getBounds() saveshape = lonb.shape lonb = _flatten(lonb) latb = _flatten(latb) ncell, nnode = lonb.shape lonb2 = take(lonb, nonConvexCells, axis=0) latb2 = take(latb, nonConvexCells, axis=0) newbadcells = [] for k in range(len(nonConvexCells)): savelons = lonb2[k] # Loop twice for node in range(2 * nnode): n0 = node % nnode n1 = (n0 + 1) % nnode vec0lon = lonb2[k, n1] - lonb2[k, n0] if vec0lon > threshold: lonb2[k, n1] -= 360.0 elif vec0lon < -threshold: lonb2[k, n1] += 360.0 # If the cross-product test still fails, restore # the original values and add to the nonConvexCells list for n0 in range(nnode): n1 = (n0 + 1) % nnode n2 = (n1 + 1) % nnode vec0lon = lonb2[k, n1] - lonb2[k, n0] vec0lat = latb2[k, n1] - latb2[k, n0] vec1lon = lonb2[k, n2] - lonb2[k, n1] vec1lat = latb2[k, n2] - latb2[k, n1] cross = vec0lon * vec1lat - vec0lat * vec1lon if cross < 0: lonb2[k] = savelons newbadcells.append(nonConvexCells[k]) break # Scatter the repaired cell bounds back to the original bounds # and reset the grid bounds. for k in range(len(nonConvexCells)): lonb[nonConvexCells[k]] = lonb2[k] lonb.shape = saveshape self.getLongitude().setBounds(lonb) return array(newbadcells)
class AbstractCurveGrid(AbstractHorizontalGrid): """Create a curvilinear grid. """
[docs] def __init__(self, latAxis, lonAxis, id=None, maskvar=None, tempmask=None, node=None): """Create a curvilinear grid. """ if latAxis.shape != lonAxis.shape: raise CDMSError( 'Latitude and longitude axes must have the same shape.') AbstractHorizontalGrid.__init__( self, latAxis, lonAxis, id, maskvar, tempmask, node) self._index_ = None
[docs] def clone(self, copyData=1): """ Not documented """ newlat = self._lataxis_.clone(copyData) newlon = self._lonaxis_.clone(copyData) return TransientCurveGrid(newlat, newlon, id=self.id)
def __repr__(self): return "<CurveGrid, id: %s, shape: %s>" % (self.id, repr(self.shape)) __str__ = __repr__
[docs] def getMesh(self, transpose=None): """Generate a mesh array for the meshfill graphics method. If transpose is defined to a tuple, say (1,0), first transpose latbounds and lonbounds according to the tuple, (1,0,2) in this case. """ if self._mesh_ is None: LAT = 0 LON = 1 latbounds, lonbounds = self.getBounds() # following work aronud a numpy.ma bug # latbounds=latbounds.filled() # lonbounds=lonbounds.filled() if latbounds is None or lonbounds is None: raise CDMSError( 'No boundary data is available for grid %s' % self.id) if (transpose is not None) and (transpose[1] == 0): latbounds = numpy.transpose(latbounds, (1, 0, 2)) lonbounds = numpy.transpose(lonbounds, (1, 0, 2)) mesh = numpy.zeros( (self.size(), 2, latbounds.shape[-1]), latbounds.dtype.char) mesh[:, LAT, :] = numpy.reshape( latbounds, (self.size(), latbounds.shape[-1])) mesh[:, LON, :] = numpy.reshape( lonbounds, (self.size(), latbounds.shape[-1])) self._mesh_ = mesh return self._mesh_
[docs] def _getShape(self): """ Not documented """ return self._lataxis_.shape
# Don't try to generate bounds for curvilinear grids
[docs] def genBounds(self): """ Not documented """ return (None, None)
# Get the n-th index axis. naxis is 0 or 1.
[docs] def getAxis(self, naxis): """ Not documented """ return self._lataxis_.getAxis(naxis)
[docs] def getMask(self): """Get the mask array, if any, otherwise None is returned.""" if self._maskVar_ is None: return self._tempMask_ else: return self._maskVar_[:]
[docs] def size(self): """ Not documented """ return self._lataxis_.size()
[docs] def writeScrip(self, cufile, gridTitle=None): """Write a grid to a SCRIP file. Parameter --------- cufile : is a Cdunif file, NOT a CDMS file. gridtitle : is a string identifying the grid. """ lat = numpy.ma.filled(self._lataxis_[:]) lon = numpy.ma.filled(self._lonaxis_[:]) blat, blon = self.getBounds() mask = self.getMask() ni, nj = self.shape if mask is None: mask = numpy.ones((ni, nj), numpy.int32) else: tmp = 1 - mask mask[:] = tmp.astype(mask.dtype.char) mask = mask.astype(numpy.int32) ngrid = ni * nj centerLat = copy.copy(lat) centerLat.shape = (ngrid,) centerLon = copy.copy(lon) centerLon.shape = (ngrid,) mask.shape = (ngrid,) clat = numpy.ma.filled(copy.copy(blat)) clat.shape = (ngrid, 4) clon = numpy.ma.filled(copy.copy(blon)) clon.shape = (ngrid, 4) # Write the file if gridTitle is None: gridTitle = self.id cufile.title = gridTitle cufile.createDimension("grid_size", ngrid) cufile.createDimension("grid_corners", 4) cufile.createDimension("grid_rank", 2) griddims = cufile.createVariable("grid_dims", 'i', ("grid_rank",)) gridcenterlat = cufile.createVariable( "grid_center_lat", 'd', ("grid_size",)) gridcenterlat.units = "degrees" gridcenterlon = cufile.createVariable( "grid_center_lon", 'd', ("grid_size",)) gridcenterlon.units = "degrees" gridimask = cufile.createVariable("grid_imask", 'i', ("grid_size",)) gridimask.units = "unitless" gridcornerlat = cufile.createVariable( "grid_corner_lat", 'd', ("grid_size", "grid_corners")) gridcornerlat.units = "degrees" gridcornerlon = cufile.createVariable( "grid_corner_lon", 'd', ("grid_size", "grid_corners")) gridcornerlon.units = "degrees" griddims[:] = numpy.array([nj, ni], numpy.int32) gridcenterlat[:] = centerLat gridcenterlon[:] = centerLon gridimask[:] = mask gridcornerlat[:] = clat gridcornerlon[:] = clon
[docs] def toGenericGrid(self, gridid=None): """ Not documented """ from .auxcoord import TransientAuxAxis1D from .coord import TransientVirtualAxis from .gengrid import TransientGenericGrid lat = numpy.ma.filled(self._lataxis_[:]) latunits = self._lataxis_.units lon = numpy.ma.filled(self._lonaxis_[:]) lonunits = self._lonaxis_.units blat, blon = self.getBounds() mask = self.getMask() ni, nj = self.shape ngrid = ni * nj centerLat = copy.copy(lat) centerLat.shape = (ngrid,) centerLon = copy.copy(lon) centerLon.shape = (ngrid,) if mask is not None: mask.shape = (ngrid,) cornerLat = numpy.ma.filled(copy.copy(blat)) cornerLat.shape = (ngrid, 4) cornerLon = numpy.ma.filled(copy.copy(blon)) cornerLon.shape = (ngrid, 4) iaxis = TransientVirtualAxis("cell", ngrid) lataxis = TransientAuxAxis1D(centerLat, axes=(iaxis,), bounds=cornerLat, attributes={'units': latunits}, id="latitude") lonaxis = TransientAuxAxis1D(centerLon, axes=(iaxis,), bounds=cornerLon, attributes={'units': lonunits}, id="longitude") grid = TransientGenericGrid(lataxis, lonaxis, id=gridid, tempmask=mask) return grid
[docs] def toCurveGrid(self, gridid=None): """ Not documented """ if gridid is None: gridid = self.id result = self.clone() result.id = gridid return result
[docs] def writeToFile(self, file): """ Not documented """ latvar = self._lataxis_.writeToFile(file) lonvar = self._lonaxis_.writeToFile(file) if self._maskVar_ is not None: maskid = "mask_" + self.id file.write(self._maskVar_, id=maskid) latvar.maskid = maskid lonvar.maskid = maskid return (latvar, lonvar)
[docs] def writeg(self, file): """Write self as a Gridspec file representing a curvilinear grid. The file, normally a CdmsFile, should already be open for writing and will be closed.""" import time # Set attributes if (hasattr(file, 'Conventions')): if (file.Conventions.find('Gridspec') < 0): file.Conventions = file.Conventions + ' Gridspec-0.0' else: file.Conventions = 'Gridspec-0.0' if (hasattr(file, 'gs_filetypes')): if (file.gs_filetypes.find('Curvilinear_Tile') < 0): file.gs_filetypes = file.gs_filetypes + ' Curvilinear_Tile' else: file.gs_filetypes = 'Curvilinear_Tile' t = time.time() id = int((t - int(t)) * 1.0e9) file.gs_id = id file.gs_originalfilename = os.path.basename(file.id) newhistory = '\n' + time.ctime() + ' CDAT/CDMS AbstractCurveGrid' # ... The \n gives a newline in the CDMS Python and in the Cdunif C code # which gets called to write to a file. Someplace in the NetCDF libraries, # or possibly the ncdump utility, the newline is converted back to a "\n" # string, so you don't see a newline when you view the file. If we want # a real newline as the CF specification says, the libraries or ncdump # will have to be changed. # ... In the future we may want to add more history information. file.history = getattr(self, 'history', '') + \ getattr(file, 'history', '') + newhistory # former tile variable and attributes if (hasattr(self, 'long_name') and self.long_name is not None): file.long_name = self.long_name else: file.long_name = 'gridspec_tile' # gs_geometryType is no longer required of Gridspec files, but as yet # there is no other proposal for describing the geometry (July 2010) if (hasattr(self, 'gs_geometryType') and self.gs_geometryType is not None): file.gs_geometryType = self.gs_geometryType else: file.gs_geometryType = 'spherical' # gs_discretizationType is no longer required of Gridspec files, but it's # harmless and may come in useful if (hasattr(self, 'gs_discretizationType') and self.gs_discretizationType is not None): file.gs_discretizationType = self.gs_discretizationType else: file.gs_discretizationType = 'logically_rectangular' file.gs_lonv = 'gs_x' file.gs_latv = 'gs_y' # Set up and write variables. When written, cdms writes not only the arrays # but also their coordinates, e.g. gs_nip. x = self._lonaxis_ if (not hasattr(x, 'units')): print("Warning, no units found for longitude") x.units = 'degree_east' if (not hasattr(x, 'standard_name')): print("Warning, no standard_name found for longitude axis") x.standard_name = 'longitude' if (x.standard_name == 'geographic_longitude'): # temporary for updating test files x.standard_name = 'longitude' x.id = file.gs_lonv # _lonaxis_ is a TransientAxis2D, hence a TransientVariable # But I don't know where the attribute _TransientVariable__domain comes # from y = self._lataxis_ if (not hasattr(y, 'units')): print("Warning, no units found for latitude") y.units = 'degree_north' if (not hasattr(y, 'standard_name')): print("Warning, no standard_name found for latitude axis") y.standard_name = 'latitude' if (y.standard_name == 'geographic_latitude'): # temporary for updating test files y.standard_name = 'latitude' y.id = file.gs_latv if(not hasattr(x, '_TransientVariable__domain')): # There probably doesn't exist enough information to write a correct # grid, but this will help. x._TransientVariable__domain = [(x,), (y,)] x._TransientVariable__domain[0][0].id = 'gs_njp' x._TransientVariable__domain[1][0].id = 'gs_nip' if (not hasattr(y, '_TransientVariable__domain')): # There probably doesn't exist enough information to write a correct # grid, but this will help. y._TransientVariable__domain = [(x,), (y,)] y._TransientVariable__domain[0][0].id = 'gs_njp' y._TransientVariable__domain[1][0].id = 'gs_nip' file.write(x) file.write(y) file.close()
[docs] def write_gridspec(self, filename): """Writes this grid to a Gridspec-compliant file, or does nothing if there is already a known file corresponding to this grid. The filename should be a complete path.""" # This method was never completed because the libCF functionality I had planned to # use never appeared (yet). # The functionality (other than checking gsfile) is now done by the writeg # method above. if (not hasattr(self, "gsfile")): self.gsfile = None self.gspath = None if (self.gsfile is not None): return (self.gsfile, self.gspath) else: raise RuntimeError( 'The libCF/Gridspec API does not provide for writing CurveGrids<<<')
[docs] def init_from_gridspec(self, filename): """Reads to grid from a Gridspec-compliant file. The filename should be a complete path. The contents of the file may overwrite data in the existing grid object.""" # - This is really a kind of init function. The __init__ function should # determine what kind of initialization is being done (from a file, from # another object, from arguments specifying the contents e.g. axes) and branch # to call a method such as this one. # - Another way to read a file is with the standard CDMS # pattern file=cdms2.open(...); g=file('grid') or g=file('') try: f = cdms2.open(filename) except IOError: print("Cannot open grid file for reading: ", filename) return self.init_from_gridspec_file(self, f) f.close()
[docs] def init_from_gridspec_file(self, f): """Reads to grid from a Gridspec-compliant file, f. This f should be a CdmsFile object, already open for reading. The contents of the file may overwrite data in the existing grid object.""" # As for the above init_from_gridspec method, this is really a kind of # init function and should be called from __init__ . ax, ay, gs_attr = f.gridspec_file_contents() # ... gridspec_file_contents is defined in dataset.py self.__init__(ay, ax) self.gsfile = gs_attr['filebase'] self.gspath = gs_attr['filepath'] self.long_name = gs_attr['long_name'] # gs_geometryType is no longer required of Gridspec files, but as yet # there is no other proposal for describing the geometry (July 2010) self.gs_geometryType = gs_attr['gs_geometryType'] # gs_discretizationType is no longer required of Gridspec files, but it's # harmless and may come in useful self.gs_discretizationType = gs_attr['gs_discretizationType'] return self
[docs] def subSlice(self, *specs, **keys): """Get a transient subgrid based on an argument list <specs> of slices.""" newlat = self._lataxis_.subSlice(*specs, **keys) newlon = self._lonaxis_.subSlice(*specs, **keys) if self._maskVar_ is None: newmask = None else: newmask = self._maskVar_.subSlice(*specs, **keys) result = TransientCurveGrid(newlat, newlon, maskvar=newmask) return result
[docs] def getGridSlices(self, domainlist, newaxislist, slicelist): """Determine which slices in slicelist correspond to the lat/lon elements of the grid. Parameters ---------- domainlist : is a list of axes of a variable. newaxislist : is a list of result axes after the slicelist is applied to domainlist. slicelist : is a list of slices. All lists are of equal length. Returns ------- value : is (newslicelist, gridaxislist) where newslicelist : is the elements of slicelist that correspond to the grid, in the preferred order of the grid. gridaxislist : is the elements of newaxislist that correspond to the grid, in the preferred order of the grid. """ iaxis = self._lataxis_.getAxis(0) jaxis = self._lataxis_.getAxis(1) k = 0 i = j = -1 for d in domainlist: if d.shape == iaxis.shape: if numpy.allclose(d[:], iaxis[:]) is True: inewaxis = newaxislist[k] islice = slicelist[k] i = k if d.shape == jaxis.shape: if numpy.allclose(d[:], jaxis[:]) is True: jnewaxis = newaxislist[k] jslice = slicelist[k] j = k k += 1 if i == -1 or j == -1: raise RuntimeError( 'Grid lat/lon domains do not match variable domain') return ((islice, jslice), (inewaxis, jnewaxis))
[docs] def getIndex(self): """Get the grid index""" if self._index_ is None: # Trying to stick in Stephane Raynaud's patch for autodetection nj, ni = self._lataxis_.shape dlon = numpy.max(self._lonaxis_[:]) - numpy.min(self._lonaxis_[:]) dx = max(dlon / ni, dlon / nj) dlat = numpy.max(self._lataxis_[:]) - numpy.min(self._lataxis_[:]) dy = max(dlat / ni, dlat / nj) latlin = numpy.ravel(numpy.ma.filled(self._lataxis_[:])) lonlin = numpy.ravel(numpy.ma.filled(self._lonaxis_[:])) _bindex.setDeltas(dx, dy) self._index_ = bindex.bindexHorizontalGrid(latlin, lonlin) return self._index_
[docs] def intersect(self, spec): """Intersect with the region specification. Parameters ---------- 'spec' : is a region specification of the form defined in the grid module. Returns ------- (mask, indexspecs) where 'mask' : is the mask of the result grid AFTER self and region spec are interested. 'indexspecs' : is a list of index specifications suitable for slicing a variable with the given grid. """ ni, nj = self.shape index = self.getIndex() latspec = spec[CoordTypeToLoc[LatitudeType]] lonspec = spec[CoordTypeToLoc[LongitudeType]] latlin = numpy.ravel(numpy.ma.filled(self._lataxis_[:])) lonlin = numpy.ravel(numpy.ma.filled(self._lonaxis_[:])) points = bindex.intersectHorizontalGrid( latspec, lonspec, latlin, lonlin, index) if len(points) == 0: raise CDMSError( 'No data in the specified region, longitude=%s, latitude=%s' % (repr(lonspec), repr(latspec))) fullmask = numpy.ones(ni * nj) numpy.put(fullmask, points, 0) fullmask = numpy.reshape(fullmask, (ni, nj)) iind = points // nj jind = points - iind * nj imin, imax, jmin, jmax = ( min(iind), max(iind) + 1, min(jind), max(jind) + 1) submask = fullmask[imin:imax, jmin:jmax] yid = self.getAxis(0).id xid = self.getAxis(1).id indexspecs = {yid: slice(imin, imax), xid: slice(jmin, jmax)} return submask, indexspecs
def getAxisList(self): """ Not documented """ return (self._lataxis_.getAxis(0), self._lataxis_.getAxis(1))
[docs] def isClose(self, g): """ Is Close Returns ------- 1 if g : is a grid of the same type and shape. A real element-by-element comparison would be too expensive here.""" if g is None: return 0 elif self.shape != g.shape: return 0 elif not isinstance(g, AbstractCurveGrid): return 0 else: return 1
[docs] def checkAxes(self, axes): """ Check Axes Returns ------- 1 iff every element of self.getAxisList() is in the list 'axes'.""" for item in self.getAxisList(): # if all [False, False, ....] result=0 if not any([allclose(item[:], axis[:]) for axis in axes]): result = 0 break else: result = 1 return result
[docs] def reconcile(self, axes): """ Reconcile Returns ------- a grid that is consistent with the axes, or None. For curvilinear grids this means that the grid-related axes are contained in the 'axes' list. """ result = self selfaxes = self.getAxisList() missing = [] for i in range(2): if selfaxes[i] not in axes: missing.append(i) result = None # Some of the grid axes are not in the 'axes' list if result is None: result = self.clone() used = [] # axes already matched for i in missing: for item in axes: if (item not in used) and len(selfaxes[i]) == len( item) and allclose(selfaxes[i], item): result._lataxis_.setAxis(i, item) result._lonaxis_.setAxis(i, item) used.append(item) break else: result = None break return result
[docs] def flatAxes(self): """ Flat Axes Returns ------- (flatlat, flatlon) where flatlat is a 1D NumPy array having the same length as the number of cells in the grid, similarly for flatlon. """ if self._flataxes_ is None: from . import MV2 as MV alat = MV.filled(self.getLatitude()) alon = MV.filled(self.getLongitude()) alatflat = numpy.ravel(alat) alonflat = numpy.ravel(alon) self._flataxes_ = (alatflat, alonflat) return self._flataxes_
shape = property(_getShape, None) # PropertiedClasses.set_property (AbstractCurveGrid, 'shape', # AbstractCurveGrid._getShape, nowrite=1, # nodelete=1) class DatasetCurveGrid(AbstractCurveGrid): def __init__(self, latAxis, lonAxis, id, parent=None, maskvar=None, tempmask=None, node=None): """Create a file curvilinear grid. """ AbstractCurveGrid.__init__( self, latAxis, lonAxis, id, maskvar, tempmask, node) self.parent = parent def __repr__(self): return "<DatasetCurveGrid, id: %s, shape: %s>" % ( self.id, repr(self.shape)) class FileCurveGrid(AbstractCurveGrid): """ Not documented """ def __init__(self, latAxis, lonAxis, id, parent=None, maskvar=None, tempmask=None, node=None): """Create a file curvilinear grid. """ AbstractCurveGrid.__init__( self, latAxis, lonAxis, id, maskvar, tempmask, node) self.parent = parent def __repr__(self): return "<FileCurveGrid, id: %s, shape: %s>" % ( self.id, repr(self.shape)) class TransientCurveGrid(AbstractCurveGrid): """ Not documented """ grid_count = 0
[docs] def __init__(self, latAxis, lonAxis, id=None, maskvar=None, tempmask=None): """Create a file curvilinear grid. """ if id is None: TransientCurveGrid.grid_count += 1 id = 'grid_' + str(TransientCurveGrid.grid_count) AbstractCurveGrid.__init__( self, latAxis, lonAxis, id, maskvar, tempmask)
def __repr__(self): return "<TransientCurveGrid, id: %s, shape: %s>" % ( self.id, repr(self.shape))
[docs] def toCurveGrid(self, gridid=None): """ Not documented """ if gridid is None: result = self else: result = self.clone() result.id = gridid return result
[docs]def readScripCurveGrid(fileobj, dims, whichType, whichGrid): """Read a 'native' SCRIP grid file, returning a transient curvilinear grid. Parameters ---------- fileobj : is an open CDMS dataset or file object. dims : is the grid shape. whichType : is the type of file, either "grid" or "mapping" f whichType is "mapping", whichGrid is the choice of grid, either "source" or "destination" Returns ------- """ from .coord import TransientAxis2D if 'S' in list(fileobj.variables.keys()): if whichType == "grid": gridCornerLatName = 'grid_corner_lat' gridCornerLonName = 'grid_corner_lon' gridMaskName = 'grid_imask' gridCenterLatName = 'grid_center_lat' gridCenterLonName = 'grid_center_lon' titleName = 'title' elif whichGrid == "destination": gridCornerLatName = 'yv_b' gridCornerLonName = 'xv_b' gridMaskName = 'mask_b' gridCenterLatName = 'yc_b' gridCenterLonName = 'xc_b' titleName = 'dest_grid' else: gridCornerLatName = 'yv_a' gridCornerLonName = 'xv_a' gridMaskName = 'mask_a' gridCenterLatName = 'yc_a' gridCenterLonName = 'xc_a' titleName = 'source_grid' else: if whichType == "grid": gridCornerLatName = 'grid_corner_lat' gridCornerLonName = 'grid_corner_lon' gridMaskName = 'grid_imask' gridCenterLatName = 'grid_center_lat' gridCenterLonName = 'grid_center_lon' titleName = 'title' elif whichGrid == "destination": gridCornerLatName = 'dst_grid_corner_lat' gridCornerLonName = 'dst_grid_corner_lon' gridMaskName = 'dst_grid_imask' gridCenterLatName = 'dst_grid_center_lat' gridCenterLonName = 'dst_grid_center_lon' titleName = 'dest_grid' else: gridCornerLatName = 'src_grid_corner_lat' gridCornerLonName = 'src_grid_corner_lon' gridMaskName = 'src_grid_imask' gridCenterLatName = 'src_grid_center_lat' gridCenterLonName = 'src_grid_center_lon' titleName = 'source_grid' vardict = fileobj.variables cornerLat = fileobj(gridCornerLatName) cornerLon = fileobj(gridCornerLonName) ncorners = cornerLat.shape[-1] ni = dims[1] nj = dims[0] gridshape = (ni, nj) boundsshape = (ni, nj, ncorners) if hasattr(cornerLat, 'units') and cornerLat.units[0:6].lower() == 'radian': cornerLat = (cornerLat * (180.0 / numpy.pi)).reshape(boundsshape) cornerLon = (cornerLon * (180.0 / numpy.pi)).reshape(boundsshape) else: cornerLat = cornerLat.reshape(boundsshape) cornerLon = cornerLon.reshape(boundsshape) iaxis = TransientVirtualAxis("i", ni) jaxis = TransientVirtualAxis("j", nj) if gridMaskName in vardict: # SCRIP convention: 0 for invalid data # numpy.ma convention: 1 for invalid data mask = 1 - fileobj(gridMaskName) mask = mask.reshape(gridshape) else: mask = None if gridCenterLatName in vardict: centerLat = fileobj(gridCenterLatName).reshape(gridshape) gclat = fileobj[gridCenterLatName] if hasattr(gclat, "units") and gclat.units.lower() == 'radians': centerLat *= (180.0 / numpy.pi) else: centerLat = cornerLat[:, :, 0] if gridCenterLonName in vardict: centerLon = fileobj(gridCenterLonName).reshape(gridshape) gclon = fileobj[gridCenterLonName] if hasattr(gclon, "units") and gclon.units.lower() == 'radians': centerLon *= (180.0 / numpy.pi) else: centerLon = cornerLon[:, :, 0] if hasattr(fileobj, titleName): gridid = getattr(fileobj, titleName) gridid = gridid.strip().replace(' ', '_') else: gridid = "<None>" lataxis = TransientAxis2D(centerLat, axes=(iaxis, jaxis), bounds=cornerLat, attributes={'units': 'degrees_north'}, id="latitude") lonaxis = TransientAxis2D(centerLon, axes=(iaxis, jaxis), bounds=cornerLon, attributes={'units': 'degrees_east'}, id="longitude") grid = TransientCurveGrid(lataxis, lonaxis, id=gridid, tempmask=mask) return grid