Skip to content

Commit

Permalink
add custom exceptions for better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jlmaurer committed Sep 22, 2023
1 parent 004f3b0 commit 13ae10e
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 43 deletions.
34 changes: 21 additions & 13 deletions tools/RAiDER/cli/raider.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
from RAiDER.cli.parser import add_out, add_cpus, add_verbose
from RAiDER.cli.validators import DateListAction, date_type
from RAiDER.models.allowed import ALLOWED_MODELS
from RAiDER.models.customExceptions import *
from RAiDER.utilFcns import get_dt
from RAiDER.s1_azimuth_timing import get_s1_azimuth_time_grid, get_inverse_weights_for_dates, get_times_for_azimuth_interpolation


import traceback

TIME_INTERPOLATION_METHODS = ['none', 'center_time', 'azimuth_time_grid']


HELP_MESSAGE = """
Command line options for RAiDER processing. Default options can be found by running
Expand Down Expand Up @@ -269,26 +272,31 @@ def calcDelays(iargs=None):
wfiles = []
for tt in times:
try:
wfile = RAiDER.processWM.prepareWeatherModel(model,
tt,
aoi.bounds(),
makePlots=params['verbose'])
wfile = RAiDER.processWM.prepareWeatherModel(
model,
tt,
aoi.bounds(),
makePlots=params['verbose']
)
wfiles.append(wfile)

# catch when requested datetime fails
except RuntimeError:
continue
except TryToKeepGoingError:
if interp_method in ['azimuth_time_grid', 'none']:
raise DatetimeFailed(model.Model(), tt)
else:
continue

# catch when something else within weather model class fails
# log when something else happens and then re-raise the error
except Exception as e:
S, N, W, E = wm_bounds
logger.info(f'Weather model point bounds are {S:.2f}/{N:.2f}/{W:.2f}/{E:.2f}')
logger.info(f'Query datetime: {tt}')
msg = f'Downloading and/or preparation of {model._Name} failed.'
logger.error(e)
logger.error('Weather model files are: {}'.format(wfiles))
logger.error(msg)
if interp_method == 'azimuth_time_grid':
break
raise

# dont process the delays for download only
if dl_only:
continue
Expand Down Expand Up @@ -478,7 +486,7 @@ def calcDelaysGUNW(iargs: list[str] = None) -> xr.Dataset:

p.add_argument(
'-interp', '--interpolate-time', default='azimuth_time_grid', type=str,
choices=['none', 'center_time', 'azimuth_time_grid'],
choices=TIME_INTERPOLATION_METHODS,
help=('How to interpolate across model time steps. Possible options are: '
'[\'none\', \'center_time\', \'azimuth_time_grid\'] '
'None: means nearest model time; center_time: linearly across center time; '
Expand Down Expand Up @@ -595,13 +603,13 @@ def getWeatherFile(wfiles, times, t, interp_method='none'):
'''

# time interpolation method: number of expected files
ALLOWED_METHODS = {'none': 1, 'center_time': 2, 'azimuth_time_grid': 3}
EXPECTED_NUM_FILES = {'none': 1, 'center_time': 2, 'azimuth_time_grid': 3}

Nfiles = len(wfiles)
Ntimes = len(times)

try:
Nfiles_expected = ALLOWED_METHODS[interp_method]
Nfiles_expected = EXPECTED_NUM_FILES[interp_method]
except KeyError:
raise ValueError('getWeatherFile: interp_method {} is not known'.format(interp_method))

Expand Down
39 changes: 39 additions & 0 deletions tools/RAiDER/models/customExceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class DatetimeFailed(Exception):
def __init__(self, model, time):
msg = f"Weather model {model} failed to download for datetime {time}"
super().__init__(msg)


class DatetimeNotAvailable(Exception):
def __init__(self, model, time):
msg = f"Weather model {model} was not found for datetime {time}"
super().__init__(msg)


class DatetimeOutsideRange(Exception):
def __init__(self, model, time):
msg = f"Time {time} is outside the available date range for weather model {model}"
super().__init__(msg)


class ExistingWeatherModelTooSmall(Exception):
def __init__(self):
msg = 'The weather model passed does not cover all of the input ' \
'points; you may need to download a larger area.'
super().__init__(msg)


class TryToKeepGoingError(Exception):
def __init__(self, date=None):
if date is not None:
msg = 'The weather model does not exist for date {date}, so I will try to use the closest available date.'
else:
msg = 'I will try to keep going'
super().__init__(msg)


class CriticalError(Exception):
def __init__(self):
msg = 'I have experienced a critical error, please take a look at the log files'
super().__init__(msg)

20 changes: 6 additions & 14 deletions tools/RAiDER/models/weatherModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from RAiDER.interpolator import fillna3D
from RAiDER.logger import logger
from RAiDER.models import plotWeather as plots, weatherModel
from RAiDER.models.customExceptions import *
from RAiDER.utilFcns import (
robmax, robmin, write2NETCDF4core, calcgeoh, transform_coords, clip_bbox
)
Expand All @@ -31,7 +32,6 @@
'HRRR-AK': 3,
}


class WeatherModel(ABC):
'''
Implement a generic weather model for getting estimated SAR delays
Expand Down Expand Up @@ -160,12 +160,9 @@ def fetch(self, out, time):
# write the error raised by the weather model API to the log
try:
self._fetch(out)
err = False

except Exception as E:
err = E

return err
logger.exception(E)
raise


@abstractmethod
Expand Down Expand Up @@ -308,22 +305,17 @@ def checkTime(self, time):
self.Model(), self._valid_range[0].date(), end_time
)

msg = f"Weather model {self.Model()} is not available at: {time}"

if time < self._valid_range[0]:
logger.error(msg)
raise RuntimeError(msg)
raise DatetimeOutsideRange(self.Model(), time)

if self._valid_range[1] is not None:
if self._valid_range[1] == 'Present':
pass
elif self._valid_range[1] < time:
logger.error(msg)
raise RuntimeError(msg)
raise DatetimeOutsideRange(self.Model(), time)

if time > datetime.datetime.utcnow() - self._lag_time:
logger.error(msg)
raise RuntimeError(msg)
raise DatetimeOutsideRange(self.Model(), time)


def setLevelType(self, levelType):
Expand Down
27 changes: 11 additions & 16 deletions tools/RAiDER/processWM.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from RAiDER.logger import logger
from RAiDER.utilFcns import getTimeFromFile
from RAiDER.models.weatherModel import make_raw_weather_data_filename, checkContainment_raw

from RAiDER.models.customExceptions import *

def prepareWeatherModel(
weather_model,
Expand Down Expand Up @@ -67,10 +67,10 @@ def prepareWeatherModel(

# if no weather model files supplied, check the standard location
else:
E = weather_model.fetch(path_wm_raw, time)
if E:
logger.warning (E)
raise RuntimeError
try:
weather_model.fetch(path_wm_raw, time)
except DatetimeOutsideRange:
raise TryToKeepGoingError

# If only downloading, exit now
if download_only:
Expand All @@ -89,11 +89,9 @@ def prepareWeatherModel(
)

containment = weather_model.checkContainment(ll_bounds)
if not containment and weather_model.Model() in 'GMAO ERA5 ERA5T HRES'.split():
msg = 'The weather model passed does not cover all of the input ' \
'points; you may need to download a larger area.'
logger.error(msg)
raise RuntimeError(msg)
if not containment and weather_model.Model() not in 'HRRR'.split():
raise ExistingWeatherModelTooSmall

return f

# Logging some basic info
Expand Down Expand Up @@ -131,17 +129,14 @@ def prepareWeatherModel(
except Exception as e:
logger.exception("Unable to save weathermodel to file")
logger.exception(e)
raise RuntimeError("Unable to save weathermodel to file")
raise CriticalError

finally:
wm = weather_model.Model()
del weather_model

if not containment and wm in 'GMAO ERA5 ERA5T HRES'.split():
msg = 'The weather model passed does not cover all of the input ' \
'points; you may need to download a larger area.'
logger.error(msg)
raise RuntimeError(msg)
if not containment and wm not in 'HRRR'.split():
raise ExistingWeatherModelTooSmall
else:
return f

Expand Down

0 comments on commit 13ae10e

Please sign in to comment.