def test_smooth_n_pt_wrong_number(): """Test the smooth_n_pt function using wrong number of points.""" hght = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], [5728., 5712., 5692., 5678., 5662.], [5772., 5748., 5718., 5697., 5673.], [5816., 5784., 5744., 5716., 5684.]]) with pytest.raises(ValueError): smooth_n_point(hght, 7)
def test_smooth_n_pt_9_units(): """Test the smooth_n_pt function using 9 points with units.""" hght = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], [5728., 5712., 5692., 5678., 5662.], [5772., 5748., 5718., 5697., 5673.], [5816., 5784., 5744., 5716., 5684.]]) * units.meter shght = smooth_n_point(hght, 9, 1) s_true = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5675.5, 5666.75, 5658.75, 5651.], [5728., 5711., 5693.5, 5677.5, 5662.], [5772., 5746.5, 5720.25, 5696.25, 5673.], [5816., 5784., 5744., 5716., 5684.]]) * units.meter assert_array_almost_equal(shght, s_true)
def test_smooth_n_pt_temperature(): """Test the smooth_n_pt function with temperature units.""" t = np.array([[2.73, 3.43, 6.53, 7.13, 4.83], [ 3.73, 4.93, 6.13, 6.63, 8.23 ], [3.03, 4.83, 6.03, 7.23, 7.63], [3.33, 4.63, 7.23, 6.73, 6.23], [3.93, 3.03, 7.43, 9.23, 9.23]]) * units.degC smooth_t = smooth_n_point(t, 9, 1) smooth_t_true = np.array([[2.73, 3.43, 6.53, 7.13, 4.83], [3.73, 4.6425, 5.96125, 6.81124, 8.23], [3.03, 4.81125, 6.1175, 6.92375, 7.63], [3.33, 4.73625, 6.43, 7.3175, 6.23], [3.93, 3.03, 7.43, 9.23, 9.23]]) * units.degC assert_array_almost_equal(smooth_t, smooth_t_true, 4)
def test_smooth_n_pt_5(): """Test the smooth_n_pt function using 5 points.""" hght = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], [5728., 5712., 5692., 5678., 5662.], [5772., 5748., 5718., 5697., 5673.], [5816., 5784., 5744., 5716., 5684.]]) shght = smooth_n_point(hght, 5, 1) s_true = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5675.75, 5666.375, 5658.875, 5651.], [5728., 5711.5, 5692.75, 5677.75, 5662.], [5772., 5747.25, 5719.125, 5696.625, 5673.], [5816., 5784., 5744., 5716., 5684.]]) assert_array_almost_equal(shght, s_true)
def test_smooth_n_pt_9_repeat(): """Test the smooth_n_pt function using 9 points with two passes.""" hght = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], [5728., 5712., 5692., 5678., 5662.], [5772., 5748., 5718., 5697., 5673.], [5816., 5784., 5744., 5716., 5684.]]) shght = smooth_n_point(hght, 9, 2) s_true = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5675.4375, 5666.9375, 5658.8125, 5651.], [5728., 5710.875, 5693.875, 5677.625, 5662.], [5772., 5746.375, 5720.625, 5696.375, 5673.], [5816., 5784., 5744., 5716., 5684.]]) assert_array_almost_equal(shght, s_true)
def plot_files(dss, **args): # Using args we don't have to change the prototype function if we want to add other parameters! first = True for time_sel in dss.time: data = dss.sel(time=time_sel) data['prmsl'].values = mpcalc.smooth_n_point(data['prmsl'].values, n=9, passes=10) time, run, cum_hour = get_time_run_cum(data) # Build the name of the output image filename = subfolder_images[projection] + '/' + variable_name + '_%s.png' % cum_hour cs = args['ax'].contourf(args['x'], args['y'], data['geop'], extend='both', cmap=args['cmap'], levels=args['levels_gph']) c = args['ax'].contour(args['x'], args['y'], data['prmsl'], levels=args['levels_mslp'], colors='white', linewidths=1.5) labels = args['ax'].clabel(c, c.levels, inline=True, fmt='%4.0f', fontsize=6) maxlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'max', 100, symbol='H', color='royalblue', random=True) minlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'min', 100, symbol='L', color='coral', random=True) an_fc = annotation_forecast(args['ax'], time) an_var = annotation(args['ax'], 'Geopotential height @500hPa [m] and MSLP (hPa)', loc='lower left', fontsize=6) an_run = annotation_run(args['ax'], run) logo = add_logo_on_map(ax=args['ax'], zoom=0.1, pos=(0.95, 0.08)) if first: plt.colorbar(cs, orientation='horizontal', label='Geopotential height [m]', pad=0.03, fraction=0.04) if debug: plt.show(block=True) else: plt.savefig(filename, **options_savefig) remove_collections([c, cs, labels, an_fc, an_var, an_run, maxlabels, minlabels, logo]) first = False
def test_smooth_n_pt_5(array_type): """Test the smooth_n_pt function using 5 points.""" hght = np.array([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], [5728., 5712., 5692., 5678., 5662.], [5772., 5748., 5718., 5697., 5673.], [5816., 5784., 5744., 5716., 5684.]]) mask = np.zeros_like(hght) mask[::2, ::2] = 1 hght = array_type(hght, '', mask=mask) shght = smooth_n_point(hght, 5, 1) s_true = array_type([[5640., 5640., 5640., 5640., 5640.], [5684., 5675.75, 5666.375, 5658.875, 5651.], [5728., 5711.5, 5692.75, 5677.75, 5662.], [5772., 5747.25, 5719.125, 5696.625, 5673.], [5816., 5784., 5744., 5716., 5684.]], '') assert_array_almost_equal(shght, s_true)
def test_smooth_n_pt_3d_units(): """Test the smooth_n_point function with a 3D array with units.""" hght = [[[5640.0, 5640.0, 5640.0, 5640.0, 5640.0], [5684.0, 5676.0, 5666.0, 5659.0, 5651.0], [5728.0, 5712.0, 5692.0, 5678.0, 5662.0], [5772.0, 5748.0, 5718.0, 5697.0, 5673.0], [5816.0, 5784.0, 5744.0, 5716.0, 5684.0]], [[6768.0, 6768.0, 6768.0, 6768.0, 6768.0], [6820.8, 6811.2, 6799.2, 6790.8, 6781.2], [6873.6, 6854.4, 6830.4, 6813.6, 6794.4], [6926.4, 6897.6, 6861.6, 6836.4, 6807.6], [6979.2, 6940.8, 6892.8, 6859.2, 6820.8]]] * units.m shght = smooth_n_point(hght, 9, 2) s_true = [[[5640., 5640., 5640., 5640., 5640.], [5684., 5675.4375, 5666.9375, 5658.8125, 5651.], [5728., 5710.875, 5693.875, 5677.625, 5662.], [5772., 5746.375, 5720.625, 5696.375, 5673.], [5816., 5784., 5744., 5716., 5684.]], [[6768., 6768., 6768., 6768., 6768.], [6820.8, 6810.525, 6800.325, 6790.575, 6781.2], [6873.6, 6853.05, 6832.65, 6813.15, 6794.4], [6926.4, 6895.65, 6864.75, 6835.65, 6807.6], [6979.2, 6940.8, 6892.8, 6859.2, 6820.8]]] * units.m assert_array_almost_equal(shght, s_true)
# Set subset slice for the geographic extent of data to limit download lon_slice = slice(200, 350) lat_slice = slice(85, 10) # Grab lat/lon values (GFS will be 1D) lats = ds.lat.sel(lat=lat_slice).values lons = ds.lon.sel(lon=lon_slice).values # Grab the pressure levels and select the data to be imported # Need all pressure levels for Temperatures, U and V Wind, and Rel. Humidity # Smooth with the gaussian filter from scipy pres = ds['isobaric3'].values[:] * units('Pa') tmpk_var = ds['Temperature_isobaric'].metpy.sel(lat=lat_slice, lon=lon_slice).squeeze() tmpk = mpcalc.smooth_n_point(tmpk_var, 9, 2) thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd_var = ds['u-component_of_wind_isobaric'].metpy.sel( lat=lat_slice, lon=lon_slice).squeeze() vwnd_var = ds['v-component_of_wind_isobaric'].metpy.sel( lat=lat_slice, lon=lon_slice).squeeze() uwnd = mpcalc.smooth_n_point(uwnd_var, 9, 2) vwnd = mpcalc.smooth_n_point(vwnd_var, 9, 2) # Create a clean datetime object for plotting based on time of Geopotential heights vtime = ds.time.data[0].astype('datetime64[ms]').astype('O') ###################################################################### # Use MetPy to compute the baroclinic potential vorticity on all isobaric # levels and other variables
va='center', transform=axes[1, 0].transAxes) for i, tidx in enumerate([36, 48, 60]): ds1 = metpy_temp_adv(fname1, plevs, tidx) ds2 = metpy_temp_adv(fname2, plevs, tidx) ds3 = ds2 - ds1 plev = 850 ax = axes[0, i] cf = ax.contourf(ds2.lon, ds2.lat, mpcalc.smooth_n_point(ds2.accrain.values, 9), clevs, cmap=cmap, norm=norm, extend='max') #cf = ax.contourf(ds2.lon, ds2.lat, ndimage.gaussian_filter(ds2.accrain.values, sigma=3, order=0), # clevs, cmap=cmap, norm=norm, extend='max') #cs = ax.contour(ds2.lon, ds2.lat, mpcalc.smooth_n_point(ds2.z.sel(level=plev).values/9.8, 9), # np.arange(1300,1580,10), colors='grey', linewidths=1) #ax.clabel(cs, fontsize=7, inline=1, inline_spacing=3, fmt='%im') fmin = np.min(mpcalc.smooth_n_point(ds2.accrain.values, 9)) fmax = np.max(mpcalc.smooth_n_point(ds2.accrain.values, 9)) ax.set_title('Min/Max = {:0.1f}/{:0.1f} mm'.format(np.array(fmin), np.array(fmax)),
def plot_files(dss, **args): first = True for time_sel in dss.time: data = dss.sel(time=time_sel) data['prmsl'].values = mpcalc.smooth_n_point(data['prmsl'].values, n=9, passes=10) time, run, cum_hour = get_time_run_cum(data) # Build the name of the output image filename = subfolder_images[ projection] + '/' + variable_name + '_%s.png' % cum_hour cs = args['ax'].contourf(args['x'], args['y'], data['tp'], extend='max', cmap=args['cmap'], norm=args['norm'], levels=args['levels_precip']) c = args['ax'].contour(args['x'], args['y'], data['prmsl'], levels=args['levels_mslp'], colors='black', linewidths=1.) labels = args['ax'].clabel(c, c.levels, inline=True, fmt='%4.0f', fontsize=6) maxlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'max', 150, symbol='H', color='royalblue', random=True) minlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'min', 150, symbol='L', color='coral', random=True) an_fc = annotation_forecast(args['ax'], time) an_var = annotation(args['ax'], 'Accumulated precipitation and MSLP [hPa]', loc='lower left', fontsize=6) an_run = annotation_run(args['ax'], run) logo = add_logo_on_map(ax=args['ax'], zoom=0.1, pos=(0.95, 0.08)) if first: plt.colorbar(cs, orientation='horizontal', label='Accumulated precipitation [mm]', pad=0.035, fraction=0.04) if debug: plt.show(block=True) else: plt.savefig(filename, **options_savefig) remove_collections( [c, cs, labels, an_fc, an_var, an_run, logo, maxlabels, minlabels]) first = False
# Add geopolitical boundaries for map reference ax1.add_feature(cfeature.COASTLINE.with_scale('50m')) ax1.add_feature(cfeature.LAKES.with_scale('50m'), color='black', linewidths=0.5) # Set some titles plt.title('Hovmoller Diagram', loc='left') plt.title('ERA5 Reanalysis', loc='right') # Bottom plot for Hovmoller diagram ax2 = fig.add_subplot(gs[1, 0]) ax2.invert_yaxis() # Reverse the time order to do oldest first # Plot of chosen variable averaged over latitude and slightly smoothed clevs = np.arange(-25, 25, 2.5) cf = ax2.contourf(lons, vtimes, mpcalc.smooth_n_point( avg_data, 9, 2), clevs, cmap=plt.cm.bwr, extend='both') cs = ax2.contour(lons, vtimes, mpcalc.smooth_n_point( avg_data, 9, 2), clevs, colors='k', linewidths=0.5) cbar = plt.colorbar(cf, orientation='horizontal', pad=0.05, aspect=50, extendrect=True) cbar.set_label('m $s^{-1}$') # Make some ticks and tick labels ax2.set_xticks([100, 120, 140, 160]) ax2.set_xticklabels(x_tick_labels) ax2.set_yticks(vtimes[5::24]) ax2.set_yticklabels(vtimes[5::24]) ax2.yaxis.set_major_formatter(DateFormatter('%Y')) # Set some titles plt.title('Potential intensity (m/s)', loc='left', fontsize=10)
# Set subset slice for the geographic extent of data to limit download lon_slice = slice(200, 350) lat_slice = slice(85, 10) # Grab lat/lon values (GFS will be 1D) lats = ds.lat.sel(lat=lat_slice).values lons = ds.lon.sel(lon=lon_slice).values # Grab the pressure levels and select the data to be imported # Need all pressure levels for Temperatures, U and V Wind, and Rel. Humidity # Smooth with the gaussian filter from scipy pres = ds['isobaric3'].values[:] * units('Pa') tmpk_var = ds['Temperature_isobaric'].sel(lat=lat_slice, lon=lon_slice).values[0] tmpk = mpcalc.smooth_n_point(tmpk_var, 9, 2) * units.K thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd_var = ds['u-component_of_wind_isobaric'].sel(lat=lat_slice, lon=lon_slice).values[0] vwnd_var = ds['v-component_of_wind_isobaric'].sel(lat=lat_slice, lon=lon_slice).values[0] uwnd = mpcalc.smooth_n_point(uwnd_var, 9, 2) * (units.meter / units.second) vwnd = mpcalc.smooth_n_point(vwnd_var, 9, 2) * (units.meter / units.second) # Create a clean datetime object for plotting based on time of Geopotential heights vtime = ds.time.data[0].astype('datetime64[ms]').astype('O') ###################################################################### # Use MetPy to compute the baroclinic potential vorticity on all isobaric # levels and other variables
def plot_files(dss, **args): first = True for time_sel in dss.time: data = dss.sel(time=time_sel) data['prmsl'].values = mpcalc.smooth_n_point(data['prmsl'].values, n=9, passes=10) time, run, cum_hour = get_time_run_cum(data) # Build the name of the output image filename = subfolder_images[ projection] + '/' + variable_name + '_%s.png' % cum_hour cs = args['ax'].contourf(args['x'], args['y'], data['VMAX_10M'], extend='max', cmap=args['cmap'], levels=args['levels_winds_10m']) c = args['ax'].contour(args['x'], args['y'], data['prmsl'], levels=args['levels_mslp'], colors='red', linewidths=1.) labels = args['ax'].clabel(c, c.levels, inline=True, fmt='%4.0f', fontsize=6) maxlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'max', 100, symbol='H', color='royalblue', random=True) minlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'min', 100, symbol='L', color='coral', random=True) # We need to reduce the number of points before plotting the vectors, # these values work pretty well density = 15 cv = args['ax'].quiver(args['x'][::density, ::density], args['y'][::density, ::density], data['10u'][::density, ::density], data['10v'][::density, ::density], scale=None, alpha=0.5, color='gray') an_fc = annotation_forecast(args['ax'], time) an_var = annotation(args['ax'], '10m Winds direction and max. wind gust', loc='lower left', fontsize=6) an_run = annotation_run(args['ax'], run) logo = add_logo_on_map(ax=args['ax'], zoom=0.1, pos=(0.95, 0.08)) if first: plt.colorbar(cs, orientation='horizontal', label='Wind [km/h]', pad=0.03, fraction=0.03) if debug: plt.show(block=True) else: plt.savefig(filename, **options_savefig) remove_collections([ c, cs, labels, an_fc, an_var, an_run, cv, maxlabels, minlabels, logo ]) first = False
def metpy_read_wrf_cross(fname, plevels, tidx_in, lons_out, lats_out, start, end): ds = xr.open_dataset(fname).metpy.parse_cf().squeeze() ds = ds.isel(Time=tidx) print(ds) ds1 = xr.Dataset() p = units.Quantity(to_np(ds.p), 'hPa') z = units.Quantity(to_np(ds.z), 'meter') u = units.Quantity(to_np(ds.u), 'm/s') v = units.Quantity(to_np(ds.v), 'm/s') w = units.Quantity(to_np(ds.w), 'm/s') tk = units.Quantity(to_np(ds.tk), 'kelvin') th = units.Quantity(to_np(ds.th), 'kelvin') eth = units.Quantity(to_np(ds.eth), 'kelvin') wspd = units.Quantity(to_np(ds.wspd), 'm/s') omega = units.Quantity(to_np(ds.omega), 'Pa/s') plevels_unit = plevels * units.hPa z, u, v, w, tk, th, eth, wspd, omega = log_interpolate_1d(plevels_unit, p, z, u, v, w, tk, th, eth, wspd, omega, axis=0) coords, dims = [plevs, ds.lat.values, ds.lon.values], ["level", "lat", "lon"] for name, var in zip( ['z', 'u', 'v', 'w', 'tk', 'th', 'eth', 'wspd', 'omega'], [z, u, v, w, tk, th, eth, wspd, omega]): #g = ndimage.gaussian_filter(var, sigma=3, order=0) ds1[name] = xr.DataArray(to_np(mpcalc.smooth_n_point(var, 9)), coords=coords, dims=dims) dx, dy = mpcalc.lat_lon_grid_deltas(ds.lon.values * units('degrees_E'), ds.lat.values * units('degrees_N')) # Calculate temperature advection using metpy function for i, plev in enumerate(plevs): adv = mpcalc.advection(eth[i, :, :], [u[i, :, :], v[i, :, :]], (dx, dy), dim_order='yx') * units('K/sec') adv = ndimage.gaussian_filter(adv, sigma=3, order=0) * units('K/sec') ds1['eth_adv_{:03d}'.format(plev)] = xr.DataArray( np.array(adv), coords=[ds.lat.values, ds.lon.values], dims=["lat", "lon"]) div = mpcalc.divergence(u[i, :, :], v[i, :, :], dx, dy, dim_order='yx') div = ndimage.gaussian_filter(div, sigma=3, order=0) * units('1/sec') ds1['div_{:03d}'.format(plev)] = xr.DataArray( np.array(div), coords=[ds.lat.values, ds.lon.values], dims=["lat", "lon"]) ds1['accrain'] = xr.DataArray(ds.accrain.values, coords=[ds.lat.values, ds.lon.values], dims=["lat", "lon"]) eth2 = mpcalc.equivalent_potential_temperature( ds.slp.values * units.hPa, ds.t2m.values * units('K'), ds.td2.values * units('celsius')) ds1['eth2'] = xr.DataArray(eth2, coords=[ds.lat.values, ds.lon.values], dims=["lat", "lon"]) #ds1['sst'] = xr.DataArray(ndimage.gaussian_filter(ds.sst.values, sigma=3, order=0)-273.15, coords=[ds.lat.values,ds.lon.values], dims=["lat","lon"]) ds1 = ds1.metpy.parse_cf().squeeze() cross = cross_section(ds1, start, end).set_coords(('lat', 'lon')) cross.u.attrs['units'] = 'm/s' cross.v.attrs['units'] = 'm/s' cross['t_wind'], cross['n_wind'] = mpcalc.cross_section_components( cross['u'], cross['v']) weights = np.cos(np.deg2rad(ds.lat)) ds_weighted = ds.weighted(weights) weighted = ds_weighted.mean(("lat")) return ds1, cross, weighted
def PV_Div_uv(initial_time=None, fhour=6, day_back=0,model='ECMWF', map_ratio=19/9,zoom_ratio=20,cntr_pnt=[102,34], levels=[1000, 950, 925, 900, 850, 800, 700,600,500,400,300,250,200,100],lvl_ana=250, Global=False, south_China_sea=True,area = '全国',city=False,output_dir=None ): # micaps data directory try: data_dir = [utl.Cassandra_dir(data_type='high',data_source=model,var_name='RH',lvl=''), utl.Cassandra_dir(data_type='high',data_source=model,var_name='UGRD',lvl=''), utl.Cassandra_dir(data_type='high',data_source=model,var_name='VGRD',lvl=''), utl.Cassandra_dir(data_type='high',data_source=model,var_name='TMP',lvl=''), utl.Cassandra_dir(data_type='high',data_source=model,var_name='HGT',lvl='')] except KeyError: raise ValueError('Can not find all directories needed') # get filename if(initial_time != None): filename = utl.model_filename(initial_time, fhour) else: filename=utl.filename_day_back_model(day_back=day_back,fhour=fhour) # retrieve data from micaps server rh=get_model_3D_grid(directory=data_dir[0][0:-1],filename=filename,levels=levels, allExists=False) if rh is None: return u=get_model_3D_grid(directory=data_dir[1][0:-1],filename=filename,levels=levels, allExists=False) if u is None: return v=get_model_3D_grid(directory=data_dir[2][0:-1],filename=filename,levels=levels, allExists=False) if v is None: return t=get_model_3D_grid(directory=data_dir[3][0:-1],filename=filename,levels=levels, allExists=False) if t is None: return # get filename if(initial_time != None): filename = utl.model_filename(initial_time, fhour) else: filename=utl.filename_day_back_model(day_back=day_back,fhour=fhour) lats = np.squeeze(rh['lat'].values) lons = np.squeeze(rh['lon'].values) pres = np.array(levels)*100 * units('Pa') tmpk = mpcalc.smooth_n_point(t['data'].values.squeeze(), 9, 2)*units('degC') thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd = mpcalc.smooth_n_point(u['data'].values.squeeze(), 9, 2)*units.meter/units.second vwnd = mpcalc.smooth_n_point(v['data'].values.squeeze(), 9, 2)*units.meter/units.second dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats) # Comput the PV on all isobaric surfaces pv = mpcalc.potential_vorticity_baroclinic(thta, pres[:, None, None], uwnd, vwnd, dx[None, :, :], dy[None, :, :], lats[None, :, None] * units('degrees')) div = mpcalc.divergence(uwnd, vwnd, dx[None, :, :], dy[None, :, :], dim_order='yx') # prepare data idx_z1 = list(pres.m).index(((lvl_ana * units('hPa')).to(pres.units)).m) if(area != None): cntr_pnt,zoom_ratio=utl.get_map_area(area_name=area) map_extent=[0,0,0,0] map_extent[0]=cntr_pnt[0]-zoom_ratio*1*map_ratio map_extent[1]=cntr_pnt[0]+zoom_ratio*1*map_ratio map_extent[2]=cntr_pnt[1]-zoom_ratio*1 map_extent[3]=cntr_pnt[1]+zoom_ratio*1 delt_x=(map_extent[1]-map_extent[0])*0.2 delt_y=(map_extent[3]-map_extent[2])*0.1 #+ to solve the problem of labels on all the contours idx_x1 = np.where((lons > map_extent[0]-delt_x) & (lons < map_extent[1]+delt_x)) idx_y1 = np.where((lats > map_extent[2]-delt_y) & (lats < map_extent[3]+delt_y)) #- to solve the problem of labels on all the contours init_time = u.coords['forecast_reference_time'].values pv = { 'lon': lons[idx_x1], 'lat': lats[idx_y1], 'data': np.array(pv)[idx_z1,idx_y1[0][0]:(idx_y1[0][-1]+1),idx_x1[0][0]:(idx_x1[0][-1]+1)], 'lev':str(lvl_ana), 'model':model, 'fhour':fhour, 'init_time':init_time} uv = { 'lon': lons[idx_x1], 'lat': lats[idx_y1], 'udata': np.array(uwnd)[idx_z1,idx_y1[0][0]:(idx_y1[0][-1]+1),idx_x1[0][0]:(idx_x1[0][-1]+1)], 'vdata': np.array(vwnd)[idx_z1,idx_y1[0][0]:(idx_y1[0][-1]+1),idx_x1[0][0]:(idx_x1[0][-1]+1)], 'lev':str(lvl_ana)} div = { 'lon': lons[idx_x1], 'lat': lats[idx_y1], 'data': np.array(div)[idx_z1,idx_y1[0][0]:(idx_y1[0][-1]+1),idx_x1[0][0]:(idx_x1[0][-1]+1)], 'lev':str(lvl_ana)} synoptic_graphics.draw_PV_Div_uv( pv=pv, uv=uv, div=div, map_extent=map_extent, regrid_shape=20, city=city,south_China_sea=south_China_sea, output_dir=output_dir,Global=Global)
def PV_Div_uv(initTime=None, fhour=6, day_back=0, model='ECMWF', map_ratio=14 / 9, zoom_ratio=20, cntr_pnt=[104, 34], levels=[ 1000, 950, 925, 900, 850, 800, 700, 600, 500, 400, 300, 250, 200, 100 ], lvl_ana=250, Global=False, south_China_sea=True, area=None, city=False, output_dir=None, data_source='MICAPS', **kwargs): # micaps data directory if (area != None): south_China_sea = False # micaps data directory if (data_source == 'MICAPS'): try: data_dir = [ utl.Cassandra_dir(data_type='high', data_source=model, var_name='RH', lvl=''), utl.Cassandra_dir(data_type='high', data_source=model, var_name='UGRD', lvl=''), utl.Cassandra_dir(data_type='high', data_source=model, var_name='VGRD', lvl=''), utl.Cassandra_dir(data_type='high', data_source=model, var_name='TMP', lvl=''), utl.Cassandra_dir(data_type='high', data_source=model, var_name='HGT', lvl='') ] except KeyError: raise ValueError('Can not find all directories needed') # get filename if (initTime != None): filename = utl.model_filename(initTime, fhour) else: filename = utl.filename_day_back_model(day_back=day_back, fhour=fhour) # retrieve data from micaps server rh = MICAPS_IO.get_model_3D_grid(directory=data_dir[0][0:-1], filename=filename, levels=levels, allExists=False) if rh is None: return u = MICAPS_IO.get_model_3D_grid(directory=data_dir[1][0:-1], filename=filename, levels=levels, allExists=False) if u is None: return v = MICAPS_IO.get_model_3D_grid(directory=data_dir[2][0:-1], filename=filename, levels=levels, allExists=False) if v is None: return t = MICAPS_IO.get_model_3D_grid(directory=data_dir[3][0:-1], filename=filename, levels=levels, allExists=False) if t is None: return if (data_source == 'CIMISS'): # get filename if (initTime != None): filename = utl.model_filename(initTime, fhour, UTC=True) else: filename = utl.filename_day_back_model(day_back=day_back, fhour=fhour, UTC=True) try: # retrieve data from CIMISS server rh = CMISS_IO.cimiss_model_3D_grid( data_code=utl.CMISS_data_code(data_source=model, var_name='RHU'), init_time_str='20' + filename[0:8], valid_time=fhour, fcst_levels=levels, fcst_ele="RHU", units='%') if rh is None: return u = CMISS_IO.cimiss_model_3D_grid( data_code=utl.CMISS_data_code(data_source=model, var_name='WIU'), init_time_str='20' + filename[0:8], valid_time=fhour, fcst_levels=levels, fcst_ele="WIU", units='m/s') if u is None: return v = CMISS_IO.cimiss_model_3D_grid( data_code=utl.CMISS_data_code(data_source=model, var_name='WIV'), init_time_str='20' + filename[0:8], valid_time=fhour, fcst_levels=levels, fcst_ele="WIV", units='m/s') if v is None: return t = CMISS_IO.cimiss_model_3D_grid( data_code=utl.CMISS_data_code(data_source=model, var_name='TEM'), init_time_str='20' + filename[0:8], valid_time=fhour, fcst_levels=levels, fcst_ele="TEM", units='K') if t is None: return t['data'].values = t['data'].values - 273.15 t['data'].attrs['units'] = 'C' except KeyError: raise ValueError('Can not find all data needed') if (area != None): cntr_pnt, zoom_ratio = utl.get_map_area(area_name=area) map_extent = [0, 0, 0, 0] map_extent[0] = cntr_pnt[0] - zoom_ratio * 1 * map_ratio map_extent[1] = cntr_pnt[0] + zoom_ratio * 1 * map_ratio map_extent[2] = cntr_pnt[1] - zoom_ratio * 1 map_extent[3] = cntr_pnt[1] + zoom_ratio * 1 delt_x = (map_extent[1] - map_extent[0]) * 0.2 delt_y = (map_extent[3] - map_extent[2]) * 0.1 #+ to solve the problem of labels on all the contours mask1 = (rh['lon'] > map_extent[0] - delt_x) & (rh['lon'] < map_extent[1] + delt_x) & ( rh['lat'] > map_extent[2] - delt_y) & (rh['lat'] < map_extent[3] + delt_y) mask2 = (u['lon'] > map_extent[0] - delt_x) & (u['lon'] < map_extent[1] + delt_x) & ( u['lat'] > map_extent[2] - delt_y) & (u['lat'] < map_extent[3] + delt_y) mask3 = (t['lon'] > map_extent[0] - delt_x) & (t['lon'] < map_extent[1] + delt_x) & ( t['lat'] > map_extent[2] - delt_y) & (t['lat'] < map_extent[3] + delt_y) #- to solve the problem of labels on all the contours rh = rh.where(mask1, drop=True) u = u.where(mask2, drop=True) v = v.where(mask2, drop=True) t = t.where(mask3, drop=True) uv = xr.merge([u.rename({'data': 'u'}), v.rename({'data': 'v'})]) lats = np.squeeze(rh['lat'].values) lons = np.squeeze(rh['lon'].values) pres = np.array(levels) * 100 * units('Pa') tmpk = mpcalc.smooth_n_point( (t['data'].values.squeeze() + 273.15), 9, 2) * units('kelvin') thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd = mpcalc.smooth_n_point(u['data'].values.squeeze(), 9, 2) * units.meter / units.second vwnd = mpcalc.smooth_n_point(v['data'].values.squeeze(), 9, 2) * units.meter / units.second dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats) # Comput the PV on all isobaric surfaces pv_raw = mpcalc.potential_vorticity_baroclinic( thta, pres[:, None, None], uwnd, vwnd, dx[None, :, :], dy[None, :, :], lats[None, :, None] * units('degrees')) div_raw = mpcalc.divergence(uwnd, vwnd, dx[None, :, :], dy[None, :, :], dim_order='yx') # prepare data idx_z1 = list(pres.m).index(((lvl_ana * units('hPa')).to(pres.units)).m) pv = rh.copy(deep=True) pv['data'].values = np.array(pv_raw).reshape( np.append(1, np.array(pv_raw).shape)) pv['data'].attrs['units'] = str(pv_raw.units) pv.attrs['model'] = model pv = pv.where(pv['level'] == lvl_ana, drop=True) div = u.copy(deep=True) div['data'].values = np.array(div_raw).reshape( np.append(1, np.array(div_raw).shape)) div['data'].attrs['units'] = str(div_raw.units) div = div.where(div['level'] == lvl_ana, drop=True) uv = uv.where(uv['level'] == lvl_ana, drop=True) synoptic_graphics.draw_PV_Div_uv(pv=pv, uv=uv, div=div, map_extent=map_extent, regrid_shape=20, city=city, south_China_sea=south_China_sea, output_dir=output_dir, Global=Global)
# for over North America. Data are smoothed for aesthetic reasons. # # Get GFS data and subset to North America for Geopotential Height and Temperature ds = xr.open_dataset( 'https://thredds.ucar.edu/thredds/dodsC/grib/NCEP/GFS/Global_0p5deg_ana/' 'GFS_Global_0p5deg_ana_{0:%Y%m%d}_{0:%H}00.grib2'.format( date)).metpy.parse_cf() # Geopotential height and smooth hght = ds.Geopotential_height_isobaric.metpy.sel(vertical=level * units.hPa, time=date, lat=slice(70, 15), lon=slice( 360 - 145, 360 - 50)) smooth_hght = mpcalc.smooth_n_point(hght, 9, 10) # Temperature, smooth, and convert to Celsius tmpk = ds.Temperature_isobaric.metpy.sel(vertical=level * units.hPa, time=date, lat=slice(70, 15), lon=slice(360 - 145, 360 - 50)) smooth_tmpc = (mpcalc.smooth_n_point(tmpk, 9, 10)).to('degC') ###################################################################### # Create DIFAX Replication # ------------------------ # # Plot the observational data and contours on a Lambert Conformal map and # add features that resemble the historic DIFAX maps. #
def uaPlot(data, level, date, save_dir, ds, td_option): custom_layout = StationPlotLayout() custom_layout.add_barb('eastward_wind', 'northward_wind', units='knots') custom_layout.add_value('NW', 'air_temperature', fmt='.0f', units='degC', color='darkred') # Geopotential height and smooth hght = ds.Geopotential_height_isobaric.metpy.sel( vertical=level * units.hPa, time=date, lat=slice(85, 15), lon=slice(360 - 200, 360 - 10)) smooth_hght = mpcalc.smooth_n_point(hght, 9, 10) # Temperature, smooth, and convert to Celsius tmpk = ds.Temperature_isobaric.metpy.sel(vertical=level * units.hPa, time=date, lat=slice(85, 15), lon=slice(360 - 200, 360 - 10)) smooth_tmpc = (mpcalc.smooth_n_point(tmpk, 9, 10)).to('degC') #Calculate Theta-e rh = ds.Relative_humidity_isobaric.metpy.sel(vertical=level * units.hPa, time=date, lat=slice(85, 15), lon=slice( 360 - 200, 360 - 10)) td = mpcalc.dewpoint_from_relative_humidity(tmpk, rh) te = mpcalc.equivalent_potential_temperature(level * units.hPa, tmpk, td) smooth_te = mpcalc.smooth_n_point(te, 9, 10) #decide on the height format based on the level if level == 250: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[1:4], units='m', color='black') cint = 120 tint = 5 if level == 300: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[1:4], units='m', color='black') cint = 120 tint = 5 if level == 500: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[0:3], units='m', color='black') cint = 60 tint = 5 if level == 700: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[1:4], units='m', color='black') custom_layout.add_value('SW', 'tdd', units='degC', color='green') custom_layout.add_value('SE', 'thetae', units='degK', color='orange') temps = 'Tdd, and Theta-e' cint = 30 tint = 4 if level == 850: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[1:4], units='m', color='black') if td_option == True: custom_layout.add_value('SW', 'dew_point_temperature', units='degC', color='green') temps = 'Td, and Theta-e' if td_option == False: custom_layout.add_value('SW', 'tdd', units='degC', color='green') temps = 'Tdd, and Theta-e' # custom_layout.add_value('SW', 'tdd', units='degC', color='green') # temps = 'Tdd, and Theta-e' custom_layout.add_value('SE', 'thetae', units='degK', color='orange') cint = 30 tint = 4 if level == 925: custom_layout.add_value('NE', 'height', fmt=lambda v: format(v, '1')[1:4], units='m', color='black') if td_option == True: custom_layout.add_value('SW', 'dew_point_temperature', units='degC', color='green') temps = 'Td, and Theta-e' if td_option == False: custom_layout.add_value('SW', 'tdd', units='degC', color='green') temps = 'Tdd, and Theta-e' custom_layout.add_value('SE', 'thetae', units='degK', color='orange') cint = 30 tint = 4 globe = ccrs.Globe(ellipse='sphere', semimajor_axis=6371200., semiminor_axis=6371200.) proj = ccrs.Stereographic(central_longitude=-105., central_latitude=90., globe=globe, true_scale_latitude=60) # Plot the image fig = plt.figure(figsize=(40, 40)) ax = fig.add_subplot(1, 1, 1, projection=proj) state_boundaries = feat.NaturalEarthFeature( category='cultural', name='admin_1_states_provinces_lines', scale='10m', facecolor='none') coastlines = feat.NaturalEarthFeature('physical', 'coastline', '50m', facecolor='none') lakes = feat.NaturalEarthFeature('physical', 'lakes', '50m', facecolor='none') countries = feat.NaturalEarthFeature('cultural', 'admin_0_countries', '50m', facecolor='none') ax.add_feature(state_boundaries, zorder=2, edgecolor='grey') ax.add_feature(lakes, zorder=2, edgecolor='grey') ax.add_feature(coastlines, zorder=2, edgecolor='grey') ax.add_feature(lakes, zorder=2, edgecolor='grey') ax.add_feature(countries, zorder=2, edgecolor='grey') ax.coastlines(resolution='50m', zorder=2, color='grey') ax.set_extent([-132., -70, 26., 80.], ccrs.PlateCarree()) stationData = dataDict(data) stationplot = StationPlot(ax, stationData['longitude'], stationData['latitude'], transform=ccrs.PlateCarree(), fontsize=22) custom_layout.plot(stationplot, stationData) # Plot Solid Contours of Geopotential Height cs = ax.contour(hght.lon, hght.lat, smooth_hght.m, range(0, 20000, cint), colors='black', transform=ccrs.PlateCarree()) clabels = plt.clabel(cs, fmt='%d', colors='white', inline_spacing=5, use_clabeltext=True, fontsize=22) # Contour labels with black boxes and white text for t in clabels: t.set_bbox({'facecolor': 'black', 'pad': 4}) t.set_fontweight('heavy') #Check levels for different contours if level == 250 or level == 300 or level == 500: # Plot Dashed Contours of Temperature cs2 = ax.contour(hght.lon, hght.lat, smooth_tmpc.m, range(-60, 51, tint), colors='red', transform=ccrs.PlateCarree()) clabels = plt.clabel(cs2, fmt='%d', colors='red', inline_spacing=5, use_clabeltext=True, fontsize=22) # Set longer dashes than default for c in cs2.collections: c.set_dashes([(0, (5.0, 3.0))]) temps = 'T' if level == 700 or level == 850 or level == 925: # Plot Dashed Contours of Temperature cs2 = ax.contour(hght.lon, hght.lat, smooth_te.m, range(210, 360, tint), colors='orange', transform=ccrs.PlateCarree()) clabels = plt.clabel(cs2, fmt='%d', colors='orange', inline_spacing=5, use_clabeltext=True, fontsize=22) # Set longer dashes than default for c in cs2.collections: c.set_dashes([(0, (5.0, 3.0))]) dpi = plt.rcParams['savefig.dpi'] = 255 date = date + timedelta(hours=6) text = AnchoredText(str(level) + 'mb Wind, Heights, and ' + temps + ' Valid: {0:%Y-%m-%d} {0:%H}:00UTC'.format(date), loc=3, frameon=True, prop=dict(fontsize=22)) ax.add_artist(text) plt.tight_layout() save_fname = '{0:%Y%m%d_%H}z_'.format(date) + str(level) + 'mb.pdf' plt.savefig(save_dir / save_fname, dpi=dpi, bbox_inches='tight')
lats = ds.lat.data lons = ds.lon.data # Grab x, y data and make 2D for wind component plotting # because u- and v-components are grid relative x = ds['u-component_of_wind_isobaric'].x y = ds['u-component_of_wind_isobaric'].y xx, yy = np.meshgrid(x, y) # Grab Cartopy CRS from metadata for plotting wind barbs datacrs = ds['u-component_of_wind_isobaric'].metpy.cartopy_crs # Select and grab 500-hPa geopotential heights and wind components, smooth with gaussian_filter level = 500 * units.hPa hght_500 = mpcalc.smooth_n_point( ds.Geopotential_height_isobaric.metpy.sel(vertical=level).squeeze(), 9, 50) uwnd_500 = mpcalc.smooth_n_point( ds['u-component_of_wind_isobaric'].metpy.sel(vertical=level).squeeze(), 9, 50) vwnd_500 = mpcalc.smooth_n_point( ds['v-component_of_wind_isobaric'].metpy.sel(vertical=level).squeeze(), 9, 50) # Compute north-relative wind components for plotting purposes uwnd_er, vwnd_er = earth_relative_wind_components( ds['u-component_of_wind_isobaric'], uwnd_500, vwnd_500) # Create a clean datetime object for plotting based on time of Geopotential heights vtime = ds.time.data[0].astype('datetime64[ms]').astype('O') ######################################################################
def plot_files(dss, **args): first = True for time_sel in dss.time: data = dss.sel(time=time_sel) data['prmsl'].values = mpcalc.smooth_n_point(data['prmsl'].values, n=9, passes=10) time, run, cum_hour = get_time_run_cum(data) # Build the name of the output image filename = subfolder_images[ projection] + '/' + variable_name + '_%s.png' % cum_hour cs_rain = args['ax'].contourf(args['x'], args['y'], data['rain_rate'], extend='max', cmap=args['cmap_rain'], norm=args['norm_rain'], levels=args['levels_rain'], zorder=4, antialiased=True) cs_snow = args['ax'].contourf(args['x'], args['y'], data['snow_rate'], extend='max', cmap=args['cmap_snow'], norm=args['norm_snow'], levels=args['levels_snow'], zorder=5) cs_clouds_low = args['ax'].contourf(args['x'], args['y'], data['CLCL'], extend='max', cmap=args['cmap_clouds'], levels=args['levels_clouds'], zorder=3) cs_clouds_high = args['ax'].contourf(args['x'], args['y'], data['CLCH'], extend='max', cmap=args['cmap_clouds_high'], levels=args['levels_clouds'], zorder=2, alpha=0.5, antialiased=True) c = args['ax'].contour(args['x'], args['y'], data['prmsl'], levels=args['levels_mslp'], colors='whitesmoke', linewidths=1., zorder=7, alpha=1.0) labels = args['ax'].clabel(c, c.levels, inline=True, fmt='%4.0f', fontsize=6) maxlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'max', 150, symbol='H', color='royalblue', random=True) minlabels = plot_maxmin_points(args['ax'], args['x'], args['y'], data['prmsl'], 'min', 150, symbol='L', color='coral', random=True) an_fc = annotation_forecast(args['ax'], time) an_var = annotation( args['ax'], 'Clouds (grey-low, orange-high), rain, snow and MSLP', loc='lower left', fontsize=6) an_run = annotation_run(args['ax'], run) logo = add_logo_on_map(ax=args['ax'], zoom=0.1, pos=(0.95, 0.08)) if first: if projection == "it": x_cbar_0, y_cbar_0, x_cbar_size, y_cbar_size = 0.15, 0.2, 0.35, 0.02 x_cbar2_0, y_cbar2_0, x_cbar2_size, y_cbar2_size = 0.55, 0.2, 0.35, 0.02 elif projection == "de": x_cbar_0, y_cbar_0, x_cbar_size, y_cbar_size = 0.17, 0.06, 0.32, 0.02 x_cbar2_0, y_cbar2_0, x_cbar2_size, y_cbar2_size = 0.55, 0.06, 0.32, 0.02 elif projection == "nord": x_cbar_0, y_cbar_0, x_cbar_size, y_cbar_size = 0.15, 0.09, 0.35, 0.02 x_cbar2_0, y_cbar2_0, x_cbar2_size, y_cbar2_size = 0.55, 0.09, 0.35, 0.02 ax_cbar = plt.gcf().add_axes( [x_cbar_0, y_cbar_0, x_cbar_size, y_cbar_size]) ax_cbar_2 = plt.gcf().add_axes( [x_cbar2_0, y_cbar2_0, x_cbar2_size, y_cbar2_size]) cbar_snow = plt.gcf().colorbar(cs_snow, cax=ax_cbar, orientation='horizontal', label='Snow [cm/hr]') cbar_rain = plt.gcf().colorbar(cs_rain, cax=ax_cbar_2, orientation='horizontal', label='Rain [mm/hr]') cbar_snow.ax.tick_params(labelsize=8) cbar_rain.ax.tick_params(labelsize=8) if debug: plt.show(block=True) else: plt.savefig(filename, **options_savefig) remove_collections([ c, cs_rain, cs_snow, cs_clouds_low, cs_clouds_high, labels, an_fc, an_var, an_run, maxlabels, minlabels, logo ]) first = False
# # Open dataset using xarray ds = xr.open_dataset('https://thredds.ucar.edu/thredds/dodsC/' 'casestudies/python-gallery/NARR_19930313_1800.nc') # Get lat/lon data from file lats = ds.lat.data lons = ds.lon.data # Calculate variable dx, dy values for use in calculations dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats) # Get 700-hPa data and smooth hght_700 = mpcalc.smooth_n_point( ds['Geopotential_height_isobaric'].sel(isobaric1=700).data[0], 9) * units.meter tmpk_700 = mpcalc.smooth_n_point( ds['Temperature_isobaric'].sel(isobaric1=700).data[0], 9) * units.kelvin uwnd_700 = mpcalc.smooth_n_point( ds['u-component_of_wind_isobaric'].sel(isobaric1=700).data[0], 9) * (units.meter / units.seconds) vwnd_700 = mpcalc.smooth_n_point( ds['v-component_of_wind_isobaric'].sel(isobaric1=700).data[0], 9) * (units.meter / units.seconds) # Get 300-hPa data and smooth hght_300 = mpcalc.smooth_n_point( ds['Geopotential_height_isobaric'].sel(isobaric1=300).data[0], 9) * units.meter tmpk_300 = mpcalc.smooth_n_point(
color='black', linewidths=0.5) # Set some titles plt.title('Hovmoller Diagram', loc='left') plt.title('NCEP/NCAR Reanalysis', loc='right') # Bottom plot for Hovmoller diagram ax2 = fig.add_subplot(gs[1, 0]) ax2.invert_yaxis() # Reverse the time order to do oldest first # Plot of chosen variable averaged over latitude and slightly smoothed clevs = np.arange(-50, 51, 5) cf = ax2.contourf(lons, vtimes, mpcalc.smooth_n_point(avg_data, 9, 2), clevs, cmap=plt.cm.bwr, extend='both') cs = ax2.contour(lons, vtimes, mpcalc.smooth_n_point(avg_data, 9, 2), clevs, colors='k', linewidths=1) cbar = plt.colorbar(cf, orientation='horizontal', pad=0.04, aspect=50, extendrect=True) cbar.set_label('m $s^{-1}$')
for i, j in product(range(3), range(3)): ax[i, j].axis('off') # Gaussian Smoother ax[0, 0].imshow(mpcalc.smooth_gaussian(raw_data, 3), vmin=0, vmax=1) ax[0, 0].set_title('Gaussian - Low Degree') ax[0, 1].imshow(mpcalc.smooth_gaussian(raw_data, 8), vmin=0, vmax=1) ax[0, 1].set_title('Gaussian - High Degree') # Rectangular Smoother ax[0, 2].imshow(mpcalc.smooth_rectangular(raw_data, (3, 7), 2), vmin=0, vmax=1) ax[0, 2].set_title('Rectangular - 3x7 Window\n2 Passes') # 5-point smoother ax[1, 0].imshow(mpcalc.smooth_n_point(raw_data, 5, 1), vmin=0, vmax=1) ax[1, 0].set_title('5-Point - 1 Pass') ax[1, 1].imshow(mpcalc.smooth_n_point(raw_data, 5, 4), vmin=0, vmax=1) ax[1, 1].set_title('5-Point - 4 Passes') # Circular Smoother ax[1, 2].imshow(mpcalc.smooth_circular(raw_data, 2, 2), vmin=0, vmax=1) ax[1, 2].set_title('Circular - Radius 2\n2 Passes') # 9-point smoother ax[2, 0].imshow(mpcalc.smooth_n_point(raw_data, 9, 1), vmin=0, vmax=1) ax[2, 0].set_title('9-Point - 1 Pass') ax[2, 1].imshow(mpcalc.smooth_n_point(raw_data, 9, 4), vmin=0, vmax=1) ax[2, 1].set_title('9-Point - 4 Passes')
# Data Retrieval # -------------- # # This code retrieves the necessary data from the file and completes some # smoothing of the geopotential height and wind fields using the SciPy # function gaussian_filter. A nicely formated valid time (vtime) variable # is also created. # # Grab lat/lon values (NAM will be 2D) lats = ds.lat.data lons = ds.lon.data # Select and grab 500-hPa geopotential heights and wind components, # smooth with gaussian_filter hght_500 = mpcalc.smooth_n_point( ds.Geopotential_height_isobaric.sel(isobaric=500).data[0], 9, 50) uwnd_500 = mpcalc.smooth_n_point( ds['u-component_of_wind_isobaric'].sel(isobaric=500).data[0], 9, 50) * units('m/s') vwnd_500 = mpcalc.smooth_n_point( ds['v-component_of_wind_isobaric'].sel(isobaric=500).data[0], 9, 50) * units('m/s') # Create a clean datetime object for plotting based on time of Geopotential heights vtime = datetime.strptime(str(ds.time.data[0].astype('datetime64[ms]')), '%Y-%m-%dT%H:%M:%S.%f') ###################################################################### # MetPy Absolute Vorticity Calculation # ------------------------------------ #
def __init__(self, datea, fhr, atcf, config): # Forecast fields to compute wnd_lev_1 = [250, 500] wnd_lev_2 = [350, 500] n_wnd_lev = len(wnd_lev_1) # Read steering flow parameters, or use defaults steerp1 = float(config['fields'].get('steer_level1', '300')) steerp2 = float(config['fields'].get('steer_level2', '850')) tcradius = float(config['fields'].get('steer_radius', '333')) # lat_lon info lat1 = float(config['fields'].get('min_lat', '0.')) lat2 = float(config['fields'].get('max_lat', '65.')) lon1 = float(config['fields'].get('min_lon', '-180.')) lon2 = float(config['fields'].get('max_lon', '-10.')) if not 'min_lat' in config: config.update({'min_lat': lat1}) config.update({'max_lat': lat2}) config.update({'min_lon': lon1}) config.update({'max_lon': lon2}) self.fhr = fhr self.atcf_files = atcf.atcf_files self.config = config self.nens = int(len(self.atcf_files)) df_files = {} self.datea_str = datea self.datea = dt.datetime.strptime(datea, '%Y%m%d%H') self.datea_s = self.datea.strftime("%m%d%H%M") self.fff = str(self.fhr + 1000)[1:] datea_1 = self.datea + dt.timedelta(hours=self.fhr) datea_1 = datea_1.strftime("%m%d%H%M") self.dpp = importlib.import_module(config['io_module']) logging.warning("Computing hour {0} ensemble fields".format(self.fff)) # Obtain the ensemble lat/lon information, replace missing values with mean self.ens_lat, self.ens_lon = atcf.ens_lat_lon_time(self.fhr) e_cnt = 0 m_lat = 0.0 m_lon = 0.0 for n in range(self.nens): if self.ens_lat[n] != atcf.missing and self.ens_lon[ n] != atcf.missing: e_cnt = e_cnt + 1 m_lat = m_lat + self.ens_lat[n] m_lon = m_lon + self.ens_lon[n] if e_cnt > 0: m_lon = m_lon / e_cnt m_lat = m_lat / e_cnt for n in range(self.nens): if self.ens_lat[n] == atcf.missing or self.ens_lon[ n] == atcf.missing: self.ens_lat[n] = m_lat self.ens_lon[n] = m_lon # Read grib file information for this forecast hour g1 = self.dpp.ReadGribFiles(self.datea_str, self.fhr, self.config) dencode = { 'ensemble_data': { 'dtype': 'float32' }, 'latitude': { 'dtype': 'float32' }, 'longitude': { 'dtype': 'float32' }, 'ensemble': { 'dtype': 'int32' } } # Compute steering wind components uoutfile = '{0}/{1}_f{2}_usteer_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) voutfile = '{0}/{1}_f{2}_vsteer_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(uoutfile) or not os.path.isfile(voutfile)) and config['fields'].get( 'calc_uvsteer', 'True') == 'True': logging.warning(" Computing steering wind information") inpDict = {'isobaricInhPa': (steerp1, steerp2)} inpDict = g1.set_var_bounds('zonal_wind', inpDict) # Create output arrays outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'zonal steering wind', 'units': 'm/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('zonal_wind', outDict) uensmat = g1.create_ens_array('zonal_wind', self.nens, outDict) outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'meridional steering wind', 'units': 'm/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('meridional_wind', outDict) vensmat = g1.create_ens_array('meridional_wind', self.nens, outDict) outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'steering wind vorticity', 'units': '1/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('zonal_wind', outDict) vortmat = g1.create_ens_array('zonal_wind', self.nens, outDict) wencode = { 'latitude': { 'dtype': 'float32' }, 'longitude': { 'dtype': 'float32' } } for n in range(self.nens): # Read global zonal and meridional wind, write to file uwnd = g1.read_grib_field('zonal_wind', n, inpDict).rename('u') vwnd = g1.read_grib_field('meridional_wind', n, inpDict).rename('v') # print(uwnd[:,0,0]) # print(vwnd[:,0,0]) # sys.exit(2) uwnd.to_netcdf('wind_info.nc', mode='w', encoding=wencode, format='NETCDF3_CLASSIC') vwnd.to_netcdf('wind_info.nc', mode='a', encoding=wencode, format='NETCDF3_CLASSIC') latvec = uwnd.latitude.values lonvec = uwnd.longitude.values if e_cnt > 0: latcen = latvec[np.abs(latvec - self.ens_lat[n]).argmin()] loncen = lonvec[np.abs(lonvec - self.ens_lon[n]).argmin()] # Call NCL to remove TC winds, read result from file os.system('ncl -Q {0}/tc_steer.ncl tclat={1} tclon={2} tcradius={3}'.format(config['script_dir'],\ str(latcen), str(loncen), str(tcradius))) wfile = nc.Dataset('wind_info.nc') uwnd[:, :, :] = wfile.variables['u'][:, :, :] vwnd[:, :, :] = wfile.variables['v'][:, :, :] os.remove('wind_info.nc') # Integrate the winds over the layer to obtain the steering wind pres, lat, lon = uwnd.indexes.values() nlev = len(pres) uint = uwnd[0, :, :] uint[:, :] = 0.0 vint = vwnd[0, :, :] vint[:, :] = 0.0 for k in range(nlev - 1): uint[:, :] = uint[:, :] + 0.5 * (uwnd[k, :, :] + uwnd[ k + 1, :, :]) * abs(pres[k + 1] - pres[k]) vint[:, :] = vint[:, :] + 0.5 * (vwnd[k, :, :] + vwnd[ k + 1, :, :]) * abs(pres[k + 1] - pres[k]) # if pres[0] > pres[-1]: # uint = -np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # vint = -np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # else: # uint = np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # vint = np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) if lat[0] > lat[-1]: slat1 = lat2 slat2 = lat1 else: slat1 = lat1 slat2 = lat2 # Write steering flow to ensemble arrays uensmat[n, :, :] = np.squeeze( uint.sel(latitude=slice(slat1, slat2), longitude=slice(lon1, lon2))) / abs(pres[-1] - pres[0]) vensmat[n, :, :] = np.squeeze( vint.sel(latitude=slice(slat1, slat2), longitude=slice(lon1, lon2))) / abs(pres[-1] - pres[0]) # Compute the vorticity associated with the steering wind # circ = VectorWind(unew, vnew).vorticity() * 1.0e5 # vortmat[n,:,:] = np.squeeze(circ.sel(latitude=slice(lat2, lat1), longitude=slice(lon1, lon2))) uensmat.to_netcdf(uoutfile, encoding=dencode) vensmat.to_netcdf(voutfile, encoding=dencode) # vortmat.to_netcdf(vortfile, encoding=dencode) else: logging.warning(" Obtaining steering wind information from file") # Read geopotential height from file, if ensemble file is not present if config['fields'].get('calc_height', 'True') == 'True': if 'height_levels' in config['fields']: height_list = json.loads(config['fields'].get('height_levels')) else: height_list = [500] for level in height_list: levstr = '%0.3i' % int(level) outfile = '{0}/{1}_f{2}_h{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if not os.path.isfile(outfile): logging.warning( ' Computing {0} hPa height'.format(levstr)) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa height'.format(levstr), 'units': 'm', '_FillValue': -9999. } vDict = g1.set_var_bounds('geopotential_height', vDict) ensmat = g1.create_ens_array('geopotential_height', g1.nens, vDict) for n in range(g1.nens): ensmat[n, :, :] = np.squeeze( g1.read_grib_field('geopotential_height', n, vDict)) ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa height data from {1}".format( levstr, outfile)) # Compute 250 hPa PV if the file does not exist outfile = '{0}/{1}_f{2}_pv250_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(outfile) and config['fields'].get('calc_pv250hPa', 'True') == 'True'): logging.warning(" Computing 250 hPa PV") vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (200, 300), 'description': '250 hPa Potential Vorticity', 'units': 'PVU', '_FillValue': -9999. } vDict = g1.set_var_bounds('zonal_wind', vDict) ensmat = g1.create_ens_array('zonal_wind', self.nens, vDict) for n in range(self.nens): # Read all the necessary files from file, smooth fields, so sensitivities are useful tmpk = g1.read_grib_field('temperature', n, vDict) * units('K') lats = tmpk.latitude.values * units('degrees') lons = tmpk.longitude.values * units('degrees') pres = tmpk.isobaricInhPa.values * units('hPa') tmpk = mpcalc.smooth_n_point(tmpk, 9, 4) thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd = mpcalc.smooth_n_point( g1.read_grib_field('zonal_wind', n, vDict) * units('m/s'), 9, 4) vwnd = mpcalc.smooth_n_point( g1.read_grib_field('meridional_wind', n, vDict) * units('m/s'), 9, 4) dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats, x_dim=-1, y_dim=-2, geod=None) # Compute PV and place in ensemble array pvout = mpcalc.potential_vorticity_baroclinic( thta, pres[:, None, None], uwnd, vwnd, dx[None, :, :], dy[None, :, :], lats[None, :, None]) ensmat[n, :, :] = np.squeeze(pvout[np.where( pres == 250 * units('hPa'))[0], :, :]) * 1.0e6 ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining 250 hPa PV data from {0}".format(outfile)) # Compute the equivalent potential temperature (if desired and file is missing) if config['fields'].get('calc_theta-e', 'False') == 'True': if 'theta-e_levels' in config['fields']: thetae_list = json.loads( config['fields'].get('theta-e_levels')) else: thetae_list = [700, 850] for level in thetae_list: levstr = '%0.3i' % int(level) outfile = '{0}/{1}_f{2}_e{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if not os.path.isfile(outfile): logging.warning( ' Computing {0} hPa Theta-E'.format(levstr)) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa Equivalent Potential Temperature'.format( levstr), 'units': 'K', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) ensmat = g1.create_ens_array('temperature', g1.nens, vDict) for n in range(g1.nens): tmpk = g1.read_grib_field('temperature', n, vDict) * units.K pres = tmpk.isobaricInhPa.values * units.hPa if g1.has_specific_humidity: qvap = np.squeeze( g1.read_grib_field('specific_humidity', n, vDict)) tdew = mpcalc.dewpoint_from_specific_humidity( pres[None, None], tmpk, qvap) else: relh = g1.read_grib_field('relative_humidity', n, vDict) relh = np.minimum(np.maximum(relh, 0.01), 100.0) * units.percent tdew = mpcalc.dewpoint_from_relative_humidity( tmpk, relh) ensmat[n, :, :] = np.squeeze( mpcalc.equivalent_potential_temperature( pres[None, None], tmpk, tdew)) ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa Theta-e data from {1}".format( levstr, outfile)) # Compute the 500-850 hPa water vapor mixing ratio (if desired and file is missing) outfile = '{0}/{1}_f{2}_q500-850hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(outfile) and config['fields'].get( 'calc_q500-850hPa', 'False') == 'True'): logging.warning(" Computing 500-850 hPa Water Vapor") vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': '500-850 hPa Integrated Water Vapor', 'units': 'hPa', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) ensmat = g1.create_ens_array('temperature', len(self.atcf_files), vDict) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (500, 850), 'description': '500-850 hPa Integrated Water Vapor', 'units': 'hPa', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) for n in range(self.nens): tmpk = np.squeeze(g1.read_grib_field('temperature', n, vDict)) * units('K') pres = (tmpk.isobaricInhPa.values * units.hPa).to(units.Pa) if g1.has_specific_humidity: qvap = mpcalc.mixing_ratio_from_specific_humidity( g1.read_grib_field('specific_humidity', n, vDict)) else: relh = np.minimum( np.maximum( g1.read_grib_field('relative_humidity', n, vDict), 0.01), 100.0) * units('percent') qvap = mpcalc.mixing_ratio_from_relative_humidity( pres[:, None, None], tmpk, relh) # Integrate water vapor over the pressure levels ensmat[n, :, :] = np.abs(np.trapz( qvap, pres, axis=0)) / mpcon.earth_gravity ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining 500-850 hPa water vapor data from {0}".format( outfile)) # Compute wind-related forecast fields (if desired and file is missing) if config['fields'].get('calc_winds', 'False') == 'True': if 'wind_levels' in config['fields']: wind_list = json.loads(config['fields'].get('wind_levels')) else: wind_list = [850] for level in wind_list: levstr = '%0.3i' % int(level) ufile = '{0}/{1}_f{2}_u{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) vfile = '{0}/{1}_f{2}_v{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if (not os.path.isfile(ufile)) or (not os.path.isfile(vfile)): logging.warning( ' Computing {0} hPa wind information'.format(levstr)) uDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa zonal wind'.format(levstr), 'units': 'm/s', '_FillValue': -9999. } uDict = g1.set_var_bounds('zonal_wind', uDict) uensmat = g1.create_ens_array('zonal_wind', g1.nens, uDict) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa meridional wind'.format(levstr), 'units': 'm/s', '_FillValue': -9999. } vDict = g1.set_var_bounds('meridional_wind', vDict) vensmat = g1.create_ens_array('meridional_wind', g1.nens, vDict) for n in range(g1.nens): uwnd = g1.read_grib_field('zonal_wind', n, uDict).squeeze() vwnd = g1.read_grib_field('meridional_wind', n, vDict).squeeze() uensmat[n, :, :] = uwnd[:, :] vensmat[n, :, :] = vwnd[:, :] uensmat.to_netcdf(ufile, encoding=dencode) vensmat.to_netcdf(vfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa wind information from file". format(levstr))
# Plot colorfilled RH >= 70% clevs_relh = np.arange(70, 101, 1) cf = ax.contourf(lons, lats, isentrelh[ilev], clevs_relh, cmap=plt.cm.Greens, norm=plt.Normalize(70, 110), transform=datacrs) plt.colorbar(cf, orientation='horizontal', pad=0, aspect=50) # Plot isobars every 50 hPa clevs_pres = np.arange(0, 1100, 50) cs1 = ax.contour(lons, lats, mpcalc.smooth_n_point(isentprs[ilev], 9), clevs_pres, colors='black', transform=datacrs) plt.clabel(cs1, fmt='%d', fontsize='large') # Plot wind barbs ax.barbs(lons, lats, isentu[ilev].m, isentv[ilev].m, pivot='middle', color='black', regrid_shape=20, transform=datacrs)
# Subset data based on latitude and longitude values and select only data # from 850 hPa # # Set subset slice for the geographic extent of data to limit download lon_slice = slice(200, 350) lat_slice = slice(85, 10) # Grab lat/lon values (GFS will be 1D) lats = ds.lat.sel(lat=lat_slice).values lons = ds.lon.sel(lon=lon_slice).values # Grab data and smooth using a nine-point filter applied 50 times to grab the synoptic signal level = 850 * units.hPa hght_850 = mpcalc.smooth_n_point( ds.Geopotential_height_isobaric.metpy.sel(vertical=level, lat=lat_slice, lon=lon_slice).squeeze(), 9, 50) tmpk_850 = mpcalc.smooth_n_point( ds.Temperature_isobaric.metpy.sel(vertical=level, lat=lat_slice, lon=lon_slice).squeeze(), 9, 25) uwnd_850 = mpcalc.smooth_n_point( ds['u-component_of_wind_isobaric'].metpy.sel(vertical=level, lat=lat_slice, lon=lon_slice).squeeze(), 9, 50) vwnd_850 = mpcalc.smooth_n_point( ds['v-component_of_wind_isobaric'].metpy.sel(vertical=level, lat=lat_slice, lon=lon_slice).squeeze(), 9, 50)
# Define the CRS and inset axes data_crs = data2['accrain'].metpy.cartopy_crs ax_inset = fig.add_axes([0.65, 0.495, 0.3, 0.3], projection=data_crs) gl = ax_inset.gridlines(draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle=':') gl.xlabels_top, gl.xlabels_bottom, gl.ylabels_left, gl.ylabels_right = [ False, True, True, False ] gl.xformatter, gl.yformatter = [LONGITUDE_FORMATTER, LATITUDE_FORMATTER] ax_inset.tick_params(labelsize=8) cf = ax_inset.contourf(data['lon'], data['lat'], mpcalc.smooth_n_point(data['accrain'], 9), levels=np.arange(-22, 26, 4), cmap=drywet, extend='both') #levels=np.arange(-20,24,4), cmap=drywet, extend='both') axins = inset_axes(ax_inset, width="5%", height="70%", loc='lower left') cbar = fig.colorbar(cf, cax=axins, orientation="vertical", ticks=[-18, -6, 6, 18]) cbar.ax.tick_params(which='both', direction='in') cbar.ax.tick_params(which='both', length=0) #ax_inset.plot(obs_lon, obs_lat, color='k', ms=10, marker='*', zorder=100, alpha=0.5 ) ax_inset.scatter([start[1], end[1]], [start[0], end[0]], c='r', zorder=2) ax_inset.plot(cross['lon'], cross['lat'], c='r', zorder=2)
lats = ds.lat.data lons = ds.lon.data # Grab x, y data and make 2D for wind component plotting because # u- and v-components are grid relative x = ds['u-component_of_wind_isobaric'].x y = ds['u-component_of_wind_isobaric'].y xx, yy = np.meshgrid(x, y) # Grab Cartopy CRS from metadata for plotting wind barbs datacrs = ds['u-component_of_wind_isobaric'].metpy.cartopy_crs # Select and grab 500-hPa geopotential heights and smooth with n-point smoother level = 500 * units.hPa hght_500 = mpcalc.smooth_n_point( ds.Geopotential_height_isobaric.metpy.sel(vertical=level).squeeze(), 9, 50) # Select and grab 500-hPa wind components uwnd_500 = ds['u-component_of_wind_isobaric'].metpy.sel( vertical=level).squeeze() vwnd_500 = ds['v-component_of_wind_isobaric'].metpy.sel( vertical=level).squeeze() # Compute north-relative wind components for plotting purposes uwnd_er, vwnd_er = earth_relative_wind_components(uwnd_500, vwnd_500) # Smooth wind components as desired uwnd_er = mpcalc.smooth_n_point(uwnd_er, 9, 50) vwnd_er = mpcalc.smooth_n_point(vwnd_er, 9, 50) # Create a clean datetime object for plotting based on time of Geopotential heights