Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/data.atmosphere/R/Ameriflux_met_ensemble.R
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ AmeriFlux_met_ensemble <- function(site_id,
verbose = verbose
)

fill_vars <- coverage_results$fill_vars
fill_vars <- coverage_results$fill_vars_cds

if (length(fill_vars) > 0) {
start_year <- lubridate::year(as.Date(start_date))
Expand Down
8 changes: 6 additions & 2 deletions modules/data.atmosphere/R/check_met_coverage_for_fallback.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
#'
#' @return list with:
#' \itemize{
#' \item fill_vars: ERA5 variables to request
#' \item fill_vars_cds: ERA5 CDS variable names to request
#' \item fill_vars_cf: CF standard names corresponding to \code{fill_vars_cds}
#' \item coverage: named list of coverage fractions
#' }
#'
Expand Down Expand Up @@ -69,8 +70,11 @@ check_met_coverage_for_fallback <- function(cf_file,
fill_vars <- c(fill_vars, "volumetric_soil_water_layer_1")
}

fill_vars_cf <- unname(cds_to_cf_varnames(fill_vars))

list(
fill_vars = fill_vars,
fill_vars_cds = fill_vars,
fill_vars_cf = fill_vars_cf,
coverage = list(
rg = rg_coverage,
par = par_coverage,
Expand Down
44 changes: 22 additions & 22 deletions modules/data.atmosphere/R/pecan_standard_met_table.R
Original file line number Diff line number Diff line change
Expand Up @@ -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 , ~era5_cds,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, ~era5 , ~era5_cds,

What's the difference between the existing ERA column and the new ERA5_cds?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The era5 and era5_cds columns represent two different stages of the ERA5 workflow and are not interchangeable.

  • era5: Short variable names (e.g., swvl1, ssrd) as stored in downloaded NetCDF files. Used by extract.nc.ERA5() to read variables from disk.
  • era5_cds: Full variable names required by the Copernicus CDS API (e.g., volumetric_soil_water_layer_1). Used by download.ERA5_cds() via the variables= argument when requesting data.

The CDS API requires the long-form names, while NetCDF files store variables using short names, so both are needed.

Most entries in era5_cds are intentionally NA. Only variables explicitly requested by the fallback pipeline via download.ERA5_cds() require a value.

Variables such as t2m or strd have era5 short names because they can be extracted from existing files, but they are not directly requested by the fallback pipeline. Their CDS names are therefore omitted to avoid implying otherwise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really do not think we want to be adding a separate column to this table for every different source of the same dataset.

@dlebauer dlebauer Apr 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@infotroph how do you suggest handling different versions of the same dataset that use different naming conventions? What are the advantages and disadvantages of each approach? More generally, what is your suggested fix for this bug?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we have 6 columns in this lookup table and 11 met2CF functions, it seems clear the de facto system is already for each function to handle its own name conversions -- and any unit conversions, which this table doesn't cover -- for itself.

Having all the PEcAn standard output names and units in a lookup table is definitely useful. I just don't think it's useful to make every input be listed here when all the logic for it already needs to be present in the relevant met2CF function.

"air_temperature" , "K" , TRUE, "airT" , "tasAdjust" , "tair" , "air" , "TA (C)" , "t2m" , NA,
"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" , NA,
"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 , NA,
"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" , NA,
"surface_downwelling_shortwave_flux_in_air" , "W m-2" , TRUE, "solar_radiation" , "rsdsAdjust" , "swdown" , "dswrf" , "Rg" , "ssrd" , "surface_solar_radiation_downwards",
"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" , NA,
"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 , NA,
"eastward_wind" , "m s-1" , TRUE, "eastward_wind" , NA , NA , NA , "CALC(WS+WD)" , "u10" , NA,
"northward_wind" , "m s-1" , TRUE, "northward_wind" , NA , NA , NA , "CALC(WS+WD)" , "v10" , NA,
"volume_fraction_of_condensed_water_in_soil" , "1" , FALSE, "soilM" , NA , NA , NA , "SWC_1" , "swvl1" , "volumetric_soil_water_layer_1"
)
89 changes: 89 additions & 0 deletions modules/data.atmosphere/R/translate_met_varnames.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#' Translate meteorological variable names between naming conventions
#'
#' Looks up variable names in one column of the PEcAn standard meteorology
#' table and returns the corresponding names from another column.
#'
#' @param vars Character vector of variable names to translate.
#' @param from Character scalar. Column in \code{table} containing the
#' input naming convention.
#' @param to Character scalar. Column in \code{table} containing the
#' output naming convention.
#' @param table Data frame used as the lookup source. Defaults to
#' \code{pecan_standard_met_table}.
#'
#' @return Named character vector of translated variable names. Names are
#' the input values; elements are the mapped values. Variables not found
#' in \code{table[[from]]} return \code{NA} and emit a log warning.
#' Returns \code{character(0)} for empty input.
#'
#' @details
#' Rows where \code{table[[from]]} is \code{NA} or empty string are excluded
#' from the lookup. Translation is not guaranteed to be one-to-one; ensure
#' uniqueness in \code{table[[to]]} if round-trip mapping is required.
#'
#' @examples
#' # ERA5 CDS name to CF standard name
#' translate_met_varnames(
#' "surface_solar_radiation_downwards",
#' from = "era5_cds",
#' to = "cf_standard_name"
#' )
#'
#' # CF standard name to ERA5 CDS name
#' translate_met_varnames(
#' "surface_downwelling_shortwave_flux_in_air",
#' from = "cf_standard_name",
#' to = "era5_cds"
#' )
#'
#' @export
translate_met_varnames <- function(vars,
from,
to,
table = pecan_standard_met_table) {
if (length(vars) == 0L) return(character(0))

lookup <- table |>
dplyr::select(dplyr::all_of(c(from, to))) |>
dplyr::filter(!is.na(.data[[from]]), .data[[from]] != "")

result <- lookup[[to]]
names(result) <- lookup[[from]]

translated <- result[vars]

unknown <- vars[is.na(translated)]
if (length(unknown) > 0L) {
PEcAn.logger::logger.warn(
"translate_met_varnames: no", to, "mapping for", from,
"variable(s):", paste(unknown, collapse = ", "),
"-- returning NA for those entries"
)
}

translated
}


#' Translate ERA5 CDS API names to CF standard names
#'
#' @param cds_vars Character vector of ERA5 CDS API variable names.
#' @return Named character vector of CF standard names.
#' @noRd
cds_to_cf_varnames <- function(cds_vars) {
translate_met_varnames(cds_vars,
from = "era5_cds",
to = "cf_standard_name")
}


#' Translate CF standard names to ERA5 CDS API names
#'
#' @param cf_vars Character vector of CF standard names.
#' @return Named character vector of ERA5 CDS variable names.
#' @noRd
cf_to_cds_varnames <- function(cf_vars) {
translate_met_varnames(cf_vars,
from = "cf_standard_name",
to = "era5_cds")
}
Loading
Loading