def test_pressure_to_heights_basic(array_type): """Test basic pressure to height calculation for standard atmosphere.""" mask = [False, True, False, True] pressures = array_type([975.2, 987.5, 956., 943.], 'mbar', mask=mask) heights = pressure_to_height_std(pressures) values = array_type([321.5, 216.5, 487.6, 601.7], 'meter', mask=mask) assert_array_almost_equal(heights, values, 1)
def p2h(x): """ x in hPa """ y = mpcalc.pressure_to_height_std(np.array(x) * units.hPa) # y = y.metpy.convert_units('m') y = y.to('m') return y.magnitude
def pressure_to_height(target_p, hgt3D, targetdir=".", debug=False): junktime = datetime.datetime(2015, 1, 1) surface_height = scalardata('surface_height', junktime, targetdir=targetdir, debug=debug) lcl_height_MSL = pressure_to_height_std(target_p) # subtract surface geopotential height to get height AGL # switch from xarray to pint quantities for now. lcl_height_AGL = lcl_height_MSL - surface_height.metpy.unit_array lv_ISBL0 = hgt3D.lv_ISBL0.metpy.unit_array # subtract pint quantities. surface_height broadcasted to multiple vertical levels in hgt3D AGL3D = hgt3D.metpy.unit_array - surface_height.metpy.unit_array shp = target_p.shape nz = len(lv_ISBL0) junk = np.empty(shp) for i, p in enumerate(target_p.metpy.unit_array.flatten()): ituple = np.unravel_index( i, shp) # 277 lat , 349 lon - ituple may have 2 or 3 dims # interpolate linearly in ln(p). convert to same units, remove units, before applying natural log lclinterp = np.interp(np.log(p.to('hPa').m), np.log(lv_ISBL0.to('hPa').m), AGL3D[:, ituple[-2], ituple[-1]]) if not hasattr(lclinterp, "units"): # TODO: figure out why lclinterp sometimes has and sometimes doesn't have this attribute. lclinterp *= AGL3D.units # Prior to pint 0.11 np.interp lost units if debug: # this slows things down if np.abs(lclinterp - lcl_height_AGL[ituple]) > 415 * munits.meter: print('ituple={} lclinterp={} lclstd={}'.format( ituple, lclinterp, lcl_height_AGL[ituple])) pdb.set_trace() # strip units with .m magnitude attribute or get ValueError: setting an array element with a sequence. junk[ ituple] = lclinterp.m # this is slow if you update an xarray. speed things up by updating a ndarray. # Change pint quantity to xarray attrs = hgt3D.attrs # Units were stripped earlier from lclinterp. Make they are what was expected. assert attrs["units"] == lclinterp.units or (attrs["units"] == "gpm" and lclinterp.units == "meter") #attrs['units'] = lclinterp.units # replaces 'gpm' with 'meters'. TODO: figure out why this causes AttributeError: 'NoneType' object has no attribute 'evaluate' later when significant tornado function is called. lcl_height_AGL = xarray.DataArray(data=junk, coords=target_p.coords, dims=target_p.dims, name='lcl_height_AGL', attrs=attrs) lcl_height_AGL.attrs['long_name'] = 'lifted condensation level height AGL' return lcl_height_AGL
def test_pressure_to_heights_units(): """Test that passing non-mbar units works.""" assert_almost_equal(pressure_to_height_std(29 * units.inHg), 262.859 * units.meter, 3)
def test_pressure_to_heights_basic(): """Test basic pressure to height calculation for standard atmosphere.""" pressures = np.array([975.2, 987.5, 956., 943.]) * units.mbar heights = pressure_to_height_std(pressures) values = np.array([321.5, 216.5, 487.6, 601.7]) * units.meter assert_almost_equal(heights, values, 1)
def get_bounds_data(): """Provide pressure and height data for testing layer bounds calculation.""" pressures = np.linspace(1000, 100, 10) * units.hPa heights = pressure_to_height_std(pressures) return pressures, heights
def atmCalc(height, temp, humid): print("ATMCALC", height, temp, humid, file=sys.stderr) mtny = windh(MTNX, height, ratio=1, yoffset=0) windx = XVALUES windy = windh(windx, height) temp_ = temp * units.degC initp = mc.height_to_pressure_std(windy[0] * units.meters) dewpt = mc.dewpoint_from_relative_humidity(temp_, humid / 100.) lcl_ = mc.lcl(initp, temp_, dewpt, max_iters=50, eps=1e-5) LCL = mc.pressure_to_height_std(lcl_[0]) if (lcl_[0] > mc.height_to_pressure_std(max(windy) * units.meters) and LCL > windy[0] * units.meters * 1.000009): # add LCL to x xlcl = windh(LCL.to('meters').magnitude, height, inv=True) windx = np.sort(np.append(windx, xlcl)) windy = windh(windx, height) pressures = mc.height_to_pressure_std(windy * units.meters) wvmr0 = mc.mixing_ratio_from_relative_humidity(initp, temp_, humid / 100.) # now calculate the air parcel temperatures and RH at each position if (lcl_[0] <= min(pressures)): T = mc.dry_lapse(pressures, temp_) RH = [ mc.relative_humidity_from_mixing_ratio( wvmr0, t, p) for t, p in zip( T, pressures)] else: mini = np.argmin(pressures) p1 = pressures[:mini + 1] p2 = pressures[mini:] # with an overlap p11 = p1[p1 >= lcl_[0] * .9999999] # lower (with tol) with lcl p12 = p1[p1 < lcl_[0] * 1.000009] # upper (with tol) with lcl T11 = mc.dry_lapse(p11, temp_) T12 = mc.moist_lapse(p12, lcl_[1]) T1 = concatenate((T11[:-1], T12)) T2 = mc.dry_lapse(p2, T1[-1]) T = concatenate((T1, T2[1:])) wvmrtop = mc.saturation_mixing_ratio(pressures[mini], T[mini]) RH=[] for i in range(len(pressures)): if pressures[i] > lcl_[0] and i <= mini: v=mc.relative_humidity_from_mixing_ratio(pressures[i], T[i], wvmr0) else: if i < mini: v=1 else: v=mc.relative_humidity_from_mixing_ratio(pressures[i], T[i], wvmrtop) RH.append(v) #RH = [mc.relative_humidity_from_mixing_ratio(*tp, wvmr0) if tp[1] > lcl_[ #0] and i <= mini else 1.0 if i < mini else #mc.relative_humidity_from_mixing_ratio(*tp, wvmrtop) #for i, tp in enumerate(zip(pressures, T))] RH = concatenate(RH) return windx, mtny, windy, lcl_, LCL, T.to("degC"), RH
def get_bounds_data(): """Provide pressure and height data for testing layer bounds calculation.""" pressures = np.linspace(1000, 100, 10) * units.hPa heights = pressure_to_height_std(pressures) return pressures, heights
def test_pressure_to_heights_units(): """Test that passing non-mbar units works.""" assert_almost_equal(pressure_to_height_std(29 * units.inHg), 262.859 * units.meter, 3)
def test_pressure_to_heights_basic(): """Test basic pressure to height calculation for standard atmosphere.""" pressures = np.array([975.2, 987.5, 956., 943.]) * units.mbar heights = pressure_to_height_std(pressures) values = np.array([321.5, 216.5, 487.6, 601.7]) * units.meter assert_almost_equal(heights, values, 1)
def skewt_plot(p, tc, tdc, t0, date, u=None, v=None, fout='sounding.png', latlon='', title='', show=False): """ h: heights p: pressure tc: Temperature [C] tdc: Dew point [C] date: date of the forecast u,v: u,v wind components adapted from: https://geocat-examples.readthedocs.io/en/latest/gallery/Skew-T/NCL_skewt_3_2.html#sphx-glr-gallery-skew-t-ncl-skewt-3-2-py """ Pmax = 150 # XXX upper limit print('Checking units') if p.attrs['units'] != 'hPa': print('P wrong units') exit() if tc.attrs['units'] != 'degC': print('Tc wrong units') exit() if tdc.attrs['units'] != 'degC': print('Tdc wrong units') exit() if t0.attrs['units'] != 'degC': print('T0 wrong units') exit() if type(u) != type(None) and type(v) != type(None): if u.attrs['units'] != 'm s-1': print('Wind wrong units') exit() LG.info('Inside skewt plot') p = p.values tc = tc.values tdc = tdc.values t0 = t0.mean().values u = u.values * 3.6 # km/h v = v.values * 3.6 # km/h # Grid plot LG.info('creating figure') fig = plt.figure(figsize=(11, 12)) LG.info('created figure') LG.info('creating axis') gs = gridspec.GridSpec(1, 2, width_ratios=[4, 1]) fig.subplots_adjust(wspace=0., hspace=0.) # ax1 = plt.subplot(gs[1:-1,0]) # Adding the "rotation" kwarg will over-ride the default MetPy rotation of # 30 degrees for the 45 degree default found in NCL Skew-T plots LG.info('created axis') LG.info('Creatin SkewT') skew = SkewT(fig, rotation=45, subplot=gs[0, 0]) ax = skew.ax LG.info('Created SkewT') if len(latlon) > 0: ax.text(0, 1, latlon, va='top', ha='left', color='k', fontsize=12, bbox=dict(boxstyle="round", ec=None, fc=(1., 1., 1., 0.9)), zorder=100, transform=ax.transAxes) # Plot the data, T and Tdew vs pressure skew.plot(p, tc, 'C3') skew.plot(p, tdc, 'C0') LG.info('plotted dew point and sounding') # LCL lcl_pressure, lcl_temperature = mpcalc.lcl(p[0] * units.hPa, t0 * units.degC, tdc[0] * units.degC) skew.plot(lcl_pressure, lcl_temperature, 'k.') # Calculate the parcel profile #XXX units workaround parcel_prof = mpcalc.parcel_profile(p * units.hPa, t0 * units.degC, tdc[0] * units.degC).to('degC') # Plot cloud base ind_cross = np.argmin(np.abs(parcel_prof.magnitude - tc)) p_base = np.max([lcl_pressure.magnitude, p[ind_cross]]) t_base = np.max([lcl_temperature.magnitude, tc[ind_cross]]) m_base = mpcalc.pressure_to_height_std(np.array(p_base) * units.hPa) m_base = m_base.to('m').magnitude skew.ax.axhline(p_base, color=(0.5, 0.5, 0.5), ls='--') skew.plot(p_base, t_base, 'C3o', zorder=100) skew.ax.text(t_base, p_base, f'{m_base:.0f}m', ha='left') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=1) LG.info('plotted parcel profile') # shade CAPE and CIN skew.shade_cape(p * units.hPa, tc * units.degC, parcel_prof) skew.shade_cin(p * units.hPa, tc * units.degC, parcel_prof, tdc * units.degC) LG.info('plotted CAPE and CIN') if type(u) != type(None) and type(v) != type(None): LG.info('Plotting wind') ax2 = plt.subplot(gs[0, 1], sharey=ax, zorder=-1) # ax2.yaxis.set_visible(False) ax2.yaxis.tick_right() # ax2.xaxis.tick_top() wspd = np.sqrt(u * u + v * v) ax2.scatter(wspd, p, c=p, cmap=HEIGHTS, zorder=10) gnd = mpcalc.pressure_to_height_std(np.array(p[0]) * units.hPa) gnd = gnd.to('m') # Ground ax2.axhline(p[0], c='k', ls='--') ax2.text(56, p[0], f'{int(gnd.magnitude)}m', horizontalalignment='right') # Techo ax2.axhline(p_base, c=(0.5, 0.5, 0.5), ls='--') ### Background colors ## #for i,c in enumerate(WindSpeed.colors): # rect = Rectangle((i*4, 150), 4, 900, color=c, alpha=0.5,zorder=-1) # ax2.add_patch(rect) ######################### ax2.set_xlim(0, 56) ax2.set_xlabel('Wspeed (km/h)') ax2.grid() def p2h(x): """ x in hPa """ y = mpcalc.pressure_to_height_std(np.array(x) * units.hPa) # y = y.metpy.convert_units('m') y = y.to('m') return y.magnitude def h2p(x): """ x in m """ # x = x.values y = mpcalc.height_to_pressure_std(np.array(x) * units.m) # y = y.metpy.convert_units('hPa') y = y.to('hPa') return y.magnitude # print('------') # print(p[0]) # print('---') # print(p2h(p[0])) # print('---') # print(h2p(p2h(p[0]))) # print('------') # exit() ax2y = ax2.secondary_yaxis(1.02, functions=(p2h, h2p)) ax2y.set_ylabel('height (m)') # ax2y.set_yticks(np.array([1000,2000,3000,4000,6000,6000,10000])) # XXX Not working ax2y.yaxis.set_major_formatter(ScalarFormatter()) ax2y.yaxis.set_minor_formatter(ScalarFormatter()) ################# ax2y.set_color('red') ax2y.tick_params(colors='red', size=7, width=1, which='both') # 'both' refers to minor and major axes # ax2y.set_yticks([1,2,3,4]) #ax2.set_xticks([0,5,10,15,20,30,40,50]) #ax2.set_xticklabels(['0','','10','','20','30','40','50']) ax2.set_xticks([0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56]) ax2.set_xticklabels( ['0', '', '8', '', '16', '', '24', '32', '40', '48', '56'], fontsize=11, va='center') plt.setp(ax2.get_yticklabels(), visible=False) # Hodograph ax_hod = inset_axes(ax2, '110%', '30%', loc=1) ax_hod.set_yticklabels([]) ax_hod.set_xticklabels([]) L = 80 ax_hod.text(0, L - 5, 'N', horizontalalignment='center', verticalalignment='center') ax_hod.text(L - 5, 0, 'E', horizontalalignment='center', verticalalignment='center') ax_hod.text(-(L - 5), 0, 'W', horizontalalignment='center', verticalalignment='center') ax_hod.text(0, -(L - 5), 'S', horizontalalignment='center', verticalalignment='center') h = Hodograph(ax_hod, component_range=L) h.add_grid(increment=20) h.plot_colormapped( -u, -v, p, cmap=HEIGHTS ) #'viridis_r') # Plot a line colored by pressure (altitude) LG.info('Plotted wind') # # print('=-=-=-=-=-=-=') # # print(ax2.get_yscale()) # # print(ax2y.get_yscale()) # # print('=-=-=-=-=-=-=') # # ax2y.yaxis.tick_right() # # # ax2y.set_ylim(*reversed(ax.get_ylim())) # # ax2y.set_ylim(*ax.get_ylim()) # # # calc pressure to height # # locs = ax2.get_yticks() # # labels = [mpcalc.pressure_to_height_std(h*units.hPa) for h in locs] # # labels = [round(l.to('m'),1) for l in labels] # # for xp,xh in zip(locs,labels): # # print(xp,xh) # # ax2y.set_yticks(locs) # # ax2y.set_yticklabels([f'{int(l.magnitude)}' for l in labels]) # Plot only every n windbarb n = 4 inds, = np.where(p > Pmax) break_p = 25 inds_low = inds[:break_p] inds_high = inds[break_p:] inds = np.append(inds_low[::n], inds_high) skew.plot_barbs( pressure=p[inds], #[::n], # * units.hPa, u=u[inds], #[::n], v=v[inds], #[::n], xloc=0.985, # fill_empty=True, sizes=dict(emptybarb=0.075, width=0.1, height=0.2)) # # # Draw line underneath wind barbs # # line = mlines.Line2D([1.05, 1.05], [0, 1], color='gray', linewidth=0.5, # # transform=ax.transAxes, # # dash_joinstyle='round', # # clip_on=False, # # zorder=0) # # ax.add_line(line) # Add relevant special lines # Choose starting temperatures in Kelvin for the dry adiabats LG.info('Plot adiabats, and other grid lines') skew.ax.text(t0, p[0], f'{t0:.1f}°C', va='top') skew.ax.axvline(t0, color=(0.5, 0.5, 0.5), ls='--') t0 = units.K * np.arange(243.15, 473.15, 10) skew.plot_dry_adiabats(t0=t0, linestyles='solid', colors='gray', linewidth=1) # Choose temperatures for moist adiabats t0 = units.K * np.arange(281.15, 306.15, 4) msa = skew.plot_moist_adiabats(t0=t0, linestyles='solid', colors='lime', linewidths=.75) # Choose mixing ratios w = np.array([0.001, 0.002, 0.003, 0.005, 0.008, 0.012, 0.020]).reshape(-1, 1) # Choose the range of pressures that the mixing ratio lines are drawn over p_levs = units.hPa * np.linspace(1000, 400, 7) skew.plot_mixing_lines(mixing_ratio=w, pressure=p_levs, linestyle='dotted', linewidths=1, colors='lime') LG.info('Plotted adiabats, and other grid lines') skew.ax.set_ylim(1000, Pmax) skew.ax.set_xlim(-20, 43) # skew.ax.set_xlim(-30, 40) # XXX gvutil not installed # gvutil.set_titles_and_labels(ax, maintitle="ATS Rawinsonde: degC + Thin wind") # Set axes limits and ticks # gvutil.set_axes_limits_and_ticks(ax=ax, xlim=[-30, 50], # yticks=[1000, 850, 700, 500, 400, 300, 250, 200, 150, 100]) # Change the style of the gridlines ax.grid(True, which='major', axis='both', color='tan', linewidth=1.5, alpha=0.5) ax.set_xlabel("Temperature (C)") ax.set_ylabel("P (hPa)") if len(title) == 0: title = f"{(date).strftime('%d/%m/%Y-%H:%M')} (local time)" ax.set_title(title, fontsize=20) else: ax.set_title(title, fontsize=20) if show: plt.show() LG.info('saving') fig.savefig(fout, bbox_inches='tight', pad_inches=0.1, dpi=150, quality=90) LG.info('saved') plt.close('all')
def delta_height(p1, p2): h1 = mpcalc.pressure_to_height_std(p1) h2 = mpcalc.pressure_to_height_std(p2) return h2 - h1