396 lines
16 KiB
Python
396 lines
16 KiB
Python
##
|
|
# This software was developed and / or modified by Raytheon Company,
|
|
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
|
#
|
|
# U.S. EXPORT CONTROLLED TECHNICAL DATA
|
|
# This software product contains export-restricted data whose
|
|
# export/transfer/disclosure is restricted by U.S. law. Dissemination
|
|
# to non-U.S. persons whether in the United States or abroad requires
|
|
# an export license or other authorization.
|
|
#
|
|
# Contractor Name: Raytheon Company
|
|
# Contractor Address: 6825 Pine Street, Suite 340
|
|
# Mail Stop B8
|
|
# Omaha, NE 68106
|
|
# 402.291.0100
|
|
#
|
|
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
|
# further licensing information.
|
|
##
|
|
# ----------------------------------------------------------------------------
|
|
# 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.
|
|
#
|
|
# CheckTandTd
|
|
#
|
|
# Author: Tom LeFebvre
|
|
#
|
|
# Version Date: 4 January 2006
|
|
# Version: 6.5
|
|
#
|
|
# 7/27/2015 yteng Use the time range selected in the Grid Manager if any
|
|
#
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# The MenuItems list defines the GFE menu item(s) under which the
|
|
# Procedure is to appear.
|
|
# Possible items are: Populate, Edit, Consistency, Verify
|
|
MenuItems = ["Consistency"]
|
|
|
|
VariableList = [("Check or Force:" , "Check Only", "radio",
|
|
["Check Only", "Force: TMin<=T<=TMax\n and Td<=T"]),
|
|
]
|
|
|
|
import SmartScript
|
|
import TimeRange
|
|
import AbsTime
|
|
from JUtil import JavaWrapperClass
|
|
from numpy import *
|
|
|
|
MODEL = "Fcst"
|
|
LEVEL = "SFC"
|
|
DAY_IN_SECS = 24 * 3600
|
|
|
|
class Procedure (SmartScript.SmartScript):
|
|
def __init__(self, dbss):
|
|
SmartScript.SmartScript.__init__(self, dbss)
|
|
|
|
##
|
|
# Get the list of time ranges at the grid whose element name is WEName
|
|
# contains grids. The model and level of the weather element are assumed
|
|
# to be MODEL and LEVEL, respectively.
|
|
#
|
|
# @param WEName: Name of a weather element
|
|
# @type WEName: string
|
|
# @return: time ranges at which WEName has data.
|
|
# @rtype: Python list of Python TimeRange objects
|
|
def getWEInventory(self, WEName, timeRange=None):
|
|
if timeRange is None:
|
|
yesterday = self._gmtime() - (2 * DAY_IN_SECS) # two days ago
|
|
later = self._gmtime() + 10 * DAY_IN_SECS # 10 days from now
|
|
timeRange = TimeRange.TimeRange(yesterday, later)
|
|
if isinstance(timeRange, JavaWrapperClass):
|
|
timeRange = timeRange.toJavaObj()
|
|
parm = self.getParm(MODEL, WEName, LEVEL);
|
|
inv = parm.getGridInventory(timeRange)
|
|
trList = []
|
|
for gd in inv:
|
|
tr = TimeRange.TimeRange(gd.getGridTime())
|
|
trList.append(tr)
|
|
|
|
return trList
|
|
|
|
##
|
|
# Get time ranges locked by other workstations for the weather element named
|
|
# weName. The model for weName is taken from this object's mutableID() method;
|
|
# the level is LEVEL.
|
|
# @param weName: Name of a weather element.
|
|
# @type weName: string
|
|
# @return: time ranges locked by others
|
|
# @rtype: Python list of TimeRanges; if asJava is True, these are Java
|
|
# TimeRanges, otherwise they are Python TimeRanges.
|
|
def getLocksByOthers(self, weName):
|
|
# returns list of time ranges locked by others for this weather element
|
|
parm = self.getParm(self.mutableID(), weName, LEVEL)
|
|
if parm is None:
|
|
return []
|
|
lt = parm.getLockTable()
|
|
jlok = lt.lockedByOther();
|
|
lbo = []
|
|
for i in xrange(jlok.size()):
|
|
tr = jlok.get(i)
|
|
tr = TimeRange.TimeRange(tr)
|
|
lbo.append( tr )
|
|
return lbo
|
|
|
|
##
|
|
# Filter trList, returning only the time ranges that overlap timeRange.
|
|
# @param timeRange: the time range to test against
|
|
# @type timeRange: a Python TimeRange
|
|
# @param trList: the list of time ranges to filter
|
|
# @type trList: Python list of Python TimeRanges
|
|
# @return: The time ranges in trList that overlap timeRange.
|
|
# @rtype: a Python list of Python time ranges
|
|
def overlappingTRs(self, timeRange, trList):
|
|
newTRList = []
|
|
for tr in trList:
|
|
if timeRange.overlaps(tr):
|
|
newTRList.append(tr)
|
|
|
|
return newTRList
|
|
|
|
##
|
|
# method so that timeRanges will be sorted earliest to latest
|
|
# @param first: The first time range to compare
|
|
# @type first: Python TimeRange
|
|
# @param last: The second time range to compare
|
|
# @type last: Python TimeRange
|
|
# @return: -1 if first starts before last, 1 if first starts after last,
|
|
# and 0 if first and last start at the same time.
|
|
# @rtype: integer
|
|
def trSortMethod(self, first, last):
|
|
if first.startTime() < last.startTime():
|
|
return -1
|
|
elif first.startTime() == last.startTime():
|
|
return 0
|
|
else:
|
|
return 1
|
|
|
|
##
|
|
# Concatenate minTRList and maxTRList and sort by starting times.
|
|
# Duplicate time ranges are NOT eliminated.
|
|
# @param minTRList: time ranges of the minT grid
|
|
# @type minTRList: Python list of Python TimeRange objects.
|
|
# @param maxTRList: time ranges of the maxT grid
|
|
# @type maxTRList: Python list of Python TimeRange objects.
|
|
# @return: The combined and sorted collection.
|
|
# @rtype: Python list of Python TimeRange objects
|
|
def combineInventoryLists(self, minTRList, maxTRList):
|
|
bigList = minTRList + maxTRList
|
|
bigList.sort(self.trSortMethod)
|
|
return bigList
|
|
|
|
##
|
|
# Main entry point of this procedure. If varDict["Check or Force"] is
|
|
# "Check Only", temporary grids will be created. Otherwise, the minT, maxT,
|
|
# T, and Td grids may be changed.
|
|
# @param varDict: Determines whether temporary grids are created or
|
|
# temperature grids are modified.
|
|
# @type varDict: Python dictionary of strings to strings
|
|
def execute(self, timeRange, varDict):
|
|
checkOnly = varDict["Check or Force:"] == "Check Only"
|
|
|
|
# remove any temporary WEs we created
|
|
weList = ["TLessThanMin", "TGreaterThanMax", "TdGreaterThanT",
|
|
"MinGreaterThanMax", "MaxLessThanMin"]
|
|
for we in weList:
|
|
parm = self.getParm(MODEL, we, LEVEL)
|
|
if parm is not None:
|
|
self.unloadWE(MODEL, we, LEVEL)
|
|
|
|
self.setToolType("numeric")
|
|
|
|
if timeRange is None or not timeRange.isValid():
|
|
start = self._gmtime() - (2 * DAY_IN_SECS) # two days ago
|
|
end = self._gmtime() + (10 * DAY_IN_SECS) # 10 days from now
|
|
timeRange = TimeRange.TimeRange(start, end)
|
|
|
|
# get all the grids for all elements upfront and update as we modify
|
|
# any grids. We need to do this because the GFE caches the original
|
|
# version of all grids and there's no way yet to turn this off.
|
|
|
|
minTRList = self.getWEInventory("MinT", timeRange)
|
|
minTDict = self.getGrids(MODEL, "MinT", LEVEL, minTRList, mode = "First")
|
|
|
|
maxTRList = self.getWEInventory("MaxT", timeRange)
|
|
maxTDict = self.getGrids(MODEL, "MaxT", LEVEL, maxTRList, mode = "First")
|
|
|
|
TTRList = self.getWEInventory("T", timeRange)
|
|
tDict = self.getGrids(MODEL, "T", LEVEL, TTRList, mode = "First")
|
|
|
|
TdTRList = self.getWEInventory("Td", timeRange)
|
|
tdDict = self.getGrids(MODEL, "Td", LEVEL, TdTRList, mode = "First")
|
|
|
|
# get the all locks by other users, so we can detect they are locked
|
|
# before attempting to modify them
|
|
minTLocks = self.getLocksByOthers("MinT")
|
|
maxTLocks = self.getLocksByOthers("MaxT")
|
|
tLocks = self.getLocksByOthers("T")
|
|
tdLocks = self.getLocksByOthers("Td")
|
|
|
|
# get the list of edit areas
|
|
eaList = self.editAreaList()
|
|
|
|
# get the local WFO domain and make a mask with it
|
|
# local sites may wish to use a different maks so that a larger area
|
|
# is operated on by the tool - for example marine sites may wish to
|
|
# expand it to marine zones as well as land.
|
|
# To change the area, simply use a locally-defined edit area instead
|
|
# of self.getSiteID(). Example: siteID = "CWAPlusMarineZones"
|
|
#siteID = self.getSiteID() - this was set in A2 - changed to A1 below
|
|
siteID = "ISC_Send_Area"
|
|
if siteID in eaList: # make sure the edit area is there
|
|
siteEA = self.getEditArea(siteID) # get the edit area
|
|
siteMask = self.encodeEditArea(siteEA) # make a mask with siteEA
|
|
siteMask = siteMask.astype(bool8)
|
|
else:
|
|
topo = self.getGridShape()
|
|
siteMask = ones(topo, bool8)
|
|
print siteID, "edit area not found. Using entire GFE domain."
|
|
|
|
# Ensure that MinT <= MaxT first
|
|
minMaxList = self.combineInventoryLists(minTRList, maxTRList)
|
|
foundProblem = False
|
|
for i in xrange(0, len(minMaxList) - 1):
|
|
if minMaxList[i+1] in minTRList: # previous max modifies min
|
|
maxTR = minMaxList[i]
|
|
minTR = minMaxList[i+1]
|
|
# Make sure these TRs really exist in the inventory
|
|
if maxTR not in maxTRList:
|
|
continue
|
|
if minTR not in minTRList:
|
|
continue
|
|
|
|
minGrid = minTDict[minTR]
|
|
maxGrid = maxTDict[maxTR]
|
|
|
|
mask = (minGrid > maxGrid) & siteMask
|
|
if not sometrue(mask): # make sure some points are set
|
|
continue
|
|
|
|
foundProblem = True
|
|
|
|
if checkOnly:
|
|
self.createGrid(MODEL, "MinGreaterThanMax", "SCALAR", mask.astype(float32),
|
|
minTR, minAllowedValue=0.0, maxAllowedValue= 1.0)
|
|
else: # force the change
|
|
if minTR in minTLocks:
|
|
msg = "Can't modify MinT grid at " + str(minTR) + \
|
|
" locked by another user."
|
|
self.statusBarMsg(msg, "S")
|
|
continue
|
|
# calculate and modify the MinT grid
|
|
minGrid[mask] = maxGrid[mask]
|
|
self.createGrid(MODEL, "MinT", "SCALAR", minGrid, minTR)
|
|
minTDict[minTR] = minGrid # update the minT dictionary
|
|
|
|
elif minMaxList[i+1] in maxTRList: # previous min modifies max
|
|
minTR = minMaxList[i]
|
|
maxTR = minMaxList[i+1]
|
|
# Make sure these TRs really exist in the inventory
|
|
if maxTR not in maxTRList:
|
|
continue
|
|
if minTR not in minTRList:
|
|
continue
|
|
maxGrid = maxTDict[maxTR]
|
|
minGrid = minTDict[minTR]
|
|
|
|
mask = (maxGrid < minGrid) & siteMask
|
|
if not sometrue(mask): # make sure some points are set
|
|
continue
|
|
|
|
foundProblem = True
|
|
|
|
if checkOnly:
|
|
self.createGrid(MODEL, "MaxLessThanMin", "SCALAR", mask.astype(float32),
|
|
maxTR, minAllowedValue=0.0, maxAllowedValue= 1.0)
|
|
else: # force the change
|
|
if maxTR in maxTLocks:
|
|
msg = "Can't modify MaxT grid at " + str(maxTR) + \
|
|
" locked by another user."
|
|
self.statusBarMsg(msg, "S")
|
|
continue
|
|
# calculate and modify the MaxT grid
|
|
maxGrid[mask] = minGrid[mask]
|
|
self.createGrid(MODEL, "MaxT", "SCALAR", maxGrid, maxTR)
|
|
# update the minT dictionary with the modified minT grid
|
|
maxTDict[maxTR] = maxGrid
|
|
|
|
|
|
# Now check for T < MinT
|
|
for tr in minTRList:
|
|
minTGrid = minTDict[tr]
|
|
|
|
tInv = self.overlappingTRs(tr, TTRList)
|
|
if tInv == []: # empty list, keep going
|
|
continue
|
|
|
|
for tymeRng in tInv:
|
|
# find points in the siteMask where T < MinT
|
|
tGrid = tDict[tymeRng]
|
|
tTooLow = (tGrid < minTGrid) & siteMask
|
|
if not sometrue(tTooLow):
|
|
continue
|
|
|
|
foundProblem = True
|
|
|
|
if checkOnly: # just make a grid showing the mask where T < MinT
|
|
self.createGrid(MODEL, "TLessThanMin", "SCALAR", tTooLow.astype(float32), tymeRng,
|
|
minAllowedValue=0.0, maxAllowedValue= 1.0)
|
|
else: # force T to the MinT value
|
|
if tymeRng in tLocks:
|
|
msg = "Can't modify T grid at " + str(tymeRng) + \
|
|
" locked by another user."
|
|
self.statusBarMsg(msg, "S")
|
|
continue
|
|
tGrid[tTooLow] = minTGrid[tTooLow]
|
|
self.createGrid(MODEL, "T", "SCALAR", tGrid, tymeRng)
|
|
tDict[tymeRng] = tGrid # update the tDict
|
|
|
|
|
|
# check for T > MaxT
|
|
for tr in maxTRList:
|
|
# get the grid first
|
|
maxTGrid = maxTDict[tr]
|
|
|
|
# then warp the end time so we include T grids ending at 01z
|
|
startTime = tr.startTime()
|
|
endTime = tr.endTime().unixTime()
|
|
roundedTime = int((endTime + 43200) / 86400) * 86400 + 3600
|
|
endTime = max(endTime, roundedTime)
|
|
endTime = AbsTime.AbsTime(endTime)
|
|
timeRange = TimeRange.TimeRange(startTime, endTime)
|
|
|
|
# use the warpedTR to fetch the T inventory
|
|
tInv = self.overlappingTRs(timeRange, TTRList)
|
|
if tInv == []: # empty list, keep going
|
|
continue
|
|
|
|
for tymeRng in tInv:
|
|
# find points in the siteMask where T > MaxT
|
|
tGrid = tDict[tymeRng]
|
|
tTooHigh = (tGrid > maxTGrid) & siteMask
|
|
if not sometrue(tTooHigh): # make sure some points are set
|
|
continue
|
|
|
|
foundProblem = True
|
|
|
|
if checkOnly: # just make a grid
|
|
self.createGrid(MODEL, "TGreaterThanMax", "SCALAR", tTooHigh.astype(float32), tymeRng,
|
|
minAllowedValue=0.0, maxAllowedValue= 1.0)
|
|
else: # force T to the MaxT value
|
|
if tymeRng in tLocks:
|
|
msg = "Can't modify T grid at " + str(tymeRng) + \
|
|
" locked by another user."
|
|
self.statusBarMsg(msg, "S")
|
|
continue
|
|
tGrid[tTooHigh] = maxTGrid[tTooHigh]
|
|
self.createGrid(MODEL, "T", "SCALAR", tGrid, tymeRng)
|
|
tDict[tymeRng] = tGrid # update the tDict
|
|
|
|
# Now check T < Td
|
|
for tr in TTRList:
|
|
|
|
# make sure there's a matching Td grid
|
|
if not tr in TdTRList:
|
|
continue
|
|
|
|
tGrid = tDict[tr]
|
|
tdGrid = tdDict[tr]
|
|
|
|
# find points in the siteMask where Td > T
|
|
TdTooHigh = (tdGrid > tGrid) & siteMask
|
|
if not sometrue(TdTooHigh): # make sure some points are set
|
|
continue
|
|
|
|
foundProblem = True
|
|
|
|
if checkOnly: # just make a grid
|
|
self.createGrid(MODEL, "TdGreaterThanT", "SCALAR", TdTooHigh.astype(float32), tr,
|
|
minAllowedValue=0.0, maxAllowedValue= 1.0)
|
|
else: # force Td <= T
|
|
if tr in tdLocks:
|
|
msg = "Can't modify Td grid at " + str(tInv[i]) + \
|
|
" locked by another user."
|
|
self.statusBarMsg(msg, "S")
|
|
continue
|
|
tdGrid[TdTooHigh] = tGrid[TdTooHigh]
|
|
self.createGrid(MODEL, "Td", "SCALAR", tdGrid, tr)
|
|
tdDict[tr] = tdGrid # update the tdDict
|
|
|
|
|
|
if not foundProblem:
|
|
msg = "CheckTandTd found no inconsistencies."
|
|
self.statusBarMsg(msg, "R")
|
|
|