def grid_latlon(self): """ Return the latitude and longitude coordinate vectors of the model grid. """ lats, _ = gaussian_lats_wts(self.nlat) lons = np.arange(0., 360., 360. / self.nlon) return lats, lons
def inspect_gridtype(latitudes): """ Determine a grid type by examining the points of a latitude dimension. Raises a ValueError if the grid type cannot be determined. **Argument:** *latitudes* An iterable of latitude point values. **Returns:** *gridtype* Either 'gaussian' for a Gaussian grid or 'regular' for an equally-spaced grid. """ # Define a tolerance value for differences, this value must be much # smaller than expected grid spacings. tolerance = 5e-4 # Get the number of latitude points in the dimension. nlat = len(latitudes) diffs = np.abs(np.diff(latitudes)) equally_spaced = (np.abs(diffs - diffs[0]) < tolerance).all() if not equally_spaced: # The latitudes are not equally-spaced, which suggests they might # be gaussian. Construct sample gaussian latitudes and check if # the two match. gauss_reference, wts = gaussian_lats_wts(nlat) difference = np.abs(latitudes - gauss_reference) if (difference > tolerance).any(): raise ValueError('latitudes are neither equally-spaced ' 'or Gaussian') gridtype = 'gaussian' else: # The latitudes are equally-spaced. Construct reference global # equally spaced latitudes and check that the two match. if nlat % 2: # Odd number of latitudes includes the poles. equal_reference = np.linspace(90, -90, nlat) else: # Even number of latitudes doesn't include the poles. delta_latitude = 180. / nlat equal_reference = np.linspace(90 - 0.5 * delta_latitude, -90 + 0.5 * delta_latitude, nlat) difference = np.abs(latitudes - equal_reference) if (difference > tolerance).any(): raise ValueError('equally-spaced latitudes are invalid ' '(they may be non-global)') gridtype = 'regular' return gridtype
def __init__(self,sp,dt,ntrunc,ptop=0.,p0=1.e5,grav=9.80616,omega=7.292e-5,cp=1004,\ rgas=287.,efold=3600.,ndiss=8,tdrag=1.e30,tdiab=1.e30,\ umax=40,jetexp=2,delth=20,moistfact=1.0): # set model parameters self.p0 = p0 # mean surface pressure self.ptop = ptop # model top pressure self.rgas = rgas # gas constant for dry air self.grav = grav # gravity self.omega = omega # rotation rate self.cp = cp # specific heat of dry air at constant pressure self.delth = delth # static stability # factor to reduce static stability in rising air # (crude moist physics assuming air is saturated) # moistfact = 1 is dry model self.moistfact = moistfact dp = 0.5*(ptop-p0) exnf1 = cp*((p0+0.5*dp)/p0)**(rgas/cp) exnf2 = cp*((p0+1.5*dp)/p0)**(rgas/cp) self.delta_exnf = exnf2-exnf1 # diff in exner function between 2 levs. # efolding time scale for hyperdiffusion at shortest wavenumber self.efold = efold self.ndiss = ndiss # order of hyperdiffusion (2 for laplacian) self.sp = sp # Spharmt instance self.ntrunc = ntrunc # triangular truncation wavenumber self.dt = dt # time step (secs) self.tdiab = tdiab # lower layer drag timescale self.tdrag = tdrag # interface relaxation timescale # create lat/lon arrays delta = 2.*np.pi/sp.nlon if sp.gridtype == 'regular': lats1d = 0.5*np.pi-delta*np.arange(sp.nlat) wts = np.cos(lats1d) else: lats1d,wts = gaussian_lats_wts(sp.nlat) lats1d = lats1d*np.pi/180. # convert to radians. lons1d = np.arange(-np.pi,np.pi,delta) lons,lats = np.meshgrid(lons1d,lats1d) self.lons = lons; self.lats = lats # weights for computing global means. self.globalmeanwts = np.ones((sp.nlat,sp.nlon))*wts[:,np.newaxis] self.globalmeanwts = self.globalmeanwts/self.globalmeanwts.sum() self.f = 2.*omega*np.sin(lats)[:,:,np.newaxis] # coriolis # create laplacian operator and its inverse. indxm, indxn = getspecindx(ntrunc) indxn = indxn.astype(np.float32) totwavenum = indxn*(indxn+1.0) self.lap = -totwavenum/sp.rsphere**2 self.ilap = np.zeros(self.lap.shape, np.float32) self.ilap[1:] = 1./self.lap[1:] # hyperdiffusion operator self.hyperdiff = -(1./efold)*(totwavenum/totwavenum[-1])**(ndiss/2) # set equilibrium layer thicknes profile. self._interface_profile(umax,jetexp)
def grid_degree(NY=256,NX=None): ''' Return lon,lat for the spherical grid with (NX,NY) If NX is not given, default NX = NY*2 Spherical harmonic examples: NY = 256 NY = 128 ''' if NX is None: NX = NY*2 if not _SPHARM_INSTALLED: raise _import_error latdeg,wgt = spharm.gaussian_lats_wts(NY) londeg = numpy.linspace(0.,360.,NX+1)[:-1] return londeg,latdeg[::-1]
def planetaryvorticity(self, omega=None): """Planetary vorticity (Coriolis parameter). **Optional argument:** *omega* Earth's angular velocity. The default value if not specified is 7.292x10**-5 s**-1. **Returns:** *pvorticity* The planetary vorticity. **See also:** `~VectorWind.absolutevorticity`. **Example:** Compute planetary vorticity using default values:: pvrt = w.planetaryvorticity() Override the default value for Earth's angular velocity:: pvrt = w.planetaryvorticity(omega=7.2921150) """ if omega is None: # Define the Earth's angular velocity. omega = 7.292e-05 nlat = self.s.nlat if self.gridtype == 'gaussian': lat, wts = gaussian_lats_wts(nlat) else: if nlat % 2: lat = np.linspace(90, -90, nlat) else: dlat = 180. / nlat lat = np.arange(90 - dlat / 2., -90, -dlat) try: cp = 2. * omega * np.sin(np.deg2rad(lat)) except (TypeError, ValueError): raise ValueError('invalid value for omega: {!r}'.format(omega)) indices = [slice(0, None)] + [np.newaxis] * (len(self.u.shape) - 1) f = cp[indices] * np.ones(self.u.shape, dtype=np.float32) return f
def reference_solutions(container_type, gridtype): """Generate reference solutions in the required container.""" container_type = container_type.lower() if container_type not in ('standard', 'iris', 'cdms', 'xarray'): raise ValueError("unknown container type: " "'{!s}'".format(container_type)) reference = __read_reference_solutions(gridtype) if container_type == 'standard': # Reference solution already in numpy arrays. return reference # Generate coordinate dimensions for meta-data interfaces. if gridtype == 'gaussian': lats, _ = gaussian_lats_wts(72) else: lats = np.linspace(90, -90, 73) lons = np.arange(0, 360, 2.5) _get_wrapper(container_type)(reference, lats, lons) return reference
def __init__(self,sp,dt,ntrunc,theta1=300,theta2=330,grav=9.80616,omega=7.292e-5,cp=1004,\ zmid=5.e3,ztop=15.e3,efold=3600.,ndiss=8,tdrag=1.e30,tdiab=1.e30,umax=30,jetexp=4): # setup model parameters self.theta1 = theta1 # lower layer pot. temp. self.theta2 = theta2 # upper layer pot. temp. self.delth = theta2-theta1 # difference in potential temp between layers self.grav = grav # gravity self.omega = omega # rotation rate self.cp = cp # Specific Heat of Dry Air at Constant Pressure, self.zmid = zmid # resting depth of lower layer (m) self.ztop = ztop # resting depth of both layers (m) # efolding time scale for hyperdiffusion at shortest wavenumber self.efold = efold self.ndiss = ndiss # order of hyperdiffusion (2 for laplacian) self.sp = sp # Spharmt instance self.ntrunc = ntrunc # triangular truncation wavenumber self.dt = dt # time step (secs) self.tdiab = tdiab # lower layer drag timescale self.tdrag = tdrag # interface relaxation timescale # create lat/lon arrays delta = 2.*np.pi/sp.nlon if sp.gridtype == 'regular': lats1d = 0.5*np.pi-delta*np.arange(sp.nlat) else: lats1d,wts = gaussian_lats_wts(sp.nlat) lats1d = lats1d*np.pi/180. lons1d = np.arange(-np.pi,np.pi,delta) lons,lats = np.meshgrid(lons1d,lats1d) self.lons = lons self.lats = lats self.f = 2.*omega*np.sin(lats)[:,:,np.newaxis] # coriolis # create laplacian operator and its inverse. indxm, indxn = getspecindx(ntrunc) indxn = indxn.astype(np.float32)[:,np.newaxis] totwavenum = indxn*(indxn+1.0) self.lap = -totwavenum/sp.rsphere**2 self.ilap = np.zeros(self.lap.shape, np.float32) self.ilap[1:,:] = 1./self.lap[1:,:] # hyperdiffusion operator self.hyperdiff = -(1./efold)*(totwavenum/totwavenum[-1])**(ndiss/2) # initialize orography to zero. self.orog = np.zeros((sp.nlat,sp.nlon),np.float32) # set equilibrium layer thicknes profile. self._interface_profile(umax,jetexp)
def gaussian_latlon_grid(nlat, as_2d=True): """ Creates a gaussian lat/lon grid for spherical transforms. Args ---- nlat : int Number of gaussian latitudes. (# lons = 2*nlat) as_2d : bool If True, returns 2d lat/lon grids. Otherwise, returns 1d arrays. Returns ------- lo, la : numpy array Arrays of lon/lat values in degrees. """ lo = np.linspace(0., 360., 2 * nlat + 1)[:-1] la, _ = spharm.gaussian_lats_wts(nlat) if as_2d: lo, la = np.meshgrid(lo, la) return lo, la
def __init__(self, nlat, nlon, latlon_type='regular'): ''' Note: The latlon grid should not include the pole. Support latlon_type: regular, gaussian ''' self.nlat = nlat self.nlon = nlon self.latlon_type = latlon_type self.grid_type = '%s latlon'%latlon_type self.nsize = nlat*nlon self.tmp_lons = np.linspace(0, 2*pi, nlon+1) if latlon_type == 'regular': self.tmp_lats = np.linspace(-pi/2, pi/2, nlat+2) elif latlon_type == 'gaussian': import spharm # NCAR SPHEREPACK degs, wts = spharm.gaussian_lats_wts(nlat) pts = np.deg2rad(degs[::-1]) # convert to south first self.tmp_lats = np.zeros(nlat+2) self.tmp_lats[1:-1] = pts self.tmp_lats[0] = -np.pi/2 self.tmp_lats[-1] = np.pi/2 else: raise ValueError, 'Wrong latlon_type=%s. Support latlon_type: regular, gaussian'%(latlon_type) self.latlons = np.zeros((self.nsize,2), 'f8') seq = 0 for lat in self.tmp_lats[1:-1]: for lon in self.tmp_lons[:-1]: self.latlons[seq,:] = (lat,lon) seq += 1 self.dlat = self.tmp_lats[2] - self.tmp_lats[1] self.dlon = self.tmp_lons[2] - self.tmp_lons[1]
def _gridtype(self, latitudes): """Determine the type of a latitude dimension.""" # Define a tolerance value for differences, this value must be much # smaller than expected grid spacings. tolerance = 0.001 # Get the number of latitude points in the dimension. nlat = len(latitudes) diffs = np.abs(np.diff(latitudes)) equally_spaced = (np.abs(diffs - diffs[0]) < tolerance).all() if not equally_spaced: # The latitudes are not equally-spaced, which suggests they might # be gaussian. Construct sample gaussian latitudes and check if # the two match. gauss_reference, wts = gaussian_lats_wts(nlat) difference = np.abs(latitudes - gaussian_reference) if (d > tolerance).any(): raise ValueError('latitudes are unequally-spaced ' 'but are not gaussian') gridtype = 'gaussian' else: # The latitudes are equally-spaced. Construct reference global # equally spaced latitudes and check that the two match. if nlat % 2: # Odd number of latitudes includes the poles. equal_reference = np.linspace(90, -90, nlat) else: # Even number of latitudes doesn't include the poles. delta_latitude = 180. / nlat equal_reference = np.linspace(90 - 0.5 * delta_latitude, -90 + 0.5 * delta_latitude, nlat) difference = np.abs(latitudes - equal_reference) if (difference > tolerance).any(): raise ValueError('equally-spaced latitudes are ' 'invalid (non-global?)') gridtype = 'regular' return gridtype
def make_padded_lats_lons(nlat, nlon, ll_type): if 'shift_lon' in ll_type: ll_type = ll_type.split('-')[0] shift_lon = True else: ll_type = ll_type shift_lon = False if shift_lon: dlon = 2*np.pi/nlon padded_lons = np.linspace(dlon/2, 2*np.pi+dlon/2, nlon+1) else: padded_lons = np.linspace(0, 2*pi, nlon+1) assert ll_type in ['regular', 'gaussian', 'include_pole'], "ll_type {} is not supported.".foramt(ll_type) if ll_type == 'include_pole': padded_lats = np.linspace(-pi/2, pi/2, nlat) elif ll_type == 'regular': dlat = np.pi/nlat padded_lats = np.zeros(nlat+2) padded_lats[1:-1] = np.linspace((-pi+dlat)/2, (pi-dlat)/2, nlat) padded_lats[0] = -np.pi/2 padded_lats[-1] = np.pi/2 elif ll_type == 'gaussian': import spharm # NCAR SPHEREPACK degs, wts = spharm.gaussian_lats_wts(nlat) pts = np.deg2rad(degs[::-1]) # convert to south first padded_lats = np.zeros(nlat+2) padded_lats[1:-1] = pts padded_lats[0] = -np.pi/2 padded_lats[-1] = np.pi/2 return ll_type, padded_lats, padded_lons
def planetaryvorticity(self, omega=None): """Planetary vorticity (Coriolis parameter). **Optional argument:** *omega* Earth's angular velocity. The default value if not specified is 7.292x10**-5 s**-1. **Example:** Compute planetary vorticity using default values: pvrt = w.planetaryvorticity() Override the default value for Earth's angular velocity: pvrt = w.planetaryvorticity(omega=7.2921150) """ if omega is None: # Define the Earth's angular velocity. omega = 7.292e-05 nlat = self.s.nlat if self.gridtype == 'gaussian': lat, wts = gaussian_lats_wts(nlat) else: if nlat % 2: lat = np.linspace(90, -90, nlat) else: dlat = 180. / nlat lat = np.arange(90 - dlat / 2., -90, -dlat) try: cp = 2. * omega * np.sin(np.deg2rad(lat)) except TypeError, ValueError: raise ValueError('invalid value for omega: {!r}'.format(omega))
from spharm import gaussian_lats_wts, Spharmt, legendre, specintrp, getgeodesicpts, regrid import numpy, math, sys # Rossby-Haurwitz wave field def rhwave(wavenum,omega,re,lats,lons): return -re**2*omega*numpy.sin(lats)+re**2*omega*((numpy.cos(lats))**wavenum)*numpy.sin(lats)*numpy.cos(wavenum*lons) # create Rossby-Haurwitz wave data on 144x73 regular and 192x94 gaussian grids. nlats_reg = 73 nlons_reg = 144 nlats_gau = 94 nlons_gau = 192 gaulats, wts = gaussian_lats_wts(nlats_gau) lats_reg = numpy.zeros((nlats_reg,nlons_reg),numpy.float64) lons_reg = numpy.zeros((nlats_reg,nlons_reg),numpy.float64) lons_gau = numpy.zeros((nlats_gau,nlons_gau),numpy.float64) wavenum = 4.0 omega = 7.848e-6 re = 6.371e6 delat = 2.*math.pi/nlons_reg lats = (0.5*math.pi-delat*numpy.indices(lats_reg.shape)[0,:,:]) lons = (delat*numpy.indices(lons_reg.shape)[1,:,:]) psi_reg_exact = rhwave(wavenum,omega,re,lats,lons) delat = 2.*math.pi/nlons_gau lats = (math.pi/180.)*numpy.transpose(gaulats*numpy.ones((nlons_gau,nlats_gau),'d')) lons = (delat*numpy.indices(lons_gau.shape)[1,:,:]) psi_gau = rhwave(wavenum,omega,re,lats,lons)
def __init__(self, nlat, nlon, ll_type='regular', geo='sphere'): ''' Note: The latlon grid should not include the pole. Support ll_type: regular, gaussian Support shape: sphere, plane ''' self.nlat = nlat self.nlon = nlon self.ll_type = ll_type self.geo = geo self.nsize = nlat*nlon #----------------------------------------------------- # Generate latlon grid #----------------------------------------------------- lons = np.linspace(0, 2*np.pi, nlon+1)[:-1] if ll_type == 'regular': lats = np.linspace(-np.pi/2, np.pi/2, nlat+2)[1:-1] elif ll_type == 'gaussian': import spharm # NCAR SPHEREPACK degs, wts = spharm.gaussian_lats_wts(nlat) lats = np.deg2rad(degs[::-1]) # convert to south pole first else: raise ValueError, 'Wrong ll_type=%s. Support ll_type: regular, gaussian'%(ll_type) self.lons = lons self.lats = lats #----------------------------------------------------- # Set the connectivity #----------------------------------------------------- if geo == 'sphere': # vtk_cell_type is VTK_QUAD(=9) xyzs = np.zeros((nlat*nlon,3), 'f8') seq = 0 for lat in lats: for lon in lons: xyzs[seq,0] = np.cos(lat)*np.cos(lon) xyzs[seq,1] = np.cos(lat)*np.sin(lon) xyzs[seq,2] = np.sin(lat) seq += 1 link_size = (nlat-1)*nlon links = np.zeros((link_size,5), 'i4') link_seq = 0 for j in xrange(nlat-1): for i in xrange(nlon): ip = i+1 if i<nlon-1 else 0 seq1 = j*nlon + i seq2 = j*nlon + ip seq3 = (j+1)*nlon + ip seq4 = (j+1)*nlon + i links[link_seq,0] = visit_writer.quad links[link_seq,1:] = [seq1, seq2, seq3, seq4] link_seq += 1 self.pts = xyzs.ravel().tolist() self.links = links.tolist() elif geo == 'plane': if ll_type == 'regular': self.dimensions = (nlon,nlat,1) elif ll_type == 'gaussian': self.x = self.lons.tolist() self.y = self.lats.tolist() self.z = [0]