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 main(): """Go Main Go""" pgconn = get_dbconn('scan') for station in ['S2004', 'S2196', 'S2002', 'S2072', 'S2068', 'S2031', 'S2001', 'S2047']: df = read_sql(""" select extract(year from valid + '2 months'::interval) as wy, tmpf, dwpf from alldata where station = %s and tmpf is not null and dwpf is not null """, pgconn, params=(station, ), index_col=None) df['mixingratio'] = meteorology.mixing_ratio( temperature(df['dwpf'].values, 'F')).value('KG/KG') df['vapor_pressure'] = mcalc.vapor_pressure( 1000. * units.mbar, df['mixingratio'].values * units('kg/kg')).to(units('kPa')) df['saturation_mixingratio'] = ( meteorology.mixing_ratio( temperature(df['tmpf'].values, 'F')).value('KG/KG')) df['saturation_vapor_pressure'] = mcalc.vapor_pressure( 1000. * units.mbar, df['saturation_mixingratio'].values * units('kg/kg')).to(units('kPa')) df['vpd'] = df['saturation_vapor_pressure'] - df['vapor_pressure'] means = df.groupby('wy').mean() counts = df.groupby('wy').count() for yr, row in means.iterrows(): print(("%s,%s,%.0f,%.3f" ) % (yr, station, counts.at[yr, 'vpd'], row['vpd']))
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 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 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 mixingratio_to_relativehumidity(pressure, temperature, mixing_ratio): actual_vapour_pressure = mpcalc.vapor_pressure( pressure * units['hPa'], mixing_ratio * units['g/kg']).to_base_units() sat_vapour_pressure = mpcalc.saturation_vapor_pressure( temperature * units['degC']).to_base_units() rh = actual_vapour_pressure / sat_vapour_pressure return rh
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 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 run_calcs(df): """Do our maths""" df['mixingratio'] = meteorology.mixing_ratio( temperature(df['dwpf'].values, 'F')).value('KG/KG') df['vapor_pressure'] = mcalc.vapor_pressure( 1000. * units.mbar, df['mixingratio'].values * units('kg/kg')).to(units('kPa')) df['saturation_mixingratio'] = (meteorology.mixing_ratio( temperature(df['tmpf'].values, 'F')).value('KG/KG')) df['saturation_vapor_pressure'] = mcalc.vapor_pressure( 1000. * units.mbar, 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'] = meteorology.dewpoint_from_pq( pressure(1000, 'MB'), mixingratio(df['mixingratio'].values, 'KG/KG')).value('F') return df
def plot(T, p, r, f_in, dir_out): T = T * units.K p = p * units.millibar vp = mpcalc.vapor_pressure(p, r) svp = mpcalc.saturation_vapor_pressure(T) Td = mpcalc.dewpoint_from_relative_humidity(T, vp / svp) Td[np.isnan( Td)] = -99. * units.degree_Celsius # fill nan with very low temp f_in_basename = os.path.basename(f_in) prof.core(p, T, Td, u, v, saveto=dir_out + 'prof_' + f_in_basename + '.png', title=f_in_basename)
def plot_profile(f, fig, skew): ds = xr.open_dataset(f) ds = ds.isel(Time=0) Theta_m = ds.T.mean(areadims) + basetemp pm = (ds.P + ds.PB).mean(areadims) Qvm = (ds.QVAPOR).mean(areadims) U = wrf.destagger(ds.U, 2, meta=True) V = wrf.destagger(ds.V, 1, meta=True) um = U.mean(areadims) vm = V.mean(areadims) Tm = Theta_m * (pm / 1e5)**(2 / 7) p = pm.values / 100. * units.millibar Tm = Tm.values * units.K Qv = Qvm.values vp = mpcalc.vapor_pressure(p, Qv) svp = mpcalc.saturation_vapor_pressure(Tm) #svp[svp<1e-16*units.millibar] = 1e-16 * units.millibar Td = mpcalc.dewpoint_from_relative_humidity(Tm, vp / svp) Td[np.isnan( Td)] = -99. * units.degree_Celsius # fill nan with very low temp # print('Dewpoints: ', Td) # print('Temperatures: ', Tm) u = um.values v = vm.values skew.plot(p, Tm, 'r.-', ms=1, lw=.5, label='mean T') skew.plot(p, Td, 'g.-', ms=5, lw=2, label='mean Td') #skew.plot_barbs(p, u, v) return Tm, Td, p
def plotter(fdict): """ Go """ pgconn = get_dbconn("asos") ctx = get_autoplot_context(fdict, get_description()) station = ctx["zstation"] year = ctx["year"] varname = ctx["var"] df = read_sql( """ SELECT extract(year from valid) as year, coalesce(mslp, alti * 33.8639, 1013.25) as slp, extract(doy from valid) as doy, tmpf, dwpf, relh from alldata where station = %s and dwpf > -50 and dwpf < 90 and tmpf > -50 and tmpf < 120 and valid > '1950-01-01' and report_type = 2 """, pgconn, params=(station, ), index_col=None, ) if df.empty: raise NoDataFound("No Data was found.") # saturation vapor pressure # 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 the mixing ratio df["mixing_ratio"] = 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["mixing_ratio"].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"] dailymeans = df[["year", "doy", varname]].groupby(["year", "doy"]).mean() dailymeans = dailymeans.reset_index() df2 = dailymeans[["doy", varname]].groupby("doy").describe() dyear = df[df["year"] == year] df3 = dyear[["doy", varname]].groupby("doy").describe() df3[(varname, "diff")] = df3[(varname, "mean")] - df2[(varname, "mean")] (fig, ax) = plt.subplots(2, 1, figsize=(8, 6)) multiplier = 1000.0 if varname == "mixing_ratio" else 10.0 ax[0].fill_between( df2[(varname, "min")].index.values, df2[(varname, "min")].values * multiplier, df2[(varname, "max")].values * multiplier, color="gray", ) ax[0].plot( df2[(varname, "mean")].index.values, df2[(varname, "mean")].values * multiplier, label="Climatology", ) ax[0].plot( df3[(varname, "mean")].index.values, df3[(varname, "mean")].values * multiplier, color="r", label="%s" % (year, ), ) ax[0].set_title(("%s [%s]\nDaily Mean Surface %s") % (station, ctx["_nt"].sts[station]["name"], PDICT[varname])) lbl = ("Mixing Ratio ($g/kg$)" if varname == "mixing_ratio" else PDICT[varname]) ax[0].set_ylabel(lbl) ax[0].set_xlim(0, 366) ax[0].set_ylim(bottom=0) ax[0].set_xticks((1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) ax[0].set_xticklabels(calendar.month_abbr[1:]) ax[0].grid(True) ax[0].legend(loc=2, fontsize=10) cabove = "b" if varname == "mixing_ratio" else "r" cbelow = "r" if cabove == "b" else "b" rects = ax[1].bar( df3[(varname, "diff")].index.values, df3[(varname, "diff")].values * multiplier, facecolor=cabove, edgecolor=cabove, ) for rect in rects: if rect.get_height() < 0.0: rect.set_facecolor(cbelow) rect.set_edgecolor(cbelow) plunits = "$g/kg$" if varname == "mixing_ratio" else "hPa" ax[1].set_ylabel("%.0f Departure (%s)" % (year, plunits)) ax[1].set_xlim(0, 366) ax[1].set_xticks((1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) ax[1].set_xticklabels(calendar.month_abbr[1:]) ax[1].grid(True) return fig, df3
e = 6.112 * np.exp((17.67 * T) / (T + 243.5)) * (RH / 100) r = 0.622 * (e) / (p - e) * 1000. # e = mpcalc.vapor_pressure(1000. * units.mbar, mixing) df = pd.DataFrame({ 'pressure': p, 'temperature': T, 'r': r, 'speed': ws, 'direction': wd }) p = df['pressure'].values * units.hPa T = df['temperature'].values * units.degC r = df['r'].values * units('g/kg') e = mpcalc.vapor_pressure(p, r) Td = mpcalc.dewpoint(e) wind_speed = df['speed'].values * units.meter / (units.second) wind_dir = df['direction'].values * units.degrees u, v = mpcalc.wind_components(wind_speed, wind_dir) lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) lfc_pressure, lfc_temperature = mpcalc.lfc(p, T, Td) parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') cape, cin = mpcalc.cape_cin(p, T, Td, parcel_prof) fig = plt.figure(figsize=(12., 9.)) fig.subplots_adjust(top=0.9, bottom=0.1, left=0.05, right=0.96,
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 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)
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)
def test_vapor_pressure(): """Test vapor pressure calculation.""" assert_almost_equal(vapor_pressure(998. * units.mbar, 0.04963), 73.74925 * units.mbar, 5)
vgrd = v[j, 2:29, :, lugar] q = q[j, 2:29, :, lugar] t = t[j, 2:29, :, lugar] omega = w[j, 2:29, :, lugar] ## hay que transformar omega a w: rgas = 287.058 g = 9.80665 vvel = np.zeros([int((omega.shape)[0]), int((omega.shape)[1])]) for ii in range(0, int((omega.shape)[0])): rho = niveles[ii] / (rgas * t[ii, :]) a = -omega[ii, :] / (rho * g) vvel[ii, :] = a ## calculo la temperatura potencial equivalente e = mpcalc.vapor_pressure(1000. * units.mbar, q) td = mpcalc.dewpoint(e) titae = np.zeros([int((omega.shape)[0]), int((omega.shape)[1])]) for iii in range(0, int((omega.shape)[0])): titae[iii, :] = mpcalc.equivalent_potential_temperature( (niveles[iii] * 100) * units.pascal, t[iii, :] * units.kelvin, td[iii, :]) titae = ndimage.gaussian_filter(titae, sigma=1, order=0) ## paso el geopotencial del terreno a presion para poder graficarlo junto a lo demas z_sup = z_sup[0, :, lugar_z] / 9.80665 press = (100000 - (1.2) * 9.81 * (z_sup)) / 100 ####### GRAFICADO ######## if i == 0: fig = plt.figure(figsize=(18, 12))
def plotter(fdict): """ Go """ pgconn = get_dbconn('asos') ctx = get_autoplot_context(fdict, get_description()) station = ctx['zstation'] network = ctx['network'] nt = NetworkTable(network) year = ctx['year'] varname = ctx['var'] df = read_sql(""" SELECT extract(year from valid) as year, coalesce(mslp, alti * 33.8639, 1013.25) as slp, extract(doy from valid) as doy, tmpf, dwpf from alldata where station = %s and dwpf > -50 and dwpf < 90 and tmpf > -50 and tmpf < 120 and valid > '1950-01-01' and report_type = 2 """, pgconn, params=(station, ), index_col=None) # compute RH df['relh'] = mcalc.relative_humidity_from_dewpoint( df['tmpf'].values * units('degF'), df['dwpf'].values * units('degF')) # saturation vapor pressure # 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 the relative humidity df['relh'] = mcalc.relative_humidity_from_dewpoint( df['tmpf'].values * units('degF'), df['dwpf'].values * units('degF')) # Compute the mixing ratio df['mixing_ratio'] = mcalc.mixing_ratio_from_relative_humidity( df['relh'].values, 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['mixing_ratio'].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'] dailymeans = df[['year', 'doy', varname]].groupby(['year', 'doy']).mean() dailymeans = dailymeans.reset_index() df2 = dailymeans[['doy', varname]].groupby('doy').describe() dyear = df[df['year'] == year] df3 = dyear[['doy', varname]].groupby('doy').describe() df3[(varname, 'diff')] = df3[(varname, 'mean')] - df2[(varname, 'mean')] (fig, ax) = plt.subplots(2, 1, figsize=(8, 6)) multiplier = 1000. if varname == 'mixing_ratio' else 10. ax[0].fill_between(df2[(varname, 'min')].index.values, df2[(varname, 'min')].values * multiplier, df2[(varname, 'max')].values * multiplier, color='gray') ax[0].plot(df2[(varname, 'mean')].index.values, df2[(varname, 'mean')].values * multiplier, label="Climatology") ax[0].plot(df3[(varname, 'mean')].index.values, df3[(varname, 'mean')].values * multiplier, color='r', label="%s" % (year, )) ax[0].set_title(("%s [%s]\nDaily Mean Surface %s") % (station, nt.sts[station]['name'], PDICT[varname])) lbl = ("Mixing Ratio ($g/kg$)" if varname == 'mixing_ratio' else PDICT[varname]) ax[0].set_ylabel(lbl) ax[0].set_xlim(0, 366) ax[0].set_ylim(bottom=0) ax[0].set_xticks( (1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 365)) ax[0].set_xticklabels(calendar.month_abbr[1:]) ax[0].grid(True) ax[0].legend(loc=2, fontsize=10) cabove = 'b' if varname == 'mixing_ratio' else 'r' cbelow = 'r' if cabove == 'b' else 'b' rects = ax[1].bar(df3[(varname, 'diff')].index.values, df3[(varname, 'diff')].values * multiplier, facecolor=cabove, edgecolor=cabove) for rect in rects: if rect.get_height() < 0.: rect.set_facecolor(cbelow) rect.set_edgecolor(cbelow) plunits = '$g/kg$' if varname == 'mixing_ratio' else 'hPa' ax[1].set_ylabel("%.0f Departure (%s)" % (year, plunits)) ax[1].set_xlim(0, 366) ax[1].set_xticks( (1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 365)) ax[1].set_xticklabels(calendar.month_abbr[1:]) ax[1].grid(True) return fig, df3
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 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 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)
a value for vapor pressure assuming both 1000mb and 850mb ambient air pressure values. It also demonstrates converting the resulting dewpoint temperature to degrees Fahrenheit. """ import metpy.calc as mcalc from metpy.units import units ########################################### # Create a test value of mixing ratio in grams per kilogram 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'))
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
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[
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"]
a value for vapor pressure assuming both 1000mb and 850mb ambient air pressure values. It also demonstrates converting the resulting dewpoint temperature to degrees Fahrenheit. """ import metpy.calc as mpcalc from metpy.units import units ########################################### # Create a test value of mixing ratio in grams per kilogram 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'))
temp = data.variables['air_temperature'][:] * units('degC') dewp = data.variables['dew_point_temperature'][:] * units('degC') slp = data.variables['inches_ALTIM'][:] * units('inHg') wspd = data.variables['wind_speed'][:] * units('m/s') wdir = data.variables['wind_from_direction'][:] * units('degree') ######################################## # Use MetPy Calculations to calculate RH # -------------------------------------- # Get ambient partial pressure, use to calculate mixing ratio es = mpcalc.saturation_vapor_pressure(dewp) mixr = mpcalc.mixing_ratio(es, slp) # Calculate vapor pressure vp = mpcalc.vapor_pressure(slp, mixr) # Calculate saturation vapor pressure svp = mpcalc.saturation_vapor_pressure(temp) # Calculate relative humidity as a percentage rh = (vp / svp) * 100 ######################################## # Make Meteogram Plot # ------------------- # Create the plots fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, sharex=True, figsize=(12, 10)) ax1.plot(time, temp, ls='solid', marker='o', color='tab:red', ms=5)
df_Temp = pd.DataFrame(T) df_Temp = df_Temp.loc[[index_CP],:].T df_Temp.columns = ['FG_Temperature'] df_Temp = df_Temp[0:int(index_min_pressure)] FG_T_NUCAPS_1 = df_Temp['FG_Temperature'].values -273.15 FG_T_NUCAPS = (df_Temp['FG_Temperature'].values * units.kelvin).to(units.degC) ### MOISTURE # H2O MR var = "H2O_MR" global_scene.load([var], pressure_levels=True) WVMR = global_scene[var].values # mass mixing ratio (mWV / mDA) kg/kg WVMR = WVMR * 1000 # convert to grams WVMR = WVMR * units('g/kg') e_1 = mpcalc.vapor_pressure(p_NUCAPS_orig, WVMR) T_d = mpcalc.dewpoint(e_1) df_Temp_D = pd.DataFrame(T_d) df_Temp_D = df_Temp_D.loc[[index_CP],:].T df_Temp_D.columns = ['Temperature_D'] df_Temp_D = df_Temp_D[0:int(index_min_pressure)] #T_d_NUCAPS_1 = df_Temp_D['Temperature_D'].values -273.15 T_d_NUCAPS = (df_Temp_D['Temperature_D'].values) df_Temp_D = pd.DataFrame(T_d.magnitude) df_Temp_D = df_Temp_D.loc[[index_CP],:].T df_Temp_D.columns = ['Temperature_D'] df_Temp_D = df_Temp_D[0:int(index_min_pressure)] #T_d_NUCAPS_1 = df_Temp_D['Temperature_D'].values -273.15 T_d_NUCAPS_1 = (df_Temp_D['Temperature_D'].values)