Source code for regrid2.mvGenericRegrid

# This code is provided with the hope that it will be useful.
# No guarantee is provided whatsoever. Use at your own risk.
#
# David Kindig and Alex Pletzer, Tech-X Corp. (2012)

"""
Generic interface to multiple regrid classes. No dependence on cdms2 variables.
"""
import operator
import numpy

import regrid2
import re
from distarray import MultiArrayIter
from functools import reduce

# used to locate fully masked cells
EPS = 10 * 1.19209e-07


[docs]def guessPeriodicity(srcBounds): """ Guess if a src grid is periodic Parameters ---------- srcBounds : the nodal src set of coordinates Returns ------- 1 if periodic, warp around, 0 otherwise """ res = 0 if srcBounds is not None: res = 1 # assume longitude to be the last coordinate lonsb = srcBounds[-1] nlon = lonsb.shape[-1] dlon = (lonsb.max() - lonsb.min()) / float(nlon) tol = 1.e-2 * dlon if abs((lonsb[..., -1] - 360.0 - lonsb[..., 0] ).sum() / float(lonsb.size)) > tol: # looks like a regional model res = 0 return res
[docs]class GenericRegrid: """ Generic Regrid class. Constructor Parameters ---------- srcGrid : list of numpy arrays, source horizontal coordinates dstGrid list of numpy arrays, destination horizontal coordinate dtype numpy data type for src/dst data regridMethod linear (bi, tri,...) default or conservative regridTool currently either 'libcf' or 'esmf' srcGridMask array of same shape as srcGrid srcBounds list of numpy arrays of same shape as srcGrid srcGridAreas array of same shape as srcGrid dstGridMask array of same shape as dstGrid dstBounds list of numpy arrays of same shape as dstGrid dstGridAreas array of same shape as dstGrid **args additional arguments to be passed to the specific tool libcf': mkCyclic={True, False}, handleCut={True,False} 'esmf': periodicity={0,1}, coordSys={'deg', 'cart'}, ... """
[docs] def __init__(self, srcGrid, dstGrid, dtype, regridMethod, regridTool, srcGridMask=None, srcBounds=None, srcGridAreas=None, dstGridMask=None, dstBounds=None, dstGridAreas=None, **args): """ """ self.nGridDims = len(srcGrid) self.regridMethod = regridMethod if len(srcGrid) != len(dstGrid): msg = 'mvGenericRegrid.__init__: mismatch in number of dims' msg += ' len(srcGrid) = %d != len(dstGrid) = %d' % \ (self.nGridDims, len(dstGrid)) raise regrid2.RegridError(msg) # parse the options if re.search('libcf', regridTool.lower()) or \ re.search('gsreg', regridTool.lower()): # LibCF self.tool = regrid2.LibCFRegrid(srcGrid, dstGrid, srcGridMask=srcGridMask, srcBounds=srcBounds, **args) elif re.search('esm', regridTool.lower()): # ESMF staggerLoc = args.get('staggerLoc', 'center') if 'staggerLoc' in args: del args['staggerLoc'] periodicity = args.get('periodicity', guessPeriodicity(srcBounds)) if 'periodicity' in args: del args['periodicity'] coordSys = args.get('coordSys', 'deg') if 'coordSys' in args: del args['coordSys'] # Get the shapes self.srcGridShape = srcGrid[0].shape self.dstGridShape = dstGrid[0].shape self.hasSrcBounds = False self.hasDstBounds = False if srcBounds is not None: self.hasSrcBounds = True if dstBounds is not None: self.hasDstBounds = True self.srcGridAreasShape = None self.dstGridAreasShape = None if srcGridAreas is not None: self.srcGridAreasShape = srcGridAreas[0].shape if dstGridAreas is not None: self.dstGridAreasShape = dstGridAreas[0].shape # Initialize self.tool = regrid2.ESMFRegrid(self.srcGridShape, self.dstGridShape, dtype=dtype, regridMethod=regridMethod, staggerLoc=staggerLoc, periodicity=periodicity, coordSys=coordSys, hasSrcBounds=self.hasSrcBounds, hasDstBounds=self.hasDstBounds, srcGridAreasShape=self.srcGridAreasShape, dstGridAreasShape=self.dstGridAreasShape, **args) self.tool.setCoords(srcGrid, dstGrid, srcGridMask=srcGridMask, srcBounds=srcBounds, srcGridAreas=srcGridAreas, dstGridMask=dstGridMask, dstBounds=dstBounds, dstGridAreas=dstGridAreas, globalIndexing=True, **args) else: msg = """mvGenericRegrid.__init__: ERROR unrecognized tool %s, valid choices are: 'libcf', 'esmf'""" % regridTool raise regrid2.RegridError(msg)
[docs] def computeWeights(self, **args): """ Compute Weights """ self.tool.computeWeights(**args)
[docs] def apply(self, srcData, dstData, rootPe=None, missingValue=None, **args): """ Regrid source to destination Parameters ---------- srcData : array (input) dstData : array (output) rootPe : if other than None, then results will be MPI gathered missingValue : if not None, then data mask will be interpolated and data value set to missingValue when masked """ # assuming the axes are the slowly varying indices srcHorizShape = srcData.shape[-self.nGridDims:] dstHorizShape = dstData.shape[-self.nGridDims:] srcDataMaskFloat = None dstDataMaskFloat = None dstMask = None if missingValue is not None: srcDataMaskFloat = numpy.zeros(srcHorizShape, srcData.dtype) dstDataMaskFloat = numpy.zeros(dstHorizShape, dstData.dtype) nonHorizShape = srcData.shape[: -self.nGridDims] if len(nonHorizShape) == 0: # # no axis... just call apply # # adjust for masking if missingValue is not None: srcDataMaskFloat[:] = (srcData == missingValue) # set field values to zero where missing, we'll add the mask # contribution later indata = numpy.array(srcData * (1 - (srcDataMaskFloat == 1)), dtype=srcData.dtype) # interpolate mask self.tool.apply(srcDataMaskFloat, dstDataMaskFloat, rootPe=rootPe, globalIndexing=True, **args) if re.search('conserv', self.regridMethod.lower(), re.I): dstMask = numpy.array( (dstDataMaskFloat > 1 - EPS), numpy.int32) else: dstMask = numpy.array((dstDataMaskFloat > 0), numpy.int32) # Initialize output to missin_value dstData[:] = missingValue # interpolate the data self.tool.apply(indata, dstData, rootPe=rootPe, globalIndexing=True, **args) # add missing values dstData *= (1 - dstMask) dstData += dstMask * missingValue else: # no masking, just interpolate the data self.tool.apply(srcData, dstData, rootPe=rootPe, globalIndexing=True, **args) else: nonHorizShape2 = dstData.shape[: -self.nGridDims] if not numpy.all(nonHorizShape2 == nonHorizShape): msg = 'mvGenericRegrid.apply: axes detected ' msg += 'but %s != %s ' % (str(nonHorizShape2), str(nonHorizShape)) raise regrid2.RegridError(msg) # # iterate over all axes # # create containers to hold input/output values # (a copy is essential here) zros = '[' + ('0,' * len(nonHorizShape)) + '...]' indata = numpy.array(eval('srcData' + zros)) outdata = numpy.array(eval('dstData' + zros)) # now iterate over all non lat/lon coordinates for it in MultiArrayIter(nonHorizShape): indices = it.getIndices() slce = '[' slce += reduce(operator.add, ['%d,' % i for i in indices]) slce += '...]' indata = eval('srcData' + slce) # adjust for masking if missingValue is not None: srcDataMaskFloat[:] = (indata == missingValue) # set field values to zero where missing, we'll add the mask # contribution later # indata *= (1 - (srcDataMaskFloat == 1)) # srcDataMaskFloatData = srcDataMaskFloat * numpy.random.rand(srcHorizShape[0],srcHorizShape[1])*100 # interpolate mask self.tool.apply(srcDataMaskFloat, dstDataMaskFloat, rootPe=rootPe, globalIndexing=True, srcDataMask=(1 - srcDataMaskFloat), **args) if re.search('conserv', self.regridMethod.lower(), re.I): # cell interpolation dstMask = numpy.array( (dstDataMaskFloat > 1 - EPS), numpy.int32) else: # nodal interpolation dstMask = numpy.array( (dstDataMaskFloat > 0), numpy.int32) # interpolate the data, using the appropriate tool self.tool.apply(indata, outdata, rootPe=rootPe, globalIndexing=True, srcDataMask=srcDataMaskFloat, **args) # import vcs # pp = vcs.init() # pp.plot(indata) # pp.interact() # pp.clear() # pp.plot(outdata) # pp.interact() # pp.clear() # apply missing value contribution if missingValue is not None: # add mask contribution outdata *= (1 - dstMask) outdata += dstMask * missingValue # fill in dstData exec('dstData' + slce + ' = outdata')
[docs] def getDstGrid(self): """ Return the destination grid, may be different from the dst grid provided to the constructor due to domain decomposition Returns ------- local grid on this processor """ return self.tool.getDstGrid()
[docs] def fillInDiagnosticData(self, diag, rootPe=None): """ Fill in diagnostic data Parameters ---------- diag : a dictionary whose entries, if present, will be filled entries are tool dependent rootPe : root processor where data should be gathered (or None if local areas are to be returned) """ self.tool.fillInDiagnosticData(diag, rootPe=rootPe)