def convert_specific_humidity_to_mixing_ratio(Q): from metpy.calc import mixing_ratio_from_specific_humidity from metpy.units import units try: q_unit = Q.attrs['units'] except KeyError: q_unit = 'g/kg' q_values = Q.values * units(q_unit) MR = mixing_ratio_from_specific_humidity(q_values) da = Q.copy(data=MR.magnitude) da.attrs['units'] = q_unit da.attrs['long_name'] = 'Mixing Ratio' return da
def virt_temp(t, q): """ Compute virtual temperature in [K] from temperature and specific humidity. Arguments: t -- temperature in [K] q -- specific humidity in [kg/kg] t and q can be scalars of NumPy arrays. They just have to either all scalars, or all arrays. Returns: Virtual temperature in [K]. Same dimension as input fields. """ t = units.Quantity(t, "K") mix_rat = mpcalc.mixing_ratio_from_specific_humidity(q) v_temp = mpcalc.virtual_temperature(t, mix_rat) return v_temp
def test_mixing_ratio_from_specific_humidity(): """Tests mixing ratio from specific humidity.""" q = 0.012 w = mixing_ratio_from_specific_humidity(q) assert_almost_equal(w, 0.01215, 3)
def test_mixing_ratio_from_specific_humidity(): """Test mixing ratio from specific humidity.""" q = 0.012 * units.dimensionless w = mixing_ratio_from_specific_humidity(q) assert_almost_equal(w, 0.01215, 3)
def get_density_vertical_velocity_and_omega(circle): den_m = [None] * len(circle.sounding) for n in range(len(circle.sounding)): if len(circle.isel(sounding=n).sonde_id.values) > 1: mr = mpcalc.mixing_ratio_from_specific_humidity( circle.isel(sounding=n).q_sounding.values ) den_m[n] = mpcalc.density( circle.isel(sounding=n).p_sounding.values * units.Pa, circle.isel(sounding=n).ta_sounding.values * units.kelvin, mr, ).magnitude else: den_m[n] = np.nan circle["density"] = (["sounding", "circle", "alt"], den_m) circle["mean_density"] = (["circle", "alt"], np.nanmean(den_m, axis=0)) D = circle.D.values mean_den = circle.mean_density nan_ids = np.where(np.isnan(D) == True) # [0] w_vel = np.full([len(circle["circle"]), len(circle.alt)], np.nan) p_vel = np.full([len(circle["circle"]), len(circle.alt)], np.nan) w_vel[:, 0] = 0 # last = 0 for cir in range(len(circle["circle"])): last = 0 for m in range(1, len(circle.alt)): if ( len( np.intersect1d( np.where(nan_ids[1] == m)[0], np.where(nan_ids[0] == cir)[0] ) ) > 0 ): ids_for_nan_ids = np.intersect1d( np.where(nan_ids[1] == m)[0], np.where(nan_ids[0] == cir)[0] ) w_vel[nan_ids[0][ids_for_nan_ids], nan_ids[1][ids_for_nan_ids]] = np.nan else: w_vel[cir, m] = w_vel[cir, last] - circle.D.isel(circle=cir).isel( alt=m ).values * 10 * (m - last) last = m for n in range(1, len(circle.alt)): p_vel[cir, n] = ( -circle.mean_density.isel(circle=cir).isel(alt=n) * 9.81 * w_vel[cir, n] # * 60 # * 60 # / 100 ) circle["W"] = (["circle", "alt"], w_vel) circle["omega"] = (["circle", "alt"], p_vel) return print("Finished estimating density, W and omega ...")
def test_mixing_ratio_from_specific_humidity(): """Test mixing ratio from specific humidity.""" q = 0.012 * units.dimensionless w = mixing_ratio_from_specific_humidity(q) assert_almost_equal(w, 0.01215, 3)
ws = df['speed'].to_numpy() ws = ws * units.knot u = df['u_wind'].to_numpy() u = u * units.knot v = df['v_wind'].to_numpy() v = v * units.knot rh = mpcalc.relative_humidity_from_dewpoint(t,td) rh = rh * units.percent rh = rh * 100. #compute mixing ratio, pot. temp, spec. humidity, virtual pot. temp. pt = mpcalc.potential_temperature(p,t) # this is written for metpy 1.0, older versions of metpy want the arguments reversed in the below q to dp function # check your version with metpy.__version__ q = mpcalc.specific_humidity_from_dewpoint(p,td) mr = mpcalc.mixing_ratio_from_specific_humidity(q) vpt = mpcalc.virtual_potential_temperature(p, t, mr) mr = mr * 1000 * (units.gram/units.kilogram) #adjust units for viz # if using NCAR research sondes if grade == 1: file_path = '/Users/elizabeth.smith/Documents/CHEESEHEAD/sondes/' snd_name = 'CHEESEHEAD_ISS1_RS41_v1_'+f"{date:%Y%m%d_%H}" snd_file = glob.glob(file_path+snd_name+'*.nc') if len(snd_file)<1: #glob found none #print("Missing obs on "+f"{date:%Y%m%d_%H}") continue else: snd_file = np.sort(glob.glob(file_path+snd_name+'*.nc'))[0] print("Found obs on "+f"{date:%Y%m%d_%H}") ### JTC 4/8/21 Adding full date out to seconds and
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))