def test_3d(self): 'Test of geostrophic wind calculation with 3D array' z = np.array([[48, 49, 48], [49, 50, 49], [48, 49, 48]]) * 100. # Using g as the value for f allows it to cancel out z3d = np.dstack((z, z)) * units.meter ug, vg = geostrophic_wind(z3d, g.magnitude / units.sec, 100. * units.meter, 100. * units.meter) true_u = np.array([[-1, 0, 1]] * 3) * units('m/s') true_v = -true_u.T true_u = concatenate((true_u[..., None], true_u[..., None]), axis=2) true_v = concatenate((true_v[..., None], true_v[..., None]), axis=2) assert_array_equal(ug, true_u) assert_array_equal(vg, true_v)
def test_bunkers_motion(): """Test Bunkers storm motion with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') motion = concatenate(bunkers_storm_motion(data['pressure'], data['u_wind'], data['v_wind'], data['height'])) truth = [1.4537892577864744, 2.0169333025630616, 10.587950761120482, 13.915130377372801, 6.0208700094534775, 7.9660318399679308] * units('m/s') assert_almost_equal(motion.flatten(), truth, 8)
def test_bunkers_motion(): """Test Bunkers storm motion with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') motion = concatenate(bunkers_storm_motion(data['pressure'], data['u_wind'], data['v_wind'], data['height'])) truth = [2.18346161, 0.86094706, 11.6006767, 12.53639395, 6.89206916, 6.69867051] * units('m/s') assert_almost_equal(motion.flatten(), truth, 8)
def parcel_profile(pressure, temperature, dewpt): r"""Calculate the profile a parcel takes through the atmosphere. The parcel starts at `temperature`, and `dewpt`, lifted up dry adiabatically to the LCL, and then moist adiabatically from there. `pressure` specifies the pressure levels for the profile. Parameters ---------- pressure : `pint.Quantity` The atmospheric pressure level(s) of interest. The first entry should be the starting point pressure. temperature : `pint.Quantity` The starting temperature dewpt : `pint.Quantity` The starting dew point Returns ------- `pint.Quantity` The parcel temperatures at the specified pressure levels. See Also -------- lcl, moist_lapse, dry_lapse """ # Find the LCL lcl_pressure = lcl(pressure[0], temperature, dewpt)[0].to(pressure.units) # Find the dry adiabatic profile, *including* the LCL. We need >= the LCL in case the # LCL is included in the levels. It's slightly redundant in that case, but simplifies # the logic for removing it later. press_lower = concatenate( (pressure[pressure >= lcl_pressure], lcl_pressure)) t1 = dry_lapse(press_lower, temperature) # Find moist pseudo-adiabatic profile starting at the LCL press_upper = concatenate( (lcl_pressure, pressure[pressure < lcl_pressure])) t2 = moist_lapse(press_upper, t1[-1]).to(t1.units) # Return LCL *without* the LCL point return concatenate((t1[:-1], t2[1:]))
def test_concatenate_masked(): """Test concatenate preserves masks.""" d1 = units.Quantity(np.ma.array([1, 2, 3], mask=[False, True, False]), 'degC') result = concatenate((d1, 32 * units.degF)) truth = np.ma.array([1, np.inf, 3, 0]) truth[1] = np.ma.masked assert_array_almost_equal(result, units.Quantity(truth, 'degC'), 6) assert_array_equal(result.mask, np.array([False, True, False, False]))
def _find_append_zero_crossings(x, y): r""" Find and interpolate zero crossings. Estimate the zero crossings of an x,y series and add estimated crossings to series, returning a sorted array with no duplicate values. Parameters ---------- x : `pint.Quantity` x values of data y : `pint.Quantity` y values of data Returns ------- x : `pint.Quantity` x values of data y : `pint.Quantity` y values of data """ # Find and append crossings to the data crossings = find_intersections(x[1:], y[1:], np.zeros_like(y[1:]) * y.units) print(crossings) x = concatenate((x, crossings[0])) y = concatenate((y, crossings[1])) # Resort so that data are in order sort_idx = np.argsort(x) x = x[sort_idx] y = y[sort_idx] # Remove duplicate data points if there are any keep_idx = np.ediff1d(x, to_end=[1]) > 0 x = x[keep_idx] y = y[keep_idx] return x, y
skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) # Y limits from 1000 in the botton to 100 millibars to the top skew.ax.set_xlim(-40, 60) # X limits # Add the relevant special lines\n", skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() plt.show() # Calculate LCL height and plot as black dot\n", from metpy.calc import lcl, dry_lapse from metpy.units import units, concatenate l = lcl(p[0], T[0], Td[0]) # we can calculate the lcl level from the starting p, T, Td lcl_temp = dry_lapse(concatenate((p[0], l)), T[0])[-1].to('degC') # Temperature of the lcl using the dry lapse skew.plot(l, lcl_temp, 'ko', markerfacecolor='black') # plot the lcl level on the temperature with a black circle (ko) filled # Calculate full parcel profile and add to plot as black line\n", from metpy.calc import parcel_profile prof = parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Example of coloring area between profiles skew.ax.fill_betweenx(p, T, prof, where=T>= prof, facecolor='blue', alpha=0.4) skew.ax.fill_betweenx(p, T, prof, where=T< prof, facecolor='red', alpha=0.4) # # An example of a slanted line at constant T -- in this case the 0 isotherm level = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # highligh the 0 degree isoterm from metpy.plots import Hodograpgh
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 test_concatenate(): """Test basic functionality of unit-aware concatenate.""" result = concatenate((3 * units.meter, 400 * units.cm)) assert_array_equal(result, np.array([3, 4]) * units.meter) assert not isinstance(result.m, np.ma.MaskedArray)
########################################### # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, rotation=45) # 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(-40, 60) # Calculate LCL height and plot as black dot l = mpcalc.lcl(p[0], T[0], Td[0]) lcl_temp = mpcalc.dry_lapse(concatenate((p[0], l)), T[0])[-1].to('degC') skew.plot(l, lcl_temp, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Example of coloring area between profiles greater = T >= prof skew.ax.fill_betweenx(p, T, prof, where=greater, facecolor='blue', alpha=0.4) skew.ax.fill_betweenx(p, T, prof, where=~greater, facecolor='red', alpha=0.4) # An example of a slanted line at constant T -- in this case the 0 # isotherm l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2)
# # Often times we will want to calculate some thermodynamic parameters of a # sounding. The MetPy calc module has many such calculations already implemented! # # * **Lifting Condensation Level (LCL)** - The level at which an air parcel's # relative humidity becomes 100% when lifted along a dry adiabatic path. # * **Parcel Path** - Path followed by a hypothetical parcel of air, beginning # at the surface temperature/pressure and rising dry adiabatically until # reaching the LCL, then rising moist adiabatially. # Calculate the LCL level parcel_lcl = mpcalc.lcl(p[0], T[0], Td[0]) # Determine the LCL temperature by lifting a parcel from the surface # pressure and temperature via a dry adiabatic process. lcl_temperature = mpcalc.dry_lapse(concatenate((p[0], parcel_lcl)), T[0])[-1].to('degC') print(parcel_lcl, lcl_temperature) # Calculate the parcel profile. parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') ########################################################################## # Basic Skew-T Plotting # --------------------- # # The Skew-T (log-P) diagram is the standard way to view rawinsonde data. The # y-axis is height in pressure coordinates and the x-axis is temperature. The # y coordinates are plotted on a logarithmic scale and the x coordinate system # is skewed. An explanation of skew-T interpretation is beyond the scope of this
def plot(self, t, td, p, u, v, lat, long, time): r"""Displays the Skew-T data on a matplotlib figure. Args: t (array-like): A list of temperature values. td (array-like): A list of dewpoint values. p (array-like): A list of pressure values. u (array-like): A list of u-wind component values. v (array-like): A list of v-wind component values. lat (string): A string containing the requested latitude value. long (string): A string containing the requested longitude value. time (string): A string containing the UTC time requested with seconds truncated. Returns: None. Raises: None. """ # Create a new figure. The dimensions here give a good aspect ratio self.skew = SkewT(self.figure, rotation=40) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot self.skew.plot(p, t, 'r') self.skew.plot(p, td, 'g') self.skew.plot_barbs(p, u, v, barbcolor='#FF0000', flagcolor='#FF0000') self.skew.ax.set_ylim(1000, 100) self.skew.ax.set_xlim(-40, 60) # Axis colors self.skew.ax.tick_params(axis='x', colors='#A3A3A4') self.skew.ax.tick_params(axis='y', colors='#A3A3A4') # Calculate LCL height and plot as black dot l = lcl(p[0], t[0], td[0]) lcl_temp = dry_lapse(concatenate((p[0], l)), t[0])[-1].to('degC') self.skew.plot(l, lcl_temp, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = parcel_profile(p, t[0], td[0]).to('degC') self.skew.plot(p, prof, 'k', linewidth=2) # Color shade areas between profiles self.skew.ax.fill_betweenx(p, t, prof, where=t >= prof, facecolor='#5D8C53', alpha=0.7) self.skew.ax.fill_betweenx(p, t, prof, where=t < prof, facecolor='#CD6659', alpha=0.7) # Add the relevant special lines self.skew.plot_dry_adiabats() self.skew.plot_moist_adiabats() self.skew.plot_mixing_lines() # Set title deg = u'\N{DEGREE SIGN}' self.skew.ax.set_title('Sounding for ' + lat + deg + ', ' + long + deg + ' at ' + time + 'z', y=1.02, color='#A3A3A4') # Discards old graph, works poorly though # skew.ax.hold(False) # Figure and canvas widgets that display the figure in the GUI # set canvas size to display Skew-T appropriately self.canvas.setMaximumSize(QtCore.QSize(800, 2000)) # refresh canvas self.canvas.draw()