# ---------------------------------------------------------------------------- # This software is in the public domain, furnished "as is", without technical # support, and with no warranty, express or implied, as to its usefulness for # any purpose. # # Tropical_Overrides: PLEASE READ EVERY COMMENT (KEY WORD COMMENT) BEFORE EDITING. # # Authors: Pablo Santos (MIA) Created: 02/26/2007 # Guy Rader (MIA) Last Modified: 04/17/2008 # Matthew Belk (BOX) # Tracy Hansen (GSD) # David Sharp (MLB) # Matthew Volkmer (MLB) # Matthew Hirsch (MLB) # # "Steps to customize your Tropical Cyclone formatters: # 1. Read through the Comments (key word COMMENT) in this file. # 2. For any method you want to override for your Site: # --If the method is a Product Component (Period_1, CWF_Period, etc.) # go ahead and make the change in the Site Overrides file. This # information will be read into the Tropical Overrides file from the Site # Overrides file. # --If the method is not a Product Component and it appears in the # Tropical Overrides file, make the changes in this Tropical Overrides # file, NOT just in the Site Overrides file. This is necessary since the # Tropical Overrides file will supercede the Site Overrides file. # --If the method does not appear in the Tropical Overrides file, go # ahead and override it in the Site Overrides file. # # COMMENT: DO NOT DELETE COMPONENTS BELOWS. THEY ARE ALL NEEDED # ONE WAY OR ANOTHER FOR THE TROPICAL CYCLONE FORMATTER LOGIC TO WORK PROPERLY. # EXCEPTIONS ARE IN AREAS WHERE SOME MINOR CUSTOMIZATION (LOCAL) MIGHT BE NEEDED # AS HIGHLIGHTED ABOVE AND IN THE COMMENT SECTIONS BELOW. # # GENERAL EXCEPTION: ANY METHOD OVERWRITTEN IN THIS FILE NEED TO BE REVISED AGAINST # THE METHOD IN YOUR LOCAL SITE FILE. IF THERE ARE LOCAL EFFECTS, PHRASES, ETC THAT # YOU ARE HANDLING IN YOUR LOCAL SITE FILE THROUGH A METHOD, THOSE ENTRIES WILL HAVE # TO BEE ADDED TO THE METHOD IN THIS FILE IN ORDER TO PORT THEM TO THE TROPICAL # FORMATTERS. THE EXCEPTION TO THIS IS ANY LOCAL EFFECTS COMING THROUGH THE LOCAL SITE # COMPONENT PERIOD DEFINITI0NS. THOSE ARE LOADED IN THIS TROPICAL OVERRIDE FILE ALREADY # BY DESIGN. # # A GOOD EXAMPLE OF THESE GENERAL EXCEPTIONS IS THE phrase_descriptor_dict METHOD INCLUDED # BELOW. NOTICE THE COMMENT AND KMFL ENTRIES. # # ANY PROBLEMS SHOULD BE BROUGH UP TO THE TEAM MEMBERS ABOVE. # ---------------------------------------------------------------------------- ## import string, time, re, os, types, copy, AFPS ## import TextRules, SampleAnalysis ## import HazardUtils import string, time, re, os, types, copy, AFPS import TextRules import HazardUtils import VTECTable import SampleAnalysis import ModuleAccessor import AreaDictionary # Import the baseline AreaFcst module so we can access the original VaribleList import AreaFcst # Define Tropical overrides of Product Definition settings and # default values of additional Tropical Definition settings # ( This Definition section must be before the Class definition) #***** THIS NEXT LINE IS REQUIRED ***** Definition = {} #------------------------------------------------------------------------------- # ER Definitions: # Definition statements must start in column 1 ### Tropical settings of baseline options: ### Definition["displayName"] = "None" # Do not use period combining - required for proper operation of logic Definition["periodCombining"] = 0 # If 1, do period combining # Define forecast period number for first component of this product. This is # for greater flexibility in production of a tropical SAF Definition["firstComponentPeriod"] = 1 # valid values 1-14 # Define tropical wind speed probability thresholds for each forecast period # NOTE: The first periods uses two thresholds for each wind speed category # (34 kt and 64 kt). All other periods only use one threshold for each wind # speed category. # # Format: List of tuples. Each tuple represents a forecast period like this: # # (34 kt threshold, 64 kt threshold) # # The exception is in the first period, where the threshold definition is: # # ((34 kt low, 34 kt high), (64 kt low, 64 kt high)) Definition["windSpdProbThresholds"] = [((50.0, 70.0), (15.0, 30.0)), # Per 1 (40.0, 12.0), # Per 2 (35.0, 10.0), # Per 3 (30.0, 9.0), # Per 4 (25.0, 8.0), # Per 5 (20.0, 7.0), # Per 6 (17.5, 6.0), # Per 7 (15.0, 5.0), # Per 8 (12.5, 4.0), # Per 9 (10.0, 3.0), # Per 10 ] # END Tropical definitions ############################################################ #********************************************************************** # MAKE NO CHANGES HERE # The minimum contents of this file are the above Definition = {} line # plus following class definition and the __init__ method with only # the "pass" line in it. class Tropical_Overrides: """Class Tropical_Overrides - Version: IFPS OB7.2-1 (02/27/2007)""" def __init__(self): pass # End MAKE NO CHANGES HERE #********************************************************************** # Add methods here making sure to indent inside the class statement # Tropical_Overrides ------------------------ # It is helpful to put a debug statement at the beginning of each # method to help with trouble-shooting. #def _method(self): #self.debug_print("Debug: _method in Tropical_Overrides") ############################################################################ # VectorRelatedThreshold method overrides ############################################################################ # # COMMENT: THIS THRESHOLD HAS BEEN LOWERED FROM 10 IN BASELINE TO MINIMIZE # OR ELIMINATE GUSTS DROP OUTS DURING TROPICAL CYCLONE SITUATIONS. # def gust_wind_difference_nlValue(self, tree, node): """Tropical_Overrides version of VectorRelatedPhrases.gust_wind_difference_nlValue. Modified to lower threshold value from 10 MPH to 5 MPH. """ self.debug_print("\tTropical_Overrides version of " + "VectorRelatedPhrases.gust_wind_difference_nlValue") # Difference between gust and maxWind below which gusts are not # mentioned. Units are MPH return 5 def simple_vector_phrase(self, tree, node, elementInfo, checkRepeating=1): """Tropical_Overrides version of VectorRelatedPhrases.simple_vector_phrase. Modified to create and populate a 'maxMagList' component level variable. """ self.debug_print("\tTropical_Overrides version of " + "VectorRelatedPhrases.simple_vector_phrase") # Create a vector subPhrase # Do not repeat mag, dir if same as previous phrase elementName = elementInfo.name statDict = node.getStatDict() stats = self.getStats(statDict, elementName) if stats is None: return "" mag, dir = stats self.debug_print("Simple stats = %s" % (repr(stats)), 1) self.debug_print("Simple wind mag = %s" % (repr(mag)), 1) minMag, maxMag = self.getValue(mag, "MinMax") #!!! COMMENT: Save maxMag at component level for other methods to use. # THIS IS PARTICULARLY IMPORTANT FOR USE IN THE getIncludeOnly def # below to eliminate certainly wx elements during tropical cyclone # situations when certain conditions are met. component = node.getComponent() maxMagList = component.get("maxMagList") if maxMagList is None: maxMagList = [maxMag] else: maxMagList.append(maxMag) component.set("maxMagList", maxMagList) # print "Saving maxMagList from simple_vector_phrase", maxMagList, node.getTimeRange(), node.getAreaLabel() #!!! words = self.vector_mag(tree, node, minMag, maxMag, elementInfo.outUnits, elementName) if words == "null": return words magStr = words dirStr = self.vector_dir(dir) if checkRepeating: # Set for future reference node.set("dirStr", dirStr) node.set("magStr", magStr) node.set("minMag", minMag) node.set("maxMag", maxMag) if minMag == 0.0: minMag = maxMag # Check for repeating mag or dir prevNode = node.getPrev() if prevNode is not None: prevDirStr = prevNode.get("dirStr") prevMagStr = prevNode.get("magStr") prevMin = prevNode.get("minMag") prevMax = prevNode.get("maxMag") if prevMin == 0.0: prevMin = prevMax if prevMin is None or prevMax is None or \ prevDirStr is None or prevMagStr is None: pass elif prevDirStr == dirStr and prevMagStr == magStr: pass elif prevDirStr == dirStr: dirStr = "" elif prevMagStr == magStr: magStr = "" # Prevent "around 10 becoming 5 to 10" # "around 10 becoming 10 to 15" elif prevMin == prevMax: if (minMag == prevMax - 5.0) or (maxMag == prevMax + 5.0): magStr = "" # Prevent "5 to 10 becoming around 10" # "10 to 15 becoming around 10" elif minMag == maxMag: if (prevMin == maxMag - 5.0) or (prevMax == maxMag + 5.0): magStr = "" words = dirStr + self.format(magStr) return words.lstrip() def embedded_gust_phrase(self, tree, node, gustStats, maxWind, subRange): """Tropical_Overrides version of VectorRelatedPhrases.embedded_gust_phrase. Modified to only report gusts explicitly if criteria has been met, using rounded values of Wind and WindGust. Also modified to remove general gust phrases which are not permitted by 10-503 Directive. """ self.debug_print("\tTropical_Overrides version of " + "VectorRelatedPhrases.embedded_gust_phrase") # Determine what type of gust phrase to add. Day and night are treated # differently with gusts phrases toned down a bit for night. gusts = None if gustStats is None: # If useWindForGusts_flag is set, use max Wind for reporting gusts if self.useWindsForGusts_flag(tree, node) == 1: windStats = tree.stats.get("Wind", subRange, node.getAreaLabel(), ## statLabel="vectorModeratedMinMax", mergeMethod="Max") if windStats is None: return "" else: gusts, dir = windStats else: gusts = self.getValue(gustStats,"Max") if gusts is None: return "" # Round gusts and maxWind to the nearest 5 kt gusts = self.round(gusts, 'Nearest', 5.0) maxWind = self.round(maxWind, 'Nearest', 5.0) # Get the threshold for reporting wind gusts threshold = self.nlValue(self.null_nlValue(tree, node, "WindGust", "WindGust"), gusts) # Display debug info - if flag is set self.debug_print('node timeRange = %s' % (repr(node.getTimeRange())) ,1) self.debug_print('gust = %s\tmaxWind = %s\tthreshold = %s' % (gusts, maxWind, threshold), 1) if gusts < threshold: return "" gustPhrase = "" outUnits = self.element_outUnits(tree, node, "WindGust", "WindGust") units = self.units_descriptor(tree, node, "units", outUnits) windDifference = self.nlValue(self.gust_wind_difference_nlValue(tree, node), maxWind) # ER policy is to report wind gust that meets or exceed the threshold if (gusts - maxWind) >= windDifference: # was '>' gustPhrase = " with gusts to around " + `int(gusts)` + " " + units return gustPhrase ############################################################################ # SampleAnalysis method overrides ############################################################################ def moderated_dict(self, parmHisto, timeRange, componentName): """Tropical_Overrides version of SampleAnalysis.moderated_dict. Modifed to lower the high end filter threshold from 20 MPH to 15 MPH. """ self.debug_print(" Tropical_Overrides version of " + "SampleAnalysis.moderated_dict") # COMMENT: This dictionary defines the low and high limit at which # outliers will be removed when calculating moderated stats. # By convention the first value listed is the percentage # allowed for low values and second the percentage allowed # for high values. The thresholds chosen below gave best results # during testing with 2004 and 2005 tropical cyclones. This dict # is used with the moderatedMinMax analysis method specified in the # TropicalPeriod definitions specified further down for use with # tropical cyclones with wind parameters. # Get Baseline thresholds dict = SampleAnalysis.SampleAnalysis.moderated_dict(self, parmHisto, timeRange, componentName) # Change thresholds for Wind, WindGust, WaveHeight and Swell dict["Wind"] = (0, 15) dict["WindGust"] = (0, 15) dict["WaveHeight"] = (0, 15) dict["Swell"] = (0, 15) # Print debug message if flag is set self.debug_print("\tdict = %s" % (dict), 1) return dict def temporalCoverage_hours(self, parmHisto, timeRange, componentName): """Tropical_Overrides version of SampleAnalysis.temporalCoverage_hours. Modifed to require a 1 hr grid overlap for inclusion in sampling. """ self.debug_print(" Tropical_Overrides version of " + "SampleAnalysis.temporalCoverage_hours") # This is the required hours of overlap of a grid with the TIMERANGE # in order to include it in the analysis. # In addition, if the temporalCoverage_hours is greater than or equal to the # TIMERANGE duration and the grid covers the entire TIMERANGE, # it will be included. # Temporal coverage hours default value # (if not found in temporalCoverage_hours_dict) # Used by temporalCoverage_flag # # COMMENT: At WFO MFL we use 3 hrly wind grids. If you use 1 hrly wind grids # and this parameter is 2 or higher, tropical cyclone winds affecting the very # early or latter part of a forecast period might be neglected. 1 assures # maximum sensitivity. return 1 def temporalCoverage_hours_dict(self, parmHisto, timeRange, componentName): """Tropical_Overrides version of SampleAnalysis.temporalCoverage_hours_dict. Modifed to require a 1 hr grid overlap for inclusion in sampling. """ self.debug_print(" Tropical_Overrides version of " + "SampleAnalysis.temporalCoverage_hours_dict") # This is the temporalCoverage_hours specified per weather element. # Used by temporalCoverage_flag # Get Baseline thresholds dict = SampleAnalysis.SampleAnalysis.temporalCoverage_hours_dict(self, parmHisto, timeRange, componentName) # COMMENT: Add local site override entries here if any. # KMFL # dict["PoP"] = 2 # dict["Wx"] = 2 # dict["WindChill"] = 2 # Add thresholds for tropical wind speed probability grids. dict["pws34"] = 4 dict["pws64"] = 4 dict["pwsD34"] = 4 dict["pwsN34"] = 4 dict["pwsD64"] = 4 dict["pwsN64"] = 4 # Print debug message if flag is set self.debug_print("\tdict = %s" % (dict), 1) return dict ############################################################################ # ConfigVariables method overrides ############################################################################ def phrase_descriptor_dict(self, tree, node): """Tropical_Overrides version of ConfigVariables.phrase_descriptor_dict. Modified to add new descriptors to handle tropical phrases. Also prevented the use of areal PoP descriptors and changed the term used for new snow/sleet/ice accumulations to 'additional'. """ self.debug_print("\tTropical_Overrides version of " + "ConfigVariables.phrase_descriptor_dict") # Baseline descriptors for phrases dict = TextRules.TextRules.phrase_descriptor_dict(self, tree, node) # Add new descriptors for tropical phrases. REQUIRED dict["iminHR"] = "HURRICANE CONDITIONS" dict["iminTS"] = "TROPICAL STORM CONDITIONS" dict["iminTSposHR"] = \ "TROPICAL STORM CONDITIONS WITH HURRICANE CONDITIONS POSSIBLE" dict["posTS"] = "TROPICAL STORM CONDITIONS POSSIBLE" dict["posTSbcmgposHR"] = \ "TROPICAL STORM CONDITIONS POSSIBLE WITH HURRICANE CONDITIONS " + \ "ALSO POSSIBLE" dict["expTS"] = "TROPICAL STORM CONDITIONS EXPECTED" dict["posHR"] = "HURRICANE CONDITIONS POSSIBLE" dict["expHR"] = "HURRICANE CONDITIONS EXPECTED" dict["expTSposHR"] = "TROPICAL STORM CONDITIONS EXPECTED WITH " + \ "HURRICANE CONDITIONS POSSIBLE" dict["posTSorHR"] = "TROPICAL STORM OR HURRICANE CONDITIONS POSSIBLE" # COMMENT: Add phrases in the local site Override file for this method. # Otherwise these phrases will not work in the tropicalCWF. # KMFL #dict["bay chop"] = "bay waters" #dict["inland chop"] = "INTRACOASTAL WATERS" #dict["gulf chop"] = "BAY AND INLAND WATERS" #dict["lake chop"] = "lake waters" dict["seas"] = "seas" # Print debug message if flag is set self.debug_print("\tdict = %s" % (dict), 1) #print "DICT HERE IS: ", dict return dict def maximum_range_nlValue_dict(self, tree, node): """Tropical_Overrides version of ConfigVariables.maximum_range_nlValue_dict. Modified to report temperatures with a maximum range of 5 degrees, swell with a maximum of 2 ft and to report wind speeds using the following maximum ranges: Max Wind Speed < 4 mph -> 0 mph Max Wind Speed 4-32 mph -> 5 mph Max Wind Speed 33-51 mph -> 10 mph Max Wind Speed > 51 mph -> 20 mph """ self.debug_print("\tTropical_Overrides version of " + "ConfigVariables.maximum_range_nlValue_dict") # Maximum range to be reported within a phrase (e.g. 5 to 10 mph) # Units depend on the product # Baseline max ranges for certain fields dict = TextRules.TextRules.maximum_range_nlValue_dict(self, tree, node) #----------------------------------------------------------------------- # COMMENT: Override max ranges for certain fields # This dict specifications allows for wind speed ranges of up to 20 mph # during tropical cyclone situations allowing for far better wind speed # phrases. #----------------------------------------------------------------------- dict["Wind"] = {'default': 5, (0.0, 4.0): 0, (4.0, 33.0): 5, (33.0, 52.0): 10, (52.0, 200.0): 20, } # Print debug message if flag is set self.debug_print("\tdict = %s" % (dict), 1) return dict ############################################################################ # PhraseBuilder method overrides ############################################################################ def assemblePhrases(self, tree, component): """Tropical_Overrides version of PhraseBuilder.assemblePhrases" Modified to skip some phrases in certain conditions. """ self.debug_print("\tTropical_Overrides version of " + "PhraseBuilder.assemblePhrases") # Assemble component phrases and add Label # Qualify the phrases with local effect qualifiers # if present. # e.g. "near the coast" phrases = [] for phrase in component.get("childList"): words = phrase.get("words") if words is None: return if words != "": phrases.append(phrase) #print "\nAssemblePhrases" # Remove empty word phrases component.childList = phrases self.orderWxPhrases(tree, component) self.consolidateLocalEffectPhrases(tree, component) #print fcst = "" lastQualifier = None lastPhrase = None phraseList = [] # COMMENT: See which phrases we should include. This is needed by # tropical cyclone formatters. includeOnlyPhrases = [] includeOnlyPhrases = self._getIncludeOnly(tree, component) for phrase in component.get("childList"): words = phrase.get("words") words = self.adjustWords(tree, phrase, words) #print phrase.get('name'), phrase.getAreaLabel() #print " ", words # If the list of phrases to include is not empty, and this phrase # is not in that list # if includeOnlyPhrases != [] and \ # phrase.get('name') not in includeOnlyPhrases: if type(includeOnlyPhrases) is types.ListType and len(includeOnlyPhrases) > 0 and \ phrase.get('name') not in includeOnlyPhrases: # Do not include this phrase continue words, lastQualifier = self.qualifyWords( phrase, words, "conjunctiveQualifier", lastQualifier, lastPhrase) lastPhrase = phrase if words not in phraseList: phraseList.append(words) fcst = fcst + words # Add label curLocalTime, shift = self.determineTimeShift() issuanceInfo = tree.get("issuanceInfo") index = component.getIndex() label = self.createLabel(tree, component, component.get("timeRange"), issuanceInfo, curLocalTime, shift, index) fcst = self.combineSentences(fcst) return self.setWords(component, label + fcst) ############################################################################ # AreaFcst method overrides ############################################################################ # COMMENT: This _getVariable def is the same as the default. But you still need it # in case it has been modified in the site override file to allow # forecasters to choose whether they want period combination or not. In the # Definition section above, the variable PeriodCombination has also # been set to 0. This is the case because in order for the tropical cyclone # formatter logic to work no period combination can be allowed. This assumes # that in the accompanying text product files the herarchy is such that # baseline files are overwritten and/or modified by the local site files which # are then overwritten and/or modified by the tropical formatter files. def _getVariables(self, argDict): # """Tropical_Overrides copy of AreaFcst or CWF _getVariables method. # """ self.debug_print("\tTropical_Overrides version of " + "AreaFcst._getVariables") # Make argDict accessible self.__argDict = argDict # Get Definition variables self._definition = argDict["forecastDef"] for key in self._definition.keys(): exec "self._" + key + "= self._definition[key]" # Get VariableList and _issuance_list variables varDict = argDict["varDict"] for key in varDict.keys(): if type(key) is types.TupleType: label, variable = key exec "self._" + variable + "= varDict[key]" # COMMENT: Next 4 lines are applicable only to WFO MFL so you should delete them. #if self._definition.has_key("directiveType"): # # self._PBIRip = self._getPBIRIPRISK(self._PBIRip) # self._MIARip = self._getMIARIPRISK(self._MIARip) # self._APFRip = self._getAPFRIPRISK(self._APFRip) self._language = argDict["language"] return None ############################################################################ # Universal versions of methods from AreaFcst and CWF modules ############################################################################ # COMMENT: This issuance list assumes you are following the baseline Period definitions. # See comments under the TropicalPeriod definitions further down. ############################################################################ def _issuance_list(self, argDict): # This method sets up configurable issuance times with associated # narrative definitions. See the Text Product User Guide for documentation. # expireAm = 4.5 + 6 # expireLateAm = 10.5 + 6 # expireAftn = 16.5 + 6 # expireNight = 22.5 + 6 # See which product we're dealing with # print "I AM IN ISSUANCE_LIST\n"; if self._definition.has_key("directiveType"): # ZFP only # Use the ZFP settings narrativeDefAM = [ ("TropicalPeriod_1", "period1"), ("TropicalPeriod_2_3", 12), ("TropicalPeriod_2_3", 12), ("TropicalPeriod_4_5", 12), ("TropicalPeriod_4_5", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ] narrativeDefPM = [ ("TropicalPeriod_1", "period1"), ("TropicalPeriod_2_3", 12), ("TropicalPeriod_2_3", 12), ("TropicalPeriod_4_5", 12), ("TropicalPeriod_4_5", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ("TropicalPeriod_6_14", 12), ] # Otherwise, use the CWF settings else: narrativeDefAM = [ ("TropicalCWFPeriod", "period1"), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ] narrativeDefPM = [ ("TropicalCWFPeriod", "period1"), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ("TropicalCWFPeriod", 12), ] return [ ##################################################################### # COMMENT: DO NOT CHANGE THESE OPTIONS. There should only be # 4 options with the tropical cyclone formatters. The tropical # cyclone formatter assume you update the ZFP and CWF with each # advisory time and it is built to work only with 12 hours # periods and not THIS EVENING and or EARLY MORNING PERIODS. ##################################################################### ("* Morning Package", self.DAY(), self.NIGHT(), self.NIGHT(), ".TODAY...", "early in the morning", "late in the afternoon", 1, narrativeDefAM), ("Morning Update or Early Afternoon Update", "issuanceHour", self.NIGHT(), self.NIGHT(), ".REST OF TODAY...", "early in the morning", "late in the afternoon", 1, narrativeDefAM), ("* Afternoon Package - beginning at 7 or 8 PM", self.NIGHT(), 24 + self.DAY(), self.NIGHT()+12, ".TONIGHT...", "late in the night", "early in the evening", 1, narrativeDefPM), ("Evening Update or Overnight", "issuanceHour", 24 + self.DAY(), self.NIGHT() + 12, ".REST OF TONIGHT...", "late in the night", "early in the evening", 1, narrativeDefPM), ] def _determineTimeRanges(self, argDict): """Tropical_Overrides version of AreaFcst._determineTimeRanges. Ensures we're not using use fixed issuance times in the MND header, particularly for the CWF. """ self.debug_print("\tTropical_Overrides version of " + "AreaFcst._determineTimeRanges") # Set up the Narrative Definition and initial Time Range self._issuanceInfo = self.getIssuanceInfo( self._productIssuance, self._issuance_list(argDict)) self._timeRange = self._issuanceInfo.timeRange() argDict["productTimeRange"] = self._timeRange self._expireTime = self._issuanceInfo.expireTime() self._issueTime = self._issuanceInfo.issueTime() self._definition["narrativeDef"] = self._issuanceInfo.narrativeDef() if self._periodCombining: self._definition["methodList"] = \ [self.combineComponentStats, self.assembleChildWords] else: self._definition["methodList"] = [self.assembleChildWords] # Calculate current times self._ddhhmmTime = string.upper(self.getCurrentTime( argDict, "%d%H%M", shiftToLocal=0, stripLeading=0)) # Use current time for label and not a fixed issuance time self._timeLabel = self.getCurrentTime( argDict, "%l%M %p %Z %a %b %e %Y", stripLeading=1) # Set up an expiration time string for post-processing use expireTimeRange = AFPS.TimeRange(self._expireTime, self._expireTime + 3600) self._expireTimeStr = string.upper(self.timeDisplay(expireTimeRange, "", "", "%d%H%M", "")) ################################################################################ ################################################################################ # # Tropical Phrase additions # ################################################################################ ################################################################################ #--------------------------------------------------------------------------- # COMPONENT PRODUCT DEFINITIONS - ZFP #--------------------------------------------------------------------------- def TropicalPeriod_1(self): """Tropical_Overrides addition of TropicalPeriod_1. Modified to add tropical sampling and phrases to pre-existing version. """ self.debug_print("\tTropical_Overrides addition of TropicalPeriod_1") # Get already existing component definition ############################################################################## # COMMENT: The TropicalPeriod definitions only contain items needed to enable # the tropical cyclone formatter logic to work. So the first thing they do is # call the Period_ components from baseline or a modified version you might # have in a local site file. This is why it is important that if you modified # the baseline period defs in your local site override files, that you stick # to baseline naming. Otherwise, the call to the Period_ def in the following # line would have to be modified to whatever you used in the local site file. # This applies to all the TropicalPeriod definitions below. It would still work # but only load baseline components to this point and those in your local site # file would be skipped if not named according to baseline. ############################################################################### component = self.Period_1() # Get ready to make a new analysisList newAnalysisList = [] # Look through the current analysis list for entry in component["analysisList"]: ################################################################################## # Sampling defined as a tuple (field, statistic, temporal rate) # If this is NOT a Wind or WindGust statistic ################################################################################## # COMMENT: As stated in the documentation, the formatters have been tested # and fine tuned to work with the vectorModeratedMinMax analysis method for Wind # components. The following lines of code make sure that the Analysis List in # the baseline or local site Period_ definitions are substituted with this # analysis method when using the tropical cyclone formatters. Other needed elements # for the tropical cyclone formatter logic are also added here. ################################################################################### if entry[0] not in ["Hazards", "Wind", "WindGust"]: # Add this statistic to the new analysisList newAnalysisList.append(entry) # Now that all Wind and WindGust sampling has been removed, # add in the statistics we want for Wind and WindGust # It also adds Hazards to the analysis list to be able # to get hazard keys from pws_words. newAnalysisList.append(("Hazards", self.discreteTimeRangesByKey)) newAnalysisList.append(("Wind", self.vectorModeratedMinMax, [6])) newAnalysisList.append(("WindGust", self.moderatedMinMax, [6])) # Add tropical sampling newAnalysisList.append(("pws34", self.maximum)) newAnalysisList.append(("pws64", self.maximum)) newAnalysisList.append(("pwsN34", self.maximum)) newAnalysisList.append(("pwsN64", self.maximum)) newAnalysisList.append(("pwsD34", self.maximum)) newAnalysisList.append(("pwsD64", self.maximum)) # Put the cleaned up analysisList back into the component component["analysisList"] = newAnalysisList # Add the probabilistic wind phrase and wind summary phrases first component["phraseList"].insert(0, self.pws_phrase) return component def TropicalPeriod_2_3(self): """Tropical_Overrides addition of TropicalPeriod_2_3. Modified to add tropical sampling and phrases to pre-existing version. """ self.debug_print("\tTropical_Overrides addition of TropicalPeriod_2_3") # Get already existing component definition component = self.Period_2_3() # Get ready to make a new analysisList newAnalysisList = [] # Look through the current analysis list for entry in component["analysisList"]: # Sampling defined as a tuple (field, statistic, temporal rate) # If this is NOT a Wind or WindGust statistic if entry[0] not in ["Hazards", "Wind", "WindGust"]: # Add this statistic to the new analysisList newAnalysisList.append(entry) # Now that all Wind and WindGust sampling has been removed, # add in the statistics we want for Wind and WindGust # It also adds Hazards to the analysis list to be able # to get hazard keys from pws_words. newAnalysisList.append(("Hazards", self.discreteTimeRangesByKey)) newAnalysisList.append(("Wind", self.vectorModeratedMinMax, [6])) newAnalysisList.append(("WindGust", self.moderatedMinMax, [6])) # Add tropical sampling newAnalysisList.append(("pws34", self.maximum)) newAnalysisList.append(("pws64", self.maximum)) newAnalysisList.append(("pwsN34", self.maximum)) newAnalysisList.append(("pwsN64", self.maximum)) newAnalysisList.append(("pwsD34", self.maximum)) newAnalysisList.append(("pwsD64", self.maximum)) # Put the cleaned up analysisList back into the component component["analysisList"] = newAnalysisList # Add the probabilistic wind phrase and wind summary phrases first component["phraseList"].insert(0, self.pws_phrase) return component def TropicalPeriod_4_5(self): """Tropical_Overrides addition of TropicalPeriod_4_5. Modified to add tropical sampling and phrases to pre-existing version. """ self.debug_print("\tTropical_Overrides addition of TropicalPeriod_4_5") # Get already existing component definition component = self.Period_4_5() # Get ready to make a new analysisList newAnalysisList = [] # Look through the current analysis list for entry in component["analysisList"]: # Sampling defined as a tuple (field, statistic, temporal rate) # If this is NOT a Wind or WindGust statistic if entry[0] not in ["Hazards", "Wind", "WindGust"]: # Add this statistic to the new analysisList newAnalysisList.append(entry) # Now that all Wind and WindGust sampling has been removed, # add in the statistics we want for Wind and WindGust # It also adds Hazards to the analysis list to be able # to get hazard keys from pws_words. newAnalysisList.append(("Hazards", self.discreteTimeRangesByKey)) newAnalysisList.append(("Wind", self.vectorModeratedMinMax, [6])) newAnalysisList.append(("WindGust", self.moderatedMinMax, [6])) # Add tropical sampling newAnalysisList.append(("pws34", self.maximum)) newAnalysisList.append(("pws64", self.maximum)) newAnalysisList.append(("pwsN34", self.maximum)) newAnalysisList.append(("pwsN64", self.maximum)) newAnalysisList.append(("pwsD34", self.maximum)) newAnalysisList.append(("pwsD64", self.maximum)) # Put the cleaned up analysisList back into the component component["analysisList"] = newAnalysisList # Add the probabilistic wind phrase and wind summary phrases first component["phraseList"].insert(0, self.pws_phrase) return component def TropicalPeriod_6_14(self): """Tropical_Overrides addition of TropicalPeriod_6_14. Modified to add tropical sampling and phrases to pre-existing version. """ self.debug_print("\tTropical_Overrides addition of TropicalPeriod_6_14") # Get already existing component definition component = self.Period_6_14() # Get ready to make a new analysisList newAnalysisList = [] # Look through the current analysis list for entry in component["analysisList"]: # Sampling defined as a tuple (field, statistic, temporal rate) # If this is NOT a Wind or WindGust statistic if entry[0] not in ["Wind", "WindGust"]: # Add this statistic to the new analysisList newAnalysisList.append(entry) # Now that all Wind and WindGust sampling has been removed, # add in the statistics we want for Wind and WindGust newAnalysisList.append(("Wind", self.vectorModeratedMinMax, [6])) newAnalysisList.append(("WindGust", self.moderatedMinMax, [6])) # Add tropical sampling newAnalysisList.append(("pws34", self.maximum)) newAnalysisList.append(("pws64", self.maximum)) newAnalysisList.append(("pwsN34", self.maximum)) newAnalysisList.append(("pwsN64", self.maximum)) newAnalysisList.append(("pwsD34", self.maximum)) newAnalysisList.append(("pwsD64", self.maximum)) # Put the cleaned up analysisList back into the component component["analysisList"] = newAnalysisList # Add the probabilistic wind phrase and wind summary phrases first component["phraseList"].insert(0, self.pws_phrase) return component #--------------------------------------------------------------------------- # COMPONENT PRODUCT DEFINITIONS - CWF #--------------------------------------------------------------------------- def TropicalCWFPeriod(self): """Tropical_Overrides addition of TropicalCWFPeriod_1. Modified to add tropical sampling and phrases to pre-existing version. """ self.debug_print("\tTropical_Overrides addition of TropicalCWFPeriod_1") #Baseline Period definition would need to follow something as follows: component = self.CWFPeriod() #### FORGOT self. #### # Get ready to make a new analysisList newAnalysisList = [] # Look through the current analysis list for entry in component["analysisList"]: # Sampling defined as a tuple (field, statistic, temporal rate) # If this is NOT a Wind or WindGust statistic if entry[0] not in ["Hazards", "Wind", "WindGust", "WaveHeight", "Swell"]: # Add this statistic to the new analysisList newAnalysisList.append(entry) # Now that all Wind and WindGust sampling has been removed, # add in the statistics we want for Wind and WindGust # It also adds Hazards to the analysis list to be able # to get hazard keys from pws_words. newAnalysisList.append(("Hazards", self.discreteTimeRangesByKey)) newAnalysisList.append(("Wind", self.vectorModeratedMinMax, [6])) newAnalysisList.append(("WindGust", self.moderatedMinMax, [6])) newAnalysisList.append(("WaveHeight", self.moderatedMax, [6])) newAnalysisList.append(("Swell", self.vectorModeratedMinMax, [6])) # Add tropical sampling newAnalysisList.append(("pws34", self.maximum)) newAnalysisList.append(("pws64", self.maximum)) newAnalysisList.append(("pwsN34", self.maximum)) newAnalysisList.append(("pwsN64", self.maximum)) newAnalysisList.append(("pwsD34", self.maximum)) newAnalysisList.append(("pwsD64", self.maximum)) # Put the cleaned up analysisList back into the component component["analysisList"] = newAnalysisList # Add the probabilistic wind phrase first component["phraseList"].insert(0, self.pws_phrase) return component #--------------------------------------------------------------------------- # Probabilistic Wind Phrase #--------------------------------------------------------------------------- def pws_phrase(self): """Tropical_Overrides addition of pws_phrase. Add to produce the tropical probabilistic wind phrase. """ self.debug_print("\tTropical_Overrides addition of pws_phrase") return { "setUpMethod": self.pws_setUp, "wordMethod": self.pws_words, "phraseMethods": [ self.combineWords, self.fillNulls, self.timeDescriptorModeration, self.assembleSubPhrases, ], } def pws_setUp(self, tree, node): """Tropical_Overrides addition of pws_setUp. Setup method for the tropical probabilistic wind phrase. """ self.debug_print("\tTropical_Overrides addition of pws_setUp") elementInfoList = [] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) return self.DONE() def _getIncludeOnly(self, tree, component): """Tropical_Overrides addition of _getIncludeOnly. Determines which phrases to keep in each period of the ZFP. """ self.debug_print("\tTropical_Overrides addition of _getIncludeOnly") # Return list of phrases to include in the component # Return an empty list of all phrases should be included # See which period we are in compPeriod = int(component.getIndex() + self._firstComponentPeriod) self.debug_print("Working in Period %d" % (compPeriod), 1) # See which list of periods we may want to modify if self._definition.has_key('directiveType'): # ZFP includeSomeList = [1] else: includeSomeList = [6,7,8,9,10] # If this is not one of the periods we might want to remove phrases if compPeriod not in includeSomeList: # Ensure all phrases are used return [] # Grab thresholds for this period - handle the first period case if compPeriod == 1: (thresh34low, thresh34high) = self._windSpdProbThresholds[0][0] (thresh64low, thresh64high) = self._windSpdProbThresholds[0][1] # Display thresholds so we know what we're using self.debug_print("34 kt thresholds = (%.2f, %.2f)" % (thresh34low, thresh34high), 1) self.debug_print("64 kt thresholds = (%.2f, %.2f)" % (thresh64low, thresh64high), 1) # Otherwise, handle all other periods else: index = int(component.getIndex()) (thresh34, thresh64) = self._windSpdProbThresholds[index] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) # Get some information about this forecast period dayNight = self.getPeriod(component.getTimeRange(), 1) timeRange = component.getTimeRange() areaLabel = component.getAreaLabel() self.debug_print("dayNight = %s\ttimeRange = %s" % (dayNight, repr(timeRange)), 1) # Get pws64 if dayNight == 1: pws64 = tree.stats.get("pwsD64", timeRange, areaLabel, mergeMethod="Max") self.debug_print("USING pwsD64", 1) else: pws64 = tree.stats.get("pwsN64", timeRange, areaLabel, mergeMethod="Max") self.debug_print("USING pwsN64", 1) self.debug_print("PWS64 = %s" % (pws64), 1) if pws64 is None: return [] # Get pws34 if dayNight == 1: pws34 = tree.stats.get("pwsD34", timeRange, areaLabel, mergeMethod="Max") self.debug_print("USING pwsD34", 1) else: pws34 = tree.stats.get("pwsN34", timeRange, areaLabel, mergeMethod="Max") self.debug_print("USING pwsN34", 1) self.debug_print("PWS34 = %s" % (pws34), 1) if pws34 is None: return [] # statLabel not required if only one sampling of Wind ## wind = tree.stats.get("Wind", timeRange, areaLabel, mergeMethod="Max") ## if wind is None: ## return [] # COMMENT: Get the stored wind stats from the component level. IF WE WERE TO LIMIT ELEMENTS # IN THE ZFP BEYOND PERIOD 5, THE WIND STAT LABEL ABOVE WOULD ALSO BE NEEDED. maxMagList = component.get("maxMagList") if maxMagList is None: # return self.setWords(node, "") return self.setWords(component, "") self.debug_print("maxMag from getIncludeOnly: %s " % (maxMagList), 1) print "maxMagList from getIncludeOnly: ", maxMagList maxMag = 0.0 for mag in maxMagList: if mag > maxMag: maxMag = mag ## maxMag, dir = wind if self._definition.has_key('directiveType'): maxMag = maxMag*0.868976242 self.debug_print("maxMag in getIncludeOnly: %s " % (maxMag), 1) print "maxMag in getIncludeOnly: ", maxMag if maxMag is None: maxMag = 0.0 # Retrieve the headlineKeys stored at the component level headlineKeys = component.get("headlineKeys") if headlineKeys is None: headlineKeys = [] # If this is the first period, and in the list of periods we might # want to modify # See which list of periods we may want to modify if self._pil.find("ZFP") == 0: productType = "ZFP" includeSomeList = [1] else: productType = "CWF" includeSomeList = [6,7,8,9,10] if productType == "ZFP": if compPeriod == 1 and compPeriod in includeSomeList: if "HU.W" in headlineKeys or "HI.W" in headlineKeys: if pws64 >= thresh64high and maxMag >= 64.0: # Limit the phrases we'll report return ["pws_phrase", "wind_withGusts_phrase", "weather_phrase"] elif pws64 >= thresh64low and maxMag >= 50.0: # Keep all phrases return [] elif pws34 >= thresh34high and maxMag >= 34.0: # Limit the phrases we'll report return ["pws_phrase", "wind_withGusts_phrase", "weather_phrase"] elif "TR.W" in headlineKeys or "TI.W" in headlineKeys: if pws34 >= thresh34high and maxMag >= 34.0: # Limit the phrases we'll report return ["pws_phrase", "wind_withGusts_phrase", "weather_phrase"] else: return [] # keep all phrases # If this period is beyond the fifth period, and in the list of # periods we might want to modify else: if compPeriod >= 6 and compPeriod in includeSomeList: if ((pws34 >= thresh34 or pws34+2.5 >= thresh34) and maxMag >= 20.0) or ((pws64 >= thresh64 or pws64+1.0 >= thresh64) and maxMag >= 20.0) or maxMag >= 34.0: # Limit the phrases we'll report return ["pws_phrase", "weather_phrase"] else: # Return all phrases return [] def pws_words(self, tree, node): """ Words method for the tropical probabilistic wind phrase. """ # Get Wind self.debug_print("\nBegin period***********", 1) self.debug_print("\nNode time range -> %s" % (repr(node.getTimeRange())), 1) self.debug_print("Parent time range -> %s" % (repr(node.parent.getTimeRange())), 1) # Get name and index of this node's component component = node.getComponent() compIndex = node.getComponent().getIndex() compPeriod = int(compIndex + self._firstComponentPeriod) print "COMPONENT IN pws_words", compPeriod componentName = node.getComponentName() if self._pil.find("ZFP") == 0: productType = "ZFP" else: productType = "CWF" # COMMENT: If this is one of the first 5 periods of the ZFP, or this is the CWF if not productType == "ZFP" or compPeriod <= 5: print "I AM IN: ", node.getTimeRange() #!!! Wait for wind phrase to complete # We're assuming that all the wind phrases have completed (including # local effect phrases) if one has. if productType == "ZFP": phraseList = ["wind_withGusts_phrase"] else: phraseList = ["marine_wind_withGusts_phrase"] windWords = self.findWords(tree, node, "Wind", phraseList = phraseList) self.debug_print("windWords = '%s'" % (windWords), 1) # Wait for Wind phrase if windWords is None: return # Get the stored wind stats from the component level maxMagList = component.get("maxMagList") if maxMagList is None: return self.setWords(node, "") self.debug_print("MaxMagList from pws_words %s %s" % (maxMagList, repr(node.getTimeRange())), 1) # print "MaxMagList from pws_words", maxMagList, node.getTimeRange() maxMag = 0.0 for mag in maxMagList: if mag > maxMag: maxMag = mag if productType == "ZFP": # print "PWS MAXMAG in MPH IS: ", maxMag maxMag = maxMag*0.868976242 # print "PWS MAXMAG IN KNOTS: ", maxMag # # COMMENT: Othwerwise Periods 6 and beyond in the ZFP. # Although wind phrases are not included in extended ZFP you # still need to do the analysis so tropical cyclone formatter # logic can be carried out through the extended (day 5) periods. # else: print "I AM IN: ", node.getTimeRange() windStats = tree.stats.get( "Wind", node.getTimeRange(), node.getAreaLabel(), statLabel="vectorModeratedMinMax", mergeMethod="Max") ## print "WINDSTATS", windStats if windStats is None: return self.setWords(node, "") maxMag, dir = windStats maxMag = maxMag*0.868976242 # Display maximum wind speed in MPH and KTS self.debug_print("PWS MAXMAG in MPH IS: %s" % (maxMag), 1) self.debug_print("PWS MAXMAG in KTS IS: %s" % (maxMag), 1) dayNight = self.getPeriod(node.getTimeRange(), 1) self.debug_print("dayNight IS %s" % (dayNight), 1) # See which grids to use for probability of 34 and 64 kts if dayNight == 1: prob34 = "pwsD34" prob64 = "pwsD64" else: prob34 = "pwsN34" prob64 = "pwsN64" self.debug_print("USING pws34 = "+prob34, 1) self.debug_print("USING pws64 = "+prob64, 1) pws64 = tree.stats.get(prob64, node.getTimeRange(), node.getAreaLabel(), mergeMethod="Max") if pws64 is None: self.debug_print("pws64 NONE", 1) return self.setWords(node, "") pws34 = tree.stats.get(prob34, node.getTimeRange(), node.getAreaLabel(), mergeMethod="Max") if pws34 is None: self.debug_print("pws34 NONE", 1) return self.setWords(node, "") #print "check ", "check" #################################################################### #print "WORDS1", words words = "" areaLabel = tree.getAreaLabel() print "\nBegin period***********", node.getTimeRange() self.debug_print("\nNode time range -> %s" % (repr(node.getTimeRange())), 1) self.debug_print("Parent time range -> %s" % (repr(node.parent.getTimeRange())), 1) self.debug_print("MAXMAG IS -> %s KTS" % (maxMag), 1) self.debug_print("\nNode time and label -> %s %s" % (repr(node.getTimeRange()), repr(node.getAreaLabel())), 1) #tree.stats.printDictionary("Hazards") # Get Hazards headlines = tree.stats.get("Hazards", node.getTimeRange(), areaLabel, mergeMethod = "List") self.debug_print("maxMag = %s" % (maxMag), 1) self.debug_print("warningpws64 = %s" % (pws64), 1) self.debug_print("warningpws34 = %s" % (pws34), 1) self.debug_print("Headline stats for warning -> %s" % (repr(headlines)), 1) print "maxMag = ", maxMag print "warningpws64 = ", pws64 print "warningpws34 = ", pws34 print "Headline stats for warning ", headlines print "Product Type is: ", productType if headlines is not None: # Sort the headlines by startTime temp = [] for h, tr in headlines: temp.append((tr.startTime(), (h, tr))) temp.sort() newList = [] for t in temp: newList.append(t[1]) headlines = newList # Fetch the set of local headlines allowed for this product allowedHazards = [] for key, allActions, cat in self.allowedHazards(): allowedHazards.append(key) #Key during OB8.2.1 and OB8.3 to account for removal of hazards from allowedHazards for CWF. #allowedHeadlines = self.allowedHeadlines() allowedHeadlines = [] for key, allActions, cat in self.allowedHeadlines(): allowedHeadlines.append(key) # Create a list of headline keys as strings e.g. HU.A headlineKeys = [] for key, tr in headlines: # value == list of subkeys #if key not in allowedHazards: #Key during OB8.2.1 and OB8.3 to account for removal of hazards from allowedHazards for CWF. if key not in allowedHazards and key not in allowedHeadlines: continue # Don't call headlinesTimeRange_descriptor function due to # an exception which is caused - DR19483 #timeDescriptor = self.headlinesTimeRange_descriptor( # tree, node, key, tr, areaLabel, issuanceTime) if key == "": continue if key not in headlineKeys: headlineKeys.append(key) self.debug_print("key: %s" % (key), 1) self.debug_print("headlineKeys: %s" % (repr(headlineKeys)), 1) words = self.getTropicalDescription( tree, node, headlineKeys, maxMag, pws64, pws34) # Store the headlineKeys at the component node for later examination component = node.getComponent() component.set("headlineKeys", headlineKeys) elif headlines is None or headlines is NoData: words = words + self.getTropicalDescription( tree, node, "", maxMag, pws64, pws34) # COMMENT: If we have words from the pws_phrase during tropical cyclones # the following lines of code will make sure wind_summary is # not printed out. if words is not None and len(words.strip()) > 0: # Remove the wind sumamry phrase from this component and any local # effect areas - no need to replace undesirable phrases later on self.removeComponentPhrases(tree, node, "wind_summary", areaLabels=[node.getAreaLabel(), node.getComponent().getAreaLabel() ]) self.debug_print("\nSetting words '%s' for %s" % (words, node.getAncestor('name')), 1) self.debug_print("%s %s\n" % (node.getComponentName(), repr(node.getTimeRange())), 1) return self.setWords(node, words) def getTropicalDescription(self, tree, node, headlineKeys, maxMag, pws64, pws34): """ Determines which tropical descriptions to use for current period. """ self.debug_print("\tgetTropicalDescription") # Get some information about the component of this node compName = node.getComponentName() compIndex = node.getComponent().getIndex() # Convert convert component index to a forecast period number compPeriod = int(compIndex + self._firstComponentPeriod) self.debug_print("-"*80, 1) self.debug_print("Component name = %s" % (compName) + "\tForecast Period = %d" % (compPeriod) + "\tmaxMag = %s" % (maxMag), 1) descMethod = None words = "" # If this is one of the first 4 periods of the forecast if compPeriod <= 4: exec "descMethod = self.getPeriod_%d_Desc" % (compPeriod) # Otherwise, If this is one of the fifth to ninth forecast periods elif 5 <= compPeriod <= 9: descMethod = self.getPeriod_5_9_Desc # Otherwise else: descMethod = self.getPeriod_10_14_Desc # Ensure the tropical boolean variables are set using current # set of headlines self.tropicalBooleanConditions(headlineKeys) # Get the description from this method if descMethod is not None: desc = descMethod(tree, node, maxMag, pws64, pws34) # If we found the description - prepare it to be returned if desc != "": words = " " + self.phrase_descriptor(tree, node, desc, desc) return words def tropicalBooleanConditions(self, headlineKeys): """ Sets various boolean variables used by the pws_phrase logic based upon contents of current headlines. """ self.debug_print("\ttropicalBooleanConditions") # COMMENT: All boolean variables are defined globally within the tropical # formatter, so there is nothing to 'return' # These are all the tropical headline combinations accounted for. conditionLists = [ ["HI.W"], ["HI.A"], ["HU.W"], ['HU.A'], ["TI.W"], ["TI.A"], ["TR.W"], ["TR.A"], ["HU.A", "TR.W"], ["HI.A", "TI.W"], ["HU.W", "TI.W"], ["HI.W", "TI.W"], ["HI.W", "TR.W"], ["HI.A", "TR.A"], ["HU.A", "TI.A"], ["HI.A", "TI.A"], ["TI.W", "TR.A"], ["TI.W", "HU.A"], ["HI.W", "TR.A"], ["HI.W", "HU.A"], ["HI.W", "HU.W"], ["HI.A", "HU.A"], ["TI.A", "TR.A"], ["TI.W", "TR.W"], ["HU.W", "TR.W"], ["HU.A", "TR.A"], ["HU.W", "HI.W", "TR.W"], ["HU.W", "TI.W", "TR.W"], ["HU.A", "HI.A", "TR.A"], ["HU.A", "TI.A", "TR.A"], ["HI.A", "TI.W", "TR.W"], ["HI.A", "TI.A", "TI.W", "TR.W"], ["HI.A", "HI.W", "TI.W", "TR.W"], ["HU.W", "HI.W", "TI.W", "TR.W"], ["HU.A", "HI.A", "TI.A", "TR.A"], ["HI.A", "TI.W", "HU.A", "TR.W"], ["HI.A", "TI.A", "TI.W", "HU.A", "TR.W"], ["HI.A", "HI.W", "TI.W", "HU.A", "TR.W"], ["HI.A", "TI.W", "HU.A", "TR.A", "TR.W"], ["HI.A", "TI.W", "HU.A", "HU.W", "TR.W"], ["HI.A", "TI.A", "HI.W", "TI.W", "TR.W"], ["HI.A", "TI.A", "TI.W", "HU.W", "TR.W"], ["HI.A", "HI.W", "TI.W", "HU.W", "TR.W"], ["HI.A", "TI.A", "HI.W", "TI.W", "HU.W", "TR.W"], ["HI.A", "TI.A", "HI.W", "TI.W", "HU.A", "TR.W"], ["HI.A", "TI.W", "HU.A", "TR.A", "HU.W", "TR.W"], ["HI.A", "TI.A", "TI.W", "HU.A", "TR.W", "TR.A"], ["HI.A", "TI.A", "TI.W", "HU.A", "TR.W", "HU.W"], ["HI.A", "HI.W", "TI.W", "HU.A", "TR.W", "TR.A"], ["HI.A", "HI.W", "TI.W", "HU.A", "TR.W", "HU.W"], ["HI.A", "HI.W", "TI.W", "HU.A", "TR.W", "TR.A", "HU.W"], ["HI.A", "TI.A", "TI.W", "HU.A", "TR.W", "TR.A", "HU.W"], ["HI.A", "TI.A", "HI.W", "TI.W", "HU.A", "TR.W", "TR.A"], ["HI.A", "TI.A", "HI.W", "TI.W", "HU.A", "TR.W", "HU.W"], ["HI.A", "TI.A", "HI.W", "TI.W", "HU.A", "TR.W", "TR.A", "HU.W"], ] kLen = len(headlineKeys) for keyList in conditionLists: conditionName = "" klLen = len(keyList) if kLen == klLen: cond = True else: cond = False for keyStr in keyList: conditionName = conditionName + "_" + keyStr if kLen == klLen: if keyStr not in headlineKeys: cond = False conditionName = conditionName.replace(".", "_") exec "self." + conditionName + "= cond" # # IMPORTANT: DON'T HAVE DEBUG PRINT STATEMENTS FOR ALL COMBINATIONS ABOVE YET. # self.debug_print("HU_A_TR_W %s" % (self._HU_A_TR_W), 1) self.debug_print("HI_A_TI_W %s" % (self._HI_A_TI_W), 1) self.debug_print("HI_A_TI_W_HU_A_TR_W %s" % (self._HI_A_TI_W_HU_A_TR_W), 1) self.debug_print("HI_A_TI_W_TR_W %s" % (self._HI_A_TI_W_TR_W), 1) self.debug_print("HI_A_TI_W_HU_A_TR_W %s" % (self._HI_A_TI_W_HU_A_TR_W), 1) self.debug_print("HU_W_TI_W %s" % (self._HU_W_TI_W), 1) self.debug_print("HI_W_TI_W %s" % (self._HI_W_TI_W), 1) self.debug_print("HI_W_TR_W %s" % (self._HI_W_TR_W), 1) self.debug_print("HI_A_TR_A %s" % (self._HI_A_TR_A), 1) self.debug_print("HU_A_TI_A %s" % (self._HU_A_TI_A), 1) self.debug_print("HI_A_TI_A %s" % (self._HI_A_TI_A), 1) self.debug_print("TI_W_TR_A %s" % (self._TI_W_TR_A), 1) self.debug_print("TI_W_HU_A %s" % (self._TI_W_HU_A), 1) self.debug_print("HI_W_TR_A %s" % (self._HI_W_TR_A), 1) self.debug_print("HI_W_HU_A %s" % (self._HI_W_HU_A), 1) self.debug_print("HI_W %s" % (self._HI_W), 1) self.debug_print("HI_A %s" % (self._HI_A), 1) self.debug_print("HU_W %s" % (self._HU_W), 1) self.debug_print("HU_A %s" % (self._HU_A), 1) self.debug_print("HI_W_HU_W %s" % (self._HI_W_HU_W), 1) self.debug_print("HI_A_HU_A %s" % (self._HI_A_HU_A), 1) self.debug_print("TI_W %s" % (self._TI_W), 1) self.debug_print("TI_A %s" % (self._TI_A), 1) self.debug_print("TR_W %s" % (self._TR_W), 1) self.debug_print("TR_A %s" % (self._TR_A), 1) self.debug_print("TI_A_TR_A %s" % (self._TI_A_TR_A), 1) self.debug_print("TI_W_TR_W %s" % (self._TI_W_TR_W), 1) # # COMMENT: getPeriod_#_ definitions below contain the guts of the tropical # cyclone formatter logic used to determine pws phrases or expressions of # uncertainty. # def getPeriod_1_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a first period forecast. """ self.debug_print("\tgetPeriod_1_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period - special case 2 for each component = node.getComponent() #windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) windSpdProb_thresholds = self._windSpdProbThresholds (thresh34low, thresh34high) = windSpdProb_thresholds[0][0] (thresh64low, thresh64high) = windSpdProb_thresholds[0][1] # Display thresholds so we know what we're using self.debug_print("34 kt thresholds = (%.2f, %.2f)" % (thresh34low, thresh34high), 1) self.debug_print("64 kt thresholds = (%.2f, %.2f)" % (thresh64low, thresh64high), 1) if self._HU_A_TR_W or self._HI_A_TI_W or self._HI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_TR_W or self._TI_W_HU_A or \ self._HI_A_TI_A_TI_W_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_TR_A_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A: if maxMag >= 34.0: if pws34 >= thresh34high: desc = "iminTSposHR" else: desc = "expTSposHR" elif pws34 >= thresh34low and maxMag >= 25.0: desc = "expTSposHR" elif pws64 >= thresh64low: desc = "posTSbcmgposHR" elif pws34 >= thresh34low or pws34+10.0 >= thresh34low or maxMag >= 25.0: desc = "posTS" else: desc = "" # or regular phrasing self.debug_print("ifelse1!!! %s" % (maxMag)) elif self._HI_W or self._HI_A or self._HU_W or self._HU_A or \ self._HI_W_HU_W or self._HI_A_HU_A or self._HU_W_TI_W or \ self._HI_W_TI_W or self._HI_W_TR_A or self._HI_W_HU_A or \ self._HU_A_TI_A or self._HI_A_TI_A or self._HI_W_TR_W or \ self._HI_A_TR_A or self._HU_W_TR_W or self._HU_W_HI_W_TR_W or \ self._HU_W_TI_W_TR_W or self._HU_W_HI_W_TI_W_TR_W or self._HU_A_TR_A or \ self._HU_A_HI_A_TR_A or self._HU_A_TI_A_TR_A or self._HU_A_HI_A_TI_A_TR_A or \ self._HI_A_HI_W_TI_W_TR_W or self._HI_A_TI_A_HI_W_TI_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_W_TR_W or self._HI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_TI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_HU_W_TR_W or self._HI_A_TI_W_HU_A_TR_A_HU_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A or self._HI_A_HI_W_TI_W_HU_A_TR_W_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A or \ self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W: if maxMag >= 64.0: if pws64 >= thresh64high: desc = "iminHR" else: desc = "expHR" elif pws64 >= thresh64low and maxMag >= 50.0: desc = "expHR" elif maxMag >= 34.0: if pws34 >= thresh34high: desc = "iminTSposHR" else: desc = "expTSposHR" elif pws34 >= thresh34low and maxMag >= 25.0: desc = "expTSposHR" elif pws64 >= thresh64low: desc = "posHR" elif pws34 >= thresh34low or pws34+10.0 >= thresh34low or maxMag >= 25.0: desc = "posTSbcmgposHR" else: desc = "" # or regular phrasing self.debug_print("ifelse2!!! %s" % (maxMag)) elif self._TI_W or self._TI_A or self._TR_W or self._TR_A or \ self._TI_A_TR_A or self._TI_W_TR_W or self._TI_W_TR_A: if maxMag >= 34.0: if pws34 >= thresh34high: desc = "iminTS" else: desc = "expTS" elif pws34 >= thresh34low and maxMag >= 25.0: desc = "expTS" elif pws64 >= thresh64low: desc = "posTSbcmgposHR" elif pws34 >= thresh34low or pws34+10.0 >= thresh34low or maxMag >= 25.0: desc = "posTS" else: desc = "" # or regular phrasing self.debug_print("ifelse3!!! %s" % (maxMag)) else: print "check.......... ", "check" if maxMag >= 64.0: desc = "posHR" elif maxMag >= 34.0: desc = "posTS" elif pws64 >= thresh64low or pws64 +2.0 >= thresh64low: desc = "posHR" elif pws34 >= thresh34low or pws34+10.0 >= thresh34low: desc = "posTS" else: desc = "" return desc def getPeriod_2_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a second period forecast. """ self.debug_print("\tgetPeriod_2_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period component = node.getComponent() #windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) windSpdProb_thresholds = self._windSpdProbThresholds (thresh34, thresh64) = windSpdProb_thresholds[1] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) if self._HU_A_TR_W or self._HI_A_TI_W or self._HI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_TR_W or self._TI_W_HU_A or \ self._HI_A_TI_A_TI_W_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_TR_A_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+10.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" # or regular phrasing self.debug_print("ifelse1!!! %s" % (maxMag)) elif self._HI_W or self._HI_A or self._HU_W or self._HU_A or \ self._HI_W_HU_W or self._HI_A_HU_A or self._HU_W_TI_W or \ self._HI_W_TI_W or self._HI_W_TR_A or self._HI_W_HU_A or \ self._HU_A_TI_A or self._HI_A_TI_A or self._HI_W_TR_W or \ self._HI_A_TR_A or self._HU_W_TR_W or self._HU_W_HI_W_TR_W or \ self._HU_W_TI_W_TR_W or self._HU_W_HI_W_TI_W_TR_W or self._HU_A_TR_A or \ self._HU_A_HI_A_TR_A or self._HU_A_TI_A_TR_A or self._HU_A_HI_A_TI_A_TR_A or \ self._HI_A_HI_W_TI_W_TR_W or self._HI_A_TI_A_HI_W_TI_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_W_TR_W or self._HI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_TI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_HU_W_TR_W or self._HI_A_TI_W_HU_A_TR_A_HU_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A or self._HI_A_HI_W_TI_W_HU_A_TR_W_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A or \ self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W: if maxMag >= 64.0 or (pws64 >= thresh64 and maxMag >= 50.0): desc = "expHR" elif maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posHR" elif pws34 >= thresh34 or pws34+10.0 >= thresh34 or maxMag >= 25.0: desc = "posTSbcmgposHR" else: desc = "" # or regular phrasing self.debug_print("ifelse2!!! %s" % (maxMag)) elif self._TI_W or self._TI_A or self._TR_W or self._TR_A or \ self._TI_A_TR_A or self._TI_W_TR_W or self._TI_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTS" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+10.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" # or regular phrasing self.debug_print("ifelse3!!! %s" % (maxMag)) else: # print "check.......... ", "check" if maxMag >= 64.0: desc = "posHR" elif maxMag >= 34.0: desc = "posTS" elif pws64 >= thresh64 or pws64 +2.0 >= thresh64: desc = "posHR" elif pws34 >= thresh34 or pws34+10.0 >= thresh34: desc = "posTS" else: desc = "" return desc def getPeriod_3_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a third period forecast. """ self.debug_print("\tgetPeriod_3_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period component = node.getComponent() #windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) windSpdProb_thresholds = self._windSpdProbThresholds (thresh34, thresh64) = windSpdProb_thresholds[2] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) if self._HU_A_TR_W or self._HI_A_TI_W or self._HI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_TR_W or self._TI_W_HU_A or \ self._HI_A_TI_A_TI_W_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_TR_A_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse1!!! %s" % (maxMag)) elif self._HI_W or self._HU_W or \ self._HI_W_HU_W or self._HU_W_TI_W or self._HI_W_TI_W or \ self._HI_W_TR_A or self._HI_W_HU_A or self._HI_W_TR_W or \ self._HU_W_TR_W or self._HU_W_HI_W_TR_W or self._HU_W_TI_W_TR_W or \ self._HU_W_HI_W_TI_W_TR_W or self._HI_A_HI_W_TI_W_TR_W or self._HI_A_TI_A_HI_W_TI_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_W_TR_W or self._HI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_TI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_HU_W_TR_W or self._HI_A_TI_W_HU_A_TR_A_HU_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A or self._HI_A_HI_W_TI_W_HU_A_TR_W_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A or \ self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W: if maxMag >= 64.0 or (pws64 >= thresh64 and maxMag >= 50.0): desc = "expHR" elif maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTSbcmgposHR" else: desc = "" self.debug_print("ifelse2!!! %s" % (maxMag)) elif self._TI_W or self._TR_W or self._TI_W_TR_W or self._TI_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTS" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse3!!! %s" % (maxMag)) elif self._HU_A or self._HI_A or self._HI_A_HU_A or self._HU_A_TI_A or \ self._HI_A_TI_A or self._HI_A_TR_A or \ self._HU_A_TR_A or self._HU_A_HI_A_TR_A or self._HU_A_TI_A_TR_A or \ self._HU_A_HI_A_TI_A_TR_A: if maxMag >= 50.0 or pws64 >= thresh64: desc = "posHR" elif maxMag >= 25.0 or pws34 >= thresh34 or pws34+5.0 >= thresh34: desc = "posTSbcmgposHR" else: desc = "" self.debug_print("ifelse4!!! %s" % (maxMag)) elif self._TR_A or self._TI_A or self._TI_A_TR_A: if maxMag >= 34.0: if pws64 >= thresh64: desc = "posTSbcmgposHR" else: desc = "posTS" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse5!!! %s" % (maxMag)) else: self.debug_print("HERE I AM") if pws64 >= thresh64 or pws64+1.0 >= thresh64: desc = "posHR" elif maxMag >= 64.0: desc = "posHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34: desc = "posTS" elif maxMag >= 34.0: desc = "posTS" else: desc = "" return desc def getPeriod_4_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a fourth period forecast. """ self.debug_print("\tgetPeriod_4_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period component = node.getComponent() # windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) windSpdProb_thresholds = self._windSpdProbThresholds (thresh34, thresh64) = windSpdProb_thresholds[3] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) if self._HU_A_TR_W or self._HI_A_TI_W or self._HI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_TR_W or self._TI_W_HU_A or \ self._HI_A_TI_A_TI_W_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_TR_A_TR_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse1!!! %s" % (maxMag)) elif self._HI_W or self._HU_W or \ self._HI_W_HU_W or self._HU_W_TI_W or self._HI_W_TI_W or \ self._HI_W_TR_A or self._HI_W_HU_A or self._HI_W_TR_W or \ self._HU_W_TR_W or self._HU_W_HI_W_TR_W or self._HU_W_TI_W_TR_W or \ self._HU_W_HI_W_TI_W_TR_W or self._HI_A_HI_W_TI_W_TR_W or self._HI_A_TI_A_HI_W_TI_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_W_TR_W or self._HI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_TI_A_HI_W_TI_W_HU_W_TR_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W or \ self._HI_A_TI_W_HU_A_HU_W_TR_W or self._HI_A_TI_W_HU_A_TR_A_HU_W_TR_W or \ self._HI_A_TI_A_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_TI_W_HU_A_TR_W_TR_A_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A or self._HI_A_HI_W_TI_W_HU_A_TR_W_HU_W or \ self._HI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A or \ self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_HU_W or self._HI_A_TI_A_HI_W_TI_W_HU_A_TR_W_TR_A_HU_W: if maxMag >= 64.0 or (pws64 >= thresh64 and maxMag >= 50.0): desc = "expHR" elif maxMag >= 34 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTSposHR" elif pws64 >= thresh64: desc = "posHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTSbcmgposHR" else: desc = "" self.debug_print("ifelse2!!! %s" % (maxMag)) elif self._TI_W or self._TR_W or self._TI_W_TR_W or self._TI_W_TR_A: if maxMag >= 34.0 or (pws34 >= thresh34 and maxMag >= 25.0): desc = "expTS" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse3!!! %s" % (maxMag)) elif self._HU_A or self._HI_A or self._HI_A_HU_A or self._HU_A_TI_A or \ self._HI_A_TI_A or self._HI_A_TR_A or \ self._HU_A_TR_A or self._HU_A_HI_A_TR_A or self._HU_A_TI_A_TR_A or \ self._HU_A_HI_A_TI_A_TR_A: if maxMag >= 50.0 or pws64 >= thresh64: desc = "posHR" elif maxMag >= 25.0 or pws34 >= thresh34 or pws34+5.0 >= thresh34: desc = "posTSbcmgposHR" else: desc = "" self.debug_print("ifelse4!!! %s" % (maxMag)) elif self._TR_A or self._TI_A or self._TI_A_TR_A: if maxMag >= 34.0: if pws64 >= thresh64: desc = "posTSbcmgposHR" else: desc = "posTS" elif pws64 >= thresh64: desc = "posTSbcmgposHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34 or maxMag >= 25.0: desc = "posTS" else: desc = "" self.debug_print("ifelse5!!! %s" % (maxMag)) else: self.debug_print("HERE I AM") if pws64 >= thresh64 or pws64+1 >= thresh64: desc = "posHR" elif maxMag >= 64.0: desc = "posHR" elif pws34 >= thresh34 or pws34+5.0 >= thresh34: desc = "posTS" elif maxMag >= 34.0: desc = "posTS" else: desc = "" return desc def getPeriod_5_9_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a fifth to ninth period forecast. """ self.debug_print("\tgetPeriod_5_9_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period component = node.getComponent() # windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) windSpdProb_thresholds = self._windSpdProbThresholds (thresh34, thresh64) = \ windSpdProb_thresholds[node.getComponent().getIndex()] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) if (pws64 >= thresh64 or (pws64 + 1.0) >= thresh64) and maxMag >= 20.0: desc = "posHR" elif maxMag >= 64.0: desc = "posHR" elif (pws34 >= thresh34 or (pws34 + 2.5) >= thresh34) and maxMag >= 20.0: desc = "posTS" elif maxMag >= 34.0: desc = "posTS" else: desc = "" return desc def getPeriod_10_14_Desc(self, tree, node, maxMag, pws64, pws34): """ Determines contents of PWS phrase for a fourth period forecast. """ self.debug_print("\tgetPeriod_4_Desc") desc = "" self.debug_print("Period time range = %s" % (repr(node.getComponent().getTimeRange())), 1) self.debug_print("PWS34_wrng = %s" % (pws34), 1) self.debug_print("PWS64_wrng = %s" % (pws64), 1) # Grab thresholds for this period component = node.getComponent() windSpdProb_thresholds = self._windSpdProbThresholds # windSpdProb_thresholds = self.windSpdProb_thresholds(tree, component) (thresh34, thresh64) = windSpdProb_thresholds[9] # Display thresholds so we know what we're using self.debug_print("(34 kt threshold, 64 kt threshold) = (%.2f, %.2f)" % (thresh34, thresh64), 1) if (pws64 >= thresh64 or (pws64 + 1.0) >= thresh64) and maxMag >= 20.0: desc = "posHR" elif maxMag >= 64.0: desc = "posHR" elif (pws34 >= thresh34 or (pws34 + 2.5) >= thresh34) and maxMag >= 20.0: desc = "posTS" elif maxMag >= 34.0: desc = "posTS" else: desc = "" return desc # # Still needed for OB8.2.1 to account for removal of these hazards from CWF VTEC # def allowedHeadlines(self): # used for probabilistic wording allActions = ["NEW", "EXA", "EXB", "EXT", "UPG", "CAN", "CON", "EXP"] return [ ('HU.W', allActions, 'Tropical'), # HURRICANE WARNING ('TR.W', allActions, 'Tropical1'), # TROPICAL STORM WARNING ('HU.A', allActions, 'Tropical'), # HURRICANE WATCH ('TR.A', allActions, 'Tropical1'), # TROPICAL STORM WATCH ]