grib2io.utils

Collection of utility functions to assist in the encoding and decoding of GRIB2 Messages.

  1"""
  2Collection of utility functions to assist in the encoding and decoding
  3of GRIB2 Messages.
  4"""
  5
  6import g2clib
  7import datetime
  8import numpy as np
  9import struct
 10
 11from .. import tables
 12
 13def int2bin(i,nbits=8,output=str):
 14    """
 15    Convert integer to binary string or list
 16
 17    Parameters
 18    ----------
 19
 20    **`i`**: Integer value to convert to binary representation.
 21
 22    **`nbits`**: Number of bits to return.  Valid values are 8 [DEFAULT], 16,
 23    32, and 64.
 24
 25    **`output`**: Return data as `str` [DEFAULT] or `list` (list of ints).
 26
 27    Returns
 28    -------
 29
 30    `str` or `list` (list of ints) of binary representation of the integer value.
 31    """
 32    i = int(i) if not isinstance(i,int) else i
 33    assert nbits in [8,16,32,64]
 34    bitstr = "{0:b}".format(i).zfill(nbits)
 35    if output is str:
 36        return bitstr
 37    elif output is list:
 38        return [int(b) for b in bitstr]
 39
 40
 41def ieee_float_to_int(f):
 42    """
 43    Convert an IEEE 32-bit float to a 32-bit integer.
 44
 45    Parameters
 46    ----------
 47
 48    **`f`**: Float value.
 49
 50    Returns
 51    -------
 52
 53    Numpy Int32 representation of an IEEE 32-bit float.
 54    """
 55    i = struct.unpack('>i',struct.pack('>f',np.float32(f)))[0]
 56    return np.int32(i)
 57
 58
 59
 60def ieee_int_to_float(i):
 61    """
 62    Convert a 32-bit integer to an IEEE 32-bit float.
 63
 64    Parameters
 65    ----------
 66
 67    **`i`**: Integer value.
 68
 69    Returns
 70    -------
 71
 72    Numpy float32
 73    """
 74    f = struct.unpack('>f',struct.pack('>i',np.int32(i)))[0]
 75    return np.float32(f)
 76
 77
 78
 79def getmd5str(a):
 80    """
 81    Generate a MD5 hash string from input list
 82    """
 83    import hashlib
 84    assert isinstance(a,list) or isinstance(a,bytes)
 85    return hashlib.md5(''.join([str(i) for i in a]).encode()).hexdigest()
 86
 87
 88def getdate(year,month,day,hour,minute=None,second=None):
 89    """
 90    Build an integer date from component input.
 91
 92    **`year`**: Year in 4-digit format.
 93
 94    **`month`**: Month in 2-digit format.
 95
 96    **`day`**: Day in 2-digit format.
 97
 98    **`hour`**: Hour in 2-digit format.
 99
100    **`minute`**: Minute in 2-digit format. This argument is required if second is provided, otherwise
101    it is optional.
102
103    **`second`**: Second in 2-digit format [OPTIONAL].
104    """
105    year_exp = 6
106    month_exp = 4
107    day_exp = 2
108    hour_exp = 0
109    #if second is not None and minute is None:
110    #    raise ValueError("Must provide minute argument if second argument is provided.")
111    #year_exp = 6
112    #month_exp = 4
113    #day_exp = 2
114    #hour_exp = 0
115    #minute_exp = -2
116    #second_exp = -4
117    #if minute is not None:
118    #    assert minute >= 0 and minute <= 60
119    #    year_exp += 2
120    #    month_exp += 2
121    #    day_exp += 2
122    #    hour_exp += 2
123    #    minute_exp += 2
124    #    second_exp += 2
125    #if second is not None:
126    #    assert second >= 0 and second <= 60
127    #    year_exp += 2
128    #    month_exp += 2
129    #    day_exp += 2
130    #    hour_exp += 2
131    #    minute_exp += 2
132    #    second_exp += 2
133    idate = (year*pow(10,year_exp))+(month*pow(10,month_exp))+\
134            (day*pow(10,day_exp))+(hour*pow(10,hour_exp))
135    #if minute is not None:
136    #    idate += minute*pow(10,minute_exp)
137    #if second is not None:
138    #    idate += second*pow(10,second_exp)
139    return idate
140
141
142def getleadtime(idsec,pdtn,pdt):
143    """
144    Computes the lead time (in units of hours) from using information from
145    GRIB2 Identification Section (Section 1), Product Definition Template
146    Number, and Product Definition Template (Section 4).
147
148    Parameters
149    ----------
150
151    **`idsec`**: seqeunce containing GRIB2 Identification Section (Section 1).
152
153    **`pdtn`**: GRIB2 Product Definition Template Number
154
155    **`idsec`**: seqeunce containing GRIB2 Product Definition Template (Section 4).
156
157    Returns
158    -------
159
160    **`lt`**: Lead time in units of hours
161    """
162    refdate = datetime.datetime(*idsec[5:11])
163    if pdtn == 8:
164        enddate = datetime.datetime(*pdt[15:21])
165        td = enddate - refdate
166        lt = (td).total_seconds()/3600.0
167    elif pdtn == 9:
168        enddate = datetime.datetime(*pdt[22:28])
169        td = enddate - refdate
170        lt = (td).total_seconds()/3600.0
171    elif pdtn == 10:
172        enddate = datetime.datetime(*pdt[16:22])
173        td = enddate - refdate
174        lt = (td).total_seconds()/3600.0
175    elif pdtn == 11:
176        enddate = datetime.datetime(*pdt[18:24])
177        td = enddate - refdate
178        lt = (td).total_seconds()/3600.0
179    elif pdtn == 12:
180        enddate = datetime.datetime(*pdt[17:23])
181        td = enddate - refdate
182        lt = (td).total_seconds()/3600.0
183    else:
184        lt = pdt[8]*(tables.get_value_from_table(pdt[7],'scale_time_hours'))
185    return int(lt)
186
187
188def getduration(pdtn,pdt):
189    """
190    Computes the duration time (in units of hours) from using information from
191    Product Definition Template Number, and Product Definition Template (Section 4).
192
193    Parameters
194    ----------
195
196    **`pdtn`**: GRIB2 Product Definition Template Number
197
198    **`pdt`**: sequence containing GRIB2 Product Definition Template (Section 4).
199
200    Returns
201    -------
202
203    **`dur`**: Duration time in units of hours
204    """
205    if pdtn == 8:
206        dur = pdt[26]*(tables.get_value_from_table(pdt[25],'scale_time_hours'))
207    elif pdtn == 9:
208        dur = pdt[33]*(tables.get_value_from_table(pdt[32],'scale_time_hours'))
209    elif pdtn == 10:
210        dur = pdt[27]*(tables.get_value_from_table(pdt[26],'scale_time_hours'))
211    elif pdtn == 11:
212        dur = pdt[29]*(tables.get_value_from_table(pdt[28],'scale_time_hours'))
213    elif pdtn == 12:
214        dur = pdt[28]*(tables.get_value_from_table(pdt[27],'scale_time_hours'))
215    else:
216        dur = 0
217    return int(dur)
218
219
220def decode_wx_strings(lus):
221    """
222    Decode GRIB2 Local Use Section to obtain NDFD/MDL Weather Strings.  The
223    decode procedure is defined here:
224
225    https://vlab.noaa.gov/web/mdl/nbm-gmos-grib2-wx-info
226
227    Parameters
228    ----------
229
230    **`lus`**: GRIB2 Local Use Section containing NDFD weather strings.
231
232    Returns
233    -------
234
235    **`list`**: List of NDFD weather strings.
236    """
237    assert lus[0] == 1
238    # Unpack information related to the simple packing method
239    # the packed weather string data.
240    ngroups = struct.unpack('>H',lus[1:3])[0]
241    nvalues = struct.unpack('>i',lus[3:7])[0]
242    refvalue = struct.unpack('>i',lus[7:11])[0]
243    dsf = struct.unpack('>h',lus[11:13])[0]
244    nbits = lus[13]
245    datatype = lus[14]
246    if datatype == 0: # Floating point
247        refvalue = np.float32(ieee_int_to_float(refvalue)*10**-dsf)
248    elif datatype == 1: # Integer
249        refvalue = np.int32(ieee_int_to_float(refvalue)*10**-dsf)
250    # Upack each byte starting at byte 15 to end of the local use 
251    # section, create a binary string and append to the full 
252    # binary string.
253    b = ''
254    for i in range(15,len(lus)):
255        iword = struct.unpack('>B',lus[i:i+1])[0]
256        b += bin(iword).split('b')[1].zfill(8)
257    # Iterate over the binary string (b). For each nbits
258    # chunk, convert to an integer, including the refvalue,
259    # and then convert the int to an ASCII character, then
260    # concatenate to wxstring.
261    wxstring = ''
262    for i in range(0,len(b),nbits):
263        wxstring += chr(int(b[i:i+nbits],2)+refvalue)
264    # Return string as list, split by null character.
265    return list(filter(None,wxstring.split('\0')))
266
267
268def get_wgrib2_prob_string(probtype,sfacl,svall,sfacu,svalu):
269    """
270    Return a wgrib2-formatted string explaining probabilistic
271    threshold informaiton.  Logic from wgrib2 source, [Prob.c](https://github.com/NOAA-EMC/NCEPLIBS-wgrib2/blob/develop/wgrib2/Prob.c),
272    is replicated here.
273
274    Parameters
275    ----------
276
277    **`probtype`**: `int` type of probability (Code Table 4.9).
278
279    **`sfacl`**: `int` scale factor of lower limit.
280
281    **`svall`**: `int` scaled value of lower limit.
282
283    **`sfacu`**: `int` scale factor of upper limit.
284
285    **`svalu`**: `int` scaled value of upper limit.
286
287    Returns
288    -------
289
290    **`str`**: wgrib2-formatted string of probability threshold.
291    """
292    probstr = ''
293    lower = svall/(10**sfacl)
294    upper = svalu/(10**sfacu)
295    if probtype == 0:
296        probstr = 'prob <%g' % (lower)
297    elif probtype == 1:
298        probstr = 'prob >%g' % (upper)
299    elif probtype == 2:
300        if lower == upper:
301            probstr = 'prob =%g' % (lower)
302        else:
303            probstr = 'prob >=%g <%g' % (lower,upper)
304    elif probtype == 3:
305        probstr = 'prob >%g' % (lower)
306    elif probtype == 4:
307        probstr = 'prob <%g' % (upper)
308    return probstr
def int2bin(i, nbits=8, output=<class 'str'>):
14def int2bin(i,nbits=8,output=str):
15    """
16    Convert integer to binary string or list
17
18    Parameters
19    ----------
20
21    **`i`**: Integer value to convert to binary representation.
22
23    **`nbits`**: Number of bits to return.  Valid values are 8 [DEFAULT], 16,
24    32, and 64.
25
26    **`output`**: Return data as `str` [DEFAULT] or `list` (list of ints).
27
28    Returns
29    -------
30
31    `str` or `list` (list of ints) of binary representation of the integer value.
32    """
33    i = int(i) if not isinstance(i,int) else i
34    assert nbits in [8,16,32,64]
35    bitstr = "{0:b}".format(i).zfill(nbits)
36    if output is str:
37        return bitstr
38    elif output is list:
39        return [int(b) for b in bitstr]

Convert integer to binary string or list

Parameters

i: Integer value to convert to binary representation.

nbits: Number of bits to return. Valid values are 8 [DEFAULT], 16, 32, and 64.

output: Return data as str [DEFAULT] or list (list of ints).

Returns

str or list (list of ints) of binary representation of the integer value.

def ieee_float_to_int(f):
42def ieee_float_to_int(f):
43    """
44    Convert an IEEE 32-bit float to a 32-bit integer.
45
46    Parameters
47    ----------
48
49    **`f`**: Float value.
50
51    Returns
52    -------
53
54    Numpy Int32 representation of an IEEE 32-bit float.
55    """
56    i = struct.unpack('>i',struct.pack('>f',np.float32(f)))[0]
57    return np.int32(i)

Convert an IEEE 32-bit float to a 32-bit integer.

Parameters

f: Float value.

Returns

Numpy Int32 representation of an IEEE 32-bit float.

def ieee_int_to_float(i):
61def ieee_int_to_float(i):
62    """
63    Convert a 32-bit integer to an IEEE 32-bit float.
64
65    Parameters
66    ----------
67
68    **`i`**: Integer value.
69
70    Returns
71    -------
72
73    Numpy float32
74    """
75    f = struct.unpack('>f',struct.pack('>i',np.int32(i)))[0]
76    return np.float32(f)

Convert a 32-bit integer to an IEEE 32-bit float.

Parameters

i: Integer value.

Returns

Numpy float32

def getmd5str(a):
80def getmd5str(a):
81    """
82    Generate a MD5 hash string from input list
83    """
84    import hashlib
85    assert isinstance(a,list) or isinstance(a,bytes)
86    return hashlib.md5(''.join([str(i) for i in a]).encode()).hexdigest()

Generate a MD5 hash string from input list

def getdate(year, month, day, hour, minute=None, second=None):
 89def getdate(year,month,day,hour,minute=None,second=None):
 90    """
 91    Build an integer date from component input.
 92
 93    **`year`**: Year in 4-digit format.
 94
 95    **`month`**: Month in 2-digit format.
 96
 97    **`day`**: Day in 2-digit format.
 98
 99    **`hour`**: Hour in 2-digit format.
100
101    **`minute`**: Minute in 2-digit format. This argument is required if second is provided, otherwise
102    it is optional.
103
104    **`second`**: Second in 2-digit format [OPTIONAL].
105    """
106    year_exp = 6
107    month_exp = 4
108    day_exp = 2
109    hour_exp = 0
110    #if second is not None and minute is None:
111    #    raise ValueError("Must provide minute argument if second argument is provided.")
112    #year_exp = 6
113    #month_exp = 4
114    #day_exp = 2
115    #hour_exp = 0
116    #minute_exp = -2
117    #second_exp = -4
118    #if minute is not None:
119    #    assert minute >= 0 and minute <= 60
120    #    year_exp += 2
121    #    month_exp += 2
122    #    day_exp += 2
123    #    hour_exp += 2
124    #    minute_exp += 2
125    #    second_exp += 2
126    #if second is not None:
127    #    assert second >= 0 and second <= 60
128    #    year_exp += 2
129    #    month_exp += 2
130    #    day_exp += 2
131    #    hour_exp += 2
132    #    minute_exp += 2
133    #    second_exp += 2
134    idate = (year*pow(10,year_exp))+(month*pow(10,month_exp))+\
135            (day*pow(10,day_exp))+(hour*pow(10,hour_exp))
136    #if minute is not None:
137    #    idate += minute*pow(10,minute_exp)
138    #if second is not None:
139    #    idate += second*pow(10,second_exp)
140    return idate

Build an integer date from component input.

year: Year in 4-digit format.

month: Month in 2-digit format.

day: Day in 2-digit format.

hour: Hour in 2-digit format.

minute: Minute in 2-digit format. This argument is required if second is provided, otherwise it is optional.

second: Second in 2-digit format [OPTIONAL].

def getleadtime(idsec, pdtn, pdt):
143def getleadtime(idsec,pdtn,pdt):
144    """
145    Computes the lead time (in units of hours) from using information from
146    GRIB2 Identification Section (Section 1), Product Definition Template
147    Number, and Product Definition Template (Section 4).
148
149    Parameters
150    ----------
151
152    **`idsec`**: seqeunce containing GRIB2 Identification Section (Section 1).
153
154    **`pdtn`**: GRIB2 Product Definition Template Number
155
156    **`idsec`**: seqeunce containing GRIB2 Product Definition Template (Section 4).
157
158    Returns
159    -------
160
161    **`lt`**: Lead time in units of hours
162    """
163    refdate = datetime.datetime(*idsec[5:11])
164    if pdtn == 8:
165        enddate = datetime.datetime(*pdt[15:21])
166        td = enddate - refdate
167        lt = (td).total_seconds()/3600.0
168    elif pdtn == 9:
169        enddate = datetime.datetime(*pdt[22:28])
170        td = enddate - refdate
171        lt = (td).total_seconds()/3600.0
172    elif pdtn == 10:
173        enddate = datetime.datetime(*pdt[16:22])
174        td = enddate - refdate
175        lt = (td).total_seconds()/3600.0
176    elif pdtn == 11:
177        enddate = datetime.datetime(*pdt[18:24])
178        td = enddate - refdate
179        lt = (td).total_seconds()/3600.0
180    elif pdtn == 12:
181        enddate = datetime.datetime(*pdt[17:23])
182        td = enddate - refdate
183        lt = (td).total_seconds()/3600.0
184    else:
185        lt = pdt[8]*(tables.get_value_from_table(pdt[7],'scale_time_hours'))
186    return int(lt)

Computes the lead time (in units of hours) from using information from GRIB2 Identification Section (Section 1), Product Definition Template Number, and Product Definition Template (Section 4).

Parameters

idsec: seqeunce containing GRIB2 Identification Section (Section 1).

pdtn: GRIB2 Product Definition Template Number

idsec: seqeunce containing GRIB2 Product Definition Template (Section 4).

Returns

lt: Lead time in units of hours

def getduration(pdtn, pdt):
189def getduration(pdtn,pdt):
190    """
191    Computes the duration time (in units of hours) from using information from
192    Product Definition Template Number, and Product Definition Template (Section 4).
193
194    Parameters
195    ----------
196
197    **`pdtn`**: GRIB2 Product Definition Template Number
198
199    **`pdt`**: sequence containing GRIB2 Product Definition Template (Section 4).
200
201    Returns
202    -------
203
204    **`dur`**: Duration time in units of hours
205    """
206    if pdtn == 8:
207        dur = pdt[26]*(tables.get_value_from_table(pdt[25],'scale_time_hours'))
208    elif pdtn == 9:
209        dur = pdt[33]*(tables.get_value_from_table(pdt[32],'scale_time_hours'))
210    elif pdtn == 10:
211        dur = pdt[27]*(tables.get_value_from_table(pdt[26],'scale_time_hours'))
212    elif pdtn == 11:
213        dur = pdt[29]*(tables.get_value_from_table(pdt[28],'scale_time_hours'))
214    elif pdtn == 12:
215        dur = pdt[28]*(tables.get_value_from_table(pdt[27],'scale_time_hours'))
216    else:
217        dur = 0
218    return int(dur)

Computes the duration time (in units of hours) from using information from Product Definition Template Number, and Product Definition Template (Section 4).

Parameters

pdtn: GRIB2 Product Definition Template Number

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

Returns

dur: Duration time in units of hours

def decode_wx_strings(lus):
221def decode_wx_strings(lus):
222    """
223    Decode GRIB2 Local Use Section to obtain NDFD/MDL Weather Strings.  The
224    decode procedure is defined here:
225
226    https://vlab.noaa.gov/web/mdl/nbm-gmos-grib2-wx-info
227
228    Parameters
229    ----------
230
231    **`lus`**: GRIB2 Local Use Section containing NDFD weather strings.
232
233    Returns
234    -------
235
236    **`list`**: List of NDFD weather strings.
237    """
238    assert lus[0] == 1
239    # Unpack information related to the simple packing method
240    # the packed weather string data.
241    ngroups = struct.unpack('>H',lus[1:3])[0]
242    nvalues = struct.unpack('>i',lus[3:7])[0]
243    refvalue = struct.unpack('>i',lus[7:11])[0]
244    dsf = struct.unpack('>h',lus[11:13])[0]
245    nbits = lus[13]
246    datatype = lus[14]
247    if datatype == 0: # Floating point
248        refvalue = np.float32(ieee_int_to_float(refvalue)*10**-dsf)
249    elif datatype == 1: # Integer
250        refvalue = np.int32(ieee_int_to_float(refvalue)*10**-dsf)
251    # Upack each byte starting at byte 15 to end of the local use 
252    # section, create a binary string and append to the full 
253    # binary string.
254    b = ''
255    for i in range(15,len(lus)):
256        iword = struct.unpack('>B',lus[i:i+1])[0]
257        b += bin(iword).split('b')[1].zfill(8)
258    # Iterate over the binary string (b). For each nbits
259    # chunk, convert to an integer, including the refvalue,
260    # and then convert the int to an ASCII character, then
261    # concatenate to wxstring.
262    wxstring = ''
263    for i in range(0,len(b),nbits):
264        wxstring += chr(int(b[i:i+nbits],2)+refvalue)
265    # Return string as list, split by null character.
266    return list(filter(None,wxstring.split('\0')))

Decode GRIB2 Local Use Section to obtain NDFD/MDL Weather Strings. The decode procedure is defined here:

https://vlab.noaa.gov/web/mdl/nbm-gmos-grib2-wx-info

Parameters

lus: GRIB2 Local Use Section containing NDFD weather strings.

Returns

list: List of NDFD weather strings.

def get_wgrib2_prob_string(probtype, sfacl, svall, sfacu, svalu):
269def get_wgrib2_prob_string(probtype,sfacl,svall,sfacu,svalu):
270    """
271    Return a wgrib2-formatted string explaining probabilistic
272    threshold informaiton.  Logic from wgrib2 source, [Prob.c](https://github.com/NOAA-EMC/NCEPLIBS-wgrib2/blob/develop/wgrib2/Prob.c),
273    is replicated here.
274
275    Parameters
276    ----------
277
278    **`probtype`**: `int` type of probability (Code Table 4.9).
279
280    **`sfacl`**: `int` scale factor of lower limit.
281
282    **`svall`**: `int` scaled value of lower limit.
283
284    **`sfacu`**: `int` scale factor of upper limit.
285
286    **`svalu`**: `int` scaled value of upper limit.
287
288    Returns
289    -------
290
291    **`str`**: wgrib2-formatted string of probability threshold.
292    """
293    probstr = ''
294    lower = svall/(10**sfacl)
295    upper = svalu/(10**sfacu)
296    if probtype == 0:
297        probstr = 'prob <%g' % (lower)
298    elif probtype == 1:
299        probstr = 'prob >%g' % (upper)
300    elif probtype == 2:
301        if lower == upper:
302            probstr = 'prob =%g' % (lower)
303        else:
304            probstr = 'prob >=%g <%g' % (lower,upper)
305    elif probtype == 3:
306        probstr = 'prob >%g' % (lower)
307    elif probtype == 4:
308        probstr = 'prob <%g' % (upper)
309    return probstr

Return a wgrib2-formatted string explaining probabilistic threshold informaiton. Logic from wgrib2 source, Prob.c, is replicated here.

Parameters

probtype: int type of probability (Code Table 4.9).

sfacl: int scale factor of lower limit.

svall: int scaled value of lower limit.

sfacu: int scale factor of upper limit.

svalu: int scaled value of upper limit.

Returns

str: wgrib2-formatted string of probability threshold.