def epv_sphere(theta, u, v, levs, lats, lons): """ Computes the Ertel Potential Vorticity (PV) on a latitude/longitude grid. :param theta: 3D potential temperature array on isobaric levels :param u: 3D u components of the horizontal wind on isobaric levels :param v: 3D v components of the horizontal wind on isobaric levels :param levs: 1D pressure vectors :param lats: 1D latitude vectors :param lons: 1D longitude vectors :return: Ertel PV in potential vorticity units (PVU) """ iz, iy, ix = theta.shape dthdp, dthdy, dthdx = gradient_sphere(theta, levs, lats, lons) dudp, dudy, dudx = gradient_sphere(u, levs, lats, lons) dvdp, dvdy, dvdx = gradient_sphere(v, levs, lats, lons) abvort = np.zeros_like(theta).astype('f') for kk in range(0, iz): abvort[kk, :, :] = vertical_vorticity_latlon(u[kk, :, :].squeeze(), v[kk, :, :].squeeze(), lats, lons, abs_opt=True) epv = (-9.81 * (-dvdp * dthdx - dudp * dthdy + abvort * dthdp)) * 1.0e6 return epv
def epv_sphere(theta, pres, u, v, lats, lons): """ Computes the Ertel Potential Vorticity (PV) on a latitude/longitude grid https://github.com/scavallo/python_scripts/blob/master/utils/weather_modules.py Input: theta: 3D potential temperature array on isobaric levels pres: 3D pressure array u,v: 3D u and v components of the horizontal wind on isobaric levels lats,lons: 1D latitude and longitude vectors Output: epv: Ertel PV in potential vorticity units (PVU) Steven Cavallo October 2012 University of Oklahoma """ iz, iy, ix = theta.shape dthdp, dthdy, dthdx = gradient_sphere(theta, pres, lats, lons) dudp, dudy, dudx = gradient_sphere(u, pres, lats, lons) dvdp, dvdy, dvdx = gradient_sphere(v, pres, lats, lons) avort = np.zeros_like(theta).astype('f') for kk in range(0, iz): avort[kk, :, :] = vertical_vorticity_latlon(u[kk, :, :].squeeze(), v[kk, :, :].squeeze(), lats, lons, 1) epv = (-9.81 * (-dvdp * dthdx - dudp * dthdy + avort * dthdp)) * 10**6 return epv
def vertical_vorticity_latlon(u, v, lats, lons, abs_opt=False): """ Calculate the vertical vorticity on a latitude/longitude grid. :param u: 2 dimensional u wind arrays, dimensioned by (lats,lons). :param v: 2 dimensional v wind arrays, dimensioned by (lats,lons). :param lats: latitude vector :param lons: longitude vector :param abs_opt: True to compute absolute vorticity, False for relative vorticity only :return: Two dimensional array of vertical vorticity. """ dudy, dudx = gradient_sphere(u, lats, lons) dvdy, dvdx = gradient_sphere(v, lats, lons) if abs_opt: # 2D latitude array glats = np.zeros_like(u).astype('f') for jj in range(0, len(lats)): glats[jj, :] = lats[jj] # Coriolis parameter f = 2 * 7.292e-05 * np.sin(np.deg2rad(glats)) else: f = 0. vert_vort = dvdx - dudy + f return vert_vort
def eliassen_palm_flux_sphere(geop, theta, lats, lons, levs, normalize_option): """ Computes the 3-D Eliassen-Palm flux vectors and divergence on a latitude/longitude grid with any vertical coordinate Computation is Equation 5.7 from: R. A. Plumb, On the Three-dimensional Propagation of Stationary Waves, J. Atmos. Sci., No. 3, 42 (1985). Input: geop: 3D geopotential (m^2 s-2) theta: 3D potential temperature (K) lats,lons: 1D latitude and longitude vectors levs: 1D pressure vector (Pa) normalize_option: 0 = no normalizing, 1 = normalize by maximum value at each vertical level, 2 = standardize Output: Fx, Fy, Fz: Eliassen-Palm flux x, y, z vector components divF: Eliassen-Palm flux divergence Steven Cavallo March 2014 University of Oklahoma """ iz, iy, ix = geop.shape # First need to filter out numeric nans geop = arr.filter_numeric_nans(geop, 0, 0, 'low') theta = arr.filter_numeric_nans(theta, 200, 0, 'low') theta = arr.filter_numeric_nans(theta, 10000, 0, 'high') theta_anom, theta_anom_std = calculate.spatial_anomaly(theta, 1) geop_anom, geop_anom_std = calculate.spatial_anomaly(geop, 1) latarr = np.zeros_like(geop).astype('f') farr = np.zeros_like(geop).astype('f') for kk in range(0, iz): for jj in range(0, iy): latarr[kk, jj, :] = lats[jj] farr[kk, jj, :] = 2.0 * constants.omega * np.sin(lats[jj] * (np.pi / 180.0)) psi = geop / farr psi_anom, psi_anom_std = calculate.spatial_anomaly(psi, 1) pres = np.zeros_like(theta_anom).astype('f') for kk in range(0, iz): pres[kk, :, :] = levs[kk] coef = pres * np.cos(latarr * (np.pi / 180.0)) arg1 = coef / (2.0 * np.pi * (constants.Re**2) * np.cos(latarr * (np.pi / 180.0)) * np.cos(latarr * (np.pi / 180.0))) arg2 = coef / (2.0 * np.pi * (constants.Re**2) * np.cos(latarr * (np.pi / 180.0))) arg3 = coef * (2.0 * (constants.omega**2) * np.sin(latarr * (np.pi / 180.0)) * np.sin(latarr * (np.pi / 180.0))) dthdz, yy, xx = gradient_sphere(theta, geop / g, lats, lons) xx, dthdy, dthdx = gradient_sphere(theta_anom, geop / g, lats, lons) dpsidz, dpsidy, dpsidx = gradient_sphere(psi_anom, geop / g, lats, lons) d2psidxdz, d2psidxdy, d2psidx2 = gradient_sphere(dpsidx, geop / g, lats, lons) aaa, d2psidxdz, ccc = gradient_sphere(dpsidz, geop / g, lats, lons) N2 = (g / theta) * (dthdz) arg4 = arg3 / (N2 * constants.Re * np.cos(latarr * (np.pi / 180.0))) Fx = arg1 * (dpsidx**2.0 - (psi_anom * d2psidx2)) Fy = arg2 * ((dpsidy * dpsidx) - (psi_anom * d2psidxdy)) Fz = arg4 * ((dpsidx * dpsidz) - (psi_anom * d2psidxdz)) Fx_z, Fx_y, Fx_x = gradient_sphere(Fx, geop / g, lats, lons) Fy_z, Fy_y, Fy_x = gradient_sphere(Fy, geop / g, lats, lons) Fz_z, Fz_y, Fz_x = gradient_sphere(Fz, geop / g, lats, lons) divF = Fx_x + Fy_y + Fz_z if normalize_option == 1: for kk in range(0, iz): Fx[kk, :, :] = Fx[kk, :, :] / np.nanmax(np.abs(Fx[kk, :, :])) Fy[kk, :, :] = Fy[kk, :, :] / np.nanmax(np.abs(Fy[kk, :, :])) Fz[kk, :, :] = Fz[kk, :, :] / np.nanmax(np.abs(Fz[kk, :, :])) if normalize_option == 2: for kk in range(0, iz): Fx[kk, :, :] = Fx[kk, :, :] / np.nanstd(Fx[kk, :, :]) Fy[kk, :, :] = Fy[kk, :, :] / np.nanstd(Fy[kk, :, :]) Fz[kk, :, :] = Fz[kk, :, :] / np.nanstd(Fz[kk, :, :]) return Fx, Fy, Fz, divF