def theta_plots(pressure, temperature, mixing_ratio): """ plots for vertical profiles of potential temperature, equivalent potential temperature, and saturated equivalent potential temperature """ p = pressure * units('mbar') T = temperature * units('degC') q = mixing_ratio * units('kilogram/kilogram') lev = find_nearest(p.magnitude, 100) Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q)) # dewpoint theta = mpcalc.potential_temperature(p, T) theta_e = mpcalc.equivalent_potential_temperature(p, T, Td) theta_es = mpcalc.equivalent_potential_temperature(p, T, T) plt.figure(figsize=(7, 7)) plt.plot(theta[:lev], p[:lev], '-ok') plt.plot(theta_e[:lev], p[:lev], '-ob') plt.plot(theta_es[:lev], p[:lev], '-or') plt.xlabel('Temperature [K]', fontsize=12) plt.ylabel('Pressure [hpa]', fontsize=12) plt.gca().invert_yaxis() plt.legend(['$\\theta$', '$\\theta_e$', '$\\theta_{es}$'], loc=1) plt.grid() return (plt)
def run_calcs(df, ctx): """Do our maths.""" # Convert sea level pressure to station pressure df['pressure'] = mcalc.add_height_to_pressure( df['slp'].values * units('millibars'), ctx['nt'].sts[ctx['station']]['elevation'] * units('m')).to( units('millibar')) # Compute the mixing ratio df['mixingratio'] = mcalc.mixing_ratio_from_relative_humidity( df['relh'].values * units('percent'), df['tmpf'].values * units('degF'), df['pressure'].values * units('millibars')) # Compute the saturation mixing ratio df['saturation_mixingratio'] = mcalc.saturation_mixing_ratio( df['pressure'].values * units('millibars'), df['tmpf'].values * units('degF')) df['vapor_pressure'] = mcalc.vapor_pressure( df['pressure'].values * units('millibars'), df['mixingratio'].values * units('kg/kg')).to(units('kPa')) df['saturation_vapor_pressure'] = mcalc.vapor_pressure( df['pressure'].values * units('millibars'), df['saturation_mixingratio'].values * units('kg/kg')).to(units('kPa')) df['vpd'] = df['saturation_vapor_pressure'] - df['vapor_pressure'] group = df.groupby('year') df = group.aggregate(np.average) df['dwpf'] = mcalc.dewpoint(df['vapor_pressure'].values * units('kPa')).to( units('degF')).m return df
def add_entropy(ax, pressure, temperature, mixing_ratio, ds=100, linewidth=1.0): "add entropy curves and rescale values to fit in by 0.5*entropy + ds" p = pressure * units('mbar') T = temperature * units('degC') q = mixing_ratio * units('kilogram/kilogram') qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p) Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q)) # dewpoint Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # parcel profile # specific entropy [joule/(kg*K)] # sd : specific entropy of dry air # sm1 : specific entropy of airborne mositure in state 1 (water vapor) # sm2 : specific entropy of airborne mositure in state 2 (saturated water vapor) sd = entropy(T, q * 0, p) sm1 = entropy(T, q, p) sm2 = entropy(T, qs, p) ax.plot(sd.magnitude * 0.5 + ds, p, '--k') ax.plot(sm1.magnitude * 0.5 + ds, p, '--b') ax.plot(sm2.magnitude * 0.5 + ds, p, '--r')
def run_calcs(df, ctx): """Do our maths.""" # Convert sea level pressure to station pressure df["pressure"] = mcalc.add_height_to_pressure( df["slp"].values * units("millibars"), ctx["_nt"].sts[ctx["station"]]["elevation"] * units("m"), ).to(units("millibar")) # Compute the mixing ratio df["mixingratio"] = mcalc.mixing_ratio_from_relative_humidity( df["relh"].values * units("percent"), df["tmpf"].values * units("degF"), df["pressure"].values * units("millibars"), ) # Compute the saturation mixing ratio df["saturation_mixingratio"] = mcalc.saturation_mixing_ratio( df["pressure"].values * units("millibars"), df["tmpf"].values * units("degF"), ) df["vapor_pressure"] = mcalc.vapor_pressure( df["pressure"].values * units("millibars"), df["mixingratio"].values * units("kg/kg"), ).to(units("kPa")) df["saturation_vapor_pressure"] = mcalc.vapor_pressure( df["pressure"].values * units("millibars"), df["saturation_mixingratio"].values * units("kg/kg"), ).to(units("kPa")) df["vpd"] = df["saturation_vapor_pressure"] - df["vapor_pressure"] # remove any NaN rows df = df.dropna() group = df.groupby("year") df = group.aggregate(np.average) df["dwpf"] = (mcalc.dewpoint(df["vapor_pressure"].values * units("kPa")).to(units("degF")).m) return df
def plot(variables, prev_vars, pltenv): cont_int = 10 cont_smooth = 0.5 x = pltenv['x'] y = pltenv['y'] m = pltenv['map'] bbox = dict(boxstyle="square", ec='None', fc=(1, 1, 1, 0.75)) #var = (variables['T2'][0]-273.15) * 1.8 + 32 var = variables['Q2'][0] * 1000. varp = variables['AFWA_MSLP'][0] * 0.01 var = var * units('g/kg') vare = mcalc.vapor_pressure(varp * units.mbar, var) vartd = mcalc.dewpoint(vare) vartd = vartd.to('degF') var2 = ndimage.gaussian_filter(vartd, sigma=cont_smooth) levels = np.arange(-100, 150, cont_int) levels2 = np.arange(-40, 140, 1) P = m.contour(x, y, var2, levels=levels, colors='k') plt.clabel(P, inline=1, fontsize=10, fmt='%1.0f', inline_spacing=1) P = m.contour(x, y, var2, levels=[32], colors='r') plt.clabel(P, inline=1, fontsize=10, fmt='%1.0f', inline_spacing=1) m.contourf(x, y, vartd, cmap='gist_ncar', levels=levels2, extend='both')
def convert_H2O_MR_to_Td(H2O_MR, p): # input water vapour mass mixing ration in (mWV / mDA) kg/kg WVMR = H2O_MR * 1000 # convert to grams WVMR = WVMR * units('g/kg') e_1 = mpcalc.vapor_pressure(p_NUCAPS_orig, WVMR) T_d = mpcalc.dewpoint(e_1) return T_d
def test_dewpoint_weird_units(): """Test dewpoint using non-standard units. Revealed from odd dimensionless units and ending up using numpy.ma math functions instead of numpy ones. """ assert_almost_equal(dewpoint(15825.6 * units('g * mbar / kg')), 13.8564 * units.degC, 4)
def add_curves(ax, pressure, temperature, mixing_ratio, altitude, linewidth=1.0, LH_Tdepend=False): """ overlaying new curves of multiple soundings from profiles """ p = pressure * units('mbar') T = temperature * units('degC') q = mixing_ratio * units('kilogram/kilogram') qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p) Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q)) # dewpoint Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # parcel profile # Altitude based on the hydrostatic eq. if len(altitude) == len(pressure): # (1) altitudes for whole levels altitude = altitude * units('meter') elif len(altitude ) == 1: # (2) known altitude where the soundings was launched z_surf = altitude.copy() * units('meter') # given altitude altitude = np.zeros((np.size(T))) * units('meter') for i in range(np.size(T)): altitude[i] = mpcalc.thickness_hydrostatic( p[:i + 1], T[:i + 1]) + z_surf # Hypsometric Eq. for height else: print( '***NOTE***: the altitude at the surface is assumed 0 meter, and altitudes are derived based on the hypsometric equation' ) altitude = np.zeros( (np.size(T))) * units('meter') # surface is 0 meter for i in range(np.size(T)): altitude[i] = mpcalc.thickness_hydrostatic( p[:i + 1], T[:i + 1]) # Hypsometric Eq. for height # specific energies if LH_Tdepend == False: mse = mpcalc.moist_static_energy(altitude, T, q) mse_s = mpcalc.moist_static_energy(altitude, T, qs) dse = mpcalc.dry_static_energy(altitude, T) else: # A short course in cloud physics, Roger and Yau (1989) Lvt = (2500.8 - 2.36 * T.magnitude + 0.0016 * T.magnitude**2 - 0.00006 * T.magnitude**3) * units( 'joule/gram') # latent heat of evaporation #Lf = 2834.1 - 0.29*T - 0.004*T**2 # latent heat of fusion mse = Cp_d * T + g * altitude + Lvt * q mse_s = Cp_d * T + g * altitude + Lvt * qs dse = mpcalc.dry_static_energy(altitude, T) ax.plot(dse, p, '--k', linewidth=linewidth) ax.plot(mse, p, '--b', linewidth=linewidth) ax.plot(mse_s, p, '--r', linewidth=linewidth)
def down_cape(p_start=None): if p_start not in ls.lev: raise ValueError( "Please provide pressure of one level of large-scale dataset to start calculating DCAPE from." ) # find index of p_start start_level = int((abs(ls.lev - p_start)).argmin()) # get temperature and humidity from large-scale state temp = ls.T.sel(lev=slice(None, 990)).metpy.unit_array mix = ls.r.sel(lev=slice(None, 990)).metpy.unit_array.to('kg/kg') p_vector = ls.lev.sel(lev=slice(None, 990)).metpy.unit_array # get dew-point temperature vap_pres = mpcalc.vapor_pressure(p_vector, mix) dew_temp = mpcalc.dewpoint(vap_pres) # pressure levels to integrate over p_down = ls.lev.sel(lev=slice(p_start, 990)) # find NaNs l_valid = ls.T[:, start_level].notnull().values d_cape = xr.full_like(ls.cape, np.nan) x = p_down.values temp = temp[l_valid, :] dew_temp = dew_temp[l_valid, :] # loop over all non-NaN times in large-scale state for i, this_time in enumerate(ls.T[l_valid].time): print(i) # bug: p_start has to be multiplied with units when given as argument, not beforehand wb_temp = mpcalc.wet_bulb_temperature(p_start * units['hPa'], temp[i, start_level], dew_temp[i, start_level]) # create placeholder for moist adiabat temperature moist_adiabat = temp[i, start_level:].to('degC') moist_adiabat_below = mpcalc.moist_lapse(p_vector[start_level + 1:], wb_temp, p_start * units['hPa']) moist_adiabat[0] = wb_temp moist_adiabat[1:] = moist_adiabat_below env_temp = temp[i, start_level:] temp_diff = moist_adiabat - env_temp y = temp_diff.magnitude d_cape.loc[this_time] = (mpconsts.Rd * (np.trapz(y, np.log(x)) * units.degK)).to( units('J/kg')) d_cape.attrs['long_name'] = 'Downward CAPE' return xr.merge([ls, xr.Dataset({'d_cape': d_cape})])
def entropy_plots(pressure, temperature, mixing_ratio, altitude, h0_std=2000, ensemble_size=20, ent_rate=np.arange(0, 2, 0.05), entrain=False): """ plotting the summarized entropy diagram with annotations and thermodynamic parameters """ p = pressure * units('mbar') T = temperature * units('degC') q = mixing_ratio * units('kilogram/kilogram') qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p) Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q)) # dewpoint Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # parcel profile # Altitude based on the hydrostatic eq. if len(altitude) == len(pressure): # (1) altitudes for whole levels altitude = altitude * units('meter') elif len(altitude ) == 1: # (2) known altitude where the soundings was launched z_surf = altitude.copy() * units('meter') # given altitude altitude = np.zeros((np.size(T))) * units('meter') for i in range(np.size(T)): altitude[i] = mpcalc.thickness_hydrostatic( p[:i + 1], T[:i + 1]) + z_surf # Hypsometric Eq. for height else: print( '***NOTE***: the altitude at the surface is assumed 0 meter, and altitudes are derived based on the hypsometric equation' ) altitude = np.zeros( (np.size(T))) * units('meter') # surface is 0 meter for i in range(np.size(T)): altitude[i] = mpcalc.thickness_hydrostatic( p[:i + 1], T[:i + 1]) # Hypsometric Eq. for height # specific entropy [joule/(kg*K)] # sd : specific entropy of dry air # sm1 : specific entropy of airborne mositure in state 1 (water vapor) # sm2 : specific entropy of airborne mositure in state 2 (saturated water vapor) sd = entropy(T.magnitude, q.magnitude * 1e-6, p.magnitude) sm1 = entropy(T.magnitude, q.magnitude, p.magnitude) sm2 = entropy(T.magnitude, qs.magnitude, p.magnitude) ############################### # Water vapor calculations p_PWtop = min(p) #p_PWtop = max(200*units.mbar, min(p) + 1*units.mbar) # integrating until 200mb cwv = mpcalc.precipitable_water(Td, p, top=p_PWtop) # column water vapor [mm] cwvs = mpcalc.precipitable_water( T, p, top=p_PWtop) # saturated column water vapor [mm] crh = (cwv / cwvs) * 100. # column relative humidity [%] #================================================ # plotting MSE vertical profiles fig = plt.figure(figsize=[12, 8]) ax = fig.add_axes([0.1, 0.1, 0.6, 0.8]) ax.plot(sd, p, '-k', linewidth=2) ax.plot(sm1, p, '-b', linewidth=2) ax.plot(sm2, p, '-r', linewidth=2) # mse based on different percentages of relative humidity qr = np.zeros((9, np.size(qs))) * units('kilogram/kilogram') sm1_r = qr # container for i in range(9): qr[i, :] = qs * 0.1 * (i + 1) sm1_r[i, :] = entropy(T.magnitude, qr[i, :].magnitude, p.magnitude) for i in range(9): ax.plot(sm1_r[i, :], p[:], '-', color='grey', linewidth=0.7) ax.text(sm1_r[i, 3].magnitude - 2, p[3].magnitude, str((i + 1) * 10)) # drawing LCL and LFC levels [lcl_pressure, lcl_temperature] = mpcalc.lcl(p[0], T[0], Td[0]) lcl_idx = np.argmin(np.abs(p.magnitude - lcl_pressure.magnitude)) [lfc_pressure, lfc_temperature] = mpcalc.lfc(p, T, Td) lfc_idx = np.argmin(np.abs(p.magnitude - lfc_pressure.magnitude)) # conserved mse of air parcel arising from 1000 hpa sm1_p = np.squeeze(np.ones((1, np.size(T))) * sm1[0]) # illustration of CAPE el_pressure, el_temperature = mpcalc.el(p, T, Td) # equilibrium level el_idx = np.argmin(np.abs(p.magnitude - el_pressure.magnitude)) ELps = [el_pressure.magnitude ] # Initialize an array of EL pressures for detrainment profile [CAPE, CIN] = mpcalc.cape_cin(p[:el_idx], T[:el_idx], Td[:el_idx], Tp[:el_idx]) plt.plot(sm1_p, p, color='green', linewidth=2) #ax.fill_betweenx(p[lcl_idx:el_idx+1],sm1_p[lcl_idx:el_idx+1],sm2[lcl_idx:el_idx+1],interpolate=True # ,color='green',alpha='0.3') ax.fill_betweenx(p, sd, sm1, color='deepskyblue', alpha='0.5') ax.set_xlabel('Specific entropies: sd, sm, sm_sat [J K$^{-1}$ kg$^{-1}$]', fontsize=14) ax.set_ylabel('Pressure [hPa]', fontsize=14) ax.set_xticks([0, 50, 100, 150, 200, 250, 300, 350]) ax.set_xlim([0, 440]) ax.set_ylim(1030, 120) if entrain is True: # Depict Entraining parcels # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z) # M=1 at ground without loss of generality # Distribution of surface parcel h offsets h0offsets = np.sort(np.random.normal( 0, h0_std, ensemble_size)) * units('joule/kilogram') # Distribution of entrainment rates entrainment_rates = ent_rate / (units('km')) for h0offset in h0offsets: h4ent = sm1.copy() h4ent[0] += h0offset for eps in entrainment_rates: M = np.exp(eps * (altitude - altitude[0])).to('dimensionless') # dM is the mass contribution at each level, with 1 at the origin level. M[0] = 0 dM = np.gradient(M) # parcel mass is a sum of all the dM's at each level # conserved linearly-mixed variables like h are weighted averages if eps.magnitude == 0.0: hent = np.ones(len(h4ent)) * h4ent[0] # no mixing else: hent = np.cumsum(dM * h4ent) / np.cumsum(dM) # Boolean for positive buoyancy, and its topmost altitude (index) where curve is clippes posboy = (hent > sm2) posboy[0] = True # so there is always a detrainment level # defining the first EL by posboy as the detrainment layer, swiching from positive buoyancy to # negative buoyancy (0 to 1) and skipping the surface ELindex_ent = 0 for idx in range(len(posboy) - 1): if posboy[idx + 1] == 0 and posboy[idx] == 1 and idx > 0: ELindex_ent = idx break # Plot the curve plt.plot(hent[0:ELindex_ent + 2], p[0:ELindex_ent + 2], linewidth=0.6, color='g') #plt.plot( hent[0:], p[0:], linewidth=0.6, color='g') # Keep a list for a histogram plot (detrainment profile) if p[ELindex_ent].magnitude < lfc_pressure.magnitude: # buoyant parcels only ELps.append(p[ELindex_ent].magnitude) # Plot a crude histogram of parcel detrainment levels NBINS = 20 pbins = np.linspace(1000, 150, num=NBINS) # pbins for detrainment levels hist = np.zeros((len(pbins) - 1)) for x in ELps: for i in range(len(pbins) - 1): if (x < pbins[i]) & (x >= pbins[i + 1]): hist[i] += 1 break det_per = hist / sum(hist) * 100 # percentages of detrainment ensumbles at levels ax2 = fig.add_axes([0.705, 0.1, 0.1, 0.8], facecolor=None) ax2.barh(pbins[1:], det_per, color='lightgrey', edgecolor='k', height=15 * (20 / NBINS)) ax2.set_xlim([0, 100]) ax2.set_xticks([0, 20, 40, 60, 80, 100]) ax2.set_ylim([1030, 120]) ax2.set_xlabel('Detrainment [%]') ax2.grid() ax2.set_zorder(2) ax.plot([400, 400], [1100, 0]) ax.annotate('Detrainment', xy=(362, 320), color='dimgrey') ax.annotate('ensemble: ' + str(ensemble_size * len(entrainment_rates)), xy=(364, 340), color='dimgrey') ax.annotate('Detrainment', xy=(362, 380), color='dimgrey') ax.annotate(' scale: 0 - 2 km', xy=(365, 400), color='dimgrey') # Overplots on the mess: undilute parcel and CAPE, etc. ax.plot((1, 1) * sm1[0], (1, 0) * (p[0]), color='g', linewidth=2) # Replot the sounding on top of all that mess ax.plot(sm2, p, color='r', linewidth=1.5) ax.plot(sm1, p, color='b', linewidth=1.5) # label LCL and LCF ax.plot((sm2[lcl_idx] + (-2000, 2000) * units('joule/kilogram')), lcl_pressure + (0, 0) * units('mbar'), color='orange', linewidth=3) ax.plot((sm2[lfc_idx] + (-2000, 2000) * units('joule/kilogram')), lfc_pressure + (0, 0) * units('mbar'), color='magenta', linewidth=3) # Plot a crude histogram of parcel detrainment levels # Text parts ax.text(30, pressure[3], 'RH (%)', fontsize=11, color='k') ax.text(20, 200, 'CAPE = ' + str(np.around(CAPE.magnitude, decimals=2)) + ' [J/kg]', fontsize=12, color='green') ax.text(20, 250, 'CIN = ' + str(np.around(CIN.magnitude, decimals=2)) + ' [J/kg]', fontsize=12, color='green') ax.text(20, 300, 'LCL = ' + str(np.around(lcl_pressure.magnitude, decimals=2)) + ' [hpa]', fontsize=12, color='darkorange') ax.text(20, 350, 'LFC = ' + str(np.around(lfc_pressure.magnitude, decimals=2)) + ' [hpa]', fontsize=12, color='magenta') ax.text(20, 400, 'CWV = ' + str(np.around(cwv.magnitude, decimals=2)) + ' [mm]', fontsize=12, color='deepskyblue') ax.text(20, 450, 'CRH = ' + str(np.around(crh.magnitude, decimals=2)) + ' [%]', fontsize=12, color='blue') ax.legend(['DEnt', 'MEnt', 'SMEnt'], fontsize=12, loc=1) ax.set_zorder(3) return (ax)
def main(): img_dir = Path("hail_plots/soundings/") if not img_dir.exists(): img_dir.mkdir(parents=True) data_dir = Path("/HOME/huziy/skynet3_rech1/hail/soundings_from_erai/") # dates = [datetime(1991, 9, 7), datetime(1991, 9, 7, 6), datetime(1991, 9, 7, 12), datetime(1991, 9, 7, 18), # datetime(1991, 9, 8, 0), datetime(1991, 9, 8, 18)] # # dates.extend([datetime(1991, 9, 6, 0), datetime(1991, 9, 6, 6), datetime(1991, 9, 6, 12), datetime(1991, 9, 6, 18)]) # # dates = [datetime(1990, 7, 7), datetime(2010, 7, 12), datetime(1991, 9, 8, 0)] dates_s = """ - 07/09/1991 12:00 - 07/09/1991 18:00 - 08/09/1991 00:00 - 08/09/1991 06:00 - 08/09/1991 12:00 - 13/09/1991 12:00 - 13/09/1991 18:00 - 14/09/1991 00:00 - 14/09/1991 06:00 - 14/09/1991 12:00 """ dates = [ datetime.strptime(line.strip()[1:].strip(), "%d/%m/%Y %H:%M") for line in dates_s.split("\n") if line.strip() != "" ] def __date_parser(s): return pd.datetime.strptime(s, '%Y-%m-%d %H:%M:%S') tt = pd.read_csv(data_dir.joinpath("TT.csv"), index_col=0, parse_dates=['Time']) uu = pd.read_csv(data_dir.joinpath("UU.csv"), index_col=0, parse_dates=['Time']) vv = pd.read_csv(data_dir.joinpath("VV.csv"), index_col=0, parse_dates=['Time']) hu = pd.read_csv(data_dir.joinpath("HU.csv"), index_col=0, parse_dates=['Time']) print(tt.head()) print([c for c in tt]) print(list(tt.columns.values)) temp_perturbation_degc = 0 for the_date in dates: p = np.array([float(c) for c in tt]) fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) tsel = tt.select(lambda d: d == the_date) usel = uu.select(lambda d: d == the_date) vsel = vv.select(lambda d: d == the_date) husel = hu.select(lambda d: d == the_date) tvals = tsel.values.mean(axis=0) uvals = usel.values.mean(axis=0) * mul_mpers_per_knot vvals = vsel.values.mean(axis=0) * mul_mpers_per_knot huvals = husel.values.mean(axis=0) * units("g/kg") # ignore the lowest level all_vars = [p, tvals, uvals, vvals, huvals] for i in range(len(all_vars)): all_vars[i] = all_vars[i][:-5] p, tvals, uvals, vvals, huvals = all_vars assert len(p) == len(huvals) tdvals = calc.dewpoint( calc.vapor_pressure(p * units.mbar, huvals).to(units.mbar)) print(tvals, tdvals) # Calculate full parcel profile and add to plot as black line parcel_profile = calc.parcel_profile( p[::-1] * units.mbar, (tvals[-1] + temp_perturbation_degc) * units.degC, tdvals[-1]).to('degC') parcel_profile = parcel_profile[::-1] skew.plot(p, parcel_profile, 'k', linewidth=2) # Example of coloring area between profiles greater = tvals * units.degC >= parcel_profile skew.ax.fill_betweenx(p, tvals, parcel_profile, where=greater, facecolor='blue', alpha=0.4) skew.ax.fill_betweenx(p, tvals, parcel_profile, where=~greater, facecolor='red', alpha=0.4) skew.plot(p, tvals, "r") skew.plot(p, tdvals, "g") skew.plot_barbs(p, uvals, vvals) # Plot a zero degree isotherm l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() plt.title("{} (dT={})".format(the_date, temp_perturbation_degc)) img_path = "{}_dT={}.png".format(the_date.strftime("%Y%m%d_%H%M%S"), temp_perturbation_degc) img_path = img_dir.joinpath(img_path) fig.savefig(str(img_path), bbox_inches="tight") plt.close(fig)
from metpy.vis import meteogram from metpy.constants import C2F from metpy.calc import dewpoint, windchill from metpy.cbook import rec_append_fields fields = ('stid', 'time', 'relh', 'tair', 'wspd', 'wmax', 'wdir', 'pres', 'srad', 'rain') data = read_mesonet_data('data/20080117nrmn.mts', fields, lookup_stids=False) #Add a reasonable time range if we're doing current data end = get_last_time(data) times = (end - datetime.timedelta(hours=24), end) #Calculate dewpoint in F from relative humidity and temperature dewpt = C2F(dewpoint(data['TAIR'], data['RELH'] / 100.)) data = rec_append_fields(data, ('dewpoint', ), (dewpt, )) #Convert temperature and dewpoint to Farenheit mod_units = mesonet_units.copy() mod_units['TAIR'] = 'F' mod_units['dewpoint'] = 'F' data['TAIR'] = C2F(data['TAIR']) #Convert wind speeds to MPH data['WSPD'] *= sconsts.hour / sconsts.mile data['WMAX'] *= sconsts.hour / sconsts.mile mod_units['WSPD'] = 'MPH' mod_units['WMAX'] = 'MPH' #Convert rainfall to inches
def main(): img_dir = Path("hail_plots/soundings/") if not img_dir.exists(): img_dir.mkdir(parents=True) data_dir = Path("/HOME/huziy/skynet3_rech1/hail/soundings_from_erai/") # dates = [datetime(1991, 9, 7), datetime(1991, 9, 7, 6), datetime(1991, 9, 7, 12), datetime(1991, 9, 7, 18), # datetime(1991, 9, 8, 0), datetime(1991, 9, 8, 18)] # # dates.extend([datetime(1991, 9, 6, 0), datetime(1991, 9, 6, 6), datetime(1991, 9, 6, 12), datetime(1991, 9, 6, 18)]) # # dates = [datetime(1990, 7, 7), datetime(2010, 7, 12), datetime(1991, 9, 8, 0)] dates_s = """ - 07/09/1991 12:00 - 07/09/1991 18:00 - 08/09/1991 00:00 - 08/09/1991 06:00 - 08/09/1991 12:00 - 13/09/1991 12:00 - 13/09/1991 18:00 - 14/09/1991 00:00 - 14/09/1991 06:00 - 14/09/1991 12:00 """ dates = [datetime.strptime(line.strip()[1:].strip(), "%d/%m/%Y %H:%M") for line in dates_s.split("\n") if line.strip() != ""] def __date_parser(s): return pd.datetime.strptime(s, '%Y-%m-%d %H:%M:%S') tt = pd.read_csv(data_dir.joinpath("TT.csv"), index_col=0, parse_dates=['Time']) uu = pd.read_csv(data_dir.joinpath("UU.csv"), index_col=0, parse_dates=['Time']) vv = pd.read_csv(data_dir.joinpath("VV.csv"), index_col=0, parse_dates=['Time']) hu = pd.read_csv(data_dir.joinpath("HU.csv"), index_col=0, parse_dates=['Time']) print(tt.head()) print([c for c in tt]) print(list(tt.columns.values)) temp_perturbation_degc = 0 for the_date in dates: p = np.array([float(c) for c in tt]) fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) tsel = tt.select(lambda d: d == the_date) usel = uu.select(lambda d: d == the_date) vsel = vv.select(lambda d: d == the_date) husel = hu.select(lambda d: d == the_date) tvals = tsel.values.mean(axis=0) uvals = usel.values.mean(axis=0) * mul_mpers_per_knot vvals = vsel.values.mean(axis=0) * mul_mpers_per_knot huvals = husel.values.mean(axis=0) * units("g/kg") # ignore the lowest level all_vars = [p, tvals, uvals, vvals, huvals] for i in range(len(all_vars)): all_vars[i] = all_vars[i][:-5] p, tvals, uvals, vvals, huvals = all_vars assert len(p) == len(huvals) tdvals = calc.dewpoint(calc.vapor_pressure(p * units.mbar, huvals).to(units.mbar)) print(tvals, tdvals) # Calculate full parcel profile and add to plot as black line parcel_profile = calc.parcel_profile(p[::-1] * units.mbar, (tvals[-1] + temp_perturbation_degc) * units.degC, tdvals[-1]).to('degC') parcel_profile = parcel_profile[::-1] skew.plot(p, parcel_profile, 'k', linewidth=2) # Example of coloring area between profiles greater = tvals * units.degC >= parcel_profile skew.ax.fill_betweenx(p, tvals, parcel_profile, where=greater, facecolor='blue', alpha=0.4) skew.ax.fill_betweenx(p, tvals, parcel_profile, where=~greater, facecolor='red', alpha=0.4) skew.plot(p, tvals, "r") skew.plot(p, tdvals, "g") skew.plot_barbs(p, uvals, vvals) # Plot a zero degree isotherm l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() plt.title("{} (dT={})".format(the_date, temp_perturbation_degc)) img_path = "{}_dT={}.png".format(the_date.strftime("%Y%m%d_%H%M%S"), temp_perturbation_degc) img_path = img_dir.joinpath(img_path) fig.savefig(str(img_path), bbox_inches="tight") plt.close(fig)
mixing = 10 * units('g/kg') print(mixing) ########################################### # Now throw that value with units into the function to calculate # the corresponding vapor pressure, given a surface pressure of 1000 mb e = mcalc.vapor_pressure(1000. * units.mbar, mixing) print(e) ########################################### # Take the odd units and force them to millibars print(e.to(units.mbar)) ########################################### # Take the raw vapor pressure and throw into the dewpoint function td = mcalc.dewpoint(e) print(td) ########################################### # Which can of course be converted to Fahrenheit print(td.to('degF')) ########################################### # Now do the same thing for 850 mb, approximately the pressure of Denver e = mcalc.vapor_pressure(850. * units.mbar, mixing) print(e.to(units.mbar)) ########################################### # And print the corresponding dewpoint td = mcalc.dewpoint(e) print(td, td.to('degF'))
def plotter(fdict): """ Go """ pgconn = get_dbconn('asos') ctx = get_autoplot_context(fdict, get_description()) station = ctx['zstation'] network = ctx['network'] month = ctx['month'] nt = NetworkTable(network) if month == 'all': months = range(1, 13) elif month == 'fall': months = [9, 10, 11] elif month == 'winter': months = [12, 1, 2] elif month == 'spring': months = [3, 4, 5] elif month == 'summer': months = [6, 7, 8] else: ts = datetime.datetime.strptime("2000-"+month+"-01", '%Y-%b-%d') # make sure it is length two for the trick below in SQL months = [ts.month, 999] df = read_sql(""" SELECT tmpf::int as tmpf, dwpf, coalesce(mslp, alti * 33.8639, 1013.25) as slp from alldata where station = %s and drct is not null and dwpf is not null and dwpf <= tmpf and sknt > 3 and drct::int %% 10 = 0 and extract(month from valid) in %s and report_type = 2 """, pgconn, params=(station, tuple(months))) # Convert sea level pressure to station pressure df['pressure'] = mcalc.add_height_to_pressure( df['slp'].values * units('millibars'), nt.sts[station]['elevation'] * units('m') ).to(units('millibar')) # compute RH df['relh'] = mcalc.relative_humidity_from_dewpoint( df['tmpf'].values * units('degF'), df['dwpf'].values * units('degF') ) # compute mixing ratio df['mixingratio'] = mcalc.mixing_ratio_from_relative_humidity( df['relh'].values, df['tmpf'].values * units('degF'), df['pressure'].values * units('millibars') ) # compute pressure df['vapor_pressure'] = mcalc.vapor_pressure( df['pressure'].values * units('millibars'), df['mixingratio'].values * units('kg/kg') ).to(units('kPa')) means = df.groupby('tmpf').mean().copy() # compute dewpoint now means['dwpf'] = mcalc.dewpoint( means['vapor_pressure'].values * units('kPa') ).to(units('degF')).m means.reset_index(inplace=True) # compute RH again means['relh'] = mcalc.relative_humidity_from_dewpoint( means['tmpf'].values * units('degF'), means['dwpf'].values * units('degF') ) * 100. (fig, ax) = plt.subplots(1, 1, figsize=(8, 6)) ax.bar( means['tmpf'].values - 0.5, means['dwpf'].values - 0.5, ec='green', fc='green', width=1 ) ax.grid(True, zorder=11) ax.set_title(("%s [%s]\nAverage Dew Point by Air Temperature (month=%s) " "(%s-%s)\n" "(must have 3+ hourly observations at the given temperature)" ) % (nt.sts[station]['name'], station, month.upper(), nt.sts[station]['archive_begin'].year, datetime.datetime.now().year), size=10) ax.plot([0, 140], [0, 140], color='b') ax.set_ylabel("Dew Point [F]") y2 = ax.twinx() y2.plot(means['tmpf'].values, means['relh'].values, color='k') y2.set_ylabel("Relative Humidity [%] (black line)") y2.set_yticks([0, 5, 10, 25, 50, 75, 90, 95, 100]) y2.set_ylim(0, 100) ax.set_ylim(0, means['tmpf'].max() + 2) ax.set_xlim(0, means['tmpf'].max() + 2) ax.set_xlabel(r"Air Temperature $^\circ$F") return fig, means[['tmpf', 'dwpf', 'relh']]
def plotter(fdict): """ Go """ pgconn = get_dbconn('asos') ctx = get_autoplot_context(fdict, get_description()) station = ctx['zstation'] network = ctx['network'] month = ctx['month'] nt = NetworkTable(network) if month == 'all': months = range(1, 13) elif month == 'fall': months = [9, 10, 11] elif month == 'winter': months = [12, 1, 2] elif month == 'spring': months = [3, 4, 5] elif month == 'summer': months = [6, 7, 8] else: ts = datetime.datetime.strptime("2000-" + month + "-01", '%Y-%b-%d') # make sure it is length two for the trick below in SQL months = [ts.month, 999] df = read_sql(""" SELECT drct::int as t, dwpf, tmpf, relh, coalesce(mslp, alti * 33.8639, 1013.25) as slp from alldata where station = %s and drct is not null and dwpf is not null and dwpf <= tmpf and sknt > 3 and drct::int %% 10 = 0 and extract(month from valid) in %s and report_type = 2 """, pgconn, params=(station, tuple(months))) # Convert sea level pressure to station pressure df['pressure'] = mcalc.add_height_to_pressure( df['slp'].values * units('millibars'), nt.sts[station]['elevation'] * units('m')).to(units('millibar')) # compute mixing ratio df['mixingratio'] = mcalc.mixing_ratio_from_relative_humidity( df['relh'].values * units('percent'), df['tmpf'].values * units('degF'), df['pressure'].values * units('millibars')) # compute pressure df['vapor_pressure'] = mcalc.vapor_pressure( df['pressure'].values * units('millibars'), df['mixingratio'].values * units('kg/kg')).to(units('kPa')) means = df.groupby('t').mean().copy() # compute dewpoint now means['dwpf'] = mcalc.dewpoint(means['vapor_pressure'].values * units('kPa')).to(units('degF')).m (fig, ax) = plt.subplots(1, 1) ax.bar(means.index.values, means['dwpf'].values, ec='green', fc='green', width=10, align='center') ax.grid(True, zorder=11) ax.set_title(("%s [%s]\nAverage Dew Point by Wind Direction (month=%s) " "(%s-%s)\n" "(must have 3+ hourly obs > 3 knots at given direction)") % (nt.sts[station]['name'], station, month.upper(), max([1973, nt.sts[station]['archive_begin'].year ]), datetime.datetime.now().year), size=10) ax.set_ylabel("Dew Point [F]") ax.set_ylim(means['dwpf'].min() - 5, means['dwpf'].max() + 5) ax.set_xlim(-5, 365) ax.set_xticks([0, 45, 90, 135, 180, 225, 270, 315, 360]) ax.set_xticklabels(['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']) ax.set_xlabel("Wind Direction") return fig, means['dwpf']
def plotter(fdict): """ Go """ pgconn = get_dbconn("asos") ctx = get_autoplot_context(fdict, get_description()) station = ctx["zstation"] month = ctx["month"] if month == "all": months = range(1, 13) elif month == "fall": months = [9, 10, 11] elif month == "winter": months = [12, 1, 2] elif month == "spring": months = [3, 4, 5] elif month == "summer": months = [6, 7, 8] else: ts = datetime.datetime.strptime("2000-" + month + "-01", "%Y-%b-%d") # make sure it is length two for the trick below in SQL months = [ts.month, 999] df = read_sql( """ SELECT drct::int as t, dwpf, tmpf, relh, coalesce(mslp, alti * 33.8639, 1013.25) as slp from alldata where station = %s and drct is not null and dwpf is not null and dwpf <= tmpf and sknt > 3 and drct::int %% 10 = 0 and extract(month from valid) in %s and report_type = 2 """, pgconn, params=(station, tuple(months)), ) if df.empty: raise NoDataFound("No Data Found.") # Convert sea level pressure to station pressure df["pressure"] = mcalc.add_height_to_pressure( df["slp"].values * units("millibars"), ctx["_nt"].sts[station]["elevation"] * units("m"), ).to(units("millibar")) # compute mixing ratio df["mixingratio"] = mcalc.mixing_ratio_from_relative_humidity( df["relh"].values * units("percent"), df["tmpf"].values * units("degF"), df["pressure"].values * units("millibars"), ) # compute pressure df["vapor_pressure"] = mcalc.vapor_pressure( df["pressure"].values * units("millibars"), df["mixingratio"].values * units("kg/kg"), ).to(units("kPa")) means = df.groupby("t").mean().copy() # compute dewpoint now means["dwpf"] = (mcalc.dewpoint(means["vapor_pressure"].values * units("kPa")).to(units("degF")).m) (fig, ax) = plt.subplots(1, 1) ax.bar( means.index.values, means["dwpf"].values, ec="green", fc="green", width=10, align="center", ) ax.grid(True, zorder=11) ab = ctx["_nt"].sts[station]["archive_begin"] if ab is None: raise NoDataFound("Unknown station metadata.") ax.set_title( ("%s [%s]\nAverage Dew Point by Wind Direction (month=%s) " "(%s-%s)\n" "(must have 3+ hourly obs > 3 knots at given direction)") % ( ctx["_nt"].sts[station]["name"], station, month.upper(), max([1973, ab.year]), datetime.datetime.now().year, ), size=10, ) ax.set_ylabel("Dew Point [F]") ax.set_ylim(means["dwpf"].min() - 5, means["dwpf"].max() + 5) ax.set_xlim(-5, 365) ax.set_xticks([0, 45, 90, 135, 180, 225, 270, 315, 360]) ax.set_xticklabels(["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"]) ax.set_xlabel("Wind Direction") return fig, means["dwpf"]
def dewpoint_from_relative_humidity(temperature, relative_humidity): """Similar to `metpy.calc.dewpoint_from_relative_humidity`, but without a check for values greater than 120%""" return mpcalc.dewpoint(relative_humidity * mpcalc.saturation_vapor_pressure(temperature))
def msed_plots(pressure, temperature, mixing_ratio, h0_std=2000, ensemble_size=20, ent_rate=np.arange(0, 2, 0.05), entrain=False): """ plotting the summarized static energy diagram with annotations and thermodynamic parameters """ p = pressure * units('mbar') T = temperature * units('degC') q = mixing_ratio * units('kilogram/kilogram') qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p) Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q)) # dewpoint Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # parcel profile # Altitude based on the hydrostatic eq. altitude = np.zeros((np.size(T))) * units('meter') # surface is 0 meter for i in range(np.size(T)): altitude[i] = mpcalc.thickness_hydrostatic( p[:i + 1], T[:i + 1]) # Hypsometric Eq. for height # Static energy calculations mse = mpcalc.moist_static_energy(altitude, T, q) mse_s = mpcalc.moist_static_energy(altitude, T, qs) dse = mpcalc.dry_static_energy(altitude, T) # Water vapor calculations p_PWtop = max(200 * units.mbar, min(p) + 1 * units.mbar) # integrating until 200mb cwv = mpcalc.precipitable_water(Td, p, top=p_PWtop) # column water vapor [mm] cwvs = mpcalc.precipitable_water( T, p, top=p_PWtop) # saturated column water vapor [mm] crh = (cwv / cwvs) * 100. # column relative humidity [%] #================================================ # plotting MSE vertical profiles fig = plt.figure(figsize=[12, 8]) ax = fig.add_axes([0.1, 0.1, 0.6, 0.8]) ax.plot(dse, p, '-k', linewidth=2) ax.plot(mse, p, '-b', linewidth=2) ax.plot(mse_s, p, '-r', linewidth=2) # mse based on different percentages of relative humidity qr = np.zeros((9, np.size(qs))) * units('kilogram/kilogram') mse_r = qr * units('joule/kilogram') # container for i in range(9): qr[i, :] = qs * 0.1 * (i + 1) mse_r[i, :] = mpcalc.moist_static_energy(altitude, T, qr[i, :]) for i in range(9): ax.plot(mse_r[i, :], p[:], '-', color='grey', linewidth=0.7) ax.text(mse_r[i, 3].magnitude / 1000 - 1, p[3].magnitude, str((i + 1) * 10)) # drawing LCL and LFC levels [lcl_pressure, lcl_temperature] = mpcalc.lcl(p[0], T[0], Td[0]) lcl_idx = np.argmin(np.abs(p.magnitude - lcl_pressure.magnitude)) [lfc_pressure, lfc_temperature] = mpcalc.lfc(p, T, Td) lfc_idx = np.argmin(np.abs(p.magnitude - lfc_pressure.magnitude)) # conserved mse of air parcel arising from 1000 hpa mse_p = np.squeeze(np.ones((1, np.size(T))) * mse[0].magnitude) # illustration of CAPE el_pressure, el_temperature = mpcalc.el(p, T, Td) # equilibrium level el_idx = np.argmin(np.abs(p.magnitude - el_pressure.magnitude)) ELps = [el_pressure.magnitude ] # Initialize an array of EL pressures for detrainment profile [CAPE, CIN] = mpcalc.cape_cin(p[:el_idx], T[:el_idx], Td[:el_idx], Tp[:el_idx]) plt.plot(mse_p, p, color='green', linewidth=2) ax.fill_betweenx(p[lcl_idx:el_idx + 1], mse_p[lcl_idx:el_idx + 1], mse_s[lcl_idx:el_idx + 1], interpolate=True, color='green', alpha='0.3') ax.fill_betweenx(p, dse, mse, color='deepskyblue', alpha='0.5') ax.set_xlabel('Specific static energies: s, h, hs [kJ kg$^{-1}$]', fontsize=14) ax.set_ylabel('Pressure [hpa]', fontsize=14) ax.set_xticks([280, 300, 320, 340, 360, 380]) ax.set_xlim([280, 390]) ax.set_ylim(1030, 120) if entrain is True: # Depict Entraining parcels # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z) # M=1 at ground without loss of generality # Distribution of surface parcel h offsets H0STDEV = h0_std # J/kg h0offsets = np.sort(np.random.normal( 0, H0STDEV, ensemble_size)) * units('joule/kilogram') # Distribution of entrainment rates entrainment_rates = ent_rate / (units('km')) for h0offset in h0offsets: h4ent = mse.copy() h4ent[0] += h0offset for eps in entrainment_rates: M = np.exp(eps * (altitude - altitude[0])).to('dimensionless') # dM is the mass contribution at each level, with 1 at the origin level. M[0] = 0 dM = np.gradient(M) # parcel mass is a sum of all the dM's at each level # conserved linearly-mixed variables like h are weighted averages hent = np.cumsum(dM * h4ent) / np.cumsum(dM) # Boolean for positive buoyancy, and its topmost altitude (index) where curve is clippes posboy = (hent > mse_s) posboy[0] = True # so there is always a detrainment level ELindex_ent = np.max(np.where(posboy)) # Plot the curve plt.plot(hent[0:ELindex_ent + 2], p[0:ELindex_ent + 2], linewidth=0.25, color='g') # Keep a list for a histogram plot (detrainment profile) if p[ELindex_ent].magnitude < lfc_pressure.magnitude: # buoyant parcels only ELps.append(p[ELindex_ent].magnitude) # Plot a crude histogram of parcel detrainment levels NBINS = 20 pbins = np.linspace(1000, 150, num=NBINS) # pbins for detrainment levels hist = np.zeros((len(pbins) - 1)) for x in ELps: for i in range(len(pbins) - 1): if (x < pbins[i]) & (x >= pbins[i + 1]): hist[i] += 1 break det_per = hist / sum(hist) * 100 # percentages of detrainment ensumbles at levels ax2 = fig.add_axes([0.705, 0.1, 0.1, 0.8], facecolor=None) ax2.barh(pbins[1:], det_per, color='lightgrey', edgecolor='k', height=15 * (20 / NBINS)) ax2.set_xlim([0, max(det_per)]) ax2.set_ylim([1030, 120]) ax2.set_xlabel('Detrainment [%]') ax2.grid() ax2.set_zorder(2) ax.plot([400, 400], [1100, 0]) ax.annotate('Detrainment', xy=(362, 320), color='dimgrey') ax.annotate('ensemble: ' + str(ensemble_size * len(entrainment_rates)), xy=(364, 340), color='dimgrey') ax.annotate('Detrainment', xy=(362, 380), color='dimgrey') ax.annotate(' scale: 0 - 2 km', xy=(365, 400), color='dimgrey') # Overplots on the mess: undilute parcel and CAPE, etc. ax.plot((1, 1) * mse[0], (1, 0) * (p[0]), color='g', linewidth=2) # Replot the sounding on top of all that mess ax.plot(mse_s, p, color='r', linewidth=1.5) ax.plot(mse, p, color='b', linewidth=1.5) # label LCL and LCF ax.plot((mse_s[lcl_idx] + (-2000, 2000) * units('joule/kilogram')), lcl_pressure + (0, 0) * units('mbar'), color='orange', linewidth=3) ax.plot((mse_s[lfc_idx] + (-2000, 2000) * units('joule/kilogram')), lfc_pressure + (0, 0) * units('mbar'), color='magenta', linewidth=3) ### Internal waves (100m adiabatic displacements, assumed adiabatic: conserves s, sv, h). #dZ = 100 *mpunits.units.meter dp = 1000 * units.pascal # depict displacements at sounding levels nearest these target levels targetlevels = [900, 800, 700, 600, 500, 400, 300, 200] * units.hPa for ilev in targetlevels: idx = np.argmin(np.abs(p - ilev)) # dp: hydrostatic rho = (p[idx]) / Rd / (T[idx]) dZ = -dp / rho / g # dT: Dry lapse rate dT/dz_dry is -g/Cp dT = (-g / Cp_d * dZ).to('kelvin') Tdisp = T[idx].to('kelvin') + dT # dhsat dqs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(Tdisp), p[idx] + dp) - qs[idx] dhs = g * dZ + Cp_d * dT + Lv * dqs # Whiskers on the data plots ax.plot((mse_s[idx] + dhs * (-1, 1)), p[idx] + dp * (-1, 1), linewidth=3, color='r') ax.plot((dse[idx] * (1, 1)), p[idx] + dp * (-1, 1), linewidth=3, color='k') ax.plot((mse[idx] * (1, 1)), p[idx] + dp * (-1, 1), linewidth=3, color='b') # annotation to explain it if ilev == 400 * ilev.units: ax.plot(360 * mse_s.units + dhs * (-1, 1) / 1000, 440 * units('mbar') + dp * (-1, 1), linewidth=3, color='r') ax.annotate('+/- 10mb', xy=(362, 440), fontsize=8) ax.annotate(' adiabatic displacement', xy=(362, 460), fontsize=8) # Plot a crude histogram of parcel detrainment levels # Text parts ax.text(290, pressure[3], 'RH (%)', fontsize=11, color='k') ax.text(285, 200, 'CAPE = ' + str(np.around(CAPE.magnitude, decimals=2)) + ' [J/kg]', fontsize=12, color='green') ax.text(285, 250, 'CIN = ' + str(np.around(CIN.magnitude, decimals=2)) + ' [J/kg]', fontsize=12, color='green') ax.text(285, 300, 'LCL = ' + str(np.around(lcl_pressure.magnitude, decimals=2)) + ' [hpa]', fontsize=12, color='darkorange') ax.text(285, 350, 'LFC = ' + str(np.around(lfc_pressure.magnitude, decimals=2)) + ' [hpa]', fontsize=12, color='magenta') ax.text(285, 400, 'CWV = ' + str(np.around(cwv.magnitude, decimals=2)) + ' [mm]', fontsize=12, color='deepskyblue') ax.text(285, 450, 'CRH = ' + str(np.around(crh.magnitude, decimals=2)) + ' [%]', fontsize=12, color='blue') ax.legend(['DSE', 'MSE', 'SMSE'], fontsize=12, loc=1) ax.set_zorder(3) return (ax)
def plotter(fdict): """ Go """ pgconn = get_dbconn("asos") ctx = get_autoplot_context(fdict, get_description()) station = ctx["zstation"] month = ctx["month"] if month == "all": months = range(1, 13) elif month == "fall": months = [9, 10, 11] elif month == "winter": months = [12, 1, 2] elif month == "spring": months = [3, 4, 5] elif month == "summer": months = [6, 7, 8] else: ts = datetime.datetime.strptime("2000-" + month + "-01", "%Y-%b-%d") # make sure it is length two for the trick below in SQL months = [ts.month, 999] df = read_sql( """ SELECT tmpf::int as tmpf, dwpf, relh, coalesce(mslp, alti * 33.8639, 1013.25) as slp from alldata where station = %s and drct is not null and dwpf is not null and dwpf <= tmpf and sknt > 3 and drct::int %% 10 = 0 and extract(month from valid) in %s and report_type = 2 """, pgconn, params=(station, tuple(months)), ) if df.empty: raise NoDataFound("No Data Found.") # Convert sea level pressure to station pressure df["pressure"] = mcalc.add_height_to_pressure( df["slp"].values * units("millibars"), ctx["_nt"].sts[station]["elevation"] * units("m"), ).to(units("millibar")) # compute mixing ratio df["mixingratio"] = mcalc.mixing_ratio_from_relative_humidity( df["relh"].values * units("percent"), df["tmpf"].values * units("degF"), df["pressure"].values * units("millibars"), ) # compute pressure df["vapor_pressure"] = mcalc.vapor_pressure( df["pressure"].values * units("millibars"), df["mixingratio"].values * units("kg/kg"), ).to(units("kPa")) means = df.groupby("tmpf").mean().copy() # compute dewpoint now means["dwpf"] = ( mcalc.dewpoint(means["vapor_pressure"].values * units("kPa")) .to(units("degF")) .m ) means.reset_index(inplace=True) # compute RH again means["relh"] = ( mcalc.relative_humidity_from_dewpoint( means["tmpf"].values * units("degF"), means["dwpf"].values * units("degF"), ) * 100.0 ) (fig, ax) = plt.subplots(1, 1, figsize=(8, 6)) ax.bar( means["tmpf"].values - 0.5, means["dwpf"].values - 0.5, ec="green", fc="green", width=1, ) ax.grid(True, zorder=11) ab = ctx["_nt"].sts[station]["archive_begin"] if ab is None: raise NoDataFound("Unknown station metadata.") ax.set_title( ( "%s [%s]\nAverage Dew Point by Air Temperature (month=%s) " "(%s-%s)\n" "(must have 3+ hourly observations at the given temperature)" ) % ( ctx["_nt"].sts[station]["name"], station, month.upper(), ab.year, datetime.datetime.now().year, ), size=10, ) ax.plot([0, 140], [0, 140], color="b") ax.set_ylabel("Dew Point [F]") y2 = ax.twinx() y2.plot(means["tmpf"].values, means["relh"].values, color="k") y2.set_ylabel("Relative Humidity [%] (black line)") y2.set_yticks([0, 5, 10, 25, 50, 75, 90, 95, 100]) y2.set_ylim(0, 100) ax.set_ylim(0, means["tmpf"].max() + 2) ax.set_xlim(0, means["tmpf"].max() + 2) ax.set_xlabel(r"Air Temperature $^\circ$F") return fig, means[["tmpf", "dwpf", "relh"]]
def test_dewpoint(): """Test dewpoint calculation.""" assert_almost_equal(dewpoint(6.112 * units.mbar), 0. * units.degC, 2)
T_NUCAPS = NUCAPS_data.Temperature.values - 273.15 T_NUCAPS = pd.DataFrame({ 'temperature_degC': np.reshape(T_NUCAPS, (T_NUCAPS.shape[0] * T_NUCAPS.shape[1])) }) T_NUCAPS_1 = (NUCAPS_data.Temperature.values - 273.15) * units.degC H2O_MR = np.reshape(NUCAPS_data['H2O_MR'].values, (T_NUCAPS.shape[0] * T_NUCAPS.shape[1])) * units('g/kg') H2O_MR_1 = NUCAPS_data['H2O_MR'].values H2O_MR = pd.DataFrame({'mixing_ratio': H2O_MR}) p_NUCAPS_2 = NUCAPS_data.Pressure.values * units.hPa WVMR = (H2O_MR_1 * 1000) * units('g/kg') e_1 = mpcalc.vapor_pressure(p_NUCAPS_2, WVMR) T_d = mpcalc.dewpoint(e_1) T_d_NUCAPS = pd.DataFrame({ 'dew_point_degC': np.reshape(T_d, (T_NUCAPS.shape[0] * T_NUCAPS.shape[1])) }) datetime_NUCAPS = NUCAPS_data.datetime.values datetime_NUCAPS = pd.DataFrame( {'time_YMDHMS_1': (np.repeat(datetime_NUCAPS, 100))}) # round datime to next RS time (0000 or 1200) datetime_NUCAPS_round = pd.DataFrame( datetime_NUCAPS.time_YMDHMS_1.dt.ceil('60 min').values, columns=['time_YMDHMS']) datetime_NUCAPS_round.time_YMDHMS[ datetime_NUCAPS_round.time_YMDHMS.dt.hour ==
print('Time length: ', len(time)) # From dimension to variable. press = np.zeros_like(temp) for cnt in range(temp.shape[0]): press[cnt, :] = lev pressure = press * units.hPa temperature = temp * units.K mixing_ratio = wvmr * units('kg/kg') # get dew point relative_humidity = mpcalc.relative_humidity_from_mixing_ratio( mixing_ratio, temperature, pressure) e = mpcalc.vapor_pressure(pressure, mixing_ratio) dew_point = mpcalc.dewpoint(e) def get_cape(inargs, return_parcel_profile=False): pres_prof, temp_prof, dp_prof = inargs try: prof = mpcalc.parcel_profile(pres_prof, temp_prof[0], dp_prof[0]) cape, cin = mpcalc.cape_cin(pres_prof, temp_prof, dp_prof, prof) except Exception: cape, cin, prof = np.NaN, np.NaN, np.NaN print('Problem during CAPE-calculation. Likely NaN-related.') if return_parcel_profile: return cape, cin, prof else: return cape, cin
mixing = 10 * units('g/kg') print(mixing) ########################################### # Now throw that value with units into the function to calculate # the corresponding vapor pressure, given a surface pressure of 1000 mb e = mpcalc.vapor_pressure(1000. * units.mbar, mixing) print(e) ########################################### # Take the odd units and force them to millibars print(e.to(units.mbar)) ########################################### # Take the raw vapor pressure and throw into the dewpoint function td = mpcalc.dewpoint(e) print(td) ########################################### # Which can of course be converted to Fahrenheit print(td.to('degF')) ########################################### # Now do the same thing for 850 mb, approximately the pressure of Denver e = mpcalc.vapor_pressure(850. * units.mbar, mixing) print(e.to(units.mbar)) ########################################### # And print the corresponding dewpoint td = mpcalc.dewpoint(e) print(td, td.to('degF'))
from metpy.vis import meteogram from metpy.constants import C2F from metpy.calc import dewpoint, windchill from metpy.cbook import rec_append_fields fields = ('stid', 'time', 'relh', 'tair', 'wspd', 'wmax', 'wdir', 'pres', 'srad', 'rain') data = read_mesonet_data('data/20080117nrmn.mts', fields, lookup_stids=False) #Add a reasonable time range if we're doing current data end = get_last_time(data) times = (end - datetime.timedelta(hours=24), end) #Calculate dewpoint in F from relative humidity and temperature dewpt = C2F(dewpoint(data['TAIR'], data['RELH']/100.)) data = rec_append_fields(data, ('dewpoint',), (dewpt,)) #Convert temperature and dewpoint to Farenheit mod_units = mesonet_units.copy() mod_units['TAIR'] = 'F' mod_units['dewpoint'] = 'F' data['TAIR'] = C2F(data['TAIR']) #Convert wind speeds to MPH data['WSPD'] *= sconsts.hour / sconsts.mile data['WMAX'] *= sconsts.hour / sconsts.mile mod_units['WSPD'] = 'MPH' mod_units['WMAX'] = 'MPH' #Convert rainfall to inches