AORC Data#

DayMet’s API has recently gone down, leading us to experiment with a new type of met data, AORC, which is the dataset used to drive the National Water Model.

%load_ext autoreload
%autoreload 2

AORC provides gridded meteorological data for the conterminous US and Alaska at ~800m spatial resolution with hourly timestep from 1979 (Conterminous US) or 1981 (Alaska) ~ present. website

Available variables:

Variable

Description (units)

APCP_surface

Total precipitation [mm]

DLWRF_surface

Incident longwave radiation flux density [W m^-2]

DSWRF_surface

Incident shortwave radiation flux density [W m^-2]

PRES_surface

Air pressure [Pa]

SPFH_2maboveground

Specific Humidity [g/g]

TMP_2maboveground

Air temperature [°C]

UGRD_10maboveground

West-East component of the wind velocity [m s^-1]

VGRD_10maboveground

North-South component of the wind velocity [m s^-1]

Notes:

  • AORC, unlike DayMet, provides all Gregorian calendar days

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['figure.dpi'] = 150
import logging
import numpy as np
import os
import cftime, datetime

import watershed_workflow
import watershed_workflow.ui
import watershed_workflow.sources
import watershed_workflow.meteorology
import watershed_workflow.io

watershed_workflow.ui.setup_logging(1,None)
coweeta_shapefile = 'Coweeta/input_data/coweeta_basin.shp'
crs = watershed_workflow.crs.default_crs

Load the Watershed#

coweeta_source = watershed_workflow.sources.ManagerShapefile(coweeta_shapefile, id_name='BASIN_CODE')
coweeta = coweeta_source.getShapes(out_crs=crs)

watershed = watershed_workflow.SplitHUCs(coweeta)
watershed_workflow.split_hucs.simplify(watershed, 60)

Download the raw DayMet raster#

returned raw data has dim(nband, ncol, nrow)

startdate = "2010-1-1"
enddate = "2013-12-31"
# setting vars = None to download all available variables
source = watershed_workflow.sources.ManagerAORC()
met_data = source.getDataset(coweeta, start=startdate, end=enddate)
Variable size: 79.2 TB
<xarray.Dataset> Size: 79TB
Dimensions:              (time: 35064, latitude: 4201, longitude: 8401)
Coordinates:
  * latitude             (latitude) float64 34kB 20.0 20.01 20.02 ... 54.99 55.0
  * longitude            (longitude) float64 67kB -130.0 -130.0 ... -60.01 -60.0
  * time                 (time) datetime64[ns] 281kB 2010-01-01 ... 2013-12-3...
Data variables:
    APCP_surface         (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    DLWRF_surface        (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    DSWRF_surface        (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    PRES_surface         (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    SPFH_2maboveground   (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    TMP_2maboveground    (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    UGRD_10maboveground  (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
    VGRD_10maboveground  (time, latitude, longitude) float64 10TB dask.array<chunksize=(144, 128, 256), meta=np.ndarray>
Subsetting:
  lon: (np.float64(-83.4868), np.float64(-83.4133))
  lat: (np.float64(35.019), np.float64(35.0822))
Variable size: 0.142 GB

Warp the dataset#

AORC comes back as a non-projected dataset in lat-lon. We need to warp it to a projected dataset for use in ATS.

met_data_warped = watershed_workflow.warp.dataset(met_data, crs, 'bilinear')

Save to Disk#

NetCDF file#

By default, the raw data is saved to $DATA_DIR/meteorology/aorc/ in a netcdf file.

Write to ATS format#

This will write daymet in a format that ATS can read. E.g., this will partition precipitation into rain and snow, convert vapor pressure to relative humidity, get mean air temperature and so on.

  • dout has dims of (ntime, nrow, ncol) or (ntime, ny, nx)

# convert the data to ATS units, partitioning precip into rain and snow, and computing daily averages
met_data_ats = watershed_workflow.meteorology.convertAORCToATS(met_data_warped)

filename = os.path.join('Coweeta', 'output_data', 'watershed_daymet-ats.h5')
watershed_workflow.io.writeDatasetToHDF5(
    filename,
    met_data_ats,
    met_data_ats.attrs)
    
# plot one pixel as a function of time
fig = plt.figure()
ax = fig.add_subplot(121)

met_data_single_pixel = met_data_ats.isel({'time':slice(0,365),
                                           'x' : 5,
                                           'y' : 5})

met_data_single_pixel['precipitation rain [m s^-1]'].plot(color='b', label='rain')
met_data_single_pixel['precipitation snow [m SWE s^-1]'].plot(color='c', label='snow')
ax.set_ylabel('precip [m s^-1]')
ax.set_title('')
ax.legend()

ax = fig.add_subplot(122)
met_data_single_pixel['incoming shortwave radiation [W m^-2]'].plot(color='r', label='qSW_in')
ax.set_ylabel('incoming shortwave radiation [W m^-2]')
ax.set_title('')

plt.show()
../_images/a67e8a2ce79ee2c27a7707238e0a23ed5e39bd5f087569f6229ed4c8da8e36be.png

Smooth to form a typical year#

A “typical” year is commonly used in a cyclic, annual steady-state spinup. We form this by averaging all Jan 1s in the record, averaging all Jan 2s, etc.

met_data
<xarray.Dataset> Size: 142MB
Dimensions:              (time: 35041, latitude: 7, longitude: 9)
Coordinates:
  * latitude             (latitude) float64 56B 35.02 35.03 ... 35.07 35.07
  * longitude            (longitude) float64 72B -83.49 -83.48 ... -83.43 -83.42
  * time                 (time) object 280kB 2010-01-01 00:00:00 ... 2013-12-...
Data variables:
    APCP_surface         (time, latitude, longitude) float64 18MB ...
    DLWRF_surface        (time, latitude, longitude) float64 18MB ...
    DSWRF_surface        (time, latitude, longitude) float64 18MB ...
    PRES_surface         (time, latitude, longitude) float64 18MB ...
    SPFH_2maboveground   (time, latitude, longitude) float64 18MB ...
    TMP_2maboveground    (time, latitude, longitude) float64 18MB ...
    UGRD_10maboveground  (time, latitude, longitude) float64 18MB ...
    VGRD_10maboveground  (time, latitude, longitude) float64 18MB ...
Attributes:
    name:     AORC v1.1
    source:   NOAA AWS S3 Zarr
# aggregate to daily data
met_data_daily = met_data.resample(time=datetime.timedelta(hours=24)).mean()

ndays = met_data_daily['APCP_surface'].shape[0]
print('nyears = ', ndays // 365)
print('n days leftover = ', ndays % 365)
print('note, that leftover day is a leap day')
nyears =  4
n days leftover =  1
note, that leftover day is a leap day
# cannot compute a typical year for leap year -- need to align days-of-the-year

# remove leap day (Dec 31 of leap years)
met_data_noleap = watershed_workflow.data.filterLeapDay(met_data_daily)

# smooth the raw data
met_data_smooth = watershed_workflow.data.smoothTimeSeries(met_data_noleap, method='savgol', window_length=181, polyorder=2)

# then compute a "typical" year
met_data_typical = watershed_workflow.data.computeAverageYear(met_data_smooth, 'time', 2010, 2)

# then reproject
met_data_typical_warped = watershed_workflow.warp.dataset(met_data_typical, crs, 'bilinear')

# and lastly convert to ATS
met_data_typical_ats = watershed_workflow.meteorology.convertAORCToATS(met_data_typical_warped)
# plot one pixel as a function of time
fig = plt.figure()
ax = fig.add_subplot(121)

met_data_single_pixel = met_data_typical_ats.isel({'time':slice(0,365),
                                           'x' : 5,
                                           'y' : 5})

met_data_single_pixel['precipitation rain [m s^-1]'].plot(color='b', label='rain')
met_data_single_pixel['precipitation snow [m SWE s^-1]'].plot(color='c', label='snow')
ax.set_ylabel('precip [m s^-1]')
ax.set_title('')
ax.legend()

ax = fig.add_subplot(122)
met_data_single_pixel['incoming shortwave radiation [W m^-2]'].plot(color='r', label='qSW_in')
ax.set_ylabel('incoming shortwave radiation [W m^-2]')
ax.set_title('')

plt.show()
../_images/64dffc21934baf27c963f0cfe3d08547df2d67e38b0d9d295850ce2feb2f5728.png

Other choices for Precip#

Often smoothing precip like that is a bad idea – you now have every day misting with low intensity rain which can result in a ton of interception and canopy evaporation and no transpiration. Another approach is to just take the median total rainfall year and repeat that year multiple times.

met_data_noleap['APCP_surface'].shape[0] % 365
0
precip_raw = met_data_noleap['APCP_surface']
shape_xy = precip_raw.shape[1:]

precip_raw = precip_raw.values.reshape((-1, 365,)+shape_xy)
annual_precip_raw = precip_raw.sum(axis=(1,2,3))
print(annual_precip_raw)
[4867.39590586 5486.27508175 5143.10007664 7351.33344288]
# find the median...
# note -- don't use np.median here... for even number of years it will not appear.  Instead, sort and take the halfway point
median_i = sorted(((i,v) for (i,v) in enumerate(annual_precip_raw)), key=lambda x : x[1])[len(annual_precip_raw)//2][0]

spatially_averaged_typical_year_precip = precip_raw[median_i]
spatially_averaged_typical_year_precip.shape
(365, 7, 9)
# stuff the median year into the typical data, tiled for number of years being generated (here 2)
typical_precip_raw = np.tile(precip_raw[median_i], (2,1,1))
met_data_typical['APCP_surface'] = (['time', 'latitude', 'longitude'], typical_precip_raw)
# then reproject
met_data_typical_warped = watershed_workflow.warp.dataset(met_data_typical, crs, 'bilinear')

# convert that to ATS
met_data_typical_ats = watershed_workflow.meteorology.convertAORCToATS(met_data_typical_warped)
# plot one pixel as a function of time
fig = plt.figure()
ax = fig.add_subplot(121)

met_data_single_pixel = met_data_typical_ats.isel({'time':slice(0,365),
                                           'x' : 5,
                                           'y' : 5})

met_data_single_pixel['precipitation rain [m s^-1]'].plot(color='b', label='rain')
met_data_single_pixel['precipitation snow [m SWE s^-1]'].plot(color='c', label='snow')
ax.set_ylabel('precip [m s^-1]')
ax.set_title('')
ax.legend()

ax = fig.add_subplot(122)
met_data_single_pixel['incoming shortwave radiation [W m^-2]'].plot(color='r', label='qSW_in')
ax.set_ylabel('incoming shortwave radiation [W m^-2]')
ax.set_title('')

plt.show()
../_images/78ab9b43707919329aad153c08aeff09b6653344346f8d5dde0ea6cd36edefd9.png