Source code for ocr.conus404
from typing import cast
import xarray as xr
from xclim import convert
[docs]
def load_conus404(add_spatial_constants: bool = True) -> xr.Dataset:
"""
Load the CONUS 404 dataset.
Parameters
----------
add_spatial_constants : bool, optional
If True, adds spatial constant variables (SINALPHA, COSALPHA) to the dataset.
Returns
-------
ds : xr.Dataset
The CONUS 404 dataset.
"""
from ocr import catalog
variables = ['PSFC', 'Q2', 'T2', 'TD2', 'U10', 'V10']
dsets = []
for variable in variables:
dset = (
catalog.get_dataset(f'conus404-hourly-{variable}').to_xarray().drop_vars(['lat', 'lon'])
)
dsets.append(dset)
ds = xr.merge(dsets)
if add_spatial_constants:
INPUT_ZARR_STORE_CONFIG = {
'url': 's3://hytest/conus404/conus404_hourly.zarr',
'storage_options': {
'anon': True,
'client_kwargs': {'endpoint_url': 'https://usgs.osn.mghpcc.org/'},
},
}
variables = ['SINALPHA', 'COSALPHA', 'crs']
spatial_constant_ds = xr.open_dataset(
INPUT_ZARR_STORE_CONFIG['url'],
storage_options=INPUT_ZARR_STORE_CONFIG['storage_options'],
engine='zarr',
chunks={},
)[variables]
ds = xr.merge([ds, spatial_constant_ds])
# add lat, lon back in
dset = catalog.get_dataset('conus404-hourly-T2').to_xarray()
ds = ds.assign_coords(lat=dset['lat'], lon=dset['lon'])
if 'crs' in ds:
ds = ds.set_coords(['crs'])
return ds
[docs]
def compute_relative_humidity(ds: xr.Dataset) -> xr.DataArray:
"""
Compute relative humidity from specific humidity, temperature, and pressure.
Parameters
----------
ds : xr.Dataset
Input dataset containing 'Q2' (specific humidity), 'T2' (temperature in K), and 'PSFC' (pressure in Pa).
Returns
-------
hurs : xr.DataArray
Relative humidity as a percentage.
"""
with xr.set_options(keep_attrs=True):
hurs = convert.relative_humidity_from_dewpoint(tas=ds['T2'], tdps=ds['TD2'])
hurs = cast(xr.DataArray, hurs)
hurs.name = 'hurs'
return hurs
[docs]
def rotate_winds_to_earth(ds: xr.Dataset) -> tuple[xr.DataArray, xr.DataArray]:
"""Rotate grid-relative 10 m winds (U10,V10) to earth-relative components.
Uses SINALPHA / COSALPHA convention from WRF.
Parameters
----------
ds : xr.Dataset
Input dataset containing 'U10', 'V10', 'SINALPHA', and 'COSALPHA'.
Returns
-------
earth_u : xr.DataArray
Earth-relative U component of wind at 10 m.
earth_v : xr.DataArray
Earth-relative V component of wind at 10 m.
"""
# rotate the grid-relative winds to earth-relative winds: https://forum.mmm.ucar.edu/threads/rotating-wrf-u-and-v-winds-before-and-after-reprojection.11788/
# earth_u = u*cosa(ix,iy)-v*sina(ix,iy)
# earth_v = v*cosa(ix,iy)+u*sina(ix,iy)
with xr.set_options(keep_attrs=True):
earth_u = ds.U10 * ds.COSALPHA - ds.V10 * ds.SINALPHA
earth_v = ds.V10 * ds.COSALPHA + ds.U10 * ds.SINALPHA
earth_u.name = 'u10_earth'
earth_v.name = 'v10_earth'
return earth_u, earth_v
[docs]
def compute_wind_speed_and_direction(u10: xr.DataArray, v10: xr.DataArray) -> xr.Dataset:
"""Derive hourly wind speed (m/s) and direction (degrees from) using xclim.
Parameters
----------
u10 : xr.DataArray
U component of wind at 10 m (m/s).
v10 : xr.DataArray
V component of wind at 10 m (m/s).
Returns
-------
wind_ds : xr.Dataset
Dataset containing wind speed ('sfcWind') and wind direction ('sfcWindfromdir').
"""
winds = convert.wind_speed_from_vector(uas=u10, vas=v10)
winds = cast(tuple[xr.DataArray, xr.DataArray], winds)
# xclim returns a tuple-like (speed, direction). Merge keeps names (sfcWind, sfcWindfromdir)
wind_ds = xr.merge(winds)
return wind_ds