Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Daymet Daily V4 R1 from NASA Earthdata Cloud via OPeNDAP (Hyrax)

Regional and temporal subsetting for the Tennessee Valley Authority (TVA) power service area

ORNL DAAC Tutorial by Bharat Sharma — built on earlier works by Michele Thornton and Rupesh Shrestha (ORNL DAAC)

1. Overview

Daymet daily data now live in the NASA Earthdata Cloud (NASA AWS), and the legacy ORNL DAAC THREDDS endpoint is retired. This notebook shows the current, supported way to pull a space + time subset of Daymet Daily V4 R1 out of the cloud archive without downloading whole continental files — using the NASA Earthdata OPeNDAP Hyrax server and the DAP4 protocol.

A single Daymet daily North American granule is one variable, one year, on a 1 km grid (8075 x 7814 x 365) — roughly 90 GB uncompressed. The TVA power service area covers less 1% of that grid (437k Grid Cells covered by TVA vs total 63M gridcells in NA).

What this notebook does

StepActionTool
2Authenticate with Earthdata Login (EDL)earthaccess
3Build the region of interest from a TVA polygon, reproject to Daymet’s Lambert Conformal Conic gridgeopandas
4Set the time range and variables of interestdatetime
5Discover OPeNDAP granule URLs from NASA’s CMRpydap.client.get_cmr_urls
6Open a remote granule lazily over DAP4 and inspect itxarray + pydap
7Subset in space and time, concatenate years, write netCDFxarray
8Clip to the TVA polygon, map it, export GeoTIFFrioxarray
9Regional analyses: area-mean time series, hot-day counts, single-pixel extractionxarray / matplotlib
10Optional: bulk parallel streaming of many granulespydap.client.to_netcdf

Prerequisites

  1. A free NASA Earthdata Login account.

  2. Your EDL profile must have OPeNDAP-related applications authorized (EDL profile -> Applications -> Authorized Apps).

  3. Python 3.11+ and the packages in the next cell. Suggested environment:

conda create -n daymet_opendap -c conda-forge python=3.12 earthaccess "pydap>=3.5.5" \
    "xarray>=2025.1" netcdf4 geopandas rioxarray matplotlib requests jupyterlab
conda activate daymet_opendap

Data citation

Thornton, M. M., Shrestha, R., Wei, Y., Thornton, P. E., & Kao, S.-C. (2022). Daymet: Daily Surface Weather Data on a 1-km Grid for North America, Version 4 R1 (Version 4.1). ORNL Distributed Active Archive Center. Thornton et al. (2022) Date Accessed: 2026-09-23

Collection landing page: Daymet Daily V4 R1 in the Earthdata catalog | OPeNDAP entry point: opendap.earthdata.nasa.gov

Daymet at a glance

VariableDescription (units)
tmaxDaily maximum 2 m air temperature (deg C)
tminDaily minimum 2 m air temperature (deg C)
prcpDaily total precipitation (mm/day)
sradIncident shortwave radiation flux density (W/m2)
vpWater vapor pressure (Pa)
sweSnow water equivalent (kg/m2)
daylDuration of the daylight period (s/day)
Region codeDomainYears
naContinental North America1980 - present
hiHawaii1980 - present
prPuerto Rico1950 - present

Granule naming in the cloud archive: Daymet_Daily_V4R1.daymet_v4_daily_<region>_<variable>_<year>.nc

Daymet uses a 365-day year: December 31 is dropped in leap years. The grid is Lambert Conformal Conic, so spatial subsetting happens in projected metres (x, y), not in degrees.

This notebook was produced with:
  earthaccess    0.19.0
  pydap          3.5.11
  xarray         2026.7.0
  geopandas      1.1.1
  numpy          2.3.2

Outputs -> /Users/ud4/repos/GitHub/sharma-bharat/DAAC/Task1/daymet_tva

2. Authenticate with Earthdata Login

Everything behind opendap.earthdata.nasa.gov is authenticated. earthaccess handles the EDL handshake and hands us a requests.Session carrying the right credentials/token, which we then pass to pydap/xarray.

  • strategy="netrc" reuses credentials already stored in ~/.netrc.

  • strategy="interactive" prompts for username/password; persist=True writes them to ~/.netrc so you only do this once.

Authenticated: True
Session: SessionWithHeaderRedirection

3. Region of interest: the TVA power service area

Any polygon works — a watershed, a county, a park, a utility footprint. Here we use the TVA Power Service Area polygon published as a public ArcGIS feature service (item page). The polygon is cached locally on first download, so the notebook is reproducible offline afterward.

Swap in your own polygon by pointing LOCAL_POLYGON at a shapefile/GeoJSON, e.g. the Great Smoky Mountains boundary in the older ORNL DAAC tutorials.

CRS              : EPSG:4326
Geographic bounds: [-90.347  32.323 -81.647  37.596]

Reproject the polygon to Daymet’s Lambert Conformal Conic grid

Two equivalent ways to get Daymet’s CRS:

  1. Read it off any Daymet granule with rioxarray (ds.rio.crs) — done later in Step 6.

  2. Use the PROJ string from the Daymet Daily V4 R1 User Guide:

+proj=lcc +ellps=WGS84 +a=6378137 +b=6356752.314245 +lat_1=25 +lat_2=60
+lon_0=-100 +lat_0=42.5 +x_0=0 +y_0=0 +units=m +no_defs

We keep two bounding boxes: geographic (WGS84) for CMR metadata searches, and LCC (metres) for the actual OPeNDAP subsetting.

CMR search bbox (WGS84) : [-90.3467, 32.323, -81.6468, 37.5963]
Subset bbox (Daymet LCC): (848091, -1031136, 1565384, -422553)
Approx. subset size     : 717 x 609 km (~437k grid cells)
<Figure size 1200x500 with 2 Axes>

4. Time range and variables of interest

A full year of daily 1 km data over the TVA footprint is roughly 717 x 609 x 365 x 4 bytes per variable (~640 MB), and a June-August season is about 160 MB. Start with one season and one or two variables, confirm the workflow, then scale up.

Time is sliced with date strings rather than datetime objects, so the same code works whether the granule’s time axis decodes to datetime64 or to cftime objects.

time range : 2023-04-01T00:00:00Z,2023-06-30T00:00:00Z
years      : [2023]
variables  : ['tmax', 'prcp']
region     : na

5. Discover OPeNDAP URLs from NASA’s CMR

NASA’s Common Metadata Repository (CMR) is the searchable index for everything in Earthdata. pydap.client.get_cmr_urls queries CMR and returns the OPeNDAP URLs for the matching granules directly — no manual JSON parsing of the granule response, and no hard-coded file paths.

You can search by ccid (collection concept ID), doi, or short_name. For Daymet Daily V4 R1:

  • DOI: 10.3334/ORNLDAAC/2129

  • short name: Daymet_Daily_V4R1_2129

  • concept ID: C2532426483-ORNL_CLOUD

URLs come back in the form https://opendap.earthdata.nasa.gov/collections/<ccid>/granules/<granule-name>.

collection concept ID: C2532426483-ORNL_CLOUD
21 granule URLs returned by CMR for 2023-04-01 to 2023-06-30

tmax: 1 granule(s)
   https://opendap.earthdata.nasa.gov/collections/C2532426483-ORNL_CLOUD/granules/Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2023.nc

prcp: 1 granule(s)
   https://opendap.earthdata.nasa.gov/collections/C2532426483-ORNL_CLOUD/granules/Daymet_Daily_V4R1.daymet_v4_daily_na_prcp_2023.nc

Note on searching by bounding box. get_cmr_urls also accepts bounding_box=[W, S, E, N]. Daymet granules are continental in extent, so a bounding-box filter mainly serves to exclude the Hawaii and Puerto Rico granules; here we filter on the region code in the filename instead, which is unambiguous.

6. Open a remote granule over DAP4 and inspect it

Two things matter here:

  1. Use the dap4:// scheme. Swapping https:// for dap4:// tells pydap/xarray to use DAP4 syntax rather than the older DAP2, which is what the NASA Hyrax deployment expects for these files.

  2. Nothing is downloaded yet. xr.open_dataset(..., engine="pydap") fetches only the DMR (metadata). Array values travel only when you slice and .load()/.compute() them, and Hyrax sends only the hyperslab you asked for.

dap4://opendap.earthdata.nasa.gov/collections/C2532426483-ORNL_CLOUD/granules/Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2023.nc 

Loading...
CRS from file : PROJCS["undefined",GEOGCS["undefined",DATUM["undefined",SPHEROID["undefined",6378137,298.257223563]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",25],PARAMETER["standard_parallel_2",60],PARAMETER["latitude_of_origin",42.5],PARAMETER["central_meridian",-100],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH]]
dims          : {'time': 365, 'y': 8075, 'x': 7814, 'nv': 2}
x range (m)   : -4560250.0 -> 3252750.0
y range (m)   : -3090000.0 -> 4984000.0 (descending: north to south)
time          : 2023-01-01 -> 2023-12-31

How big is the request?

Estimate before you pull. y is stored north to south (descending), so a .sel() slice on y must be given as slice(ymax, ymin).

subset shape      : {'time': 91, 'y': 609, 'x': 717}
values            : 39,735,423
approx. transfer  : 159 MB per variable
full-granule size : 92.1 GB
reduction         : 580x smaller
{'x': slice(np.float64(848090.8969096304), np.float64(1565383.573167402), None), 'y': slice(np.float64(-422553.4359278971), np.float64(-1031136.1577225582), None)}

7. Subset in space and time, then stream it down

The loop below opens each granule lazily, slices it to the TVA bounding box and the date range, concatenates multi-year granules along time, and calls .load() once — that single call is where the network transfer happens. Results are written to netCDF so later steps (and later sessions) don’t re-request anything.

If a request times out, shrink the date range or the box rather than retrying blindly; Hyrax is happier with several moderate requests than with one enormous one.

opening Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2023.nc ...
  tmax: {'time': 91, 'y': 609, 'x': 717} in 43.6 s

opening Daymet_Daily_V4R1.daymet_v4_daily_na_prcp_2023.nc ...
  prcp: {'time': 91, 'y': 609, 'x': 717} in 41.3 s

Loading...
{'x': slice(np.float64(848090.8969096304), np.float64(1565383.573167402), None), 'y': slice(np.float64(-422553.4359278971), np.float64(-1031136.1577225582), None)}
wrote daymet_tva/data/daymet_v4_tva_tmax_prcp_20230401_20230630.nc  (94.6 MB on disk)

8. Clip to the TVA polygon and export

The bounding-box subset still contains cells outside the service area. rioxarray.clip masks to the polygon itself. Since the data and the polygon are both in Daymet LCC, no reprojection is needed.

wrote daymet_tva/data/daymet_v4_tva_tmax_prcp_20230401_20230630_tvaclip.nc
43% of the bounding-box cells fall inside the TVA polygon
<Figure size 1400x550 with 4 Axes>
wrote daymet_v4_tva_tmax_mean_20230401_20230630.tif
wrote daymet_v4_tva_tmax_days_above_32C_20230401_20230630.tif
wrote daymet_v4_tva_prcp_total_20230401_20230630.tif
<Figure size 1800x500 with 6 Axes>
Saved: daymet_tva/tva_daily_precipitation.gif
<IPython.core.display.Image object>

9. Regional analyses

With the subset in memory, ordinary xarray reductions give the products a regional center typically wants: area-average daily series, extremes, and single-pixel extractions for a city or gauge location.

Area-mean tmax  : 25.59 deg C
Hottest day     : 2023-06-30 (33.47 deg C area mean)
Area-mean total precipitation: 337 mm
<Figure size 1200x700 with 2 Axes>
<Figure size 1200x450 with 1 Axes>

10. Optional: bulk streaming with pydap.client.to_netcdf

For many granules — say tmax for 1980-2024 — the xarray route above is convenient but serial. pydap.client.to_netcdf streams a list of DAP4 URLs straight to local netCDF files in parallel, requesting only the variables and index ranges you name. It is the fastest path for building a multi-decadal regional archive.

Two differences from the xarray workflow:

  • variables are given as DAP4 paths ("/tmax", "/x", ... — the leading / is the root group);

  • slices are integer index ranges (first, last), not coordinate values — so we convert the LCC bounding box into x/y indices first.

dim_slices: {'/x': (5409, 6125), '/y': (5407, 6015)}

Data files are large, so downloading in chunks.

streaming 10 granules
Writing to: /Users/ud4/repos/GitHub/sharma-bharat/DAAC/Task1/daymet_tva/data/bulk

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2015.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2016.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2017.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2018.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2019.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2020.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2021.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2022.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2023.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015

Downloading Daymet_Daily_V4R1.daymet_v4_daily_na_tmax_2024.nc
  y indices 5407:5455
  y indices 5455:5503
  y indices 5503:5551
  y indices 5551:5599
  y indices 5599:5647
  y indices 5647:5695
  y indices 5695:5743
  y indices 5743:5791
  y indices 5791:5839
  y indices 5839:5887
  y indices 5887:5935
  y indices 5935:5983
  y indices 5983:6015
The final datasets are stored here: daymet_tva/data/bulk.
Loading...

SAVE STORAGE: Delete the dir tiles, since data is concated from tiles and is stored in the bulk dir

Smoke Test. If the code above does not work, test if your connections are setup correct

done. Open the result with:
  xr.open_mfdataset(str(WORKDIR / 'data' / 'smoke_test' / '*.nc'), decode_coords='all')
References
  1. Thornton, M. M., Shrestha, R., Wei, Y., Thornton, P. E., & Kao, S.-C. (2022). Daymet: Daily Surface Weather Data on a 1-km Grid for North America, Version 4 R1. ORNL Distributed Active Archive Center. 10.3334/ORNLDAAC/2129