348 lines
12 KiB
Python
348 lines
12 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.
|
||
|
##
|
||
|
#
|
||
|
# Name:
|
||
|
# ClimLib.py
|
||
|
# GFS1-NHD:A9007.0000-SCRIPT;3
|
||
|
#
|
||
|
# Status:
|
||
|
# DELIVERED
|
||
|
#
|
||
|
# History:
|
||
|
# Revision 3 (DELIVERED)
|
||
|
# Created: 24-MAY-2006 09:05:37 TROJAN
|
||
|
# spr 7144: ficed weather element dictionary, added VLIFR
|
||
|
# category
|
||
|
#
|
||
|
# Revision 2 (DELIVERED)
|
||
|
# Created: 16-FEB-2006 14:35:36 TROJAN
|
||
|
# added check for cache age
|
||
|
#
|
||
|
# Revision 1 (APPROVED)
|
||
|
# Created: 30-JAN-2006 07:51:28 TROJAN
|
||
|
# stdr 945
|
||
|
#
|
||
|
# Change Document History:
|
||
|
# 1:
|
||
|
# Change Document: GFS1-NHD_SPR_7144
|
||
|
# Action Date: 14-FEB-2007 12:27:26
|
||
|
# Relationship Type: In Response to
|
||
|
# Status: CLOSED
|
||
|
# Title: AvnFPS: Incorrect method to specify filtering criteria in Cig/Vis Monthly tool
|
||
|
#
|
||
|
#
|
||
|
# ClimLib.py
|
||
|
# common code for Climate tools
|
||
|
# George Trojan, SAIC/MDL, November 2005
|
||
|
# last update: 05/24/06
|
||
|
|
||
|
##
|
||
|
# This is a base file that is not intended to be overridden.
|
||
|
##
|
||
|
|
||
|
import logging, os, pickle
|
||
|
import Avn
|
||
|
|
||
|
_Logger = logging.getLogger(__name__)
|
||
|
|
||
|
PType = Avn.Bunch(freezing=1<<0, frozen=1<<1, liquid=1<<2)
|
||
|
PBest = Avn.Bunch(drizzle=1<<0, continuous=1<<1, shower=1<<2)
|
||
|
PInt = Avn.Bunch(lgt=1<<0, mod=1<<1, hvy=1<<2)
|
||
|
|
||
|
PTypeStr = {
|
||
|
(PType.freezing, PBest.drizzle): 'FZDZ',
|
||
|
(PType.freezing, PBest.continuous): 'FZRA',
|
||
|
(PType.freezing, PBest.shower): 'SHPL',
|
||
|
(PType.frozen, PBest.drizzle): 'DZSN',
|
||
|
(PType.frozen, PBest.continuous): 'SN',
|
||
|
(PType.frozen, PBest.shower): 'SHSN',
|
||
|
(PType.liquid, PBest.drizzle): 'DZ',
|
||
|
(PType.liquid, PBest.continuous): 'RA',
|
||
|
(PType.liquid, PBest.shower): 'SHRA',
|
||
|
}
|
||
|
|
||
|
# Codes used in pres_wxm_code
|
||
|
MWxTable = {
|
||
|
0: {'str': ''},
|
||
|
1: {'str': ''},
|
||
|
2: {'str': ''},
|
||
|
3: {'str': ''},
|
||
|
4: {'str': 'FU'},
|
||
|
5: {'str': 'HZ'},
|
||
|
6: {'str': 'DU'},
|
||
|
7: {'str': 'DU'},
|
||
|
8: {'str': 'DU'},
|
||
|
9: {'str': 'BLSA'},
|
||
|
10: {'str': 'BR'},
|
||
|
11: {'str': 'BCFG'},
|
||
|
12: {'str': 'MIFG'},
|
||
|
13: {'str': 'TS'},
|
||
|
14: {'str': ''},
|
||
|
15: {'str': ''},
|
||
|
16: {'str': ''},
|
||
|
17: {'str': 'TS'},
|
||
|
18: {'str': ''},
|
||
|
19: {'str': ''},
|
||
|
20: {'str': ''},
|
||
|
21: {'str': ''},
|
||
|
22: {'str': ''},
|
||
|
23: {'str': ''},
|
||
|
24: {'str': ''},
|
||
|
25: {'str': ''},
|
||
|
26: {'str': ''},
|
||
|
27: {'str': ''},
|
||
|
28: {'str': ''},
|
||
|
29: {'str': ''},
|
||
|
30: {'str': 'BLSA'},
|
||
|
31: {'str': 'BLSA'},
|
||
|
32: {'str': 'BLSA'},
|
||
|
33: {'str': 'BLSA'},
|
||
|
34: {'str': 'BLSA'},
|
||
|
35: {'str': 'BLSA'},
|
||
|
36: {'str': 'DRSN'},
|
||
|
37: {'str': 'DRSN'},
|
||
|
38: {'str': 'BLSN'},
|
||
|
39: {'str': 'BLSN'},
|
||
|
40: {'str': ''},
|
||
|
41: {'str': 'BCFG'},
|
||
|
42: {'str': 'FG'},
|
||
|
43: {'str': 'FG'},
|
||
|
44: {'str': 'FG'},
|
||
|
45: {'str': 'FG'},
|
||
|
46: {'str': 'FG'},
|
||
|
47: {'str': 'FG'},
|
||
|
48: {'str': 'FG'},
|
||
|
49: {'str': 'FG'},
|
||
|
50: {'int': PInt.lgt, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
51: {'int': PInt.lgt, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
52: {'int': PInt.mod, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
53: {'int': PInt.mod, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
54: {'int': PInt.hvy, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
55: {'int': PInt.hvy, 'str': 'DZ',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
56: {'int': PInt.lgt, 'str': 'FZDZ',
|
||
|
'best': PBest.drizzle, 'type': PType.freezing},
|
||
|
57: {'int': PInt.mod|PInt.hvy, 'str': 'FZDZ',
|
||
|
'best': PBest.drizzle, 'type': PType.freezing},
|
||
|
58: {'int': PInt.lgt, 'str': 'DZRA',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
59: {'int': PInt.mod|PInt.hvy, 'str': 'DZRA',
|
||
|
'best': PBest.drizzle, 'type': PType.liquid},
|
||
|
60: {'int': PInt.lgt, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
61: {'int': PInt.lgt, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
62: {'int': PInt.mod, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
63: {'int': PInt.mod, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
64: {'int': PInt.hvy, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
65: {'int': PInt.hvy, 'str': 'RA',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
66: {'int': PInt.lgt, 'str': 'FZRA',
|
||
|
'best': PBest.continuous, 'type': PType.freezing},
|
||
|
67: {'int': PInt.mod|PInt.hvy, 'str': 'FZRA',
|
||
|
'best': PBest.continuous, 'type': PType.freezing},
|
||
|
68: {'int': PInt.lgt, 'str': 'RASN',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
69: {'int': PInt.mod|PInt.hvy, 'str': 'RASN',
|
||
|
'best': PBest.continuous, 'type': PType.liquid},
|
||
|
70: {'int': PInt.lgt, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
71: {'int': PInt.lgt, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
72: {'int': PInt.mod, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
73: {'int': PInt.mod, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
74: {'int': PInt.hvy, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
75: {'int': PInt.hvy, 'str': 'SN',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
76: {'int': PInt.lgt, 'str': 'IC',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
77: {'int': PInt.lgt, 'str': 'SG',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
78: {'int': PInt.lgt, 'str': 'IC',
|
||
|
'best': PBest.continuous, 'type': PType.frozen},
|
||
|
79: {'int': PInt.lgt, 'str': 'PL',
|
||
|
'best': PBest.continuous, 'type': PType.freezing},
|
||
|
80: {'int': PInt.lgt, 'str': 'SHRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
81: {'int': PInt.mod|PInt.hvy, 'str': 'SHRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
82: {'int': PInt.hvy, 'str': 'SHRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
83: {'int': PInt.lgt, 'str': 'SHRASN',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
84: {'int': PInt.mod|PInt.hvy, 'str': 'SHRASN',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
85: {'int': PInt.lgt, 'str': 'SHSN',
|
||
|
'best': PBest.shower, 'type': PType.frozen},
|
||
|
86: {'int': PInt.mod|PInt.hvy, 'str': 'SHSN',
|
||
|
'best': PBest.shower, 'type': PType.frozen},
|
||
|
87: {'int': PInt.lgt, 'str': 'SHGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
88: {'int': PInt.mod|PInt.hvy, 'str': 'SHGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
89: {'int': PInt.lgt, 'str': 'SHGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
90: {'int': PInt.mod|PInt.hvy, 'str': 'SHGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
91: {'int': PInt.lgt, 'str': 'TSRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
92: {'int': PInt.mod|PInt.hvy, 'str': 'TSRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
93: {'int': PInt.lgt, 'str': 'TSGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
94: {'int': PInt.mod|PInt.hvy, 'str': 'TSGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
95: {'int': PInt.lgt|PInt.mod, 'str': 'TSRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
96: {'int': PInt.lgt|PInt.mod, 'str': 'TSGS',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
97: {'int': PInt.hvy, 'str': 'TSRA',
|
||
|
'best': PBest.shower, 'type': PType.liquid},
|
||
|
98: {'int': 0, 'str': 'TS SS', 'best': 0, 'type': 0},
|
||
|
99: {'int': PInt.hvy, 'str': 'TSGR',
|
||
|
'best': PBest.shower, 'type': PType.freezing},
|
||
|
}
|
||
|
|
||
|
MOS_obvis = {5, 10, 30, 31, 32, 33, 34, 35, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49}
|
||
|
|
||
|
# disjoint
|
||
|
MOS_ptype = {}
|
||
|
for key in PType.values():
|
||
|
MOS_ptype[key] = {x for x in MWxTable
|
||
|
if x >= 50
|
||
|
and MWxTable[x]['type'] & key
|
||
|
}
|
||
|
|
||
|
# disjoint
|
||
|
MOS_pbest = {}
|
||
|
for key in PBest.values():
|
||
|
MOS_pbest[key] = {x for x in MWxTable
|
||
|
if x >= 50
|
||
|
and MWxTable[x]['best'] & key
|
||
|
}
|
||
|
|
||
|
# not disjoint
|
||
|
MOS_pint = {}
|
||
|
for key in PInt.values():
|
||
|
MOS_pint[key] = {x for x in MWxTable
|
||
|
if x >= 50
|
||
|
and MWxTable[x]['int'] & key
|
||
|
}
|
||
|
|
||
|
###############################################################################
|
||
|
# matches GFS MOS
|
||
|
# 1 2 3 4 5 6 7 8
|
||
|
CigCat = [0, 190, 450, 950, 1950, 3050, 6550, 12000, 999999]
|
||
|
# 1 2 3 4 5 6 7
|
||
|
VisCat = [0, 0.45, 0.95, 1.95, 2.9, 5.5, 6.5, 999.0]
|
||
|
UnltdCig = 22000 # as appears in hd5 file
|
||
|
|
||
|
###############################################################################
|
||
|
# conversion functions
|
||
|
Unlimited = 99999
|
||
|
|
||
|
def hd2us_cig(cig):
|
||
|
'''Converts ceiling from [m] to [ft].
|
||
|
Unlimited ceiling is set to 99999'''
|
||
|
if 0 <= cig <= 12000:
|
||
|
return cig/0.305
|
||
|
elif cig < 30000:
|
||
|
return Unlimited
|
||
|
return -1
|
||
|
|
||
|
def us2hd_cig(cig, unlimited):
|
||
|
'''Converts ceiling from [ft] to [m].
|
||
|
Unlimited (99999) ceiling is set to "unlimited", which should be
|
||
|
set to fh.variables['cig'].Unlimited'''
|
||
|
if cig == Unlimited:
|
||
|
return unlimited
|
||
|
return cig * 0.305
|
||
|
|
||
|
def hd2us_vsby(vsby):
|
||
|
'''Converts visibility from [m] to statue miles.'''
|
||
|
if 0 <= vsby <= 100000:
|
||
|
return vsby / 1609.0
|
||
|
return -1
|
||
|
|
||
|
def us2hd_vsby(vsby):
|
||
|
'''Converts visibility from statue miles to [m].'''
|
||
|
return vsby * 1609.0
|
||
|
|
||
|
def hd2us_wind_speed(speed):
|
||
|
'''Converts speed from [m/s] to [kt].'''
|
||
|
if 0 <= speed <= 100:
|
||
|
return int(speed * 3600.0 / 1852.0 + 0.5)
|
||
|
return -1
|
||
|
|
||
|
def us2hd_wind_speed(speed):
|
||
|
'''Converts speed from [kt] to [m/s].'''
|
||
|
return speed * 1852.0 / 3600.0
|
||
|
|
||
|
# flight category definition (metric)
|
||
|
FlightCats = {
|
||
|
0: {'cig': us2hd_cig(190, 22000), 'vis': us2hd_vsby(0.45)},
|
||
|
1: {'cig': us2hd_cig(490, 22000), 'vis': us2hd_vsby(0.95)},
|
||
|
2: {'cig': us2hd_cig(950, 22000), 'vis': us2hd_vsby(2.9)},
|
||
|
3: {'cig': us2hd_cig(3050, 22000), 'vis': us2hd_vsby(5.2)},
|
||
|
}
|
||
|
|
||
|
###############################################################################
|
||
|
class Cache:
|
||
|
def __init__(self):
|
||
|
self.home = os.path.join(os.environ['TOP_DIR'], 'data', 'climate',
|
||
|
'cache')
|
||
|
|
||
|
def put(self, id_, key, data):
|
||
|
dir_ = os.path.join(self.home, id_)
|
||
|
try:
|
||
|
if not os.path.isdir(dir_):
|
||
|
os.mkdir(dir_)
|
||
|
path = os.path.join(dir_, key)
|
||
|
with open(path, 'wb') as f:
|
||
|
pickle.dump(data, f)
|
||
|
return True
|
||
|
except IOError as e:
|
||
|
_Logger.exception('put() failed for %s\n%s', path, e)
|
||
|
return False
|
||
|
|
||
|
def get(self, id_, key, cpath=None):
|
||
|
path = os.path.join(self.home, id_, key)
|
||
|
# compare times, return None if climate file newer than cache
|
||
|
try:
|
||
|
if cpath and os.path.getmtime(path) < os.path.getmtime(cpath):
|
||
|
_Logger.info('climate data file newer than cache')
|
||
|
return None
|
||
|
with open(path, 'rb') as f:
|
||
|
return pickle.load(f)
|
||
|
except (ValueError, IOError, OSError) as e:
|
||
|
_Logger.info('get() failed for %s\n%s', path, e)
|
||
|
return None
|