def report_servo_distances2(servo_agent, processed, area_graphs): r = Report() nmap = processed['nmap'] y_goal = processed['y_goal'] figparams = dict(figsize=(6, 6)) px = [] py = [] distances = [] for i in range(nmap.npoints()): obs = nmap.get_observations_at(i) # d = np.linalg.norm(obs - y_goal) d = servo_agent.get_distance(obs, y_goal) p = nmap.get_R2_point_at_index(i) px.append(p[0]) py.append(p[1]) distances.append(d) px = np.array(px) py = np.array(py) mx = np.linspace(px.min(), px.max(), 75) my = np.linspace(py.min(), py.max(), 75) XI, YI = np.meshgrid(mx, my) from scipy.interpolate import Rbf rbf = Rbf(px, py, distances, epsilon=0.05) ZI = rbf(XI, YI) D = np.zeros(shape=XI.shape) for i, j in itertools.product(range(len(mx)), range(len(my))): D[i, j] = np.min(np.hypot(mx[j] - px, my[i] - py)) # not a bug outside = D > 0.12 ZI[outside] = np.nan ZI = masked_invalid(ZI) f = r.figure() with f.plot('rbfs', **figparams) as pylab: pylab.gca().set_rasterized(True) from matplotlib import cm pylab.pcolormesh(XI, YI, ZI, cmap=cm.jet) # @UndefinedVariable pylab.scatter(px, py, 20, distances, cmap=cm.jet) # @UndefinedVariable pylab.colorbar() plot_style_servo_field_xy(pylab, area_graphs=area_graphs) with f.plot('interpolation', **figparams) as pylab: pylab.gca().set_rasterized(True) cmap = cm.jet # @UndefinedVariable cmap.set_bad('w', 0.) # pylab.pcolormesh(XI, YI, ZI, cmap=cm.jet) # @UndefinedVariable # bug with pcolormesh: black background pylab.pcolormesh(XI, YI, ZI, cmap=cmap, shading='gouraud') # @UndefinedVariable plot_style_servo_field_xy(pylab, area_graphs=area_graphs) return r
def addField(self, fieldName, fieldValue, longName=None, units=None, **kwargs): """ Add a field to the sounding .. _ndarray: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.htm Parameters ---------- fieldName : str Name of the field to be added. It supports abbreviations (see :py:attr:`abbreviations`) fieldValue : any Corresponding field value. If the field value is an ndarray_ then it will be converted to a :py:class:`~SkewTplus.sounding.soundingArray` with the corresponding metadata. longName : str, optional Long name of the field units : str , optional units of the field missingValue : float, optional Value for missing data. Occurrences of this value will be masked. The default missingValue can be change with :py:meth:`setMissingValue` """ missingValue = kwargs.pop('missingValue', self.missingValue) fieldName = self._getFieldName(fieldName) if isinstance(fieldValue, ndarray): _fieldValue = masked_invalid(fieldValue) _fieldValue = masked_values(_fieldValue, missingValue) _fieldValue.harden_mask() self._soundingData[fieldName] = soundingArray( _fieldValue, longName=longName, units=units, missingValue=missingValue) else: self._soundingData[fieldName] = fieldValue
def virtualTemp3(T, Td, p): """Virtual Temperature, but using vapor mixing ratio instead the vapor pressure Parameters --------- T: float Temperature in Celsius degrees Td: float Dew Point Temperature un Celsius Degrees p: float Atmospheric pressure (Pa) Returns ------- Virtual temperature : float32 In Celsius degrees """ T = masked_invalid(T) Td = masked_where(numpy.array(Td) < -degCtoK, Td) Td = masked_invalid(Td) _vaporMixingRatio = vaporMixingRatio(VaporPressure(Td), p) T.filled(-99999) #print(_vaporMixingRatio) Tc = (T + degCtoK) factor = (1. + (_vaporMixingRatio.data / Epsilon)) / (1. + _vaporMixingRatio.data) _virtualTemp = (Tc * factor) - degCtoK noTDMask = getmaskarray(_vaporMixingRatio) _virtualTemp.data[noTDMask] = T[noTDMask] _virtualTemp.mask = getmask(T) return _virtualTemp
def virtualTemp4(T, p): """Virtual Temperature, but using vapor mixing ratio instead the vapor pressure Parameters --------- T: float Temperature in Celsius degrees Td: float Dew Point Temperature un Celsius Degrees p: float Atmospheric pressure (Pa) Returns ------- Virtual temperature : float32 In Celsius degrees """ T = masked_invalid(T) _vaporMixingRatio = vaporMixingRatio(VaporPressure(T), p) return ((T + degCtoK) * (1. + (_vaporMixingRatio / Epsilon)) / (1. + _vaporMixingRatio)) - degCtoK
def fetchFromTxtFile(self, filePath, headerLength=6): """ Reads the raw profile data from a University of Wyoming sounding file. Notes ----- 1. Data can be downloaded from http://weather.uwyo.edu/upperair/sounding.html 2. The input file has to conform *Exactly* to the University of Wyoming file format. 3. The diagnostics at the end of the file are ignored. """ # Now this class uses Numpy's genfromtxt function to read the TXT files, # Idea taken from pymeteo.uwyo.py , fetch_from_file method def isValidValue(valueString): # True if the string can be converted to float try: _ = float(valueString) value = True except ValueError: value = False return value with open(filePath, 'r') as fileHandler: lines = fileHandler.readlines() # New: handle whitespace at top of file if present offset = 0 while not lines[0].strip(): print(lines[0]) lines.pop(0) offset += 1 # Handle the file header # First line for WRF profiles differs from the UWYO soundings header = lines[0] if header[:5] == '00000': # WRF profile self.addField('StationNumber', '00000') self.addField('Longitude', float(header.split()[5].strip(","))) self.addField('Latitude', float(header.split()[6])) self.addField('SoundingDate', header.split()[-1]) else: self.addField('StationNumber', header[:5]) dstr = (' ').join(header.split()[-4:]) soundingDate = datetime.strptime(dstr, "%HZ %d %b %Y") self.addField('SoundingDate', soundingDate.strftime("%Y-%m-%d_%H:%M:%S")) fieldsNames = lines[headerLength - 3].split() filedsUnits = lines[headerLength - 2].split() arePressureValues = [isValidValue(_line[0:7]) for _line in lines] fieldsData = numpy.genfromtxt( filePath, unpack=True, skip_header=headerLength + offset, max_rows=numpy.argwhere(arePressureValues).max() - headerLength + 1, delimiter=7, usemask=True, missing_values=self.missingValue) for fieldName, fieldValues, units in zip(fieldsNames, fieldsData, filedsUnits): # Set mask for missing data fieldValues = masked_invalid(fieldValues) fieldValues.harden_mask() self.addField(fieldName, fieldValues, units=units, missingValue=self.missingValue, longName=_txtSoundingLongNames[fieldName.lower()])
def __init__(self, inputData=None, fileFormat=None, stationId=None): """ Sounding Initialization It supports 4 types of initialization: * From a University of Wyoming txt file * From a University of Wyoming Website * Form an ARM sounding Netcdf file * From a dictionary with the field names, field values pairs All the field are added trough the :py:meth:`addField` method. The fields names support abbreviations when they are set or get. But internally the field names stored are not abbreviated. See :py:attr:`SkewT.sounding.abbreviations`. If the fileFormat keyword is not specified, it is assumed that the input data correspond to a filePath and it will try determine the format automatically. .. _datetime:`https://docs.python.org/2/library/datetime.html` Parameters ---------- inputData : str, datetime_ or dict If inputData is a dictionary, the sounding fields are taken from the dictionary (field names, field values pairs). If fileFormat is "web", then the inputData is considered the date of the sounding. See :py:meth:fetchFromWeb for date input format. Otherwise, if inputData is an string, it is considered a file path. If fileFormat is None (default) the initialization will try to automatically detect the file format. If inputData is None, the create instance correspond to an empty sounding. fileFormat : str File format. Supported values: * 'txt', "wrf", "uw or "wyoming" : University of Wyoming * "netcdf", "arm" : ARM netcdf soundings * 'web' : University of Wyoming website stationId : str Station ID. Used only when "web" is selected as format. See :py:meth:fetchFromWeb for more details. """ self._soundingData = dict() self.missingValue = -9999. self.addField('StationNumber', '(No Number)') self.addField('SoundingDate', '(No Date)') if inputData is not None: if fileFormat == "web": self.fetchFromWeb(inputData, stationId) else: if isinstance(inputData, str): # A file path was given if not isfile(inputData): raise fatalError("The file does not exists: %s\n" % (inputData)) if fileFormat is None: # Try automatic detection of file format try: self.fetchFromARMFile(inputData) except OSError: # If it is not a NETCDF , try TXT self.fetchFromTxtFile(inputData) else: # If the argument is an string assume that is # in the university of wyoming format if fileFormat.lower() in ('txt', "wrf", "uw", "wyoming"): self.fetchFromTxtFile(inputData) elif fileFormat.lower() in ('netcdf', 'arm'): self.fetchFromARMFile(inputData) else: raise fatalError( "Error loading Sounding", "Selected format not supported:%s" % fileFormat, "Accepted formats:%s" % str( ('txt', "wrf", "uw", "wyoming", "netcdf"))) elif isinstance(inputData, dict): for fieldName, fieldValue in inputData.items(): if isinstance(fieldValue, ndarray): dataValues = masked_invalid(fieldValue) dataValues = masked_values(fieldValue, self.missingValue) dataValues.harden_mask() self.addField(fieldName, fieldValue) else: self.addField(fieldName, fieldValue) else: raise fatalError( "Input Data not supported", "type(inputData)=%s" % str(type(inputData)))
def addProfile(self, pressure, temperature, dewPointTemperature, hPa=True, celsius=True, method=0, initialLevel=0, parcel=True, label=None, diagnostics=False, markers=True, useVirtual=True, **kwargs): """ Add a profile to the Skew-T axis .. _`matplotlib.legend`: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.legend .. _ndarray: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html .. _MaskedArray: https://docs.scipy.org/doc/numpy/reference/maskedarray.html The input data can be ndarray_ or MaskedArray_. If MaskedArray_ are used, the pressure levels with masked or invalid entries in the pressure and temperature arrays are removed. Masked values in the dewPointTemperature are not plotted. Parameters ---------- pressure : ndarray_ or MaskedArray_ Pressure levels of the sounding temperature : ndarray_ or MaskedArray_ Temperature profile at pressure levels dewPointTemperature : : ndarray_ or MaskedArray_ Dew point temperature at pressure levels hPa: bool, optional If is True, the pressure levels are in hPa. Otherwise Pascals is assumed celsius : bool, optional If is True, the temperatures units correspond to celsius degrees. Otherwise Kelvin degrees units are assumed parcel : bool, optional If True, the parcel analysis is carried out. method : int, optional Parcel analysis method used. Supported: * Most Unstable : method=0 * Single Parcel: method=1 * Mixed Layer : method=2 (Not supported yet) initialLevel : int, optional Initial level (index) used to compute the parcel analysis. Levels below this value are ignored. For the Single Parcel analysis, this level correspond to the parcel used. By default, the initial level is 0 (surface). For the Most Unstable method, this value is ignored. label : str, optional Label assigned to the profile lines. If None, no label is used. When a line is labeled, the legend is added automatically to the plot. use keyword **loc** to control the position. diagnostics : bool, optional If True a text box is added to the upper right corner with some diagnostics from the parcel analysis. markers: bool, optional If True, the LCL, LFC and EL are marked in the plot with markers. useVirtual : bool, optional If True, in the parcel analysis, CAPE and CIN are computed used the virtual temperature. The temperatures plotted in the SkewT diagram will correspond to virtual temperature instead of temperature. If False, virtual temperatures corrections are neglected and the original temperature is plotted. Other Parameters ---------------- loc : str Legend location. See `matplotlib.legend`_ for more details. kwargs : extra The remaining extra keyword arguments are passed to the plot function when plotting the temperature profiles. """ mask = numpy.zeros_like(pressure, dtype=bool) if isinstance(pressure, masked_array): pressure[pressure.mask] = numpy.nan mask = logical_or(mask, getmaskarray(pressure)) if isinstance(temperature, masked_array): temperature[temperature.mask] = numpy.nan mask = logical_or(mask, getmaskarray(temperature)) if isinstance(dewPointTemperature, masked_array): dewPointMask = getmaskarray(dewPointTemperature) dewPointTemperature.data[dewPointMask] = numpy.nan dewPointTemperature = numpy.asarray(dewPointTemperature.data, dtype=numpy.float32) pressure = masked_invalid(pressure[~mask]) temperature = masked_invalid(temperature[~mask]) dewPointTemperature = masked_invalid(dewPointTemperature[~mask]) # Convert temperatures and pressure to hPa if not hPa: pressure /= 100 if not celsius: temperature -= degCtoK dewPointTemperature -= degCtoK if parcel: parcelAnalysisResult = parcelAnalysis(pressure, temperature, dewPointTemperature, hPa=True, celsius=True, useVirtual=1, initialLevel=initialLevel, method=method) initialLevel = parcelAnalysisResult['initialLevel'] initialTemperature = temperature[initialLevel] pressureAtLCL = parcelAnalysisResult['pressureAtLCL'] temperatureAtLCL = parcelAnalysisResult['temperatureAtLCL'] pressureAtLFC = parcelAnalysisResult['pressureAtLFC'] temperatureAtLFC = parcelAnalysisResult['temperatureAtLFC'] pressureAtEL = parcelAnalysisResult['pressureAtEL'] temperatureAtEL = parcelAnalysisResult['temperatureAtEL'] parcelTemperature = liftParcel(initialTemperature, pressure, pressureAtLCL, initialLevel=initialLevel, hPa=True, celsius=True) # Add LCL belowLCL = numpy.where(pressure > pressureAtLCL, True, False) newParcelTemperature = numpy.concatenate( (parcelTemperature[belowLCL], [temperatureAtLCL], parcelTemperature[~belowLCL])) newPressure = numpy.concatenate( (pressure[belowLCL], [pressureAtLCL], pressure[~belowLCL])) # Add EL belowEL = numpy.where(newPressure > pressureAtEL, True, False) newParcelTemperature = numpy.concatenate( (newParcelTemperature[belowEL], [temperatureAtEL], newParcelTemperature[~belowEL])) newPressure = numpy.concatenate( (newPressure[belowEL], [pressureAtEL], newPressure[~belowEL])) belowLFC = numpy.where(newPressure > pressureAtLFC, True, False) newParcelTemperature = numpy.concatenate( (newParcelTemperature[belowLFC], [temperatureAtLFC], newParcelTemperature[~belowLFC])) newPressure = numpy.concatenate( (newPressure[belowLFC], [pressureAtLFC], newPressure[~belowLFC])) newTemperature = numpy.interp(newPressure, pressure[::-1], temperature[::-1]) newParcelTemperature = masked_invalid(newParcelTemperature) if useVirtual: newDewPointTemperature = numpy.interp( newPressure, pressure[::-1], dewPointTemperature[::-1]) newTemperature = virtualTemp3(newTemperature, newDewPointTemperature, newPressure * 100) belowLCL = (newPressure >= pressureAtLCL) newParcelTemperature[belowLCL] = virtualTemp3( newParcelTemperature[belowLCL], dewPointTemperature[initialLevel], newPressure[belowLCL] * 100) aboveLCL = (newPressure < pressureAtLCL) newParcelTemperature[aboveLCL] = virtualTemp4( newParcelTemperature[aboveLCL], newPressure[aboveLCL] * 100) else: newTemperature = temperature newPressure = pressure kwargs['zorder'] = kwargs.pop('zorder', 5) kwargs['linewidth'] = kwargs.pop('linewidth', 2.0) kwargs['linestyle'] = kwargs.pop('linestyle', '-') loc = kwargs.pop('loc', 'best') temperatureLine, = self.plot(newTemperature, newPressure, 'r', **kwargs) self.plot(dewPointTemperature, pressure, 'b', **kwargs) if label is not None: self.linesHandlers.append(temperatureLine) self.linesLabels.append(label) self.legend(self.linesHandlers, self.linesLabels, loc=loc) if parcel: self.plot(newParcelTemperature, newPressure, 'k', **kwargs) if parcelAnalysisResult['CAPE'] > 0: # Positive Buoyancy cond1 = (newPressure <= pressureAtLFC) * (newPressure >= pressureAtEL) self.fill_betweenx(newPressure, newParcelTemperature, newTemperature, where=cond1, \ color="#ff0009", alpha=0.4, zorder=10) # Negative Buoyancy validMask = ~getmaskarray(masked_invalid(newParcelTemperature)) cond2 = ((newParcelTemperature[validMask] <= newTemperature[validMask]) * (newPressure[validMask] >= pressureAtLFC)) self.fill_betweenx(newPressure[validMask], newParcelTemperature[validMask], newTemperature[validMask], where=cond2, \ color="#045cff", alpha=0.4, zorder=10) if markers: if useVirtual: temperatureAtLCL = virtualTemp4(temperatureAtLCL, pressureAtLCL * 100) temperatureAtLFC = virtualTemp4(temperatureAtLFC, pressureAtLFC * 100) temperatureAtEL = virtualTemp4(temperatureAtEL, pressureAtEL * 100) self.plot(temperatureAtLCL, pressureAtLCL, ls='', marker='o', color='r') self.plot(temperatureAtLFC, pressureAtLFC, ls='', marker='o', color='g') self.plot(temperatureAtEL, pressureAtEL, ls='', marker='o', color='k') if diagnostics: # Add text to sounding dtext = "Diagnostics:\n" # dtext ="Parcel: %s\n"%ptype.upper() # dtext+="P :%6.1fhPa\n"%startp # dtext+="T : %4.1fC\n"%startt # dtext+="Td : %4.1fC\n"%startdp dtext += "-------------\n" dtext += "P_par:%6.1fhPa\n" % (pressure[initialLevel]) dtext += "P_LCL:%6.1fhPa\n" % ( parcelAnalysisResult['pressureAtLCL']) dtext += "T_LCL:%4.1fC\n" % ( parcelAnalysisResult['temperatureAtLCL']) dtext += "P_LFC:%6.1fhPa\n" % ( parcelAnalysisResult['pressureAtLFC']) dtext += "T_LFC:%4.1fC\n" % ( parcelAnalysisResult['temperatureAtLFC']) dtext += "P_EL:%6.1fhPa\n" % ( parcelAnalysisResult['pressureAtEL']) dtext += "T_EL:%4.1fC\n" % ( parcelAnalysisResult['temperatureAtEL']) dtext += "CAPE:%.1f\n" % parcelAnalysisResult['CAPE'] dtext += "CIN: %.1fJ" % parcelAnalysisResult['CIN'] axesBox = self.get_position().get_points() self.figure.text(axesBox[1, 0], axesBox[1, 1], dtext, fontname="monospace", backgroundcolor='white', zorder=10, verticalalignment='top', horizontalalignment='right', multialignment="left")