def test_hPa2Pa(self): """Convert pressure from hPa to Pa""" self.assertEqual(metutils.convert(0, "hPa", "Pa"), 0.0) self.assertEqual(metutils.convert(1, "hPa", "Pa"), 100.0) self.assertEqual(metutils.convert(10, "hPa", "Pa"), 1000.0) self.assertEqual(metutils.convert(15, "hPa", "Pa"), 1500.0) self.assertEqual(metutils.convert(600, "hPa", "Pa"), 60000.0)
def test_mps2kmphr(self): """Convert from m/s to km/h""" self.assertEqual(metutils.convert(0, "mps", "kph"), 0.) self.assertEqual(metutils.convert(1, "mps", "kph"), 3.6) self.assertEqual(metutils.convert(5, "mps", "kph"), 18.) self.assertEqual(metutils.convert(15, "mps", "kph"), 54.) self.assertEqual(metutils.convert(100, "mps", "kph"), 360.)
def test_kgmetre2hPa(self): """Convert from Pa to hPa""" self.assertEqual(metutils.convert(0, "Pa", "hPa"), 0.0) self.assertEqual(metutils.convert(1, "Pa", "hPa"), 0.01) self.assertEqual(metutils.convert(100, "Pa", "hPa"), 1.0) self.assertEqual(metutils.convert(200, "Pa", "hPa"), 2.0) self.assertEqual(metutils.convert(600, "Pa", "hPa"), 6.0)
def pressureProfile(self, i, R): """ Calculate the pressure profile at time `i` at the radiuses `R` around the tropical cyclone. :type i: int :param i: the time. :type R: :class:`numpy.ndarray` :param R: the radiuses around the tropical cyclone. """ from PressureInterface.pressureProfile import PrsProfile as PressureProfile p = PressureProfile(R, convert(self.track.EnvPressure[i], 'hPa', 'Pa'), convert(self.track.CentralPressure[i], 'hPa', 'Pa'), self.track.rMax[i], self.track.Latitude[i], self.track.Longitude[i], self.beta, beta1=self.beta1, beta2=self.beta2) try: pressure = getattr(p, self.profileType) except AttributeError: msg = '%s not implemented in pressureProfile' % self.profileType log.exception(msg) return pressure()
def vmax(pCentre, pEnv, type="holland", beta=1.3, rho=1.15): """ Calculate the maximum wind speed from the pressure difference. :param float pc: central pressure (Pa) :param float pe: environmental pressure (Pa) :param str type: which Vmax relation to use (Willoughby & Rahn, Holland or Atkinson & Holliday) :param float beta: Holland's (1980) beta parameter. Only used for the Holland estimation (type=holland) :param float rho: air density (default=1.15 kg/m^3) :return: maximum wind speed. For types 1 & 2, this is a gradient level wind. The relation used in type 3 (Atkinson & Holliday) was determined using surface wind observations so should be used with caution at the gradient level. :raises ValueError: if environmental pressure is lower than central pressure Note: The pressure should ideally be passed in units of Pa, but the function will accept hPa and automatically convert to Pa. """ # Convert from hPa to Pa if necessary: if pCentre < 10000: pCentre = metutils.convert(pCentre, "hPa", "Pa") if pEnv < 10000: pEnv = metutils.convert(pEnv, "hPa", "Pa") if pEnv < pCentre: raise ValueError, "Error in vmax - Environmental pressure is less than central pressure. Check values and/or order of input arguments" dP = pEnv - pCentre if type == "willoughby": # Default: Most advanced estimation technique: # Willoughby & Rahn (2004), Parametric Representation of the # Primary Hurricane Vortex. Part I: Observations and # Evaluation of the Holland (1980) Model. # Mon. Wea. Rev., 132, 3033-3048 vMax = 0.6252 * sqrt(dP) elif type == "holland": # Holland (1980), An Analytic Model of the Wind and Pressure # Profiles in Hurricanes. Mon. Wea. Rev, 108, 1212-1218 # Density of air is assumed to be 1.15 kg/m^3. # beta is assumed to be 1.3. Other values can be specified. # Gradient level wind (assumed maximum). vMax = sqrt(beta * dP / (exp(1) * rho)) elif type == "atkinson": # Atkinson and Holliday (1977), Tropical Cyclone Minimum Sea # Level Pressure / Maximum Sustained Wind Relationship for # the Western North Pacific. Mon. Wea. Rev., 105, 421-427 # Maximum 10m, 1-minute wind speed. Uses pEnv as 1010 hPa vMax = 3.04 * power(1010 - metutils.convert(pCentre, "Pa", "hPa"), 0.644) else: raise NotImplementedError, "Vmax type " + type + " not implemented" return vMax
def vmax(pCentre, pEnv, type="holland", beta=1.3, rho=1.15): """ Calculate the maximum wind speed from the pressure difference. :param float pc: central pressure (Pa) :param float pe: environmental pressure (Pa) :param str type: which Vmax relation to use (Willoughby & Rahn, Holland or Atkinson & Holliday) :param float beta: Holland's (1980) beta parameter. Only used for the Holland estimation (type=holland) :param float rho: air density (default=1.15 kg/m^3) :return: maximum wind speed. For types 1 & 2, this is a gradient level wind. The relation used in type 3 (Atkinson & Holliday) was determined using surface wind observations so should be used with caution at the gradient level. :raises ValueError: if environmental pressure is lower than central pressure Note: The pressure should ideally be passed in units of Pa, but the function will accept hPa and automatically convert to Pa. """ # Convert from hPa to Pa if necessary: if pCentre < 10000: pCentre = metutils.convert(pCentre, "hPa", "Pa") if pEnv < 10000: pEnv = metutils.convert(pEnv, "hPa", "Pa") if pEnv < pCentre: raise ValueError, "Error in vmax - Environmental pressure is less than central pressure. Check values and/or order of input arguments" dP = pEnv - pCentre if type == "willoughby": # Default: Most advanced estimation technique: # Willoughby & Rahn (2004), Parametric Representation of the # Primary Hurricane Vortex. Part I: Observations and # Evaluation of the Holland (1980) Model. # Mon. Wea. Rev., 132, 3033-3048 vMax = 0.6252*sqrt(dP) elif type == "holland": # Holland (1980), An Analytic Model of the Wind and Pressure # Profiles in Hurricanes. Mon. Wea. Rev, 108, 1212-1218 # Density of air is assumed to be 1.15 kg/m^3. # beta is assumed to be 1.3. Other values can be specified. # Gradient level wind (assumed maximum). vMax = sqrt(beta*dP/(exp(1)*rho)) elif type == "atkinson": # Atkinson and Holliday (1977), Tropical Cyclone Minimum Sea # Level Pressure / Maximum Sustained Wind Relationship for # the Western North Pacific. Mon. Wea. Rev., 105, 421-427 # Maximum 10m, 1-minute wind speed. Uses pEnv as 1010 hPa vMax = 3.04*power(1010 - metutils.convert(pCentre,"Pa","hPa"), 0.644) else: raise NotImplementedError, "Vmax type " + type + " not implemented" return vMax
def test_deg2km(self): """Convert distance in degrees to distance in km""" self.assertEqual(metutils.convert(0, "deg", "km"), 0) self.assertAlmostEqual(metutils.convert(1, "deg", "km"), (1 / (360 / (2 * pi * 6367))), 3) self.assertAlmostEqual(metutils.convert(2, "deg", "km"), (2 / (360 / (2 * pi * 6367))), 3) self.assertAlmostEqual(metutils.convert(10, "deg", "km"), (10 / (360 / (2 * pi * 6367))), 3)
def test_km2deg(self): """Convert distance in km to distance in degrees""" self.assertEqual(metutils.convert(0, "km", "deg"), 0) self.assertAlmostEqual(metutils.convert(1, "km", "deg"), 360 / (2 * pi * 6367), 3) self.assertAlmostEqual(metutils.convert(2, "km", "deg"), 720 / (2 * pi * 6367), 3) self.assertAlmostEqual(metutils.convert(10, "km", "deg"), 3600 / (2 * pi * 6367), 3)
def plotMap(self): """Plot return period wind speed maps""" lon, lat, years, inputData = self.loadFile(self.inputFile, 'wspd') if lon.min() > 180.: lon = lon - 360. [xgrid, ygrid] = np.meshgrid(lon, lat) dmask = inputData.mask inputData = metutils.convert(inputData, 'mps', self.plotUnits.units) inputData = ma.array(inputData, mask=dmask) map_kwargs = dict(llcrnrlon=xgrid.min(), llcrnrlat=ygrid.min(), urcrnrlon=xgrid.max(), urcrnrlat=ygrid.max(), projection='merc', resolution='i') for i, year in enumerate(years): log.debug("Plotting %d-year return period hazard map", year) title = '%d-Year ARI Cyclonic Wind Hazard' % (year) imageFilename = '%d_yrRP_hazard_map.png' % (year) filename = pjoin(self.plotPath, imageFilename) cbarlab = "Wind speed (%s)"%self.plotUnits.units levels = self.plotUnits.levels if self.smooth: dx = np.mean(np.diff(xgrid)) data = smooth(inputData[i, :, :], int(1/dx)) else: data = inputData[i, :, :] saveHazardMap(data, xgrid, ygrid, title, levels, cbarlab, map_kwargs, filename) self.progressbar.update((i + 1) / float(len(years)), 0.0, 0.9)
def plotMap(self): """Plot return period wind speed maps""" lon, lat, years, inputData = self.loadFile(self.inputFile, 'wspd') if lon.min() > 180.: lon = lon - 360. [xgrid, ygrid] = np.meshgrid(lon, lat) inputData = metutils.convert(inputData, 'mps', self.plotUnits.units) map_kwargs = dict(llcrnrlon=xgrid.min(), llcrnrlat=ygrid.min(), urcrnrlon=xgrid.max(), urcrnrlat=ygrid.max(), projection='merc', resolution='i') for i, year in enumerate(years): log.debug("Plotting %d-year return period hazard map"%(year)) title = '%d-Year Return Period Cyclonic Wind Hazard' % (year) imageFilename = '%d_yrRP_hazard_map.png' % (year) filename = pjoin(self.plotPath, imageFilename) cbarlab = "Wind speed (%s)"%self.plotUnits.units levels = self.plotUnits.levels saveHazardMap(inputData[i, :, :], xgrid, ygrid, title, levels, cbarlab, map_kwargs, filename) self.progressbar.update((i + 1) / float(len(years)), 0.0, 0.9)
def cP(self): """ Current pressure. """ cP = self.profile.cP if cP < 10000: cP = metutils.convert(cP, "hPa", "Pa") return cP
def eP(self): """ Environment pressure. """ eP = self.profile.eP if eP < 10000: eP = metutils.convert(eP, "hPa", "Pa") return eP
def eP(self): """ Environment pressure. """ eP = self.profile.eP if eP < 10000: eP = metutils.convert(eP, 'hPa', 'Pa') return eP
def __init__(self, lat, lon, eP, cP, rMax): beta = 1.881093 - 0.010917 * np.abs(lat) -\ 0.005567 * metutils.convert(rMax, "m", "nm") if beta < 0.8: beta = 0.8 if beta > 2.2: beta = 2.2 HollandWindProfile.__init__(self, lat, lon, eP, cP, rMax, beta)
def cP(self): """ Current pressure. """ cP = self.profile.cP if cP < 10000: cP = metutils.convert(cP, 'hPa', 'Pa') return cP
def __init__(self, lat, lon, eP, cP, rMax, windSpeedModel): self.rho = 1.15 # density of air self.lat = lat self.lon = lon self.eP = eP self.cP = cP self.rMax = rMax self.speed = windSpeedModel(self) self.f = metutils.coriolis(lat) self.vMax_ = None if eP < 10000.: self.eP = metutils.convert(eP, 'hPa', 'Pa') else: self.eP = eP if cP < 10000.: self.cP = metutils.convert(cP, 'hPa', 'Pa') else: self.cP = cP
def localWindField(self, i): """ Calculate the local wind field at time `i` around the tropical cyclone. :type i: int :param i: the time. """ lat = self.track.Latitude[i] lon = self.track.Longitude[i] beta = self.track.beta[i] eP = convert(self.track.EnvPressure[i], 'hPa', 'Pa') cP = convert(self.track.CentralPressure[i], 'hPa', 'Pa') rMax = self.track.rMax[i]*1000 vFm = convert(self.track.Speed[i], 'kph', 'mps') thetaFm = bearing2theta(self.track.Bearing[i] * np.pi/180.) thetaMax = self.thetaMax beta = self.track.beta[i] #FIXME: temporary way to do this cls = windmodels.profile(self.profileType) params = windmodels.profileParams(self.profileType) values = [getattr(self, p) for p in params if hasattr(self, p)] profile = cls(lat, lon, eP, cP, rMax, beta, *values) R, theta = self.polarGridAroundEye(i) P = self.pressureProfile(i, R) #FIXME: temporary way to do this cls = windmodels.field(self.windFieldType) params = windmodels.fieldParams(self.windFieldType) values = [getattr(self, p) for p in params if hasattr(self, p)] windfield = cls(profile, *values) Ux, Vy = windfield.field(R * 1000, theta, vFm, thetaFm, thetaMax) log.debug("UU: {0}, VV: {1}".format(Ux.max(), Vy.max())) return (Ux, Vy, P)
def pDiff(vMax, pEnv, vMaxType="holland", beta=1.3, rho=1.15): """ Inverse functions to calculate central pressure from vMax Assumes vMax is given in metres/second. Returns pCentre in Pa. """ if pEnv < 10000: pEnv = metutils.convert(pEnv, "hPa", "Pa") if vMaxType == "willoughby": dP = (vMax / 0.6252)**2 elif vMaxType == "holland": dP = rho * exp(1) * (vMax**2) / beta elif vMaxType == "atkinson": dP = (vMax / 3.04)**(1 / 0.644) dP = metutils.convert(dP, "hPa", "Pa") else: raise NotImplementedError("Vmax type " + vMaxType + " not implemented") pCentre = pEnv - dP return pCentre
def calculateWindField(lon, lat, pEnv, pCentre, rMax, vFm, thetaFm, beta, profileType='powell', windFieldType='kepert'): pCentre = metutils.convert(pCentre, 'hPa', 'Pa') pEnv = metutils.convert(pEnv, 'hPa', 'Pa') vFm = metutils.convert(vFm, 'kmh', 'mps') thetaFm = bearing2theta(np.pi * thetaFm / 180.) thetaMax = 70. rmax = metutils.convert(rMax, 'km', 'm') cls = windmodels.profile(profileType) if profileType=="holland": profile = cls(lat, lon, pEnv, pCentre, rmax, beta) else: profile = cls(lat, lon, pEnv, pCentre, rmax) R, theta = polarGridAroundEye(lon, lat, 5.) gradV = profile.velocity(R*1000) cls = windmodels.field(windFieldType) windfield = cls(profile) Ux, Vy = windfield.field(R*1000, theta, vFm, thetaFm, thetaMax) surfV = np.sqrt(Ux*Ux+Vy*Vy)*1.268 # Gust conversion factor return gradV, surfV
def pDiff(vMax, pEnv, vMaxType="holland", beta=1.3, rho=1.15): """ Inverse functions to calculate central pressure from vMax Assumes vMax is given in metres/second. Returns pCentre in Pa. """ if pEnv < 10000: pEnv = metutils.convert(pEnv, "hPa", "Pa") if vMaxType == "willoughby": dP = (vMax/0.6252)**2 elif vMaxType == "holland": dP = rho*exp(1)*(vMax**2)/beta elif vMaxType == "atkinson": dP = (vMax/3.04)**(1/0.644) dP = metutils.convert(dP, "hPa", "Pa") else: raise NotImplementedError, \ "Vmax type " + vMaxType + " not implemented" pCentre = pEnv - dP return pCentre
def plotPressurefield(xGrid, yGrid, pressure, title='Cyclone pressure field', xlabel='Longitude', ylabel='Latitude', infostring='', fileName=None): """ Plot the wind field for a given grid. xGrid, yGrid and speed are arrays of the same size. (xGrid and yGrid are typically generated by [xGrid,yGrid] = meshgrid(lon,lat)) """ pMin = pressure.min() if pMin > 8000.: pressure = metutils.convert(pressure, 'Pa', 'hPa') pylab.clf() dl = 5. llLon = min(xGrid[0, :]) urLon = max(xGrid[0, :]) llLat = min(yGrid[:, 0]) urLat = max(yGrid[:, 0]) meridians = np.arange(dl*floor(llLon/dl), dl*ceil(urLon/dl), dl) parallels = np.arange(dl*floor(llLat/dl), dl*ceil(urLat/dl), dl) levels = np.arange(900, 1020, 5) m = Basemap(projection='cyl', resolution='i', llcrnrlon=llLon, urcrnrlon=urLon, llcrnrlat=llLat, urcrnrlat=urLat) m.contourf(xGrid, yGrid, pressure, levels) #m.plot(cLon, cLat, 'k.-', markersize=2) pylab.colorbar() pylab.title(title) m.drawcoastlines() m.drawparallels(parallels, labels=[1, 0, 0, 1], fontsize=9) m.drawmeridians(meridians, labels=[1, 0, 0, 1], fontsize=9) """ if cfgPlot['Basemap.fill_continents']: m.fillcontinents() """ pylab.grid(True) if fileName is not None: pylab.savefig(fileName)
def minimumDistance(self, points): """ Calculate the minimum distance between a track and a collection of :class:`shapely.geometry.Point` points. Assumes the points and the :attr:`Longitude` and :attr:`Latitude` attributes share the same coordinate system (presumed to be geographic coordinates). :param points: sequence of :class:`shapely.geometry.Point` objects. :returns: :class:`numpy.ndarray` of minimum distances between the set of points and the line features (in km). """ coords = [(x, y) for x, y in zip(self.Longitude, self.Latitude)] if len(coords) == 1: point_feature = Point(self.Longitude, self.Latitude) distances = [point_feature.distance(point) for point in points] else: line_feature = LineString(coords) distances = [line_feature.distance(point) for point in points] return convert(distances, 'deg', 'km')
from Utilities.metutils import convert from timeseries import TimeSeriesFigure, saveFigure DATEFORMAT = "%Y-%m-%d %H:%M" INPUT_COLS = ('Time', 'Longitude', 'Latitude', 'Speed', 'UU', 'VV', 'Bearing', 'Pressure') INPUT_FMTS = ('object', 'f', 'f', 'f', 'f', 'f', 'f', 'f') INPUT_TITLES = ("Time", "Longitude", "Latitude", "Wind speed", "Eastward wind", "Northward wind", "Wind direction", "Sea level pressure") INPUT_UNIT = ('%Y-%m-%d %H:%M', 'degrees', 'degrees', 'm/s', 'm/s', 'm/s','degrees', 'Pa') INPUT_CNVT = { 0: lambda s: datetime.strptime(s.strip(), INPUT_UNIT[0]), 7: lambda s: convert(float(s.strip() or 0), INPUT_UNIT[7], 'hPa') } def loadTimeseriesData(datafile): try: return np.loadtxt(datafile, comments = "#", delimiter = ',', dtype = { 'names': INPUT_COLS, 'formats': INPUT_FMTS}, converters = INPUT_CNVT) except ValueError: return np.empty(0, dtype={ 'names': INPUT_COLS, 'formats': INPUT_FMTS})
"Latitude", "Speed", "Bearing", "CentralPressure", "EnvPressure", "rMax", ) TRACKFILE_UNIT = ("", "%Y-%m-%d %H:%M:%S", "hr", "degree", "degree", "kph", "degrees", "hPa", "hPa", "km") TRACKFILE_FMTS = ("i", datetime, "f", "f", "f", "f", "f", "f", "f", "f") TRACKFILE_CNVT = { 0: lambda s: int(float(s.strip() or 0)), 1: lambda s: datetime.strptime(s.strip(), TRACKFILE_UNIT[1]), 5: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[5], "mps"), 6: lambda s: bearing2theta(float(s.strip() or 0) * np.pi / 180.0), 7: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[7], "Pa"), 8: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[8], "Pa"), } def readTrackData(trackfile): """ Read a track .csv file into a numpy.ndarray. The track format and converters are specified with the global variables TRACKFILE_COLS -- The column names TRACKFILE_FMTS -- The entry formats TRACKFILE_CNVT -- The column converters
log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) TRACKFILE_COLS = ('CycloneNumber', 'Datetime', 'TimeElapsed', 'Longitude', 'Latitude', 'Speed', 'Bearing', 'CentralPressure', 'EnvPressure', 'rMax') TRACKFILE_UNIT = ('', '%Y-%m-%d %H:%M:%S', 'hr', 'degree', 'degree', 'kph', 'degrees', 'hPa', 'hPa', 'km') TRACKFILE_FMTS = ('i', datetime, 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f') TRACKFILE_CNVT = { 0: lambda s: int(float(s.strip() or 0)), 1: lambda s: datetime.strptime(s.strip(), TRACKFILE_UNIT[1]), 5: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[5], 'mps'), 6: lambda s: bearing2theta(float(s.strip() or 0) * np.pi / 180.), 7: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[7], 'hPa'), 8: lambda s: convert(float(s.strip() or 0), TRACKFILE_UNIT[8], 'hPa'), } def readTrackData(trackfile): """ Read a track .csv file into a numpy.ndarray. The track format and converters are specified with the global variables TRACKFILE_COLS -- The column names TRACKFILE_FMTS -- The entry formats TRACKFILE_CNVT -- The column converters
def maximum(self): cP = metutils.convert(self.cP, "Pa", "hPa") return 3.04 * pow(1010.0 - cP, 0.644)
def test_m2km(self): """Convert distance in m to distance in km""" self.assertEqual(metutils.convert(0, "m", "km"), 0) self.assertAlmostEqual(metutils.convert(1000., "m", "km"), 1.) self.assertAlmostEqual(metutils.convert(10000., "m", "km"), 10.)
def plotHazardCurves(self, inputFile, plotPath): """ Plot the hazard values stored in hazardFile, at the stns stored in stnFile. """ log.info(("Plotting return period curves for locations within the " "model domain")) # Open data file try: ncobj = nctools.ncLoadFile(inputFile) lon = nctools.ncGetDims(ncobj, 'lon') lat = nctools.ncGetDims(ncobj, 'lat') years = nctools.ncGetDims(ncobj, 'ari') except (IOError, RuntimeError, KeyError): log.critical("Cannot load input file: %s"%inputFile) raise placeNames, placeID, placeLats, placeLons, locations = self.getLocations() for name, plat, plon, pID in zip(placeNames, placeLats, placeLons, placeID): pID = int(pID) log.debug("Plotting return period curve for %s"%name) i = find_index(lon, plon) j = find_index(lat, plat) xlabel = 'Average recurrence interval (years)' ylabel = 'Wind speed (%s)'%self.plotUnits.label title = "Return period wind speeds at " + name + ", \n(%5.1f,%5.1f)"%(plon, plat) name.replace(' ', '') log.debug("Working on {0}".format(name)) filename = pjoin(plotPath, 'ARI_curve_%s.%s'%(pID, "png")) log.debug("Saving hazard curve for %s to %s"%(name, filename)) wspd = ncobj.variables['wspd'][:, j, i] recs = database.queries.locationRecords(self.db, pID) data = np.zeros(int(self.numsimulations * 365.25)) if len(recs) > 0: data[-len(recs):] = recs['wspd'] allevents = np.sort(data) log.debug("allevents length = {0}".format(len(allevents))) placeWspd = metutils.convert(wspd, 'mps', self.plotUnits.units) if np.all(placeWspd.mask): log.debug("All values for {0} are null".format(name)) continue if self.ciBounds: wspdLower = ncobj.variables['wspdlower'][:, j, i] wspdUpper = ncobj.variables['wspdupper'][:, j, i] placeWspdLower = metutils.convert(wspdLower, 'mps', self.plotUnits.units) placeWspdUpper = metutils.convert(wspdUpper, 'mps', self.plotUnits.units) else: placeWspdUpper = np.zeros(len(placeWspd)) placeWspdLower = np.zeros(len(placeWspd)) saveHazardCurve(years, allevents, placeWspd, placeWspdUpper, placeWspdLower, xlabel, ylabel, title, filename, self.fit) ncobj.close()
def maximum(self): cP = metutils.convert(self.cP, 'Pa', 'hPa') return 3.04 * pow(1010.0 - cP, 0.644)
def test_km2deg(self): """Convert distance in km to distance in degrees""" self.assertEqual(metutils.convert(0, "km", "deg"), 0) self.assertAlmostEqual(metutils.convert(1, "km", "deg"), 360/(2*pi*6367), 3) self.assertAlmostEqual(metutils.convert(2, "km", "deg"), 720/(2*pi*6367), 3) self.assertAlmostEqual(metutils.convert(10, "km", "deg"), 3600/(2*pi*6367), 3)
def test_celcius2F(self): """Convert temperatures in Celcius to Farenheit""" self.assertEqual(metutils.convert(0, "C", "F"), 32.0) self.assertAlmostEqual(metutils.convert(37.78, "C", "F"), 100, 2) self.assertAlmostEqual(metutils.convert(-40, "C", "F"), -40, 2)
from timeseries import TimeSeriesFigure, saveFigure DATEFORMAT = "%Y-%m-%d %H:%M" INPUT_COLS = ('Station', 'Time', 'Longitude', 'Latitude', 'Speed', 'UU', 'VV', 'Bearing', 'Pressure') INPUT_FMTS = ('|S16', 'object', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8') INPUT_TITLES = ("Station", "Time", "Longitude", "Latitude", "Wind speed", "Eastward wind", "Northward wind", "Wind direction", "Sea level pressure") INPUT_UNIT = ('', '%Y-%m-%d %H:%M', 'degrees', 'degrees', 'm/s', 'm/s', 'm/s','degrees', 'Pa') INPUT_CNVT = { 1: lambda s: datetime.strptime(s.strip(), INPUT_UNIT[1]), 8: lambda s: convert(float(s.strip() or 0), INPUT_UNIT[8], 'hPa') } def loadTimeseriesData(datafile): logging.debug("Loading timeseries data from {0}".format(datafile)) try: return np.genfromtxt(datafile, dtype=INPUT_FMTS, names=INPUT_COLS, comments='#', delimiter=',', skip_header=1, converters=INPUT_CNVT) except ValueError: logging.warn("Timeseries data file is empty - returning empty array") return np.empty(0, dtype={ 'names': INPUT_COLS, 'formats': INPUT_FMTS}) def plotTimeseries(inputPath, outputPath, locID=None):
def test_farenheit2C(self): """Convert temperatures in Farenheit to Celcius""" self.assertEqual(metutils.convert(32, "F", "C"), 0.0) self.assertAlmostEqual(metutils.convert(100, "F", "C"), 37.78, 2) self.assertAlmostEqual(metutils.convert(-40, "F", "C"), -40, 2)
def regionalExtremes(self, gridLimit, timeStepCallback=None): """ Calculate the maximum potential wind gust and minimum pressure over the region throughout the life of the tropical cyclone. :type gridLimit: :class:`dict` :param gridLimit: the domain where the tracks will be considered. The :class:`dict` should contain the keys :attr:`xMin`, :attr:`xMax`, :attr:`yMin` and :attr:`yMax`. The *y* variable bounds the latitude and the *x* variable bounds the longitude. :type timeStepCallback: function :param timeStepCallback: the function to be called on each time step. """ if len(self.track.data) > 0: envPressure = convert(self.track.EnvPressure[0], 'hPa', 'Pa') else: envPressure = np.NaN # Get the limits of the region xMin = gridLimit['xMin'] xMax = gridLimit['xMax'] yMin = gridLimit['yMin'] yMax = gridLimit['yMax'] # Setup a 'millidegree' integer grid for the region gridMargin = int(100. * self.margin) gridStep = int(100. * self.resolution) minLat = int(100. * yMin) - gridMargin maxLat = int(100. * yMax) + gridMargin minLon = int(100. * xMin) - gridMargin maxLon = int(100. * xMax) + gridMargin latGrid = np.arange(minLat, maxLat + gridStep, gridStep, dtype=int) lonGrid = np.arange(minLon, maxLon + gridStep, gridStep, dtype=int) [cGridX, cGridY] = np.meshgrid(lonGrid, latGrid) # Initialise the region UU = np.zeros_like(cGridX, dtype='f') VV = np.zeros_like(cGridY, dtype='f') bearing = np.zeros_like(cGridX, dtype='f') gust = np.zeros_like(cGridX, dtype='f') pressure = np.ones_like(cGridX, dtype='f') * envPressure lonCDegree = np.array(100. * self.track.Longitude, dtype=int) latCDegree = np.array(100. * self.track.Latitude, dtype=int) # We only consider the times when the TC track falls in the region timesInRegion = np.where((xMin <= self.track.Longitude) & (self.track.Longitude <= xMax) & (yMin <= self.track.Latitude) & (self.track.Latitude <= yMax))[0] nsteps = len(self.track.TimeElapsed) for i in tqdm.tqdm(timesInRegion, disable=None): log.debug(("Calculating wind field at timestep " "{0} of {1}".format(i, nsteps))) # Map the local grid to the regional grid # Set up max/min over the whole domain jmin, jmax = 0, int( (maxLat - minLat + 2. * gridMargin) / gridStep) + 1 imin, imax = 0, int( (maxLon - minLon + 2. * gridMargin) / gridStep) + 1 # If domain is set in gridLimit in config, use only those # limits instead if self.domain == 'bounded': jmin = int((latCDegree[i] - minLat - gridMargin) / gridStep) jmax = int( (latCDegree[i] - minLat + gridMargin) / gridStep) + 1 imin = int((lonCDegree[i] - minLon - gridMargin) / gridStep) imax = int( (lonCDegree[i] - minLon + gridMargin) / gridStep) + 1 # Calculate the local wind speeds and pressure at time i Ux, Vy, P = self.localWindField(i) # Calculate the local wind gust and bearing Ux *= self.gustFactor Vy *= self.gustFactor localGust = np.sqrt(Ux**2 + Vy**2) localBearing = ((np.arctan2(-Ux, -Vy)) * 180. / np.pi) # Handover this time step to a callback if required if timeStepCallback is not None: timeStepCallback(self.track.Datetime[i], localGust, Ux, Vy, P, lonGrid[imin:imax] / 100., latGrid[jmin:jmax] / 100.) # Retain when there is a new maximum gust mask = localGust > gust[jmin:jmax, imin:imax] gust[jmin:jmax, imin:imax] = np.where(mask, localGust, gust[jmin:jmax, imin:imax]) bearing[jmin:jmax, imin:imax] = np.where(mask, localBearing, bearing[jmin:jmax, imin:imax]) UU[jmin:jmax, imin:imax] = np.where(mask, Ux, UU[jmin:jmax, imin:imax]) VV[jmin:jmax, imin:imax] = np.where(mask, Vy, VV[jmin:jmax, imin:imax]) # Retain the lowest pressure pressure[jmin:jmax, imin:imax] = np.where(P < pressure[jmin:jmax, imin:imax], P, pressure[jmin:jmax, imin:imax]) return gust, bearing, UU, VV, pressure, lonGrid / 100., latGrid / 100.
# Define format for TCRM output track files: ISO_FORMAT = "%Y-%m-%d %H:%M:%S" TCRM_COLS = ('CycloneNumber', 'Datetime', 'TimeElapsed', 'Longitude', 'Latitude', 'Speed', 'Bearing', 'CentralPressure', 'EnvPressure', 'rMax') TCRM_UNIT = ('', '', 'hr', 'degree', 'degree', 'kph', 'degrees', 'hPa', 'hPa', 'km') TCRM_FMTS = ('i', 'object', 'f', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8') TCRM_CNVT = { 0: lambda s: int(float(s.strip() or 0)), 1: lambda s: datetime.strptime(s.strip(), ISO_FORMAT), 5: lambda s: convert(float(s.strip() or 0), TCRM_UNIT[5], 'mps'), 6: lambda s: bearing2theta(float(s.strip() or 0) * np.pi / 180.), 7: lambda s: convert(float(s.strip() or 0), TCRM_UNIT[7], 'Pa'), 8: lambda s: convert(float(s.strip() or 0), TCRM_UNIT[8], 'Pa'), } TRACK_DT_ERR = "Track data does not have required \ attributes to convert to datetime object" TRACK_EMPTY_GROUP = """No track groups in this netcdf file: {0}""" def ncReadTrackData(trackfile): """ Read a netcdf-format track file into a collection of :class:`Track` objects. The returned :class:`Track` objects *must*
def test_m2nm(self): self.assertEqual(metutils.convert(0, "m", "nm"), 0) self.assertAlmostEqual(metutils.convert(1000., "m", "nm"), 0.539957)
def test_deg2km(self): """Convert distance in degrees to distance in km""" self.assertEqual(metutils.convert(0, "deg", "km"), 0) self.assertAlmostEqual(metutils.convert(1, "deg", "km"), (1/(360/(2*pi*6367))), 3) self.assertAlmostEqual(metutils.convert(2, "deg", "km"), (2/(360/(2*pi*6367))), 3) self.assertAlmostEqual(metutils.convert(10, "deg", "km"), (10/(360/(2*pi*6367))), 3)
def plotHazardCurves(self, inputFile, plotPath): """ Plot the hazard values stored in hazardFile, at the stns stored in stnFile. """ log.info(("Plotting return period curves for locations within the " "model domain")) # Open data file try: ncobj = nctools.ncLoadFile(inputFile) lon = nctools.ncGetDims(ncobj, 'lon') lat = nctools.ncGetDims(ncobj, 'lat') years = nctools.ncGetDims(ncobj, 'years') except (IOError, RuntimeError, KeyError): log.critical("Cannot load input file: %s"%inputFile) raise # Load data #wspd = nctools.ncGetData(ncobj, 'wspd') #try: # wLower = nctools.ncGetData(ncobj, 'wspdlower') # wUpper = nctools.ncGetData(ncobj, 'wspdupper') ciBounds = True #except KeyError: # ciBounds = False #ncobj.close() minLon = min(lon) maxLon = max(lon) minLat = min(lat) maxLat = max(lat) # Use the same maximum value for all localities to simplify # intercomparisons: #defaultMax = np.ceil(metutils.convert(100.0, 'mps', # self.plotUnits.units)/10.0)*10.0 placeNames, parentCountries, placeLats, placeLons = \ self.getLocations(minLon, maxLon, minLat, maxLat) for name, plat, plon, country in zip(placeNames, placeLats, placeLons, parentCountries): log.debug("Plotting return period curve for %s"%name) i = find_index(lon, plon) j = find_index(lat, plat) xlabel = 'Average recurrence interval (years)' ylabel = 'Wind speed (%s)'%self.plotUnits.label title = "Return period wind speeds at " + name + ", " \ + country + "\n(%5.1f,%5.1f)"%(plon, plat) name = unicodedata.normalize('NFKD', name).encode('ascii', 'ignore') name.replace(' ', '') filename = pjoin(plotPath, 'ARI_curve_%s.%s'%(name, "png")) log.debug("Saving hazard curve for %s to %s"%(name, filename)) wspd = ncobj.variables['wspd'][:, j, i] placeWspd = metutils.convert(wspd, 'mps', self.plotUnits.units) if np.all(placeWspd.mask): log.debug("All values for {0} are null".format(name)) continue if ciBounds: wspdLower = ncobj.variables['wspdlower'][:, j, i] wspdUpper = ncobj.variables['wspdupper'][:, j, i] placeWspdLower = metutils.convert(wspdLower, 'mps', self.plotUnits.units) placeWspdUpper = metutils.convert(wspdUpper, 'mps', self.plotUnits.units) saveHazardCurve(years, placeWspd, placeWspdUpper, placeWspdLower, xlabel, ylabel, title, filename) ncobj.close()
def test_km2m(self): """Convert distance in km to distance in m""" self.assertEqual(metutils.convert(0, "km", "m"), 0) self.assertAlmostEqual(metutils.convert(1, "km", "m"), 1000)
def plotHazardCurves(self, inputFile, plotPath): """ Plot the hazard values stored in hazardFile, at the stns stored in stnFile. """ log.info(("Plotting return period curves for locations within the " "model domain")) # Open data file try: ncobj = nctools.ncLoadFile(inputFile) lon = nctools.ncGetDims(ncobj, 'lon') lat = nctools.ncGetDims(ncobj, 'lat') years = nctools.ncGetDims(ncobj, 'years') except (IOError, RuntimeError, KeyError): log.critical("Cannot load input file: %s"%inputFile) raise # Load data wspd = nctools.ncGetData(ncobj, 'wspd') try: wLower = nctools.ncGetData(ncobj, 'wspdlower') wUpper = nctools.ncGetData(ncobj, 'wspdupper') ciBounds = True except KeyError: ciBounds = False ncobj.close() minLon = min(lon) maxLon = max(lon) minLat = min(lat) maxLat = max(lat) # Use the same maximum value for all localities to simplify # intercomparisons: defaultMax = np.ceil(metutils.convert(100.0, 'mps', self.plotUnits.units)/10.0)*10.0 placeNames, parentCountries, placeLats, placeLons = \ self.getLocations(minLon, maxLon, minLat, maxLat) for name, plat, plon, country in zip(placeNames, placeLats, placeLons, parentCountries): log.debug("Plotting return period curve for %s"%name) i = find_index(lon, plon) j = find_index(lat, plat) xlabel = 'Average recurrence interval (years)' ylabel = 'Wind speed (%s)'%self.plotUnits.label title = "Return period wind speeds at " + name + ", " \ + country + "\n(%5.1f,%5.1f)"%(plon, plat) name = unicodedata.normalize('NFKD', name).encode('ascii', 'ignore') name.replace(' ', '') filename = pjoin(plotPath, 'ARI_curve_%s.%s'%(name,"png")) log.debug("Saving hazard curve for %s to %s"%(name, filename)) placeWspd = metutils.convert(wspd[:, j, i], 'mps', self.plotUnits.units) maxWspd = placeWspd.max() if ciBounds: placeWspdLower = metutils.convert(wLower[:,j,i], 'mps', self.plotUnits.units) placeWspdUpper = metutils.convert(wUpper[:,j,i], 'mps', self.plotUnits.units) saveHazardCurve(years, placeWspd, placeWspdUpper, placeWspdLower, xlabel, ylabel, title, filename)
def processData(self, restrictToWindfieldDomain=False): """ Process raw data into ASCII files that can be read by the main components of the system :param bool restrictToWindfieldDomain: if True, only process data within the wind field domain, otherwise, process data from across the track generation domain. """ config = ConfigParser() config.read(self.configFile) self.logger.info("Running {0}".format(flModuleName())) if config.has_option('DataProcess', 'InputFile'): inputFile = config.get('DataProcess', 'InputFile') if config.has_option('DataProcess', 'Source'): source = config.get('DataProcess', 'Source') self.logger.info('Loading %s dataset', source) fn = config.get(source, 'filename') path = config.get(source, 'path') inputFile = pjoin(path, fn) # If input file has no path information, default to tcrm input folder if len(os.path.dirname(inputFile)) == 0: inputFile = pjoin(self.tcrm_input_dir, inputFile) self.logger.info("Processing {0}".format(inputFile)) self.source = config.get('DataProcess', 'Source') inputData = colReadCSV(self.configFile, inputFile, self.source) inputSpeedUnits = config.get(self.source, 'SpeedUnits') inputPressureUnits = config.get(self.source, 'PressureUnits') inputLengthUnits = config.get(self.source, 'LengthUnits') startSeason = config.getint('DataProcess', 'StartSeason') indicator = loadData.getInitialPositions(inputData) lat = np.array(inputData['lat'], 'd') lon = np.mod(np.array(inputData['lon'], 'd'), 360) if restrictToWindfieldDomain: # Filter the input arrays to only retain the tracks that # pass through the windfield domain. CD = CalcTrackDomain(self.configFile) self.domain = CD.calcDomainFromTracks(indicator, lon, lat) domainIndex = self.extractTracks(indicator, lon, lat) inputData = inputData[domainIndex] indicator = indicator[domainIndex] lon = lon[domainIndex] lat = lat[domainIndex] if self.progressbar is not None: self.progressbar.update(0.125) # Sort date/time information try: dt = np.empty(indicator.size, 'f') dt[1:] = np.diff(inputData['age']) except (ValueError, KeyError): try: self.logger.info(("Filtering input data by season:" "season > {0}".format(startSeason))) # Find indicies that satisfy minimum season filter idx = np.where(inputData['season'] >= startSeason)[0] # Filter records: inputData = inputData[idx] indicator = indicator[idx] lon = lon[idx] lat = lat[idx] except (ValueError, KeyError): pass year, month, day, hour, minute, datetimes \ = loadData.parseDates(inputData, indicator) # Time between observations: dt = loadData.getTimeDelta(year, month, day, hour, minute) # Calculate julian days: jdays = loadData.julianDays(year, month, day, hour, minute) delta_lon = np.diff(lon) delta_lat = np.diff(lat) # Split into separate tracks if large jump occurs (delta_lon > # 15 degrees or delta_lat > 5 degrees) This avoids two tracks # being accidentally combined when seasons and track numbers # match but basins are different as occurs in the IBTrACS # dataset. This problem can also be prevented if the # 'tcserialno' column is specified. indicator[np.where(delta_lon > 15)[0] + 1] = 1 indicator[np.where(delta_lat > 5)[0] + 1] = 1 # Save information required for frequency auto-calculation try: origin_seasonOrYear = np.array(inputData['season'], 'i').compress(indicator) header = 'Season' except (ValueError, KeyError): origin_seasonOrYear = year.compress(indicator) header = 'Year' flSaveFile(self.origin_year, np.transpose(origin_seasonOrYear), header, ',', fmt='%d') pressure = np.array(inputData['pressure'], 'd') novalue_index = np.where(pressure == sys.maxint) pressure = metutils.convert(pressure, inputPressureUnits, "hPa") pressure[novalue_index] = sys.maxint # Convert any non-physical central pressure values to maximum integer # This is required because IBTrACS has a mix of missing value codes # (i.e. -999, 0, 9999) in the same global dataset. pressure = np.where((pressure < 600) | (pressure > 1100), sys.maxint, pressure) if self.progressbar is not None: self.progressbar.update(0.25) try: vmax = np.array(inputData['vmax'], 'd') except (ValueError, KeyError): self.logger.warning("No max wind speed data") vmax = np.empty(indicator.size, 'f') else: novalue_index = np.where(vmax == sys.maxint) vmax = metutils.convert(vmax, inputSpeedUnits, "mps") vmax[novalue_index] = sys.maxint assert lat.size == indicator.size assert lon.size == indicator.size assert pressure.size == indicator.size #assert vmax.size == indicator.size try: rmax = np.array(inputData['rmax']) novalue_index = np.where(rmax == sys.maxint) rmax = metutils.convert(rmax, inputLengthUnits, "km") rmax[novalue_index] = sys.maxint self._rmax(rmax, indicator) self._rmaxRate(rmax, dt, indicator) except (ValueError, KeyError): self.logger.warning("No rmax data available") if self.ncflag: self.data['index'] = indicator # ieast : parameter used in latLon2Azi # FIXME: should be a config setting describing the input data. ieast = 1 # Determine the index of initial cyclone observations, excluding # those cyclones that have only one observation. This is used # for calculating initial bearing and speed indicator2 = np.where(indicator > 0, 1, 0) initIndex = np.concatenate( [np.where(np.diff(indicator2) == -1, 1, 0), [0]]) # Calculate the bearing and distance (km) of every two # consecutive records using ll2azi bear_, dist_ = maputils.latLon2Azi(lat, lon, ieast, azimuth=0) assert bear_.size == indicator.size - 1 assert dist_.size == indicator.size - 1 bear = np.empty(indicator.size, 'f') bear[1:] = bear_ dist = np.empty(indicator.size, 'f') dist[1:] = dist_ self._lonLat(lon, lat, indicator, initIndex) self._bearing(bear, indicator, initIndex) self._bearingRate(bear, dt, indicator) if self.progressbar is not None: self.progressbar.update(0.375) self._speed(dist, dt, indicator, initIndex) self._speedRate(dist, dt, indicator) self._pressure(pressure, indicator) self._pressureRate(pressure, dt, indicator) self._windSpeed(vmax) try: self._frequency(year, indicator) self._juliandays(jdays, indicator, year) except (ValueError, KeyError): pass self.logger.info("Completed {0}".format(flModuleName())) if self.progressbar is not None: self.progressbar.update(0.5)
def processData(self, restrictToWindfieldDomain=False): """ Process raw data into ASCII files that can be read by the main components of the system :param bool restrictToWindfieldDomain: if True, only process data within the wind field domain, otherwise, process data from across the track generation domain. """ config = ConfigParser() config.read(self.configFile) self.logger.info("Running %s" % flModuleName()) if config.has_option('DataProcess', 'InputFile'): inputFile = config.get('DataProcess', 'InputFile') if config.has_option('DataProcess', 'Source'): source = config.get('DataProcess', 'Source') self.logger.info('Loading %s dataset', source) fn = config.get(source, 'filename') path = config.get(source, 'path') inputFile = pjoin(path, fn) # If input file has no path information, default to tcrm input folder if len(os.path.dirname(inputFile)) == 0: inputFile = pjoin(self.tcrm_input_dir, inputFile) self.logger.info("Processing %s" % inputFile) self.source = config.get('DataProcess', 'Source') inputData = colReadCSV(self.configFile, inputFile, self.source) inputSpeedUnits = config.get(self.source, 'SpeedUnits') inputPressureUnits = config.get(self.source, 'PressureUnits') inputLengthUnits = config.get(self.source, 'LengthUnits') startSeason = config.getint('DataProcess', 'StartSeason') indicator = loadData.getInitialPositions(inputData) lat = np.array(inputData['lat'], 'd') lon = np.mod(np.array(inputData['lon'], 'd'), 360) if restrictToWindfieldDomain: # Filter the input arrays to only retain the tracks that # pass through the windfield domain. CD = CalcTrackDomain(self.configFile) self.domain = CD.calcDomainFromTracks(indicator, lon, lat) domainIndex = self.extractTracks(indicator, lon, lat) inputData = inputData[domainIndex] indicator = indicator[domainIndex] lon = lon[domainIndex] lat = lat[domainIndex] if self.progressbar is not None: self.progressbar.update(0.125) # Sort date/time information try: dt = np.empty(indicator.size, 'f') dt[1:] = np.diff(inputData['age']) except (ValueError, KeyError): try: self.logger.info("Filtering input data by season: season > %d"%startSeason) # Find indicies that satisfy minimum season filter idx = np.where(inputData['season'] >= startSeason)[0] # Filter records: inputData = inputData[idx] indicator = indicator[idx] lon = lon[idx] lat = lat[idx] except (ValueError, KeyError): pass year, month, day, hour, minute, datetimes \ = loadData.parseDates(inputData, indicator) # Time between observations: dt = loadData.getTimeDelta(year, month, day, hour, minute) # Calculate julian days: jdays = loadData.julianDays(year, month, day, hour, minute) delta_lon = np.diff(lon) delta_lat = np.diff(lat) # Split into separate tracks if large jump occurs (delta_lon > # 15 degrees or delta_lat > 5 degrees) This avoids two tracks # being accidentally combined when seasons and track numbers # match but basins are different as occurs in the IBTrACS # dataset. This problem can also be prevented if the # 'tcserialno' column is specified. indicator[np.where(delta_lon > 15)[0] + 1] = 1 indicator[np.where(delta_lat > 5)[0] + 1] = 1 # Save information required for frequency auto-calculation try: origin_seasonOrYear = np.array( inputData['season'], 'i').compress(indicator) header = 'Season' except (ValueError, KeyError): origin_seasonOrYear = year.compress(indicator) header = 'Year' flSaveFile(self.origin_year, np.transpose(origin_seasonOrYear), header, ',', fmt='%d') pressure = np.array(inputData['pressure'], 'd') novalue_index = np.where(pressure == sys.maxint) pressure = metutils.convert(pressure, inputPressureUnits, "hPa") pressure[novalue_index] = sys.maxint # Convert any non-physical central pressure values to maximum integer # This is required because IBTrACS has a mix of missing value codes # (i.e. -999, 0, 9999) in the same global dataset. pressure = np.where((pressure < 600) | (pressure > 1100), sys.maxint, pressure) if self.progressbar is not None: self.progressbar.update(0.25) try: vmax = np.array(inputData['vmax'], 'd') except (ValueError, KeyError): self.logger.warning("No max wind speed data") vmax = np.empty(indicator.size, 'f') else: novalue_index = np.where(vmax == sys.maxint) vmax = metutils.convert(vmax, inputSpeedUnits, "mps") vmax[novalue_index] = sys.maxint assert lat.size == indicator.size assert lon.size == indicator.size assert pressure.size == indicator.size #assert vmax.size == indicator.size try: rmax = np.array(inputData['rmax']) novalue_index = np.where(rmax == sys.maxint) rmax = metutils.convert(rmax, inputLengthUnits, "km") rmax[novalue_index] = sys.maxint self._rmax(rmax, indicator) self._rmaxRate(rmax, dt, indicator) except (ValueError, KeyError): self.logger.warning("No rmax data available") if self.ncflag: self.data['index'] = indicator # ieast : parameter used in latLon2Azi # FIXME: should be a config setting describing the input data. ieast = 1 # Determine the index of initial cyclone observations, excluding # those cyclones that have only one observation. This is used # for calculating initial bearing and speed indicator2 = np.where(indicator > 0, 1, 0) initIndex = np.concatenate([np.where(np.diff(indicator2) == -1, 1, 0), [0]]) # Calculate the bearing and distance (km) of every two # consecutive records using ll2azi bear_, dist_ = maputils.latLon2Azi(lat, lon, ieast, azimuth=0) assert bear_.size == indicator.size - 1 assert dist_.size == indicator.size - 1 bear = np.empty(indicator.size, 'f') bear[1:] = bear_ dist = np.empty(indicator.size, 'f') dist[1:] = dist_ self._lonLat(lon, lat, indicator, initIndex) self._bearing(bear, indicator, initIndex) self._bearingRate(bear, dt, indicator) if self.progressbar is not None: self.progressbar.update(0.375) self._speed(dist, dt, indicator, initIndex) self._speedRate(dist, dt, indicator) self._pressure(pressure, indicator) self._pressureRate(pressure, dt, indicator) self._windSpeed(vmax) try: self._frequency(year, indicator) self._juliandays(jdays, indicator, year) except (ValueError, KeyError): pass self.logger.info("Completed %s" % flModuleName()) if self.progressbar is not None: self.progressbar.update(0.5)