Esempio n. 1
1
def run_post(conf):

  FIXdir = conf.get("DEFAULT","FIXbwrf")

  sfc_switch = conf.getint("post","plot_sfc")
  cloud_switch = conf.getint("post","plot_clouds")
  sfcdiags_switch = conf.getint("post","plot_sfcdiags")
  xcdiags_switch = conf.getint("post","plot_xcdiags")
  switch_700mb = conf.getint("post","plot_700mb")
  switch_500mb = conf.getint("post","plot_500mb")
  switch_300mb = conf.getint("post","plot_300mb")

#  sfc_switch = 0
#  cloud_switch = 0
#  sfcdiags_switch = 0
#  xcdiags_switch = 0
#  switch_700mb = 0
#  switch_500mb = 0
#  switch_300mb = 0

  blat = conf.getfloat("post","blat")
  blon = conf.getfloat("post","blon")

  dx = conf.getfloat("wrf","dx")

  POSTwork = conf.get("DEFAULT","POSTwork")
  os.chdir(POSTwork)

  file_wrf = glob.glob("wrfout*")[0]
  print("Processing "+file_wrf)
  init_time = file_wrf[11:21]+" "+file_wrf[22:24]+"Z"

# Download the states and coastlines
  states = cfeature.NaturalEarthFeature(category='cultural', scale='50m', facecolor='none',
                             name='admin_1_states_provinces_shp')
# Get counties.
  reader = shpreader.Reader(FIXdir+'/shapefiles/countyp010g.shp')
  counties = list(reader.geometries())
  COUNTIES = cfeature.ShapelyFeature(counties, crs.PlateCarree())

# Open the NetCDF file
  ncfile = Dataset(file_wrf)

# Get the times and convert to datetimes
  times = getvar(ncfile, "Times", timeidx=ALL_TIMES, meta=False)
  dtimes = [datetime.strptime(str(atime), '%Y-%m-%dT%H:%M:%S.000000000') for atime in times]
  numTimes = len(times)

# Reflectivity
  ref = getvar(ncfile, "REFL_10CM", timeidx=ALL_TIMES)
  ref[0,0,0,0]=-21.0 # hack to plot a blank contour plot at the initial time

# Get upper-air quantities.
  p = getvar(ncfile, "pressure", timeidx=ALL_TIMES)
  z = getvar(ncfile, "z", units="dm", timeidx=ALL_TIMES)
  u, v = getvar(ncfile, "uvmet", units="kt", timeidx=ALL_TIMES, meta=False)
  w = getvar(ncfile, "wa", units="m s-1", timeidx=ALL_TIMES)*100. # m/s to cm/s
  rh = getvar(ncfile, "rh", timeidx=ALL_TIMES)
  wspeed = (u**2.0+v**2.0)**0.5
  tc = getvar(ncfile, "tc", timeidx=ALL_TIMES)
  dewT = getvar(ncfile, "td", units="degC", timeidx=ALL_TIMES)
  cloudfrac = getvar(ncfile, "cloudfrac", low_thresh=20., 
                     mid_thresh=955., high_thresh=4500., timeidx=ALL_TIMES)
  total_cloudfrac=np.max(cloudfrac,axis=0)
  low_cloudfrac = cloudfrac[0,:,:,:]
  mid_cloudfrac = cloudfrac[1,:,:,:]
  high_cloudfrac = cloudfrac[2,:,:,:]

# Interpolate
  z_500 = smooth2d(interplevel(z, p, 500), 3)
  tc_500 = smooth2d(interplevel(tc, p, 500), 3)
  u_500 = interplevel(u, p, 500)
  v_500 = interplevel(v, p, 500)
  wspeed_500 = interplevel(wspeed, p, 500)

  z_300 = smooth2d(interplevel(z, p, 300), 3)
  u_300 = interplevel(u, p, 300)
  v_300 = interplevel(v, p, 300)

  z_700 = interplevel(z, p, 700)
  tc_700 = interplevel(tc, p, 700)
  u_700 = interplevel(u, p, 700)
  v_700 = interplevel(v, p, 700)
  w_700 = interplevel(w, p, 700)
  rh_700 = interplevel(rh, p, 700)

  if switch_700mb == 1:

# Interpolate over NaNs.

    for itime in range(numTimes):

      z_700[itime,:,:] = interpnan(z_700[itime,:,:])
      tc_700[itime,:,:] = interpnan(tc_700[itime,:,:])
      rh_700[itime,:,:] = interpnan(rh_700[itime,:,:])
      u_700[itime,:,:] = interpnan(u_700[itime,:,:])
      v_700[itime,:,:] = interpnan(v_700[itime,:,:])
      w_700[itime,:,:] = interpnan(w_700[itime,:,:])

    z_700 = smooth2d(z_700, 3)
    tc_700 = smooth2d(tc_700, 3)
    w_700 = smooth2d(w_700, 3)

  div_300 = smooth2d(divergence([u_300*0.514444, v_300*0.514444], dx), 3) # kt to m/s

# Get the sea level pressure
  slp = getvar(ncfile, "slp", timeidx=ALL_TIMES)

# Get the wet bulb temperature
  twb = getvar(ncfile, "twb", units="degC", timeidx=ALL_TIMES)

  slp_levels=np.arange(980,1040,2)
  z_levels=np.arange(504,620,3)
  z_levels_300=np.arange(804,996,6)
  z_levels_700=np.arange(285,351,3)
  t2_levels=np.arange(-20,125,5)
  tc_levels=np.arange(-40,30,2)
  wspeed_levels=np.arange(40,150,10)
  ref_levels=np.arange(-20,60,5)
  rh_levels=np.arange(70,105,5)
  cldfrac_levels=np.arange(0.,1.1,0.1)
  twb_levels=np.arange(0,1,1)

  wup_levels=np.arange(5,55,10)
  wdown_levels=np.arange(-55,-5,10)

  div_levels=np.arange(-110,120,10)

# Get the 10-m u and v wind components.
  u_10m, v_10m = getvar(ncfile, "uvmet10", units="kt", timeidx=ALL_TIMES)
  wind_10m = (u_10m**2.0+v_10m**2.0)**0.5

# Smooth the sea level pressure since it tends to be noisy near the mountains
  smooth_slp = smooth2d(slp, 3)
  twb=twb[:,0,:,:] # lowest model level
  twb=smooth2d(twb,3)

# Get the latitude and longitude points
  lats, lons = latlon_coords(slp)

# Get the cartopy mapping object
  cart_proj = get_cartopy(slp)

  if sfcdiags_switch == 1:

    bx, by = ll_to_xy(ncfile, blat, blon, meta=False, as_int=True)

#   Create a figure
    fig = plt.figure(figsize=(12,9))
    fileout = "precip.png"

  # should be variable ACSNOW
  #  accumulated_snow = getvar(ncfile, "SNOWNC", timeidx=ALL_TIMES)

    liq_equiv = (getvar(ncfile, "RAINC", timeidx=ALL_TIMES) +
                 getvar(ncfile, "RAINNC", timeidx=ALL_TIMES))/25.4 # mm to inches

    liq_equiv_bdu = liq_equiv[:,by,bx]

    prate = np.zeros(numTimes)

    for itime in range(numTimes):

      if itime > 0 and itime < numTimes-1:
        prate[itime] = 0.5*((liq_equiv_bdu[itime+1]+liq_equiv_bdu[itime]) - 
                            (liq_equiv_bdu[itime-1]+liq_equiv_bdu[itime]))
      elif itime == 0:
        prate[0] = liq_equiv_bdu[0] 
      else:
        prate[numTimes-1] = liq_equiv_bdu[numTimes-1]-liq_equiv_bdu[numTimes-2]

    plt.bar(times, prate, width=0.0425, color="black")
    plt.plot(times, liq_equiv_bdu, color="blue")
    plt.xlim(min(times), max(times))
    plt.title("Forecast Precipitation: Boulder, CO")
    plt.xlabel("Time (UTC)")
    plt.ylabel("Running total (line) and rate (bars, inches of liquid)")

    plt.grid(b=True, which="major", axis="both", color="gray", linestyle="--")

    fig.savefig("sfcdiags/"+fileout,bbox_inches='tight')
    plt.close(fig)

#   Create a figure
    fig, ax1 = plt.subplots(figsize=(12,9))
    fileout = "t2m_td2m_wind10m_prate.png"

    t2m = 1.8*(getvar(ncfile, "T2", timeidx=ALL_TIMES)-273.15)+32.
    td2m = getvar(ncfile, "td2", units="degF", timeidx=ALL_TIMES)

    color = 'tab:blue'
    ax1.bar(times, prate, width=0.0425, color="blue")
    ax1.plot(times, liq_equiv_bdu, color="blue")
    ax1.set_ylim(0.,max([max(prate)/0.1+0.01,max(liq_equiv_bdu)+0.1]))
    ax1.set_xlabel("Time (UTC)")
    ax1.set_ylabel("Precipitation liquid amount (in) and rate (bars, in/hr)", color=color)
    ax1.tick_params(axis="y", labelcolor=color)
    ax1.grid(b=True, which="major", axis="x", color="gray", linestyle="--")

    ax2=ax1.twinx()
    ax2.plot(times, wind_10m[:,by,bx], color="black")
    ax2.barbs(times, wind_10m[:,by,bx], u_10m[:,by,bx], v_10m[:,by,bx])

    ax2.plot(times, t2m[:,by,bx], color="red")
    ax2.plot(times, td2m[:,by,bx], color="green")

    ax2.set_xlim(min(times), max(times))
    plt.title("Forecast near-surface variables: Boulder, CO")
    ax2.set_ylabel("2-m T (red) and 2-m Td (green, degF), 10-m wind (black, kt)")

    ax2.grid(b=True, which="major", axis="y", color="gray", linestyle="--")

    fig.tight_layout()
    fig.savefig("sfcdiags/"+fileout,bbox_inches='tight')
    plt.close(fig)

  if xcdiags_switch == 1:

    zinterp = np.arange(550, 880, 10)

    u_xc = vertcross(u, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    w_xc = vertcross(w, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    tc_xc = vertcross(tc, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    rh_xc = vertcross(rh, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    nx = np.shape(u_xc)[-1]
    xinterp = np.arange(0,nx,1)

    bx, by = ll_to_xy(ncfile, blat, blon, meta=False, as_int=True)

    for itime in range(numTimes):

#     Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "mtnwave_xc"+str(itime).zfill(2)+".png"

      ax=plt.gca()
      ax.set_facecolor("black")

      plt.contourf(xinterp, zinterp, u_xc[itime,:,:], 
        cmap=get_cmap("seismic"), levels=np.arange(-50,55,5))
      plt.colorbar(shrink=.62)

      w_contour = plt.contour(xinterp, zinterp, w_xc[itime,:,:],
        "--", levels=np.arange(-120,-20,20),colors="black")
      plt.clabel(w_contour, inline=1, fontsize=10, fmt="%i")

      w_contour = plt.contour(xinterp, zinterp, w_xc[itime,:,:],
        levels=np.arange(20,120,20),colors="black")
      plt.clabel(w_contour, inline=1, fontsize=10, fmt="%i")

      t_contour = plt.contour(xinterp, zinterp, tc_xc[itime,:,:],
        levels=[-20,-10,0], colors="yellow")
      plt.clabel(t_contour, inline=1, fontsize=10, fmt="%i")

#     Add location of Boulder to plot.
      plt.scatter(bx,np.max(zinterp),c='r',marker='+')

      plt.ylim([np.max(zinterp),np.min(zinterp)])
#      plt.yscale("log")
#      plt.xticks([np.arange(900,475,-25)], ["900", "875", 
#        "850", "825", "800", "775", "750", "725", "700", "675", "650",
#        "625", "600", "575", "550", "525", "500"])

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": zonal wind (fill, kt), temp (yellow lines, degC), VV (black lines, cm/s)")

      fig.savefig("xcdiags/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background xcdiags/mtnwave_xc*.png -loop 0 xcdiags/mtnwave_xc.gif")

    for itime in range(numTimes):

#     Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "mtnwave_xc_rh"+str(itime).zfill(2)+".png"

      ax=plt.gca()
      ax.set_facecolor("black")

      plt.contourf(xinterp, zinterp, rh_xc[itime,:,:],
        cmap=get_cmap("Greens"), levels=rh_levels, extend='both')
      plt.colorbar(shrink=.62)

      skip=2
      plt.quiver(xinterp[::skip], zinterp[::skip], u_xc[itime,::skip,::skip], w_xc[itime,::skip,::skip]/2.,
        scale=500, headwidth=3, color='black', pivot='middle')

      t_contour = plt.contour(xinterp, zinterp, tc_xc[itime,:,:],
        levels=[-20,-10,0], colors="yellow")
      plt.clabel(t_contour, inline=1, fontsize=10, fmt="%i")

#     Add location of Boulder to plot.
      plt.scatter(bx,np.max(zinterp),c='r',marker='+')

      plt.ylim([np.max(zinterp),np.min(zinterp)])

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": rh (fill), temp (yellow lines, degC), wind (arrows)")

      fig.savefig("xcdiags_rh/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background xcdiags_rh/mtnwave_xc_rh*.png -loop 0 xcdiags_rh/mtnwave_xc_rh.gif")

    zinterp = np.arange(150, 900, 25)

    rh_xc = vertcross(rh, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    tc_xc = vertcross(tc, p, levels=zinterp, wrfin=ncfile,
      stagger='u', pivot_point=CoordPair(lat=blat,lon=blon), angle=90., meta=False)

    nx = np.shape(u_xc)[-1]
    xinterp = np.arange(0,nx,1)

    bx, by = ll_to_xy(ncfile, blat, blon, meta=False, as_int=True)

    for itime in range(numTimes):

#     Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "mtnwave_xc_rh_big"+str(itime).zfill(2)+".png"

      ax=plt.gca()
      ax.set_facecolor("black")

      plt.contourf(xinterp, zinterp, rh_xc[itime,:,:],
        cmap=get_cmap("Greens"), levels=rh_levels, extend='both')
      plt.colorbar(shrink=.62)

      t_contour = plt.contour(xinterp, zinterp, tc_xc[itime,:,:],
        levels=[-20,-10,0], colors="yellow")
      plt.clabel(t_contour, inline=1, fontsize=10, fmt="%i")

#     Add location of Boulder to plot.
      plt.scatter(bx,np.max(zinterp),c='r',marker='+')

      plt.ylim([np.max(zinterp),np.min(zinterp)])

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": rh (fill), temp (yellow lines, degC)")

      fig.savefig("xcdiags_rh_big/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background xcdiags_rh_big/mtnwave_xc_rh_big*.png -loop 0 xcdiags_rh_big/mtnwave_xc_rh_big.gif")

  if sfc_switch == 1:

    bx, by = ll_to_xy(ncfile, blat, blon, meta=False, as_int=True)

    for itime in range(numTimes):

      ps = p[itime,:,by,bx]
      ps_temp = ps
      T = tc[itime,:,by,bx]
      Td = dewT[itime,:,by,bx]
      us = u[itime,:,by,bx]
      vs = v[itime,:,by,bx]

      ps = ps[ps_temp >= 100.]
      T = T[ps_temp >= 100.]
      Td = Td[ps_temp >= 100.]
      us = us[ps_temp >= 100.]
      vs = vs[ps_temp >= 100.]

      fig = plt.figure(figsize=(9, 9))
      skew = SkewT(fig, rotation=45)

      fileout = "sounding"+str(itime).zfill(2)+".png"

#     Plot the data using normal plotting functions, in this case using
#     log scaling in Y, as dictated by the typical meteorological plot.
      skew.plot(ps, T, 'r')
      skew.plot(ps, Td, 'g')
      skew.plot_barbs(ps, us, vs)
      skew.ax.set_ylim(1000, 100)
      skew.ax.set_xlim(-40, 60)

#     Calculate LCL height and plot as black dot. Because `p`'s first value is
#     ~1000 mb and its last value is ~250 mb, the `0` index is selected for
#     `p`, `T`, and `Td` to lift the parcel from the surface. If `p` was inverted,
#     i.e. start from low value, 250 mb, to a high value, 1000 mb, the `-1` index
#     should be selected.
      lcl_pressure, lcl_temperature = mpcalc.lcl(ps[0], T[0], Td[0])
      skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black')

#     Calculate full parcel profile and add to plot as black line
      prof = mpcalc.parcel_profile(ps, T[0], Td[0]).to('degC')
      skew.plot(ps, prof, 'k', linewidth=2)

#     Shade areas of CAPE and CIN
#      skew.shade_cin(ps, T, prof, Td)
      [cape, cin] = mpcalc.cape_cin(ps, T, Td, prof)
#      skew.shade_cape(ps, T, prof)

#     An example of a slanted line at constant T -- in this case the 0
#     isotherm
      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_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": Boulder, CO, CAPE: "+str(round(cape.magnitude))+" J/kg CIN: "+str(round(cin.magnitude))+" J/kg")
      fig.savefig("sounding/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background sounding/sounding*.png -loop 0 sounding/sounding.gif")

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "sfc_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   Reflectivity at the lowest model level.
      plt.contourf(to_np(lons), to_np(lats), to_np(ref[itime,0,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("jet"), levels=ref_levels)

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Contour the wetbulb temperature at 0 degC.
      c_p = plt.contour(to_np(lons), to_np(lats), to_np(twb[itime,:,:]), transform=crs.PlateCarree(),
             colors="red", levels=twb_levels)
      plt.clabel(c_p, inline=1, fontsize=10, fmt="%i")

#   Make the contour outlines and filled contours for the smoothed sea level pressure.
      c_p = plt.contour(to_np(lons), to_np(lats), to_np(smooth_slp[itime,:,:]), transform=crs.PlateCarree(),
             colors="black", levels=slp_levels)
      plt.clabel(c_p, inline=1, fontsize=10, fmt="%i")

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Add the 10-m wind barbs, only plotting every other data point.
      skip=2
      plt.barbs(to_np(lons[::skip,::skip]), to_np(lats[::skip,::skip]), to_np(u_10m[itime, ::skip, ::skip]),
          to_np(v_10m[itime, ::skip, ::skip]), transform=crs.PlateCarree(), length=5.25, linewidth=0.5)

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(smooth_slp))
      ax.set_ylim(cartopy_ylim(smooth_slp))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": SLP (fill, hPa), 10-m wind (barbs, kt), LML Ref (fill, dBZ), LML WBT (red, degC)")
      fig.savefig("sfc/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background sfc/sfc*.png -loop 0 sfc/sfc.gif")

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "sfc_temp_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   2-m air temperature
      t2 = 1.8*(getvar(ncfile, "T2", timeidx=ALL_TIMES) - 273.15)+32.
      plt.contourf(to_np(lons), to_np(lats), to_np(t2[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("jet"), levels=t2_levels)

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Make the contour outlines and filled contours for the smoothed sea level pressure.
      c_p = plt.contour(to_np(lons), to_np(lats), to_np(smooth_slp[itime,:,:]), transform=crs.PlateCarree(),
             colors="black", levels=slp_levels)
      plt.clabel(c_p, inline=1, fontsize=10, fmt="%i")

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Add the 10-m wind barbs, only plotting every other data point.
      skip=2
      plt.barbs(to_np(lons[::skip,::skip]), to_np(lats[::skip,::skip]), to_np(u_10m[itime, ::skip, ::skip]),
          to_np(v_10m[itime, ::skip, ::skip]), transform=crs.PlateCarree(), length=5.25, linewidth=0.5)

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(smooth_slp))
      ax.set_ylim(cartopy_ylim(smooth_slp))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": SLP (contours, hPa), 10-m wind (barbs, kt), 2-m T (fill, degF)")
      fig.savefig("sfc_temp/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background sfc_temp/sfc_temp*.png -loop 0 sfc_temp/sfc_temp.gif")

  if cloud_switch == 1:

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "cldfrac_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   Compute and plot the total cloud fraction.
      plt.contourf(to_np(lons), to_np(lats), to_np(total_cloudfrac[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("Greys"), levels=cldfrac_levels, extend='both')

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(cloudfrac))
      ax.set_ylim(cartopy_ylim(cloudfrac))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": Total cloud fraction (fill)")
      fig.savefig("cldfrac/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background cldfrac/cldfrac*.png -loop 0 cldfrac/cldfrac.gif")

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "low_cldfrac_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   Compute and plot the cloud fraction.
      plt.contourf(to_np(lons), to_np(lats), to_np(low_cloudfrac[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("Greys"), levels=cldfrac_levels, extend='both')

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(cloudfrac))
      ax.set_ylim(cartopy_ylim(cloudfrac))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": Low cloud fraction (fill)")
      fig.savefig("low_cldfrac/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background low_cldfrac/low_cldfrac*.png -loop 0 low_cldfrac/low_cldfrac.gif")

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "mid_cldfrac_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   Compute and plot the cloud fraction.
      plt.contourf(to_np(lons), to_np(lats), to_np(mid_cloudfrac[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("Greys"), levels=cldfrac_levels, extend='both')

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(cloudfrac))
      ax.set_ylim(cartopy_ylim(cloudfrac))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": Mid cloud fraction (fill)")
      fig.savefig("mid_cldfrac/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background mid_cldfrac/mid_cldfrac*.png -loop 0 mid_cldfrac/mid_cldfrac.gif")

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "high_cldfrac_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   Compute and plot the cloud fraction.
      plt.contourf(to_np(lons), to_np(lats), to_np(high_cloudfrac[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("Greys"), levels=cldfrac_levels, extend='both')

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(cloudfrac))
      ax.set_ylim(cartopy_ylim(cloudfrac))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": High cloud fraction (fill)")
      fig.savefig("high_cldfrac/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background high_cldfrac/high_cldfrac*.png -loop 0 high_cldfrac/high_cldfrac.gif")

  if switch_500mb == 1:

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "500mb_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   wind color fill
      if np.max(wspeed_500[itime,:,:]) > np.min(wspeed_levels):
        plt.contourf(to_np(lons), to_np(lats), to_np(wspeed_500[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("rainbow"), levels=wspeed_levels)

#   Make the 500 mb height contours.
      c_z = plt.contour(to_np(lons), to_np(lats), to_np(z_500[itime,:,:]), transform=crs.PlateCarree(),
             colors="black", levels=z_levels)
      plt.clabel(c_z, inline=1, fontsize=10, fmt="%i")

#   Make the 500 mb temp contours.
      c_t = plt.contour(to_np(lons), to_np(lats), to_np(tc_500[itime,:,:]), transform=crs.PlateCarree(),
             colors="red", levels=tc_levels)
      plt.clabel(c_t, inline=1, fontsize=10, fontcolor="red", fmt="%i")

#   Add a color bar
#   plt.colorbar(ax=ax, shrink=.62)

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Add the 500mb wind barbs, only plotting every other data point.
      skip=3
      plt.barbs(to_np(lons[::skip,::skip]), to_np(lats[::skip,::skip]), to_np(u_500[itime, ::skip, ::skip]),
          to_np(v_500[itime, ::skip, ::skip]), transform=crs.PlateCarree(), length=5.25, linewidth=0.5)

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(z_500))
      ax.set_ylim(cartopy_ylim(z_500))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": 500-mb height (black, dm), temp (red, degC), and wind (fill/barbs, kt)")
      fig.savefig("500mb/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background 500mb/500mb*.png -loop 0 500mb/500mb.gif")

  if switch_700mb == 1:

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "700mb_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   rh color fill
      plt.contourf(to_np(lons), to_np(lats), to_np(rh_700[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("Greens"), levels=rh_levels, extend='both')

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Make the 700 mb height contours.
      c_z = plt.contour(to_np(lons), to_np(lats), to_np(z_700[itime,:,:]), transform=crs.PlateCarree(),
             colors="black", levels=z_levels_700)
      plt.clabel(c_z, inline=1, fontsize=10, fmt="%i")

#   Make the 700 mb temp contours.
      c_t = plt.contour(to_np(lons), to_np(lats), to_np(tc_700[itime,:,:]), transform=crs.PlateCarree(),
             colors="red", levels=tc_levels)
      plt.clabel(c_t, inline=1, fontsize=10, fontcolor="red", fmt="%i")

#   Make the 700 mb VV contours.
      c_d = plt.contour(to_np(lons), to_np(lats), to_np(w_700[itime,:,:]), transform=crs.PlateCarree(),
             colors="magenta", levels=wup_levels, linewidths=0.9)
      plt.clabel(c_d, inline=1, fontsize=10, fontcolor="magenta", fmt="%i")

      c_c = plt.contour(to_np(lons), to_np(lats), to_np(w_700[itime,:,:]), transform=crs.PlateCarree(),
             colors="blue", levels=wdown_levels, linewidths=0.9)
      plt.clabel(c_c, inline=1, fontsize=10, fontcolor="blue", fmt="%i")

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Add the 700mb wind barbs, only plotting every other data point.
      skip=3
      plt.barbs(to_np(lons[::skip,::skip]), to_np(lats[::skip,::skip]), to_np(u_700[itime, ::skip, ::skip]),
          to_np(v_700[itime, ::skip, ::skip]), transform=crs.PlateCarree(), length=5.25, linewidth=0.5)

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(z_700))
      ax.set_ylim(cartopy_ylim(z_700))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": 700-mb hgt (black, dm), T (red, degC), wind (barbs, kt), VV (cm/s), rh (fill, %)")
      fig.savefig("700mb/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background 700mb/700mb*.png -loop 0 700mb/700mb.gif")

  if switch_300mb == 1:

    for itime in range(numTimes):

#   Create a figure
      fig = plt.figure(figsize=(12,9))
      fileout = "300mb_fhr"+str(itime).zfill(2)+".png"

#   Set the GeoAxes to the projection used by WRF
      ax = plt.axes(projection=cart_proj)

#   Add the states and coastlines
      ax.add_feature(states, linewidth=0.8, edgecolor='gray')
      ax.add_feature(COUNTIES, linewidth=0.4, facecolor='none', edgecolor='gray')
      ax.coastlines('50m', linewidth=0.8)

#   300 mb divergence
      plt.contourf(to_np(lons), to_np(lats), to_np(div_300[itime,:,:]), transform=crs.PlateCarree(),
             cmap=get_cmap("seismic"), levels=div_levels)

#   Add a color bar
      plt.colorbar(ax=ax, shrink=.62)

#   Make the 300 mb height contours.
      c_z = plt.contour(to_np(lons), to_np(lats), to_np(z_300[itime,:,:]), transform=crs.PlateCarree(),
             colors="black", levels=z_levels_300)
      plt.clabel(c_z, inline=1, fontsize=10, fmt="%i")

#   Make the 300 mb divergence contours.
#      c_d = plt.contour(to_np(lons), to_np(lats), to_np(div_300[itime,:,:]), transform=crs.PlateCarree(),
#             colors="red", levels=div_levels, linewidths=0.8)
#      plt.clabel(c_d, inline=1, fontsize=10, fontcolor="red", fmt="%i")
#
#      c_c = plt.contour(to_np(lons), to_np(lats), to_np(div_300[itime,:,:]), transform=crs.PlateCarree(),
#             colors="blue", levels=conv_levels, linewidths=0.8)
#      plt.clabel(c_c, inline=1, fontsize=10, fontcolor="blue", fmt="%i")

#   Add location of Boulder to plot.
      plt.scatter(blon,blat,c='r',marker='+',transform=crs.PlateCarree())

#   Add the 300mb wind barbs, only plotting every other data point.
      skip=3
      plt.barbs(to_np(lons[::skip,::skip]), to_np(lats[::skip,::skip]), to_np(u_300[itime, ::skip, ::skip]),
          to_np(v_300[itime, ::skip, ::skip]), transform=crs.PlateCarree(), length=5.25, linewidth=0.5)

#   Set the map limits.
      ax.set_xlim(cartopy_xlim(z_300))
      ax.set_ylim(cartopy_ylim(z_300))

      plt_time=str(dtimes[itime])
      plt_time=plt_time[0:13]

      plt.title(plt_time+"Z fhr "+str(itime).zfill(2)+": 300-mb height (black, dm), wind (barbs, kt), and divergence x 10^5 (red/blue, s^-1)")
      fig.savefig("300mb/"+fileout,bbox_inches='tight')
      plt.close(fig)

    os.system("convert -delay 90 -dispose background 300mb/300mb*.png -loop 0 300mb/300mb.gif")
Esempio n. 2
0
    def calculate_basic_thermo(self):

        #Enclose in try, except because not every sounding will have a converging parcel path or CAPE.
        try:

            #Precipitable Water
            self.pw = mc.precipitable_water(self.sounding["pres"],
                                            self.sounding["dewp"])

            #Lifting condensation level
            self.lcl_pres, self.lcl_temp = mc.lcl(self.sounding["pres"][0],
                                                  self.sounding["temp"][0],
                                                  self.sounding["dewp"][0])

            #Surface-based CAPE and CIN
            self.parcel_path = mc.parcel_profile(self.sounding["pres"],
                                                 self.sounding["temp"][0],
                                                 self.sounding["dewp"][0])
            self.sfc_cape, self.sfc_cin = mc.cape_cin(self.sounding["pres"],
                                                      self.sounding["temp"],
                                                      self.sounding["dewp"],
                                                      self.parcel_path)

        #Do this when parcel path fails to converge
        except Exception as e:
            print("WARNING: No LCL, CAPE, or PW stats because:\n{}.".format(e))
            self.parcel_path = numpy.nan
            self.pw = numpy.nan
            self.lcl_pres = numpy.nan
            self.lcl_temp = numpy.nan
            self.sfc_cape = numpy.nan
            self.sfc_cin = numpy.nan

        #Returning
        return
def get_skewt_vars(p, tc, tdc, pro):
    """This function processes the dataset values and returns a string element
    which can be used as a subtitle to replicate the styles of NCL Skew-T
    Diagrams.

    Args:

        p (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Pressure level input from dataset

        tc (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Temperature for parcel from dataset

        tdc (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Dew point temperature for parcel from dataset

        pro (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Parcel profile temperature converted to degC


    Returns:
        :class: 'str'
    """

    # CAPE
    cape = mpcalc.cape_cin(p, tc, tdc, pro)
    cape = cape[0].magnitude

    # Precipitable Water
    pwat = mpcalc.precipitable_water(p, tdc)
    pwat = (pwat.magnitude / 10) * units.cm  # Convert mm to cm
    pwat = pwat.magnitude

    # Pressure and temperature of lcl
    lcl = mpcalc.lcl(p[0], tc[0], tdc[0])
    plcl = lcl[0].magnitude
    tlcl = lcl[1].magnitude

    # Showalter index
    shox = showalter_index(p, tc, tdc)
    shox = shox[0].magnitude

    # Place calculated values in iterable list
    vals = [plcl, tlcl, shox, pwat, cape]
    vals = [round(num) for num in vals]

    # Define variable names for calculated values
    names = ['Plcl=', 'Tlcl[C]=', 'Shox=', 'Pwat[cm]=', 'Cape[J]=']

    # Combine the list of values with their corresponding labels
    lst = list(chain.from_iterable(zip(names, vals)))
    lst = map(str, lst)

    # Create one large string for later plotting use
    joined = ' '.join(lst)

    return joined
Esempio n. 4
0
def test_cape_cin():
    """Tests the basic CAPE and CIN calculation."""
    p = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
    temperature = np.array([22.2, 14.6, 12., 9.4, 7., -38.]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0])
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 58.0368212 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, -89.8073512 * units('joule / kilogram'), 6)
Esempio n. 5
0
def test_cape_cin_no_el():
    """Tests that CAPE works with no EL."""
    p = np.array([959., 779.2, 751.3, 724.3]) * units.mbar
    temperature = np.array([22.2, 14.6, 12., 9.4]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0]).to('degC')
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 0.08750805 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, -89.8073512 * units('joule / kilogram'), 6)
Esempio n. 6
0
def test_cape_cin_no_lfc():
    """Test that CAPE is zero with no LFC."""
    p = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
    temperature = np.array([22.2, 24.6, 22., 20.4, 18., -10.]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0]).to('degC')
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 0.0 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, 0.0 * units('joule / kilogram'), 6)
Esempio n. 7
0
def test_cape_cin_no_el():
    """Test that CAPE works with no EL."""
    p = np.array([959., 779.2, 751.3, 724.3]) * units.mbar
    temperature = np.array([22.2, 14.6, 12., 9.4]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0]).to('degC')
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 0.08750805 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, -89.8073512 * units('joule / kilogram'), 6)
Esempio n. 8
0
def test_cape_cin():
    """Test the basic CAPE and CIN calculation."""
    p = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
    temperature = np.array([22.2, 14.6, 12., 9.4, 7., -38.]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0])
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 58.0368212 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, -89.8073512 * units('joule / kilogram'), 6)
Esempio n. 9
0
def test_cape_cin_custom_profile():
    """Test the CAPE and CIN calculation with a custom profile passed to LFC and EL."""
    p = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
    temperature = np.array([22.2, 14.6, 12., 9.4, 7., -38.]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0]) + 5 * units.delta_degC
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 1443.505086499895 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, 0.0 * units('joule / kilogram'), 6)
Esempio n. 10
0
def test_cape_cin_no_lfc():
    """Tests that CAPE is zero with no LFC."""
    p = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
    temperature = np.array([22.2, 24.6, 22., 20.4, 18., -10.]) * units.celsius
    dewpoint = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius
    parcel_prof = parcel_profile(p, temperature[0], dewpoint[0]).to('degC')
    cape, cin = cape_cin(p, temperature, dewpoint, parcel_prof)
    assert_almost_equal(cape, 0.0 * units('joule / kilogram'), 6)
    assert_almost_equal(cin, 0.0 * units('joule / kilogram'), 6)
def calcMLCAPE(levels, temperature, dewpoint, depth=100.0 * units.hPa):
    _, T_parc, Td_par = mixed_parcel(
        levels,
        temperature,
        dewpoint,
        depth=depth,
        interpolate=False,
    )
    profile = parcel_profile(levels, T_parc, Td_parc)
    cape, cin = cape_cin(levels, temperature, dewpoint, profile)
    return cape
Esempio n. 12
0
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
Esempio n. 13
0
    for i in range(1, 5):
        soundlat = 27.15
        soundlon = 360 - (startlat + (londelt * i))
        sound_temps = data["temperature"].interp(lat=soundlat, lon=soundlon) - 273.15
        sound_rh = data["rh"].interp(lat=soundlat, lon=soundlon)
        sound_dp = mpcalc.dewpoint_from_relative_humidity(
            sound_temps.data * units.degC, sound_rh.data * units.percent
        )
        skew = SkewT(fig=fig, rect=(0.75 - (0.15 * i), 0.2, 0.15, 0.1))

        parcel_prof = mpcalc.parcel_profile(
            sound_pres, sound_temps[0].data * units.degC, sound_dp[0]
        )
        cape = mpcalc.cape_cin(
            sound_pres, sound_temps.data * units.degC, sound_dp, parcel_prof
        )
        capeout = int(cape[0].m)
        cinout = int(cape[1].m)

        skew.plot(sound_pres, sound_dp, "g", linewidth=3)
        skew.plot(sound_pres, sound_temps, "r", linewidth=3)

        if capeout > capemin:
            # Shade areas of CAPE and CIN
            skew.shade_cin(sound_pres, sound_temps.data * units.degC, parcel_prof)
            skew.shade_cape(sound_pres, sound_temps.data * units.degC, parcel_prof)
            skew.plot(sound_pres, parcel_prof, color="fuchsia", linewidth=1)

        skew.ax.axvline(0, color="purple", linestyle="--", linewidth=3)
        skew.ax.set_ylim((1000, ptop))
    def getData(self, time, model_vars, mdl2stnd, previous_data=None):
        '''
    Name:
      awips_model_base
    Purpose:
      A function to get data from NAM40 model to create HDWX products
    Inputs:
      request    : A DataAccessLayer request object
      time       : List of datatime(s) for data to grab
      model_vars : Dictionary with variables/levels to get
      mdl2stnd   : Dictionary to convert from model variable names
                    to standardized names
    Outputs:
      Returns a dictionary containing all data
    Keywords:
      previous_data : Dictionary with data from previous time step
    '''
        log = logging.getLogger(__name__)
        # Set up function for logger
        initTime, fcstTime = get_init_fcst_times(time[0])
        data = {
            'model': self._request.getLocationNames()[0],
            'initTime': initTime,
            'fcstTime': fcstTime
        }
        # Initialize empty dictionary

        log.info('Attempting to download {} data'.format(data['model']))

        for var in model_vars:  # Iterate over variables in the vars list
            log.debug('Getting: {}'.format(var))
            self._request.setParameters(*model_vars[var]['parameters'])
            # Set parameters for the download request
            self._request.setLevels(*model_vars[var]['levels'])
            # Set levels for the download request

            response = DAL.getGridData(self._request, time)  # Request the data

            for res in response:  # Iterate over all data request responses
                varName = res.getParameter()
                # Get name of the variable in the response
                varLvl = res.getLevel()
                # Get level of the variable in the response
                varName = mdl2stnd[varName]
                # Convert variable name to local standarized name
                if varName not in data:
                    data[varName] = {}
                    # If variable name NOT in data dictionary, initialize new dictionary under key
                data[varName][varLvl] = res.getRawData()
                # Add data under level name
                try:  # Try to
                    unit = units(res.getUnit())
                    # Get units and convert to MetPy units
                except:  # On exception
                    unit = '?'
                    # Set units to ?
                else:  # If get units success
                    data[varName][varLvl] *= unit
                    # Get data and create MetPy quantity by multiplying by units

                log.debug(
                    'Got data for:\n  Var:  {}\n  Lvl:  {}\n  Unit: {}'.format(
                        varName, varLvl, unit))
        data['lon'], data['lat'] = res.getLatLonCoords()
        # Get latitude and longitude values
        data['lon'] *= units('degree')
        # Add units of degree to longitude
        data['lat'] *= units('degree')
        # Add units of degree to latitude

        # Absolute vorticity
        dx, dy = lat_lon_grid_deltas(data['lon'], data['lat'])
        # Get grid spacing in x and y
        uTag = mdl2stnd[model_vars['wind']['parameters'][0]]
        # Get initial tag name for u-wind
        vTag = mdl2stnd[model_vars['wind']['parameters'][1]]
        # Get initial tag name for v-wind
        if (uTag in data) and (
                vTag in data):  # If both tags are in the data structure
            data['abs_vort'] = {}
            # Add absolute vorticity key
            for lvl in model_vars['wind'][
                    'levels']:  # Iterate over all leves in the wind data
                if (lvl in data[uTag]) and (
                        lvl in data[vTag]
                ):  # If given level in both u- and v-wind dictionaries
                    log.debug('Computing absolute vorticity at {}'.format(lvl))
                    data['abs_vort'][ lvl ] = \
                      absolute_vorticity( data[uTag][lvl], data[vTag][lvl],
                                          dx, dy, data['lat'] )
                    # Compute absolute vorticity

        # 1000 MB equivalent potential temperature
        if ('temperature' in data) and (
                'dewpoint'
                in data):  # If temperature AND depoint data were downloaded
            data['theta_e'] = {}
            T, Td = 'temperature', 'dewpoint'
            if ('1000.0MB' in data[T]) and (
                    '1000.0MB' in data[Td]
            ):  # If temperature AND depoint data were downloaded
                log.debug(
                    'Computing equivalent potential temperature at 1000 hPa')
                data['theta_e']['1000.0MB'] = equivalent_potential_temperature(
                    1000.0 * units('hPa'), data[T]['1000.0MB'],
                    data[Td]['1000.0MB'])

            return data
            # MLCAPE
            log.debug('Computing mixed layer CAPE')
            T_lvl = list(data[T].keys())
            Td_lvl = list(data[Td].keys())
            levels = list(set(T_lvl).intersection(Td_lvl))
            levels = [float(lvl.replace('MB', '')) for lvl in levels]
            levels = sorted(levels, reverse=True)

            nLvl = len(levels)
            if nLvl > 0:
                log.debug(
                    'Found {} matching levels in temperature and dewpoint data'
                    .format(nLvl))
                nLat, nLon = data['lon'].shape

                data['MLCAPE'] = np.zeros((
                    nLat,
                    nLon,
                ), dtype=np.float32) * units('J/kg')
                TT = np.zeros((
                    nLvl,
                    nLat,
                    nLon,
                ), dtype=np.float32) * units('degC')
                TTd = np.zeros((
                    nLvl,
                    nLat,
                    nLon,
                ), dtype=np.float32) * units('degC')

                log.debug('Sorting temperature and dewpoint data by level')
                for i in range(nLvl):
                    key = '{:.1f}MB'.format(levels[i])
                    TT[i, :, :] = data[T][key].to('degC')
                    TTd[i, :, :] = data[Td][key].to('degC')

                levels = np.array(levels) * units.hPa
                depth = 100.0 * units.hPa

                log.debug('Iterating over grid boxes to compute MLCAPE')
                for j in range(nLat):
                    for i in range(nLon):
                        try:
                            _, T_parc, Td_parc = mixed_parcel(
                                levels,
                                TT[:, j, i],
                                TTd[:, j, i],
                                depth=depth,
                                interpolate=False,
                            )
                            profile = parcel_profile(levels, T_parc, Td_parc)
                            cape, cin = cape_cin(levels, TT[:, j, i],
                                                 TTd[:, j, i], profile)
                        except:
                            log.warning(
                                'Failed to compute MLCAPE for lon/lat: {}; {}'.
                                format(data['lon'][j, i], data['lat'][j, i]))
                        else:
                            data['MLCAPE'][j, i] = cape
        return data
Esempio n. 15
0
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)
Esempio n. 16
0
def plot_soundings(fig,ax,temp,rh,centerlat,centerlon,domainsize,cape):
    '''
    This function will plot a bunch of little soundings onto a matplotlib fig,ax.

    temp is an xarray dataarray with temperature data on pressure levels at least
    between 1000 and 300mb (you can change the ylimits for other datasets)

    rh is an xarray dataarray with temperature data on pressure levels at least
    between 1000 and 300mb (you can change )

    centerlat and centerlon are the coordinates around which you want your map
    to be centered. both are floats or integers and are in degrees of latitude
    and degrees of longitude west (i.e. 70W would be input as positive 70 here)

    domainsize is a string either 'local' for ~WFO-size domains or 'regional' for
    NE/SE/Mid-Atlantic-size domains (12 deg lat by 15 deg lon). More will be added soon.

    cape is a boolean to indicate whether you want to overlay parcel paths and
    shade CAPE/CIN on soundings with >100 J/kg of CAPE (this value can be changed)

    note that this function doesn't "return" anything but if you just call it and
    provide the right arguments, it works.

    for example:
        import soundingmaps as smap
        ...
        smap.plot_soundings(fig,ax1,data['temperature'],data['rh'],30.5,87.5,'local',cape=True)

    '''
    r=5
    if domainsize=='local':
        init_lat_delt = 1.625
        init_lon_delt = 0.45
        lat_delts = [.2,.7,1.2,1.75,2.25,2.8]
        londelt = 0.76
        startlon = centerlon-2+0.45

    elif domainsize=='regional':
        init_lat_delt = 6
        init_lon_delt = 1.6
        lat_delts = [0.6,2.5,4.5,6.4,8.4,10.25]
        londelt = 2.9
        startlon = centerlon-7.5+1.6

    startlat = centerlat-init_lat_delt
    startlon = centerlon-2+0.45

    sound_lats=[]
    sound_lons=[]
    for i in range(0,6):
        lats = startlat+lat_delts[i]
        sound_lats.append(lats)

    for i in range(0,r):
        lons = -startlon-(londelt*i)
        sound_lons.append(lons)

    plot_elevs=[0.2,0.3,0.4,0.5,0.6,0.7]

    dashed_red_line = lines.Line2D([], [], linestyle='solid', color='r', label='Temperature')
    dashed_purple_line = lines.Line2D([],[],linestyle='dashed',color='purple',label='0C Isotherm')
    dashed_green_line = lines.Line2D([], [], linestyle='solid', color='g', label='Dew Point')
    grey_line = lines.Line2D([], [], color='darkgray', label='MSLP (hPa)')
    blue_line = lines.Line2D([], [], color='b',label='2m 0C Isotherm')
    pink_line = lines.Line2D([], [], color='fuchsia',label='Surface-Based Parcel Path')
    red = mpatches.Patch(color='tab:red',label='CAPE')
    blue = mpatches.Patch(color='tab:blue',label='CIN')

    if cape==True:
        for k in range(len(plot_elevs)):
            soundlat = sound_lats[k]
            plot_elev = plot_elevs[k]

            if k==0:
                s=1
            else:
                s=0

            for i in range(s,r):
                sound_pres = temp.lev
                soundlon = -(startlon+(londelt*i))
                sound_temps = temp.interp(lat=soundlat,lon=soundlon)-273.15
                sound_rh = rh.interp(lat=soundlat,lon=soundlon)
                sound_dp = mpcalc.dewpoint_from_relative_humidity(sound_temps.data*units.degC,sound_rh.data*units.percent)
                skew = SkewT(fig=fig,rect=(0.75-(0.15*i),plot_elev,.15,.1))

                parcel_prof = mpcalc.parcel_profile(sound_pres,sound_temps[0].data*units.degC,sound_dp[0])
                cape = mpcalc.cape_cin(sound_pres,sound_temps.data*units.degC,sound_dp,parcel_prof)
                capeout = int(cape[0].m)
                cinout = int(cape[1].m)

                skew.plot(sound_pres,sound_dp,'g',linewidth=3)
                skew.plot(sound_pres,sound_temps,'r',linewidth=3)

                if capeout >100:
                    # Shade areas of CAPE and CIN
                    print(sound_temps)
                    print(parcel_prof)
                    skew.shade_cin(sound_pres, sound_temps.data*units.degC, parcel_prof)
                    skew.shade_cape(sound_pres, sound_temps.data*units.degC, parcel_prof)
                    skew.plot(sound_pres,parcel_prof,color='fuchsia',linewidth=1)

                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000,300))
                skew.ax.axis('off')

        leg = ax.legend(handles=[dashed_red_line,dashed_green_line,dashed_purple_line,pink_line,red,blue],title='Sounding Legend',loc=4,framealpha=1)

    else:
        for k in range(len(plot_elevs)):
            soundlat = sound_lats[k]
            plot_elev = plot_elevs[k]

            if k==0:
                s=1
            else:
                s=0

            for i in range(s,r):
                soundlon = -(startlon+(londelt*i))
                sound_pres = temp.lev
                sound_temps = temp.interp(lat=soundlat,lon=soundlon)-273.15
                sound_rh = rh.interp(lat=soundlat,lon=soundlon)
                sound_dp = mpcalc.dewpoint_from_relative_humidity(sound_temps.data*units.degC,sound_rh.data*units.percent)
                skew = SkewT(fig=fig,rect=(0.75-(0.15*i),plot_elev,.15,.1))
                skew.plot(sound_pres,sound_dp,'g',linewidth=3)
                skew.plot(sound_pres,sound_temps,'r',linewidth=3)
                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000,300))
                skew.ax.axis('off')
        leg = ax.legend(handles=[dashed_red_line,dashed_green_line,dashed_purple_line],title='Sounding Legend',loc=4,framealpha=1)
Esempio n. 17
0
 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
Esempio n. 18
0
def plot_upper_air(station='11035', date=False):
    '''
    -----------------------------
    Default use of plot_upper_air:

    This will plot a SkewT sounding for station '11035' (Wien Hohe Warte)
    plot_upper_air(station='11035', date=False)
    '''
    # sns.set(rc={'axes.facecolor':'#343837', 'figure.facecolor':'#343837',
    #  'grid.linestyle':'','axes.labelcolor':'#04d8b2','text.color':'#04d8b2',
    #  'xtick.color':'#04d8b2','ytick.color':'#04d8b2'})
    # Get time in UTC
    station = str(station)
    if date is False:
        now = datetime.utcnow()
        # If morning then 0z sounding, otherwise 12z
        if now.hour < 12:
            hour = 0
        else:
            hour = 12
        date = datetime(now.year, now.month, now.day, hour)
        datestr = date.strftime('%Hz %Y-%m-%d')
        print('{}'.format(date))
    else:
        year = int(input('Please specify the year: '))
        month = int(input('Please specify the month: '))
        day = int(input('Please specify the day: '))
        hour = int(input('Please specify the hour: '))
        if hour < 12:
            hour = 0
        else:
            hour = 12
        date = datetime(year, month, day, hour)
        datestr = date.strftime('%Hz %Y-%m-%d')
        print('You entered {}'.format(date))

    # This requests the data 11035 is
    df = WyomingUpperAir.request_data(date, station)

    # Create single variables wih the right units
    p = df['pressure'].values * units.hPa
    T = df['temperature'].values * units.degC
    Td = df['dewpoint'].values * units.degC
    wind_speed = df['speed'].values * units.knots
    wind_dir = df['direction'].values * units.degrees

    wind_speed_6k = df['speed'][df.height <= 6000].values * units.knots
    wind_dir_6k = df['direction'][df.height <= 6000].values * units.degrees

    u, v = mpcalc.get_wind_components(wind_speed, wind_dir)
    u6, v6 = mpcalc.get_wind_components(wind_speed_6k, wind_dir_6k)

    # Calculate the LCL
    lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
    print(lcl_pressure, lcl_temperature)
    # Calculate the parcel profile.
    parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')
    cape, cin = mpcalc.cape_cin(p, T, Td, parcel_prof)

    #############################
    # Create a new figure. The dimensions here give a good aspect ratio
    fig = plt.figure(figsize=(9, 9))
    gs = gridspec.GridSpec(3, 3)
    skew = SkewT(fig, rotation=45, subplot=gs[:, :2])

    # Plot the data using normal plotting functions, in this case using
    # log scaling in Y, as dictated by the typical meteorological plot
    skew.plot(p, T, 'r')
    skew.plot(p, Td, 'g')
    skew.plot_barbs(p, u, v)
    skew.ax.set_ylim(1000, 100)
    skew.ax.set_xlim(-45, 40)

    # Plot LCL as black dot
    skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black')

    # Plot the parcel profile as a black line
    skew.plot(p, parcel_prof, 'k', linewidth=2)

    # Shade areas of CAPE and CIN
    skew.shade_cin(p, T, parcel_prof)
    skew.shade_cape(p, T, parcel_prof)

    # Plot a zero degree isotherm
    skew.ax.axvline(0, color='c', linestyle='--', linewidth=2)
    skew.ax.set_title('Station: ' + str(station) + '\n' + datestr)  # set title
    skew.ax.set_xlabel('Temperature (C)')
    skew.ax.set_ylabel('Pressure (hPa)')

    # Add the relevant special lines
    skew.plot_dry_adiabats(linewidth=0.7)
    skew.plot_moist_adiabats(linewidth=0.7)
    skew.plot_mixing_lines(linewidth=0.7)

    # Create a hodograph
    # Create an inset axes object that is 40% width and height of the
    # figure and put it in the upper right hand corner.
    # ax_hod = inset_axes(skew.ax, '40%', '40%', loc=1)
    ax = fig.add_subplot(gs[0, -1])
    h = Hodograph(ax, component_range=60.)
    h.add_grid(increment=20)
    # Plot a line colored by windspeed
    h.plot_colormapped(u6, v6, wind_speed_6k)

    # add another subplot for the text of the indices
    # ax_t = fig.add_subplot(gs[1:,2])
    skew2 = SkewT(fig, rotation=0, subplot=gs[1:, 2])
    skew2.plot(p, T, 'r')
    skew2.plot(p, Td, 'g')
    # skew2.plot_barbs(p, u, v)
    skew2.ax.set_ylim(1000, 700)
    skew2.ax.set_xlim(-30, 10)

    # Show the plot
    plt.show()

    return cape
Esempio n. 19
0
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)
Esempio n. 20
0
def EnergyMassPlot(pressure, temperature, dewpoint,
                  height, uwind, vwind, sphum=None, rh=None,
                  label='', size=(12,10), return_fig=False): 
    p=pressure
    Z=height
    T=temperature
    Td=dewpoint
    if isinstance(sphum,np.ndarray) and isinstance(rh,np.ndarray):
        q=sphum
        qs=q/rh
    else:
        q = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(Td),p)
        qs= mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T),p)
    
    s = g*Z + Cp_d*T 
    sv= g*Z + Cp_d*mpcalc.virtual_temperature(T,q)
    h = s            + Lv*q
    hs= s            + Lv*qs
    
    parcel_Tprofile = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')
    CAPE,CIN = mpcalc.cape_cin(p,T,Td,parcel_Tprofile)
    ELp,ELT = mpcalc.el(p,T,Td)
    ELindex = np.argmin(np.abs(p - ELp))
    lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
    
    p_PWtop = max(200*units.mbar, min(p) +1*units.mbar)
    PW = mpcalc.precipitable_water(Td,p, top=p_PWtop)
    PWs = mpcalc.precipitable_water(T,p, top=p_PWtop)
    CRH = (PW/PWs).magnitude *100. 
    
    fig,ax=setup_fig(size=size,label=label)
    
    ax.plot(s  /1000, p, color='r', linewidth=1.5)  ### /1000 for kJ/kg
    ax.plot(sv /1000, p, color='r', linestyle='-.') 
    ax.plot(h  /1000, p, color='b', linewidth=1.5) 
    ax.plot(hs /1000, p, color='r', linewidth=1.5) 
    ### RH rulings between s and h lines: annotate near 800 hPa level
    annot_level = 800 #hPa
    idx = np.argmin(np.abs(p - annot_level *units.hPa))
    right_annot_loc = 380
    for iRH in np.arange(10,100,10):
        ax.plot( (s+ Lv*qs*iRH/100.)/1000, p, linewidth=0.5, linestyle=':', color='k')
        ax.annotate(str(iRH), xy=( (s[idx]+Lv*qs[idx]*iRH/100.)/1000, annot_level),                    
                    horizontalalignment='center',fontsize=6)
    ax.annotate('RH (%)', xy=(right_annot_loc, annot_level), fontsize=10)
    
    if  not np.isnan(CAPE.magnitude) and CAPE.magnitude >10:  
        parcelh  = h [0]        # for a layer mean: np.mean(h[idx1:idx2])
        parcelsv = sv[0]
        parcelp0 = p[0]

        # Undilute parcel
        ax.plot( (1,1)*parcelh/1000., (1,0)*parcelp0, linewidth=0.5, color='g')
        maxbindex = np.argmax(parcel_Tprofile - T)
        ax.annotate('CAPE='+str(int(CAPE.magnitude)), 
                    xy=(parcelh/1000., p[maxbindex]), color='g')

        # Plot LCL at saturation point, above the lifted sv of the surface parcel 
        lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
        ax.annotate('LCL', xy=(sv[0]/1000., lcl_pressure), fontsize=10, color='g', horizontalalignment='right')
        # Purple fill for negative buoyancy below LCL:
        ax.fill_betweenx(p, sv/1000., parcelsv/1000., where=p>lcl_pressure, facecolor='purple', alpha=0.4)

        # Positive moist convective buoyancy in green 
        # Above LCL:
        ax.fill_betweenx(p, hs/1000., parcelh/1000., where= parcelh>hs, facecolor='g', alpha=0.4)


        # Depict Entraining parcels
        # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z)
        # M=1 at ground without loss of generality
        entrainment_distance = 10000., 5000., 2000. 
        ax.annotate('entrain: 10,5,2 km',  xy=(parcelh/1000, 140), color='g', 
                    fontsize=8, horizontalalignment='right')
        ax.annotate('parcel h',            xy=(parcelh/1000, 120), color='g', 
                    fontsize=10, horizontalalignment='right')

        for ED in entrainment_distance: 
            eps = 1.0 / (ED*units.meter)
            M = np.exp(eps * (Z-Z[0]).to('m')).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*h) / np.cumsum(dM)

            ax.plot( hent[0:ELindex+3]/1000., p[0:ELindex+3], linewidth=0.5, color='g')
        ### Internal waves (100m adiabatic displacements, assumed adiabatic: conserves s, sv, h). 
    dZ = 100 *units.meter

    # 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))

        # dT: Dry lapse rate dT/dz_dry is -g/Cp
        dT = (-g/Cp_d *dZ).to('kelvin')    
        Tdisp = T[idx].to('kelvin') + dT

        # dp: hydrostatic
        rho = (p[idx]/Rd/T[idx])
        dp = -rho*g*dZ

        # dhsat
        #qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T)     ,p)
        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( (hs[idx]+dhs*(-1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
        ax.plot( (s [idx]    *( 1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
        ax.plot( (h [idx]    *( 1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='b')  

        # annotation to explain it 
        if ilev == 600*ilev.units:
            ax.plot(right_annot_loc*hs.units +dhs*(-1,1)/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
            ax.annotate('+/- 100m', xy=(right_annot_loc,600), fontsize=8)
            ax.annotate('  internal', xy=(right_annot_loc,630), fontsize=8)
            ax.annotate('  waves', xy=(right_annot_loc,660), fontsize=8)


    ### Blue fill proportional to precipitable water, and blue annotation
    ax.fill_betweenx(p, s/1000., h/1000., where=h > s, facecolor='b', alpha=0.4)

    # Have to specify the top of the PW integral. 
    # I want whole atmosphere of course, but 200 hPa captures it all really. 
    #import metpy.calc as mpcalc
    p_PWtop = max(200*units.mbar, min(p) +1*units.mbar)
    PW = mpcalc.precipitable_water(Td,p, top=p_PWtop)
    PWs = mpcalc.precipitable_water(T,p, top=p_PWtop)
    CRH = (PW/PWs).magnitude *100. 

    # PW annotation arrow tip at 700 mb
    idx = np.argmin(np.abs(p - 700*p.units))
    centerblue = (s[idx]+h[idx])/2.0 /1000.

    ax.annotate('CWV='+str(round(PW.to('mm').magnitude, 1))+'mm',
                xy=(centerblue, 700), xytext=(285, 200), 
                color='blue', fontsize=15,
                arrowprops=dict(width = 1, edgecolor='blue', shrink=0.02),
                )
    ax.annotate('(' + str(round(CRH,1)) +'% of sat)',
                xy=(285, 230), color='blue', fontsize=12)



    ### Surface water values at 1C intervals, for eyeballing surface fluxes
    sT = np.trunc(T[0].to('degC'))
    sTint = int(sT.magnitude)

    for idT in [-2,0,2,4]:
        ssTint = sTint + idT # UNITLESS degC integers, for labels

        # Kelvin values for computations
        ssTC = ssTint * units.degC
        ssTK = ssTC.to('kelvin')
        ss = g*Z[0] + Cp_d*ssTK 
        hs = ss     + Lv*mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(ssTK) ,p[0])

        ax.annotate(str(ssTint), xy=(ss/1000., p[0]+0*p.units), 
                    verticalalignment='top', horizontalalignment='center',
                    color='red', fontsize=7)
        ax.annotate(str(ssTint), xy=(hs/1000., p[0]+0*p.units), 
                    verticalalignment='top', horizontalalignment='center',
                    color='red', fontsize=9)
        ax.annotate('\u00b0C water', xy=(right_annot_loc, p[0]), verticalalignment='top', 
                    fontsize=10, color='r')
    if return_fig:
        return ax,fig
Esempio n. 21
0
    'dewpoint': Td,
    'speed': ws,
    'direction': wd
})

p = df['pressure'].values * units.hPa
T = df['temperature'].values * units.degC
Td = df['dewpoint'].values * units.degC
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,
                    wspace=0.08,
                    hspace=0.25)
gs = gridspec.GridSpec(21, 5)
skew = SkewT(fig, subplot=gs[:, :4], rotation=45)

# Plot the data using normal plotting functions, in this case using
# log scaling in Y, as dictated by the typical meteorological plot
skip = 100
skew.plot(p, T, 'r', linewidth=3)
Esempio n. 22
0
 def cape_cin(self, index_from):
     return mpcalc.cape_cin(self.p[index_from:], self.T[index_from:],
                            self.Td[index_from:],
                            self.parcel_trace(index_from))
Esempio n. 23
0
def fmi2skewt(station, time, img_name):

    apikey = 'e72a2917-1e71-4d6f-8f29-ff4abfb8f290'

    url = 'http://data.fmi.fi/fmi-apikey/' + str(
        apikey
    ) + '/wfs?request=getFeature&storedquery_id=fmi::observations::weather::sounding::multipointcoverage&fmisid=' + str(
        station) + '&starttime=' + str(time) + '&endtime=' + str(time) + '&'

    req = requests.get(url)
    xmlstring = req.content
    tree = ET.ElementTree(ET.fromstring(xmlstring))
    root = tree.getroot()

    #reading location and time data to "positions" from XML
    positions = ""
    for elem in root.getiterator(
            tag='{http://www.opengis.net/gmlcov/1.0}positions'):
        positions = elem.text

    #'positions' is string type variable
    #--> split positions into a list by " "
    #then remove empty chars and "\n"
    # from pos_split --> data into positions_data

    try:
        pos_split = positions.split(' ')
    except NameError:
        return "Sounding data not found: stationid " + station + " time " + time

    pos_split = positions.split(' ')

    positions_data = []
    for i in range(0, len(pos_split)):
        if not (pos_split[i] == "" or pos_split[i] == "\n"):
            positions_data.append(pos_split[i])

    #index for height: 2,6,10 etc in positions_data
    height = []
    myList = range(2, len(positions_data))
    for i in myList[::4]:
        height.append(positions_data[i])

    p = []
    for i in range(0, len(height)):
        p.append(height2pressure(float(height[i])))

    #reading wind speed, wind direction, air temperature and dew point data to 'values'
    values = ""
    for elem in root.getiterator(
            tag='{http://www.opengis.net/gml/3.2}doubleOrNilReasonTupleList'):
        values = elem.text

    #split 'values' into a list by " "
    #then remove empty chars and "\n"

    val_split = values.split(' ')
    values_data = []
    for i in range(0, len(val_split)):
        if not (val_split[i] == "" or val_split[i] == "\n"):
            values_data.append(val_split[i])

    #data in values_data: w_speed, w_dir, t_air, t_dew
    wind_speed = []
    wind_dir = []
    T = []
    Td = []
    myList = range(0, len(values_data))
    for i in myList[::4]:
        wind_speed.append(float(values_data[i]))
        wind_dir.append(float(values_data[i + 1]))
        T.append(float(values_data[i + 2]))
        Td.append(float(values_data[i + 3]))

    if stationid == "101104":
        loc_time = "Jokioinen Ilmala " + time
    elif stationid == "101932":
        loc_time = "Sodankyla Tahtela " + time
    else:
        return None

    #calculate wind components u,v:
    u = []
    v = []
    for i in range(0, len(wind_speed)):
        u1, v1 = getWindComponent(wind_speed[i], wind_dir[i])
        u.append(u1)
        v.append(v1)

    #find index for pressure < 100hPa (for number of wind bars)
    if min(p) > 100:
        wthin = len(p) / 20
        u_plot = u
        v_plot = v
        p_plot = p
    else:
        for i in range(0, len(p)):
            if p[i] - 100 <= 0:
                wthin = i / 20
                u_plot = u[0:i]
                v_plot = v[0:i]
                p_plot = p[0:i]
                break

    #units
    wind_speed = wind_speed * units("m/s")
    wind_dir = wind_dir * units.deg
    T = T * units.degC
    Td = Td * units.degC
    p = p * units("hPa")

    #calculate pwat, lcl, cape, cin and plot cape
    pwat = mpcalc.precipitable_water(Td, p, bottom=None, top=None)
    lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
    prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')

    try:
        cape, cin = mpcalc.cape_cin(p, T, Td, prof)
    except IndexError:
        cape = 0 * units("J/kg")
        cin = 0 * units("J/kg")

    #__________________plotting__________________
    fig = plt.figure(figsize=(9, 9))
    skew = SkewT(fig, rotation=45)
    font_par = {
        'family': 'monospace',
        'color': 'darkred',
        'weight': 'normal',
        'size': 10,
    }
    font_title = {
        'family': 'monospace',
        'color': 'black',
        'weight': 'normal',
        'size': 20,
    }
    font_axis = {
        'family': 'monospace',
        'color': 'black',
        'weight': 'normal',
        'size': 10,
    }
    # Plot the data using normal plotting functions, in this case using
    # log scaling in Y, as dictated by the typical meteorological plot
    skew.plot(p, T, 'k')
    skew.plot(p, Td, 'b')
    skew.ax.set_ylim(1000, 100)
    skew.ax.set_xlim(-50, 30)
    skew.plot_barbs(p_plot[0::wthin], u_plot[0::wthin], v_plot[0::wthin])
    skew.plot_dry_adiabats(alpha=0.4)
    skew.plot_moist_adiabats(alpha=0.4)
    skew.plot_mixing_lines(alpha=0.4)
    #skew.shade_cape(p, T, prof,color="orangered")
    plt.title(loc_time, fontdict=font_title)
    plt.xlabel("T (C)", fontdict=font_axis)
    plt.ylabel("P (hPa)", fontdict=font_axis)

    #round and remove units from cape,cin,plcl,tlcl,pwat
    if cape.magnitude > 0:
        capestr = str(np.round(cape.magnitude))
    else:
        capestr = "NaN"

    if cin.magnitude > 0:
        cinstr = str(np.round(cin.magnitude))
    else:
        cinstr = "NaN"

    lclpstr = str(np.round(lcl_pressure.magnitude))
    lclTstr = str(np.round(lcl_temperature.magnitude))
    pwatstr = str(np.round(pwat.magnitude))

    #    str_par = "CAPE[J/kg]=" + capestr + " CIN[J/kg]=" + cinstr + " Plcl[hPa]=" + lclpstr + " Tlcl[C]=" + lclTstr + " pwat[mm]=" + pwatstr
    #    font = {'family': 'monospace',
    #        'color':  'darkred',
    #        'weight': 'normal',
    #        'size': 10,
    #        }
    #    plt.text(-20,1250,str_par,fontdict=font_par)
    save_file = img_name
    plt.savefig(save_file)
Esempio n. 24
0
def plot_soundings(fig,
                   ax,
                   temp,
                   rh,
                   sfc_pressure,
                   centerlat,
                   centerlon,
                   domainsize,
                   model,
                   cape=False,
                   wetbulb=False):
    """
    This function will plot a bunch of little soundings onto a matplotlib fig,ax.

    temp is an xarray dataarray with temperature data on pressure levels at least
    between 1000 and 300mb (you can change the ylimits for other datasets)

    rh is an xarray dataarray with temperature data on pressure levels at least
    between 1000 and 300mb (you can change )

    sfc_pressure is an xarray dataarray with surface pressure data (NOT MSLP!)

    centerlat and centerlon are the coordinates around which you want your map
    to be centered. both are floats or integers and are in degrees of latitude
    and degrees of longitude west (i.e. 70W would be input as positive 70 here)

    domainsize is a string either 'local' for ~WFO-size domains or 'regional' for
    NE/SE/Mid-Atlantic-size domains (12 deg lat by 15 deg lon). More will be added soon.

    model is a string that specifies which model is providing data for the plots.
    This determines a few things, most importantly longitude selections. Models
    currently supported are 'GFS','NAM',and 'RAP'

    cape is a boolean to indicate whether you want to overlay parcel paths and
    shade CAPE/CIN on soundings with >100 J/kg of CAPE (this value can be changed)

    wetbulb is a boolean to indicate whether you want to draw wet bulb profiles

    note that this function doesn't "return" anything but if you just call it and
    provide the right arguments, it works.

    for example:
        import soundingmaps as smap
        ...
        smap.plot_soundings(fig,ax1,data['temperature'],data['rh'],30.5,87.5,'local',cape=True)

    """
    r = 5
    if domainsize == "local":
        init_lat_delt = 1.625
        init_lon_delt = 0.45
        lat_delts = [0.2, 0.7, 1.2, 1.75, 2.25, 2.8]
        londelt = 0.76
        startlon = centerlon - 2 + 0.45

    elif domainsize == "regional":
        init_lat_delt = 6
        init_lon_delt = 1.6
        lat_delts = [0.6, 2.5, 4.5, 6.4, 8.4, 10.25]
        londelt = 2.9
        startlon = centerlon - 7.5 + 1.6

    # Lon adjustment for GFS because it's [0,360] not [-180,180]
    if model == 'GFS':
        startlon = 360 - startlon

    # set lat/lon grid from which to pull data to plot soundings
    startlat = centerlat - init_lat_delt

    sound_lats = []
    sound_lons = []
    for i in range(0, 6):
        lats = startlat + lat_delts[i]
        sound_lats.append(lats)

    for i in range(0, r):
        if model == 'GFS':
            lons = startlon - (londelt * i)
        else:
            lons = -startlon - (londelt * i)
        sound_lons.append(lons)

    # this sets how high each row of soundings is on the plot
    plot_elevs = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7]

    # whole bunch of legend stuff
    dashed_red_line = lines.Line2D([], [],
                                   linestyle='solid',
                                   color='r',
                                   label='Temperature')
    dashed_purple_line = lines.Line2D([], [],
                                      linestyle='dashed',
                                      color='purple',
                                      label='0C Isotherm')
    dashed_green_line = lines.Line2D([], [],
                                     linestyle='solid',
                                     color='g',
                                     label='Dew Point')
    grey_line = lines.Line2D([], [], color='darkgray', label='MSLP (hPa)')
    blue_line = lines.Line2D([], [], color='b', label='Wet Bulb')
    pink_line = lines.Line2D([], [],
                             color='fuchsia',
                             label='Surface-Based Parcel Path')
    teal_line = lines.Line2D([], [],
                             linestyle='dashed',
                             color='teal',
                             label='HGZ')
    green_dot = lines.Line2D([], [],
                             marker='o',
                             color='forestgreen',
                             label='LCL')
    black_dot = lines.Line2D([], [],
                             marker='o',
                             color='k',
                             label='Sounding Origin')

    red = mpatches.Patch(color='tab:red', label='CAPE')
    blue = mpatches.Patch(color='tab:blue', label='CIN')

    # do the plotting based on user inputs
    if cape and wetbulb is True:
        print('CAPE + Wetbulb')
        for i, plot_elev in enumerate(plot_elevs):
            soundlat = sound_lats[i]

            if k < 2:
                s = 1
            else:
                s = 0

            for i in range(s, r):
                levs_abv_ground = []
                soundlon = sound_lons[i]
                sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15
                sound_rh = rh.interp(lat=soundlat, lon=soundlon)
                sound_pres = temp.lev
                spres = sfc_pressure.interp(lat=soundlat, lon=soundlon)
                sound_dp = mpcalc.dewpoint_from_relative_humidity(
                    sound_temps.data * units.degC,
                    sound_rh.data * units.percent)
                sound_wb = mpcalc.wet_bulb_temperature(
                    sound_pres, sound_temps.data * units.degC, sound_dp)

                #Only want data above the ground
                abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps,
                                                      sound_pres)[0]
                abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp,
                                                      sound_pres)[0]
                abv_sfc_wetb = spt.mask_below_terrain(spres, sound_wb,
                                                      sound_pres)[0]
                pres_abv_ground = spt.mask_below_terrain(
                    spres, sound_temps, sound_pres)[1]

                #sound_wb = sound_wb*units.degC
                skew = SkewT(fig=fig,
                             rect=(0.75 - (0.15 * i), plot_elev, .15, .1))

                parcel_prof = mpcalc.parcel_profile(
                    pres_abv_ground, abv_sfc_temp[0].data * units.degC,
                    abv_sfc_dewp[0])
                cape = mpcalc.cape_cin(pres_abv_ground,
                                       abv_sfc_temp.data * units.degC,
                                       abv_sfc_dewp, parcel_prof)
                capeout = int(cape[0].m)
                cinout = int(cape[1].m)

                #skew.ax.axvspan(-30, -10, color='cyan', alpha=0.4)

                skew.plot(pres_abv_ground, abv_sfc_wetb, 'b', linewidth=2)
                skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3)
                skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3)

                if capeout > 100:
                    # Shade areas of CAPE and CIN
                    print(pres_abv_ground)
                    print(abv_sfc_temp.data * units.degC)
                    print(parcel_prof)
                    skew.shade_cin(pres_abv_ground,
                                   abv_sfc_temp.data * units.degC, parcel_prof)
                    skew.shade_cape(pres_abv_ground,
                                    abv_sfc_temp.data * units.degC,
                                    parcel_prof)
                    skew.plot(pres_abv_ground,
                              parcel_prof,
                              color='fuchsia',
                              linewidth=1)
                    lcl_pressure, lcl_temperature = mpcalc.lcl(
                        pres_abv_ground[0], abv_sfc_temp.data[0] * units.degC,
                        abv_sfc_dewp[0])
                    skew.plot(lcl_pressure,
                              lcl_temperature,
                              'ko',
                              markerfacecolor='forestgreen')
                    skew.ax.axvline(-30,
                                    color='teal',
                                    linestyle='--',
                                    linewidth=1)
                    skew.ax.axvline(-10,
                                    color='teal',
                                    linestyle='--',
                                    linewidth=1)
                skew.plot(975, 0, 'ko', markerfacecolor='k')

                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000, 300))
                skew.ax.axis('off')

        leg = ax.legend(handles=[
            dashed_red_line, dashed_green_line, blue_line, dashed_purple_line,
            teal_line, green_dot, pink_line, red, blue, black_dot
        ],
                        title='Sounding Legend',
                        loc=4,
                        framealpha=1)
    elif cape == True and wetbulb == False:
        print('CAPE no wetbulb')
        for k in range(len(plot_elevs)):
            soundlat = sound_lats[k]
            plot_elev = plot_elevs[k]

            if k == 0:
                s = 1
            else:
                s = 0

            for i in range(s, r):
                levs_abv_ground = []
                soundlon = sound_lons[i]
                sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15
                sound_rh = rh.interp(lat=soundlat, lon=soundlon)
                sound_pres = temp.lev
                spres = sfc_pressure.interp(lat=soundlat, lon=soundlon)
                sound_dp = mpcalc.dewpoint_from_relative_humidity(
                    sound_temps.data * units.degC,
                    sound_rh.data * units.percent)

                abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps,
                                                      sound_pres)[0]
                abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp,
                                                      sound_pres)[0]
                pres_abv_ground = spt.mask_below_terrain(
                    spres, sound_temps, sound_pres)[1]

                skew = SkewT(fig=fig,
                             rect=(0.75 - (0.15 * i), plot_elev, .15, .1))

                parcel_prof = mpcalc.parcel_profile(
                    pres_abv_ground, abv_sfc_temp[0].data * units.degC,
                    abv_sfc_dewp[0])
                cape = mpcalc.cape_cin(pres_abv_ground,
                                       abv_sfc_temp.data * units.degC,
                                       abv_sfc_dewp, parcel_prof)
                capeout = int(cape[0].m)
                cinout = int(cape[1].m)

                skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3)
                skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3)

                if capeout > 100:
                    # Shade areas of CAPE and CIN
                    skew.shade_cin(pres_abv_ground,
                                   abv_sfc_temp.data * units.degC, parcel_prof)
                    skew.shade_cape(pres_abv_ground,
                                    abv_sfc_temp.data * units.degC,
                                    parcel_prof)
                    skew.plot(pres_abv_ground,
                              parcel_prof,
                              color='fuchsia',
                              linewidth=1)
                    print(abv_sfc_temp)
                    lcl_pressure, lcl_temperature = mpcalc.lcl(
                        pres_abv_ground[0], abv_sfc_temp.data[0] * units.degC,
                        abv_sfc_dewp[0])
                    skew.plot(lcl_pressure,
                              lcl_temperature,
                              'ko',
                              markerfacecolor='forestgreen')
                    skew.ax.axvline(-30,
                                    color='teal',
                                    linestyle='--',
                                    linewidth=1)
                    skew.ax.axvline(-10,
                                    color='teal',
                                    linestyle='--',
                                    linewidth=1)

                skew.plot(975, 0, 'ko', markerfacecolor='k')

                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000, 300))
                skew.ax.axis('off')

        leg = ax.legend(handles=[
            dashed_red_line, dashed_green_line, dashed_purple_line, teal_line,
            green_dot, pink_line, red, blue, black_dot
        ],
                        title='Sounding Legend',
                        loc=4,
                        framealpha=1)

    elif wetbulb == True and cape == False:
        print('Wetbulb no CAPE')
        for k in range(len(plot_elevs)):
            soundlat = sound_lats[k]
            plot_elev = plot_elevs[k]

            if k == 0:
                s = 1
            else:
                s = 0

            for i in range(s, r):
                levs_abv_ground = []
                soundlon = sound_lons[i]
                sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15
                sound_rh = rh.interp(lat=soundlat, lon=soundlon)
                sound_pres = temp.lev
                spres = sfc_pressure.interp(lat=soundlat, lon=soundlon)

                sound_dp = mpcalc.dewpoint_from_relative_humidity(
                    sound_temps.data * units.degC,
                    sound_rh.data * units.percent)

                sound_wb = mpcalc.wet_bulb_temperature(
                    sound_pres, sound_temps.data * units.degC, sound_dp)

                abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps,
                                                      sound_pres)[0]
                abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp,
                                                      sound_pres)[0]
                abv_sfc_wetb = spt.mask_below_terrain(spres, sound_wb,
                                                      sound_pres)[0]
                pres_abv_ground = spt.mask_below_terrain(
                    spres, sound_temps, sound_pres)[1]

                #sound_wb = sound_wb*units.degC
                skew = SkewT(fig=fig,
                             rect=(0.75 - (0.15 * i), plot_elev, .15, .1))

                skew.plot(pres_abv_ground, abv_sfc_wetb, 'b', linewidth=2)
                skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3)
                skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3)

                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000, 300))
                skew.ax.axis('off')
    else:
        print('No Wetbulb or CAPE')
        for k in range(len(plot_elevs)):
            soundlat = sound_lats[k]
            plot_elev = plot_elevs[k]

            if k == 0:
                s = 1
            else:
                s = 0

            for i in range(s, r):
                sound_pres = temp.lev
                sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15
                sound_rh = rh.interp(lat=soundlat, lon=soundlon)
                sound_dp = mpcalc.dewpoint_from_relative_humidity(
                    sound_temps.data * units.degC,
                    sound_rh.data * units.percent)
                skew = SkewT(fig=fig,
                             rect=(0.75 - (0.15 * i), plot_elev, .15, .1))
                skew.plot(sound_pres, sound_dp, 'g', linewidth=3)
                skew.plot(sound_pres, sound_temps, 'r', linewidth=3)
                skew.plot(1000, 0, 'ko', markerfacecolor='k')

                skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3)
                skew.ax.set_ylim((1000, 300))
                skew.ax.axis('off')

        leg = ax.legend(handles=[
            dashed_red_line, dashed_green_line, blue_line, dashed_purple_line,
            black_dot
        ],
                        title='Sounding Legend',
                        loc=4,
                        framealpha=1)