def calcgw_gfs(v, lat, lon): height, lats, lons = v.data(lat1=lat - gw_gfs_margin_deg, lat2=lat + gw_gfs_margin_deg, lon1=lon - gw_gfs_margin_deg, lon2=lon + gw_gfs_margin_deg) i = np.searchsorted(lats[:, 0], lat) if abs(lats[i + 1, 0] - lat) < abs(lats[i, 0] - lat): i = i + 1 j = np.searchsorted(lons[0, :], lon) if abs(lons[0, i + 1] - lon) < abs(lons[0, i] - lon): j = j + 1 #print('level', v.level, 'height', height[i,j], lats[i,j], lons[i,j]) # Set up some constants based on our projection, including the Coriolis parameter and # grid spacing, converting lon/lat spacing to Cartesian f = mpcalc.coriolis_parameter(np.deg2rad(lats)).to('1/s') dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats) res_km = (dx[i, j] + dy[i, j]).magnitude / 2000. # Smooth height data. Sigma=1.5 for gfs 0.5deg height = ndimage.gaussian_filter(height, sigma=1.5 * 50 / res_km, order=0) # In MetPy 0.5, geostrophic_wind() assumes the order of the dimensions is (X, Y), # so we need to transpose from the input data, which are ordered lat (y), lon (x). # Once we get the components,transpose again so they match our original data. geo_wind_u, geo_wind_v = mpcalc.geostrophic_wind(height * units.m, f, dx, dy) return height[i, j], geo_wind_u[i, j], geo_wind_v[i, j]
def test_geostrophic_wind_asym(): """Test geostrophic wind calculation with a complicated field.""" z = np.array([[1, 2, 4], [4, 8, 4], [8, 6, 4]]) * 200. * units.meter # Using g as the value for f allows it to cancel out ug, vg = geostrophic_wind(z, g.magnitude / units.sec, 200. * units.meter, 100. * units.meter, dim_order='yx') true_u = -np.array([[6, 12, 0], [7, 4, 0], [8, -4, 0]]) * units('m/s') true_v = np.array([[1, 1.5, 2], [4, 0, -4], [-2, -2, -2]]) * units('m/s') assert_array_equal(ug, true_u) assert_array_equal(vg, true_v) # Now try for xy ordered ug, vg = geostrophic_wind(z.T, g.magnitude / units.sec, 200. * units.meter, 100. * units.meter, dim_order='xy') assert_array_equal(ug, true_u.T) assert_array_equal(vg, true_v.T)
def test_geostrophic_geopotential(): """Test geostrophic wind calculation with geopotential.""" z = np.array([[48, 49, 48], [49, 50, 49], [48, 49, 48]]) * 100. * units('m^2/s^2') ug, vg = geostrophic_wind(z, 1 / units.sec, 100. * units.meter, 100. * units.meter) true_u = np.array([[-1, 0, 1]] * 3) * units('m/s') true_v = -true_u.T assert_array_equal(ug, true_u) assert_array_equal(vg, true_v)
def calcgw_wrf(f, lat, lon, levels, tidx=0): # MFDataset removes the time dimension from XLAT, XLONG xlat = f.variables['XLAT'] xlslice = (0, ) * (len(xlat.shape) - 2) + (slice(None), slice(None)) xlat = xlat[xlslice] xlong = f.variables['XLONG'][xlslice] (iy, ix), area, (iby, ibx) = get_wrf_dims(f, lat, lon, xlat, xlong) areat = (tidx, ) + area areatz = (tidx, slice(None)) + area #print('wrf coords', lat, lon, xlat[iy,ix], xlong[iy,ix]) #print(xlat[area][iby,ibx], xlong[area][iby,ibx], areat) # load area hgt = (f.variables['PH'][areatz] + f.variables['PHB'][areatz]) / 9.81 hgtu = (hgt[:-1] + hgt[1:]) * .5 pres = f.variables['P'][areatz] + f.variables['PB'][areatz] terrain = f.variables['HGT'][areat] # find suitable pressure levels yminpres, xminpres = np.unravel_index(pres[0].argmin(), pres[0].shape) pres1 = pres[0, yminpres, xminpres] - 1. aglpt = hgtu[:, iby, ibx] - terrain[iby, ibx] pres0 = pres[np.searchsorted(aglpt, levels[-1]), iby, ibx] # plevels = np.arange(pres1, min(pres0, pres1)-1, -1000.) plevels = np.arange(pres1, min(pres0, pres1) - 1000, -1000.) # !!!! # interpolate wrf into pressure levels phgt = log_interpolate_1d(plevels, pres, hgtu, axis=0) # Set up some constants based on our projection, including the Coriolis parameter and # grid spacing, converting lon/lat spacing to Cartesian coriol = mpcalc.coriolis_parameter(np.deg2rad(xlat[area])).to('1/s') # lat_lon_grid_deltas doesn't work under py2, but for WRF grid it is still # not very accurate, better use direct values. #dx, dy = mpcalc.lat_lon_grid_deltas(xlong[area], xlat[area]) dx = f.DX * units.m dy = f.DY * units.m # Smooth height data. Sigma=1.5 for gfs 0.5deg res_km = f.DX / 1000. ug = np.zeros(plevels.shape, 'f8') vg = np.zeros(plevels.shape, 'f8') for i in range(len(plevels)): sh = ndimage.gaussian_filter(phgt[i, :, :], sigma=1.5 * 50 / res_km, order=0) # ugl, vgl = mpcalc.geostrophic_wind(sh * units.m, coriol, dx, dy) # bug1 ugl, vgl = mpcalc.geostrophic_wind(sh * units.m, dx, dy, np.deg2rad(xlat[area])) ug[i] = ugl[iby, ibx].magnitude vg[i] = vgl[iby, ibx].magnitude return phgt[:, iby, ibx], ug, vg
def test_geostrophic_wind(): """Test geostrophic wind calculation with basic conditions.""" z = np.array([[48, 49, 48], [49, 50, 49], [48, 49, 48]]) * 100. * units.meter # Using g as the value for f allows it to cancel out ug, vg = geostrophic_wind(z, g.magnitude / units.sec, 100. * units.meter, 100. * units.meter) true_u = np.array([[-1, 0, 1]] * 3) * units('m/s') true_v = -true_u.T assert_array_equal(ug, true_u) assert_array_equal(vg, true_v)
def test_geostrophic_3d(): """Test geostrophic wind calculation with 3D array.""" z = np.array([[48, 49, 48], [49, 50, 49], [48, 49, 48]]) * 100. # Using g as the value for f allows it to cancel out z3d = np.dstack((z, z)) * units.meter ug, vg = geostrophic_wind(z3d, g.magnitude / units.sec, 100. * units.meter, 100. * units.meter) true_u = np.array([[-1, 0, 1]] * 3) * units('m/s') true_v = -true_u.T true_u = concatenate((true_u[..., None], true_u[..., None]), axis=2) true_v = concatenate((true_v[..., None], true_v[..., None]), axis=2) assert_array_equal(ug, true_u) assert_array_equal(vg, true_v)
def test_geostrophic_gempak(): """Test of geostrophic wind calculation against gempak values.""" z = np.array([[5586387.00, 5584467.50, 5583147.50], [5594407.00, 5592487.50, 5591307.50], [5604707.50, 5603247.50, 5602527.50]]).T \ * (9.80616 * units('m/s^2')) * 1e-3 dx = np.deg2rad(0.25) * Re * np.cos(np.deg2rad(44)) # Inverting dy since latitudes in array increase as you go up dy = -np.deg2rad(0.25) * Re f = (2 * omega * np.sin(np.deg2rad(44))).to('1/s') ug, vg = geostrophic_wind(z * units.m, f, dx, dy, dim_order='xy') true_u = np.array([[21.97512, 21.97512, 22.08005], [31.89402, 32.69477, 33.73863], [38.43922, 40.18805, 42.14609]]) true_v = np.array([[-10.93621, -7.83859, -4.54839], [-10.74533, -7.50152, -3.24262], [-8.66612, -5.27816, -1.45282]]) assert_almost_equal(ug[1, 1], true_u[1, 1] * units('m/s'), 2) assert_almost_equal(vg[1, 1], true_v[1, 1] * units('m/s'), 2)
def test_geostrophic_gempak(): """Test of geostrophic wind calculation against gempak values.""" z = np.array([[5586387.00, 5584467.50, 5583147.50], [5594407.00, 5592487.50, 5591307.50], [5604707.50, 5603247.50, 5602527.50]]).T \ * (9.80616 * units('m/s^2')) * 1e-3 dx = np.deg2rad(0.25) * Re * np.cos(np.deg2rad(44)) # Inverting dy since latitudes in array increase as you go up dy = -np.deg2rad(0.25) * Re f = (2 * omega * np.sin(np.deg2rad(44))).to('1/s') ug, vg = geostrophic_wind(z * units.m, f, dx, dy) true_u = np.array([[21.97512, 21.97512, 22.08005], [31.89402, 32.69477, 33.73863], [38.43922, 40.18805, 42.14609]]) true_v = np.array([[-10.93621, -7.83859, -4.54839], [-10.74533, -7.50152, -3.24262], [-8.66612, -5.27816, -1.45282]]) assert_almost_equal(ug[1, 1], true_u[1, 1] * units('m/s'), 2) assert_almost_equal(vg[1, 1], true_v[1, 1] * units('m/s'), 2)
def geostr_met(array_2d_geoheight, array_1d_lat, array_1d_lon) : ''' Calculate Geostrophic wind map (2d) from 2d array area. https://unidata.github.io/python-gallery/examples/Ageostrophic_Wind_Example.html Parameters ---------- array_2d_geoheight : read numpy array [m] array_1d_lat : read numpy array [deg] array_1d_lon : read numpy array [deg] Returns ------- array_2d : return interplated and extrapolated 2d array ''' import numpy as np import metpy.calc as mpcalc from metpy.units import units # Combine 1D latitude and longitudes into a 2D grid of locations lon_2d, lat_2d = np.meshgrid(array_1d_lon, array_1d_lat) # Set up some constants based on our projection, including the Coriolis parameter and # grid spacing, converting lon/lat spacing to Cartesian f = mpcalc.coriolis_parameter(np.deg2rad(lat_2d)).to('1/s') dx, dy = mpcalc.lat_lon_grid_deltas(lon_2d + 360, lat_2d) dx, dy = np.array(dx), np.array(dy) dy *= -1 # In MetPy 0.5, geostrophic_wind() assumes the order of the dimensions is (X, Y), # so we need to transpose from the input data, which are ordered lat (y), lon (x). # Once we get the components,transpose again so they match our original data. geo_wind_u, geo_wind_v = mpcalc.geostrophic_wind(array_2d_geoheight.data * units.m, f, dx, dy) return(geo_wind_u, geo_wind_v)
# # Most of the calculations in `metpy.calc` will accept DataArrays by converting them # into their corresponding unit arrays. While this may often work without any issues, we must # keep in mind that because the calculations are working with unit arrays and not DataArrays: # # - The calculations will return unit arrays rather than DataArrays # - Broadcasting must be taken care of outside of the calculation, as it would only recognize # dimensions by order, not name # # As an example, we calculate geostropic wind at 500 hPa below: lat, lon = xr.broadcast(y, x) f = mpcalc.coriolis_parameter(lat) dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init) heights = data['height'].loc[time[0]].loc[{vertical.name: 500.}] u_geo, v_geo = mpcalc.geostrophic_wind(heights, f, dx, dy) print(u_geo) print(v_geo) ######################################################################### # Also, a limited number of calculations directly support xarray DataArrays or Datasets (they # can accept *and* return xarray objects). Right now, this includes # # - Derivative functions # - ``first_derivative`` # - ``second_derivative`` # - ``gradient`` # - ``laplacian`` # - Cross-section functions # - ``cross_section_components`` # - ``normal_component``
# keep in mind that because the calculations are working with unit arrays and not DataArrays: # # - The calculations will return unit arrays rather than DataArrays # - Broadcasting must be taken care of outside of the calculation, as it would only recognize # dimensions by order, not name # # Also, some of the units used in CF conventions (such as 'degrees_north') are not recognized # by pint, so we must implement a workaround. # # As an example, we calculate geostropic wind at 500 hPa below: lat, lon = xr.broadcast(y, x) f = mpcalc.coriolis_parameter(lat.values * units.degrees) dx, dy = mpcalc.lat_lon_grid_deltas(lon.values, lat.values) heights = data['height'].loc[time[0]].loc[{vertical.name: 500.}] u_geo, v_geo = mpcalc.geostrophic_wind(heights, f, dx, dy, dim_order='yx') print(u_geo) print(v_geo) ######################################################################### # Plotting # -------- # # Like most meteorological data, we want to be able to plot these data. DataArrays can be used # like normal numpy arrays in plotting code, or we can use some of xarray's plotting # functionality. # # (More detail beyond the following can be found at `xarray's plotting reference # <http://xarray.pydata.org/en/stable/plotting.html>`_.) # A very simple example example of a plot of 500 hPa heights
# T = digging_300hPa_trough(parameter='temp') ###################################################################### # Set geographic parameters for analytic grid to then # lats = np.linspace(35, 50, 101) lons = np.linspace(260, 290, 101) lon, lat = np.meshgrid(lons, lats) # Calculate Geostrophic Wind from Analytic Heights f = mpcalc.coriolis_parameter(lat * units('degrees')) dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats) ugeo, vgeo = mpcalc.geostrophic_wind(Z * units.meter, f, dx, dy, dim_order='yx') # Get the wind direction for each point wdir = mpcalc.wind_direction(ugeo, vgeo) # Compute the Gradient Wind via an approximation dydx = mpcalc.first_derivative(Z, delta=dx, axis=1) d2ydx2 = mpcalc.first_derivative(dydx, delta=dx, axis=1) R = ((1 + dydx.m**2)**(3. / 2.)) / d2ydx2.m geo_mag = mpcalc.wind_speed(ugeo, vgeo) grad_mag = geo_mag.m - (geo_mag.m**2) / (f.magnitude * R) ugrad, vgrad = mpcalc.wind_components(grad_mag * units('m/s'), wdir)
# Combine 1D latitude and longitudes into a 2D grid of locations lon_2d, lat_2d = np.meshgrid(lon, lat) # Smooth height data height = ndimage.gaussian_filter(height, sigma=1.5, order=0) # Set up some constants based on our projection, including the Coriolis parameter and # grid spacing, converting lon/lat spacing to Cartesian f = mpcalc.coriolis_parameter(np.deg2rad(lat_2d)).to('1/s') dx, dy = mpcalc.lat_lon_grid_spacing(lon_2d, lat_2d) dy *= -1 # In MetPy 0.5, geostrophic_wind() assumes the order of the dimensions is (X, Y), # so we need to transpose from the input data, which are ordered lat (y), lon (x). # Once we get the components,transpose again so they match our original data. geo_wind_u, geo_wind_v = mpcalc.geostrophic_wind(height * units.m, f, dx, dy) geo_wind_u = geo_wind_u geo_wind_v = geo_wind_v # Calculate ageostrophic wind components ageo_wind_u = u_wind - geo_wind_u ageo_wind_v = v_wind - geo_wind_v # Create new figure fig = plt.figure(figsize=(15, 10), facecolor='black') # Add the map and set the extent ax = plt.axes(projection=ccrs.PlateCarree()) ax.set_extent([-105., -93., 35., 43.]) ax.background_patch.set_fill(False)
# Accessing just the underlying units: height_units = heights.metpy.units height_units ######################################################################### # Calculations # ------------ # # MetPy's xarray integration extends to its calculation suite as well. Most grid-capable # calculations (such as thermodynamics, kinematics, and smoothers) fully support xarray # ``DataArray``\s by accepting them as inputs, returning them as outputs, and automatically # using the attached coordinate data/metadata to determine grid arguments heights = data_parsed.metpy.parse_cf('Geopotential_height_isobaric').metpy.sel( time='2017-09-05 18:00', vertical=500 * units.hPa) u_g, v_g = mpcalc.geostrophic_wind(heights) u_g ######################################################################### # For profile-based calculations (and most remaining calculations in the ``metpy.calc`` # module), xarray ``DataArray``\s are accepted as inputs, but the outputs remain Pint # Quantities (typically scalars). Note that MetPy's profile calculations (such as CAPE and # CIN) require the sounding to be ordered from highest to lowest pressure. As seen earlier # in this tutorial, this data is ordered the other way, so we need to reverse the inputs # to ``mpcalc.surface_based_cape_cin``. data_at_point = data.metpy.sel(time1='2017-09-05 12:00', latitude=30 * units.degrees_north, longitude=260 * units.degrees_east) dewpoint = mpcalc.dewpoint_from_relative_humidity( data_at_point['Temperature_isobaric'],
# into their corresponding unit arrays. While this may often work without any issues, we must # keep in mind that because the calculations are working with unit arrays and not DataArrays: # # - The calculations will return unit arrays rather than DataArrays # - Broadcasting must be taken care of outside of the calculation, as it would only recognize # dimensions by order, not name # # As an example, we calculate geostropic wind at 500 hPa below: lat, lon = xr.broadcast(y, x) dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init) heights = data['height'].metpy.loc[{ 'time': time[0], 'vertical': 500. * units.hPa }] u_geo, v_geo = mpcalc.geostrophic_wind(heights, dx, dy, lat) print(u_geo) print(v_geo) ######################################################################### # Also, a limited number of calculations directly support xarray DataArrays or Datasets (they # can accept *and* return xarray objects). Right now, this includes # # - Derivative functions # - ``first_derivative`` # - ``second_derivative`` # - ``gradient`` # - ``laplacian`` # - Cross-section functions # - ``cross_section_components`` # - ``normal_component``
# Combine 1D latitude and longitudes into a 2D grid of locations lon_2d, lat_2d = np.meshgrid(lon, lat) # Smooth height data height = ndimage.gaussian_filter(height, sigma=1.5, order=0) # Set up some constants based on our projection, including the Coriolis parameter and # grid spacing, converting lon/lat spacing to Cartesian f = coriolis_parameter(np.deg2rad(lat_2d)).to('1/s') dx, dy = calc_dx_dy(lon_2d, lat_2d) # In MetPy 0.5, geostrophic_wind() assumes the order of the dimensions is (X, Y), # so we need to transpose from the input data, which are ordered lat (y), lon (x). # Once we get the components,transpose again so they match our original data. geo_wind_u, geo_wind_v = geostrophic_wind(height.T * units.m, f.T, dx.T, dy.T) geo_wind_u = geo_wind_u.T geo_wind_v = geo_wind_v.T # Calculate ageostrophic wind components ageo_wind_u = u_wind - geo_wind_u ageo_wind_v = v_wind - geo_wind_v # Create new figure fig = plt.figure(figsize=(15, 10), facecolor='black') # Add the map and set the extent ax = plt.axes(projection=ccrs.PlateCarree()) ax.set_extent([-105., -93., 35., 43.]) ax.background_patch.set_fill(False)