From 2eae2651e77b05940c513eaef8a5ed991d0bbc15 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:17:46 -0400 Subject: [PATCH 01/19] add download.CalAdaptWRF for Cal-Adapt WRF met data --- .../data.atmosphere/R/download.CalAdaptWRF.R | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 modules/data.atmosphere/R/download.CalAdaptWRF.R diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R new file mode 100644 index 0000000000..904a2dfeb4 --- /dev/null +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -0,0 +1,260 @@ +#' Download Cal-Adapt WRF CMIP6 outputs for a single site and convert to CF +#' +#' Fetches hourly WRF dynamically downscaled data from the Cal-Adapt Analytics +#' Engine (CADCAT S3 bucket) via caladaptR, extracts the nearest grid cell to +#' the site, converts units to CF-1.8, and writes one NetCDF per year. +#' +#' WRF grids are cached in tempdir() so that when met.process calls this for +#' multiple sites in the same R session, each grid is only fetched from S3 once. +#' For 200 sites x 8 vars x 20 years that cuts S3 round trips from 32,000 to 160. +#' +#' Available models (all at 45 km, ssp370): CESM2, CNRM-ESM2-1, EC-Earth3, +#' EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. +#' Only CESM2 has ssp245 and ssp585 in addition to ssp370. +#' +#' Precipitation for MPI-ESM1-2-HR, MIROC6, and TaiESM1 is derived from +#' rainc + rainnc components; caladaptR handles this transparently. +#' +#' @param outfolder Directory for storing output +#' @param start_date Start date for met data +#' @param end_date End date for met data +#' @param site_id BETY site id +#' @param lat.in Latitude of site (decimal degrees, WGS84) +#' @param lon.in Longitude of site (decimal degrees, WGS84) +#' @param model WRF GCM name, default "CESM2" +#' @param scenario SSP experiment id, default "ssp370" +#' @param overwrite Overwrite existing files? Default FALSE +#' @param verbose Extra debug output? Default FALSE +#' @param ... further arguments, currently ignored +#' +#' @return invisible data.frame with file info for BETY registration +#' +#' @importFrom rlang .data +#' @export +#' @author Akash B V +download.CalAdaptWRF <- function(outfolder, start_date, end_date, + site_id, lat.in, lon.in, + model = "CESM2", scenario = "ssp370", + overwrite = FALSE, verbose = FALSE, ...) { + + if (!requireNamespace("caladaptR", quietly = TRUE)) { + PEcAn.logger::logger.severe( + "caladaptR package required but not installed. ", + "Install with: remotes::install_github('lebauerapproach/caladaptR')") + } + if (!requireNamespace("sf", quietly = TRUE)) { + PEcAn.logger::logger.severe("sf package required for CRS transform") + } + if (!requireNamespace("stars", quietly = TRUE)) { + PEcAn.logger::logger.severe("stars package required for grid extraction") + } + + # null guard, convert_input sometimes passes NULL for optional args + if (is.null(model)) model <- "CESM2" + if (is.null(scenario)) scenario <- "ssp370" + + start_year <- lubridate::year(start_date) + end_year <- lubridate::year(end_date) + + # BETY site id formatting + site_id <- tryCatch(as.numeric(site_id), + warning = function(w) as.character(site_id)) + if (is.numeric(site_id) && site_id > 1e9) { + siteid_str <- paste0(site_id %/% 1e9, "-", site_id %% 1e9) + } else { + siteid_str <- as.character(site_id) + } + outfolder <- paste0(outfolder, "_site_", siteid_str) + + lat.in <- as.numeric(lat.in) + lon.in <- as.numeric(lon.in) + + dir.create(outfolder, showWarnings = FALSE, recursive = TRUE) + + ##variable mapping from the central met table + wrf_tbl <- pecan_standard_met_table |> + dplyr::filter(!is.na(.data$caladapt_wrf) & nzchar(.data$caladapt_wrf)) + + # separate direct-fetch vars from derived ones (wind_speed = CALC) + fetch_tbl <- wrf_tbl |> + dplyr::filter(!grepl("^CALC", .data$caladapt_wrf)) + derived_tbl <- wrf_tbl |> + dplyr::filter(grepl("^CALC", .data$caladapt_wrf)) + + wrf_to_cf <- stats::setNames(fetch_tbl$cf_standard_name, fetch_tbl$caladapt_wrf) + + ylist <- seq(start_year, end_year, by = 1) + rows <- length(ylist) + + results <- data.frame( + file = character(rows), + host = character(rows), + mimetype = character(rows), + formatname = character(rows), + startdate = character(rows), + enddate = character(rows), + dbfile.name = paste("CalAdaptWRF", model, scenario, sep = "."), + stringsAsFactors = FALSE + ) + + for (i in seq_len(rows)) { + year <- ylist[i] + + loc.file <- file.path( + outfolder, + paste("CalAdaptWRF", model, scenario, year, "nc", sep = ".") + ) + + results$file[i] <- loc.file + results$host[i] <- PEcAn.remote::fqdn() + results$startdate[i] <- paste0(year, "-01-01 00:00:00") + results$enddate[i] <- paste0(year, "-12-31 23:59:59") + results$mimetype[i] <- "application/x-netcdf" + results$formatname[i] <- "CF Meteorology" + + if (file.exists(loc.file) && !isTRUE(overwrite)) { + PEcAn.logger::logger.debug("File '", loc.file, "' already exists, skipping") + next + } + + PEcAn.logger::logger.info( + "CalAdaptWRF: fetching ", model, " ", scenario, + " year ", year, " (", i, " of ", rows, ")" + ) + + year_start <- paste0(year, "-01-01T00:00:00") + year_end <- paste0(year, "-12-31T23:00:00") + + ##fetch each variable, using session cache to avoid redundant S3 reads + # WRF 45km grid is small (~20-30 MB per var per year). We stash + # the full grid in tempdir() so that subsequent sites in the same + # met.process/papply session reuse it instead of hitting S3 again + dat.list <- list() + time_vals <- NULL + pt_native <- NULL # build once after first grid fetch sets the CRS + + for (wrf_var in names(wrf_to_cf)) { + cache_key <- paste("caladapt_grid", model, scenario, + wrf_var, year, sep = "_") + cache_file <- file.path(tempdir(), paste0(cache_key, ".rds")) + + if (file.exists(cache_file)) { + if (verbose) { + PEcAn.logger::logger.debug(" cache hit: ", wrf_var, " ", year) + } + grid <- readRDS(cache_file) + } else { + PEcAn.logger::logger.info(" fetching ", wrf_var, " from S3") + grid <- caladaptR::ca_fetch( + variable = wrf_var, + model = model, + scenario = scenario, + timescale = "1hr", + resolution = "d01", + start_time = year_start, + end_time = year_end + ) + saveRDS(grid, cache_file) + } + + # grab time dimension and build the projected point once + if (is.null(time_vals)) { + time_vals <- stars::st_get_dimension_values(grid, "time") + pt_wgs84 <- sf::st_as_sf( + data.frame(lon = lon.in, lat = lat.in), + coords = c("lon", "lat"), crs = 4326 + ) + pt_native <- sf::st_transform(pt_wgs84, sf::st_crs(grid)) + } + + # extract nearest grid cell + extracted <- stars::st_extract(grid, pt_native) + + vals <- extracted[[1]] + if (inherits(vals, "units")) vals <- units::drop_units(vals) + dat.list[[wrf_var]] <- as.numeric(vals) + } + + ##unit conversions + # precip: WRF hourly accumulation (mm) -> CF flux (kg/m2/s) + # 1 mm water = 1 kg/m2, divide by 3600s for hourly timestep + if ("prec" %in% names(dat.list)) { + dat.list[["prec"]] <- dat.list[["prec"]] / 3600 + } + + # specific humidity: WRF stores mixing ratio q (kg/kg) + # q_specific = q / (1 + q) + if ("q2" %in% names(dat.list)) { + q <- dat.list[["q2"]] + dat.list[["q2"]] <- q / (1 + q) + } + + ##derived variables + # wind speed from u10 and v10 components + wind_speed <- NULL + if (nrow(derived_tbl) > 0 && + all(c("u10", "v10") %in% names(dat.list))) { + wind_speed <- sqrt(dat.list[["u10"]]^2 + dat.list[["v10"]]^2) + } + + ##write CF NetCDF, one file per year + time_secs <- as.numeric(difftime( + time_vals, + as.POSIXct(paste0(year, "-01-01 00:00:00"), tz = "UTC"), + units = "secs" + )) + + lat_dim <- ncdf4::ncdim_def("latitude", "degree_north", + lat.in, create_dimvar = TRUE) + lon_dim <- ncdf4::ncdim_def("longitude", "degree_east", + lon.in, create_dimvar = TRUE) + time_dim <- ncdf4::ncdim_def("time", + paste("seconds since", results$startdate[i]), + time_secs, + create_dimvar = TRUE, unlim = TRUE) + dim <- list(lat_dim, lon_dim, time_dim) + + # build ncdf4 variable defs from met table + var.list <- list() + var.names <- character() + for (j in seq_len(nrow(fetch_tbl))) { + cf_name <- fetch_tbl$cf_standard_name[j] + var.list[[j]] <- ncdf4::ncvar_def( + name = cf_name, + units = fetch_tbl$units[j], + dim = dim, + missval = -9999.0, + verbose = verbose + ) + var.names[j] <- fetch_tbl$caladapt_wrf[j] + } + + # wind speed as derived variable + if (!is.null(wind_speed) && nrow(derived_tbl) > 0) { + ws_row <- derived_tbl[derived_tbl$cf_standard_name == "wind_speed", ] + if (nrow(ws_row) > 0) { + idx <- length(var.list) + 1 + var.list[[idx]] <- ncdf4::ncvar_def( + name = "wind_speed", + units = ws_row$units[1], + dim = dim, + missval = -9999.0, + verbose = verbose + ) + } + } + + loc <- ncdf4::nc_create(loc.file, var.list, verbose = verbose) + for (j in seq_len(nrow(fetch_tbl))) { + ncdf4::ncvar_put(loc, var.list[[j]], dat.list[[var.names[j]]]) + } + if (!is.null(wind_speed)) { + ncdf4::ncvar_put(loc, var.list[[length(var.list)]], wind_speed) + } + ncdf4::nc_close(loc) + + PEcAn.logger::logger.info(" wrote ", loc.file) + } + + return(invisible(results)) +} ##download.CalAdaptWRF From a7902f7e3b1614126ece274f1f347874c177168c Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:17:59 -0400 Subject: [PATCH 02/19] add CalAdaptWRF registration xml --- .../inst/registration/register.CalAdaptWRF.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 modules/data.atmosphere/inst/registration/register.CalAdaptWRF.xml diff --git a/modules/data.atmosphere/inst/registration/register.CalAdaptWRF.xml b/modules/data.atmosphere/inst/registration/register.CalAdaptWRF.xml new file mode 100644 index 0000000000..68538f94c8 --- /dev/null +++ b/modules/data.atmosphere/inst/registration/register.CalAdaptWRF.xml @@ -0,0 +1,10 @@ + + +regional + + 33 + CF Meteorology + application/x-netcdf + nc + + From 5e7cad074f863530ebb78879218bd5054d87b4bb Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:18:07 -0400 Subject: [PATCH 03/19] add caladapt_wrf column to pecan_standard_met_table --- .../R/pecan_standard_met_table.R | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/modules/data.atmosphere/R/pecan_standard_met_table.R b/modules/data.atmosphere/R/pecan_standard_met_table.R index cb29ff0e71..db4ea44083 100644 --- a/modules/data.atmosphere/R/pecan_standard_met_table.R +++ b/modules/data.atmosphere/R/pecan_standard_met_table.R @@ -2,25 +2,25 @@ #' #' @export pecan_standard_met_table <- tibble::tribble( - ~`cf_standard_name` , ~units , ~is_required, ~bety , ~isimip , ~cruncep , ~narr , ~ameriflux , ~era5 , - "air_temperature" , "K" , TRUE, "airT" , "tasAdjust" , "tair" , "air" , "TA (C)" , "t2m" , - "air_temperature_max" , "K" , FALSE, NA , "tasmaxAdjust" , NA , "tmax" , NA , NA , - "air_temperature_min" , "K" , FALSE, NA , "tasminAdjust" , NA , "tmin" , NA , NA , - "air_pressure" , "Pa" , TRUE, "air_pressure" , NA , NA , NA , "PRESS (KPa)" , "sp" , - "dew_point_temperature" , "K" , FALSE, NA , NA , NA , NA , NA , "d2m" , - "mole_fraction_of_carbon_dioxide_in_air" , "1" , FALSE, NA , NA , NA , NA , "CO2" , NA , - "moisture_content_of_soil_layer" , "kg m-2" , FALSE, NA , NA , NA , NA , NA , NA , - "soil_temperature" , "K" , FALSE, "soilT" , NA , NA , NA , "TS1 *(NOT DONE)*" , NA , - "relative_humidity" , "%" , FALSE, "relative_humidity" , "rhurs" , NA , "rhum" , "RH" , NA , - "specific_humidity" , "1" , TRUE, "specific_humidity" , NA , "qair" , "shum" , "CALC(RH)" , NA , - "water_vapor_saturation_deficit" , "Pa" , FALSE, "VPD" , NA , NA , NA , "VPD *(NOT DONE)*" , NA , - "surface_downwelling_longwave_flux_in_air" , "W m-2" , TRUE, "same" , "rldsAdjust" , "lwdown" , "dlwrf" , "Rgl" , "strd", - "surface_downwelling_shortwave_flux_in_air" , "W m-2" , TRUE, "solar_radiation" , "rsdsAdjust" , "swdown" , "dswrf" , "Rg" , "ssrd", - "surface_downwelling_photosynthetic_photon_flux_in_air" , "mol m-2 s-1" , FALSE, "PAR" , NA , NA , NA , "PAR *(NOT DONE)*" , NA , - "precipitation_flux" , "kg m-2 s-1" , TRUE, "cccc" , "prAdjust" , "rain" , "acpc" , "PREC (mm/s)" , "tp" , - "wind_to_direction" , "degrees" , FALSE, "wind_direction" , NA , NA , NA , "WD" , NA , - "wind_speed" , "m s-1" , FALSE, "Wspd" , NA , NA , NA , "WS" , NA , - "eastward_wind" , "m s-1" , TRUE, "eastward_wind" , NA , NA , NA , "CALC(WS+WD)" , "u10" , - "northward_wind" , "m s-1" , TRUE, "northward_wind" , NA , NA , NA , "CALC(WS+WD)" , "v10" , - "volume_fraction_of_condensed_water_in_soil" , "1" , FALSE, "soilM" , NA , NA , NA , "SWC_1" , "swvl1" + ~`cf_standard_name` , ~units , ~is_required, ~bety , ~isimip , ~cruncep , ~narr , ~ameriflux , ~era5 , ~caladapt_wrf , + "air_temperature" , "K" , TRUE, "airT" , "tasAdjust" , "tair" , "air" , "TA (C)" , "t2m" , "t2" , + "air_temperature_max" , "K" , FALSE, NA , "tasmaxAdjust" , NA , "tmax" , NA , NA , NA , + "air_temperature_min" , "K" , FALSE, NA , "tasminAdjust" , NA , "tmin" , NA , NA , NA , + "air_pressure" , "Pa" , TRUE, "air_pressure" , NA , NA , NA , "PRESS (KPa)" , "sp" , "psfc" , + "dew_point_temperature" , "K" , FALSE, NA , NA , NA , NA , NA , "d2m" , NA , + "mole_fraction_of_carbon_dioxide_in_air" , "1" , FALSE, NA , NA , NA , NA , "CO2" , NA , NA , + "moisture_content_of_soil_layer" , "kg m-2" , FALSE, NA , NA , NA , NA , NA , NA , NA , + "soil_temperature" , "K" , FALSE, "soilT" , NA , NA , NA , "TS1 *(NOT DONE)*" , NA , NA , + "relative_humidity" , "%" , FALSE, "relative_humidity" , "rhurs" , NA , "rhum" , "RH" , NA , NA , + "specific_humidity" , "1" , TRUE, "specific_humidity" , NA , "qair" , "shum" , "CALC(RH)" , NA , "q2" , + "water_vapor_saturation_deficit" , "Pa" , FALSE, "VPD" , NA , NA , NA , "VPD *(NOT DONE)*" , NA , NA , + "surface_downwelling_longwave_flux_in_air" , "W m-2" , TRUE, "same" , "rldsAdjust" , "lwdown" , "dlwrf" , "Rgl" , "strd" , "lwdnb" , + "surface_downwelling_shortwave_flux_in_air" , "W m-2" , TRUE, "solar_radiation" , "rsdsAdjust" , "swdown" , "dswrf" , "Rg" , "ssrd" , "swdnb" , + "surface_downwelling_photosynthetic_photon_flux_in_air" , "mol m-2 s-1" , FALSE, "PAR" , NA , NA , NA , "PAR *(NOT DONE)*" , NA , NA , + "precipitation_flux" , "kg m-2 s-1" , TRUE, "cccc" , "prAdjust" , "rain" , "acpc" , "PREC (mm/s)" , "tp" , "prec" , + "wind_to_direction" , "degrees" , FALSE, "wind_direction" , NA , NA , NA , "WD" , NA , NA , + "wind_speed" , "m s-1" , FALSE, "Wspd" , NA , NA , NA , "WS" , NA , "CALC(u10+v10)", + "eastward_wind" , "m s-1" , TRUE, "eastward_wind" , NA , NA , NA , "CALC(WS+WD)" , "u10" , "u10" , + "northward_wind" , "m s-1" , TRUE, "northward_wind" , NA , NA , NA , "CALC(WS+WD)" , "v10" , "v10" , + "volume_fraction_of_condensed_water_in_soil" , "1" , FALSE, "soilM" , NA , NA , NA , "SWC_1" , "swvl1", NA ) From a0d783e5305353e7de33eb59465cb486e9e88dd2 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:18:21 -0400 Subject: [PATCH 04/19] add CalAdaptWRF to met.process skip list --- modules/data.atmosphere/R/met.process.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/data.atmosphere/R/met.process.R b/modules/data.atmosphere/R/met.process.R index 8a6870f117..cd8bd0352e 100644 --- a/modules/data.atmosphere/R/met.process.R +++ b/modules/data.atmosphere/R/met.process.R @@ -204,7 +204,7 @@ met.process <- function(site, input_met, start_date, end_date, model, dbparms=dbparms ) - if (met %in% c("CRUNCEP", "GFDL", "NOAA_GEFS", "MERRA")) { + if (met %in% c("CRUNCEP", "GFDL", "NOAA_GEFS", "MERRA", "CalAdaptWRF")) { ready.id <- raw.id # input_met$id overwrites ready.id below, needs to be populated here input_met$id <- raw.id From 73cd4e1412c8d4a6d78d6a12e118c9417f0f8092 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:20:41 -0400 Subject: [PATCH 05/19] add caladaptR and stars to Suggests --- modules/data.atmosphere/DESCRIPTION | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/data.atmosphere/DESCRIPTION b/modules/data.atmosphere/DESCRIPTION index 9c978a6fd3..7b9d7d6bb3 100644 --- a/modules/data.atmosphere/DESCRIPTION +++ b/modules/data.atmosphere/DESCRIPTION @@ -65,6 +65,7 @@ Imports: xts, zoo Suggests: + caladaptR, doParallel, ecmwfr (>= 2.0.0), doSNOW, @@ -80,9 +81,11 @@ Suggests: progress, reticulate, rmarkdown, + stars, testthat (>= 3.1.7), withr Remotes: + github::lebauerapproach/caladaptR, github::adokter/suntools, github::chuhousen/amerifluxr, github::ropensci/geonames, From b4a1ed28cf9b3b608a83b4338375fff71a741ba3 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:20:49 -0400 Subject: [PATCH 06/19] add Cal-Adapt WRF section to met driver docs --- .../06_data/01_meteorology.Rmd | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/book_source/03_topical_pages/06_data/01_meteorology.Rmd b/book_source/03_topical_pages/06_data/01_meteorology.Rmd index d521bf3219..d19d72f571 100644 --- a/book_source/03_topical_pages/06_data/01_meteorology.Rmd +++ b/book_source/03_topical_pages/06_data/01_meteorology.Rmd @@ -41,6 +41,7 @@ General guidance: | [CMIP5](#cmip5) | Global | 3 hr | 2006–2100 | | [PalEON](#paleon) | Regional | 6 hr, 0.5° | 850–2010 | | [Geostreams](#geostreams) | Site | Varies | Varies | +| [Cal-Adapt WRF](#caladaptwrf) | Regional (Western US) | 1 hr, 45/9/3 km | 1980--2100 | ## Ameriflux @@ -175,3 +176,23 @@ Resolution: 30 min Availability: Varies by [site](https://meta.icos-cp.eu/collections/q4V7P1VLZevIrnlsW6SJO1Rz) Notes: To use this option, set `source` as `ICOS` and a `product` tag containing `etc` in `pecan.xml` + +## Cal-Adapt WRF {#caladaptwrf} + +Scale: Regional (Western US) + +Resolution: 1 hr, 45 km (d01), 9 km (d02), 3 km (d03) + +Availability: 1980--2100 + +Notes: CMIP6 dynamically downscaled projections from the Cal-Adapt Analytics Engine (WUS-D3 dataset, Rahimi et al. 2024). Eight GCMs available under SSP3-7.0; CESM2 also has SSP2-4.5 and SSP5-8.5. Data is publicly available on AWS S3 (no authentication required). Requires the `caladaptR` package from GitHub. To use this option, set `source` as `CalAdaptWRF` and specify `model` and `scenario` in the `met` section of `pecan.xml`: + +```xml + + CalAdaptWRF + CESM2 + ssp370 + +``` + +Available GCMs: CESM2, CNRM-ESM2-1, EC-Earth3, EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. See `caladaptR::ca_models(activity = "WRF")` for the current list. From ddc97bb67cba7a291b09a9fb7f1fd8b409d6a273 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:25:09 -0400 Subject: [PATCH 07/19] add CalAdaptWRF entry to NEWS.md --- modules/data.atmosphere/NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/data.atmosphere/NEWS.md b/modules/data.atmosphere/NEWS.md index dbae9f222b..2006cd9dd3 100644 --- a/modules/data.atmosphere/NEWS.md +++ b/modules/data.atmosphere/NEWS.md @@ -1,6 +1,8 @@ # PEcAn.data.atmosphere 1.9.1 ## Added +* New function `download.CalAdaptWRF()` fetches hourly WRF dynamically downscaled CMIP6 data from the Cal-Adapt Analytics Engine (CADCAT S3 bucket) via the `caladaptR` package. Supports 8 GCMs under SSP3-7.0 at 45 km resolution, with session-level grid caching to cut S3 round trips when processing multiple sites. +* Added `caladapt_wrf` column to `pecan_standard_met_table` for Cal-Adapt WRF variable mapping. * New function `sat_vapor_pressure()` computes saturation vapor pressure from temperature (#3597). * New function `AmeriFlux_met_ensemble()` generates weather ensembles from Ameriflux data with ERA5 fallback for missing radiation and soil moisture (#3586). * `ERA5_met_process()` gains option `n_cores` to process ensemble data efficiently in parallel (#3563). From e0f435730eb1a4eb9c75db57a87faf576b076eb1 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:25:13 -0400 Subject: [PATCH 08/19] add CalAdaptWRF entry to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3d69171da..e40446aca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ For more information about this file see also [Keep a Changelog](http://keepacha ## Unreleased ### Added +- Added `download.CalAdaptWRF()` to `PEcAn.data.atmosphere` for fetching Cal-Adapt WRF CMIP6 hourly met data at 45 km resolution. Includes registration XML, met table mapping, and book documentation. - Added PEcAn.PEPRMT model, including a demo run with example data - Add `format_try_for_ma()` and `try_trait_mapping()` to `PEcAn.data.remote` to convert trait data from the external TRY database into the tabular format required by the PEcAn meta-analysis module (#3717). - Add function `qsub_sda()` for submitting SDA batch jobs by splitting a large number of sites into multiple small groups of sites (#3634). From ab5581626b181bc6cbab4e8d841a998c7e807c69 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 15 Apr 2026 21:25:22 -0400 Subject: [PATCH 09/19] update NAMESPACE and man pages for CalAdaptWRF --- modules/data.atmosphere/NAMESPACE | 1 + .../man/download.CalAdaptWRF.Rd | 66 +++++++++++++++++++ .../man/pecan_standard_met_table.Rd | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 modules/data.atmosphere/man/download.CalAdaptWRF.Rd diff --git a/modules/data.atmosphere/NAMESPACE b/modules/data.atmosphere/NAMESPACE index 5f282f7dfe..c8a8e8120c 100644 --- a/modules/data.atmosphere/NAMESPACE +++ b/modules/data.atmosphere/NAMESPACE @@ -20,6 +20,7 @@ export(debias.met.regression) export(download.Ameriflux) export(download.AmerifluxLBL) export(download.CRUNCEP) +export(download.CalAdaptWRF) export(download.ERA5_cds) export(download.FACE) export(download.Fluxnet2015) diff --git a/modules/data.atmosphere/man/download.CalAdaptWRF.Rd b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd new file mode 100644 index 0000000000..09a15140ac --- /dev/null +++ b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd @@ -0,0 +1,66 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/download.CalAdaptWRF.R +\name{download.CalAdaptWRF} +\alias{download.CalAdaptWRF} +\title{Download Cal-Adapt WRF CMIP6 outputs for a single site and convert to CF} +\usage{ +download.CalAdaptWRF( + outfolder, + start_date, + end_date, + site_id, + lat.in, + lon.in, + model = "CESM2", + scenario = "ssp370", + overwrite = FALSE, + verbose = FALSE, + ... +) +} +\arguments{ +\item{outfolder}{Directory for storing output} + +\item{start_date}{Start date for met data} + +\item{end_date}{End date for met data} + +\item{site_id}{BETY site id} + +\item{lat.in}{Latitude of site (decimal degrees, WGS84)} + +\item{lon.in}{Longitude of site (decimal degrees, WGS84)} + +\item{model}{WRF GCM name, default "CESM2"} + +\item{scenario}{SSP experiment id, default "ssp370"} + +\item{overwrite}{Overwrite existing files? Default FALSE} + +\item{verbose}{Extra debug output? Default FALSE} + +\item{...}{further arguments, currently ignored} +} +\value{ +invisible data.frame with file info for BETY registration +} +\description{ +Fetches hourly WRF dynamically downscaled data from the Cal-Adapt Analytics +Engine (CADCAT S3 bucket) via caladaptR, extracts the nearest grid cell to +the site, converts units to CF-1.8, and writes one NetCDF per year. +} +\details{ +WRF grids are cached in tempdir() so that when met.process calls this for +multiple sites in the same R session, each grid is only fetched from S3 once. +For 200 sites x 8 vars x 20 years that cuts S3 round trips from 32,000 to 160. + +Available models (all at 45 km, ssp370): CESM2, CNRM-ESM2-1, EC-Earth3, +EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. +Only CESM2 has ssp245 and ssp585 in addition to ssp370. + +Precipitation for MPI-ESM1-2-HR, MIROC6, and TaiESM1 is derived from +rainc + rainnc components; caladaptR handles this transparently. +} +\author{ +Akash B V +} diff --git a/modules/data.atmosphere/man/pecan_standard_met_table.Rd b/modules/data.atmosphere/man/pecan_standard_met_table.Rd index 11bacb8ffb..7699a661a7 100644 --- a/modules/data.atmosphere/man/pecan_standard_met_table.Rd +++ b/modules/data.atmosphere/man/pecan_standard_met_table.Rd @@ -5,7 +5,7 @@ \alias{pecan_standard_met_table} \title{Conversion table for PEcAn standard meteorology} \format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 20 rows and 9 columns. +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 20 rows and 10 columns. } \usage{ pecan_standard_met_table From c22bf37b3818df817afded8454ef290f5dc32c5b Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:37:36 -0400 Subject: [PATCH 10/19] swap caladaptR for caladaptaer in deps --- modules/data.atmosphere/DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data.atmosphere/DESCRIPTION b/modules/data.atmosphere/DESCRIPTION index 7b9d7d6bb3..37208eeaf7 100644 --- a/modules/data.atmosphere/DESCRIPTION +++ b/modules/data.atmosphere/DESCRIPTION @@ -65,7 +65,7 @@ Imports: xts, zoo Suggests: - caladaptR, + caladaptaer, doParallel, ecmwfr (>= 2.0.0), doSNOW, @@ -85,7 +85,7 @@ Suggests: testthat (>= 3.1.7), withr Remotes: - github::lebauerapproach/caladaptR, + github::lebauerapproach/caladaptaer, github::adokter/suntools, github::chuhousen/amerifluxr, github::ropensci/geonames, From 49106449f636ef20566d139b37be5ae4b65efebe Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:37:36 -0400 Subject: [PATCH 11/19] point download fn at cae_fetch --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index 904a2dfeb4..f7623fffc5 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -1,7 +1,7 @@ #' Download Cal-Adapt WRF CMIP6 outputs for a single site and convert to CF #' #' Fetches hourly WRF dynamically downscaled data from the Cal-Adapt Analytics -#' Engine (CADCAT S3 bucket) via caladaptR, extracts the nearest grid cell to +#' Engine (CADCAT S3 bucket) via caladaptaer, extracts the nearest grid cell to #' the site, converts units to CF-1.8, and writes one NetCDF per year. #' #' WRF grids are cached in tempdir() so that when met.process calls this for @@ -13,7 +13,7 @@ #' Only CESM2 has ssp245 and ssp585 in addition to ssp370. #' #' Precipitation for MPI-ESM1-2-HR, MIROC6, and TaiESM1 is derived from -#' rainc + rainnc components; caladaptR handles this transparently. +#' rainc + rainnc components; caladaptaer handles this transparently. #' #' @param outfolder Directory for storing output #' @param start_date Start date for met data @@ -37,10 +37,10 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, model = "CESM2", scenario = "ssp370", overwrite = FALSE, verbose = FALSE, ...) { - if (!requireNamespace("caladaptR", quietly = TRUE)) { + if (!requireNamespace("caladaptaer", quietly = TRUE)) { PEcAn.logger::logger.severe( - "caladaptR package required but not installed. ", - "Install with: remotes::install_github('lebauerapproach/caladaptR')") + "caladaptaer package required but not installed. ", + "Install with: remotes::install_github('lebauerapproach/caladaptaer')") } if (!requireNamespace("sf", quietly = TRUE)) { PEcAn.logger::logger.severe("sf package required for CRS transform") @@ -145,7 +145,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, grid <- readRDS(cache_file) } else { PEcAn.logger::logger.info(" fetching ", wrf_var, " from S3") - grid <- caladaptR::ca_fetch( + grid <- caladaptaer::cae_fetch( variable = wrf_var, model = model, scenario = scenario, From 06dbbe48a7bd8f9d0ce1d1c6e1ba2653c6078ea7 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:37:36 -0400 Subject: [PATCH 12/19] regen Rd for caladaptaer rename --- modules/data.atmosphere/man/download.CalAdaptWRF.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data.atmosphere/man/download.CalAdaptWRF.Rd b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd index 09a15140ac..46b8fb5550 100644 --- a/modules/data.atmosphere/man/download.CalAdaptWRF.Rd +++ b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd @@ -46,7 +46,7 @@ invisible data.frame with file info for BETY registration } \description{ Fetches hourly WRF dynamically downscaled data from the Cal-Adapt Analytics -Engine (CADCAT S3 bucket) via caladaptR, extracts the nearest grid cell to +Engine (CADCAT S3 bucket) via caladaptaer, extracts the nearest grid cell to the site, converts units to CF-1.8, and writes one NetCDF per year. } \details{ @@ -59,7 +59,7 @@ EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. Only CESM2 has ssp245 and ssp585 in addition to ssp370. Precipitation for MPI-ESM1-2-HR, MIROC6, and TaiESM1 is derived from -rainc + rainnc components; caladaptR handles this transparently. +rainc + rainnc components; caladaptaer handles this transparently. } \author{ Akash B V From 3e3e7a7430b9c8b177f181df5c5122267d219233 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:37:36 -0400 Subject: [PATCH 13/19] bump NEWS to caladaptaer --- modules/data.atmosphere/NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/data.atmosphere/NEWS.md b/modules/data.atmosphere/NEWS.md index 2006cd9dd3..fc918d9d45 100644 --- a/modules/data.atmosphere/NEWS.md +++ b/modules/data.atmosphere/NEWS.md @@ -1,7 +1,7 @@ # PEcAn.data.atmosphere 1.9.1 ## Added -* New function `download.CalAdaptWRF()` fetches hourly WRF dynamically downscaled CMIP6 data from the Cal-Adapt Analytics Engine (CADCAT S3 bucket) via the `caladaptR` package. Supports 8 GCMs under SSP3-7.0 at 45 km resolution, with session-level grid caching to cut S3 round trips when processing multiple sites. +* New function `download.CalAdaptWRF()` fetches hourly WRF dynamically downscaled CMIP6 data from the Cal-Adapt Analytics Engine (CADCAT S3 bucket) via the `caladaptaer` package. Supports 8 GCMs under SSP3-7.0 at 45 km resolution, with session-level grid caching to cut S3 round trips when processing multiple sites. * Added `caladapt_wrf` column to `pecan_standard_met_table` for Cal-Adapt WRF variable mapping. * New function `sat_vapor_pressure()` computes saturation vapor pressure from temperature (#3597). * New function `AmeriFlux_met_ensemble()` generates weather ensembles from Ameriflux data with ERA5 fallback for missing radiation and soil moisture (#3586). From 4b327db9ddce6cd5bb0f77012e4ef9c25094e5da Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:37:36 -0400 Subject: [PATCH 14/19] update met docs for caladaptaer --- book_source/03_topical_pages/06_data/01_meteorology.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book_source/03_topical_pages/06_data/01_meteorology.Rmd b/book_source/03_topical_pages/06_data/01_meteorology.Rmd index d19d72f571..f9d24242d3 100644 --- a/book_source/03_topical_pages/06_data/01_meteorology.Rmd +++ b/book_source/03_topical_pages/06_data/01_meteorology.Rmd @@ -185,7 +185,7 @@ Resolution: 1 hr, 45 km (d01), 9 km (d02), 3 km (d03) Availability: 1980--2100 -Notes: CMIP6 dynamically downscaled projections from the Cal-Adapt Analytics Engine (WUS-D3 dataset, Rahimi et al. 2024). Eight GCMs available under SSP3-7.0; CESM2 also has SSP2-4.5 and SSP5-8.5. Data is publicly available on AWS S3 (no authentication required). Requires the `caladaptR` package from GitHub. To use this option, set `source` as `CalAdaptWRF` and specify `model` and `scenario` in the `met` section of `pecan.xml`: +Notes: CMIP6 dynamically downscaled projections from the Cal-Adapt Analytics Engine (WUS-D3 dataset, Rahimi et al. 2024). Eight GCMs available under SSP3-7.0; CESM2 also has SSP2-4.5 and SSP5-8.5. Data is publicly available on AWS S3 (no authentication required). Requires the `caladaptaer` package from GitHub. To use this option, set `source` as `CalAdaptWRF` and specify `model` and `scenario` in the `met` section of `pecan.xml`: ```xml @@ -195,4 +195,4 @@ Notes: CMIP6 dynamically downscaled projections from the Cal-Adapt Analytics Eng ``` -Available GCMs: CESM2, CNRM-ESM2-1, EC-Earth3, EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. See `caladaptR::ca_models(activity = "WRF")` for the current list. +Available GCMs: CESM2, CNRM-ESM2-1, EC-Earth3, EC-Earth3-Veg, FGOALS-g3, MPI-ESM1-2-HR, MIROC6, TaiESM1. See `caladaptaer::cae_models(activity = "WRF")` for the current list. From 48742b16a7de9262980bbcccc21a8d461d2767bf Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:56:21 -0400 Subject: [PATCH 15/19] swap rds cache for netcdf with lazy read --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index f7623fffc5..0ffa2cb9cc 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -136,13 +136,15 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, for (wrf_var in names(wrf_to_cf)) { cache_key <- paste("caladapt_grid", model, scenario, wrf_var, year, sep = "_") - cache_file <- file.path(tempdir(), paste0(cache_key, ".rds")) + cache_file <- file.path(tempdir(), paste0(cache_key, ".nc")) if (file.exists(cache_file)) { if (verbose) { PEcAn.logger::logger.debug(" cache hit: ", wrf_var, " ", year) } - grid <- readRDS(cache_file) + # lazy read so st_extract pulls only the requested cell + # rather than deserializing the full grid into memory + grid <- stars::read_stars(cache_file, proxy = TRUE) } else { PEcAn.logger::logger.info(" fetching ", wrf_var, " from S3") grid <- caladaptaer::cae_fetch( @@ -154,7 +156,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, start_time = year_start, end_time = year_end ) - saveRDS(grid, cache_file) + stars::write_stars(grid, cache_file) } # grab time dimension and build the projected point once From 1380bf0ad6add35b78c327ddd7c32b360bcf2921 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 22:58:51 -0400 Subject: [PATCH 16/19] let callers pick the WRF grid resolution --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 14 ++++++++++---- .../data.atmosphere/man/download.CalAdaptWRF.Rd | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index 0ffa2cb9cc..8ee9ec4fd8 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -23,6 +23,10 @@ #' @param lon.in Longitude of site (decimal degrees, WGS84) #' @param model WRF GCM name, default "CESM2" #' @param scenario SSP experiment id, default "ssp370" +#' @param resolution WRF nested-domain id: "d01" (45 km, default and only +#' one with full coverage), "d02" (9 km), or "d03" (3 km). Higher +#' resolutions are only partially released; check +#' \code{caladaptaer::cae_check_variables()} for availability. #' @param overwrite Overwrite existing files? Default FALSE #' @param verbose Extra debug output? Default FALSE #' @param ... further arguments, currently ignored @@ -35,6 +39,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, site_id, lat.in, lon.in, model = "CESM2", scenario = "ssp370", + resolution = "d01", overwrite = FALSE, verbose = FALSE, ...) { if (!requireNamespace("caladaptaer", quietly = TRUE)) { @@ -50,8 +55,9 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, } # null guard, convert_input sometimes passes NULL for optional args - if (is.null(model)) model <- "CESM2" - if (is.null(scenario)) scenario <- "ssp370" + if (is.null(model)) model <- "CESM2" + if (is.null(scenario)) scenario <- "ssp370" + if (is.null(resolution)) resolution <- "d01" start_year <- lubridate::year(start_date) end_year <- lubridate::year(end_date) @@ -135,7 +141,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, for (wrf_var in names(wrf_to_cf)) { cache_key <- paste("caladapt_grid", model, scenario, - wrf_var, year, sep = "_") + resolution, wrf_var, year, sep = "_") cache_file <- file.path(tempdir(), paste0(cache_key, ".nc")) if (file.exists(cache_file)) { @@ -152,7 +158,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, model = model, scenario = scenario, timescale = "1hr", - resolution = "d01", + resolution = resolution, start_time = year_start, end_time = year_end ) diff --git a/modules/data.atmosphere/man/download.CalAdaptWRF.Rd b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd index 46b8fb5550..ff0f8b9ca8 100644 --- a/modules/data.atmosphere/man/download.CalAdaptWRF.Rd +++ b/modules/data.atmosphere/man/download.CalAdaptWRF.Rd @@ -13,6 +13,7 @@ download.CalAdaptWRF( lon.in, model = "CESM2", scenario = "ssp370", + resolution = "d01", overwrite = FALSE, verbose = FALSE, ... @@ -35,6 +36,11 @@ download.CalAdaptWRF( \item{scenario}{SSP experiment id, default "ssp370"} +\item{resolution}{WRF nested-domain id: "d01" (45 km, default and only +one with full coverage), "d02" (9 km), or "d03" (3 km). Higher +resolutions are only partially released; check +\code{caladaptaer::cae_check_variables()} for availability.} + \item{overwrite}{Overwrite existing files? Default FALSE} \item{verbose}{Extra debug output? Default FALSE} From cab2ce86720e73cc2319c65bd4da4e77e14ffb57 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Tue, 28 Apr 2026 23:00:27 -0400 Subject: [PATCH 17/19] use ud_convert for hour to seconds --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index 8ee9ec4fd8..d8e8acbc09 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -59,6 +59,8 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, if (is.null(scenario)) scenario <- "ssp370" if (is.null(resolution)) resolution <- "d01" + hour_to_second <- PEcAn.utils::ud_convert(1, "h", "s") + start_year <- lubridate::year(start_date) end_year <- lubridate::year(end_date) @@ -185,9 +187,9 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, ##unit conversions # precip: WRF hourly accumulation (mm) -> CF flux (kg/m2/s) - # 1 mm water = 1 kg/m2, divide by 3600s for hourly timestep + # 1 mm water = 1 kg/m2, divide by seconds-per-hour if ("prec" %in% names(dat.list)) { - dat.list[["prec"]] <- dat.list[["prec"]] / 3600 + dat.list[["prec"]] <- dat.list[["prec"]] / hour_to_second } # specific humidity: WRF stores mixing ratio q (kg/kg) From 849fc7e057d97e086a7ef65303ef358a944815eb Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 29 Apr 2026 14:03:45 -0400 Subject: [PATCH 18/19] Revert "swap rds cache for netcdf with lazy read" This reverts commit 48742b16a7de9262980bbcccc21a8d461d2767bf. --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index d8e8acbc09..2aefd702cf 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -144,15 +144,13 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, for (wrf_var in names(wrf_to_cf)) { cache_key <- paste("caladapt_grid", model, scenario, resolution, wrf_var, year, sep = "_") - cache_file <- file.path(tempdir(), paste0(cache_key, ".nc")) + cache_file <- file.path(tempdir(), paste0(cache_key, ".rds")) if (file.exists(cache_file)) { if (verbose) { PEcAn.logger::logger.debug(" cache hit: ", wrf_var, " ", year) } - # lazy read so st_extract pulls only the requested cell - # rather than deserializing the full grid into memory - grid <- stars::read_stars(cache_file, proxy = TRUE) + grid <- readRDS(cache_file) } else { PEcAn.logger::logger.info(" fetching ", wrf_var, " from S3") grid <- caladaptaer::cae_fetch( @@ -164,7 +162,7 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, start_time = year_start, end_time = year_end ) - stars::write_stars(grid, cache_file) + saveRDS(grid, cache_file) } # grab time dimension and build the projected point once From c5189a6ddf156ae7f2f3afaf4b6bcf5e5ff181f8 Mon Sep 17 00:00:00 2001 From: divne7022 Date: Wed, 29 Apr 2026 14:25:19 -0400 Subject: [PATCH 19/19] Revert "use ud_convert for hour to seconds" This reverts commit cab2ce86720e73cc2319c65bd4da4e77e14ffb57. --- modules/data.atmosphere/R/download.CalAdaptWRF.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/data.atmosphere/R/download.CalAdaptWRF.R b/modules/data.atmosphere/R/download.CalAdaptWRF.R index 2aefd702cf..3f810e24ed 100644 --- a/modules/data.atmosphere/R/download.CalAdaptWRF.R +++ b/modules/data.atmosphere/R/download.CalAdaptWRF.R @@ -59,8 +59,6 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, if (is.null(scenario)) scenario <- "ssp370" if (is.null(resolution)) resolution <- "d01" - hour_to_second <- PEcAn.utils::ud_convert(1, "h", "s") - start_year <- lubridate::year(start_date) end_year <- lubridate::year(end_date) @@ -185,9 +183,9 @@ download.CalAdaptWRF <- function(outfolder, start_date, end_date, ##unit conversions # precip: WRF hourly accumulation (mm) -> CF flux (kg/m2/s) - # 1 mm water = 1 kg/m2, divide by seconds-per-hour + # 1 mm water = 1 kg/m2, divide by 3600s for hourly timestep if ("prec" %in% names(dat.list)) { - dat.list[["prec"]] <- dat.list[["prec"]] / hour_to_second + dat.list[["prec"]] <- dat.list[["prec"]] / 3600 } # specific humidity: WRF stores mixing ratio q (kg/kg)