Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide lookup functions for GRIB2 variable shortNames #109

Merged
merged 8 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions grib2io/_grib2io.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ def _isNDFD(self):
_lldivisor: float = field(init=False,repr=False,default=templates.LLDivisor())
_xydivisor: float = field(init=False,repr=False,default=templates.XYDivisor())
shapeOfEarth: templates.Grib2Metadata = field(init=False,repr=False,default=templates.ShapeOfEarth())
earthShape: str = field(init=False,repr=False,default=templates.EarthShape())
earthRadius: float = field(init=False,repr=False,default=templates.EarthRadius())
earthMajorAxis: float = field(init=False,repr=False,default=templates.EarthMajorAxis())
earthMinorAxis: float = field(init=False,repr=False,default=templates.EarthMinorAxis())
Expand Down
90 changes: 82 additions & 8 deletions grib2io/tables/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Functions for retreiving data from NCEP GRIB2 Tables.
"""
#from functools import cache # USE WHEN Python 3.9+ only
from functools import lru_cache

from .section0 import *
from .section1 import *
Expand All @@ -10,22 +12,21 @@
from .section6 import *
from .originating_centers import *

GRIB2_DISCIPLINES = [0, 1, 2, 3, 4, 10, 20]

def get_table(table, expand=False):
"""
Return GRIB2 code table as a dictionary.

Parameters
----------

**`table`**: Code table number (e.g. '1.0'). NOTE: Code table '4.1' requires a 3rd value
representing the product discipline (e.g. '4.1.0').

**`expand`**: If `True`, expand output dictionary where keys are a range.

Returns
-------

**`dict`**
"""
if len(table) == 3 and table == '4.1':
Expand Down Expand Up @@ -55,15 +56,14 @@ def get_value_from_table(value, table, expand=False):

Parameters
----------

**`value`**: `int` or `str` code table value.

**`table`**: `str` code table number.

**`expand`**: If `True`, expand output dictionary where keys are a range.

Returns
-------

Table value or `None` if not found.
"""
try:
Expand All @@ -87,7 +87,6 @@ def get_varinfo_from_table(discipline,parmcat,parmnum,isNDFD=False):

Parameters
----------

**`discipline`**: `int` or `str` of Discipline code value of a GRIB2 message.

**`parmcat`**: `int` or `str` of Parameter Category value of a GRIB2 message.
Expand All @@ -99,7 +98,6 @@ def get_varinfo_from_table(discipline,parmcat,parmnum,isNDFD=False):

Returns
-------

**`list`**: containing variable information. "Unknown" is given for item of
information if variable is not found.
- list[0] = full name
Expand All @@ -124,6 +122,84 @@ def get_varinfo_from_table(discipline,parmcat,parmnum,isNDFD=False):
return ['Unknown','Unknown','Unknown']


#@cache# USE WHEN Python 3.9+ only
@lru_cache(maxsize=None)
def get_shortnames(discipline=None, parmcat=None, parmnum=None, isNDFD=False):
"""
Returns a list of variable shortNames given GRIB2 discipline, parameter
category, and parameter number. If all 3 args are None, then shortNames
from all disciplines, parameter categories, and numbers will be returned.

Parameters
----------
**`discipline : int`** GRIB2 discipline code value.

**`parmcat : int`** GRIB2 parameter category value.

**`parmnum`**: `int` or `str` of Parameter Number value of a GRIB2 message.

Returns
-------
**`list`** of GRIB2 shortNames.
"""
shortnames = list()
if discipline is None:
discipline = GRIB2_DISCIPLINES
else:
discipline = [discipline]
if parmcat is None:
parmcat = list()
for d in discipline:
parmcat += list(get_table(f'4.1.{d}').keys())
else:
parmcat = [parmcat]
if parmnum is None:
parmnum = list(range(256))
else:
parmnum = [parmnum]
for d in discipline:

for pc in parmcat:
for pn in parmnum:
shortnames.append(get_varinfo_from_table(d,pc,pn,isNDFD)[2])

shortnames = sorted(set(shortnames))
try:
shortnames.remove('unknown')
shortnames.remove('Unknown')
except(ValueError):
pass
return shortnames


#@cache# USE WHEN Python 3.9+ only
@lru_cache(maxsize=None)
def get_metadata_from_shortname(shortname):
"""
Provide GRIB2 variable metadata attributes given a GRIB2 shortName.

Parameters
----------
**`shortname : str`** GRIB2 variable shortName.

Returns
-------
**`list`** of dictionary items where each dictionary items contains the variable
metadata key:value pairs. **NOTE:** Some variable shortNames will exist in multiple
parameter category/number tables according to the GRIB2 discipline.
"""
metadata = []
for d in GRIB2_DISCIPLINES:
parmcat = list(get_table(f'4.1.{d}').keys())
for pc in parmcat:
for pn in range(256):
varinfo = get_varinfo_from_table(d,pc,pn,False)
if shortname == varinfo[2]:
metadata.append(dict(discipline=d,parameterCategory=pc,parameterNumber=pn,
fullName=varinfo[0],units=varinfo[1]))
return metadata


def get_wgrib2_level_string(pdtn,pdt):
"""
Return a string that describes the level or layer of the GRIB2 message. The
Expand All @@ -136,14 +212,12 @@ def get_wgrib2_level_string(pdtn,pdt):

Parameters
----------

**`pdtn`**: GRIB2 Product Definition Template Number

**`pdt`**: sequence containing GRIB2 Product Definition Template (Section 4).

Returns
-------

**`str`**: wgrib2-formatted level/layer string.
"""
if pdtn == 48:
Expand Down
6 changes: 3 additions & 3 deletions grib2io/tables/section3.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@
'255':'Missing',
}

earth_params = {
table_earth_params = {
'0':{'shape':'spherical','radius':6367470.0},
'1':{'shape':'spherical','radius':None},
'2':{'shape':'oblateSpheriod','major_axis':6378160.0,'minor_axis':6356775.0,'flattening':1.0/297.0},
'3':{'shape':'oblateSpheriod','major_axis':None,'minor_axis':None,'flattening':None},
'4':{'shape':'oblateSpheriod','major_axis':6378137.0,'minor_axis':6356752.314,'flattening':1.0/298.257222101},
'5':{'shape':'oblateSpheriod','major_axis':6378137.0,'minor_axis':6356752.3142,'flattening':1.0/298.257223563},
'5':{'shape':'ellipsoid','major_axis':6378137.0,'minor_axis':6356752.3142,'flattening':1.0/298.257222101},
'6':{'shape':'spherical','radius':6371229.0},
'7':{'shape':'oblateSpheriod','major_axis':None,'minor_axis':None,'flattening':None},
'8':{'shape':'spherical','radius':6371200.0},
}
for i in range(9,256):
earth_params[str(i)] = {'shape':'unknown','radius':None}
table_earth_params[str(i)] = {'shape':'unknown','radius':None}
144 changes: 140 additions & 4 deletions grib2io/tables/section4.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,14 @@
'103':'Analysis or forecast at a horizontal level or in a horizontal layer at a point in time for waves selected by period range (see Template 4.103)',
'104':'Individual ensemble forecast, control and perturbed, at a horizontal level or in a horizontal layer at a point in time for waves selected by period range (see Template 4.104)',
'105':'Anomalies, significance and other derived products from an analysis or forecast in relation to a reference period at a horizontal level or in a horizontal layer in a continuous or non-continuous time interval (see Template 4.105)',
'99-253':'Reserved',
'106':'Anomalies, significance and other derived products from an individual ensemble forecast, control and perturbed in relation to a reference period at a horizontal level or in a horizontal layer in a continuous or non-continuous time interval (see Template 4.106)',
'107':'Anomalies, significance and other derived products from derived forecasts based on all ensemble members in relation to a reference period at a horizontal level or in a horizontal layer in a continuous or non-continuous time interval (see Template 4.107)',
'108':'Analysis or forecast at a horizontal level or in a horizontal layer at a point in time for generic optical products (see Template 4.108)',
'109':'Individual ensemble forecast, control and perturbed, at a horizontal level or in a horizontal layer at a point in time for generic optical products (see Template 4.109)',
'110':'Average, accumulation, extreme values or other statistically processed values at a horizontal level or in a horizontal layer in a continuous or non-continuous time interval for generic optical products (see Template 4.110)',
'111':'Individual ensemble forecast, control and perturbed, at a horizontal level or in a horizontal layer, in a continuous or non-continuous interval for generic optical products (see Template 4.111)',
'112':'Anomalies, significance and other derived products as probability forecasts in relation to a reference period at a horizontal level or in a horizontal layer in a continuous or non-continuous time interval (see Template 4.112)',
'113-253':'Reserved',
'254':'CCITT IA5 character string (see Template 4.254)',
'255-999':'Reserved',
'1000':'Cross-section of analysis and forecast at a point in time. (see Template 4.1000)',
Expand Down Expand Up @@ -224,7 +231,10 @@
'16':'Physical Retrieval',
'17':'Regression Analysis',
'18':'Difference Between Two Forecasts',
'19-191':'Reserved',
'19':'First guess',
'20':'Analysis increment',
'21':'Initialization increment for analysis',
'22-191':'Reserved',
'192-254':'Reserved for Local Use',
'192':'Forecast Confidence Indicator',
'193':'Probability-matched Mean',
Expand Down Expand Up @@ -276,7 +286,9 @@
'14':['Level of free convection (LFC)','unknown'],
'15':['Convection condensation level (CCL)','unknown'],
'16':['Level of neutral buoyancy or equilibrium (LNB)','unknown'],
'17-19':['Reserved','unknown'],
'17':['Departure level of the most unstable parcel of air (MUDL)','unknown'],
'18':['Departure level of a mixed layer parcel of air with specified layer depth','Pa'],
'19':['Reserved','unknown'],
'20':['Isothermal Level','K'],
'21':['Lowest level where mass density exceeds the specified value (base for a given threshold of mass density)','kg m-3'],
'22':['Highest level where mass density exceeds the specified value (top for a given threshold of mass density)','kg m-3'],
Expand Down Expand Up @@ -462,7 +474,13 @@
'7':'Covariance (temporal variance)',
'8':'Difference ( value at the beginning of the time range minus value at the end)',
'9':'Ratio',
'13-191':'Reserved',
'10':'Standardized Anomaly',
'11':'Summation',
'12':'Return period',
'13-99':'Reserved',
'100':'Severity',
'101':'Mode',
'102-191':'Reserved',
'192-254':'Reserved for Local Use',
'192':'Climatological Mean Value: multiple year averages of quantities which are themselves means over some period of time (P2) less than a year. The reference time (R) indicates the date and time of the start of a period of time, given by R to R + P2, over which a mean is formed; N indicates the number of such period-means that are averaged together to form the climatological value, assuming that the N period-mean fields are separated by one year. The reference time indicates the start of the N-year climatology. N is given in octets 22-23 of the PDS. If P1 = 0 then the data averaged in the basic interval P2 are assumed to be continuous, i.e., all available data are simply averaged together. If P1 = 1 (the units of time - octet 18, code table 4 - are not relevant here) then the data averaged together in the basic interval P2 are valid only at the time (hour, minute) given in the reference time, for all the days included in the P2 period. The units of P2 are given by the contents of octet 18 and Table 4.',
'193':'Average of N forecasts (or initialized analyses); each product has forecast period of P1 (P1=0 for initialized analyses); products have reference times at intervals of P2, beginning at the given reference time.',
Expand Down Expand Up @@ -968,6 +986,106 @@
'65535':['Missing','unknown'],
}

table_4_234 = {
'1':'Crops, mixed farming',
'2':'Short grass',
'3':'Evergreen needleleaf trees',
'4':'Deciduous needleleaf trees',
'5':'Deciduous broadleaf trees',
'6':'Evergreen broadleaf trees',
'7':'Tall grass',
'8':'Desert',
'9':'Tundra',
'10':'Irrigated corps',
'11':'Semidesert',
'12':'Ice caps and glaciers',
'13':'Bogs and marshes',
'14':'Inland water',
'15':'Ocean',
'16':'Evergreen shrubs',
'17':'Deciduous shrubs',
'18':'Mixed forest',
'19':'Interrupted forest',
'20':'Water and land mixtures',
'21-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_235 = {
'0':'Total Wave Spectrum (combined wind waves and swell)',
'1':'Generalized Partition',
'2-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_236 = {
'1':'Coarse',
'2':'Medium',
'3':'Medium-fine',
'4':'Fine',
'5':'Very-fine',
'6':'Organic',
'7':'Tropical-organic',
'8-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_238 = {
'0':'Reserved',
'1':'Aviation',
'2':'Lightning',
'3':'Biogenic Sources',
'4':'Anthropogenic sources',
'5':'Wild fires',
'6':'Natural sources',
'7':'Bio-fuel',
'8':'Volcanoes',
'9':'Fossil-fuel',
'10':'Wetlands',
'11':'Oceans',
'12':'Elevated anthropogenic sources',
'13':'Surface anthropogenic sources',
'14':'Agriculture livestock',
'15':'Agriculture soils',
'16':'Agriculture waste burning',
'17':'Agriculture (all)',
'18':'Residential, commercial and other combustion',
'19':'Power generation',
'20':'Super power stations',
'21':'Fugitives',
'22':'Industrial process',
'23':'Solvents',
'24':'Ships',
'25':'Wastes',
'26':'Road transportation',
'27':'Off-road transportation',
'28-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_239 = {
'0':'Reserved',
'1':'Bog',
'2':'Drained',
'3':'Fen',
'4':'Floodplain',
'5':'Mangrove',
'6':'Marsh',
'7':'Rice',
'8':'Riverine',
'9':'Salt Marsh',
'10':'Swamp',
'11':'Upland',
'12':'Wet tundra',
'13-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_243 = {
'0':'Reserved',
'1':'Evergreen broadleaved forest',
Expand Down Expand Up @@ -1014,6 +1132,15 @@
'255':'Missing',
}

table_4_244 = {
'0':'No Quality Information Available',
'1':'Failed',
'2':'Passed',
'3-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_4_246 = {
'0':'No thunderstorm occurrence',
'1':'Weak thunderstorm',
Expand Down Expand Up @@ -1067,6 +1194,15 @@
'255':'Missing',
}

table_4_251 = {
'0':'Undefined Sequence',
'1':'Geometric sequence,(see Note 1)',
'2':'Arithmetic sequence,(see Note 2)',
'3-191':'Reserved',
'192-254':'Reserved for Local Use',
'255':'Missing',
}

table_scale_time_hours = {
'0': 60.,
'1': 1.,
Expand Down
Loading