awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/procedures/TCWindThreat.py
2022-05-05 12:34:50 -05:00

325 lines
15 KiB
Python

# ----------------------------------------------------------------------------
# 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.
#
# A2TCWindThreatFinal_3
# This version is for use with MLB's Graphical HLS.
# This version modifies the gHLS WindThreat element.
#
# Author: lefebvre 9/11/06
# Modified by: Volkmer/MLB and Santos/MFL 5/23/07
# More modifications: by LeFebvre/Santos/Sharp 05/7/09
# LeFebvre/Santos 07/20/10
# Modified: by Santos 04/26/2011 to accommodate alternate logic for NE Coast and hide bars
# Modified: by Santos 04/20/2012 to get rid off Very Low and tweak GUI.
# Modified: by Shannon/Pablo 06/19/2012 to make A2 and DRT compatible
# Modified: by Pablo/Shannon on 05/21/2014 to fix bug introduced in 2012 when Very Low was eliminated
# Modified: By Santos/Lefebvre 09/17/2014 to make changes for official implementation in 2015.
# Modified: By Belk 07/15/2016 to make efficiency improvements, and
# refactor to make use of a utility containing common methods with other tools
# CHECKED IN FOR 17.1.1: By LeFebvre 09/23/2016 finish conversion to numpy conventions.
# Modified: By LeFebvre 09/26/16 - Removed commented out code to pass code review.
# Modified: By LeFebvre 10/31/16 - Added more code to ensure only one cyclone center point is calculated
# Modified: By LeFebvre 07/18/17 - Added option to populate based on Preliminary or Official prob guidance.
# Modified: By P. Santos 9/25/18 - Cleaned GUI options based on SPT approved wording.
# Modified: By P. Santos 1/31/2019 - Made adjustments requested by Raytheon.
# Modified: By P. Santos 10/15/2020 - Made adjustments found with wind swath by BRO During Laura. Gotr rid of AdjustWindMax method.
# Looks at CUM WPS out to 72 hour instead of 60.
# Modified: By P. Santos 04/22/2021 - Fixed issue found during 20.2.3 beta testing.
#----------------------------------------------------------------------------
##
# This is an absolute override file, indicating that a higher priority version
# of the file will completely replace a lower priority version of the file.
##
#
# THIS CODE SHALL NOT BE CHANGED WITHOUT CONSULTING ORIGINAL DEVELOPERS.
#
# The MenuItems list defines the GFE menu item(s) under which the
# Procedure is to appear.
# Possible items are: Populate, Edit, Consistency, Verify, Hazards
MenuItems = ["Populate","Edit"]
# The ToolList is optional, but recommended, if you are calling
# Smart Tools from your Script.
# If present, it can be used to show which grids will be
# modified by the Script.
VariableList = [("Probabilistic Wind Source?", "Preliminary", "radio", ["Official", "Preliminary"]),
("Confidence Level in the Deterministic Wind Forecast Used for the Wind Threat Composition:", "Typical Confidence (10% exceedance; reasonable worse case), Widest Safety Margin",
"radio", ["Typical Confidence (10% exceedance; reasonable worse case), Widest Safety Margin",
"Medium Confidence (20% exceedance; some confidence in the deterministic forecast), Wide Safety Margin - MUST COORDINATE USE",
"High Confidence (30% exceedance; more confidence in the deterministic forecast), Narrow Safety Margin - MUST COORDINATE USE",
"Highest Confidence (deterministic wind only; smoothing will be needed), No Safety Margin - MUST COORDINATE USE"]),
]
import TropicalUtility
import TimeRange
import AbsTime
import MetLib
import math
import numpy as np
class Procedure (TropicalUtility.TropicalUtility):
def __init__(self, dbss):
TropicalUtility.TropicalUtility.__init__(self, dbss)
# Finds the TPC prob database, extracts all of the grids and returns
# the last one of each type
def getProbGrids(self):
ap = self.availableParms()
searchStr = self.getSiteID() + "_GRID_D2D_" + self._probWindModelSource + "_"
probModel = ""
for elem, level, model in ap:
if searchStr in model.modelIdentifier():
probModel = model.modelIdentifier()
break
if probModel == "":
self.statusBarMsg("TPC Wind probability grids not found. Proceeding if using Deterministic Option Only.", "S")
return None, None, None, None
# make a big timeRange
timeRange = TimeRange.allTimes()
trList = self.GM_getWEInventory("prob34", probModel, "FHAG10")
timeRangeStartTime = AbsTime.AbsTime(trList[0].startTime().unixTime())
timeRangeEndTime = AbsTime.AbsTime(trList[12].startTime().unixTime())
tr = TimeRange.TimeRange(timeRangeStartTime, timeRangeEndTime)
# get the TPC prob grids, and keeps the 72th hour forecast one in the series. But first the inventory is complete.
prob34List = self.getGrids(probModel, "prob34", "FHAG10", timeRange, mode = "List")
prob50List = self.getGrids(probModel, "prob50", "FHAG10", timeRange, mode = "List")
prob64List = self.getGrids(probModel, "prob64", "FHAG10", timeRange, mode = "List")
if len(prob34List) == 21 and len(prob50List) == 21 and len(prob64List) == 21:
prob34Grid = prob34List[12]
prob50Grid = prob50List[12]
prob64Grid = prob64List[12]
else:
self.statusBarMsg("Probabilistic Elements Missing in " + probModel + " Database. Proceeding only if using Deterministic Option. Otherwise, Check and Try Again.", "S")
return None, None, None, None
return prob34Grid, prob50Grid, prob64Grid, tr
# Make a timeRange spanning from the current hour to 6 hours hence
def make6hrTimeRange(self):
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime)
endTime = AbsTime.AbsTime(cTime + (6 * 3600)) # 6 hours
tr = TimeRange.TimeRange(startTime, endTime)
return tr
# Make a timeRange spanning the usual -24 to 10-day db
def makeDBTimeRange(self):
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime - (24*3600))
endTime = AbsTime.AbsTime(cTime + (240 * 3600)) # 10 days
tr = TimeRange.TimeRange(startTime, endTime)
return tr
# Removes all grids previous grids
def removeGrids(self, elementList):
#elementList = ["Prob34", "Prob50", "Prob64", "WindMax", "WindThreat"]
ap = self.availableParms()
#tr = TimeRange.allTimes()
tr = self.makeDBTimeRange()
for elem, level, model in ap:
if elem in elementList and level == "SFC":
self.deleteCmd([elem], tr)
return
# fetch a grid that represents the maxWind everywhere for the next 5 days
def getWindMax(self, timeRange):
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime)
endTime = startTime + (3 * 24 * 3600) # 3 days from the startTime
tr = TimeRange.TimeRange(startTime, endTime)
if timeRange is not None:
if timeRange.overlaps(tr):
endTime = AbsTime.AbsTime(timeRange.endTime().unixTime())
tr = TimeRange.TimeRange(startTime, endTime)
else:
self.statusBarMsg("Winds grids and WSP model do not overlap. Check your database. Stopping", "S")
return None,None,None
try:
windMax, dir = self.getGrids(self.mutableID(), "Wind", "SFC", tr, mode = "Max")
except:
#windMax = None
self.statusBarMsg("No Wind grids found. Please define Wind grids first.", "S")
return None,None,None
maxWindValue = np.amax(windMax) # Max value over the whole grid
return windMax, maxWindValue, tr
def execute(self, varDict):
# Fetch the model source and define the model name
sourceDB = varDict["Probabilistic Wind Source?"]
if sourceDB == "Official":
self._probWindModelSource = "TPCProb"
elif sourceDB == "Preliminary":
self._probWindModelSource = "TPCProbPrelim"
else:
self.statusBarMsg("Unknown model source selected. Aborting.", "U")
return
# Get confidence value from the dialog
confidenceStr = varDict["Confidence Level in the Deterministic Wind Forecast Used for the Wind Threat Composition:"]
allWind = confidenceStr == "Highest Confidence (deterministic wind only; smoothing will be needed), No Safety Margin - MUST COORDINATE USE"
high = confidenceStr == "Medium Confidence (20% exceedance; some confidence in the deterministic forecast), Wide Safety Margin - MUST COORDINATE USE"
higher = confidenceStr == "High Confidence (30% exceedance; more confidence in the deterministic forecast), Narrow Safety Margin - MUST COORDINATE USE"
#print("allProb and allWind are: ", allProb, allWind)
# extract the percent value from this string
# pctPos = confidenceStr.find("% Default")
# pctStr = confidenceStr[pctPos - 2:pctPos]
pctStr="10"
if high:
pctStr="20"
elif higher:
pctStr="30"
# Percent thresholds for each confidence category
threatDict = {"10" : [10.0, 10.0, 10.0, 20.0, 30.0],
"20" : [20.0, 20.0, 20.0, 30.0, 40.0],
"30" : [30.0, 30.0, 30.0, 40.0, 50.0],
}
# wind thresholds for each threat category
windDict = {'None' : 0.0,
# 'Very Low' : 34.0,
#'Elevated' : 50.0,
'Elevated': 34.0,
'Mod' : 50.0,
'High1' : 64.0,
'High2' : 83.0,
'Extreme' : 96.0,
}
# Make sure the string is valid. If not then assign any value since the user
# has indicted Highest confidence in the wind field and is not using the
# probabilistic grids at all.
#if not threatDict.has_key(pctStr):
# pctStr = "10"
#print("pctStr is: ", pctStr)
# Extract the proper list and assign thresholds
thresholdList = threatDict[pctStr]
#
t34TS1 = thresholdList[0]
t50TS2 = thresholdList[1]
t64Cat1 = thresholdList[2]
t64Cat2 = thresholdList[3]
t64Cat3 = thresholdList[4]
timeRange6 = self.make6hrTimeRange()
# set up the indices for the discrete keys
keys = self.getDiscreteKeys("WindThreat")
lowIndex = self.getIndex("Elevated", keys)
modIndex = self.getIndex("Mod", keys)
highIndex = self.getIndex("High", keys)
extremeIndex = self.getIndex("Extreme", keys)
# Initialize the threat grid
threatGrid = self.empty(np.int8) # a grid of zeros
prob34Grid, prob50Grid, prob64Grid, timeRange = self.getProbGrids()
#print("Time Range for WSP is: ", timeRange)
self.removeGrids(["Prob34","Prob50","Prob64"])
# Use the old-fashioned method
# Get and adjust a grid of maximum wind over the entire storm
windMax, maxWindValue, tr = self.getWindMax(timeRange)
#print("Time Range from WindMax is: ", tr)
if windMax is None and maxWindValue is None:
return
timeRange = tr
self.removeGrids(["WindMax"])
self.createGrid(self.mutableID(), "WindMax", "SCALAR", windMax, timeRange,
minAllowedValue=0, maxAllowedValue=200, defaultColorTable="GFE/TCMWinds")
# Assign values to the grid based on the probability grids
if allWind:
threatGrid[windMax >= windDict["Elevated"]] = lowIndex
threatGrid[windMax >= windDict["Mod"]] = modIndex
threatGrid[windMax >= windDict["High1"]] = highIndex
threatGrid[windMax >= windDict["Extreme"]] = extremeIndex
else:
# high and extreme threats require maxWind to meet particular windMax criteria
# Fetch the probabilistic grids
#Prevent code from proceeding and erroring out if db or elements missing.
if prob34Grid is None or prob50Grid is None or prob64Grid is None:
return
# Show actual grids being used in algorithm
self.createGrid(self.mutableID(), "Prob34", "SCALAR", prob34Grid, timeRange, minAllowedValue=0, maxAllowedValue=100, defaultColorTable="GFE/TPCprob")
self.createGrid(self.mutableID(), "Prob50", "SCALAR", prob50Grid, timeRange, minAllowedValue=0, maxAllowedValue=100, defaultColorTable="GFE/TPCprob")
self.createGrid(self.mutableID(), "Prob64", "SCALAR", prob64Grid, timeRange, minAllowedValue=0, maxAllowedValue=100, defaultColorTable="GFE/TPCprob")
threatGrid[prob34Grid >= t34TS1] = lowIndex
threatGrid[prob50Grid >= t50TS2] = modIndex
threatGrid[prob64Grid >= t64Cat1] = highIndex
if maxWindValue >= windDict['High2']:
threatGrid[prob64Grid >= t64Cat3] = extremeIndex
if maxWindValue >= windDict['Extreme']:
threatGrid[prob64Grid >= t64Cat2] = extremeIndex
#===================================================================
# Upgrade windThreat based on windMax grid
##
# Upgrade None to Elevated
windMask = ((windMax >= windDict['Elevated']) &
(windMax < windDict['Mod']))
threatMask = threatGrid < lowIndex
threatGrid[windMask & threatMask] = lowIndex
# Upgrade Elevated to Med
windMask = ((windMax >= windDict['Mod']) &
(windMax < windDict['High1']))
threatMask = threatGrid < modIndex
threatGrid[windMask & threatMask] = modIndex
# Upgrade Med to High
windMask = ((windMax >= windDict['High1']) &
(windMax < windDict['Extreme']))
threatMask = threatGrid < highIndex
threatGrid[windMask & threatMask] =highIndex
# Upgrade High to Extreme
windMask = windMax >= windDict['Extreme']
threatMask = threatGrid < extremeIndex
threatGrid[windMask & threatMask] = extremeIndex
# Remove previous version of grids.
self.removeGrids(["WindThreat"])
# create the threat grid
self.createGrid(self.mutableID(), "WindThreat", "DISCRETE", (threatGrid, keys),
timeRange6, discreteKeys=keys, discreteAuxDataLength=5,
discreteOverlap=0)