11import datetime as dt
2- import os
32from pathlib import Path
3+ from typing import List , Tuple , Union , cast
44
5+ from RAiDER .types import BB , Array2D , FloatArray2D
56import geopandas as gpd
7+ import herbie
8+ import herbie .accessors
69import numpy as np
710import xarray as xr
8- from herbie import Herbie
911from pyproj import CRS , Transformer
1012from shapely .geometry import Polygon , box
11- from typing import Optional , Union , List , Tuple
1213
1314from RAiDER .logger import logger
1415from RAiDER .models .customExceptions import NoWeatherModelData
2930
3031def check_hrrr_dataset_availability (datetime : dt .datetime , model = 'hrrr' ) -> bool :
3132 """Note a file could still be missing within the models valid range."""
32- herbie = Herbie (
33+ h = herbie . Herbie (
3334 datetime ,
3435 model = model ,
3536 product = 'nat' ,
3637 fxx = 0 ,
3738 )
38- return herbie .grib_source is not None
39+ return h .grib_source is not None
3940
4041
4142def download_hrrr_file (ll_bounds , DATE , out : Path , model = 'hrrr' , product = 'nat' , fxx = 0 , verbose = False ) -> None :
@@ -54,7 +55,7 @@ def download_hrrr_file(ll_bounds, DATE, out: Path, model='hrrr', product='nat',
5455 Returns:
5556 None, writes data to a netcdf file
5657 """
57- herbie = Herbie (
58+ h = herbie . Herbie (
5859 DATE .strftime ('%Y-%m-%d %H:%M' ),
5960 model = model ,
6061 product = product ,
@@ -66,22 +67,27 @@ def download_hrrr_file(ll_bounds, DATE, out: Path, model='hrrr', product='nat',
6667
6768 # Iterate through the list of datasets
6869 try :
69- ds_list = herbie .xarray (':(SPFH|PRES|TMP|HGT):' , verbose = verbose )
70+ # cast: Herbie.xarray can return one Dataset or a list
71+ ds_list = cast (
72+ Union [xr .Dataset , List [xr .Dataset ]],
73+ h .xarray (':(SPFH|PRES|TMP|HGT):' , verbose = verbose ),
74+ )
75+ assert isinstance (ds_list , list )
7076 except ValueError as e :
7177 logger .error (e )
7278 raise
7379
7480 # Note order coord names are request for `test_HRRR_ztd` matters
7581 # when both coord names are retreived by Herbie is ds_list possibly in
7682 # Different orders on different machines; `hybrid` is what is expected for the test.
77- ds_list_filt_0 = [ds for ds in ds_list if 'hybrid' in ds ._coord_names ]
78- ds_list_filt_1 = [ds for ds in ds_list if 'isobaricInhPa' in ds ._coord_names ]
79- if ds_list_filt_0 :
83+ ds_list_filt_0 = [ds for ds in ds_list if 'hybrid' in ds .coords ]
84+ ds_list_filt_1 = [ds for ds in ds_list if 'isobaricInhPa' in ds .coords ]
85+ if len ( ds_list_filt_0 ) > 0 :
8086 ds_out = ds_list_filt_0 [0 ]
8187 coord = 'hybrid'
8288 # I do not think that this coord name will result in successful processing nominally as variables are
8389 # gh,gribfile_projection for test_HRRR_ztd
84- elif ds_list_filt_1 :
90+ elif len ( ds_list_filt_1 ) > 0 :
8591 ds_out = ds_list_filt_1 [0 ]
8692 coord = 'isobaricInhPa'
8793 else :
@@ -91,22 +97,25 @@ def download_hrrr_file(ll_bounds, DATE, out: Path, model='hrrr', product='nat',
9197 try :
9298 x_min , x_max , y_min , y_max = get_bounds_indices (
9399 ll_bounds ,
94- ds_out . latitude .to_numpy (),
95- ds_out . longitude .to_numpy (),
100+ ds_out [ ' latitude' ] .to_numpy (),
101+ ds_out [ ' longitude' ] .to_numpy (),
96102 )
97103 except NoWeatherModelData as e :
98104 logger .error (e )
99- logger .error ('lat/lon bounds: %s' , ll_bounds )
100- logger .error ('Weather model is {}' . format ( model ) )
105+ logger .error (f 'lat/lon bounds: { ll_bounds } ' )
106+ logger .error (f 'Weather model is { model } ' )
101107 raise
102108
103109 # bookkeepping
104110 ds_out = ds_out .rename ({'gh' : 'z' , coord : 'levels' })
105111
112+ # Herbie injects brainworms into all its xarray Datasets
113+ crs = cast (herbie .accessors .HerbieAccessor , ds_out .herbie ).crs
114+
106115 # projection information
107116 ds_out ['proj' ] = 0
108- for k , v in CRS .from_user_input (ds_out . herbie . crs ).to_cf ().items ():
109- ds_out . proj .attrs [k ] = v
117+ for k , v in CRS .from_user_input (crs ).to_cf ().items ():
118+ ds_out [ ' proj' ] .attrs [k ] = v
110119 for var in ds_out .data_vars :
111120 ds_out [var ].attrs ['grid_mapping' ] = 'proj'
112121
@@ -134,40 +143,37 @@ def download_hrrr_file(ll_bounds, DATE, out: Path, model='hrrr', product='nat',
134143 ds_sub .to_netcdf (out )
135144
136145
137- def get_bounds_indices (SNWE , lats , lons ) :
146+ def get_bounds_indices (snwe : BB . SNWE , lats : FloatArray2D , lons : FloatArray2D ) -> BB . WSEN :
138147 """Convert SNWE lat/lon bounds to index bounds."""
139148 # Unpack the bounds and find the relevent indices
140- S , N , W , E = SNWE
149+ s , n , w , e = snwe
141150
142151 # Need to account for crossing the international date line
143- if W < E :
144- m1 = (S <= lats ) & (N >= lats ) & (W <= lons ) & (E >= lons )
145- else :
152+ if w >= e :
146153 raise ValueError (
147- 'Longitude is either flipped or you are crossing the international date line;'
148- + 'if the latter please give me longitudes from 0-360'
154+ 'Longitude is either flipped or you are crossing the international date line; '
155+ 'if the latter please give me longitudes from 0-360'
149156 )
150157
151- if np .sum (m1 ) == 0 :
158+ m1 : Array2D [np .bool ] = (s <= lats ) & (n >= lats ) & (w <= lons ) & (e >= lons )
159+ if np .sum (m1 ) == 0 : # All False
152160 lons = np .mod (lons , 360 )
153- W , E = np .mod ([W , E ], 360 )
154- m1 = (S <= lats ) & (N >= lats ) & (W <= lons ) & (E >= lons )
155- if np .sum (m1 ) == 0 :
161+ w , e = np .mod ([w , e ], 360 )
162+ # try again
163+ m1 = (s <= lats ) & (n >= lats ) & (w <= lons ) & (e >= lons )
164+ if np .sum (m1 ) == 0 : # All False
156165 raise NoWeatherModelData ('Area of Interest has no overlap with the HRRR model available extent' )
157166
158167 # Y extent
159- shp = lats .shape
160- m1_y = np .argwhere (np .sum (m1 , axis = 1 ) != 0 )
161- y_min = max (m1_y [0 ][0 ], 0 )
162- y_max = min (m1_y [- 1 ][0 ], shp [0 ])
163- m1_y = None
168+ m1_y : Array2D [np .integer ] = np .argwhere (np .sum (m1 , axis = 1 ) != 0 )
169+ y_min : int = max (m1_y [0 ][0 ], 0 )
170+ y_max : int = min (m1_y [- 1 ][0 ], lats .shape [0 ])
171+ del m1_y # big
164172
165173 # X extent
166- m1_x = np .argwhere (np .sum (m1 , axis = 0 ) != 0 )
167- x_min = max (m1_x [0 ][0 ], 0 )
168- x_max = min (m1_x [- 1 ][0 ], shp [1 ])
169- m1_x = None
170- m1 = None
174+ m1_x : Array2D [np .integer ] = np .argwhere (np .sum (m1 , axis = 0 ) != 0 )
175+ x_min : int = max (m1_x [0 ][0 ], 0 )
176+ x_max : int = min (m1_x [- 1 ][0 ], lats .shape [1 ])
171177
172178 return x_min , x_max , y_min , y_max
173179
0 commit comments