def test_storm_relative_helicity(): """Test function for SRH calculations on an eigth-circle hodograph.""" # Create larger arrays for everything except pressure to make a smoother graph hgt_int = np.arange(0, 2050, 50) hgt_int = hgt_int * units('meter') dir_int = np.arange(180, 272.25, 2.25) spd_int = np.zeros((hgt_int.shape[0])) spd_int[:] = 2. u_int, v_int = get_wind_components(spd_int * units('m/s'), dir_int * units.degree) # Put in the correct value of SRH for a eighth-circle, 2 m/s hodograph # (SRH = 2 * area under hodo, in this case...) srh_true_p = (.25 * np.pi * (2**2)) * units('m^2/s^2') # Since there's only positive SRH in this case, total SRH will be equal to positive SRH and # negative SRH will be zero. srh_true_t = srh_true_p srh_true_n = 0 * units('m^2/s^2') p_srh, n_srh, T_srh = storm_relative_helicity(u_int, v_int, hgt_int, 1000 * units('meter'), bottom=0 * units('meter'), storm_u=0 * units.knot, storm_v=0 * units.knot) assert_almost_equal(p_srh, srh_true_p, 2) assert_almost_equal(n_srh, srh_true_n, 2) assert_almost_equal(T_srh, srh_true_t, 2)
def test_storm_relative_helicity_no_storm_motion(): """Test storm relative helicity with no storm motion and differing input units.""" u = np.array([0, 20, 10, 0]) * units('m/s') v = np.array([20, 0, 0, 10]) * units('m/s') u = u.to('knots') heights = np.array([0, 250, 500, 750]) * units.m positive_srh, negative_srh, total_srh = storm_relative_helicity(u, v, heights, depth=750 * units.meters) assert_almost_equal(positive_srh, 400. * units('meter ** 2 / second ** 2 '), 6) assert_almost_equal(negative_srh, -100. * units('meter ** 2 / second ** 2 '), 6) assert_almost_equal(total_srh, 300. * units('meter ** 2 / second ** 2 '), 6)
def test_storm_relative_helicity_with_interpolation(): """Test storm relative helicity with interpolation.""" u = np.array([-5, 15, 25, 15, -5]) * units('m/s') v = np.array([40, 20, 10, 10, 30]) * units('m/s') u = u.to('knots') heights = np.array([0, 100, 200, 300, 400]) * units.m pos_srh, neg_srh, total_srh = storm_relative_helicity(u, v, heights, bottom=50 * units.meters, depth=300 * units.meters, storm_u=5 * units('m/s'), storm_v=10 * units('m/s')) assert_almost_equal(pos_srh, 400. * units('meter ** 2 / second ** 2 '), 6) assert_almost_equal(neg_srh, -100. * units('meter ** 2 / second ** 2 '), 6) assert_almost_equal(total_srh, 300. * units('meter ** 2 / second ** 2 '), 6)
def test_helicity(): """Test function for SRH calculations on an eigth-circle hodograph.""" pres_test = np.asarray([ 1013.25, 954.57955706, 898.690770743, 845.481604002, 794.85264282 ]) * units('hPa') hgt_test = hgt_test = np.asarray([0, 500, 1000, 1500, 2000]) # Create larger arrays for everything except pressure to make a smoother graph hgt_int = np.arange(0, 2050, 50) hgt_int = hgt_int * units('meter') dir_int = np.arange(180, 272.25, 2.25) spd_int = np.zeros((hgt_int.shape[0])) spd_int[:] = 2. u_int, v_int = get_wind_components(spd_int * units('m/s'), dir_int * units.degree) # Interpolate pressure to that graph pres_int = np.interp(hgt_int, hgt_test, np.log(pres_test.magnitude)) pres_int = np.exp(pres_int) * units('hPa') # Put in the correct value of SRH for a eighth-circle, 2 m/s hodograph # (SRH = 2 * area under hodo, in this case...) srh_true_p = (.25 * np.pi * (2**2)) * units('m^2/s^2') # Since there's only positive SRH in this case, total SRH will be equal to positive SRH and # negative SRH will be zero. srh_true_t = srh_true_p srh_true_n = 0 * units('m^2/s^2') p_srh, n_srh, T_srh = storm_relative_helicity(u_int, v_int, pres_int, hgt_int, 1000 * units('meter'), bottom=0 * units('meter'), storm_u=0 * units.knot, storm_v=0 * units.knot) assert_almost_equal(p_srh, srh_true_p, 2) assert_almost_equal(n_srh, srh_true_n, 2) assert_almost_equal(T_srh, srh_true_t, 2)
def storm_relative_helicity(self): sm_u, sm_v = self.bunkers_storm_motion()[2] return mpcalc.storm_relative_helicity(self.u, self.v, self.z, 1000 * units.meter, 0 * units.meter, sm_u, sm_v)
def process_skewt(self): # Calculation index_p100 = get_pressure_level_index(self.p_i, 100) lcl_p, lcl_t = mpcalc.lcl(self.p_i[0], self.t_i[0], self.td_i[0]) lfc_p, lfc_t = mpcalc.lfc(self.p_i, self.t_i, self.td_i) el_p, el_t = mpcalc.el(self.p_i, self.t_i, self.td_i) prof = mpcalc.parcel_profile(self.p_i, self.t_i[0], self.td_i[0]).to('degC') cape, cin = mpcalc.cape_cin(self.p_i, self.t_i, self.td_i, prof) mucape, mucin = mpcalc.most_unstable_cape_cin(self.p_i, self.t_i, self.td_i) pwat = mpcalc.precipitable_water(self.td_i, self.p_i) i8 = get_pressure_level_index(self.p_i, 850) i7 = get_pressure_level_index(self.p_i, 700) i5 = get_pressure_level_index(self.p_i, 500) theta850 = mpcalc.equivalent_potential_temperature(850 * units('hPa'), self.t_i[i8], self.td_i[i5]) theta500 = mpcalc.equivalent_potential_temperature(500 * units('hPa'), self.t_i[i5], self.td_i[i5]) thetadiff = theta850 - theta500 k = self.t_i[i8] - self.t_i[i5] + self.td_i[i8] - (self.t_i[i7] - self.td_i[i7]) a = ((self.t_i[i8] - self.t_i[i5]) - (self.t_i[i8] - self.td_i[i5]) - (self.t_i[i7] - self.td_i[i7]) - (self.t_i[i5] - self.td_i[i5])) sw = c_sweat(np.array(self.t_i[i8].magnitude), np.array(self.td_i[i8].magnitude), np.array(self.t_i[i5].magnitude), np.array(self.u_i[i8].magnitude), np.array(self.v_i[i8].magnitude), np.array(self.u_i[i5].magnitude), np.array(self.v_i[i5].magnitude)) si = showalter_index(self.t_i[i8], self.td_i[i8], self.t_i[i5]) li = lifted_index(self.t_i[0], self.td_i[0], self.p_i[0], self.t_i[i5]) srh_pos, srh_neg, srh_tot = mpcalc.storm_relative_helicity(self.u_i, self.v_i, self.alt, 1000 * units('m')) sbcape, sbcin = mpcalc.surface_based_cape_cin(self.p_i, self.t_i, self.td_i) shr6km = mpcalc.bulk_shear(self.p_i, self.u_i, self.v_i, heights=self.alt, depth=6000 * units('m')) wshr6km = mpcalc.wind_speed(*shr6km) sigtor = mpcalc.significant_tornado(sbcape, delta_height(self.p_i[0], lcl_p), srh_tot, wshr6km)[0] # Plotting self.ax.set_ylim(1050, 100) self.ax.set_xlim(-40, 50) self.plot(self.p_i, self.t_i, 'r', linewidth=1) self.plot(self.p_i[:self.dp_idx], self.td_i[:self.dp_idx], 'g', linewidth=1) self.plot_barbs(self.p_i[:index_p100], self.u_i[:index_p100] * 1.94, self.v_i[:index_p100] * 1.94) self.plot(lcl_p, lcl_t, 'ko', markerfacecolor='black') self.plot(self.p_i, prof, 'k', linewidth=2) if cin.magnitude < 0: chi = -1 * cin.magnitude self.shade_cin(self.p_i, self.t_i, prof) elif cin.magnitude > 0: chi = cin.magnitude self.shade_cin(self.p_i, self.t_i, prof) else: chi = 0. self.shade_cape(self.p_i, self.t_i, prof) self.plot_dry_adiabats(linewidth=0.5) self.plot_moist_adiabats(linewidth=0.5) self.plot_mixing_lines(linewidth=0.5) plt.title('Skew-T Plot \nStation: {} Time: {}'.format(self.st, self.time.strftime('%Y.%m.%d %H:%M')), fontsize=14, loc='left') # Add hodograph ax = self._fig.add_axes([0.95, 0.71, 0.17, 0.17]) h = Hodograph(ax, component_range=50) h.add_grid(increment=20) h.plot_colormapped(self.u_i[:index_p100], self.v_i[:index_p100], self.alt[:index_p100], linewidth=1.2) # Annotate parameters # Annotate names namelist = ['CAPE', 'CIN', 'MUCAPE', 'PWAT', 'K', 'A', 'SWEAT', 'LCL', 'LFC', 'EL', 'SI', 'LI', 'T850-500', 'θse850-500', 'SRH', 'STP'] xcor = -50 ycor = -90 spacing = -9 for nm in namelist: ax.text(xcor, ycor, '{}: '.format(nm), fontsize=10) ycor += spacing # Annotate values varlist = [cape, chi, mucape, pwat, k, a, sw, lcl_p, lfc_p, el_p, si, li, self.t_i[i8] - self.t_i[i5], thetadiff, srh_tot, sigtor] xcor = 10 ycor = -90 for v in varlist: if hasattr(v, 'magnitude'): v = v.magnitude ax.text(xcor, ycor, str(np.round_(v, 2)), fontsize=10) ycor += spacing # Annotate units unitlist = ['J/kg', 'J/kg', 'J/kg', 'mm', '°C', '°C', '', 'hPa', 'hPa', 'hPa', '°C', '°C', '°C', '°C'] xcor = 45 ycor = -90 for u in unitlist: ax.text(xcor, ycor, ' {}'.format(u), fontsize=10) ycor += spacing
def do_profile(cursor, fid, gdf, nt): """Process this profile.""" # The inbound profile may contain mandatory level data that is below # the surface. It seems the best we can do here is to ensure both # temperature and dewpoint are valid and call that the bottom. td_profile = gdf[pd.notnull(gdf["tmpc"]) & pd.notnull(gdf["dwpc"])] wind_profile = gdf[pd.notnull(gdf["u"])] # Presently we are all or nothing here. The length is arb if len(td_profile.index) < 5 or len(wind_profile.index) < 5: msg = ("quorum fail td: %s wind: %s, skipping") % ( len(td_profile.index), len(wind_profile.index), ) raise ValueError(msg) if gdf["pressure"].min() > 500: raise ValueError("Profile only up to %s mb" % (gdf["pressure"].min(), )) # Does a crude check that our metadata station elevation is within 50m # of the profile bottom, otherwise we ABORT station_elevation_m = get_station_elevation(td_profile, nt) # get surface wind u_sfc, v_sfc = get_surface_winds(wind_profile) u_1km, v_1km = get_aloft_winds(wind_profile, station_elevation_m + 1000.0) u_3km, v_3km = get_aloft_winds(wind_profile, station_elevation_m + 3000.0) u_6km, v_6km = get_aloft_winds(wind_profile, station_elevation_m + 6000.0) shear_sfc_1km_smps = np.sqrt((u_1km - u_sfc)**2 + (v_1km - v_sfc)**2) shear_sfc_3km_smps = np.sqrt((u_3km - u_sfc)**2 + (v_3km - v_sfc)**2) shear_sfc_6km_smps = np.sqrt((u_6km - u_sfc)**2 + (v_6km - v_sfc)**2) total_totals = compute_total_totals(td_profile) sweat_index = compute_sweat_index(td_profile, total_totals) try: bunkers_rm, bunkers_lm, mean0_6_wind = bunkers_storm_motion( wind_profile["pressure"].values * units.hPa, wind_profile["u"].values * units("m/s"), wind_profile["v"].values * units("m/s"), wind_profile["height"].values * units("m"), ) except ValueError: # Profile may not go up high enough bunkers_rm = [np.nan * units("m/s"), np.nan * units("m/s")] bunkers_lm = [np.nan * units("m/s"), np.nan * units("m/s")] mean0_6_wind = [np.nan * units("m/s"), np.nan * units("m/s")] bunkers_rm_smps = wind_speed(bunkers_rm[0], bunkers_rm[1]) bunkers_rm_drct = wind_direction(bunkers_rm[0], bunkers_rm[1]) bunkers_lm_smps = wind_speed(bunkers_lm[0], bunkers_lm[1]) bunkers_lm_drct = wind_direction(bunkers_lm[0], bunkers_lm[1]) mean0_6_wind_smps = wind_speed(mean0_6_wind[0], mean0_6_wind[1]) mean0_6_wind_drct = wind_direction(mean0_6_wind[0], mean0_6_wind[1]) try: ( srh_sfc_1km_pos, srh_sfc_1km_neg, srh_sfc_1km_total, ) = storm_relative_helicity( wind_profile["u"].values * units("m/s"), wind_profile["v"].values * units("m/s"), wind_profile["height"].values * units("m"), 1000.0 * units("m"), ) except ValueError: srh_sfc_1km_pos = np.nan * units("m") # blah srh_sfc_1km_neg = np.nan * units("m") # blah srh_sfc_1km_total = np.nan * units("m") # blah try: ( srh_sfc_3km_pos, srh_sfc_3km_neg, srh_sfc_3km_total, ) = storm_relative_helicity( wind_profile["u"].values * units("m/s"), wind_profile["v"].values * units("m/s"), wind_profile["height"].values * units("m"), 3000.0 * units("m"), ) except ValueError: srh_sfc_3km_pos = np.nan * units("m") # blah srh_sfc_3km_neg = np.nan * units("m") # blah srh_sfc_3km_total = np.nan * units("m") # blah pwater = precipitable_water( td_profile["dwpc"].values * units.degC, td_profile["pressure"].values * units.hPa, ) (sbcape, sbcin) = surface_based_cape_cin( td_profile["pressure"].values * units.hPa, td_profile["tmpc"].values * units.degC, td_profile["dwpc"].values * units.degC, ) (mucape, mucin) = most_unstable_cape_cin( td_profile["pressure"].values * units.hPa, td_profile["tmpc"].values * units.degC, td_profile["dwpc"].values * units.degC, ) el_p, el_t = el( td_profile["pressure"].values * units.hPa, td_profile["tmpc"].values * units.degC, td_profile["dwpc"].values * units.degC, ) lfc_p, lfc_t = lfc( td_profile["pressure"].values * units.hPa, td_profile["tmpc"].values * units.degC, td_profile["dwpc"].values * units.degC, ) (lcl_p, lcl_t) = lcl( td_profile["pressure"].values[0] * units.hPa, td_profile["tmpc"].values[0] * units.degC, td_profile["dwpc"].values[0] * units.degC, ) vals = [ el_p.to(units("hPa")).m, lfc_p.to(units("hPa")).m, lcl_p.to(units("hPa")).m, ] [el_hght, lfc_hght, lcl_hght] = log_interp( np.array(vals, dtype="f"), td_profile["pressure"].values[::-1], td_profile["height"].values[::-1], ) el_agl = gt1(el_hght - station_elevation_m) lcl_agl = gt1(lcl_hght - station_elevation_m) lfc_agl = gt1(lfc_hght - station_elevation_m) args = ( nonull(sbcape.to(units("joules / kilogram")).m), nonull(sbcin.to(units("joules / kilogram")).m), nonull(mucape.to(units("joules / kilogram")).m), nonull(mucin.to(units("joules / kilogram")).m), nonull(pwater.to(units("mm")).m), nonull(el_agl), nonull(el_p.to(units("hPa")).m), nonull(el_t.to(units.degC).m), nonull(lfc_agl), nonull(lfc_p.to(units("hPa")).m), nonull(lfc_t.to(units.degC).m), nonull(lcl_agl), nonull(lcl_p.to(units("hPa")).m), nonull(lcl_t.to(units.degC).m), nonull(total_totals), nonull(sweat_index), nonull(bunkers_rm_smps.m), nonull(bunkers_rm_drct.m), nonull(bunkers_lm_smps.m), nonull(bunkers_lm_drct.m), nonull(mean0_6_wind_smps.m), nonull(mean0_6_wind_drct.m), nonull(srh_sfc_1km_pos.m), nonull(srh_sfc_1km_neg.m), nonull(srh_sfc_1km_total.m), nonull(srh_sfc_3km_pos.m), nonull(srh_sfc_3km_neg.m), nonull(srh_sfc_3km_total.m), nonull(shear_sfc_1km_smps), nonull(shear_sfc_3km_smps), nonull(shear_sfc_6km_smps), fid, ) cursor.execute( """ UPDATE raob_flights SET sbcape_jkg = %s, sbcin_jkg = %s, mucape_jkg = %s, mucin_jkg = %s, pwater_mm = %s, el_agl_m = %s, el_pressure_hpa = %s, el_tmpc = %s, lfc_agl_m = %s, lfc_pressure_hpa = %s, lfc_tmpc = %s, lcl_agl_m = %s, lcl_pressure_hpa = %s, lcl_tmpc = %s, total_totals = %s, sweat_index = %s, bunkers_rm_smps = %s, bunkers_rm_drct = %s, bunkers_lm_smps = %s, bunkers_lm_drct = %s, mean_sfc_6km_smps = %s, mean_sfc_6km_drct = %s, srh_sfc_1km_pos = %s, srh_sfc_1km_neg = %s, srh_sfc_1km_total = %s, srh_sfc_3km_pos = %s, srh_sfc_3km_neg = %s, srh_sfc_3km_total = %s, shear_sfc_1km_smps = %s, shear_sfc_3km_smps = %s, shear_sfc_6km_smps = %s, computed = 't' WHERE fid = %s """, args, )