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

Fix MERRA-2 access #630

Merged
merged 13 commits into from
Feb 20, 2024
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Latest Changes
### Fixed
* [620](https://github.com/dbekaert/RAiDER/issues/620) - Fix MERRA-2 access because API was updated


## [0.4.7]
### Fixed
Expand Down
2 changes: 1 addition & 1 deletion test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
WM_DIR = os.path.join(TEST_DIR, 'weather_files')
ORB_DIR = os.path.join(TEST_DIR, 'orbit_files')

WM = 'GMAO'
WM = 'MERRA2'

@contextmanager
def pushd(dir):
Expand Down
8 changes: 4 additions & 4 deletions test/test_GUNW.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def test_azimuth_timing_interp_against_center_time_interp(weather_model_name: st
assert np.nanmax(abs_diff_mm) < 1


@pytest.mark.parametrize('weather_model_name', ['GMAO', 'HRRR', 'HRES', 'ERA5', 'ERA5'])
@pytest.mark.parametrize('weather_model_name', ['HRRR', 'HRES', 'ERA5', 'ERA5T'])
def test_check_weather_model_availability(test_gunw_path_factory, weather_model_name, mocker):
# Should be True for all weather models
# S1-GUNW-D-R-071-tops-20200130_20200124-135156-34956N_32979N-PP-913f-v2_0_4.nc
Expand All @@ -288,12 +288,12 @@ def test_check_weather_model_availability(test_gunw_path_factory, weather_model_
mocker.patch("RAiDER.aria.prepFromGUNW.get_acq_time_from_slc_id", side_effect=[pd.Timestamp('2015-01-01'),
pd.Timestamp('2014-01-01')])
cond = check_weather_model_availability(test_gunw_path, weather_model_name)
if weather_model_name in ['HRRR', 'GMAO']:
if weather_model_name in ['HRRR', 'MERRA2']:
cond = not cond
assert cond


@pytest.mark.parametrize('weather_model_name', ['GMAO', 'HRRR'])
@pytest.mark.parametrize('weather_model_name', ['HRRR'])
def test_check_weather_model_availability_over_alaska(test_gunw_path_factory, weather_model_name, mocker):
# Should be True for all weather models
# S1-GUNW-D-R-059-tops-20230320_20220418-180300-00179W_00051N-PP-c92e-v2_0_6.nc
Expand All @@ -309,7 +309,7 @@ def test_check_weather_model_availability_over_alaska(test_gunw_path_factory, we
assert cond


@pytest.mark.parametrize('weather_model_name', ['HRRR', 'GMAO'])
@pytest.mark.parametrize('weather_model_name', ['HRRR'])
@pytest.mark.parametrize('location', ['california-t71', 'alaska'])
def test_weather_model_availability_integration_using_valid_range(location,
test_gunw_path_factory,
Expand Down
4 changes: 2 additions & 2 deletions tools/RAiDER/aria/prepFromGUNW.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from RAiDER.s1_orbits import ensure_orbit_credentials

## cube spacing in degrees for each model
DCT_POSTING = {'HRRR': 0.05, 'HRES': 0.10, 'GMAO': 0.10, 'ERA5': 0.10, 'ERA5T': 0.10}
DCT_POSTING = {'HRRR': 0.05, 'HRES': 0.10, 'GMAO': 0.10, 'ERA5': 0.10, 'ERA5T': 0.10, 'MERRA2': 0.1}


def _get_acq_time_from_gunw_id(gunw_id: str, reference_or_secondary: str) -> datetime:
Expand Down Expand Up @@ -95,7 +95,7 @@ def check_weather_model_availability(gunw_path: str,
----------
gunw_path : str
weather_model_name : str
Should be one of 'HRRR', 'HRES', 'ERA5', 'ERA5T', 'GMAO'.
Should be one of 'HRRR', 'HRES', 'ERA5', 'ERA5T', 'GMAO', 'MERRA2'.
Returns
-------
bool:
Expand Down
4 changes: 3 additions & 1 deletion tools/RAiDER/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
from .gmao import GMAO
from .hres import HRES
from .hrrr import HRRR, HRRRAK
from .merra2 import MERRA2
from .ncmr import NCMR

__all__ = ['HRRR', 'HRRRAK', 'GMAO', 'ERA5', 'ERA5T', 'HRES']
__all__ = ['HRRR', 'HRRRAK', 'GMAO', 'ERA5', 'ERA5T', 'HRES', 'MERRA2']
4 changes: 3 additions & 1 deletion tools/RAiDER/models/allowed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
'ERA5T',
'HRRR',
'GMAO',
'HRES'
'HRES',
'MERRA2',
'NCMR',
]
4 changes: 2 additions & 2 deletions tools/RAiDER/models/gmao.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from RAiDER.models.weatherModel import WeatherModel, TIME_RES
from RAiDER.logger import logger
from RAiDER.utilFcns import writeWeatherVars2NETCDF4, round_date, requests_retry_session
from RAiDER.utilFcns import writeWeatherVarsXarray, round_date, requests_retry_session
from RAiDER.models.model_levels import (
LEVELS_137_HEIGHTS,
)
Expand Down Expand Up @@ -145,7 +145,7 @@ def _fetch(self, out):

try:
# Note that lat/lon gets written twice for GMAO because they are the same as y/x
writeWeatherVars2NETCDF4(self, lats, lons, h, qv, p, t, outName=out)
writeWeatherVarsXarray(lat, lon, h, q, p, t, dt, crs, outName=None, NoDataValue=None, chunk=(1, 91, 144))
except Exception:
logger.exception("Unable to save weathermodel to file")

Expand Down
73 changes: 24 additions & 49 deletions tools/RAiDER/models/merra2.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
import xarray

import datetime as dt
import numpy as np
import pydap.cas.urs
Expand All @@ -8,7 +10,7 @@

from RAiDER.models.weatherModel import WeatherModel
from RAiDER.logger import logger
from RAiDER.utilFcns import writeWeatherVars2NETCDF4, read_EarthData_loginInfo
from RAiDER.utilFcns import writeWeatherVarsXarray, read_EarthData_loginInfo
from RAiDER.models.model_levels import (
LEVELS_137_HEIGHTS,
)
Expand Down Expand Up @@ -94,6 +96,8 @@ def _fetch(self, out):
self._lon_res
)

lon, lat = np.meshgrid(lons, lats)

if time.year < 1992:
url_sub = 100
elif time.year < 2001:
Expand All @@ -107,39 +111,22 @@ def _fetch(self, out):
DT = time - T0
time_ind = int(DT.total_seconds() / 3600.0 / 3.0)

ml_min = 0
ml_max = 71

# Earthdata credentials
earthdata_usr, earthdata_pwd = read_EarthData_loginInfo(EARTHDATA_RC)

# open the dataset and pull the data
url = 'https://goldsmr5.gesdisc.eosdis.nasa.gov:443/opendap/MERRA2/M2I3NVASM.5.12.4/' + time.strftime('%Y/%m') + '/MERRA2_' + str(url_sub) + '.inst3_3d_asm_Nv.' + time.strftime('%Y%m%d') + '.nc4'
url = 'https://goldsmr5.gesdisc.eosdis.nasa.gov/opendap/MERRA2/M2T3NVASM.5.12.4/' + time.strftime('%Y/%m') + '/MERRA2_' + str(url_sub) + '.tavg3_3d_asm_Nv.' + time.strftime('%Y%m%d') + '.nc4'

session = pydap.cas.urs.setup_session(earthdata_usr, earthdata_pwd, check_url=url)
ds = pydap.client.open_url(url, session=session)
stream = pydap.client.open_url(url, session=session)

############# The MERRA-2 server changes the pydap data retrieval format frequently between these two formats; so better to retain both of them rather than only using either one of them #############
try:
q = ds['QV'].data[0][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
p = ds['PL'].data[0][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
t = ds['T'].data[0][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
h = ds['H'].data[0][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
except IndexError:
q = ds['QV'].data[time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
p = ds['PL'].data[time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
t = ds['T'].data[time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
h = ds['H'].data[time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
except AttributeError:
q = ds['QV'][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
p = ds['PL'][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
t = ds['T'][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
h = ds['H'][time_ind, ml_min:(ml_max + 1), lat_min_ind:(lat_max_ind + 1), lon_min_ind:(lon_max_ind + 1)][0]
except BaseException:
logger.exception("MERRA-2: Unable to read weathermodel data")
########################################################################################################################
q = stream['QV'][0,:,lat_min_ind:lat_max_ind + 1, lon_min_ind:lon_max_ind + 1].data.squeeze()
p = stream['PL'][0,:,lat_min_ind:lat_max_ind + 1, lon_min_ind:lon_max_ind + 1].data.squeeze()
t = stream['T'][0,:,lat_min_ind:lat_max_ind + 1, lon_min_ind:lon_max_ind + 1].data.squeeze()
h = stream['H'][0,:,lat_min_ind:lat_max_ind + 1, lon_min_ind:lon_max_ind + 1].data.squeeze()

try:
writeWeatherVars2NETCDF4(self, lats, lons, h, q, p, t, outName=out)
writeWeatherVarsXarray(lat, lon, h, q, p, t, time, self._proj, outName=out)
except Exception as e:
logger.debug(e)
logger.exception("MERRA-2: Unable to save weathermodel to file")
Expand All @@ -161,51 +148,39 @@ def _load_model_level(self, filename):
'''

# adding the import here should become absolute when transition to netcdf
from netCDF4 import Dataset
with Dataset(filename, mode='r') as f:
lons = np.array(f.variables['x'][:])
lats = np.array(f.variables['y'][:])
h = np.array(f.variables['H'][:])
q = np.array(f.variables['QV'][:])
p = np.array(f.variables['PL'][:])
t = np.array(f.variables['T'][:])

# restructure the 3-D lat/lon/h in regular grid
_lons = np.broadcast_to(lons[np.newaxis, np.newaxis, :], t.shape)
_lats = np.broadcast_to(lats[np.newaxis, :, np.newaxis], t.shape)
ds = xarray.load_dataset(filename)
lons = ds['longitude'].values
lats = ds['latitude'].values
h = ds['h'].values
q = ds['q'].values
p = ds['p'].values
t = ds['t'].values

# Re-structure everything from (heights, lats, lons) to (lons, lats, heights)
p = np.transpose(p)
q = np.transpose(q)
t = np.transpose(t)
h = np.transpose(h)
_lats = np.transpose(_lats)
_lons = np.transpose(_lons)

# check this
# data cube format should be lats,lons,heights
p = p.swapaxes(0, 1)
q = q.swapaxes(0, 1)
t = t.swapaxes(0, 1)
h = h.swapaxes(0, 1)
_lats = _lats.swapaxes(0, 1)
_lons = _lons.swapaxes(0, 1)

# For some reason z is opposite the others
p = np.flip(p, axis=2)
q = np.flip(q, axis=2)
t = np.flip(t, axis=2)
h = np.flip(h, axis=2)
_lats = np.flip(_lats, axis=2)
_lons = np.flip(_lons, axis=2)

# assign the regular-grid (lat/lon/h) variables

self._p = p
self._q = q
self._t = t
self._lats = _lats
self._lons = _lons
self._xs = _lons
self._ys = _lats
self._lats = lats
self._lons = lons
self._xs = lons
self._ys = lats
self._zs = h
6 changes: 3 additions & 3 deletions tools/RAiDER/models/ncmr.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from RAiDER.models.weatherModel import WeatherModel, TIME_RES
from RAiDER.logger import logger
from RAiDER.utilFcns import (
writeWeatherVars2NETCDF4,
read_NCMR_loginInfo,
show_progress
show_progress,
writeWeatherVarsXarray,
)
from RAiDER.models.model_levels import (
LEVELS_137_HEIGHTS,
Expand Down Expand Up @@ -174,7 +174,7 @@ def _download_ncmr_file(self, out, date_time, bounding_box):
########################################################################################################################

try:
writeWeatherVars2NETCDF4(self, lats, lons, hgt, q, p, t, outName=out)
writeWeatherVarsXarray(lats, lons, hgt, q, p, t, self._time, self._proj, outName=out)
except Exception:
logger.exception("Unable to save weathermodel to file")

Expand Down
2 changes: 1 addition & 1 deletion tools/RAiDER/models/weatherModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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
robmax, robmin, writeWeatherVarsXarray, calcgeoh, transform_coords, clip_bbox
)

TIME_RES = {'GMAO': 3,
Expand Down
Loading
Loading