def fetchFromWeb(self, soundingDate, stationId): """ Load University of Wyoming sound data from the uwyo website .. _datetime:`https://docs.python.org/2/library/datetime.html` Parameters ---------- soundingDate : str or datetime_ Date of the selected sounding to download. The date could be a datetime_ object or an string. The string should have the following format: "%Y%m%d:%H" stationId : str Station ID """ # Inspired in pymeteo.uwyo.py , fetch_from_web if isinstance(soundingDate, string_types): soundingDate = datetime.strptime(soundingDate, "%Y%m%d:%H") base_url = "http://weather.uwyo.edu/cgi-bin/sounding" payload = { "TYPE": r"TEXT:LIST", "YEAR": soundingDate.strftime("%Y"), "MONTH": soundingDate.strftime("%m"), "FROM": soundingDate.strftime("%d%H"), "TO": soundingDate.strftime("%d%H"), "STNM": stationId, } myresponse = requests.get(base_url, params=payload) if "Can't get" in myresponse.text: raise fatalError( "Error retrieving sounding from UW web page.", "Observations not present for that station or that date", "Check the date and the station name", "Selected Station ID:%s" % stationId, "Selected Date:%s" % soundingDate.strftime("%Y%m%d:%H"), ) else: parsedResponse = UW_HTMLParser() parsedResponse.feed(myresponse.text) with NamedTemporaryFile(mode="w") as tempFile: tempFile.write("\n".join(parsedResponse.soundingData)) self.fetchFromTxtFile(tempFile.name)
def getCleanSounding(self): """ Clean the sounding, when Temperature or pressure has a missing values, removing that pressure level. It returns the pressure in Pa and temperatures in Celsius """ pressure = self['pressure'] temperature = self['temperature'] dewPointTemperature = self['dewPointTemperature'] dewPointTemperature[getmaskarray( dewPointTemperature)] = self.missingValue if pressure.units is not None: if pressure.units.lower() == 'hpa': hPa = True elif pressure.units.lower() == 'pa': hPa = False else: raise fatalError( "Error getting clean sounding plot", "Pressure units not supported:%s" % pressure.units, "Supported: hPa or Pa ") else: hPa = True if temperature.units is not None: if dewPointTemperature.units is not None: if temperature.units.lower( ) != dewPointTemperature.units.lower(): raise fatalError( "Error getting clean sounding plot", "Temperature units: %s" % temperature.units, "Dew Point Temp: %s" % dewPointTemperature.units) if temperature.units.lower() == 'c': celsius = True elif temperature.units.lower() == 'k': celsius = False else: raise fatalError( "Error getting clean sounding plot", "Temperature units not supported:%s" % temperature.units, "Supported: C or K ") else: celsius = True mask = ~mask_or( getmaskarray(pressure), getmaskarray(temperature), shrink=False) if hPa: _pressure = pressure.data[mask] else: _pressure = pressure.data[mask] / 100 if celsius: _temperature = temperature.data[mask] _dewPointTemperature = masked_values( dewPointTemperature.data[mask], self.missingValue) else: _temperature = temperature.data[mask] - 273.15 _dewPointTemperature = masked_values( dewPointTemperature.data[mask], self.missingValue) - 273.15 return (_pressure, _temperature, _dewPointTemperature)
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 parcelAnalysis(pressure, temperature, dewPointTemperature, hPa=False, celsius=False, maxPressureStep=2., fullFields=1, method=0, initialLevel=0, useVirtual=1, tolerance=0.1, maxIterations=20, depth=30000): """ Function to perform a parcel analysis The shape of the input arrays (*pressure*, *temperature* and *dewPointTemperature*) should be equal. By default pressure and temperature are treated as Pascals and Kelvins degrees respectively. The working units can be selected by *hPa* and *celsius* keywords. The output values will automatically be converted to the selected units. The dimensions of the arrays should be one of the followings: * [time,height,latitude or y ,longitude or x] (3D + time) * [height,latitude or y ,longitude or x] (3D) * [height] 1D) See :py:func:`_parcelAnalysis1D` for a complete description of input parameters and returned values. Parameters ---------- hPa : bool, optional If True, pressure is treated as in hPa instead of Pa. celsius : bool, optional If True, temperature is considered as Celsius instead of Kelvin degrees. Other Parameters ---------------- See :py:func:`_parcelAnalysis1D` for a complete description of input parameters. Returns ------- See :py:func:`_parcelAnalysis1D` for a complete description of the returned values. """ if ((pressure.shape != temperature.shape) or (dewPointTemperature.shape != temperature.shape)): raise fatalError( "Error computing Parcel Analysis", "Input array shape mismatch", "pressure.shape = %s" % str(pressure.shape), "temperature.shape = %s" % str(temperature.shape), "dewPointTemperature.shape = %s" % str(dewPointTemperature.shape), ) if isinstance(dewPointTemperature, masked_array): dewPointTemperature.data[dewPointTemperature.mask] = -9999. pressure = numpy.asarray(pressure, dtype=numpy.float32) temperature = numpy.asarray(temperature, dtype=numpy.float32) dewPointTemperature = numpy.asarray(dewPointTemperature, dtype=numpy.float32) if hPa: pressureConversionFactor = 100 pressureUnits = 'hPa' else: pressureConversionFactor = 1 pressureUnits = 'Pa' if celsius: temperatureConversionOffset = degCtoK temperatureUnits = 'Celsius' else: temperatureConversionOffset = 0 temperatureUnits = 'Kelvin' args = (pressure * pressureConversionFactor, temperature + temperatureConversionOffset, dewPointTemperature + temperatureConversionOffset) kwargs = dict(maxPressureStep=maxPressureStep, fullFields=fullFields, method=method, initialLevel=initialLevel, tolerance=tolerance, maxIterations=maxIterations, useVirtual=useVirtual, depth=depth) if pressure.ndim == 4: myParcelAnalysis = _parcelAnalysis4D(*args, **kwargs) elif pressure.ndim == 3: myParcelAnalysis = _parcelAnalysis3D(*args, **kwargs) elif pressure.ndim == 1: myParcelAnalysis = _parcelAnalysis1D(*args, **kwargs) else: raise fatalError( "Error computing Parcel Analysis", "Number of array dimensions not supported" "Number of array dimensions = %d" % pressure.ndim) # Convert the values to the selected units for key in myParcelAnalysis: if 'temperature' in key: myParcelAnalysis[key] -= temperatureConversionOffset if 'pressure' in key: myParcelAnalysis[key] /= pressureConversionFactor myParcelAnalysis['pressureUnits'] = pressureUnits myParcelAnalysis['temperatureUnits'] = temperatureUnits return myParcelAnalysis