U  g@sdZddddddddd d d d d ddddddddgZddlZddlZddlZddlZddlZddlZddlZ ddl Z ddl Z e ej jdZGdddeZGdddeZGdddeZGdddeZGdddeZGdddeZGdddeZd d Zd!d Zd"d#Zd$d%Zd&d Zd4d'dZd5d)dZd6d,dZd7d.dZ d8d/dZ!d9d1dZ"Gd2dde#Z$d:d3dZ%dS);a~!Defines StormInfo and related functions for interacting with vitals ATCF data. This module handles reading and manipulating individual entries of TCVitals files or CARQ entries of aid ("A deck") files. It provides each line as a StormInfo object with all available information contained within. This module does NOT supply much functionality for manipulating entire tcvitals databases. That is provided in the hwrf.revital module. @note StormInfo is good at complex manipulations of a single vitals time. However, it is inherently slow, and should not be used for large-scale manipulation of many vitals times such as multiple years of tcvitals or deck files. For example, model forecast verification packages should not use StormInfo. It is better to use compiled programs for such purposes. This slowness is inherent to Python, which is quite slow at creating and modifying objects.current_centuryStormInfoErrorInvalidBasinErrorInvalidStormInfoLine InvalidVitals CenturyError InvalidATCF NoSuchVitalsname_number_okaybasin_center_okayZvit_cmp_by_stormZvitcmp storm_keyclean_up_vitals floatlatlon quadrantinfoparse_tcvitalsfind_tcvitals_for parse_carq StormInfo expand_basinNdc@seZdZdZdS)rz!This is the base class of all exceptions raised when errors are found in the tcvitals, Best Track, Aid Deck or other storm information databases.N__name__ __module__ __qualname____doc__rr;/lfs/h1/ops/prod/packages/hmon.v3.2.7/ush/hwrf/storminfo.pyr)sc@s*eZdZdZd ddZddZddZdS) rz!This exception is raised when an invalid Tropical Cyclone basin is found. The invalid basin is available as self.basin, and the subbasin is self.subbasin (which might be None).NcCs||_||_dS)z!InvalidBasinError constructor @param basin the basin in question @param subbasin the subbasin, if known. For example, the North Indian Ocean (IO) is split into subbasins Arabian Sea (AA) and Bay of Bengal (BB)N)basinsubbasin)selfrrrrr__init__2szInvalidBasinError.__init__cCs6|jdkrdt|jfSdt|jt|jfSdS)zsz#clean_up_vitals..cSsg|]}|qSrrrArrrrDscSsg|]}|qSrrrBxrrrrDs) r r r?sortedlistsetreversedr addappend) vitalsZname_number_checkerZbasin_center_checkerZ vitals_keyZ sortvitalsZ nn_ok_vitalsZ keepvitalsZ revuniqvitalsseenr3r:Z uniqvitalsrrrr s,    $@cCsJtd|}|rF|}t|d|}d|krB|ddk rB| }|SdS)a!Converts a string like "551N" to 55.1, correctly handling the sign of each hemisphere. @code floatlatlon(string="311N",fact=10.0) = float(31.1) # degrees North @endcode @returns degrees North or degrees East @param string Latitude or longitude in sum multiple of a degree, followed by N, S, E or W to specify the hemisphere. Must be a positive number @param fact The strung value is divided by this number. The default is to convert from tenths of a degree in string to a degree return value. @note This function does not accept negative numbers. That means the tcvitals "badval" -999 or -99 or -9999 will result in a None return value. z@\A(?P0*\d+)(?:(?P[NnEe ])|(?P[SsWw]))\ZnumnegativeN)research groupdictfloat)stringZfactmmdictZlatlonrrrr s ?c Cs<t|dkstdddddddd g}t|}d gd}d } tdD]`} || d k rB|| d krBt|| || <|| d krd|| <qB|| |9<t| || } qB|dkrtd krd||f} | || <|| nd|d krd S||} tdD]D} d||| d| t|d d|f} || || <|| qd S)a!Internal function that parses wind or sea quadrant information. This is part of the internal implementation of StormInfo: it deals with parsing wind or sea quadrant information. @param[out] data Output quadrant information, a dict mapping from the quadrant name in qset. @param[out] qset output set of quadrants that were seen @param irad Integer radius. For example, 34 may indicate the 34 kt wind radius. @param qcode Special code used for circular (all quadrants) data @param qdata String quadrant data information for each quadrant @param what What type of data is this? Wind radii? Sea height radii? This should be an alphanumeric string. @param conversion Unit conversion. ZNNQZNEQZEEQZSEQZSSQZSWQZWWQZNWQNgrYrZAAAz %sCIRCLE%dz%s%s%d) lenAssertionErrorintrangerUmaxcountrKindex) dataqsetiradZqcodeZqdataZwhat conversionZquadrantZfqdataZmaxdataivarZiquadrrrrs0        * Fc Cs\t}|D]L}z|td|dWq ttfk rT}z |rDW5d}~XYq Xq |S)a !Reads data from a tcvitals file. This reads line by line from the given file object fd, parsing tcvitals. @returns A list of StormInfo objects, one per line. @param fd An opened file to read. @param raise_all If raise_all=True, exceptions will be raised immediately. Otherwise, any StormInfoError or ValueError will be logged or ignored, and parsing will continue. @param logger The logger is a logging.Logger object in which to log messages, or None (the default) to disable logging.tcvitals N)rHrLrrstripr ValueError)fdlogger raise_alloutlineerrrr3s c csPt|trt|}t|tr t|dk rbtj|d}d}|dk sJt|dk rb|d|f|dk rd|f}|dk r|d|f|dk rt|d } |dk r|d|fd} |D]l} |dk r| d d |krq|dk r| d d |krq|dk r| d | krq| d 7} t d| dVq|dk rL|d| fdS)a;!Faster way of finding tcvitals data for a specific case. A fast method of finding tcvitals in a file: instead of parsing each line into a StormInfo, it simply scans the characters of the line trying to find the right storm and time. Returns a list of matching vitals as StormInfo objects. * fd - the stream-like object to read from * logger - the logging.Logger to log to, or None * raise_all - if True, exceptions are raised immediately instead of just being logged. * when - the date to look for, or None * stnum - the storm number (ie.: 09 in 09L) * basin1 - the basin letter (ie.: L in 09L) @warning This function cannot handle errors in the formatting of the tcvitals lines. It will only work if the data in fd strictly follows the tcvitals format.N %Y%m%d %H%MabcdzVITALS: search for when=%sz%02dzVITALS: search for stnum=%srzVITALS: search for basin1=%s rkrlzVITALS: yielded %d matches) isinstancestrr`r_hwrfnumerics to_datetimestrftimedebugupperrrm) rorprqr7r0r5ZstrwhenrvZstrstnumZ strbasin1rcrsrrrrJs:    Tc Cst}d}t}|D]}t|dkr@|dk r|d|fqnNd|kr|dddd}|dksr||kr|}||qn |dkrqz$|td|||d t}d}Wqttfk r}z |r΂W5d}~XYqXq|S) aM!Scans an A deck file connected to stream-like object fd, reading it into a list of StormInfo objects. Returns the list. @param logger A logging.Logger to log to. @param raise_all If False, log and ignore errors instead of raising them. @param fd the file object to read @returns a list of StormInfo objects N(z#Ignoring short line (<40 chars): %sCARQr,r]carq)linetypeinputsrprq) rHr^warningsplitstriprLrrrn) rorprqrrr7ZstorersZwhen2rtrrrrs4   cseZdZdZd4fdd ZddZd d Zd d Zd dZd5ddZ d6ddZ d7ddZ d8ddZ d9ddZ d:ddZddZd;ddZdd$d%Zd&d'Zd(d)Zd*d+Zd?d,d-Zd@d.d/ZdAd0d1Zd2d3ZZS)Bra!Storm vitals information from ATCF, B-deck, tcvitals or message files. Represents all information about a one storm at one time as a Python object. It can read a single line of a tcvitals file, one time from a Best Track file, or CARQ entries from an Aid Deck file at a single time. It will scan multiple lines from a Best Track or CARQ group to get the last forecast hour (up to a maximum of 72hrs) and all possible radii for one time. This class is meant for complex manipulations of a small amount of data, not for manipulations of whole databases. It can be used for manipulating the entire TCVitals database, or multiple ATCF deck files, but will generally be slower than other libraries -- operations will take on the order of ten times as long. @todo Write a separate class for simple manipulations of a whole ATCF database. This could be done efficiently using an in-memory sqlite3 database.rNTc s|dk r&t|tjs&tdt|jtt|d|_ d|_ ||_ d|_ |dkrnt ||_||jnN|dkrt ||_||jn,|dkrt||_|j|t ||t|dn|dks|d kr|dk}d d }t|tstd t|j|jD]P\}} |d dkr"q|d ddkr>|r>q|| sLq| |j|<q|r|jD]<\}} || sqj|d ddkrj| |j|dd<qjntdt|fdS)a!StormInfo constructor Constructor for the StormInfo class. You should not call this directly. Instead, use the other parsing functions in this module to generate tcvitals from file objects. @param linetype type of vitals: tcvitals, message, carq (ATCF CARQ entries), old, or copy. See below @param inputs inputs, converted to a string before processing @param carq additional CARQ data to fill in more information @param logger a logging.Logger for log messages @param raise_all if True, exceptions will be raised if parser errors happen The constructor can create StormInfo objects in several ways, specified by the @c type argument: * @c tcvitals --- Parse a line of a tcvitals file. * @c message --- Parse a tropical cyclone message file. * @c carq --- Parse A deck CARQ entries. * @c old --- Take another StormInfo whose storm id/name has been replaced with another id/name through invest renumbering. Swap the old invest id/name with the current non-invest id/name. * @c copy --- Do a deep copy of the supplied StormInfoNzQIn StormInfo constructor, logger must be a logging.Logger, but instead it is a %sFrkr(r)linestechrprqoldcopycSs,ttttjtjfD]}t||rdSqdS)NTF)r}r`rUdatetimeZ timedeltar|)rjtrrr checktypes   z%StormInfo.__init__..checktypez\In StormInfo constructor, when linetype=="old", inputs must be a StormInfo object, not a %s.r_r[Zold_zAUnknown storm info format %s: only know "tcvitals" and "message".)r|loggingLogger TypeErrorr$rr'rr _cenlo_cenlaformat has_old_stnumr}rs_parse_tcvitals_line_parse_message_linerr _parse_carqbool__dict__itemsZInvalidStormInfoFormatr!) rrrrrprqrrkrCr)rrr sd      zStormInfo.__init__cCs td|S)zj!Returns a copy of this StormInfo, but with the last renumbering or renaming of the vitals undone.rrr"rrrrsz StormInfo.oldcCs td|S)z!Returns a copy if this object.rrr"rrrrszStormInfo.copycCs || S)z\!Same as self + (-amount) @param amount The amount of time to extrapolate backwards.r)ramountrrr__sub__ szStormInfo.__sub__c Cs<|}tj|d}td|jd}tjd}tjj }tjj |dd}|t |j |}|t |j |} t| ||dtj} t||t |j||dtj} tt|j| dd|_tt|j| dd|_tj|d|j|_|jd |_d D]} | |jkr|j| =qd |_|S) z!Returns a copy of this object, with the vitals extrapolated forward "amount" hours. Only the location is changed. @param amount the amount of time to extrapolate forwardrrOf@T)Znegokr] %Y%m%d%H)flatflonfhrF)rr~rZ to_timedeltarb stormspeedmathpiZ constantsRearthZ to_fractionsinstormdircosrUr6roundlonto_datetime_relr7rYMDHr havefcstloc) rrrZdtamountZvmagZpi180rZdtZdxZdyZdlatZdlonrCrrr__add__s& * zStormInfo.__add__c Cs^|jdk r |jdk r |j|jfS|j}|dk s2t|j}|jrT|jdk sLt|j}n|d}|dk sht|}|dkrz| }t|}|dkrd}|dkrd}|dkrd }|d krd }|d krd }|dkrd}|dkrd}|dkr| }|dk r| d||f||}|dkr|d8}|dkr.|d8}t d|d|dd}|dkr\|d8}|dkrn|d7}|} |dk r| d| |f| d||fd} t | t |dkr|d} |dk r| d| fd} t | t |dkr|d} |dk r| d | fd} |dk r6| s6| d!| d"| |f| ||_|_| |fS)#a!Decide domain center based on the storm location, basin, and, if available, the 72hr forecast location. Returns a tuple containing a pair of floats (cenlo, cenla) which are the domain center longitude and latitude, respectively. Results are cached internally so future calls will not have to recompute the center location. @param logger a logging.Logger for log messagesNg4@rg.@g9@#g>@rgA@,gD@r/gF@7gI@z$Averaging storm_lon=%f and avglon=%fgv@gvrOg@rgfzDecided cenlo=%f cenla=%fzStorm is at lon=%f lat=%fFrwg@z1Center is too far east of storm. Moving it to %fTz1Center is too far west of storm. Moving it to %fz2Center is within +/- 5 degrees longitude of storm.z*Final outer domain center is lon=%f lat=%f) rrrr_r6rrrfloorinfor`) rrpZ storm_lonZ storm_latZavglonZcenlaZilatZdiffresultZcenloZmovedrrrhwrf_domain_center's              zStormInfo.hwrf_domain_centerc Cs|j}||d<|||||\}}}} |s8td|dd} |D]@} | rf|j|| || ||dd} |j|| || ||dq@|dk r(z.NcSsg|] }|qSr)rrErrrrDszqCARQ entries in deck files must have at least eight fields (everything through lat & lon). Cannot parse this: %scs$g|]}|d|kqS)rr)rBjrirrrrDs)rr{r]r[zgBasin, storm number, YMDH and technique must match for ALL LINES when parsing CARQ data in _parse_carq.rwrrH) rHrar^ranyr`_parse_atcf_timerLr_) rrrrprqrrrZmyfhrrrrrs>   zStormInfo._split_carqc Csd}|ddkr8|dkr&t|d}nt|d|jd<t|d}tj|d|dd |d d |d |ddd d }||jd <|d |jd<d S)aV!Internal function for getting the time out of ATCF data. Do not call this. It is an internal implementation routine. Adds to this StormInfo object the "when" parameter that contains the analysis time. If available, will also add the "technum" technique sort number. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param data Four element array where the last two are the forecast hour and minute. @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themrrYZBESTtechnumr]i@Bi'rN)r8monthZdayZhourZminutesecondZ microsecondZtzinfor7rr)r`rrr)rrerrprqZiminZiwhenr7rrrrs$    zStormInfo._parse_atcf_timec Cszddlm}m}m}|j}d|kr*|j} nt} | |d<t|} | dkrzBt|d} |d} t || | | |ddd|| |d | f<WnRt t t t fk r} z,|d k r|jd t| |fd d |r̂W5d } ~ XYnX| dkrvzBt|d}|d}t || |||ddd|||d|f<WnJt t t t fk rt|d k rh|jdt| |fd d |rpYnXd S)a!Internal function for parsing radii and sea information in ATCF data Do not call this. It is an internal implementation routine. Adds to this StormInfo object radii and sea height data from the given input. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param instr string to parse @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themrft2mnmi2kmkts2mpsrf rYz windcode%02dNz'could not parse wind radii: %s line: %sTr"Zseasz seascode%02dz-could not parse wave height info: %s line: %s)hwrf.constantsrrrrrfrIr^r`rrrnrrrr})rinstrrerprqrrrrrfnrgZwindcodertZiseasZseascoderrrrsL      z StormInfo._parse_atcf_radii_seascs0dk r&ttjs&tdtjddlm}m}m }t }|j d} d} dd<t dd<t d d <t d d <fd d} fdd} |dkr؈ddkr| dd||dkrddkr| dd|dkrddkrdd<|dkr<ddkr<| dd|dkr`ddkr`| dd||dkrddkr| dd||d krddkr| d!d||d"kr̈d dkr| d#d ||d$krd"dkrd"} |d%krd$dkrd$d&kr| d'd$||d(krBd%dkrBd%d)<|d*kr|d(dkr|d(d+krrd,d-<n | d-d(|d.krd*dkr| d/d*||d0kr̈d.dkrtd.d1<|d2krd0dkrtd0dd3<|| | |t d4d5d1d6<dS)7a !Internal function that parses most of a line of ATCF data. Do not call this. It is an internal implementation routine. Parses just about everything except the time, radii and sea height from the input ATCF data. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param instr string to parse @param data split-up elements of a A deck line @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themNzLin _parse_atcf_meat, logger must be a logging.Logger, but instead it is a %srrr[ techniquerwtaurr6rxrc sz(t|}|dkr&t|||<Wnrttttfk r}zLdk rnjdt|t|fddrt d|t|fW5d}~XYnXdSNrzcould not parse %s: %s line: %sTrz%s: %s: line %s) r`rUrrnrrrr!r}r)sricZixrtrrerrprqrrficBs z'StormInfo._parse_atcf_meat..ficc sz t|}|dkr||<Wnrttttfk r}zLdk rfjdt|t|fddrtd|t|fW5d}~XYnXdSr) rUrrnrrrr!r}r)rriZfxrtrrrfaRs  z&StormInfo._parse_atcf_meat..fa rrYwmaxrpminr stormtyperpociryrocirmwgustseyediamLmaxseasrZinitialsXrrrr1rdepthr{Tr)r|rrrr$rrrrrr^rr`r r}r _set_basinrenumber_stormr)rrrerprqrrrrrrrrZ subregionrrrr#sh          &      zStormInfo._parse_atcf_meatcCs|j|ttjjddS)a|!Do not call this routine directly. Call StormInfo("message",instr) instead. This subroutine parses one line of a hurricane message text that is assumed to be for the current century. The format of a hurricane message is the same as for a tcvitals file, except that the century is omitted and the file is always exactly one line.r)century)rr`rZutcnowr8)rrrrrrws zStormInfo._parse_message_linec Cstd|}|s$tdt|f|td}td}td}td}td}td} td } td } |j} d | kr|| d } nt} | | d <t|}|D]\}}|d kr||krqtd t |t|f|z ||kr|| |<n||krt | | |<n|| kr$t | | |<n|| krDt | d| |<n||kr`t | | |<n|| krt | }t | | |<|dkr| |nX||krt|d}|d kr||krtdt |t |t|f||| |dd<Wqtk r@}z*tdt |t|t |t|f|W5d }~XYqXqd| kod| kod| kod| ko| ddkp| ddkp| ddkp| ddk| d<d| kod| ko| dd k o| dd k | d<d| kr| d | d<ntd|f|d| krd | krd!| kr4t | d!}n*t t}|d"ksP|d#kr^td$|fd%|t | dt | d d&f}|| d'<tj|| d(<ntd)t|f||jd*kr|jdkrd+|_d,| kr||jntd-|f||j|jd.d/| d| d0<|S)1a!Parses one line of tcvitals data Do not call this routine directly. Call StormInfo("tcvitals",instr) instead. This subroutine parses one line of a tcvitals file of a format described here: http://www.emc.ncep.noaa.gov/mmb/data_processing/tcvitals_description.htm Here is an example line with only some of the possible data: @code{.tcvitals} JTWC 31W HAIYAN 20131104 1200 061N 1483E 270 077 0989 1008 0352 23 064 0084 0074 0074 0084 M ... more stuff ... @endcode The resulting data is put in self._data. Note that, at this time, there is one new field not present in the above mentioned webpage. The "storm type parameter" is a two letter description of the type of the storm: LO=low, WV=wave, etc. (there are many possibilities). That field is at the end of the line described in the above link, after one space. The "century" argument is the first two digits of the year, so 19 for the 1900s, 20 for the 2000s and so on. If century is missing or None, and the tcvitals does not specify the century either, then InvalidVitals will be raised. If both are available, the tcvitals century is used.ab(?xi) (?P
\S+) \s+ (?P\d\d)(?P[A-Za-z]) \s+ (?P[A-Za-z_ -]+) \s+ (?P\d\d)? (?P\d\d\d\d\d\d) \s+ (?P\d\d\d\d) \s+ (?P-?0*\d+[NS ]) \s+ (?P-?0*\d+[EW ]) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P\S) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+[NS ]) \s+ (?P-?0*\d+[EW ]) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P\S\S?) )? )? )? )? )?zCannot parse vitals: %s)NE50SE50SW50NW50rfstrlatfstrlonNE64SE64SW64NW64r rawcenturyr )rawbasin rawstormnamer rawYYMMDDrawHHMMr )strlatrstrlonr)r4r)r0)rrrrrrr) NE34SE34SW34NW34rrrrrrrr)rrfNz0Mandatory variable %s had None value in line: %srOrz6Mandatory variable %s had invalid value %s in line: %sr}rYz4Cannot parse vitals key %s value %s: %s from line %srr r!r"r=rrrrr1z'No storm name detected in this line: %srrrrz8Implausable tcvitals century %d. Require 16 through 20.z %02d%06d%02drrr7z.Cannot determine date and time from vitals: %srQrz$Cannot find a basin in this line: %sT) discardoldr)rRrSrr!rIrdictrTrr}rrUr`rKr replacernrrrr~rrrr6r r r0r)rrr rWZnoneokZrawsZlatlonsZstripmeZint1Zfloat1Z float1radiiZfloat10rrfrXrrCvalreplrtZicenturyZsdaterrrrs            (4     zStormInfo._parse_tcvitals_lineFcCsd|jkr|s|j|jd<t|tr8t|dd|_n:t|dd|_|jdksZ|jdkrbd|_n|jdd|_|jdk stdS)z!Sets the two letter storm type self.stormtype. @param discardold If discardold=False (the default), then the old value, if any, is moved to self.old_stormtype. @param stormtype the storm type informationr old_stormtyperr]ZXXNrY)rrr|r}getattrr_)rrr%rrr set_stormtype1s  zStormInfo.set_stormtypecCsd|jkr|s|j|jd<t|dd|_|jdks@|jdkrnd|jdd|jdd|jdd f|_|j|jd <d S) z!Sets the name of the storm. @param newname the new storm name @param discardold If discardold=False (the default) then the old storm name is moved to self.old_stormname.r1 old_stormnamerrrkr(z%s%-9s%srNr)rr1r}rrsr)rZnewnamer%rrr rename_stormBs  zStormInfo.rename_stormcCsd|jkrP|sP|j|jd<|j|jd<|j|jd<|j|jd<|j|jd<d|_t||_d|j|jf|_d |j |jf|_d|j|j f|_|j d kr|j j d krd |j |j|j jd f|_nd |j |j|j jf|_|jdks|jdkrd|jd d|j|jd df|_dS)aU!Changes the storm number. Changes the storm number: the 09 in 09L. That changes self.stnum, stormid3, stormid3lc, stormid4 and longstormid. @param newnumber the new storm number @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc.r0 old_stnum old_stormid3old_stormid3lc old_stormid4old_longstormidTz%02d%sz%s%02drrxz %s%02d%04dr{rkr(%s%02d%srwN)rr0stormid3 stormid3lcr@r9rr`r5 pubbasin2basin1lcr6r7rr8rrs)rZ newnumberr%rrrr Ps,           zStormInfo.renumber_stormcs|fdd}|dd|dd|dd|d d |d d jd ksRjdkrxdjddjjddf_dS)z!Swaps the new and old stormid variables. The stnum and old_stnum are swapped, the stormid3 and old_stormid3 are swapped, and so on.cs<|jkr8|jkr8j|}j|j|<|j|<dSN)r)orZkeepr"rrswapnamets z(StormInfo.swap_numbers..swapnamer/r0r0r5r1r6r2r@r3r9rkr(r4rrwrxN)rrsr0)rr;rr"r swap_numbersps       zStormInfo.swap_numberscCs |jddS)zx!Returns a tcvitals version of this data. This is not cached, and will be recalculated every time it is called.F no_centuryas_tcvitals_or_messager"rrr as_tcvitalsszStormInfo.as_tcvitalscCs |jddS)z|!Returns a message line version of this data. This is not cached, and will be recalculated every time it is called.Tr=r?r"rrr as_messageszStormInfo.as_messagec sN|jfdd}fdd}dd}|r0d}nd}d t|jd d tt|jd t|jd t|jd d |j |t d||j d|j d krdndt d||j d|j d krdnd|drdnt d||j |drdnt d||jd|drdnt d||j|dr"dnt d||j|dr>dnt d||j|d rZd!nt d"||j|d#rvdnt d||j|d$rdnt d||j|d%rdnt d||j|d&rdnt d||j|d'rdnt d||jf}d(koDd)koDd*koD|jd+k oD|jd+k oD|jd+k oD|jd k}d,k}d-krh|sh|sh|Sd.|tt|d-d/d f}d0kr|s|s|Sd1||d2rdnt d||j|d3rdnt d||j|d4rdnt d||j|d0rdnt d||jf}|rd5||jd kr2d!nt d"t|jt d||jd|jd krbdndt d||jd|jd krdndf}d6kr|s|S|s|d77}d1||d8rdnt d||j |d9rdnt d||j!|d:rdnt d||j"|d6rdnt d||j#f}|s4|Sd;|t|j$d d<fS)=aP!Internal function that underlies as_tcvitals() and as_message() Returns a tcvitals or message version of this data. This is not cached, and will be recalculated every time it is called. @param no_century If no_century=True, then only two digits of the year are written, and the line will be a message.cs(|kr dS|}|dkr dS|dkS)NTrrrr(rrrbads z-StormInfo.as_tcvitals_or_message..badcs(|kr dS|}|dkr dS|dkS)NTgh㈵>rrCrDrrbad0s z.StormInfo.as_tcvitals_or_message..bad0cSsttt|Sr9)r`absr)rirrrcintz.StormInfo.as_tcvitals_or_message..cintz %y%m%d %H%MruzX%-4s %02d%s %-9s %s %03d%s %04d%s %03d %03d %04d %04d %04d %02d %03d %04d %04d %04d %04drr[rrirONSrEWrrihrirr\iLrri'ricrrr r!r"rrrNrr z%s %srrz%s %04d %04d %04d %04drrrz%s %02d %03d%s %04d%srz -9 -99N -999Wrrrz%s % 2sr])%rr}r4r`rGr0r5r1r7rminr6rrrrrrrrrr r!r"rrrr+rrrrrrrrr) rr>rErFrHZ datestringrZgotfcstZgotstrrDrr@s     z StormInfo.as_tcvitals_or_messagecCs|s@|j|jd<|j|jd<|j|jd<|j|jd<|j|jd<||||jdkrd|jdd |jd|jd d f|_d S) a=!Changes the basin of this StormInfo @param basin the primary basin (IO, L, etc.) @param subbasin the subbasin. For example, IO has the subbasins AA and BB. @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc. Zold_hwrfbasin2Z old_pubbasin2Z old_basin1Z old_basin1lcZ old_basinnamerkz%s%s%srrxrN) hwrfbasin2rr7r5r8 basinnamer rrs)rrrr%rrr change_basins       zStormInfo.change_basincCs\t||}|d|jd<|d|jd<|d|jd<|d|jd<|d|jd <d S) a!This is a utility function that creates the one and two letter basins from a raw one and/or two letter basin. If the input basin is invalid, InvalidBasinError is raised. @param basin the primary basin (IO, L, etc.) @param subbasin the subbasin. For example, IO has the subbasins AA and BB. @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc. rrPr{r7r]r5r8rrQN)rrrr)rrrr%bbrrrr s zStormInfo._set_basincCsd|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_!d|_"d|_#d|_$d|_%d|_&d|_'d|_(d|_)d|_*d|_+d|_,d|_-d|_!d|_.d|_"d|_d|_/d|_0d|_1d|_2d|_3d|_4d|_5d|_/d|_6d|_7d|_8d|_9d|_:d|_;d|_d|_?dS)z_!Ensure that self.varname exists for all member variables, so that Doxygen detects themrYr{N)@r4rrrrr1rrr7rrfZ windcode34Z windcode50Z windcode64rrr6rrrrrrrrrrrr rrrrrrrrrrrrrrrrrrr0rr r!r"r*r-r/r5r0r6r1r@r2r9r3rsr"rrrZ __doxygenszStormInfo.__doxygen)rNT)N)rNT)NT)rNT)NT)NT)N)F)F)F)F)NF)NF)rrrrr rrrrrrrrrrrrr,r.r r<rArBr@rRr Z_StormInfo__doxygenr+rrr)rrs2F I 6 /  1 T 0    V  cCst|}|dkrdn t|}|dks4|dkr|d kr(d!}n|d"kr8d#}nd$}n|d%krNd$}n|d&kr|dkrhd}n$|dkrxd'}n|d(krd)}nd*}nj|d+kr|dkrd,}n|dkrd-}nd.}n:|d(krd)}n*|d krd!}n|d"krd#}n t|||S)/a(!Converts basin identifiers Given a one-letter or two-letter tropical basin identifier, and possibly another one-letter tropical basin identifier (subbasin), attempts to determine more information about the basin. Some information may be ambiguous if a two letter basin is specified. Returns a four-element tuple: 1. The internal (HWRF/GFDL) two-letter basin identifier. These have an unambiguous mapping to the one-letter basin. 2. The public, standard two-letter basin identifier used by JTWC and others. These are ambiguous: IO can be A or B, and SH can be S or P. 3. The one-letter basin identifier. 4. A description of the meaning of the basin. @param basin the primary basin @param subbasin Optional: the subbasin, if knownNrYALr)rTrTrzNorth Atlantic (L/AL)SLr$)rUrUr$South Atlantic (Q/SL/LS)LS)rWrWr$rVEPrL)rXrXrLzNorth East Pacific (E/EP)CPC)rYrYrZzNorth Central Pacific (C/CP)SSrK)r[SHrKzSouth Pacific (S/SH)PPP)r]r\r^zSouth Indian Ocean (P/SH/PP)AAA)r_IOr`z#Indian Ocean: Arabian Sea (A/IO/AA)NA)rbrar`z#Indian Ocean: Arabian Sea (A/IO/NA)BBB)rcrardz%Indian Ocean: Bay of Bengal (B/IO/BB)WPO)ZOOrerfz2North West Pacific: South China Sea Basin (O/W/WP)T)ZTTrergz+North West Pacific: East China Sea (T/W/WP))rererMzNorth West Pacific (W/WP)rMr\)r]r\r^zSouth Indian Ocean (P/SH)U)ZUUr\rhz*South Pacific: Australian Basin (U/P/S/SH))r\r\rKz(South Pacific or South Indian Ocean (SH)ra)r_rar`z Indian Ocean: Arabian Sea (A/IO))rcrardz"Indian Ocean: Bay of Bengal (B/IO))rarardz#Unspecified North Indian Ocean (IO))r}rr)rrbrrSrrrrsh                )NNN)rO)rYrZ)NF)NFNNN)NT)N)&r__all__rRrrZ fractionsrr hwrf.numericsr~rZpdbr`Znowr8r Exceptionrrrrrrrr r r;r?r r r rrrrobjectrrrrrrsn0 /  +  5 !O