From 175ca78dda3a7c00fc3426e7cc4ed5c5386e6880 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sat, 23 Oct 2021 11:27:42 +0200 Subject: [PATCH 01/43] take savefig from example to setup block in Global radiation section in walkthrough.md --- docs/src/walkthrough.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 7bebc13..4c07eb6 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -152,6 +152,9 @@ doy = 160 datetimes = DateTime(2021) .+Day(doy-1) .+ Hour.(hours) #.- Second(round(long*deg2second)) res3 = @suppress_err @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDataFrame(_) @df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad") +``` + +```@setup doc savefig("fig/globrad.svg") ``` From a6d8ed8c8a18ff7b40eb785832dbb7716eb3d2a5 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sat, 23 Oct 2021 12:38:18 +0200 Subject: [PATCH 02/43] replace \code{...} in R docu --- inst/fromR/WUE_metrics.jl | 2 +- inst/fromR/aerodynamic_conductance.jl | 94 ++++++++++++------------ inst/fromR/bigleaf_constants.jl | 8 +- inst/fromR/bigleaf_physiology.jl | 78 ++++++++++---------- inst/fromR/boundary_layer_conductance.jl | 68 ++++++++--------- inst/fromR/check_input.jl | 4 +- inst/fromR/datasets_description.jl | 64 ++++++++-------- inst/fromR/decoupling.jl | 18 ++--- inst/fromR/energy_balance.jl | 12 +-- inst/fromR/evapotranspiration.jl | 70 +++++++++--------- inst/fromR/filter_data.jl | 80 ++++++++++---------- inst/fromR/meteorological_variables.jl | 42 +++++------ inst/fromR/potential_radiation.jl | 4 +- inst/fromR/stability_correction.jl | 12 +-- inst/fromR/surface_conditions.jl | 18 ++--- inst/fromR/surface_conductance.jl | 28 +++---- inst/fromR/surface_roughness.jl | 68 ++++++++--------- inst/fromR/unit_conversions.jl | 10 +-- 18 files changed, 340 insertions(+), 340 deletions(-) diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index 3943b30..c744d04 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -54,7 +54,7 @@ #' Zhou, S., et al., 2014: The effect of vapor pressure deficit on water #' use efficiency at the sub-daily time scale. Geophysical Research Letters 41. #' -#' @seealso \code{\link{stomatal_slope}} for a measure of intrinsic WUE +#' @seealso `\link{stomatal_slope`} for a measure of intrinsic WUE #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index cb9cebb..2b3a59f 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -16,27 +16,27 @@ #' - zr Instrument (reference) height (m) #' - zh Canopy height (m) #' - d Zero-plane displacement height (m) -#' - z0m Roughness length for momentum (m), optional; if not provided, it is estimated from \code{roughness_parameters} -#' (method="wind_profile"). Only used if \code{wind_profile = TRUE} and/or \code{Rb_model} = \code{"Su_2001"} or -#' \code{"Choudhury_1988"}. -#' - Dl Characteristic leaf dimension (m) (if \code{Rb_model} = \code{"Su_2001"}) -#' or leaf width (if \code{Rb_model} = \code{"Choudhury_1988"}); ignored otherwise. -#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if \code{Rb_model = "Su_2001"}. +#' - z0m Roughness length for momentum (m), optional; if not provided, it is estimated from `roughness_parameters` +#' (method="wind_profile"). Only used if `wind_profile = TRUE` and/or `Rb_model` = `"Su_2001"` or +#' `"Choudhury_1988"`. +#' - Dl Characteristic leaf dimension (m) (if `Rb_model` = `"Su_2001"`) +#' or leaf width (if `Rb_model` = `"Choudhury_1988"`); ignored otherwise. +#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if `Rb_model = "Su_2001"`. #' Defaults to 2. -#' - fc Fractional vegetation cover (-); only used if \code{Rb_model = "Su_2001"}. See Details. -#' - LAI One-sided leaf area index (m2 m-2); only used if \code{Rb_model} = \code{"Choudhury_1988"} or \code{"Su_2001"}. -#' - Cd Foliage drag coefficient (-); only used if \code{Rb_model = "Su_2001"}. -#' - hs Roughness length of bare soil (m); only used if \code{Rb_model = "Su_2001"}. +#' - fc Fractional vegetation cover (-); only used if `Rb_model = "Su_2001"`. See Details. +#' - LAI One-sided leaf area index (m2 m-2); only used if `Rb_model` = `"Choudhury_1988"` or `"Su_2001"`. +#' - Cd Foliage drag coefficient (-); only used if `Rb_model = "Su_2001"`. +#' - hs Roughness length of bare soil (m); only used if `Rb_model = "Su_2001"`. #' - wind_profile Should Ga for momentum be calculated based on the logarithmic wind profile equation? -#' Defaults to \code{FALSE}. -#' - stab_correction Should stability correction be applied? Defaults to \code{TRUE}. Ignored if \code{wind_profile = FALSE}. -#' - stab_formulation Stability correction function. Either \code{"Dyer_1970"} (default) or -#' \code{"Businger_1971"}. Ignored if \code{wind_profile = FALSE} or if \code{stab_correction = FALSE}. -#' - Rb_model Boundary layer resistance formulation. One of \code{"Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"}. -#' - kB_h kB-1 value for heat transfer; only used if \code{Rb_model = "constant_kB-1"} +#' Defaults to `FALSE`. +#' - stab_correction Should stability correction be applied? Defaults to `TRUE`. Ignored if `wind_profile = FALSE`. +#' - stab_formulation Stability correction function. Either `"Dyer_1970"` (default) or +#' `"Businger_1971"`. Ignored if `wind_profile = FALSE` or if `stab_correction = FALSE`. +#' - Rb_model Boundary layer resistance formulation. One of `"Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"`. +#' - kB_h kB-1 value for heat transfer; only used if `Rb_model = "constant_kB-1"` #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than -#' \code{Sc_name} +#' `Sc_name` #' - constants k - von Karman constant \cr #' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' Kelvin - conversion degree Celsius to Kelvin \cr @@ -44,7 +44,7 @@ #' pressure0 - reference atmospheric pressure at sea level (Pa) \cr #' Tair0 - reference air temperature (K) \cr #' Sc_CO2 - Schmidt number for CO2 \cr -#' Pr - Prandtl number (if \code{Sc} is provided) +#' Pr - Prandtl number (if `Sc` is provided) #' #' # Details @@ -64,44 +64,44 @@ #' additional stability correction function. #' #' An alternative method to calculate Ra_m is provided -#' (calculated if \code{wind_profile = TRUE}): +#' (calculated if `wind_profile = TRUE`): #' #' \deqn{Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)} #' #' If the roughness parameters z0m and d are unknown, they can be estimated using -#' \code{\link{roughness_parameters}}. The argument \code{stab_formulation} determines the stability correction function used +#' `\link{roughness_parameters`}. The argument `stab_formulation` determines the stability correction function used #' to account for the effect of atmospheric stability on Ra_m (Ra_m is lower for unstable #' and higher for stable stratification). Stratification is based on a stability parameter zeta (z-d/L), #' where z = reference height, d the zero-plane displacement height, and L the Monin-Obukhov length, -#' calculated with \code{\link{Monin_Obukhov_length}} -#' The stability correction function is chosen by the argument \code{stab_formulation}. Options are -#' \code{"Dyer_1970"} and \code{"Businger_1971"}. +#' calculated with `\link{Monin_Obukhov_length`} +#' The stability correction function is chosen by the argument `stab_formulation`. Options are +#' `"Dyer_1970"` and `"Businger_1971"`. #' #' The model used to determine the canopy boundary layer resistance for heat (Rb_h) is specified by -#' the argument \code{Rb_model}. The following options are implemented: -#' \code{"Thom_1972"} is an empirical formulation based on the friction velocity (ustar) (Thom 1972): +#' the argument `Rb_model`. The following options are implemented: +#' `"Thom_1972"` is an empirical formulation based on the friction velocity (ustar) (Thom 1972): #' #' \deqn{Rb_h = 6.2ustar^-0.667} #' -#' The model by Choudhury & Monteith 1988 (\code{Rb_model = "Choudhury_1988"}), -#' calculates Rb_h based on leaf width, LAI and ustar (Note that function argument \code{Dl} +#' The model by Choudhury & Monteith 1988 (`Rb_model = "Choudhury_1988"`), +#' calculates Rb_h based on leaf width, LAI and ustar (Note that function argument `Dl` #' represents leaf width (w) and not characteristic leaf dimension (Dl) -#' if \code{Rb_model} = \code{"Choudhury_1988"}): +#' if `Rb_model` = `"Choudhury_1988"`): #' #' \deqn{Gb_h = LAI((0.02/\alpha)*sqrt(u(zh)/w)*(1-exp(-\alpha/2)))} #' #' where \eqn{\alpha} is a canopy attenuation coefficient modeled in dependence on LAI, -#' u(zh) is wind speed at canopy height (calculated from \code{\link{wind_profile}}), -#' and w is leaf width (m). See \code{\link{Gb_Choudhury}} for further details. +#' u(zh) is wind speed at canopy height (calculated from `\link{wind_profile`}), +#' and w is leaf width (m). See `\link{Gb_Choudhury`} for further details. #' -#' The option \code{Rb_model = "Su_2001"} calculates Rb_h based on the physically-based Rb model by Su et al. 2001, +#' The option `Rb_model = "Su_2001"` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, #' a simplification of the model developed by Massman 1999: #' #' \deqn{kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2} #' #' where Cd is a foliage drag coefficient (defaults to 0.2), fc is fractional #' vegetation cover, Bs-1 is the inverse Stanton number for bare soil surface, -#' and Ct is a heat transfer coefficient. See \code{\link{Gb_Su}} for +#' and Ct is a heat transfer coefficient. See `\link{Gb_Su`} for #' details on the model. #' #' @@ -125,15 +125,15 @@ #' \item{Gb_h}{Canopy boundary layer conductance for heat transfer (m s-1)} #' \item{Rb_h}{Canopy boundary layer resistance for heat transfer (s m-1)} #' \item{kB_h}{kB-1 parameter for heat transfer} -#' \item{zeta}{Stability parameter 'zeta' (NA if \code{wind_profile = FALSE})} -#' \item{psi_h}{Integrated stability correction function (NA if \code{wind_profile = FALSE})} +#' \item{zeta}{Stability parameter 'zeta' (NA if `wind_profile = FALSE`)} +#' \item{psi_h}{Integrated stability correction function (NA if `wind_profile = FALSE`)} #' \item{Ra_CO2}{Aerodynamic resistance for CO2 transfer (s m-1)} #' \item{Ga_CO2}{Aerodynamic conductance for CO2 transfer (m s-1)} #' \item{Gb_CO2}{Canopy boundary layer conductance for CO2 transfer (m s-1)} -#' \item{Ga_Sc_name}{Aerodynamic conductance for \code{Sc_name} (m s-1). Only added if \code{Sc_name} and -#' the respective \code{Sc} are provided} -#' \item{Gb_Sc_name}{Boundary layer conductance for \code{Sc_name} (m s-1). Only added if \code{Sc_name} and -#' the respective \code{Sc} are provided} +#' \item{Ga_Sc_name}{Aerodynamic conductance for `Sc_name` (m s-1). Only added if `Sc_name` and +#' the respective `Sc` are provided} +#' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and +#' the respective `Sc` are provided} #' #' @note The roughness length for water and heat (z0h) is not returned by this function, but #' it can be calculated from the following relationship (e.g. Verma 1989): @@ -144,20 +144,20 @@ #' #' \deqn{z0h = z0m / exp(kB-1)} #' -#' \code{kB-1} is an output of this function. +#' `kB-1` is an output of this function. #' #' Input variables such as LAI, Dl, or zh can be either constants, or -#' vary with time (i.e. vectors of the same length as \code{data}). +#' vary with time (i.e. vectors of the same length as `data`). #' #' Note that boundary layer conductance to water vapor transfer (Gb_w) is often #' assumed to equal Gb_h. This assumption is also made in this R package, for -#' example in the function \code{\link{surface_conductance}}. +#' example in the function `\link{surface_conductance`}. #' -#' If the roughness length for momentum (\code{z0m}) is not provided as input, it is estimated -#' from the function \code{roughness_parameters} within \code{wind_profile} if \code{wind_profile = TRUE} -#' and/or \code{Rb_model} = \code{"Su_2001"} or \code{"Choudhury_1988"} The \code{roughness_parameters} -#' function estimates a single \code{z0m} value for the entire time period! If a varying \code{z0m} value -#' (e.g. across seasons or years) is required, \code{z0m} should be provided as input argument. +#' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +#' from the function `roughness_parameters` within `wind_profile` if `wind_profile = TRUE` +#' and/or `Rb_model` = `"Su_2001"` or `"Choudhury_1988"` The `roughness_parameters` +#' function estimates a single `z0m` value for the entire time period! If a varying `z0m` value +#' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' #' #' @references Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. @@ -173,7 +173,7 @@ #' Monteith, J_L., Unsworth, M_H., 2008: Principles of environmental physics. #' Third Edition. Elsevier Academic Press, Burlington, USA. #' -#' @seealso \code{\link{Gb_Thom}}, \code{\link{Gb_Choudhury}}, \code{\link{Gb_Su}} for calculations of Rb / Gb only +#' @seealso `\link{Gb_Thom`}, `\link{Gb_Choudhury`}, `\link{Gb_Su`} for calculations of Rb / Gb only #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/bigleaf_constants.jl b/inst/fromR/bigleaf_constants.jl index 8ce30cc..787bfd8 100755 --- a/inst/fromR/bigleaf_constants.jl +++ b/inst/fromR/bigleaf_constants.jl @@ -44,17 +44,17 @@ #' easily altered. E_g. the following command will change the value of #' the von Karman constant from 0.41 to 0.4: #' -#' \code{bigleaf_constants(k=0.4)} +#' `bigleaf_constants(k=0.4)` #' #' the value of a constant can be returned by calling: #' -#' \code{bigleaf_constants()$*name_of_constant*} +#' `bigleaf_constants()$*name_of_constant*` #' #' To permanently change the constants contained within this function (which #' makes sense for some of them, e.g. for the von Karman constant), -#' the command \code{\link[utils]{fixInNamespace}} can be used. E_g. +#' the command `\link[utils]{fixInNamespace`} can be used. E_g. #' -#' \code{fixInNamespace(bigleaf_constants,ns="bigleaf")} +#' `fixInNamespace(bigleaf_constants,ns="bigleaf")` #' #' Note that this has to be repeated every time the package is newly installed/loaded. #' diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index ab32abd..fa4b922 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -13,8 +13,8 @@ #' - GPP Gross primary productivity (umol CO2 m-2 s-1) #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as \code{NA} (\code{TRUE}) -#' or set to 0 (\code{FALSE}, the default)? +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' or set to 0 (`FALSE`, the default)? #' - constants DwDc - Ratio of the molecular diffusivities for water vapor and CO2 (-) #' #' # Details @@ -26,7 +26,7 @@ #' Note that Gs is required in mol m-2 s-1 for water vapor. Gs is converted to #' its value for CO2 internally. #' Ca can either be atmospheric CO2 concentration (as measured), or surface -#' CO2 concentration as calculated from \code{\link{surface_CO2}}. +#' CO2 concentration as calculated from `\link{surface_CO2`}. #' #' @note The equation is based on Fick's law of diffusion and is equivalent to the #' often used equation at leaf level (ci = ca - An/gs). @@ -87,7 +87,7 @@ end #' CO2 concentration using the Farquhar et al. 1980 model for C3 photosynthesis. #' #' - data Data_Frame or matrix with all required columns -#' - C3 C3 vegetation (\code{TRUE}, the default) or C4 vegetation (\code{FALSE})? +#' - C3 C3 vegetation (`TRUE`, the default) or C4 vegetation (`FALSE`)? #' - Temp Surface (or air) temperature (degC) #' - GPP Gross primary productivity (umol m-2 s-1) #' - Ci Bulk canopy intercellular CO2 concentration (umol mol-1) @@ -113,11 +113,11 @@ end #' - Jmax_dS Entropy term for Jmax (kJ mol-1 K-1) #' - Theta Curvature term in the light response function of J (-) #' - alpha_canopy Canopy absorptance (-) -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as \code{NA} (\code{TRUE}) -#' or set to 0 (\code{FALSE}, the default)? +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' or set to 0 (`FALSE`, the default)? #' - Ci_C4 intercellular CO2 concentration below which photosynthesis #' is considered to be CO2-limited (umol mol-1), ignored -#' if \code{C3 = TRUE}. +#' if `C3 = TRUE`. #' - constants Kelvin - conversion degree Celsius to Kelvin \cr #' Rgas - universal gas constant (J mol-1 K-1) \cr #' kJ2J - conversion kilojoule (kJ) to joule (J) \cr @@ -129,7 +129,7 @@ end #' transport rate at 25degC (Jmax25), which characterize photosynthetic capacity, #' are calculated as at leaf level. #' The required variables Gs and Ci can be calculated from -#' \code{\link{surface_conductance}} and \code{\link{intercellular_CO2}}, respectively. +#' `\link{surface_conductance`} and `\link{intercellular_CO2`}, respectively. #' #' Gas exchange parameters are taken from Bernacchi et al. 2001 (apparent values, which #' assume an infinite mesophyll conductance). Negative and very low Ci values @@ -151,8 +151,8 @@ end #' #' In this function, bulk canopy photosynthesis is assumed to be Rubisco/RuBP-regeneration #' limited, if incoming PPFD is above/below a specified threshold or range. These ranges -#' are determined by the parameters \code{PPFD_j} and \code{PPFD_c}. If, for example, -#' \code{PPFD_j = c(100,400)}, all conditions with a PPFD between 100 and 400 are assumed +#' are determined by the parameters `PPFD_j` and `PPFD_c`. If, for example, +#' `PPFD_j = c(100,400)`, all conditions with a PPFD between 100 and 400 are assumed #' to be in the RuBP-regeneration (i.e. light-limited) photosynthesis domain. The electron #' transport rate J is then only calculated for periods that meet this criterion. #' @@ -193,7 +193,7 @@ end #' #' \deqn{Vcmax = GPP} #' -#' Note that in addition to the range \code{PPFD_c}, the range \code{Ci_C4} +#' Note that in addition to the range `PPFD_c`, the range `Ci_C4` #' discards all periods with low Ci, in which photosynthesis is likely to #' be CO2-limited (see von Caemmerer 2000 for details). #' @@ -206,11 +206,11 @@ end #' #' @note The critical assumption is that bulk canopy photosynthesis is limited by #' one of the two limitation states. Incoming PPFD is assumed to determine -#' the limitation states. Note however that the ranges (\code{PPFD_j} and \code{PPFD_c}) +#' the limitation states. Note however that the ranges (`PPFD_j` and `PPFD_c`) #' are likely ecosystem-specific. E_g. dense canopies presumably require higher -#' \code{PPFD_c} thresholds than open canopies. A threshold of 500 umol m-2 s-1 PPFD +#' `PPFD_c` thresholds than open canopies. A threshold of 500 umol m-2 s-1 PPFD #' for Rubisco-limited photosynthesis was assumed a reasonable working assumption (see Kosugi et al. 2013). -#' Here, \code{PPFD_c} defaults to 1000 umol m-2 s-1. Note that even under very high/low irradiances, +#' Here, `PPFD_c` defaults to 1000 umol m-2 s-1. Note that even under very high/low irradiances, #' not all photosynthetically active plant material of an ecosystem will be in the same #' limitation state. Note that parameters describing bulk canopy photosynthetic capacity are not directly #' comparable to their leaf-level counterparts, as the former integrate over the entire canopy @@ -248,7 +248,7 @@ end #' von Caemmerer, 2000: Biochemical models of leaf photosynthesis. Techniques #' in plant sciences No. 2. CSIRO Publishing, Collingwood VIC, Australia. #' -#' @seealso \code{\link{intercellular_CO2}}, \code{\link{Arrhenius_temp_response}} +#' @seealso `\link{intercellular_CO2`}, `\link{Arrhenius_temp_response`} #' #' ```@example; output = false #' ``` @@ -482,21 +482,21 @@ end #' - VPD Vapor pressure deficit (kPa) #' - Ca Atmospheric CO2 concentration (air or surface) (umol mol-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 -#' - model Stomatal model used. One of \code{"USO","Ball&Berry","Leuning"}. -#' - robust_nls Use robust nonlinear regression (\code{\link[robustbase]{nlrob}})? Default is \code{FALSE}. +#' - model Stomatal model used. One of `"USO","Ball&Berry","Leuning"`. +#' - robust_nls Use robust nonlinear regression (`\link[robustbase]{nlrob`})? Default is `FALSE`. #' - nmin Minimum number of data required to perform the fit; defaults to 40. #' - fitg0 Should g0 and g1 be fitted simultaneously? -#' - g0 Minimum stomatal conductance (mol m-2 s-1); ignored if \code{fitg0 = TRUE}. -#' - fitD0 Should D0 be fitted along with g1 (and g0 if \code{fitg0 = TRUE})?; only used if \code{model = "Leuning"}. -#' - D0 Stomatal sensitivity parameter to VPD; only used if \code{model = "Leuning"} and \code{fitD0 = FALSE}. -#' - Gamma Canopy CO2 compensation point (umol mol-1); only used if \code{model = "Leuning"}. +#' - g0 Minimum stomatal conductance (mol m-2 s-1); ignored if `fitg0 = TRUE`. +#' - fitD0 Should D0 be fitted along with g1 (and g0 if `fitg0 = TRUE`)?; only used if `model = "Leuning"`. +#' - D0 Stomatal sensitivity parameter to VPD; only used if `model = "Leuning"` and `fitD0 = FALSE`. +#' - Gamma Canopy CO2 compensation point (umol mol-1); only used if `model = "Leuning"`. #' Can be a constant or a variable. Defaults to 50 umol mol-1. #' - constants Kelvin - conversion degree Celsius to Kelvin \cr #' Rgas - universal gas constant (J mol-1 K-1) \cr #' DwDc - Ratio of the molecular diffusivities for water vapor and CO2 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as \code{NA} (\code{TRUE}) -#' or set to 0 (\code{FALSE}, the default)? -#' - ... Additional arguments to \code{\link[stats]{nls}} or \code{\link[robustbase]{nlrob}} if \code{robust_nls = TRUE}. +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' or set to 0 (`FALSE`, the default)? +#' - ... Additional arguments to `\link[stats]{nls`} or `\link[robustbase]{nlrob`} if `robust_nls = TRUE`. #' #' # Details All stomatal models were developed at leaf-level, but its parameters @@ -518,16 +518,16 @@ end #' plant species. #' The equations above are valid at leaf-level. At ecosystem level, An is replaced by GPP (or GPP - Rleaf, #' where Rleaf is leaf respiration), and gs (stomatal conductance) by Gs (surface conductance). -#' The parameters in the models are estimated using nonlinear regression (\code{\link[stats]{nls}}) if -#' \code{robust_nls = FALSE} and weighted nonlinear regression if \code{robust_nls = TRUE}. -#' The weights are calculated from \code{\link[robustbase]{nlrob}}, and \code{\link[stats]{nls}} +#' The parameters in the models are estimated using nonlinear regression (`\link[stats]{nls`}) if +#' `robust_nls = FALSE` and weighted nonlinear regression if `robust_nls = TRUE`. +#' The weights are calculated from `\link[robustbase]{nlrob`}, and `\link[stats]{nls`} #' is used for the actual fitting. #' Alternatively to measured VPD and Ca (i.e. conditions at instrument height), conditions at -#' the big-leaf surface can be provided. Those can be calculated using \code{\link{surface_conditions}}. +#' the big-leaf surface can be provided. Those can be calculated using `\link{surface_conditions`}. #' #' #' # Value - A \code{nls} model object, containing information on the fitted parameters, their uncertainty range, + A `nls` model object, containing information on the fitted parameters, their uncertainty range, #' model fit, etc. #' #' @references Medlyn B_E., et al., 2011: Reconciling the optimal and empirical approaches to @@ -544,7 +544,7 @@ end #' Knauer, J. et al., 2018: Towards physiologically meaningful water-use efficiency estimates #' from eddy covariance data. Global Change Biology 24, 694-710. #' -#' @seealso \code{\link{surface_conductance}} +#' @seealso `\link{surface_conductance`} #' #' ```@example; output = false #' ``` @@ -749,7 +749,7 @@ end #' - PPFD Photosynthetic photon flux density (umol m-2 s-1) #' - PPFD_ref Reference PPFD (umol m-2 s-1) for which GPP_ref is estimated. #' Default is 2000 umol m-2 s-1. -#' - ... Additional arguments to \code{\link[stats]{nls}} +#' - ... Additional arguments to `\link[stats]{nls`} #' #' # Details A rectangular light response curve is fitted to NEE data. The curve @@ -767,14 +767,14 @@ end #' curve is that GPP_ref at PPFD_ref is more readily interpretable #' as it constitutes a value observed in the ecosystem, in contrast to #' GPP_ref (mostly named 'beta') in the standard model that occurs at infinite light. -#' \code{PPFD_ref} defaults to 2000 umol m-2 s-1, but other values can be used. For +#' `PPFD_ref` defaults to 2000 umol m-2 s-1, but other values can be used. For #' further details refer to Falge et al. 2001. #' #' @note Note the sign convention. Negative NEE indicates that carbon is taken up #' by the ecosystem. Reco has to be 0 or larger. #' #' # Value - A \code{nls} model object containing estimates (+/- SE) for alpha and GPP_ref. + A `nls` model object containing estimates (+/- SE) for alpha and GPP_ref. #' #' @references Falge E., et al. 2001: Gap filling strategies for defensible annual #' sums of net ecosystem exchange. Agricultural and Forest Meteorology 107, @@ -827,7 +827,7 @@ end #' # Value \item{LUE -}{Light use efficiency (-)} #' -#' @seealso \code{\link{energy_use_efficiency}} +#' @seealso `\link{energy_use_efficiency`} #' #' ```@example; output = false #' ``` @@ -853,7 +853,7 @@ end #' - data Data_frame or matrix containing all required columns #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - VPD Vapor pressure deficit (kPa) -#' - ... Additional arguments to \code{\link[stats]{nls}} +#' - ... Additional arguments to `\link[stats]{nls`} #' #' # Details The function fits the following equation (Oren et al. 1999): @@ -862,12 +862,12 @@ end #' #' where b is the reference surface conductance (Gs) at VPD=1kPa (in mol m-2 s-1), #' and m is the sensitivity parameter of Gs to VPD (in mol m-2 s-1 log(kPa)-1). -#' The two parameters b and m are fitted using \code{\link[stats]{nls}}. +#' The two parameters b and m are fitted using `\link[stats]{nls`}. #' VPD can be the one directly measured at instrument height, or the -#' one at the surface, as returned by \code{\link{surface_conditions}}. +#' one at the surface, as returned by `\link{surface_conditions`}. #' #' # Value - A \code{nls} model object containing (amongst others) estimates for the mean + A `nls` model object containing (amongst others) estimates for the mean #' and standard errors of the parameters m and b. #' #' @references Oren R., et al. 1999: Survey and synthesis of intra- and interspecific @@ -877,7 +877,7 @@ end #' Novick K_A., et al. 2016: The increasing importance of atmospheric demand #' for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. #' -#' @seealso \code{\link{surface_conductance}} +#' @seealso `\link{surface_conductance`} #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index 32bb20b..38bce8f 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -10,10 +10,10 @@ #' - ustar Friction velocity (m s-1) #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additional quantities, has to be of same length than -#' \code{Sc_name} +#' `Sc_name` #' - constants k - von-Karman constant \cr #' Sc_CO2 - Schmidt number for CO2 \cr -#' Pr - Prandtl number (if \code{Sc} is provided) +#' Pr - Prandtl number (if `Sc` is provided) #' #' #' # Details @@ -33,8 +33,8 @@ #' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} #' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} #' \item{kB_h}{kB-1 parameter for heat transfer} -#' \item{Gb_Sc_name}{Boundary layer conductance for \code{Sc_name} (m s-1). Only added if \code{Sc_name} and -#' \code{Sc_name} are provided} +#' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and +#' `Sc_name` are provided} #' #' @references Thom, A., 1972: Momentum, mass and heat exchange of vegetation. #' Quarterly Journal of the Royal Meteorological Society 98, 124-134. @@ -43,7 +43,7 @@ #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso \code{\link{Gb_Choudhury}}, \code{\link{Gb_Su}}, \code{\link{aerodynamic_conductance}} +#' @seealso `\link{Gb_Choudhury`}, `\link{Gb_Su`}, `\link{aerodynamic_conductance`} #' #' ```@example; output = false #' ``` @@ -96,25 +96,25 @@ end #' - LAI One-sided leaf area index #' - zh Canopy height (m) #' - zr Instrument (reference) height (m) -#' - d Zero-plane displacement height (-), can be calculated using \code{roughness_parameters} -#' - z0m Roughness length for momentum (m). If not provided, calculated from \code{roughness_parameters} -#' within \code{wind_profile} -#' - stab_formulation Stability correction function used (If \code{stab_correction = TRUE}). -#' Either \code{"Dyer_1970"} or \code{"Businger_1971"}. +#' - d Zero-plane displacement height (-), can be calculated using `roughness_parameters` +#' - z0m Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` +#' within `wind_profile` +#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). +#' Either `"Dyer_1970"` or `"Businger_1971"`. #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than -#' \code{Sc_name} +#' `Sc_name` #' - constants k - von-Karman constant \cr #' Sc_CO2 - Schmidt number for CO2 \cr -#' Pr - Prandtl number (if \code{Sc} is provided) +#' Pr - Prandtl number (if `Sc` is provided) #' #' # Value A data frame with the following columns: #' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} #' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} #' \item{kB_h}{kB-1 parameter for heat transfer} -#' \item{Gb_Sc_name}{Boundary layer conductance for \code{Sc_name} (m s-1). Only added if \code{Sc_name} and -#' \code{Sc_name} are provided} +#' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and +#' `Sc_name` are provided} #' #' # Details Boundary layer conductance according to Choudhury & Monteith 1988 is @@ -138,10 +138,10 @@ end #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' @note If the roughness length for momentum (\code{z0m}) is not provided as input, it is estimated -#' from the function \code{roughness_parameters} within \code{wind_profile}. This function -#' estimates a single \code{z0m} value for the entire time period! If a varying \code{z0m} value -#' (e.g. across seasons or years) is required, \code{z0m} should be provided as input argument. +#' @note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +#' from the function `roughness_parameters` within `wind_profile`. This function +#' estimates a single `z0m` value for the entire time period! If a varying `z0m` value +#' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' #' @references Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat #' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. @@ -154,7 +154,7 @@ end #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso \code{\link{Gb_Thom}}, \code{\link{Gb_Su}}, \code{\link{aerodynamic_conductance}} +#' @seealso `\link{Gb_Thom`}, `\link{Gb_Su`}, `\link{aerodynamic_conductance`} #' #' ```@example; output = false #' ``` @@ -230,33 +230,33 @@ end #' - H Sensible heat flux (W m-2) #' - zh Canopy height (m) #' - zr Reference height (m) -#' - d Zero-plane displacement height (-), can be calculated using \code{roughness_parameters} -#' - z0m Roughness length for momentum (m). If not provided, calculated from \code{roughness_parameters} -#' within \code{wind_profile} +#' - d Zero-plane displacement height (-), can be calculated using `roughness_parameters` +#' - z0m Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` +#' within `wind_profile` #' - Dl Leaf characteristic dimension (m) #' - fc Fractional vegetation cover [0-1] (if not provided, calculated from LAI) #' - LAI One-sided leaf area index (-) #' - N Number of leaf sides participating in heat exchange (defaults to 2) #' - Cd Foliage drag coefficient (-) #' - hs Roughness height of the soil (m) -#' - stab_formulation Stability correction function used (If \code{stab_correction = TRUE}). -#' Either \code{"Dyer_1970"} or \code{"Businger_1971"}. +#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). +#' Either `"Dyer_1970"` or `"Businger_1971"`. #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additional quantities, has to be of same length than -#' \code{Sc_name} +#' `Sc_name` #' - constants Kelvin - conversion degree Celsius to Kelvin \cr #' pressure0 - reference atmospheric pressure at sea level (Pa) \cr #' Tair0 - reference air temperature (K) \cr #' Sc_CO2 - Schmidt number for CO2 \cr -#' Pr - Prandtl number (if \code{Sc} is provided) +#' Pr - Prandtl number (if `Sc` is provided) #' #' # Value A DataFrame with the following columns: #' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} #' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} #' \item{kB_h}{kB-1 parameter for heat transfer} -#' \item{Gb_Sc_name}{Boundary layer conductance for \code{Sc_name} (m s-1). Only added if \code{Sc_name} and -#' \code{Sc_name} are provided} +#' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and +#' `Sc_name` are provided} #' #' # Details The formulation is based on the kB-1 model developed by Massman 1999. @@ -269,7 +269,7 @@ end #' \deqn{fc = 1 - exp(-LAI/2)} #' #' The wind speed at the top of the canopy is calculated using function -#' \code{\link{wind_profile}}. +#' `\link{wind_profile`}. #' #' Ct is the heat transfer coefficient of the leaf (Massman 1999): #' @@ -291,10 +291,10 @@ end #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' @note If the roughness length for momentum (\code{z0m}) is not provided as input, it is estimated -#' from the function \code{roughness_parameters} within \code{wind_profile}. This function -#' estimates a single \code{z0m} value for the entire time period! If a varying \code{z0m} value -#' (e.g. across seasons or years) is required, \code{z0m} should be provided as input argument. +#' @note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +#' from the function `roughness_parameters` within `wind_profile`. This function +#' estimates a single `z0m` value for the entire time period! If a varying `z0m` value +#' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' #' #' @references Su, Z., Schmugge, T., Kustas, W. & Massman, W., 2001: An evaluation of @@ -308,7 +308,7 @@ end #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso \code{\link{Gb_Thom}}, \code{\link{Gb_Choudhury}}, \code{\link{aerodynamic_conductance}} +#' @seealso `\link{Gb_Thom`}, `\link{Gb_Choudhury`}, `\link{aerodynamic_conductance`} #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/check_input.jl b/inst/fromR/check_input.jl index 21dd940..913f081 100755 --- a/inst/fromR/check_input.jl +++ b/inst/fromR/check_input.jl @@ -12,8 +12,8 @@ #' @note This function can be run for named variables (in which case the return #' value will be named according to the given name), or for placeholder #' variables that are assigned and named according to e.g. entries of a character -#' vector. In the latter case, the input variable has to be named as \code{"var"} or -#' \code{"var_qc"}. +#' vector. In the latter case, the input variable has to be named as `"var"` or +#' `"var_qc"`. #' #' @keywords internal function check_input(data,...) diff --git a/inst/fromR/datasets_description.jl b/inst/fromR/datasets_description.jl index 014f517..6daaabe 100755 --- a/inst/fromR/datasets_description.jl +++ b/inst/fromR/datasets_description.jl @@ -12,31 +12,31 @@ #' \item{doy}{day of year} #' \item{hour}{hour (0 - 23.5)} #' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of \code{Tair} [TA_F_QC]} +#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} #' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of \code{PPFD} [PPFD_IN_QC]} +#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} #' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of \code{VPD} [VPD_F_QC]} +#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} #' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} #' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of \code{precip} [P_F_QC]} +#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} #' \item{ustar}{friction velocity (m s-1) [USTAR]} #' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of \code{wind} [WS_F_QC]} +#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} #' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of \code{Ca} [CO2_F_MDS_QC]} +#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} #' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} #' \item{Rn}{Net radiation (W m-2) [NETRAD]} #' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of \code{LE} [LE_F_MDS_QC]} +#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} #' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of \code{H} [H_F_MDS_QC]} +#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} #' \item{G}{Ground heat flux (W m-2) [G_F_MDS]} -#' \item{G_qc}{Quality control of \code{G} [G_F_MDS_QC]} +#' \item{G_qc}{Quality control of `G` [G_F_MDS_QC]} #' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of \code{NEE} [NEE_VUT_USTAR50_QC]} +#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} #' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of \code{GPP} [NEE_VUT_USTAR50_QC]} +#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} #' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} #' } #' @@ -64,32 +64,32 @@ #' \item{doy}{day of year} #' \item{hour}{hour (0 - 23.5)} #' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of \code{Tair} [TA_F_QC]} +#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} #' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of \code{PPFD} [PPFD_IN_QC]} +#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} #' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of \code{VPD} [VPD_F_QC]} +#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} #' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} #' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of \code{precip} [P_F_QC]} +#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} #' \item{ustar}{friction velocity (m s-1) [USTAR]} #' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of \code{wind} [WS_F_QC]} +#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} #' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of \code{Ca} [CO2_F_MDS_QC]} +#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} #' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} #' \item{LW_down}{downward longwave radiation (W m-2) [LW_IN_F]} #' \item{Rn}{Net radiation (W m-2) [NETRAD]} #' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of \code{LE} [LE_F_MDS_QC]} +#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} #' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of \code{H} [H_F_MDS_QC]} +#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} #' \item{G}{Ground heat flux (W m-2) [G_F_MDS]} -#' \item{G_qc}{Quality control of \code{G} [G_F_MDS_QC]} +#' \item{G_qc}{Quality control of `G` [G_F_MDS_QC]} #' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of \code{NEE} [NEE_VUT_USTAR50_QC]} +#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} #' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of \code{GPP} [NEE_VUT_USTAR50_QC]} +#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} #' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} #' } #' @@ -117,29 +117,29 @@ #' \item{doy}{day of year} #' \item{hour}{hour (0 - 23.5)} #' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of \code{Tair} [TA_F_QC]} +#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} #' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of \code{PPFD} [PPFD_IN_QC]} +#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} #' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of \code{VPD} [VPD_F_QC]} +#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} #' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} #' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of \code{precip} [P_F_QC]} +#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} #' \item{ustar}{friction velocity (m s-1) [USTAR]} #' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of \code{wind} [WS_F_QC]} +#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} #' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of \code{Ca} [CO2_F_MDS_QC]} +#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} #' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} #' \item{Rn}{Net radiation (W m-2) [NETRAD]} #' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of \code{LE} [LE_F_MDS_QC]} +#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} #' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of \code{H} [H_F_MDS_QC]} +#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} #' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of \code{NEE} [NEE_VUT_USTAR50_QC]} +#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} #' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of \code{GPP} [NEE_VUT_USTAR50_QC]} +#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} #' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} #' } #' diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index 07aac90..fc60c52 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -11,12 +11,12 @@ #' - pressure Atmospheric pressure (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) #' - Gs Surface conductance (m s-1) -#' - approach Approach used to calculate omega. Either \code{"Jarvis&McNaughton_1986"} (default) -#' or \code{"Martin_1989"}. -#' - LAI Leaf area index (m2 m-2), only used if \code{approach = "Martin_1989"}. +#' - approach Approach used to calculate omega. Either `"Jarvis&McNaughton_1986"` (default) +#' or `"Martin_1989"`. +#' - LAI Leaf area index (m2 m-2), only used if `approach = "Martin_1989"`. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants Kelvin - conversion degree Celsius to Kelvin \cr #' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) \cr @@ -31,7 +31,7 @@ #' and similar conditions at the canopy surface compared to the atmosphere above #' the canopy. Values close to 1 indicate the opposite, i.e. decoupled conditions and #' a low stomatal control on transpiration (Jarvis & McNaughton 1986). \cr -#' The \code{"Jarvis&McNaughton_1986"} approach (default option) is the original +#' The `"Jarvis&McNaughton_1986"` approach (default option) is the original #' formulation for the decoupling coefficient, given by (for an amphistomatous #' canopy): #' @@ -42,7 +42,7 @@ #' with s being the slope of the saturation vapor pressure curve (Pa K-1), and \eqn{\gamma} the #' psychrometric constant (Pa K-1). #' -#' The approach \code{"Martin_1989"} by Martin 1989 additionally takes radiative coupling +#' The approach `"Martin_1989"` by Martin 1989 additionally takes radiative coupling #' into account: #' #' \deqn{\Omega = \frac{\epsilon + 1 + \frac{Gr}{Ga}}{\epsilon + (1 + \frac{Ga}{Gs}) (1 + \frac{Gr}{Ga})}}{% @@ -57,8 +57,8 @@ #' Martin P., 1989: The significance of radiative coupling between #' vegetation and the atmosphere. Agricultural and Forest Meteorology 49, 45-53. #' -#' @seealso \code{\link{aerodynamic_conductance}}, \code{\link{surface_conductance}}, -#' \code{\link{equilibrium_imposed_ET}} +#' @seealso `\link{aerodynamic_conductance`}, `\link{surface_conductance`}, +#' `\link{equilibrium_imposed_ET`} #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index 6de5dc7..0348dc6 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -64,7 +64,7 @@ end #' # Value \item{EUE -}{Energy use efficiency (-)} #' -#' @seealso \code{\link{light_use_efficiency}} +#' @seealso `\link{light_use_efficiency`} #' #' ```@example; output = false #' ``` @@ -101,10 +101,10 @@ end #' - LE Latent heat flux (W m-2) #' - H Sensible heat flux (W m-2) #' - instantaneous should the energy balance be calculated at the time step -#' of the observations (\code{TRUE}), or over the entire time period -#' provided as input (\code{FALSE}) -#' - missing_G_as_NA if \code{TRUE}, missing G are treated as \code{NA}s ,otherwise set to 0. -#' - missing_S_as_NA if \code{TRUE}, missing S are treated as \code{NA}s, otherwise set to 0. +#' of the observations (`TRUE`), or over the entire time period +#' provided as input (`FALSE`) +#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s ,otherwise set to 0. +#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' #' #' # Details @@ -123,7 +123,7 @@ end #' \item{r_squared}{r^2 of the OLS regression} #' \item{EBR}{energy balance ratio} #' -#' if \code{instantaneous = TRUE}, only \code{EBR} is returned. +#' if `instantaneous = TRUE`, only `EBR` is returned. #' #' @references Wilson K., et al. 2002: Energy balance closure at FLUXNET sites. #' Agricultural and Forest Meteorology 113, 223-243. diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index d57f774..369bb5c 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -13,41 +13,41 @@ #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - VPD Vapor pressure deficit (kPa); only used if \code{approach = "Penman-Monteith"}. -#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if \code{approach = "Penman-Monteith"}. -#' - approach Approach used. Either \code{"Priestley-Taylor"} (default), or \code{"Penman-Monteith"}. -#' - alpha Priestley-Taylor coefficient; only used if \code{approach = "Priestley-Taylor"}. +#' - VPD Vapor pressure deficit (kPa); only used if `approach = "Penman-Monteith"`. +#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = "Penman-Monteith"`. +#' - approach Approach used. Either `"Priestley-Taylor"` (default), or `"Penman-Monteith"`. +#' - alpha Priestley-Taylor coefficient; only used if `approach = "Priestley-Taylor"`. #' - Gs_pot Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; -#' only used if \code{approach = "Penman-Monteith"}. -#' - missing_G_as_NA if \code{TRUE}, missing G are treated as \code{NA}s, otherwise set to 0. -#' - missing_S_as_NA if \code{TRUE}, missing S are treated as \code{NA}s, otherwise set to 0. +#' only used if `approach = "Penman-Monteith"`. +#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air \cr #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) \cr -#' Rd - gas constant of dry air (J kg-1 K-1) (only used if \code{approach = "Penman-Monteith"}) \cr -#' Rgas - universal gas constant (J mol-1 K-1) (only used if \code{approach = "Penman-Monteith"}) \cr -#' Kelvin - conversion degree Celsius to Kelvin (only used if \code{approach = "Penman-Monteith"}) \cr +#' Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = "Penman-Monteith"`) \cr +#' Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = "Penman-Monteith"`) \cr +#' Kelvin - conversion degree Celsius to Kelvin (only used if `approach = "Penman-Monteith"`) \cr #' #' # Details Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 -#' if \code{approach = "Priestley-Taylor"} (the default): +#' if `approach = "Priestley-Taylor"` (the default): #' #' \deqn{LE_pot,PT = (\alpha * \Delta * (Rn - G - S)) / (\Delta + \gamma)} #' #' \eqn{\alpha} is the Priestley-Taylor coefficient, \eqn{\Delta} is the slope #' of the saturation vapor pressure curve (kPa K-1), and \eqn{\gamma} is the #' psychrometric constant (kPa K-1). -#' if \code{approach = "Penman-Monteith"}, potential evapotranspiration is calculated according +#' if `approach = "Penman-Monteith"`, potential evapotranspiration is calculated according #' to the Penman-Monteith equation: #' #' \deqn{LE_pot,PM = (\Delta * (Rn - G - S) + \rho * cp * VPD * Ga) / (\Delta + \gamma * (1 + Ga/Gs_pot)} #' #' where \eqn{\Delta} is the slope of the saturation vapor pressure curve (kPa K-1), #' \eqn{\rho} is the air density (kg m-3), and \eqn{\gamma} is the psychrometric constant (kPa K-1). -#' The value of \code{Gs_pot} is typically a maximum value of Gs observed at the site, e.g. the 90th +#' The value of `Gs_pot` is typically a maximum value of Gs observed at the site, e.g. the 90th #' percentile of Gs within the growing season. #' #' # Value @@ -55,10 +55,10 @@ #' \item{ET_pot}{Potential evapotranspiration (kg m-2 s-1)} #' \item{LE_pot}{Potential latent heat flux (W m-2)} #' -#' @note If the first argument \code{data} is provided (either a matrix or a DataFrame), +#' @note If the first argument `data` is provided (either a matrix or a DataFrame), #' the following variables can be provided as character (in which case they are interpreted as -#' the column name of \code{data}) or as numeric vectors, in which case they are taken -#' directly for the calculations. If \code{data} is not provided, all input variables have to be +#' the column name of `data`) or as numeric vectors, in which case they are taken +#' directly for the calculations. If `data` is not provided, all input variables have to be #' numeric vectors. #' #' @references Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux @@ -71,7 +71,7 @@ #' Novick, K_A., et al. 2016: The increasing importance of atmospheric demand #' for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. #' -#' @seealso \code{\link{surface_conductance}} +#' @seealso `\link{surface_conductance`} #' #' ```@example; output = false #' ``` @@ -160,16 +160,16 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if \code{TRUE}, missing G are treated as \code{NA}s, otherwise set to 0. -#' - missing_S_as_NA if \code{TRUE}, missing S are treated as \code{NA}s, otherwise set to 0. +#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air \cr -#' Rd - gas constant of dry air (J kg-1 K-1) (only if \code{approach = "Penman-Monteith"}) \cr -#' Rgas - universal gas constant (J mol-1 K-1) (only if \code{approach = "Penman-Monteith"}) \cr -#' Kelvin - conversion degree Celsius to Kelvin (only if \code{approach = "Penman-Monteith"}) \cr +#' Rd - gas constant of dry air (J kg-1 K-1) (only if `approach = "Penman-Monteith"`) \cr +#' Rgas - universal gas constant (J mol-1 K-1) (only if `approach = "Penman-Monteith"`) \cr +#' Kelvin - conversion degree Celsius to Kelvin (only if `approach = "Penman-Monteith"`) \cr #' #' @export function reference_ET(data,Gs_ref=0.0143,Tair="Tair",pressure="pressure",VPD="VPD",Rn="Rn",Ga="Ga_h", @@ -197,11 +197,11 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if \code{TRUE}, missing G are treated as \code{NA}s, otherwise set to 0. -#' - missing_S_as_NA if \code{TRUE}, missing S are treated as \code{NA}s, otherwise set to 0. +#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) \cr #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) @@ -212,7 +212,7 @@ end #' \deqn{ET = \Omega ET_eq + (1 - \Omega)ET_imp} #' #' where \eqn{\Omega} is the decoupling coefficient as calculated from -#' \code{\link{decoupling}}. \code{ET_eq} is the equilibrium evapotranspiration rate, +#' `\link{decoupling`}. `ET_eq` is the equilibrium evapotranspiration rate, #' the ET rate that would occur under uncoupled conditions, where the heat budget #' is dominated by radiation (when Ga -> 0): #' @@ -221,15 +221,15 @@ end #' where \eqn{\Delta} is the slope of the saturation vapor pressure curve (kPa K-1), #' \eqn{\lambda} is the latent heat of vaporization (J kg-1), and \eqn{\gamma} #' is the psychrometric constant (kPa K-1). -#' \code{ET_imp} is the imposed evapotranspiration rate, the ET rate +#' `ET_imp` is the imposed evapotranspiration rate, the ET rate #' that would occur under fully coupled conditions (when Ga -> inf): #' #' \deqn{ET_imp = (\rho * cp * VPD * Gs * \lambda) / \gamma} #' #' where \eqn{\rho} is the air density (kg m-3). #' -#' @note Surface conductance (Gs) can be calculated with \code{\link{surface_conductance}}. -#' Aerodynamic conductance (Ga) can be calculated using \code{\link{aerodynamic_conductance}}. +#' @note Surface conductance (Gs) can be calculated with `\link{surface_conductance`}. +#' Aerodynamic conductance (Ga) can be calculated using `\link{aerodynamic_conductance`}. #' #' # Value A DataFrame with the following columns: @@ -244,7 +244,7 @@ end #' Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' -#' @seealso \code{\link{decoupling}} +#' @seealso `\link{decoupling`} #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index 38eaaaa..8351ae9 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -9,43 +9,43 @@ #' #' - data Data_frame or matrix containing all required input variables in #' half-hourly or hourly resolution. Including year, month, day information -#' - quality_control Should quality control be applied? Defaults to \code{TRUE}. -#' - filter_growseas Should data be filtered for growing season? Defaults to \code{FALSE}. -#' - filter_precip Should precipitation filtering be applied? Defaults to \code{FALSE}. +#' - quality_control Should quality control be applied? Defaults to `TRUE`. +#' - filter_growseas Should data be filtered for growing season? Defaults to `FALSE`. +#' - filter_precip Should precipitation filtering be applied? Defaults to `FALSE`. #' - filter_vars Additional variables to be filtered. Vector of type character. #' - filter_vals_min Minimum values of the variables to be filtered. Numeric vector of -#' the same length than \code{filter_vars}. Set to \code{NA} to be ignored. +#' the same length than `filter_vars`. Set to `NA` to be ignored. #' - filter_vals_max Maximum values of the variables to be filtered. Numeric vector of -#' the same length than \code{filter_vars}. Set to \code{NA} to be ignored. -#' - NA_as_invalid If \code{TRUE} (the default) missing data are filtered out (applies to all variables). +#' the same length than `filter_vars`. Set to `NA` to be ignored. +#' - NA_as_invalid If `TRUE` (the default) missing data are filtered out (applies to all variables). #' - vars_qc Character vector indicating the variables for which quality filter should -#' be applied. Ignored if \code{quality_control = FALSE}. +#' be applied. Ignored if `quality_control = FALSE`. #' - quality_ext The extension to the variables' names that marks them as -#' quality control variables. Ignored if \code{quality_control = FALSE}. +#' quality control variables. Ignored if `quality_control = FALSE`. #' - good_quality Which values indicate good quality (i.e. not to be filtered) -#' in the quality control (qc) variables? Ignored if \code{quality_control = FALSE}. -#' - missing_qc_as_bad If quality control variable is \code{NA}, should the corresponding data point be -#' treated as bad quality? Defaults to \code{TRUE}. Ignored if \code{quality_control = FALSE}. +#' in the quality control (qc) variables? Ignored if `quality_control = FALSE`. +#' - missing_qc_as_bad If quality control variable is `NA`, should the corresponding data point be +#' treated as bad quality? Defaults to `TRUE`. Ignored if `quality_control = FALSE`. #' - precip Precipitation (mm time-1) -#' - GPP Gross primary productivity (umol m-2 s-1); Ignored if \code{filter_growseas = FALSE}. -#' - doy Day of year; Ignored if \code{filter_growseas = FALSE}. -#' - year Year; Ignored if \code{filter_growseas = FALSE}. +#' - GPP Gross primary productivity (umol m-2 s-1); Ignored if `filter_growseas = FALSE`. +#' - doy Day of year; Ignored if `filter_growseas = FALSE`. +#' - year Year; Ignored if `filter_growseas = FALSE`. #' - tGPP GPP threshold (fraction of 95th percentile of the GPP time series). -#' Must be between 0 and 1. Ignored if \code{filter_growseas} is \code{FALSE}. +#' Must be between 0 and 1. Ignored if `filter_growseas` is `FALSE`. #' - ws Window size used for GPP time series smoothing. -#' Ignored if \code{filter_growseas = FALSE}. +#' Ignored if `filter_growseas = FALSE`. #' - min_int Minimum time interval in days for a given state of growing season. -#' Ignored if \code{filter_growseas = FALSE}. +#' Ignored if `filter_growseas = FALSE`. #' - tprecip Precipitation threshold used to identify a precipitation event (mm). -#' Ignored if \code{filter_precip = FALSE}. +#' Ignored if `filter_precip = FALSE`. #' - precip_hours Number of hours removed following a precipitation event (h). -#' Ignored if \code{filter_precip = FALSE}. +#' Ignored if `filter_precip = FALSE`. #' - records_per_hour Number of observations per hour. I_e. 2 for half-hourly data. -#' - filtered_data_to_NA Logical. If \code{TRUE} (the default), all variables in the input -#' DataFrame/matrix are set to \code{NA} for the time step where ANY of the -#' \code{filter_vars} were beyond their acceptable range (as -#' determined by \code{filter_vals_min} and \code{filter_vals_max}). -#' If \code{FALSE}, values are not filtered, and an additional column 'valid' +#' - filtered_data_to_NA Logical. If `TRUE` (the default), all variables in the input +#' DataFrame/matrix are set to `NA` for the time step where ANY of the +#' `filter_vars` were beyond their acceptable range (as +#' determined by `filter_vals_min` and `filter_vals_max`). +#' If `FALSE`, values are not filtered, and an additional column 'valid' #' is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). #' - constants frac2percent - conversion between fraction and percent @@ -53,37 +53,37 @@ #' # Details This routine consists of two parts: #' -#' 1) Quality control: All variables included in \code{vars_qc} are filtered for +#' 1) Quality control: All variables included in `vars_qc` are filtered for #' good quality data. For these variables a corresponding quality variable with -#' the same name as the variable plus the extension as specified in \code{quality_ext} +#' the same name as the variable plus the extension as specified in `quality_ext` #' must be provided. For time steps where the value of the quality indicator is not included -#' in the argument \code{good_quality}, i.e. the quality is not considered as 'good', -#' its value is set to \code{NA}. +#' in the argument `good_quality`, i.e. the quality is not considered as 'good', +#' its value is set to `NA`. #' #' 2) Meteorological filtering. Under certain conditions (e.g. low ustar), the assumptions #' of the EC method are not fulfilled. Further, some data analysis require certain meteorological #' conditions, such as periods without rainfall, or active vegetation (growing season, daytime). #' The filter applied in this second step serves to exclude time periods that do not fulfill the criteria #' specified in the arguments. More specifically, time periods where one of the variables is higher -#' or lower than the specified thresholds (\code{filter_vals_min} and \code{filter_vals_max}) -#' are set to \code{NA} for all variables. If a threshold is set to \code{NA}, it will be ignored. +#' or lower than the specified thresholds (`filter_vals_min` and `filter_vals_max`) +#' are set to `NA` for all variables. If a threshold is set to `NA`, it will be ignored. #' #' # Value - If \code{filtered_data_to_NA = TRUE} (default), the input DataFrame/matrix with -#' observations which did not fulfill the filter criteria set to \code{NA}. -#' If \code{filtered_data_to_NA = FALSE}, the input DataFrame/matrix with an additional + If `filtered_data_to_NA = TRUE` (default), the input DataFrame/matrix with +#' observations which did not fulfill the filter criteria set to `NA`. +#' If `filtered_data_to_NA = FALSE`, the input DataFrame/matrix with an additional #' column "valid", which indicates whether all the data of a time step fulfill the #' filtering criteria (1) or not (0). #' -#' @note The thresholds set with \code{filter_vals_min} and \code{filter_vals_max} filter all data +#' @note The thresholds set with `filter_vals_min` and `filter_vals_max` filter all data #' that are smaller than ("<"), or greater than (">") the specified thresholds. That means #' if a variable has exactly the same value as the threshold, it will not be filtered. Likewise, -#' \code{tprecip} filters all data that are greater than \code{tprecip}. +#' `tprecip` filters all data that are greater than `tprecip`. #' #' Variables considered of bad quality (as specified by the corresponding quality control variables) -#' will be set to \code{NA} by this routine. Data that do not fulfill the filtering criteria are set to -#' \code{NA} if \code{filtered_data_to_NA = TRUE}. Note that with this option *all* variables of the same -#' time step are set to \code{NA}. Alternatively, if \code{filtered_data_to_NA = FALSE} data are not set to \code{NA}, +#' will be set to `NA` by this routine. Data that do not fulfill the filtering criteria are set to +#' `NA` if `filtered_data_to_NA = TRUE`. Note that with this option *all* variables of the same +#' time step are set to `NA`. Alternatively, if `filtered_data_to_NA = FALSE` data are not set to `NA`, #' and a new column "valid" is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). #' @@ -302,9 +302,9 @@ end #' in the delineation of the growing season. The window size defaults to 15 #' days, but depending on the ecosystem, other values can be appropriate. #' -#' The argument \code{min_int} serves to avoid short fluctuations in the +#' The argument `min_int` serves to avoid short fluctuations in the #' status growing season vs. no growing season by defining a minimum length -#' of the status. If a time interval shorter than \code{min_int} is labeled +#' of the status. If a time interval shorter than `min_int` is labeled #' as growing season or non-growing season, it is changed to the status of #' the neighboring values. #' diff --git a/inst/fromR/meteorological_variables.jl b/inst/fromR/meteorological_variables.jl index 0867df8..c071f7f 100755 --- a/inst/fromR/meteorological_variables.jl +++ b/inst/fromR/meteorological_variables.jl @@ -108,8 +108,8 @@ end #' corresponding slope of the saturation vapor pressure curve. #' #' - Tair Air temperature (deg C) -#' - formula Formula to be used. Either \code{"Sonntag_1990"} (Default), -#' \code{"Alduchov_1996"}, or \code{"Allen_1998"}. +#' - formula Formula to be used. Either `"Sonntag_1990"` (Default), +#' `"Alduchov_1996"`, or `"Allen_1998"`. #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details @@ -126,7 +126,7 @@ end #' #' \deqn{\Delta = dEsat / dTair} #' -#' which is solved using \code{\link[stats]{D}}. +#' which is solved using `\link[stats]{D`}. #' #' # Value A dataframe with the following columns: @@ -203,7 +203,7 @@ end #' \deqn{\gamma = cp * pressure / (eps * \lambda)} #' #' where \eqn{\lambda} is the latent heat of vaporization (J kg-1), -#' as calculated from \code{\link{latent_heat_vaporization}}. +#' as calculated from `\link{latent_heat_vaporization`}. #' #' # Value \item{\eqn{\gamma} -}{the psychrometric constant (kPa K-1)} @@ -275,11 +275,11 @@ end #' - gamma Psychrometric constant (kPa K-1) #' - accuracy Accuracy of the result (degC) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' @note Arguments \code{accuracy} and \code{Esat_formula} are passed to this function by wetbulb_temp(). +#' @note Arguments `accuracy` and `Esat_formula` are passed to this function by wetbulb_temp(). #' #' @importFrom stats optimize #' @@ -302,8 +302,8 @@ end #' - VPD Vapor pressure deficit (kPa) #' - accuracy Accuracy of the result (deg C) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) \cr #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) @@ -313,9 +313,9 @@ end #' #' \deqn{e = Esat(Tw) - gamma* (Tair - Tw)} #' -#' The equation is solved for Tw using \code{\link[stats]{optimize}}. -#' Actual vapor pressure e (kPa) is calculated from VPD using the function \code{\link{VPD_to_e}}. -#' The psychrometric constant gamma (kPa K-1) is calculated from \code{\link{psychrometric_constant}}. +#' The equation is solved for Tw using `\link[stats]{optimize`}. +#' Actual vapor pressure e (kPa) is calculated from VPD using the function `\link{VPD_to_e`}. +#' The psychrometric constant gamma (kPa K-1) is calculated from `\link{psychrometric_constant`}. #' #' # Value \item{Tw -}{wet-bulb temperature (degC)} @@ -374,11 +374,11 @@ end #' - ea Air vapor pressure (kPa) #' - accuracy Accuracy of the result (degC) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' @note Arguments \code{accuracy} and \code{Esat_formula} are passed to this function by dew_point(). +#' @note Arguments `accuracy` and `Esat_formula` are passed to this function by dew_point(). #' #' @importFrom stats optimize #' @@ -401,8 +401,8 @@ end #' - VPD Vapor pressure deficit (kPa) #' - accuracy Accuracy of the result (deg C) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details @@ -411,7 +411,7 @@ end #' \deqn{e = Esat(Td)} #' #' where e is vapor pressure of the air and Esat is the vapor pressure deficit. -#' This equation is solved for Td using \code{\link[stats]{optimize}}. +#' This equation is solved for Td using `\link[stats]{optimize`}. #' #' # Value \item{Td -}{dew point temperature (degC)} @@ -463,8 +463,8 @@ end #' - pressure Atmospheric pressure (kPa) #' - VPD Vapor pressure deficit (kPa) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants Kelvin - conversion degree Celsius to Kelvin \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) #' @@ -474,7 +474,7 @@ end #' \deqn{Tv = Tair / (1 - (1 - eps) e/pressure)} #' #' where Tair is in Kelvin (converted internally). Likewise, VPD is converted -#' to actual vapor pressure (e in kPa) with \code{\link{VPD_to_e}} internally. +#' to actual vapor pressure (e in kPa) with `\link{VPD_to_e`} internally. #' #' # Value \item{Tv -}{virtual temperature (deg C)} diff --git a/inst/fromR/potential_radiation.jl b/inst/fromR/potential_radiation.jl index 675bca7..7086916 100755 --- a/inst/fromR/potential_radiation.jl +++ b/inst/fromR/potential_radiation.jl @@ -42,14 +42,14 @@ end #' #' Compute potential radiation for given geolocation and day of year. #' -#' - doy Integer vector with day of year (start at 1), same length as \code{hour} or length 1. +#' - doy Integer vector with day of year (start at 1), same length as `hour` or length 1. #' - hour Numeric vector with daytime as decimal hour of local time zone #' - latDeg Latitude (decimal degrees) #' - longDeg Longitude (decimal degrees) #' - timezone Time zone (hours) #' - useSolartime by default corrects hour (given in local winter time) #' for latitude to solar time (where noon is exactly at 12:00). -#' Set this to \code{FALSE} to directly use local winter time. +#' Set this to `FALSE` to directly use local winter time. #' #' # Value vector of potential radiation (W m-2) diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 93012c3..84e6f62 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -32,7 +32,7 @@ #' #' @references Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. #' -#' @seealso \code{\link{stability_parameter}} +#' @seealso `\link{stability_parameter`} #' #' ```@example; output = false #' ``` @@ -77,8 +77,8 @@ end #' \deqn{\zeta = (zr - d) / L} #' #' where L is the Monin-Obukhov length (m), calculated from the function -#' \code{\link{Monin_Obukhov_length}}. The displacement height d can -#' be estimated from the function \code{\link{roughness_parameters}}. +#' `\link{Monin_Obukhov_length`}. The displacement height d can +#' be estimated from the function `\link{roughness_parameters`}. #' #' # Value \item{\eqn{\zeta} - }{stability parameter (-)} @@ -109,13 +109,13 @@ end #' from the exponential wind profile under non-neutral conditions. #' #' - zeta Stability parameter zeta (-) -#' - formulation Formulation for the stability function. Either \code{"Dyer_1970"}, -#' or \code{"Businger_1971"} +#' - formulation Formulation for the stability function. Either `"Dyer_1970"`, +#' or `"Businger_1971"` #' #' # Details The functions give the integrated form of the universal functions. They #' depend on the value of the stability parameter \eqn{\zeta}, -#' which can be calculated from the function \code{\link{stability_parameter}}. +#' which can be calculated from the function `\link{stability_parameter`}. #' The integration of the universal functions is: #' #' \deqn{\psi = -x * zeta} diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index 486f576..02eff8c 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -15,13 +15,13 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance for heat/water vapor (m s-1) -#' - calc_surface_CO2 Calculate surface CO2 concentration? Defaults to \code{FALSE}. -#' - Ca Atmospheric CO2 concentration (mol mol-1). Required if \code{calc_surface_CO2 = TRUE}. -#' - NEE Net ecosystem exchange (umol m-2 s-1). Required if \code{calc_surface_CO2 = TRUE}. -#' - Ga_CO2 Aerodynamic conductance for CO2 (m s-1). Required if \code{calc_surface_CO2 = TRUE}. +#' - calc_surface_CO2 Calculate surface CO2 concentration? Defaults to `FALSE`. +#' - Ca Atmospheric CO2 concentration (mol mol-1). Required if `calc_surface_CO2 = TRUE`. +#' - NEE Net ecosystem exchange (umol m-2 s-1). Required if `calc_surface_CO2 = TRUE`. +#' - Ga_CO2 Aerodynamic conductance for CO2 (m s-1). Required if `calc_surface_CO2 = TRUE`. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) \cr #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) @@ -51,14 +51,14 @@ #' Note that Ga is assumed to be equal for water vapor and sensible heat. #' Ga is further assumed to be the inverse of the sum of the turbulent part #' and the canopy boundary layer conductance (1/Ga = 1/Ga_m + 1/Gb; -#' see \code{\link{aerodynamic_conductance}}). Ga_CO2, the aerodynamic conductance -#' for CO2 is also calculated by \code{\link{aerodynamic_conductance}}. +#' see `\link{aerodynamic_conductance`}). Ga_CO2, the aerodynamic conductance +#' for CO2 is also calculated by `\link{aerodynamic_conductance`}. #' If Ga is replaced by Ga_m (i.e. only the turbulent conductance part), #' the results of the functions represent conditions outside the canopy #' boundary layer, i.e. in the canopy airspace. #' #' @note The following sign convention for NEE is employed (relevant if -#' \code{calc_surface_CO2 = TRUE}): +#' `calc_surface_CO2 = TRUE`): #' negative values of NEE denote net CO2 uptake by the ecosystem. #' #' # Value diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 99cbe54..953ba83 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -16,16 +16,16 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) -#' - missing_G_as_NA if \code{TRUE}, missing G are treated as \code{NA}s, otherwise they are set to 0. -#' Only used if \code{formulation = "Penman-Monteith"}. -#' - missing_S_as_NA if \code{TRUE}, missing S are treated as \code{NA}s, otherwise they are set to 0. -#' Only used if \code{formulation = "Penman-Monteith"}. -#' - formulation Formulation used. Either \code{"Penman-Monteith"} (the default) -#' using the inverted Penman-Monteith equation, or \code{"Flux-Gradient"}, +#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise they are set to 0. +#' Only used if `formulation = "Penman-Monteith"`. +#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise they are set to 0. +#' Only used if `formulation = "Penman-Monteith"`. +#' - formulation Formulation used. Either `"Penman-Monteith"` (the default) +#' using the inverted Penman-Monteith equation, or `"Flux-Gradient"`, #' for a simple flux-gradient approach requiring ET, pressure, and VPD only. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' Only used if \code{formulation = "Penman-Monteith"}. See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' Only used if `formulation = "Penman-Monteith"`. See `\link{Esat_slope`}. #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr #' eps - ratio of the molecular weight of water vapor to dry air (-) \cr #' Rd - gas constant of dry air (J kg-1 K-1) \cr @@ -36,7 +36,7 @@ #' #' #' # Details - If \code{formulation = "Penman-Monteith"} (the default), surface conductance (Gs) in m s-1 + If `formulation = "Penman-Monteith"` (the default), surface conductance (Gs) in m s-1 #' is calculated from the inverted Penman-Monteith equation: #' #' \deqn{Gs = ( LE * Ga * \gamma ) / ( \Delta * A + \rho * cp * Ga * VPD - LE * ( \Delta + \gamma ) )} @@ -45,10 +45,10 @@ #' saturation vapor pressure curve (kPa K-1), and \eqn{\rho} is air density (kg m-3). #' Available energy (A) is defined as A = Rn - G - S. If G and/or S are not provided, A = Rn. #' -#' By default, any missing data in G and S are set to 0. If \code{missing_S_as_NA = TRUE} -#' or \code{missing_S_as_NA = TRUE}, Gs will give \code{NA} for these timesteps. +#' By default, any missing data in G and S are set to 0. If `missing_S_as_NA = TRUE` +#' or `missing_S_as_NA = TRUE`, Gs will give `NA` for these timesteps. #' -#' If \code{formulation="Flux-Gradient"}, Gs (in mol m-2 s-1) is calculated from VPD and ET only: +#' If `formulation="Flux-Gradient"`, Gs (in mol m-2 s-1) is calculated from VPD and ET only: #' #' \deqn{Gs = ET/pressure * VPD} #' @@ -60,8 +60,8 @@ #' which gives Gs in m s-1. Note that Gs > Gc (canopy conductance) under conditions #' when a significant fraction of ET comes from interception or soil evaporation. #' -#' If \code{pressure} is not available, it can be approximated by elevation using the -#' function \code{\link{pressure_from_elevation}} +#' If `pressure` is not available, it can be approximated by elevation using the +#' function `\link{pressure_from_elevation`} #' #' # Value a dataframe with the following columns: diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index cd241e9..5129959 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -19,7 +19,7 @@ #' #' \deqn{Re = z0m * ustar / v} #' -#' where \code{v} is the kinematic viscosity (m2 s-1). +#' where `v` is the kinematic viscosity (m2 s-1). #' #' # Value \item{Re -}{Roughness Reynolds Number (-)} @@ -48,19 +48,19 @@ end #' A simple approximation of the two roughness parameters displacement height (d) #' and roughness length for momentum (z0m). #' -#' - method Method to use, one of \code{"canopy_height","canopy_height&LAI","wind_profile"} \cr -#' NOTE: if \code{method = "canopy_height"}, only the following three arguments -#' are used. If \code{method = "canopy_height&LAI"}, only \code{zh, LAI, cd}, -#' and \code{hs} are required. +#' - method Method to use, one of `"canopy_height","canopy_height&LAI","wind_profile"` \cr +#' NOTE: if `method = "canopy_height"`, only the following three arguments +#' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, +#' and `hs` are required. #' - zh Vegetation height (m) #' - frac_d Fraction of displacement height on canopy height (-) #' - frac_z0m Fraction of roughness length on canopy height (-) #' - LAI Leaf area index (-) #' - zr Instrument (reference) height (m) #' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. -#' Only needed if \code{method = "canopy_height&LAI"}. -#' - hs roughness length of the soil surface (m). Only needed if \code{method = "canopy_height&LAI"} -#' The following arguments are only needed if \code{method = "wind_profile"}! +#' Only needed if `method = "canopy_height&LAI"`. +#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` +#' The following arguments are only needed if `method = "wind_profile"`! #' - data Data_frame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) @@ -69,9 +69,9 @@ end #' - H Sensible heat flux (W m-2) #' - d Zero-plane displacement height (m); optional #' - z0m Roughness length for momentum (m); optional -#' - stab_roughness Should stability correction be considered? Default is \code{TRUE}. -#' - stab_formulation Stability correction function used (If \code{stab_correction = TRUE}). -#' Either \code{"Dyer_1970"} or \code{"Businger_1971"}. +#' - stab_roughness Should stability correction be considered? Default is `TRUE`. +#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). +#' Either `"Dyer_1970"` or `"Businger_1971"`. #' - constants k - von-Karman constant (-) \cr #' Kelvin - conversion degree Celsius to Kelvin \cr #' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr @@ -82,7 +82,7 @@ end #' # Details The two main roughness parameters, the displacement height (d) #' and the roughness length for momentum (z0m) can be estimated from simple -#' empirical relationships with canopy height (zh). If \code{method = "canopy_height"}, +#' empirical relationships with canopy height (zh). If `method = "canopy_height"`, #' the following formulas are used: #' #' \deqn{d = frac_d * zh} @@ -92,7 +92,7 @@ end #' where frac_d defaults to 0.7 and frac_z0m to 0.1. #' #' Alternatively, d and z0m can be estimated from both canopy height and LAI -#' (If \code{method = "canopy_height&LAI"}). +#' (If `method = "canopy_height&LAI"`). #' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed #' the following semi-empirical relations: #' @@ -104,19 +104,19 @@ end #' #' \deqn{z0m = hs * zh * (1 - d/zh) for 0.2 < X} #' -#' If \code{method = "wind_profile"}, z0m is estimated by solving +#' If `method = "wind_profile"`, z0m is estimated by solving #' the wind speed profile for z0m: #' #' \deqn{z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)} #' #' By default, d in this equation is fixed to 0.7*zh, but can be set to any -#' other value. psi_m is 0 if \code{stab_roughness = FALSE}. +#' other value. psi_m is 0 if `stab_roughness = FALSE`. #' #' # Value a DataFrame with the following columns: #' \item{d}{Zero-plane displacement height (m)} #' \item{z0m}{Roughness length for momentum (m)} -#' \item{z0m_se}{Only if \code{method = wind_profile}: Standard Error of the median for z0m (m)} +#' \item{z0m_se}{Only if `method = wind_profile`: Standard Error of the median for z0m (m)} #' #' @references Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat #' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. @@ -124,7 +124,7 @@ end #' Shaw, R. H., Pereira, A., 1982: Aerodynamic roughness of a plant canopy: #' a numerical experiment. Agricultural Meteorology, 26, 51-65. #' -#' @seealso \code{\link{wind_profile}} +#' @seealso `\link{wind_profile`} #' #' ```@example; output = false #' ``` @@ -210,26 +210,26 @@ end #' #' - data Data_frame or matrix containing all required variables #' - z Heights above ground for which wind speed is calculated. -#' Needs to be of same length as \code{data} or of length 1 +#' Needs to be of same length as `data` or of length 1 #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) #' - H Sensible heat flux (W m-2) -#' - wind Wind speed at height zr (m s-1); only used if \code{stab_correction = TRUE} +#' - wind Wind speed at height zr (m s-1); only used if `stab_correction = TRUE` #' - zr Instrument (reference) height (m) #' - zh Canopy height (m) #' - d Zero-plane displacement height (-) #' - frac_d Fraction of displacement height on canopy height (-); -#' only used if \code{d} is not available -#' - z0m Roughness length (m), optional; only used if \code{stab_correction = FALSE} (default=0.1) -#' - frac_z0m Fraction of roughness length on canopy height (-), optional; only used if \code{z0m} is not provided. +#' only used if `d` is not available +#' - z0m Roughness length (m), optional; only used if `stab_correction = FALSE` (default=0.1) +#' - frac_z0m Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. #' Default is 0.1. -#' - estimate_z0m Should \code{z0m} be estimated from the logarithmic wind profile? If \code{TRUE} (the default), -#' arguments \code{z0m} and \code{frac_z0m} are ignored. -#' See \code{\link{roughness_parameters}} for details. -#' - stab_correction Should stability correction be applied? Defaults to \code{TRUE} -#' - stab_formulation Stability correction function used (If \code{stab_correction = TRUE}). -#' Either \code{"Dyer_1970"} or \code{"Businger_1971"}. +#' - estimate_z0m Should `z0m` be estimated from the logarithmic wind profile? If `TRUE` (the default), +#' arguments `z0m` and `frac_z0m` are ignored. +#' See `\link{roughness_parameters`} for details. +#' - stab_correction Should stability correction be applied? Defaults to `TRUE` +#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). +#' Either `"Dyer_1970"` or `"Businger_1971"`. #' - constants k - von-Karman constant (-) \cr #' Kelvin - conversion degree Celsius to Kelvin \cr #' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr @@ -244,17 +244,17 @@ end #' \deqn{u(z) = (ustar/k) * (ln((z - d) / z0m) - \psi{m}} #' #' The roughness parameters zero-plane displacement height (d) and roughness length (z0m) -#' can be approximated from \code{\link{roughness_parameters}}. \eqn{\psi{m}} is omitted -#' if \code{stab_correction = FALSE} (not recommended). If \code{estimate_z0m = TRUE}, +#' can be approximated from `\link{roughness_parameters`}. \eqn{\psi{m}} is omitted +#' if `stab_correction = FALSE` (not recommended). If `estimate_z0m = TRUE`, #' z0m is first estimated from the wind profile equation and then used in the equation -#' above for the calculation of \code{u(z)} (see e.g. Newman & Klein 2014). +#' above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). #' #' @note Note that this equation is only valid for z >= d + z0m, and it is not -#' meaningful to calculate values closely above d + z0m. All values in \code{heights} +#' meaningful to calculate values closely above d + z0m. All values in `heights` #' smaller than d + z0m will return 0. #' #' # Value - A vector of wind speed at heights \code{z}. + A vector of wind speed at heights `z`. #' #' @references Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. @@ -262,7 +262,7 @@ end #' Newman, J_F., Klein, P_M., 2014: The impacts of atmospheric stability on #' the accuracy of wind speed extrapolation methods. Resources 3, 81-105. #' -#' @seealso \code{\link{roughness_parameters}} +#' @seealso `\link{roughness_parameters`} #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/unit_conversions.jl b/inst/fromR/unit_conversions.jl index 7f68aaa..a1754a6 100755 --- a/inst/fromR/unit_conversions.jl +++ b/inst/fromR/unit_conversions.jl @@ -22,7 +22,7 @@ #' \deqn{LE = \lambda ET} #' #' where \eqn{\lambda} is the latent heat of vaporization (J kg-1) as calculated by -#' \code{\link{latent_heat_vaporization}}. +#' `\link{latent_heat_vaporization`}. #' #' ```@example; output = false #' ``` @@ -125,8 +125,8 @@ end #' - VPD Vapor pressure deficit (kPa) #' - rH Relative humidity (-) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. -#' One of \code{"Sonntag_1990"} (Default), \code{"Alduchov_1996"}, or \code{"Allen_1998"}. -#' See \code{\link{Esat_slope}}. +#' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. +#' See `\link{Esat_slope`}. #' - constants eps - ratio of the molecular weight of water vapor to dry air (-) \cr #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' @@ -273,7 +273,7 @@ end #' #' \deqn{PPFD = Rg * frac_PAR * J_to_mol} #' -#' by default, the combined conversion factor (\code{frac_PAR * J_to_mol}) is 2.3 +#' by default, the combined conversion factor (`frac_PAR * J_to_mol`) is 2.3 #' #' ```@example; output = false #' ``` @@ -308,7 +308,7 @@ end #' #' - mass Numeric vector of mass in kg #' - molarMass Numeric vector of molar mass of the substance (kg mol-1) -#' e.g. as provided by \code{\link{bigleaf_constants}}()$H2Omol +#' e.g. as provided by `\link{bigleaf_constants`}()$H2Omol #' Default is molar mass of Water. #' #' # Value From b9d535c5dcad5e5541ca5116569f2f6809746f45 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sat, 23 Oct 2021 13:06:37 +0200 Subject: [PATCH 03/43] replacing constant$key from R files --- inst/fromR/WUE_metrics.jl | 4 +- inst/fromR/aerodynamic_conductance.jl | 10 ++--- inst/fromR/bigleaf_physiology.jl | 52 ++++++++++++------------ inst/fromR/boundary_layer_conductance.jl | 22 +++++----- inst/fromR/decoupling.jl | 4 +- inst/fromR/energy_balance.jl | 6 +-- inst/fromR/evapotranspiration.jl | 4 +- inst/fromR/filter_data.jl | 10 ++--- inst/fromR/meteorological_variables.jl | 38 ++++++++--------- inst/fromR/potential_radiation.jl | 2 +- inst/fromR/stability_correction.jl | 4 +- inst/fromR/surface_conditions.jl | 8 ++-- inst/fromR/surface_conductance.jl | 4 +- inst/fromR/surface_roughness.jl | 10 ++--- inst/fromR/unit_conversions.jl | 20 ++++----- 15 files changed, 99 insertions(+), 99 deletions(-) diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index c744d04..ef5463a 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -82,8 +82,8 @@ function WUE_metrics(data,GPP="GPP",NEE="NEE",LE="LE",VPD="VPD",Tair="Tair", check_input(data,list(GPP,NEE,LE,VPD,Tair)) ET = LE_to_ET(LE,Tair) # kg H2O m-2 s-1 - GPP = (GPP * constants$umol2mol * constants$Cmol) * constants$kg2g # gC m-2 s-1 - NEE = (NEE * constants$umol2mol * constants$Cmol) * constants$kg2g # gC m-2 s-1 + GPP = (GPP * constants[:umol2mol] * constants[:Cmol]) * constants[:kg2g] # gC m-2 s-1 + NEE = (NEE * constants[:umol2mol] * constants[:Cmol]) * constants[:kg2g] # gC m-2 s-1 WUE = median(GPP/ET,na_rm=TRUE) WUE_NEE = median(abs(NEE)/ET,na_rm=TRUE) diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index 2b3a59f..d258bfc 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -236,7 +236,7 @@ else if (Rb_model == "constant_kB-1") if(is_null(kB_h)) stop("value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") else - Rb_h = kB_h/(constants$k * ustar) + Rb_h = kB_h/(constants[:k] * ustar) Gb_h = 1/Rb_h if (!is_null(Sc) | !is_null(Sc_name)) @@ -248,8 +248,8 @@ end end end - Sc = c(constants$Sc_CO2,Sc) - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants$Pr)^0.67)) + Sc = c(constants[:Sc_CO2],Sc) + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) end @@ -283,11 +283,11 @@ else Choose 'stab_correction = FALSE' if no stability correction should be applied.") end - Ra_m = pmax((log((zr - d)/z0m) - psi_h),0) / (constants$k*ustar) + Ra_m = pmax((log((zr - d)/z0m) - psi_h),0) / (constants[:k]*ustar) else - Ra_m = pmax((log((zr - d)/z0m)),0) / (constants$k*ustar) + Ra_m = pmax((log((zr - d)/z0m)),0) / (constants[:k]*ustar) zeta = psi_h = rep(NA_integer_,length=length(Ra_m)) end diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index fa4b922..314e9c1 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -72,7 +72,7 @@ else Rleaf = 0 end - Ci = Ca - (GPP - Rleaf)/(Gs/constants$DwDc) + Ci = Ca - (GPP - Rleaf)/(Gs/constants[:DwDc]) return(Ci) @@ -289,19 +289,19 @@ function photosynthetic_capacity(data,C3=TRUE,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD check_input(data,list(Temp,GPP,Ci,PPFD)) - Temp = Temp + constants$Kelvin - Tref = 25.0 + constants$Kelvin + Temp = Temp + constants[:Kelvin] + Tref = 25.0 + constants[:Kelvin] if (C3){ # C3 vegetation - Kc_Ha = Kc_Ha * constants$kJ2J - Ko_Ha = Ko_Ha * constants$kJ2J - Gam_Ha = Gam_Ha * constants$kJ2J + Kc_Ha = Kc_Ha * constants[:kJ2J] + Ko_Ha = Ko_Ha * constants[:kJ2J] + Gam_Ha = Gam_Ha * constants[:kJ2J] # Temperature dependencies of photosynthetic parameters - Kc = Kc25 * exp(Kc_Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) - Ko = Ko25 * exp(Ko_Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) - Gam = Gam25 * exp(Gam_Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) - Ko = Ko * constants$J2kJ + Kc = Kc25 * exp(Kc_Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) + Ko = Ko25 * exp(Ko_Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) + Gam = Gam25 * exp(Gam_Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) + Ko = Ko * constants[:J2kJ] # basic filtering on Ci Ci[Ci < 80 | is_na(Ci)] = NA @@ -355,18 +355,18 @@ else end # calculate Vcmax25 and Jmax25 - Vcmax25 = Arrhenius_temp_response(Vcmax,Temp-constants$Kelvin,Ha=Vcmax_Ha, + Vcmax25 = Arrhenius_temp_response(Vcmax,Temp-constants[:Kelvin],Ha=Vcmax_Ha, Hd=Vcmax_Hd,dS=Vcmax_dS,constants=constants) - Jmax25 = Arrhenius_temp_response(Jmax,Temp[calcJmax]-constants$Kelvin,Ha=Jmax_Ha, + Jmax25 = Arrhenius_temp_response(Jmax,Temp[calcJmax]-constants[:Kelvin],Ha=Jmax_Ha, Hd=Jmax_Hd,dS=Jmax_dS,constants=constants) # calculate medians and standard errors of the median Vcmax25_Median = median(Vcmax25,na_rm=TRUE) - Vcmax25_SE = constants$se_median * sd(Vcmax25,na_rm=TRUE)/sqrt((sum(!is_na(Vcmax25)))) + Vcmax25_SE = constants[:se_median] * sd(Vcmax25,na_rm=TRUE)/sqrt((sum(!is_na(Vcmax25)))) Jmax25_Median = median(Jmax25,na_rm=TRUE) - Jmax25_SE = constants$se_median * sd(Jmax25,na_rm=TRUE)/sqrt((sum(!is_na(Jmax25)))) + Jmax25_SE = constants[:se_median] * sd(Jmax25,na_rm=TRUE)/sqrt((sum(!is_na(Jmax25)))) return(c("Vcmax25"=round(Vcmax25_Median,2),"Vcmax25_SE"=round(Vcmax25_SE,2), "Jmax25"=round(Jmax25_Median,2),"Jmax25_SE"=round(Jmax25_SE,2))) @@ -428,12 +428,12 @@ end #' @export function Arrhenius_temp_response(param,Temp,Ha,Hd,dS,constants=bigleaf_constants()) - Temp = Temp + constants$Kelvin - Tref = 25.0 + constants$Kelvin + Temp = Temp + constants[:Kelvin] + Tref = 25.0 + constants[:Kelvin] - Ha = ifelse(missing(Ha),NA,Ha*constants$kJ2J) - Hd = ifelse(missing(Hd),NA,Hd*constants$kJ2J) - dS = ifelse(missing(dS),NA,dS*constants$kJ2J) + Ha = ifelse(missing(Ha),NA,Ha*constants[:kJ2J]) + Hd = ifelse(missing(Hd),NA,Hd*constants[:kJ2J]) + dS = ifelse(missing(dS),NA,dS*constants[:kJ2J]) if (is_na(Ha)) @@ -443,14 +443,14 @@ end if (is_na(Hd) & is_na(dS)) - param25 = param / exp(Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) + param25 = param / exp(Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) else if (!is_na(Hd) & !is_na(dS)) param25 = param / - ( exp(Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) * - (1 + exp((Tref*dS - Hd) / (Tref * constants$Rgas))) / - (1 + exp((Temp*dS - Hd) / (Temp * constants$Rgas))) + ( exp(Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) * + (1 + exp((Tref*dS - Hd) / (Tref * constants[:Rgas]))) / + (1 + exp((Temp*dS - Hd) / (Temp * constants[:Rgas]))) ) else if ((!is_na(Hd) & is_na(dS)) | (is_na(Hd) & !is_na(dS)) ) @@ -459,7 +459,7 @@ else if ((!is_na(Hd) & is_na(dS)) | (is_na(Hd) & !is_na(dS)) ) that considers a temperature optimum and a deactivation term! Continue considering activation energy (Ha) only...") - param25 = param / exp(Ha * (Temp - Tref) / (Tref*constants$Rgas*Temp)) + param25 = param / exp(Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) end @@ -599,7 +599,7 @@ function stomatal_slope(data,Tair="Tair",pressure="pressure",GPP="GPP",Gs="Gs_mo check_input(data,list(Tair,pressure,GPP,Gs,VPD,Ca)) df = DataFrame(Tair,pressure,GPP,Gs,VPD,Ca) - DwDc = constants$DwDc # ...to work within nls() + DwDc = constants[:DwDc] # ...to work within nls() if (model == "Leuning") @@ -644,7 +644,7 @@ end else if (robust_nls) df$g0 = rep(g0,nrow(df)) # g0 as constant does not work in the nlrob function... - df$DwDc = rep(DwDc,nrow(df)) # same with constants$DwDc + df$DwDc = rep(DwDc,nrow(df)) # same with constants[:DwDc] mod_weights = nlrob(Gs ~ g0 + DwDc*(1.0 + g1/sqrt(VPD))*GPP/Ca,data=df,start=list(g1=3), na_action=na_exclude,...)$w mod = nls(Gs ~ g0 + DwDc*(1.0 + g1/sqrt(VPD))*GPP/Ca,start=list(g1=3),weights=mod_weights,...) diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index 38bce8f..d83ab9e 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -60,7 +60,7 @@ function Gb_Thom(ustar,Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) Rb_h = 6.2*ustar^-0.667 Gb_h = 1/Rb_h - kB_h = Rb_h*constants$k*ustar + kB_h = Rb_h*constants[:k]*ustar if (!is_null(Sc) | !is_null(Sc_name)) if (length(Sc) != length(Sc_name)) @@ -71,8 +71,8 @@ end end end - Sc = c(constants$Sc_CO2,Sc) - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants$Pr)^0.67)) + Sc = c(constants[:Sc_CO2],Sc) + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) @@ -202,10 +202,10 @@ end Gb_h = LAI*((0.02/alpha)*sqrt(wind_zh/leafwidth)*(1-exp(-alpha/2))) Rb_h = 1/Gb_h - kB_h = Rb_h*constants$k*ustar + kB_h = Rb_h*constants[:k]*ustar - Sc = c(constants$Sc_CO2,Sc) - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants$Pr)^0.67)) + Sc = c(constants[:Sc_CO2],Sc) + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) @@ -357,10 +357,10 @@ end Re = Reynolds_Number(Tair,pressure,ustar,hs,constants) kBs = 2.46 * (Re)^0.25 - log(7.4) Reh = Dl * wind_zh / v - Ct = 1*constants$Pr^-0.6667*Reh^-0.5*N + Ct = 1*constants[:Pr]^-0.6667*Reh^-0.5*N - kB_h = (constants$k*Cd)/(4*Ct*ustar/wind_zh)*fc^2 + kBs*(1 - fc)^2 - Rb_h = kB_h/(constants$k*ustar) + kB_h = (constants[:k]*Cd)/(4*Ct*ustar/wind_zh)*fc^2 + kBs*(1 - fc)^2 + Rb_h = kB_h/(constants[:k]*ustar) Gb_h = 1/Rb_h if (!is_null(Sc) | !is_null(Sc_name)) @@ -372,8 +372,8 @@ end end end - Sc = c(constants$Sc_CO2,Sc) - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants$Pr)^0.67)) + Sc = c(constants[:Sc_CO2],Sc) + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index fc60c52..44d6348 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -136,9 +136,9 @@ end #' @export function longwave_conductance(Tair,LAI,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin + Tair = Tair + constants[:Kelvin] - Gr = 4 * constants$sigma * Tair^3 * LAI / constants$cp + Gr = 4 * constants[:sigma] * Tair^3 * LAI / constants[:cp] return(Gr) end diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index 0348dc6..d435f82 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -222,10 +222,10 @@ function isothermal_Rn(data,Rn="Rn",Tair="Tair",Tsurf="Tsurf",emissivity, check_input(data,list(Rn,Tair,Tsurf)) - Tair = Tair + constants$Kelvin - Tsurf = Tsurf + constants$Kelvin + Tair = Tair + constants[:Kelvin] + Tsurf = Tsurf + constants[:Kelvin] - Rni = Rn + emissivity * constants$sigma * (Tsurf^4 - Tair^4) + Rni = Rn + emissivity * constants[:sigma] * (Tsurf^4 - Tair^4) return(Rni) diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index 369bb5c..5a192bd 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -133,7 +133,7 @@ else if (approach == "Penman-Monteith") Gs_pot = mol_to_ms(Gs_pot,Tair=Tair,pressure=pressure,constants=constants) rho = air_density(Tair,pressure,constants) - LE_pot = (Delta * (Rn - G - S) + rho * constants$cp * VPD * Ga) / + LE_pot = (Delta * (Rn - G - S) + rho * constants[:cp] * VPD * Ga) / (Delta + gamma * (1 + Ga / Gs_pot)) ET_pot = LE_to_ET(LE_pot,Tair) end @@ -280,7 +280,7 @@ end Delta = Esat_slope(Tair,Esat_formula,constants)[,"Delta"] LE_eq = (Delta * (Rn - G - S)) / (gamma + Delta) - LE_imp = (rho * constants$cp * Gs * VPD) / gamma + LE_imp = (rho * constants[:cp] * Gs * VPD) / gamma ET_imp = LE_to_ET(LE_imp,Tair) ET_eq = LE_to_ET(LE_eq,Tair) diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index 8351ae9..a729ac9 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -173,7 +173,7 @@ else { # same, but consider missing quality flag variables as good qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) & !is_na(get(paste0(var,quality_ext)))) end - qc_invalid_perc = round((qc_invalid/nrow(data))*constants$frac2percent,2) + qc_invalid_perc = round((qc_invalid/nrow(data))*constants[:frac2percent],2) cat(var,": ",qc_invalid," data points (",qc_invalid_perc,"%) set to NA",fill=TRUE,sep="") end @@ -222,12 +222,12 @@ end end # 4) calculate number and percentage of filtered values - invalids_perc = sapply(invalids, function(x) round((length(x)/nrow(data))*constants$frac2percent,2)) + invalids_perc = sapply(invalids, function(x) round((length(x)/nrow(data))*constants[:frac2percent],2)) additional_invalids = sapply(2:length(invalids), function(x) length(setdiff(invalids[[x]],unique(unlist(invalids[1:(x-1)]))))) - additional_invalids_perc = round(additional_invalids/nrow(data)*constants$frac2percent,2) + additional_invalids_perc = round(additional_invalids/nrow(data)*constants[:frac2percent],2) # 5) write to output @@ -252,10 +252,10 @@ end invalid = unique(unlist(invalids)) valid[invalid] = 0 - excl_perc = round((length(invalid)/nrow(data))*constants$frac2percent,2) + excl_perc = round((length(invalid)/nrow(data))*constants[:frac2percent],2) cat(length(invalid)," data points (",excl_perc,"%) excluded in total",fill=TRUE,sep="") - cat(nrow(data) - length(invalid)," valid data points (",constants$frac2percent-excl_perc,"%) remaining.",fill=TRUE,sep="") + cat(nrow(data) - length(invalid)," valid data points (",constants[:frac2percent]-excl_perc,"%) remaining.",fill=TRUE,sep="") # 6) return input data frame with filtered time steps set to NA or an additional 'valid' column diff --git a/inst/fromR/meteorological_variables.jl b/inst/fromR/meteorological_variables.jl index c071f7f..c98679b 100755 --- a/inst/fromR/meteorological_variables.jl +++ b/inst/fromR/meteorological_variables.jl @@ -31,10 +31,10 @@ """ function air_density(Tair,pressure,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin - pressure = pressure * constants$kPa2Pa + Tair = Tair + constants[:Kelvin] + pressure = pressure * constants[:kPa2Pa] - rho = pressure / (constants$Rd * Tair) + rho = pressure / (constants[:Rd] * Tair) return(rho) end @@ -80,22 +80,22 @@ end #' @export function pressure_from_elevation(elev,Tair,VPD=NULL,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin + Tair = Tair + constants[:Kelvin] if(is_null(VPD)) - pressure = constants$pressure0 / exp(constants$g * elev / (constants$Rd*Tair)) + pressure = constants[:pressure0] / exp(constants[:g] * elev / (constants[:Rd]*Tair)) else - pressure1 = constants$pressure0 / exp(constants$g * elev / (constants$Rd*Tair)) - Tv = virtual_temp(Tair - constants$Kelvin,pressure1 * constants$Pa2kPa, - VPD,Esat_formula="Sonntag_1990",constants) + constants$Kelvin + pressure1 = constants[:pressure0] / exp(constants[:g] * elev / (constants[:Rd]*Tair)) + Tv = virtual_temp(Tair - constants[:Kelvin],pressure1 * constants[:Pa2kPa], + VPD,Esat_formula="Sonntag_1990",constants) + constants[:Kelvin] - pressure = constants$pressure0 / exp(constants$g * elev / (constants$Rd*Tv)) + pressure = constants[:pressure0] / exp(constants[:g] * elev / (constants[:Rd]*Tv)) end - pressure = pressure * constants$Pa2kPa + pressure = pressure * constants[:Pa2kPa] return(pressure) end @@ -177,11 +177,11 @@ end # saturation vapor pressure Esat = a * exp((b * Tair) / (c + Tair)) - Esat = Esat * constants$Pa2kPa + Esat = Esat * constants[:Pa2kPa] # slope of the saturation vapor pressure curve Delta = eval(D(expression(a * exp((b * Tair) / (c + Tair))),name="Tair")) - Delta = Delta * constants$Pa2kPa + Delta = Delta * constants[:Pa2kPa] return(DataFrame(Esat,Delta)) end @@ -220,7 +220,7 @@ end function psychrometric_constant(Tair,pressure,constants=bigleaf_constants()) lambda = latent_heat_vaporization(Tair) - gamma = (constants$cp * pressure) / (constants$eps * lambda) + gamma = (constants[:cp] * pressure) / (constants[:eps] * lambda) return(gamma) end @@ -492,10 +492,10 @@ function virtual_temp(Tair,pressure,VPD,Esat_formula=c("Sonntag_1990","Alduchov_ constants=bigleaf_constants()) e = VPD_to_e(VPD,Tair,Esat_formula) - Tair = Tair + constants$Kelvin + Tair = Tair + constants[:Kelvin] - Tv = Tair / (1 - (1 - constants$eps) * e/pressure) - Tv = Tv - constants$Kelvin + Tv = Tair / (1 - (1 - constants[:eps]) * e/pressure) + Tv = Tv - constants[:Kelvin] return(Tv) end @@ -533,9 +533,9 @@ end #' @export function kinematic_viscosity(Tair,pressure,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin - pressure = pressure * constants$kPa2Pa + Tair = Tair + constants[:Kelvin] + pressure = pressure * constants[:kPa2Pa] - v = 1.327e-05*(constants$pressure0/pressure)*(Tair/constants$Tair0)^1.81 + v = 1.327e-05*(constants[:pressure0]/pressure)*(Tair/constants[:Tair0])^1.81 return(v) end diff --git a/inst/fromR/potential_radiation.jl b/inst/fromR/potential_radiation.jl index 7086916..7d6243f 100755 --- a/inst/fromR/potential_radiation.jl +++ b/inst/fromR/potential_radiation.jl @@ -27,7 +27,7 @@ function extraterrestrial_radiation(doy,constants = bigleaf_constants()) FracYearRad = 2 * pi * (doy - 1) / 365.24 #Eccentricity correction - ExtRadiation = constants$solar_constant * ( + ExtRadiation = constants[:solar_constant] * ( 1.00011 + 0.034221 * cos(FracYearRad) + 0.00128 * sin(FracYearRad) + 0.000719 * cos(2 * FracYearRad) + 0.000077 * sin(2 * FracYearRad) ) diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 84e6f62..6475c0f 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -46,8 +46,8 @@ function Monin_Obukhov_length(data,Tair="Tair",pressure="pressure",ustar="ustar" check_input(data,list(Tair,pressure,ustar,H)) rho = air_density(Tair,pressure,constants) - Tair = Tair + constants$Kelvin - MOL = (-rho*constants$cp*ustar^3*Tair) / (constants$k*constants$g*H) + Tair = Tair + constants[:Kelvin] + MOL = (-rho*constants[:cp]*ustar^3*Tair) / (constants[:k]*constants[:g]*H) return(MOL) end diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index 02eff8c..e3289ea 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -103,13 +103,13 @@ function surface_conditions(data,Tair="Tair",pressure="pressure",LE="LE",H="H", gamma = psychrometric_constant(Tair,pressure,constants) # 1) Temperature - Tsurf = Tair + H / (rho * constants$cp * Ga) + Tsurf = Tair + H / (rho * constants[:cp] * Ga) # 2) Humidity esat = Esat_slope(Tair,Esat_formula,constants)[,"Esat"] e = esat - VPD esat_surf = Esat_slope(Tsurf,Esat_formula,constants)[,"Esat"] - esurf = e + (LE * gamma)/(Ga * rho * constants$cp) + esurf = e + (LE * gamma)/(Ga * rho * constants[:cp]) VPD_surf = pmax(esat_surf - esurf,0) qsurf = VPD_to_q(VPD_surf,Tsurf,pressure,Esat_formula,constants) rH_surf = VPD_to_rH(VPD_surf,Tsurf,Esat_formula) @@ -213,8 +213,8 @@ function radiometric_surface_temp(data,LW_up="LW_up",LW_down="LW_down", check_input(data,list(LW_up,LW_down)) - Trad_K = ((LW_up - (1 - emissivity)*LW_down) / (constants$sigma * emissivity))^(1/4) - Trad_degC = Trad_K - constants$Kelvin + Trad_K = ((LW_up - (1 - emissivity)*LW_down) / (constants[:sigma] * emissivity))^(1/4) + Trad_degC = Trad_K - constants[:Kelvin] return(DataFrame(Trad_K,Trad_degC)) end diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 953ba83..783fc3a 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -129,7 +129,7 @@ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL check_input(data,list(Tair,pressure,VPD,LE)) - Gs_mol = (LE_to_ET(LE,Tair)/constants$Mw) * pressure / VPD + Gs_mol = (LE_to_ET(LE,Tair)/constants[:Mw]) * pressure / VPD Gs_ms = mol_to_ms(Gs_mol,Tair,pressure) else if (formulation == "Penman-Monteith") @@ -154,7 +154,7 @@ end gamma = psychrometric_constant(Tair,pressure,constants) rho = air_density(Tair,pressure,constants) - Gs_ms = ( LE * Ga * gamma ) / ( Delta * (Rn-G-S) + rho * constants$cp * Ga * VPD - LE * ( Delta + gamma ) ) + Gs_ms = ( LE * Ga * gamma ) / ( Delta * (Rn-G-S) + rho * constants[:cp] * Ga * VPD - LE * ( Delta + gamma ) ) Gs_mol = ms_to_mol(Gs_ms,Tair,pressure) end diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index 5129959..030ffe5 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -183,18 +183,18 @@ end zeta = stability_parameter(data=data,Tair=Tair,pressure=pressure,ustar=ustar,H=H, zr=zr,d=d,constants=constants) psi_m = stability_correction(zeta,formulation=stab_formulation)[,"psi_m"] - z0m_all = (zr - d) * exp(-constants$k*wind / ustar - psi_m) + z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar - psi_m) else - z0m_all = (zr - d) * exp(-constants$k*wind / ustar) + z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar) end z0m_all[z0m_all > zh] = NA z0m = median(z0m_all,na_rm=TRUE) - z0m_se = constants$se_median * (sd(z0m_all,na_rm=TRUE) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) + z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=TRUE) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) end @@ -325,11 +325,11 @@ end zeta = stability_parameter(data=data,Tair=Tair,pressure=pressure,ustar=ustar,H=H, zr=z,d=d,constants=constants) psi_m = stability_correction(zeta,formulation=stab_formulation)[,"psi_m"] - wind_heights = pmax(0,(ustar / constants$k) * (log(pmax(0,(z - d)) / z0m) - psi_m)) + wind_heights = pmax(0,(ustar / constants[:k]) * (log(pmax(0,(z - d)) / z0m) - psi_m)) else - wind_heights = pmax(0,(ustar / constants$k) * (log(pmax(0,(z - d)) / z0m))) + wind_heights = pmax(0,(ustar / constants[:k]) * (log(pmax(0,(z - d)) / z0m))) end diff --git a/inst/fromR/unit_conversions.jl b/inst/fromR/unit_conversions.jl index a1754a6..66d1273 100755 --- a/inst/fromR/unit_conversions.jl +++ b/inst/fromR/unit_conversions.jl @@ -89,10 +89,10 @@ end """ function ms_to_mol(G_ms,Tair,pressure,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin - pressure = pressure * constants$kPa2Pa + Tair = Tair + constants[:Kelvin] + pressure = pressure * constants[:kPa2Pa] - G_mol = G_ms * pressure / (constants$Rgas * Tair) + G_mol = G_ms * pressure / (constants[:Rgas] * Tair) return(G_mol) end @@ -103,10 +103,10 @@ end """ function mol_to_ms(G_mol,Tair,pressure,constants=bigleaf_constants()) - Tair = Tair + constants$Kelvin - pressure = pressure * constants$kPa2Pa + Tair = Tair + constants[:Kelvin] + pressure = pressure * constants[:kPa2Pa] - G_ms = G_mol * (constants$Rgas * Tair) / (pressure) + G_ms = G_mol * (constants[:Rgas] * Tair) / (pressure) return(G_ms) end @@ -211,7 +211,7 @@ end """ """ function e_to_q(e,pressure,constants=bigleaf_constants()) - q = constants$eps * e / (pressure - (1-constants$eps) * e) + q = constants[:eps] * e / (pressure - (1-constants[:eps]) * e) return(q) end @@ -221,7 +221,7 @@ end """ """ function q_to_e(q,pressure,constants=bigleaf_constants()) - e = q * pressure / ((1-constants$eps) * q + constants$eps) + e = q * pressure / ((1-constants[:eps]) * q + constants[:eps]) return(e) end @@ -347,7 +347,7 @@ end """ function umolCO2_to_gC(CO2_flux,constants=bigleaf_constants()) - C_flux = CO2_flux * constants$umol2mol * constants$Cmol * constants$kg2g * constants$days2seconds + C_flux = CO2_flux * constants[:umol2mol] * constants[:Cmol] * constants[:kg2g] * constants[:days2seconds] return(C_flux) end @@ -360,7 +360,7 @@ end """ function gC_to_umolCO2(C_flux,constants=bigleaf_constants()) - CO2_flux = (C_flux * constants$g2kg / constants$days2seconds) / constants$Cmol * constants$mol2umol + CO2_flux = (C_flux * constants[:g2kg] / constants[:days2seconds]) / constants[:Cmol] * constants[:mol2umol] return(CO2_flux) end \ No newline at end of file From d1f81946e601420b14344b3428042bb167b71267 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sat, 23 Oct 2021 18:22:56 +0200 Subject: [PATCH 04/43] implement global ratiation tests and help --- .github/workflows/CI.yml | 3 +- Project.toml | 5 +- docs/src/global_radiation.md | 4 +- docs/src/walkthrough.md | 15 ++--- src/Bigleaf.jl | 10 ++- src/potential_radiation.jl | 114 +++++++++++++++++++++++++++++++++++ src/sun_position.jl | 16 +++-- src/util.jl | 23 +++++++ test/potential_radiation.jl | 27 +++++++++ test/sun_position.jl | 3 +- 10 files changed, 201 insertions(+), 19 deletions(-) create mode 100755 src/potential_radiation.jl create mode 100644 test/potential_radiation.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5129220..1f4174e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - ci tags: '*' pull_request: jobs: @@ -14,7 +15,7 @@ jobs: matrix: version: - '1' - - '1.6' + - '^1.7.0-0' os: - ubuntu-latest arch: diff --git a/Project.toml b/Project.toml index fa23732..e9fa14e 100644 --- a/Project.toml +++ b/Project.toml @@ -9,17 +9,20 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" LabelledArrays = "2ee39098-c373-598a-b85f-a56591580800" Optim = "429524aa-4258-5aef-a3af-852621145aeb" +Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [compat] -julia = "1" AstroLib = "0.4" DataFrames = "1" LabelledArrays = "1" Optim = "1" RecursiveArrayTools = "2" StaticArrays = "1" +julia = "1" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/docs/src/global_radiation.md b/docs/src/global_radiation.md index 9e45a2c..bb57c13 100644 --- a/docs/src/global_radiation.md +++ b/docs/src/global_radiation.md @@ -4,6 +4,8 @@ Pages = ["global_radiation.md",] ``` ```@docs +potential_radiation +extraterrestrial_radiation calc_sun_position_hor calc_sun_position_MOD -``` \ No newline at end of file +``` diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 4c07eb6..2d75d85 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -142,7 +142,14 @@ The following figure compares them at absole scale and as difference to the ## Global radiation -Computing solar position in horizontal coordinates +Potential radiation for given time and latitude: +```@example doc +doy, hour = 160, 10.5 +lat, long = 51.0, 11.5 +potrad = potential_radiation(doy, hour, lat, long) +``` + +Calculation is based on sun's altitude, one of the horizontal coordinates of its position. ```@example doc using Plots, StatsPlots, DataFrames, Dates, Pipe, Suppressor hours = 0:24 @@ -154,12 +161,6 @@ res3 = @suppress_err @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDat @df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad") ``` -```@setup doc -savefig("fig/globrad.svg") -``` - -![](fig/globrad.svg) - The hour-angle at noon represents the difference to local time. In the following example solar time is about 55min ahead of local winter time. diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 1b7d0b0..4387caa 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -4,10 +4,14 @@ module Bigleaf using Optim using StaticArrays, LabelledArrays, RecursiveArrayTools using DataFrames -using Dates +using Dates, TimeZones +using Pipe using AstroLib +using Suppressor -export toDataFrame + + +export toDataFrame, frac_hour export bigleaf_constants export Esat_slope, Esat_from_Tair, Esat_from_Tair_deriv, LE_to_ET, ET_to_LE, ms_to_mol, mol_to_ms, VPD_to_rH, rH_to_VPD, @@ -18,11 +22,13 @@ export air_density, pressure_from_elevation, psychrometric_constant, dew_point_from_e, dew_point, wetbulb_temp_from_e_Tair_gamma, wetbulb_temp export calc_sun_position_MOD, calc_sun_position_hor +export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour include("util.jl") include("bigleaf_constants.jl") include("unit_conversions.jl") include("meteorological_variables.jl") include("sun_position.jl") +include("potential_radiation.jl") end diff --git a/src/potential_radiation.jl b/src/potential_radiation.jl new file mode 100755 index 0000000..ddd3ed9 --- /dev/null +++ b/src/potential_radiation.jl @@ -0,0 +1,114 @@ +""" + extraterrestrial_radiation(doy::Number; ...) + extraterrestrial_radiation(datetime::TimeType; ...) + +Compute the extraterrestrial solar radiation with the +eccentricity correction. Computation follows Lanini, 2010 (Master thesis, Bern University). + + +# Arguments +- doy: integer vector with day of year (DoY) +optional +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - solar_constant +- `year`=2030: year to create timestamps. Due to precession results slightly + change across decades. + +# Value +numeric vector of extraterrestrial radiation (W_m-2) + +# Examples +```jldoctest; output = false +ex_rad = extraterrestrial_radiation(1) +≈(ex_rad, 1414, atol = 1) +# output +true +``` +""" +function extraterrestrial_radiation(doy::Number;constants = bigleaf_constants(), year = 2030) + FracYearRad = 2 * π * (doy - 1) / 365.24 + #Eccentricity correction + ExtRadiation = constants[:solar_constant] * ( + 1.00011 + 0.034221 * cos(FracYearRad) + 0.00128 * sin(FracYearRad) + + 0.000719 * cos(2 * FracYearRad) + 0.000077 * sin(2 * FracYearRad) + ) +end, +function extraterrestrial_radiation(datetime::TimeType; constants = bigleaf_constants()) + # Fractional year in radians + doy = Dates.dayofyear(datetime) + extraterrestrial_radiation(doy; constants) +end + +""" + potential_radiation(datetime, lat, long) + potential_radiation(doy, hour, lat, long; timezone,year) + +Compute potential radiation for given geolocation and time. + +Because potential radiation does not change across years +(for a given location and daytime and day of year), time +can be specified alternatively by doy and hour. + +# Arguments +- datetime: UTC timestamp. +- lat: Latitude (decimal degrees) +- long: Longitude (decimal degrees) +- doy: day of year (integer starting from 1) +- hour: hour within the day (0.0 .. 24.0 float) +optional +- timezone: Timezone for doy and hour, defaults to "GMT+x" + nearest to given longitude. +- year: specific year for doy and hour + +# Value +vector of potential radiation (W m-2) + +# Examples +```@example +# assume hours in the GMT+x that is closest to given longitude +potrad = potential_radiation(160, 10.5, 51.0, 11.5) +``` +""" +function potential_radiation(doy, hour, lat, long; timezone = FixedTimeZone("UTC+"*string(round(Int32, long/15))), year = 2030) + # the following doctest does not suppress warnings and fails + # ```jldoctest; output = false + # # assume hours in the GMT+x that is closest to given longitude + # potrad = potential_radiation(160, 10.5, 51.0, 11.5) + # ≈(potrad, 1093, atol=1) + # # output + # true + # ``` + @pipe get_datetime_for_doy_hour(doy,hour; year) |> + ZonedDateTime(_, timezone) |> + potential_radiation(_, lat, long) +end, +function potential_radiation(datetime::TimeType, lat, long) + # Calculate potential radiation from solar elevation and extraterrestrial solar radiation + solElevRad = calc_sun_position_hor(datetime, lat, long).altitude + extRadiation = extraterrestrial_radiation(datetime) + potRad = extRadiation * sin(solElevRad) +end + +""" + get_datetime_for_doy_hour(doy, hour=12; year = 2030) + +Create DateTime for given day_of_year and hour. Hour defaults +to noon and year to 2030, a near future where earth axis +precession does not differ too much from year where the function +is called. Fractional hours can be provided. + +# Examples +```jldoctest output = false +get_datetime_for_doy_hour(2; year = 2021) +# output +2021-01-02T12:00:00 +``` +""" +get_datetime_for_doy_hour(doy, hour::Integer=12; year = 2030) = DateTime(year) + Day(doy-1) + Hour(hour) + +get_datetime_for_doy_hour(doy, hour; year = 2030) = DateTime(year) + Day(doy-1) + frac_hour(Dates.Millisecond, hour) + + + + + diff --git a/src/sun_position.jl b/src/sun_position.jl index 090a3ac..6980c78 100644 --- a/src/sun_position.jl +++ b/src/sun_position.jl @@ -1,10 +1,10 @@ """ - calc_sun_position_hor(datetime::DateTime, lat, long) + calc_sun_position_hor(datetime, lat, long) Compute the Sun position at given time and observer coordinates in horizontal coordinates. # Arguments: -- `datetime`: time given in UTC. +- `datetime`: time: Either a `ZonedDateTime`, or `DateTime` assumed in UTC - `lat`, `long`: latitude and longitude in degree # Value @@ -15,13 +15,17 @@ Compute the Sun position at given time and observer coordinates in horizontal co Seems to represent time [day/2pi] after solar noon. Value at local timezone noon provdes (local time - solar time). """ +function calc_sun_position_hor(datetime::ZonedDateTime, lat, long) + datetimeUTC = DateTime(datetime,UTC) + calc_sun_position_hor(datetimeUTC, lat, long) +end, function calc_sun_position_hor(datetime::DateTime, lat, long) deg2rad = π/180 jd = datetime2julian(datetime) pos_eq = calc_sun_position_MOD(jd) # precession is already account for in MOD pos_hor = eq2hor(pos_eq.α/deg2rad, pos_eq.δ/deg2rad, jd, lat, long; precession = false) .* deg2rad - SLVector(altitude = pos_hor[1], azimuth = pos_hor[2], hourangle = pos_hor[3]) + @suppress_err SLVector(altitude = pos_hor[1], azimuth = pos_hor[2], hourangle = pos_hor[3]) end @@ -57,8 +61,8 @@ end Compute the Sun position at the Julian Day `JD`. Results are represented in the Mean Equinox of Date (MOD), -i.e. accounting for precision but not for nutation and smaller pertubation -of the x axes, in Spherical coordinates. +i.e. accounting for precession but not for nutation and smaller pertubation +of the polar axes, in spherical ecliptic and equatorial coordinates. The algorithm was adapted from [Vallado 2013, p. 277-279]. # Arguments: @@ -128,7 +132,7 @@ function calc_sun_position_MOD(JD::Number) # declination δ = asin(sinϵ * sinλ_e) - S_MOD_rad = SLVector( + @suppress_err S_MOD_rad = SLVector( λ = λ_e, β = 0.0, r = r, α = α, δ = δ, ϵ = ϵ diff --git a/src/util.jl b/src/util.jl index bdc3a84..0e3c412 100644 --- a/src/util.jl +++ b/src/util.jl @@ -18,3 +18,26 @@ end # DataFrame(collect(map(idx -> getindex.(data, idx), eachindex(collect(first(data))))), collect(names)) # end +""" + frac_hour(float::AbstractFloat) + frac_hour(period::Type{<:Period}, float::AbstractFloat) + +Create a period in given type (defaults to `Nanosecond`) from +fractional hours. + +```jldoctest; output = false +using Dates +frac_hour(1+1/60) == Hour(1) + Minute(1) +# output +true +``` +""" +function frac_hour(period::Type{<:Period}, float::AbstractFloat) + #adapted from https://stackoverflow.com/a/51448499 + full_hour, Δ = divrem(float, 1) + partial = period(round(Dates.value(period(Hour(1))) * Δ)) + Hour(full_hour) + partial +end +frac_hour(float::AbstractFloat) = frac_hour(Nanosecond, float) + + diff --git a/test/potential_radiation.jl b/test/potential_radiation.jl new file mode 100644 index 0000000..840c4e7 --- /dev/null +++ b/test/potential_radiation.jl @@ -0,0 +1,27 @@ +@testset "frac_hour" begin + p = frac_hour(1+1/60) + @test p == Hour(1) + Minute(1) +end + +@testset "get_datetime_for_doy_hour summer" begin + hours = [0,π,24] + dts = get_datetime_for_doy_hour.(1, hours; year = 2021) + @test dts[1] == DateTime(2021,1,1) + @test dts[3] == DateTime(2021,1,2) + @test dts[1] < dts[2] < dts[3] +end + +@testset "Dresden summer" begin + hours = 8:16 + potRadSolar = potential_radiation.(160, hours, 39.94, -5.77; timezone = tz"UTC+1") + expSolar = [ + 484.152670743821, 717.876981534078, 925.130678985721, + 1091.78976612035, 1206.4967015669, 1261.43439723686, 1252.85893995917, + 1181.35473297567, 1051.79466982602] + @test all(.≈(potRadSolar, expSolar, rtol = 0.02)) +end + + + + + diff --git a/test/sun_position.jl b/test/sun_position.jl index 297a2a5..ac27605 100644 --- a/test/sun_position.jl +++ b/test/sun_position.jl @@ -54,5 +54,6 @@ end # azimuth increasing (aside edge cases) @test all(diff(res3.azimuth[2:(end-1)]) .> 0) end - + + From 48cfcfbb759c00f1d55c4d681d6319e174a356fe Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sat, 23 Oct 2021 19:49:03 +0200 Subject: [PATCH 05/43] reformatting evapotranspiration.jl --- Project.toml | 1 + inst/fromR/WUE_metrics.jl | 33 ++-- inst/fromR/aerodynamic_conductance.jl | 93 +++++----- inst/fromR/bigleaf_physiology.jl | 145 ++++++++------- inst/fromR/boundary_layer_conductance.jl | 94 +++++----- inst/fromR/check_input.jl | 30 +-- inst/fromR/datasets_description.jl | 193 ++++++++++---------- inst/fromR/decoupling.jl | 45 ++--- inst/fromR/energy_balance.jl | 46 ++--- inst/fromR/evapotranspiration.jl | 128 ++++++------- inst/fromR/filter_data.jl | 55 +++--- inst/fromR/meteorological_variables.jl | 128 +++++++------ inst/fromR/potential_radiation.jl | 4 +- inst/fromR/stability_correction.jl | 56 +++--- inst/fromR/surface_conditions.jl | 64 ++++--- inst/fromR/surface_conductance.jl | 55 +++--- inst/fromR/surface_roughness.jl | 76 ++++---- inst/fromR/unit_conversions.jl | 40 ++-- inst/walkthrough_todo.jmd | 12 +- src/Bigleaf.jl | 4 +- src/evapotranspiration.jl | 223 +++++++++++++++++++++++ 21 files changed, 913 insertions(+), 612 deletions(-) create mode 100755 src/evapotranspiration.jl diff --git a/Project.toml b/Project.toml index e9fa14e..2c4176b 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ AstroLib = "c7932e45-9af1-51e7-9da9-f004cd3a462b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" LabelledArrays = "2ee39098-c373-598a-b85f-a56591580800" +Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" Optim = "429524aa-4258-5aef-a3af-852621145aeb" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index ef5463a..227f4be 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -12,8 +12,8 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Tair Air temperature (deg C) -#' - constants Cmol - molar mass of carbon (kg mol-1) \cr -#' umol2mol - conversion micromole (umol) to mole (mol) \cr +#' - constants Cmol - molar mass of carbon (kg mol-1) +#' umol2mol - conversion micromole (umol) to mole (mol) #' kg2g - conversion kilogram (kg) to gram (g) #' #' # Details @@ -21,47 +21,50 @@ #' #' Water-use efficiency (WUE): #' -#' \deqn{WUE = GPP / ET} +#' ``WUE = GPP / ET`` #' #' Water-use efficiency based on NEE (WUE_NEE): #' -#' \deqn{WUE_NEE = NEE / ET} +#' ``WUE_NEE = NEE / ET`` #' #' Inherent water-use efficiency (IWUE; Beer et al. 2009): #' -#' \deqn{IWUE = (GPP * VPD) / ET} +#' ``IWUE = (GPP * VPD) / ET`` #' #' Underlying water-use efficiency (uWUE; Zhou et al. 2014): #' -#' \deqn{uWUE= (GPP * sqrt(VPD)) / ET} +#' ``uWUE= (GPP * sqrt(VPD)) / ET`` #' #' All metrics are calculated based on the median of all values. E_g. #' WUE = median(GPP/ET,na_rm=TRUE) #' #' # Value a named vector with the following elements: -#' \item{WUE}{Water-use efficiency (gC (kg H20)-1)} -#' \item{WUE_NEE}{Water-use efficiency based on NEE (gC (kg H20)-1)} -#' \item{IWUE}{Inherent water-use efficiency (gC kPa (kg H20)-1)} -#' \item{uWUE}{Underlying water-use efficiency (gC kPa^0.5 (kg H20)-1)} +#' - WUE: Water-use efficiency (gC (kg H20)-1) +#' - WUE_NEE: Water-use efficiency based on NEE (gC (kg H20)-1) +#' - IWUE: Inherent water-use efficiency (gC kPa (kg H20)-1) +#' - uWUE: Underlying water-use efficiency (gC kPa^0.5 (kg H20)-1) #' -#' @note Units for VPD can also be hPa. Units change accordingly. +#' #Note +#' Units for VPD can also be hPa. Units change accordingly. #' WUE_NEE is calculated based on the absolute value of NEE (the sign convention does not matter here). #' -#' @references Beer, C., et al., 2009: Temporal and among-site variability of inherent +#' #References +#' Beer, C., et al., 2009: Temporal and among-site variability of inherent #' water use efficiency at the ecosystem level. Global Biogeochemical Cycles 23, GB2018. #' #' Zhou, S., et al., 2014: The effect of vapor pressure deficit on water #' use efficiency at the sub-daily time scale. Geophysical Research Letters 41. #' -#' @seealso `\link{stomatal_slope`} for a measure of intrinsic WUE +#' #See also +#' [`stomatal_slope`](@ref) for a measure of intrinsic WUE #' #' ```@example; output = false #' ``` #' ## filter data for dry periods and daytime at DE-Tha in June 2014 -#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), #' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index d258bfc..bd6a739 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -28,22 +28,22 @@ #' - Cd Foliage drag coefficient (-); only used if `Rb_model = "Su_2001"`. #' - hs Roughness length of bare soil (m); only used if `Rb_model = "Su_2001"`. #' - wind_profile Should Ga for momentum be calculated based on the logarithmic wind profile equation? -#' Defaults to `FALSE`. -#' - stab_correction Should stability correction be applied? Defaults to `TRUE`. Ignored if `wind_profile = FALSE`. +#' Defaults to `false`. +#' - stab_correction Should stability correction be applied? Defaults to `TRUE`. Ignored if `wind_profile = false`. #' - stab_formulation Stability correction function. Either `"Dyer_1970"` (default) or -#' `"Businger_1971"`. Ignored if `wind_profile = FALSE` or if `stab_correction = FALSE`. +#' `"Businger_1971"`. Ignored if `wind_profile = false` or if `stab_correction = false`. #' - Rb_model Boundary layer resistance formulation. One of `"Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"`. #' - kB_h kB-1 value for heat transfer; only used if `Rb_model = "constant_kB-1"` #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than #' `Sc_name` -#' - constants k - von Karman constant \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' Kelvin - conversion degree Celsius to Kelvin \cr -#' g - gravitational acceleration (m s-2) \cr -#' pressure0 - reference atmospheric pressure at sea level (Pa) \cr -#' Tair0 - reference air temperature (K) \cr -#' Sc_CO2 - Schmidt number for CO2 \cr +#' - constants k - von Karman constant +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' Kelvin - conversion degree Celsius to Kelvin +#' g - gravitational acceleration (m s-2) +#' pressure0 - reference atmospheric pressure at sea level (Pa) +#' Tair0 - reference air temperature (K) +#' Sc_CO2 - Schmidt number for CO2 #' Pr - Prandtl number (if `Sc` is provided) #' #' # Details @@ -51,14 +51,14 @@ #' #' Aerodynamic conductance for heat (Ga_h) is calculated as: #' -#' \deqn{Ga_h = 1 / (Ra_m + Rb_h)} +#' ``Ga_h = 1 / (Ra_m + Rb_h)`` #' #' where Ra_m is the aerodynamic resistance for momentum and Rb the (quasi-laminar) canopy boundary #' layer resistance ('excess resistance'). #' #' The aerodynamic resistance for momentum Ra_m is given by: #' -#' \deqn{Ra_m = u/ustar^2} +#' ``Ra_m = u/ustar^2`` #' #' Note that this formulation accounts for changes in atmospheric stability, and does not require an #' additional stability correction function. @@ -66,14 +66,14 @@ #' An alternative method to calculate Ra_m is provided #' (calculated if `wind_profile = TRUE`): #' -#' \deqn{Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)} +#' ``Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)`` #' #' If the roughness parameters z0m and d are unknown, they can be estimated using -#' `\link{roughness_parameters`}. The argument `stab_formulation` determines the stability correction function used +#' [`roughness_parameters`](@ref). The argument `stab_formulation` determines the stability correction function used #' to account for the effect of atmospheric stability on Ra_m (Ra_m is lower for unstable #' and higher for stable stratification). Stratification is based on a stability parameter zeta (z-d/L), #' where z = reference height, d the zero-plane displacement height, and L the Monin-Obukhov length, -#' calculated with `\link{Monin_Obukhov_length`} +#' calculated with [`Monin_Obukhov_length`](@ref) #' The stability correction function is chosen by the argument `stab_formulation`. Options are #' `"Dyer_1970"` and `"Businger_1971"`. #' @@ -81,68 +81,69 @@ #' the argument `Rb_model`. The following options are implemented: #' `"Thom_1972"` is an empirical formulation based on the friction velocity (ustar) (Thom 1972): #' -#' \deqn{Rb_h = 6.2ustar^-0.667} +#' ``Rb_h = 6.2ustar^-0.667`` #' #' The model by Choudhury & Monteith 1988 (`Rb_model = "Choudhury_1988"`), #' calculates Rb_h based on leaf width, LAI and ustar (Note that function argument `Dl` #' represents leaf width (w) and not characteristic leaf dimension (Dl) #' if `Rb_model` = `"Choudhury_1988"`): #' -#' \deqn{Gb_h = LAI((0.02/\alpha)*sqrt(u(zh)/w)*(1-exp(-\alpha/2)))} +#' ``Gb_h = LAI((0.02/\\alpha)*sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` #' -#' where \eqn{\alpha} is a canopy attenuation coefficient modeled in dependence on LAI, -#' u(zh) is wind speed at canopy height (calculated from `\link{wind_profile`}), -#' and w is leaf width (m). See `\link{Gb_Choudhury`} for further details. +#' where ``\\alpha`` is a canopy attenuation coefficient modeled in dependence on LAI, +#' u(zh) is wind speed at canopy height (calculated from [`wind_profile`](@ref)), +#' and w is leaf width (m). See [`Gb_Choudhury`](@ref) for further details. #' #' The option `Rb_model = "Su_2001"` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, #' a simplification of the model developed by Massman 1999: #' -#' \deqn{kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2} +#' ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` #' #' where Cd is a foliage drag coefficient (defaults to 0.2), fc is fractional #' vegetation cover, Bs-1 is the inverse Stanton number for bare soil surface, -#' and Ct is a heat transfer coefficient. See `\link{Gb_Su`} for +#' and Ct is a heat transfer coefficient. See [`Gb_Su`](@ref) for #' details on the model. #' #' #' The models calculate the parameter kB-1, which is related to Rb_h: #' -#' \deqn{kB-1 = Rb_h * (k * ustar)} +#' ``kB-1 = Rb_h * (k * ustar)`` #' #' Rb (and Gb) for water vapor and heat are assumed to be equal in this package. #' Gb for other quantities x is calculated as (Hicks et al. 1987): #' -#' \deqn{Gb_x = Gb / (Sc_x / Pr)^0.67} +#' ``Gb_x = Gb / (Sc_x / Pr)^0.67`` #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' #' # Value a dataframe with the following columns: -#' \item{Ga_m}{Aerodynamic conductance for momentum transfer (m s-1)} -#' \item{Ra_m}{Aerodynamic resistance for momentum transfer (s m-1)} -#' \item{Ga_h}{Aerodynamic conductance for heat transfer (m s-1)} -#' \item{Ra_h}{Aerodynamic resistance for heat transfer (s m-1)} -#' \item{Gb_h}{Canopy boundary layer conductance for heat transfer (m s-1)} -#' \item{Rb_h}{Canopy boundary layer resistance for heat transfer (s m-1)} -#' \item{kB_h}{kB-1 parameter for heat transfer} -#' \item{zeta}{Stability parameter 'zeta' (NA if `wind_profile = FALSE`)} -#' \item{psi_h}{Integrated stability correction function (NA if `wind_profile = FALSE`)} -#' \item{Ra_CO2}{Aerodynamic resistance for CO2 transfer (s m-1)} -#' \item{Ga_CO2}{Aerodynamic conductance for CO2 transfer (m s-1)} -#' \item{Gb_CO2}{Canopy boundary layer conductance for CO2 transfer (m s-1)} +#' - Ga_m: Aerodynamic conductance for momentum transfer (m s-1) +#' - Ra_m: Aerodynamic resistance for momentum transfer (s m-1) +#' - Ga_h: Aerodynamic conductance for heat transfer (m s-1) +#' - Ra_h: Aerodynamic resistance for heat transfer (s m-1) +#' - Gb_h: Canopy boundary layer conductance for heat transfer (m s-1) +#' - Rb_h: Canopy boundary layer resistance for heat transfer (s m-1) +#' - kB_h: kB-1 parameter for heat transfer +#' - zeta: Stability parameter 'zeta' (NA if `wind_profile = false`) +#' - psi_h: Integrated stability correction function (NA if `wind_profile = false`) +#' - Ra_CO2: Aerodynamic resistance for CO2 transfer (s m-1) +#' - Ga_CO2: Aerodynamic conductance for CO2 transfer (m s-1) +#' - Gb_CO2: Canopy boundary layer conductance for CO2 transfer (m s-1) #' \item{Ga_Sc_name}{Aerodynamic conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' the respective `Sc` are provided} #' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' the respective `Sc` are provided} #' -#' @note The roughness length for water and heat (z0h) is not returned by this function, but +#' #Note +#' The roughness length for water and heat (z0h) is not returned by this function, but #' it can be calculated from the following relationship (e.g. Verma 1989): #' -#' \deqn{kB-1 = ln(z0m/z0h)} +#' ``kB-1 = ln(z0m/z0h)`` #' #' it follows: #' -#' \deqn{z0h = z0m / exp(kB-1)} +#' ``z0h = z0m / exp(kB-1)`` #' #' `kB-1` is an output of this function. #' @@ -151,7 +152,7 @@ #' #' Note that boundary layer conductance to water vapor transfer (Gb_w) is often #' assumed to equal Gb_h. This assumption is also made in this R package, for -#' example in the function `\link{surface_conductance`}. +#' example in the function [`surface_conductance`](@ref). #' #' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile` if `wind_profile = TRUE` @@ -160,7 +161,8 @@ #' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' #' -#' @references Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. +#' #References +#' Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. #' In: Estimation of areal evapotranspiration, IAHS Pub, 177, 13-20. #' #' Verhoef, A., De Bruin, H., Van Den Hurk, B., 1997: Some practical notes on the parameter kB-1 @@ -173,7 +175,8 @@ #' Monteith, J_L., Unsworth, M_H., 2008: Principles of environmental physics. #' Third Edition. Elsevier Academic Press, Burlington, USA. #' -#' @seealso `\link{Gb_Thom`}, `\link{Gb_Choudhury`}, `\link{Gb_Su`} for calculations of Rb / Gb only +#' #See also +#' [`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref) for calculations of Rb / Gb only #' #' ```@example; output = false #' ``` @@ -191,7 +194,7 @@ """ """ function aerodynamic_conductance(data,Tair="Tair",pressure="pressure",wind="wind",ustar="ustar",H="H", - zr,zh,d,z0m=NULL,Dl,N=2,fc=NULL,LAI,Cd=0.2,hs=0.01,wind_profile=FALSE, + zr,zh,d,z0m=NULL,Dl,N=2,fc=NULL,LAI,Cd=0.2,hs=0.01,wind_profile=false, stab_correction=TRUE,stab_formulation=c("Dyer_1970","Businger_1971"), Rb_model=c("Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"), kB_h=NULL,Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) @@ -280,7 +283,7 @@ end else stop("'stab_formulation' has to be one of 'Dyer_1970' or 'Businger_1971'. - Choose 'stab_correction = FALSE' if no stability correction should be applied.") + Choose 'stab_correction = false' if no stability correction should be applied.") end Ra_m = pmax((log((zr - d)/z0m) - psi_h),0) / (constants[:k]*ustar) @@ -295,7 +298,7 @@ end else if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model %in% c("constant_kB-1","Thom_1972")) - warning("Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = FALSE' (the default). Ra_m is calculated as wind / ustar^2") + warning("Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") end Ra_m = wind / ustar^2 diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index 314e9c1..b583b34 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -14,21 +14,22 @@ #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 #' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) -#' or set to 0 (`FALSE`, the default)? +#' or set to 0 (`false`, the default)? #' - constants DwDc - Ratio of the molecular diffusivities for water vapor and CO2 (-) #' #' # Details Bulk intercellular CO2 concentration (Ci) is given by: #' -#' \deqn{Ci = Ca - (GPP - Rleaf)/(Gs/1.6)} +#' ``Ci = Ca - (GPP - Rleaf)/(Gs/1.6)`` #' #' where Gs/1.6 (mol m-2 s-1) represents the surface conductance to CO2. #' Note that Gs is required in mol m-2 s-1 for water vapor. Gs is converted to #' its value for CO2 internally. #' Ca can either be atmospheric CO2 concentration (as measured), or surface -#' CO2 concentration as calculated from `\link{surface_CO2`}. +#' CO2 concentration as calculated from [`surface_CO2`](@ref). #' -#' @note The equation is based on Fick's law of diffusion and is equivalent to the +#' #Note +#' The equation is based on Fick's law of diffusion and is equivalent to the #' often used equation at leaf level (ci = ca - An/gs). #' Note that GPP and Gs have a different interpretation than An and gs. #' Gs comprises non-physiological contributions (i.e. physical evaporation) @@ -41,9 +42,10 @@ #' readily comparable to its leaf-level analogue and/or physiological meaningful. #' #' # Value - \item{Ci -}{Bulk canopy intercellular CO2 concentration (umol mol-1)} + - Ci -: Bulk canopy intercellular CO2 concentration (umol mol-1) #' -#' @references Kosugi Y. et al., 2013: Determination of the gas exchange phenology in an +#' #References +#' Kosugi Y. et al., 2013: Determination of the gas exchange phenology in an #' evergreen coniferous forest from 7 years of eddy covariance flux data using #' an extended big-leaf analysis. Ecol Res 28, 373-385. #' @@ -61,7 +63,7 @@ """ """ function intercellular_CO2(data,Ca="Ca",GPP="GPP",Gs="Gs_mol",Rleaf=NULL, - missing_Rleaf_as_NA=FALSE,constants=bigleaf_constants()) + missing_Rleaf_as_NA=false,constants=bigleaf_constants()) check_input(data,list(Ca,GPP,Gs)) @@ -87,7 +89,7 @@ end #' CO2 concentration using the Farquhar et al. 1980 model for C3 photosynthesis. #' #' - data Data_Frame or matrix with all required columns -#' - C3 C3 vegetation (`TRUE`, the default) or C4 vegetation (`FALSE`)? +#' - C3 C3 vegetation (`TRUE`, the default) or C4 vegetation (`false`)? #' - Temp Surface (or air) temperature (degC) #' - GPP Gross primary productivity (umol m-2 s-1) #' - Ci Bulk canopy intercellular CO2 concentration (umol mol-1) @@ -114,14 +116,14 @@ end #' - Theta Curvature term in the light response function of J (-) #' - alpha_canopy Canopy absorptance (-) #' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) -#' or set to 0 (`FALSE`, the default)? +#' or set to 0 (`false`, the default)? #' - Ci_C4 intercellular CO2 concentration below which photosynthesis #' is considered to be CO2-limited (umol mol-1), ignored #' if `C3 = TRUE`. -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' Rgas - universal gas constant (J mol-1 K-1) \cr -#' kJ2J - conversion kilojoule (kJ) to joule (J) \cr -#' J2kJ - conversion joule (J) to kilojoule (kJ) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' Rgas - universal gas constant (J mol-1 K-1) +#' kJ2J - conversion kilojoule (kJ) to joule (J) +#' J2kJ - conversion joule (J) to kilojoule (kJ) #' se_median - conversion standard error (SE) of the mean to SE of the median #' #' # Details @@ -129,7 +131,7 @@ end #' transport rate at 25degC (Jmax25), which characterize photosynthetic capacity, #' are calculated as at leaf level. #' The required variables Gs and Ci can be calculated from -#' `\link{surface_conductance`} and `\link{intercellular_CO2`}, respectively. +#' [`surface_conductance`](@ref) and [`intercellular_CO2`](@ref), respectively. #' #' Gas exchange parameters are taken from Bernacchi et al. 2001 (apparent values, which #' assume an infinite mesophyll conductance). Negative and very low Ci values @@ -139,7 +141,7 @@ end #' If net photosynthesis is Rubisco-limited (RuBP-saturated carboxylation #' rate, i.e. light has to be (near-)saturating): #' -#' \deqn{Vcmax = (GPP * (Ci + Kc*(1.0 + Oi/Ko))) / (Ci - Gam)} +#' ``Vcmax = (GPP * (Ci + Kc*(1.0 + Oi/Ko))) / (Ci - Gam)`` #' #' where Kc and Ko are the Michaelis-Menten constants for CO2 and O2 (mmol mol-1), #' respectively, Oi is the O2 concentration, and Gam is the photorespiratory CO2 @@ -147,7 +149,7 @@ end #' Under low-light conditions, the electron transport rate J is calculated from #' the RuBP regeneration-limited photosynthesis rate: #' -#' \deqn{J = (GPP * (4.0 * Ci + 8.0 * Gam) / (Ci - Gam)} +#' ``J = (GPP * (4.0 * Ci + 8.0 * Gam) / (Ci - Gam)`` #' #' In this function, bulk canopy photosynthesis is assumed to be Rubisco/RuBP-regeneration #' limited, if incoming PPFD is above/below a specified threshold or range. These ranges @@ -165,7 +167,7 @@ end #' where APPFD_PSII is the absorbed PPFD by photosystem II (PS II), #' and Theta is a curvature parameter. APPFD_PSII is calculated as #' -#' \deqn{PPFD * alpha_canopy * 0.85 * beta} +#' ``PPFD * alpha_canopy * 0.85 * beta`` #' #' where alpha_canopy is canopy-scale absorptance, 0.85 is a correction factor, #' and beta is the fraction of photons absorbed by PS II (assumed 0.5). @@ -191,7 +193,7 @@ end #' For C4 photosynthesis, the simplified model by von Caemmerer 2000 is used. #' For light-saturated photosynthesis, Vcmax is given by: #' -#' \deqn{Vcmax = GPP} +#' ``Vcmax = GPP`` #' #' Note that in addition to the range `PPFD_c`, the range `Ci_C4` #' discards all periods with low Ci, in which photosynthesis is likely to @@ -199,12 +201,13 @@ end #' #' In the light-limited case, J is calculated as: #' -#' \deqn{J = 3 * GPPj / (1 - 0.5) } +#' ``J = 3 * GPPj / (1 - 0.5) `` #' #' The calculation of Jmax25 and Vcmax25 is identical to C3 photosynthesis #' as described above. #' -#' @note The critical assumption is that bulk canopy photosynthesis is limited by +#' #Note +#' The critical assumption is that bulk canopy photosynthesis is limited by #' one of the two limitation states. Incoming PPFD is assumed to determine #' the limitation states. Note however that the ranges (`PPFD_j` and `PPFD_c`) #' are likely ecosystem-specific. E_g. dense canopies presumably require higher @@ -219,10 +222,11 @@ end #' #' # Value a DataFrame with the following columns: -#' \item{Vcmax25}{maximum bulk canopy carboxylation rate at 25degC (umol m-2 (ground) s-1)} -#' \item{Jmax25}{maximum bulk canopy electron transport rate at 25degC (umol m-2 (ground) s-1)} +#' - Vcmax25: maximum bulk canopy carboxylation rate at 25degC (umol m-2 (ground) s-1) +#' - Jmax25: maximum bulk canopy electron transport rate at 25degC (umol m-2 (ground) s-1) #' -#' @references Lloyd J. et al., 1995: A simple calibrated model of Amazon rainforest productivity +#' #References +#' Lloyd J. et al., 1995: A simple calibrated model of Amazon rainforest productivity #' based on leaf biochemical properties. Plant, Cell and Environment 18, 1129-1145. #' #' Rayment M_B., Loustau D., Jarvis P_G., 2002: Photosynthesis and respiration @@ -248,13 +252,14 @@ end #' von Caemmerer, 2000: Biochemical models of leaf photosynthesis. Techniques #' in plant sciences No. 2. CSIRO Publishing, Collingwood VIC, Australia. #' -#' @seealso `\link{intercellular_CO2`}, `\link{Arrhenius_temp_response`} +#' #See also +#' [`intercellular_CO2`](@ref), [`Arrhenius_temp_response`](@ref) #' #' ```@example; output = false #' ``` -#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), #' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, @@ -269,7 +274,7 @@ end #' # calculate Gs from the the inverted PM equation #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation="Penman-Monteith")[,"Gs_mol"] +#' formulation=Val(:Penman-Monteith))[,"Gs_mol"] #' #' # calculate Ci #' Ci = intercellular_CO2(DE_Tha_Jun_2014_2,Ca="Ca",GPP="GPP",Gs=Gs_PM) @@ -284,7 +289,7 @@ function photosynthetic_capacity(data,C3=TRUE,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD Rleaf=NULL,Oi=0.21,Kc25=404.9,Ko25=278.4,Gam25=42.75, Kc_Ha=79.43,Ko_Ha=36.38,Gam_Ha=37.83,Vcmax_Ha=65.33,Vcmax_Hd=200, Vcmax_dS=0.635,Jmax_Ha=43.9,Jmax_Hd=200,Jmax_dS=0.640, - Theta=0.7,alpha_canopy=0.8,missing_Rleaf_as_NA=FALSE,Ci_C4=100, + Theta=0.7,alpha_canopy=0.8,missing_Rleaf_as_NA=false,Ci_C4=100, constants=bigleaf_constants()) check_input(data,list(Temp,GPP,Ci,PPFD)) @@ -386,8 +391,8 @@ end #' - Ha Activation energy for param (kJ mol-1) #' - Hd Deactivation energy for param (kJ mol-1) #' - dS Entropy term for param (kJ mol-1 K-1) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' Rgas - universal gas constant (J mol-1 K-1) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' Rgas - universal gas constant (J mol-1 K-1) #' kJ2J - conversion kilojoule (kJ) to joule (J) #' #' # Details @@ -416,7 +421,8 @@ end #' # Value param25 - value of the input parameter at the reference temperature of 25degC (umol m-2 s-1) #' -#' @references Johnson F_H., Eyring H., Williams R_W. 1942: +#' #References +#' Johnson F_H., Eyring H., Williams R_W. 1942: #' The nature of enzyme inhibitions in bacterial luminescence: sulfanilamide, #' urethane, temperature and pressure. Journal of cellular and comparative #' physiology 20, 247-268. @@ -483,19 +489,19 @@ end #' - Ca Atmospheric CO2 concentration (air or surface) (umol mol-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 #' - model Stomatal model used. One of `"USO","Ball&Berry","Leuning"`. -#' - robust_nls Use robust nonlinear regression (`\link[robustbase]{nlrob`})? Default is `FALSE`. +#' - robust_nls Use robust nonlinear regression (`\link[robustbase]{nlrob`})? Default is `false`. #' - nmin Minimum number of data required to perform the fit; defaults to 40. #' - fitg0 Should g0 and g1 be fitted simultaneously? #' - g0 Minimum stomatal conductance (mol m-2 s-1); ignored if `fitg0 = TRUE`. #' - fitD0 Should D0 be fitted along with g1 (and g0 if `fitg0 = TRUE`)?; only used if `model = "Leuning"`. -#' - D0 Stomatal sensitivity parameter to VPD; only used if `model = "Leuning"` and `fitD0 = FALSE`. +#' - D0 Stomatal sensitivity parameter to VPD; only used if `model = "Leuning"` and `fitD0 = false`. #' - Gamma Canopy CO2 compensation point (umol mol-1); only used if `model = "Leuning"`. #' Can be a constant or a variable. Defaults to 50 umol mol-1. -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' Rgas - universal gas constant (J mol-1 K-1) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' Rgas - universal gas constant (J mol-1 K-1) #' DwDc - Ratio of the molecular diffusivities for water vapor and CO2 #' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) -#' or set to 0 (`FALSE`, the default)? +#' or set to 0 (`false`, the default)? #' - ... Additional arguments to `\link[stats]{nls`} or `\link[robustbase]{nlrob`} if `robust_nls = TRUE`. #' #' # Details @@ -504,33 +510,34 @@ end #' #' The unified stomatal optimization (USO) model is given by (Medlyn et al. 2011): #' -#' \deqn{gs = g0 + 1.6*(1.0 + g1/sqrt(VPD)) * An/ca} +#' ``gs = g0 + 1.6*(1.0 + g1/sqrt(VPD)) * An/ca`` #' #' The semi-empirical model by Ball et al. 1987 is defined as: #' -#' \deqn{gs = g0 + g1* ((An * rH) / ca)} +#' ``gs = g0 + g1* ((An * rH) / ca)`` #' #' Leuning 1995 suggested a revised version of the Ball&Berry model: #' -#' \deqn{gs = g0 + g1*An / ((ca - \Gamma) * (1 + VPD/D0))} +#' ``gs = g0 + g1*An / ((ca - \\gamma) * (1 + VPD/D0))`` #' -#' where \eqn{\Gamma} is by default assumed to be constant, but likely varies with temperature and among +#' where ``\\gamma`` is by default assumed to be constant, but likely varies with temperature and among #' plant species. #' The equations above are valid at leaf-level. At ecosystem level, An is replaced by GPP (or GPP - Rleaf, #' where Rleaf is leaf respiration), and gs (stomatal conductance) by Gs (surface conductance). #' The parameters in the models are estimated using nonlinear regression (`\link[stats]{nls`}) if -#' `robust_nls = FALSE` and weighted nonlinear regression if `robust_nls = TRUE`. +#' `robust_nls = false` and weighted nonlinear regression if `robust_nls = TRUE`. #' The weights are calculated from `\link[robustbase]{nlrob`}, and `\link[stats]{nls`} #' is used for the actual fitting. #' Alternatively to measured VPD and Ca (i.e. conditions at instrument height), conditions at -#' the big-leaf surface can be provided. Those can be calculated using `\link{surface_conditions`}. +#' the big-leaf surface can be provided. Those can be calculated using [`surface_conditions`](@ref). #' #' #' # Value A `nls` model object, containing information on the fitted parameters, their uncertainty range, #' model fit, etc. #' -#' @references Medlyn B_E., et al., 2011: Reconciling the optimal and empirical approaches to +#' #References +#' Medlyn B_E., et al., 2011: Reconciling the optimal and empirical approaches to #' modelling stomatal conductance. Global Change Biology 17, 2134-2144. #' #' Ball T_J., Woodrow I_E., Berry J_A. 1987: A model predicting stomatal conductance @@ -544,14 +551,15 @@ end #' Knauer, J. et al., 2018: Towards physiologically meaningful water-use efficiency estimates #' from eddy covariance data. Global Change Biology 24, 694-710. #' -#' @seealso `\link{surface_conductance`} +#' #See also +#' [`surface_conductance`](@ref) #' #' ```@example; output = false #' ``` #' ## filter data to ensure that Gs is a meaningful proxy to canopy conductance (Gc) -#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), #' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, @@ -566,23 +574,23 @@ end #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation="Penman-Monteith")[,"Gs_mol"] +#' formulation=Val(:Penman-Monteith))[,"Gs_mol"] #' #' ### Estimate the stomatal slope parameter g1 using the USO model #' mod_USO = stomatal_slope(DE_Tha_Jun_2014_2,model="USO",GPP="GPP",Gs=Gs_PM, -#' robust_nls=FALSE,nmin=40,fitg0=FALSE) +#' robust_nls=false,nmin=40,fitg0=false) #' #' ### Use robust regression to minimize influence of outliers in Gs #' mod_USO = stomatal_slope(DE_Tha_Jun_2014_2,model="USO",GPP="GPP",Gs=Gs_PM, -#' robust_nls=TRUE,nmin=40,fitg0=FALSE) +#' robust_nls=TRUE,nmin=40,fitg0=false) #' #' ### Estimate the same parameter from the Ball&Berry model and prescribe g0 #' mod_BB = stomatal_slope(DE_Tha_Jun_2014_2,model="Ball&Berry",GPP="GPP", -#' robust_nls=FALSE,Gs=Gs_PM,g0=0.01,nmin=40,fitg0=FALSE) +#' robust_nls=false,Gs=Gs_PM,g0=0.01,nmin=40,fitg0=false) #' #' ## same for the Leuning model, but this time estimate both g1 and g0 (but fix D0) #' mod_Leu = stomatal_slope(DE_Tha_Jun_2014_2,model="Leuning",GPP="GPP",Gs=Gs_PM, -#' robust_nls=FALSE,nmin=40,fitg0=FALSE,D0=1.5,fitD0=FALSE) +#' robust_nls=false,nmin=40,fitg0=false,D0=1.5,fitD0=false) #' #' @importFrom stats nls na_exclude #' @importFrom robustbase nlrob @@ -590,8 +598,8 @@ end #' @export function stomatal_slope(data,Tair="Tair",pressure="pressure",GPP="GPP",Gs="Gs_mol", VPD="VPD",Ca="Ca",Rleaf=NULL,model=c("USO","Ball&Berry","Leuning"), - robust_nls=FALSE,nmin=40,fitg0=FALSE,g0=0,fitD0=FALSE, - D0=1.5,Gamma=50,missing_Rleaf_as_NA=FALSE, + robust_nls=false,nmin=40,fitg0=false,g0=0,fitD0=false, + D0=1.5,Gamma=50,missing_Rleaf_as_NA=false, constants=bigleaf_constants(),...) model = match_arg(model) @@ -755,11 +763,11 @@ end A rectangular light response curve is fitted to NEE data. The curve #' takes the form as described in Falge et al. 2001: #' -#' \deqn{-NEE = \alpha PPFD / (1 - (PPFD / PPFD_ref) + \alpha +#' \deqn{-NEE = \\alpha PPFD / (1 - (PPFD / PPFD_ref) + \\alpha #' PPFD / GPP_ref) - Reco} #' -#' where \eqn{\alpha} is the ecosystem quantum yield (umol CO2 m-2 s-1) (umol quanta m-2 s-1)-1, -#' and GPP_ref is the GPP at the reference PPFD (usually at saturating light). \eqn{\alpha} +#' where ``\\alpha`` is the ecosystem quantum yield (umol CO2 m-2 s-1) (umol quanta m-2 s-1)-1, +#' and GPP_ref is the GPP at the reference PPFD (usually at saturating light). ``\\alpha`` #' represents the slope of the light response curve, and is a measure for the light use #' efficiency of the canopy. #' @@ -770,13 +778,15 @@ end #' `PPFD_ref` defaults to 2000 umol m-2 s-1, but other values can be used. For #' further details refer to Falge et al. 2001. #' -#' @note Note the sign convention. Negative NEE indicates that carbon is taken up +#' #Note +#' Note the sign convention. Negative NEE indicates that carbon is taken up #' by the ecosystem. Reco has to be 0 or larger. #' #' # Value A `nls` model object containing estimates (+/- SE) for alpha and GPP_ref. #' -#' @references Falge E., et al. 2001: Gap filling strategies for defensible annual +#' #References +#' Falge E., et al. 2001: Gap filling strategies for defensible annual #' sums of net ecosystem exchange. Agricultural and Forest Meteorology 107, #' 43-69. #' @@ -818,16 +828,17 @@ end #' # Details Light use efficiency is calculated as #' -#' \deqn{LUE = sum(GPP)/sum(PPFD)} +#' ``LUE = sum(GPP)/sum(PPFD)`` #' #' where both GPP and PPFD are in umol m-2 s-1. A more meaningful #' (as directly comparable across ecosystems) approach is to take #' absorbed PPFD rather than incoming PPFD as used here. #' #' # Value - \item{LUE -}{Light use efficiency (-)} + - LUE -: Light use efficiency (-) #' -#' @seealso `\link{energy_use_efficiency`} +#' #See also +#' [`energy_use_efficiency`](@ref) #' #' ```@example; output = false #' ``` @@ -858,26 +869,28 @@ end #' # Details The function fits the following equation (Oren et al. 1999): #' -#' \deqn{Gs = -m ln(VPD) + b} +#' ``Gs = -m ln(VPD) + b`` #' #' where b is the reference surface conductance (Gs) at VPD=1kPa (in mol m-2 s-1), #' and m is the sensitivity parameter of Gs to VPD (in mol m-2 s-1 log(kPa)-1). #' The two parameters b and m are fitted using `\link[stats]{nls`}. #' VPD can be the one directly measured at instrument height, or the -#' one at the surface, as returned by `\link{surface_conditions`}. +#' one at the surface, as returned by [`surface_conditions`](@ref). #' #' # Value A `nls` model object containing (amongst others) estimates for the mean #' and standard errors of the parameters m and b. #' -#' @references Oren R., et al. 1999: Survey and synthesis of intra- and interspecific +#' #References +#' Oren R., et al. 1999: Survey and synthesis of intra- and interspecific #' variation in stomatal sensitivity to vapour pressure deficit. Plant, #' Cell & Environment 22, 1515-1526. #' #' Novick K_A., et al. 2016: The increasing importance of atmospheric demand #' for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. #' -#' @seealso `\link{surface_conductance`} +#' #See also +#' [`surface_conductance`](@ref) #' #' ```@example; output = false #' ``` @@ -885,7 +898,7 @@ end #' ## May 2012. Data are filtered for daytime, sufficiently high ustar, etc. #' FR_Pue_May_2012_2 = filter_data(FR_Pue_May_2012,quality_control=TRUE, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar","VPD"), #' filter_vals_min=c(5,200,0.2,0.3), #' filter_vals_max=c(NA,NA,NA,NA), diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index d83ab9e..29d62fb 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -11,39 +11,41 @@ #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additional quantities, has to be of same length than #' `Sc_name` -#' - constants k - von-Karman constant \cr -#' Sc_CO2 - Schmidt number for CO2 \cr +#' - constants k - von-Karman constant +#' Sc_CO2 - Schmidt number for CO2 #' Pr - Prandtl number (if `Sc` is provided) #' #' #' # Details The empirical equation for Rb suggested by Thom 1972 is: #' -#' \deqn{Rb = 6.2ustar^-0.67} +#' ``Rb = 6.2ustar^-0.67`` #' #' Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. #' Gb for other quantities x is calculated as (Hicks et al. 1987): #' -#' \deqn{Gb_x = Gb / (Sc_x / Pr)^0.67} +#' ``Gb_x = Gb / (Sc_x / Pr)^0.67`` #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' #' # Value a DataFrame with the following columns: -#' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} -#' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} -#' \item{kB_h}{kB-1 parameter for heat transfer} +#' - Gb_h: Boundary layer conductance for heat transfer (m s-1) +#' - Rb_h: Boundary layer resistance for heat transfer (s m-1) +#' - kB_h: kB-1 parameter for heat transfer #' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' `Sc_name` are provided} #' -#' @references Thom, A., 1972: Momentum, mass and heat exchange of vegetation. +#' #References +#' Thom, A., 1972: Momentum, mass and heat exchange of vegetation. #' Quarterly Journal of the Royal Meteorological Society 98, 124-134. #' #' Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso `\link{Gb_Choudhury`}, `\link{Gb_Su`}, `\link{aerodynamic_conductance`} +#' #See also +#' [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) #' #' ```@example; output = false #' ``` @@ -104,15 +106,15 @@ end #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than #' `Sc_name` -#' - constants k - von-Karman constant \cr -#' Sc_CO2 - Schmidt number for CO2 \cr +#' - constants k - von-Karman constant +#' Sc_CO2 - Schmidt number for CO2 #' Pr - Prandtl number (if `Sc` is provided) #' #' # Value A data frame with the following columns: -#' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} -#' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} -#' \item{kB_h}{kB-1 parameter for heat transfer} +#' - Gb_h: Boundary layer conductance for heat transfer (m s-1) +#' - Rb_h: Boundary layer resistance for heat transfer (s m-1) +#' - kB_h: kB-1 parameter for heat transfer #' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' `Sc_name` are provided} #' @@ -120,30 +122,32 @@ end Boundary layer conductance according to Choudhury & Monteith 1988 is #' given by: #' -#' \deqn{Gb_h = LAI((2a/\alpha)*sqrt(u(h)/w)*(1-exp(-\alpha/2)))} +#' ``Gb_h = LAI((2a/\\alpha)*sqrt(u(h)/w)*(1-exp(-\\alpha/2)))`` #' #' where u(zh) is the wind speed at the canopy surface, approximated from -#' measured wind speed at sensor height zr and a wind extinction coefficient \eqn{\alpha}: +#' measured wind speed at sensor height zr and a wind extinction coefficient ``\\alpha``: #' -#' \deqn{u(zh) = u(zr) / (exp(\alpha(zr/zh -1)))}. +#' ``u(zh) = u(zr) / (exp(\\alpha(zr/zh -1)))``. #' -#' \eqn{\alpha} is modeled as an empirical relation to LAI (McNaughton & van den Hurk 1995): +#' ``\\alpha`` is modeled as an empirical relation to LAI (McNaughton & van den Hurk 1995): #' -#' \deqn{\alpha = 4.39 - 3.97*exp(-0.258*LAI)} +#' ``\\alpha = 4.39 - 3.97*exp(-0.258*LAI)`` #' #' Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. #' Gb for other quantities x is calculated as (Hicks et al. 1987): #' -#' \deqn{Gb_x = Gb / (Sc_x / Pr)^0.67} +#' ``Gb_x = Gb / (Sc_x / Pr)^0.67`` #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' @note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +#' #Note +#' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile`. This function #' estimates a single `z0m` value for the entire time period! If a varying `z0m` value #' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' -#' @references Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat +#' #References +#' Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat #' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. #' #' McNaughton, K. G., Van den Hurk, B_J_J_M., 1995: A 'Lagrangian' revision of @@ -154,7 +158,8 @@ end #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso `\link{Gb_Thom`}, `\link{Gb_Su`}, `\link{aerodynamic_conductance`} +#' #See also +#' [`Gb_Thom`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) #' #' ```@example; output = false #' ``` @@ -181,7 +186,7 @@ function Gb_Choudhury(data,Tair="Tair",pressure="pressure",wind="wind",ustar="us estimate_z0m = TRUE z0m = NULL else - estimate_z0m = FALSE + estimate_z0m = false end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, @@ -244,17 +249,17 @@ end #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additional quantities, has to be of same length than #' `Sc_name` -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' pressure0 - reference atmospheric pressure at sea level (Pa) \cr -#' Tair0 - reference air temperature (K) \cr -#' Sc_CO2 - Schmidt number for CO2 \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' pressure0 - reference atmospheric pressure at sea level (Pa) +#' Tair0 - reference air temperature (K) +#' Sc_CO2 - Schmidt number for CO2 #' Pr - Prandtl number (if `Sc` is provided) #' #' # Value A DataFrame with the following columns: -#' \item{Gb_h}{Boundary layer conductance for heat transfer (m s-1)} -#' \item{Rb_h}{Boundary layer resistance for heat transfer (s m-1)} -#' \item{kB_h}{kB-1 parameter for heat transfer} +#' - Gb_h: Boundary layer conductance for heat transfer (m s-1) +#' - Rb_h: Boundary layer resistance for heat transfer (s m-1) +#' - kB_h: kB-1 parameter for heat transfer #' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' `Sc_name` are provided} #' @@ -262,42 +267,44 @@ end The formulation is based on the kB-1 model developed by Massman 1999. #' Su et al. 2001 derived the following approximation: #' -#' \deqn{kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2} +#' ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` #' #' If fc (fractional vegetation cover) is missing, it is estimated from LAI: #' -#' \deqn{fc = 1 - exp(-LAI/2)} +#' ``fc = 1 - exp(-LAI/2)`` #' #' The wind speed at the top of the canopy is calculated using function -#' `\link{wind_profile`}. +#' [`wind_profile`](@ref). #' #' Ct is the heat transfer coefficient of the leaf (Massman 1999): #' -#' \deqn{Ct = Pr^-2/3 Reh^-1/2 N} +#' ``Ct = Pr^-2/3 Reh^-1/2 N`` #' #' where Pr is the Prandtl number (set to 0.71), and Reh is the Reynolds number for leaves: #' -#' \deqn{Reh = Dl wind(zh) / v} +#' ``Reh = Dl wind(zh) / v`` #' #' kBs-1, the kB-1 value for bare soil surface, is calculated according #' to Su et al. 2001: #' -#' \deqn{kBs^-1 = 2.46(Re)^0.25 - ln(7.4)} +#' ``kBs^-1 = 2.46(Re)^0.25 - ln(7.4)`` #' #' Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. #' Gb for other quantities x is calculated as (Hicks et al. 1987): #' -#' \deqn{Gb_x = Gb / (Sc_x / Pr)^0.67} +#' ``Gb_x = Gb / (Sc_x / Pr)^0.67`` #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' @note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +#' #Note +#' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile`. This function #' estimates a single `z0m` value for the entire time period! If a varying `z0m` value #' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' #' -#' @references Su, Z., Schmugge, T., Kustas, W. & Massman, W., 2001: An evaluation of +#' #References +#' Su, Z., Schmugge, T., Kustas, W. & Massman, W., 2001: An evaluation of #' two models for estimation of the roughness height for heat transfer between #' the land surface and the atmosphere. Journal of Applied Meteorology 40, 1933-1951. #' @@ -308,7 +315,8 @@ end #' A preliminary multiple resistance routine for deriving dry deposition velocities #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' -#' @seealso `\link{Gb_Thom`}, `\link{Gb_Choudhury`}, `\link{aerodynamic_conductance`} +#' #See also +#' [`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`aerodynamic_conductance`](@ref) #' #' ```@example; output = false #' ``` @@ -336,7 +344,7 @@ function Gb_Su(data,Tair="Tair",pressure="pressure",ustar="ustar",wind="wind", if (is_null(fc)) if (is_null(LAI)) - stop("one of 'fc' or 'LAI' must be provided",call.=FALSE) + stop("one of 'fc' or 'LAI' must be provided",call.=false) else fc = (1-exp(-LAI/2)) end @@ -346,7 +354,7 @@ end estimate_z0m = TRUE z0m = NULL else - estimate_z0m = FALSE + estimate_z0m = false end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, diff --git a/inst/fromR/check_input.jl b/inst/fromR/check_input.jl index 913f081..36242ae 100755 --- a/inst/fromR/check_input.jl +++ b/inst/fromR/check_input.jl @@ -9,7 +9,8 @@ #' - data Input DataFrame or matrix #' - ... Input variables. Either a list or individual vectors #' -#' @note This function can be run for named variables (in which case the return +#' #Note +#' This function can be run for named variables (in which case the return #' value will be named according to the given name), or for placeholder #' variables that are assigned and named according to e.g. entries of a character #' vector. In the latter case, the input variable has to be named as `"var"` or @@ -41,23 +42,23 @@ end if (is_numeric(var)) assign(varname,var,pos=sys_frame(-1)) else - stop("column representing '",varname,"' in the input matrix/DataFrame must be numeric",call.=FALSE) + stop("column representing '",varname,"' in the input matrix/DataFrame must be numeric",call.=false) end else - stop ("there is no column named '",var,"' in the input matrix/DataFrame. Indicate the name of the column representing variable '",varname,"', or alternatively, provide a numeric vector of the same length as the input matrix/DataFrame or of length 1.",call.=FALSE) + stop ("there is no column named '",var,"' in the input matrix/DataFrame. Indicate the name of the column representing variable '",varname,"', or alternatively, provide a numeric vector of the same length as the input matrix/DataFrame or of length 1.",call.=false) end else - stop("name of variable '",varname,"' must have length 1",call.=FALSE) + stop("name of variable '",varname,"' must have length 1",call.=false) end else if ("data" %in% names(formals(sys_function(which=-1)))) if (var %in% as_character(unlist(match_call(definition=sys_function(-1),call=sys_call(-1))[-1]))) - stop("variable '",var,"' is of type character and interpreted as a column name, but no input matrix/DataFrame is provided. Provide '",var,"' as a numeric vector, or an input matrix/DataFrame with a column named '",var,"'",call.=FALSE) + stop("variable '",var,"' is of type character and interpreted as a column name, but no input matrix/DataFrame is provided. Provide '",var,"' as a numeric vector, or an input matrix/DataFrame with a column named '",var,"'",call.=false) else - stop("variable '",var,"' is not provided",call.=FALSE) + stop("variable '",var,"' is not provided",call.=false) end else - stop("variable '",var,"' must be numeric",call.=FALSE) + stop("variable '",var,"' must be numeric",call.=false) end end else @@ -78,16 +79,16 @@ else if (is_numeric(var) & length(var) != nrow(data)) var = rep(var,length=nrow(data)) assign(varname,var,envir=sys_frame(-1)) else - stop("variable '",varname,"' must have the same length as the input matrix/DataFrame or length 1. Do NOT provide an input matrix/DataFrame if none of its variables are used!",call.=FALSE) + stop("variable '",varname,"' must have the same length as the input matrix/DataFrame or length 1. Do NOT provide an input matrix/DataFrame if none of its variables are used!",call.=false) end else if (!is_numeric(var)) - stop("variable '",varname,"' must be numeric",call.=FALSE) + stop("variable '",varname,"' must be numeric",call.=false) end else if (is_numeric(var)) assign(varname,var,envir=sys_frame(-1)) else - stop("variable '",varname,"' must be numeric",call.=FALSE) + stop("variable '",varname,"' must be numeric",call.=false) end end end @@ -100,15 +101,16 @@ end #' #' - varlist List of variables for which the length has to be compared #' -#' @note This function only plays a role if no input DataFrame or matrix are +#' #Note +#' This function only plays a role if no input DataFrame or matrix are #' provided. In this case it ensures that provided vectors have the same #' length to avoid trouble later up the function call. #' #' @keywords internal function check_length(varlist) - if (is_list(unlist(varlist,recursive=FALSE))) - varlist = unlist(varlist,recursive=FALSE) + if (is_list(unlist(varlist,recursive=false))) + varlist = unlist(varlist,recursive=false) end length_vars = sapply(varlist,length) @@ -116,7 +118,7 @@ end if (length(unique(length_vars)) >= 2) if (sort(unique(length_vars))[1] != 1 | length(unique(length_vars)) > 2) - stop("All input variables must have the same length or a length of 1!",call.=FALSE) + stop("All input variables must have the same length or a length of 1!",call.=false) end end return(varlist) diff --git a/inst/fromR/datasets_description.jl b/inst/fromR/datasets_description.jl index 6daaabe..5e627d4 100755 --- a/inst/fromR/datasets_description.jl +++ b/inst/fromR/datasets_description.jl @@ -7,40 +7,41 @@ #' #' @format A data frame with 1488 observations and 31 columns: #' \describe -#' \item{year}{year of measurement} -#' \item{month}{month of measurement} -#' \item{doy}{day of year} -#' \item{hour}{hour (0 - 23.5)} -#' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} -#' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} -#' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} -#' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} -#' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} -#' \item{ustar}{friction velocity (m s-1) [USTAR]} -#' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} -#' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} -#' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} -#' \item{Rn}{Net radiation (W m-2) [NETRAD]} -#' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} -#' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} -#' \item{G}{Ground heat flux (W m-2) [G_F_MDS]} -#' \item{G_qc}{Quality control of `G` [G_F_MDS_QC]} -#' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} -#' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} -#' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} +#' - year: year of measurement +#' - month: month of measurement +#' - doy: day of year +#' - hour: hour (0 - 23.5) +#' - Tair: Air temperature (degC) [TA_F] +#' - Tair_qc: Quality control of `Tair` [TA_F_QC] +#' - PPFD: Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN] +#' - PPFD_qc: Quality control of `PPFD` [PPFD_IN_QC] +#' - VPD: Vapor pressure deficit (kPa) [VPD_F] +#' - VPD_qc: Quality control of `VPD` [VPD_F_QC] +#' - pressure: Atmospheric pressure (kPa) [PA_F] +#' - precip: precipitation (mm) [P_F] +#' - precip_qc: Quality control of `precip` [P_F_QC] +#' - ustar: friction velocity (m s-1) [USTAR] +#' - wind: horizontal wind velocity (m s-1) [WS_F] +#' - wind_qc: Quality control of `wind` [WS_F_QC] +#' - Ca: CO2 concentration (ppm) [CO2_F_MDS] +#' - Ca_qc: Quality control of `Ca` [CO2_F_MDS_QC] +#' - LW_up: upward longwave radiation (W m-2) [LW_OUT] +#' - Rn: Net radiation (W m-2) [NETRAD] +#' - LE: Latent heat flux (W m-2) [LE_F_MDS] +#' - LE_qc: Quality control of `LE` [LE_F_MDS_QC] +#' - H: Sensible heat flux (W m-2) [H_F_MDS] +#' - H_qc: Quality control of `H` [H_F_MDS_QC] +#' - G: Ground heat flux (W m-2) [G_F_MDS] +#' - G_qc: Quality control of `G` [G_F_MDS_QC] +#' - NEE: Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50] +#' - NEE_qc: Quality control of `NEE` [NEE_VUT_USTAR50_QC] +#' - GPP: Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50] +#' - GPP_qc: Quality control of `GPP` [NEE_VUT_USTAR50_QC] +#' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' @note The original variable names as provided by the FLUXNET2015 dataset are +#' #Note +#' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). #' @@ -59,41 +60,42 @@ #' #' @format A data frame with 1440 observations and 32 columns: #' \describe -#' \item{year}{year of measurement} -#' \item{month}{month of measurement} -#' \item{doy}{day of year} -#' \item{hour}{hour (0 - 23.5)} -#' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} -#' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} -#' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} -#' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} -#' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} -#' \item{ustar}{friction velocity (m s-1) [USTAR]} -#' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} -#' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} -#' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} -#' \item{LW_down}{downward longwave radiation (W m-2) [LW_IN_F]} -#' \item{Rn}{Net radiation (W m-2) [NETRAD]} -#' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} -#' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} -#' \item{G}{Ground heat flux (W m-2) [G_F_MDS]} -#' \item{G_qc}{Quality control of `G` [G_F_MDS_QC]} -#' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} -#' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} -#' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} +#' - year: year of measurement +#' - month: month of measurement +#' - doy: day of year +#' - hour: hour (0 - 23.5) +#' - Tair: Air temperature (degC) [TA_F] +#' - Tair_qc: Quality control of `Tair` [TA_F_QC] +#' - PPFD: Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN] +#' - PPFD_qc: Quality control of `PPFD` [PPFD_IN_QC] +#' - VPD: Vapor pressure deficit (kPa) [VPD_F] +#' - VPD_qc: Quality control of `VPD` [VPD_F_QC] +#' - pressure: Atmospheric pressure (kPa) [PA_F] +#' - precip: precipitation (mm) [P_F] +#' - precip_qc: Quality control of `precip` [P_F_QC] +#' - ustar: friction velocity (m s-1) [USTAR] +#' - wind: horizontal wind velocity (m s-1) [WS_F] +#' - wind_qc: Quality control of `wind` [WS_F_QC] +#' - Ca: CO2 concentration (ppm) [CO2_F_MDS] +#' - Ca_qc: Quality control of `Ca` [CO2_F_MDS_QC] +#' - LW_up: upward longwave radiation (W m-2) [LW_OUT] +#' - LW_down: downward longwave radiation (W m-2) [LW_IN_F] +#' - Rn: Net radiation (W m-2) [NETRAD] +#' - LE: Latent heat flux (W m-2) [LE_F_MDS] +#' - LE_qc: Quality control of `LE` [LE_F_MDS_QC] +#' - H: Sensible heat flux (W m-2) [H_F_MDS] +#' - H_qc: Quality control of `H` [H_F_MDS_QC] +#' - G: Ground heat flux (W m-2) [G_F_MDS] +#' - G_qc: Quality control of `G` [G_F_MDS_QC] +#' - NEE: Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50] +#' - NEE_qc: Quality control of `NEE` [NEE_VUT_USTAR50_QC] +#' - GPP: Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50] +#' - GPP_qc: Quality control of `GPP` [NEE_VUT_USTAR50_QC] +#' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' @note The original variable names as provided by the FLUXNET2015 dataset are +#' #Note +#' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). #' @@ -112,38 +114,39 @@ #' #' @format A data frame with 1488 observations and 29 columns: #' \describe -#' \item{year}{year of measurement} -#' \item{month}{month of measurement} -#' \item{doy}{day of year} -#' \item{hour}{hour (0 - 23.5)} -#' \item{Tair}{Air temperature (degC) [TA_F]} -#' \item{Tair_qc}{Quality control of `Tair` [TA_F_QC]} -#' \item{PPFD}{Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN]} -#' \item{PPFD_qc}{Quality control of `PPFD` [PPFD_IN_QC]} -#' \item{VPD}{Vapor pressure deficit (kPa) [VPD_F]} -#' \item{VPD_qc}{Quality control of `VPD` [VPD_F_QC]} -#' \item{pressure}{Atmospheric pressure (kPa) [PA_F]} -#' \item{precip}{precipitation (mm) [P_F]} -#' \item{precip_qc}{Quality control of `precip` [P_F_QC]} -#' \item{ustar}{friction velocity (m s-1) [USTAR]} -#' \item{wind}{horizontal wind velocity (m s-1) [WS_F]} -#' \item{wind_qc}{Quality control of `wind` [WS_F_QC]} -#' \item{Ca}{CO2 concentration (ppm) [CO2_F_MDS]} -#' \item{Ca_qc}{Quality control of `Ca` [CO2_F_MDS_QC]} -#' \item{LW_up}{upward longwave radiation (W m-2) [LW_OUT]} -#' \item{Rn}{Net radiation (W m-2) [NETRAD]} -#' \item{LE}{Latent heat flux (W m-2) [LE_F_MDS]} -#' \item{LE_qc}{Quality control of `LE` [LE_F_MDS_QC]} -#' \item{H}{Sensible heat flux (W m-2) [H_F_MDS]} -#' \item{H_qc}{Quality control of `H` [H_F_MDS_QC]} -#' \item{NEE}{Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50]} -#' \item{NEE_qc}{Quality control of `NEE` [NEE_VUT_USTAR50_QC]} -#' \item{GPP}{Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50]} -#' \item{GPP_qc}{Quality control of `GPP` [NEE_VUT_USTAR50_QC]} -#' \item{Reco}{Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50]} +#' - year: year of measurement +#' - month: month of measurement +#' - doy: day of year +#' - hour: hour (0 - 23.5) +#' - Tair: Air temperature (degC) [TA_F] +#' - Tair_qc: Quality control of `Tair` [TA_F_QC] +#' - PPFD: Photosynthetic photon flux density (umol m-2 s-1) [PPFD_IN] +#' - PPFD_qc: Quality control of `PPFD` [PPFD_IN_QC] +#' - VPD: Vapor pressure deficit (kPa) [VPD_F] +#' - VPD_qc: Quality control of `VPD` [VPD_F_QC] +#' - pressure: Atmospheric pressure (kPa) [PA_F] +#' - precip: precipitation (mm) [P_F] +#' - precip_qc: Quality control of `precip` [P_F_QC] +#' - ustar: friction velocity (m s-1) [USTAR] +#' - wind: horizontal wind velocity (m s-1) [WS_F] +#' - wind_qc: Quality control of `wind` [WS_F_QC] +#' - Ca: CO2 concentration (ppm) [CO2_F_MDS] +#' - Ca_qc: Quality control of `Ca` [CO2_F_MDS_QC] +#' - LW_up: upward longwave radiation (W m-2) [LW_OUT] +#' - Rn: Net radiation (W m-2) [NETRAD] +#' - LE: Latent heat flux (W m-2) [LE_F_MDS] +#' - LE_qc: Quality control of `LE` [LE_F_MDS_QC] +#' - H: Sensible heat flux (W m-2) [H_F_MDS] +#' - H_qc: Quality control of `H` [H_F_MDS_QC] +#' - NEE: Net ecosystem exchange (umol m-2 s-1) [NEE_VUT_USTAR50] +#' - NEE_qc: Quality control of `NEE` [NEE_VUT_USTAR50_QC] +#' - GPP: Gross primary productivity from nighttime partitioning (umol m-2 s-1) [GPP_NT_VUT_USTAR50] +#' - GPP_qc: Quality control of `GPP` [NEE_VUT_USTAR50_QC] +#' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' @note The original variable names as provided by the FLUXNET2015 dataset are +#' #Note +#' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). #' diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index 44d6348..5d50b4e 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -16,11 +16,11 @@ #' - LAI Leaf area index (m2 m-2), only used if `approach = "Martin_1989"`. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air (-) \cr -#' sigma - Stefan-Boltzmann constant (W m-2 K-4) \cr +#' See [`Esat_slope`](@ref). +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air (-) +#' sigma - Stefan-Boltzmann constant (W m-2 K-4) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details @@ -30,35 +30,37 @@ #' characterized by high physiological (i.e. stomatal) control on transpiration #' and similar conditions at the canopy surface compared to the atmosphere above #' the canopy. Values close to 1 indicate the opposite, i.e. decoupled conditions and -#' a low stomatal control on transpiration (Jarvis & McNaughton 1986). \cr +#' a low stomatal control on transpiration (Jarvis & McNaughton 1986). #' The `"Jarvis&McNaughton_1986"` approach (default option) is the original #' formulation for the decoupling coefficient, given by (for an amphistomatous #' canopy): #' -#' \deqn{\Omega = \frac{\epsilon + 1}{\epsilon + 1 + \frac{Ga}{Gc}}}{% -#' \Omega = (\epsilon + 1) / ( \epsilon + 1 + Ga/Gc)} +#' ``\\Omega = \frac{\epsilon + 1``{\epsilon + 1 + \frac{Ga}{Gc}}}{% +#' \\Omega = (\epsilon + 1) / ( \epsilon + 1 + Ga/Gc)} #' -#' where \eqn{\epsilon = \frac{s}{\gamma}}{\epsilon = s/\gamma} is a dimensionless coefficient -#' with s being the slope of the saturation vapor pressure curve (Pa K-1), and \eqn{\gamma} the +#' where ``\epsilon = \frac{s``{\\gamma}}{\epsilon = s/\\gamma} is a dimensionless coefficient +#' with s being the slope of the saturation vapor pressure curve (Pa K-1), and ``\\gamma`` the #' psychrometric constant (Pa K-1). #' #' The approach `"Martin_1989"` by Martin 1989 additionally takes radiative coupling #' into account: #' -#' \deqn{\Omega = \frac{\epsilon + 1 + \frac{Gr}{Ga}}{\epsilon + (1 + \frac{Ga}{Gs}) (1 + \frac{Gr}{Ga})}}{% -#' \Omega = (\epsilon + 1 + Gr/Ga) / (\epsilon + (1 + Ga/Gs) (1 + Gr/Ga))} +#' ``\\Omega = \frac{\epsilon + 1 + \frac{Gr``{Ga}}{\epsilon + (1 + \frac{Ga}{Gs}) (1 + \frac{Gr}{Ga})}}{% +#' \\Omega = (\epsilon + 1 + Gr/Ga) / (\epsilon + (1 + Ga/Gs) (1 + Gr/Ga))} #' #' # Value - \item{\eqn{\Omega} -}{the decoupling coefficient Omega (-)} + - ``\\Omega`` -: the decoupling coefficient Omega (-) #' -#' @references Jarvis P_G., McNaughton K_G., 1986: Stomatal control of transpiration: +#' #References +#' Jarvis P_G., McNaughton K_G., 1986: Stomatal control of transpiration: #' scaling up from leaf to region. Advances in Ecological Research 15, 1-49. #' #' Martin P., 1989: The significance of radiative coupling between #' vegetation and the atmosphere. Agricultural and Forest Meteorology 49, 45-53. #' -#' @seealso `\link{aerodynamic_conductance`}, `\link{surface_conductance`}, -#' `\link{equilibrium_imposed_ET`} +#' #See also +#' [`aerodynamic_conductance`](@ref), [`surface_conductance`](@ref), +#' [`equilibrium_imposed_ET`](@ref) #' #' ```@example; output = false #' ``` @@ -114,19 +116,20 @@ end #' #' - Tair Air temperature (deg C) #' - LAI Leaf area index (m2 m-2) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' sigma - Stefan-Boltzmann constant (W m-2 K-4) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' sigma - Stefan-Boltzmann constant (W m-2 K-4) #' cp - specific heat of air for constant pressure (J K-1 kg-1) #' #' # Details the following formula is used (Martin, 1989): #' -#' \deqn{Gr = 4 \sigma Tair^3 LAI / cp} +#' ``Gr = 4 \sigma Tair^3 LAI / cp`` #' #' # Value - \item{Gr -}{longwave radiative transfer conductance of the canopy (m s-1)} + - Gr -: longwave radiative transfer conductance of the canopy (m s-1) #' -#' @references Martin P., 1989: The significance of radiative coupling between +#' #References +#' Martin P., 1989: The significance of radiative coupling between #' vegetation and the atmosphere. Agricultural and Forest Meteorology 49, 45-53. #' #' ```@example; output = false diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index d435f82..472f516 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -18,9 +18,10 @@ #' have been used (e.g. Blanken et al., 1997) #' #' # Value - \item{Sp -}{biochemical energy (W m-2)} + - Sp -: biochemical energy (W m-2) #' -#' @references Meyers, T_P., Hollinger, S_E. 2004: An assessment of storage terms in the surface energy +#' #References +#' Meyers, T_P., Hollinger, S_E. 2004: An assessment of storage terms in the surface energy #' balance of maize and soybean. Agricultural and Forest Meteorology 125, 105-115. #' #' Nobel, P_S., 1974: Introduction to Biophysical Plant Physiology. @@ -56,15 +57,16 @@ end #' # Details Energy use efficiency is calculated as: #' -#' \deqn{EUE = sum(GPP)/sum(Rn)} +#' ``EUE = sum(GPP)/sum(Rn)`` #' #' where the sums are calculated for complete cases of GPP and Rn over #' the entire time period. #' #' # Value - \item{EUE -}{Energy use efficiency (-)} + - EUE -: Energy use efficiency (-) #' -#' @seealso `\link{light_use_efficiency`} +#' #See also +#' [`light_use_efficiency`](@ref) #' #' ```@example; output = false #' ``` @@ -102,7 +104,7 @@ end #' - H Sensible heat flux (W m-2) #' - instantaneous should the energy balance be calculated at the time step #' of the observations (`TRUE`), or over the entire time period -#' provided as input (`FALSE`) +#' provided as input (`false`) #' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s ,otherwise set to 0. #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' @@ -110,28 +112,29 @@ end #' # Details The energy balance ratio (EBR) is calculated as: #' -#' \deqn{EBR = sum(LE + H)/sum(Rn - G - S)} +#' ``EBR = sum(LE + H)/sum(Rn - G - S)`` #' #' the sum is taken for all time steps with complete observations (i.e. where #' all energy balance terms are available). #' #' # Value a named vector containing: -#' \item{n}{number of complete (all energy balance terms available) observations} -#' \item{intercept}{intercept of the OLS regression} -#' \item{slope}{slope of the OLS regression} -#' \item{r_squared}{r^2 of the OLS regression} -#' \item{EBR}{energy balance ratio} +#' - n: number of complete (all energy balance terms available) observations +#' - intercept: intercept of the OLS regression +#' - slope: slope of the OLS regression +#' - r_squared: r^2 of the OLS regression +#' - EBR: energy balance ratio #' #' if `instantaneous = TRUE`, only `EBR` is returned. #' -#' @references Wilson K., et al. 2002: Energy balance closure at FLUXNET sites. +#' #References +#' Wilson K., et al. 2002: Energy balance closure at FLUXNET sites. #' Agricultural and Forest Meteorology 113, 223-243. #' #' ```@example; output = false #' ``` #' ## characterize energy balance closure for DE-Tha in June 2014 -#' energy_closure(DE_Tha_Jun_2014,instantaneous=FALSE) +#' energy_closure(DE_Tha_Jun_2014,instantaneous=false) #' #' ## look at half-hourly closure #' EBR_inst = energy_closure(DE_Tha_Jun_2014,instantaneous=TRUE) @@ -140,8 +143,8 @@ end #' @importFrom stats complete_cases lm """ """ -function energy_closure(data,Rn="Rn",G=NULL,S=NULL,LE="LE",H="H",instantaneous=FALSE, - missing_G_as_NA=FALSE,missing_S_as_NA=FALSE) +function energy_closure(data,Rn="Rn",G=NULL,S=NULL,LE="LE",H="H",instantaneous=false, + missing_G_as_NA=false,missing_S_as_NA=false) check_input(data,list(Rn,LE,H,G,S)) @@ -193,21 +196,22 @@ end #' - Tair Air temperature (degC) #' - Tsurf Surface temperature (degC) #' - emissivity Emissivity of the surface (-) -#' - constants sigma - Stefan-Boltzmann constant (W m-2 K-4) \cr +#' - constants sigma - Stefan-Boltzmann constant (W m-2 K-4) #' Kelvin - conversion degree Celsius to Kelvin #' #' # Details The isothermal net radiation (Rni) is given by: #' -#' \deqn{Rni = Rn + \epsilon * \sigma * (Tsurf^4 - Tair^4)} +#' ``Rni = Rn + \epsilon * \sigma * (Tsurf^4 - Tair^4)`` #' -#' where \eqn{\epsilon} is the emissivity of the surface. Tsurf and Tair +#' where ``\epsilon`` is the emissivity of the surface. Tsurf and Tair #' are in Kelvin. #' #' # Value - \item{Rni -}{isothermal net radiation (W m-2)} + - Rni -: isothermal net radiation (W m-2) #' -#' @references Jones, H. 2014: Plants and Microclimate. 3rd edition, Cambridge +#' #References +#' Jones, H. 2014: Plants and Microclimate. 3rd edition, Cambridge #' University Press. #' #' ```@example; output = false diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index 5a192bd..9c5beed 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -13,55 +13,57 @@ #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - VPD Vapor pressure deficit (kPa); only used if `approach = "Penman-Monteith"`. -#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = "Penman-Monteith"`. -#' - approach Approach used. Either `"Priestley-Taylor"` (default), or `"Penman-Monteith"`. -#' - alpha Priestley-Taylor coefficient; only used if `approach = "Priestley-Taylor"`. +#' - VPD Vapor pressure deficit (kPa); only used if `approach = Val(:Penman-Monteith)`. +#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = Val(:Penman-Monteith)`. +#' - approach Approach used. Either `Val(:Priestley-Taylor)` (default), or `Val(:Penman-Monteith)`. +#' - alpha Priestley-Taylor coefficient; only used if `approach = Val(:Priestley-Taylor)`. #' - Gs_pot Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; -#' only used if `approach = "Penman-Monteith"`. +#' only used if `approach = Val(:Penman-Monteith)`. #' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air \cr -#' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) \cr -#' Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = "Penman-Monteith"`) \cr -#' Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = "Penman-Monteith"`) \cr -#' Kelvin - conversion degree Celsius to Kelvin (only used if `approach = "Penman-Monteith"`) \cr +#' See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air +#' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) +#' Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) +#' Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) +#' Kelvin - conversion degree Celsius to Kelvin (only used if `approach = Val(:Penman-Monteith)`) #' #' # Details - Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 -#' if `approach = "Priestley-Taylor"` (the default): +#' Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 +#' if `approach = Val(:Priestley-Taylor)` (the default): #' -#' \deqn{LE_pot,PT = (\alpha * \Delta * (Rn - G - S)) / (\Delta + \gamma)} +#' ``LE_pot,PT = (\\alpha * \\Delta * (Rn - G - S)) / (\\Delta + \\gamma)`` #' -#' \eqn{\alpha} is the Priestley-Taylor coefficient, \eqn{\Delta} is the slope -#' of the saturation vapor pressure curve (kPa K-1), and \eqn{\gamma} is the +#' ``\\alpha`` is the Priestley-Taylor coefficient, ``\\Delta`` is the slope +#' of the saturation vapor pressure curve (kPa K-1), and ``\\gamma`` is the #' psychrometric constant (kPa K-1). -#' if `approach = "Penman-Monteith"`, potential evapotranspiration is calculated according +#' if `approach = Val(:Penman-Monteith)`, potential evapotranspiration is calculated according #' to the Penman-Monteith equation: #' -#' \deqn{LE_pot,PM = (\Delta * (Rn - G - S) + \rho * cp * VPD * Ga) / (\Delta + \gamma * (1 + Ga/Gs_pot)} +#' ``LE_pot,PM = (\\Delta * (Rn - G - S) + \\rho * cp * VPD * Ga) / (\\Delta + \\gamma * (1 + Ga/Gs_pot)`` #' -#' where \eqn{\Delta} is the slope of the saturation vapor pressure curve (kPa K-1), -#' \eqn{\rho} is the air density (kg m-3), and \eqn{\gamma} is the psychrometric constant (kPa K-1). +#' where ``\\Delta`` is the slope of the saturation vapor pressure curve (kPa K-1), +#' ``\\rho`` is the air density (kg m-3), and ``\\gamma`` is the psychrometric constant (kPa K-1). #' The value of `Gs_pot` is typically a maximum value of Gs observed at the site, e.g. the 90th #' percentile of Gs within the growing season. #' #' # Value - a DataFrame with the following columns: -#' \item{ET_pot}{Potential evapotranspiration (kg m-2 s-1)} -#' \item{LE_pot}{Potential latent heat flux (W m-2)} +#' a DataFrame with the following columns: +#' - ET_pot: Potential evapotranspiration (kg m-2 s-1) +#' - LE_pot: Potential latent heat flux (W m-2) #' -#' @note If the first argument `data` is provided (either a matrix or a DataFrame), +#' #Note +#' If the first argument `data` is provided (either a matrix or a DataFrame), #' the following variables can be provided as character (in which case they are interpreted as #' the column name of `data`) or as numeric vectors, in which case they are taken #' directly for the calculations. If `data` is not provided, all input variables have to be #' numeric vectors. #' -#' @references Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux +#' #References +#' Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux #' and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. #' #' Allen, R_G., Pereira L_S., Raes D., Smith M., 1998: Crop evapotranspiration - @@ -71,18 +73,19 @@ #' Novick, K_A., et al. 2016: The increasing importance of atmospheric demand #' for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. #' -#' @seealso `\link{surface_conductance`} +#' #See also +#' [`surface_conductance`](@ref) #' #' ```@example; output = false #' ``` #' # Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 #' # using Priestley-Taylor: -#' potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach="Priestley-Taylor") +#' potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach=Val(:Priestley-Taylor)) #' #' # Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) #' # using Penman-Monteith: #' LE_pot_PM = potential_ET(Gs_pot=0.5,Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400, -#' approach="Penman-Monteith")[,"LE_pot"] +#' approach=Val(:Penman-Monteith))[,"LE_pot"] #' LE_pot_PM #' #' # now cross-check with the inverted equation @@ -90,8 +93,8 @@ """ """ function potential_ET(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, - VPD="VPD",Ga="Ga_h",approach=c("Priestley-Taylor","Penman-Monteith"), - alpha=1.26,Gs_pot=0.6,missing_G_as_NA=FALSE,missing_S_as_NA=FALSE, + VPD="VPD",Ga="Ga_h",approach=c(Val(:Priestley-Taylor),Val(:Penman-Monteith)), + alpha=1.26,Gs_pot=0.6,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -121,12 +124,12 @@ end Delta = Esat_slope(Tair,Esat_formula,constants)[,"Delta"] - if (approach == "Priestley-Taylor") + if (approach == Val(:Priestley-Taylor)) LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) ET_pot = LE_to_ET(LE_pot,Tair) -else if (approach == "Penman-Monteith") +else if (approach == Val(:Penman-Monteith)) check_input(data,list(Gs_pot,VPD,Ga)) @@ -149,7 +152,7 @@ end #' #' Reference evapotranspiration calculated from the Penman-Monteith #' equation with a prescribed surface conductance. -#' This function is deprecated. Use potential_ET(...,approach="Penman-Monteith") instead. +#' This function is deprecated. Use potential_ET(...,approach=Val(:Penman-Monteith)) instead. #' #' - data Data_frame or matrix containing all required variables; optional #' - Gs_ref Reference surface conductance (m s-1); defaults to 0.0143 m s-1. @@ -164,16 +167,16 @@ end #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air \cr -#' Rd - gas constant of dry air (J kg-1 K-1) (only if `approach = "Penman-Monteith"`) \cr -#' Rgas - universal gas constant (J mol-1 K-1) (only if `approach = "Penman-Monteith"`) \cr -#' Kelvin - conversion degree Celsius to Kelvin (only if `approach = "Penman-Monteith"`) \cr +#' See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air +#' Rd - gas constant of dry air (J kg-1 K-1) (only if `approach = Val(:Penman-Monteith)`) +#' Rgas - universal gas constant (J mol-1 K-1) (only if `approach = Val(:Penman-Monteith)`) +#' Kelvin - conversion degree Celsius to Kelvin (only if `approach = Val(:Penman-Monteith)`) #' #' @export function reference_ET(data,Gs_ref=0.0143,Tair="Tair",pressure="pressure",VPD="VPD",Rn="Rn",Ga="Ga_h", - G=NULL,S=NULL,missing_G_as_NA=FALSE,missing_S_as_NA=FALSE, + G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -201,50 +204,53 @@ end #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air (-) \cr +#' See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air (-) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details Total evapotranspiration can be written in the form (Jarvis & McNaughton 1986): #' -#' \deqn{ET = \Omega ET_eq + (1 - \Omega)ET_imp} +#' ``ET = \\Omega ET_eq + (1 - \\Omega)ET_imp`` #' -#' where \eqn{\Omega} is the decoupling coefficient as calculated from -#' `\link{decoupling`}. `ET_eq` is the equilibrium evapotranspiration rate, +#' where ``\\Omega`` is the decoupling coefficient as calculated from +#' [`decoupling`](@ref). `ET_eq` is the equilibrium evapotranspiration rate, #' the ET rate that would occur under uncoupled conditions, where the heat budget #' is dominated by radiation (when Ga -> 0): #' -#' \deqn{ET_eq = (\Delta * (Rn - G - S) * \lambda) / (\Delta + \gamma)} +#' ``ET_eq = (\\Delta * (Rn - G - S) * \\lambda) / (\\Delta + \\gamma)`` #' -#' where \eqn{\Delta} is the slope of the saturation vapor pressure curve (kPa K-1), -#' \eqn{\lambda} is the latent heat of vaporization (J kg-1), and \eqn{\gamma} +#' where ``\\Delta`` is the slope of the saturation vapor pressure curve (kPa K-1), +#' ``\\lambda`` is the latent heat of vaporization (J kg-1), and ``\\gamma`` #' is the psychrometric constant (kPa K-1). #' `ET_imp` is the imposed evapotranspiration rate, the ET rate #' that would occur under fully coupled conditions (when Ga -> inf): #' -#' \deqn{ET_imp = (\rho * cp * VPD * Gs * \lambda) / \gamma} +#' ``ET_imp = (\\rho * cp * VPD * Gs * \\lambda) / \\gamma`` #' -#' where \eqn{\rho} is the air density (kg m-3). +#' where ``\\rho`` is the air density (kg m-3). #' -#' @note Surface conductance (Gs) can be calculated with `\link{surface_conductance`}. -#' Aerodynamic conductance (Ga) can be calculated using `\link{aerodynamic_conductance`}. +#' #Note +#' Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref). +#' Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). #' #' # Value A DataFrame with the following columns: -#' \item{ET_eq}{Equilibrium ET (kg m-2 s-1)} -#' \item{ET_imp}{Imposed ET (kg m-2 s-1)} -#' \item{LE_eq}{Equilibrium LE (W m-2)} -#' \item{LE_imp}{Imposed LE (W m-2)} +#' - ET_eq: Equilibrium ET (kg m-2 s-1) +#' - ET_imp: Imposed ET (kg m-2 s-1) +#' - LE_eq: Equilibrium LE (W m-2) +#' - LE_imp: Imposed LE (W m-2) #' -#' @references Jarvis, P_G., McNaughton, K_G., 1986: Stomatal control of transpiration: +#' #References +#' Jarvis, P_G., McNaughton, K_G., 1986: Stomatal control of transpiration: #' scaling up from leaf to region. Advances in Ecological Research 15, 1-49. #' #' Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' -#' @seealso `\link{decoupling`} +#' #See also +#' [`decoupling`](@ref) #' #' ```@example; output = false #' ``` @@ -255,7 +261,7 @@ end """ """ function equilibrium_imposed_ET(data,Tair="Tair",pressure="pressure",VPD="VPD",Gs="Gs_ms", - Rn="Rn",G=NULL,S=NULL,missing_G_as_NA=FALSE,missing_S_as_NA=FALSE, + Rn="Rn",G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index a729ac9..bba40e9 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -10,8 +10,8 @@ #' - data Data_frame or matrix containing all required input variables in #' half-hourly or hourly resolution. Including year, month, day information #' - quality_control Should quality control be applied? Defaults to `TRUE`. -#' - filter_growseas Should data be filtered for growing season? Defaults to `FALSE`. -#' - filter_precip Should precipitation filtering be applied? Defaults to `FALSE`. +#' - filter_growseas Should data be filtered for growing season? Defaults to `false`. +#' - filter_precip Should precipitation filtering be applied? Defaults to `false`. #' - filter_vars Additional variables to be filtered. Vector of type character. #' - filter_vals_min Minimum values of the variables to be filtered. Numeric vector of #' the same length than `filter_vars`. Set to `NA` to be ignored. @@ -19,33 +19,33 @@ #' the same length than `filter_vars`. Set to `NA` to be ignored. #' - NA_as_invalid If `TRUE` (the default) missing data are filtered out (applies to all variables). #' - vars_qc Character vector indicating the variables for which quality filter should -#' be applied. Ignored if `quality_control = FALSE`. +#' be applied. Ignored if `quality_control = false`. #' - quality_ext The extension to the variables' names that marks them as -#' quality control variables. Ignored if `quality_control = FALSE`. +#' quality control variables. Ignored if `quality_control = false`. #' - good_quality Which values indicate good quality (i.e. not to be filtered) -#' in the quality control (qc) variables? Ignored if `quality_control = FALSE`. +#' in the quality control (qc) variables? Ignored if `quality_control = false`. #' - missing_qc_as_bad If quality control variable is `NA`, should the corresponding data point be -#' treated as bad quality? Defaults to `TRUE`. Ignored if `quality_control = FALSE`. +#' treated as bad quality? Defaults to `TRUE`. Ignored if `quality_control = false`. #' - precip Precipitation (mm time-1) -#' - GPP Gross primary productivity (umol m-2 s-1); Ignored if `filter_growseas = FALSE`. -#' - doy Day of year; Ignored if `filter_growseas = FALSE`. -#' - year Year; Ignored if `filter_growseas = FALSE`. +#' - GPP Gross primary productivity (umol m-2 s-1); Ignored if `filter_growseas = false`. +#' - doy Day of year; Ignored if `filter_growseas = false`. +#' - year Year; Ignored if `filter_growseas = false`. #' - tGPP GPP threshold (fraction of 95th percentile of the GPP time series). -#' Must be between 0 and 1. Ignored if `filter_growseas` is `FALSE`. +#' Must be between 0 and 1. Ignored if `filter_growseas` is `false`. #' - ws Window size used for GPP time series smoothing. -#' Ignored if `filter_growseas = FALSE`. +#' Ignored if `filter_growseas = false`. #' - min_int Minimum time interval in days for a given state of growing season. -#' Ignored if `filter_growseas = FALSE`. +#' Ignored if `filter_growseas = false`. #' - tprecip Precipitation threshold used to identify a precipitation event (mm). -#' Ignored if `filter_precip = FALSE`. +#' Ignored if `filter_precip = false`. #' - precip_hours Number of hours removed following a precipitation event (h). -#' Ignored if `filter_precip = FALSE`. +#' Ignored if `filter_precip = false`. #' - records_per_hour Number of observations per hour. I_e. 2 for half-hourly data. #' - filtered_data_to_NA Logical. If `TRUE` (the default), all variables in the input #' DataFrame/matrix are set to `NA` for the time step where ANY of the #' `filter_vars` were beyond their acceptable range (as #' determined by `filter_vals_min` and `filter_vals_max`). -#' If `FALSE`, values are not filtered, and an additional column 'valid' +#' If `false`, values are not filtered, and an additional column 'valid' #' is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). #' - constants frac2percent - conversion between fraction and percent @@ -71,11 +71,12 @@ #' # Value If `filtered_data_to_NA = TRUE` (default), the input DataFrame/matrix with #' observations which did not fulfill the filter criteria set to `NA`. -#' If `filtered_data_to_NA = FALSE`, the input DataFrame/matrix with an additional +#' If `filtered_data_to_NA = false`, the input DataFrame/matrix with an additional #' column "valid", which indicates whether all the data of a time step fulfill the #' filtering criteria (1) or not (0). #' -#' @note The thresholds set with `filter_vals_min` and `filter_vals_max` filter all data +#' #Note +#' The thresholds set with `filter_vals_min` and `filter_vals_max` filter all data #' that are smaller than ("<"), or greater than (">") the specified thresholds. That means #' if a variable has exactly the same value as the threshold, it will not be filtered. Likewise, #' `tprecip` filters all data that are greater than `tprecip`. @@ -83,7 +84,7 @@ #' Variables considered of bad quality (as specified by the corresponding quality control variables) #' will be set to `NA` by this routine. Data that do not fulfill the filtering criteria are set to #' `NA` if `filtered_data_to_NA = TRUE`. Note that with this option *all* variables of the same -#' time step are set to `NA`. Alternatively, if `filtered_data_to_NA = FALSE` data are not set to `NA`, +#' time step are set to `NA`. Alternatively, if `filtered_data_to_NA = false` data are not set to `NA`, #' and a new column "valid" is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). #' @@ -94,9 +95,9 @@ #' # hence growing season is not filtered. #' # If filtered_data_to_NA=TRUE, all values of a row are set to NA if one filter #' # variable is beyond its bounds. -#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), #' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, @@ -106,10 +107,10 @@ #' tprecip=0.1,precip_hours=24,records_per_hour=2, #' filtered_data_to_NA=TRUE) #' -#' ## same, but with filtered_data_to_NA=FALSE -#' DE_Tha_Jun_2014_3 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' ## same, but with filtered_data_to_NA=false +#' DE_Tha_Jun_2014_3 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), #' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, @@ -117,7 +118,7 @@ #' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2, -#' filtered_data_to_NA=FALSE) +#' filtered_data_to_NA=false) #' #' # note the additional column 'valid' in DE_Tha_Jun_2014_3. #' # To remove time steps marked as filtered out (i.e. 0 values in column 'valid'): @@ -126,8 +127,8 @@ #' #' @importFrom stats aggregate #' @export -function filter_data(data,quality_control=TRUE,filter_growseas=FALSE, - filter_precip=FALSE,filter_vars=NULL, +function filter_data(data,quality_control=TRUE,filter_growseas=false, + filter_precip=false,filter_vars=NULL, filter_vals_min,filter_vals_max,NA_as_invalid=TRUE, vars_qc=NULL,quality_ext="_qc",good_quality=c(0,1), missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", @@ -296,7 +297,7 @@ end #' observed in the year. #' The GPP-threshold is calculated as: #' -#' \deqn{GPP_threshold = quantile(GPPd,0.95)*tGPP} +#' ``GPP_threshold = quantile(GPPd,0.95)*tGPP`` #' #' GPPd time series are smoothed with a moving average to avoid fluctuations #' in the delineation of the growing season. The window size defaults to 15 diff --git a/inst/fromR/meteorological_variables.jl b/inst/fromR/meteorological_variables.jl index c98679b..ef9d59e 100755 --- a/inst/fromR/meteorological_variables.jl +++ b/inst/fromR/meteorological_variables.jl @@ -8,24 +8,25 @@ #' #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) -#' - constants Kelvin - conversion degC to Kelvin \cr -#' Rd - gas constant of dry air (J kg-1 K-1) \cr +#' - constants Kelvin - conversion degC to Kelvin +#' Rd - gas constant of dry air (J kg-1 K-1) #' kPa2Pa - conversion kilopascal (kPa) to pascal (Pa) #' #' # Details - Air density (\eqn{\rho}) is calculated as: + Air density (``\\rho``) is calculated as: #' -#' \deqn{\rho = pressure / (Rd * Tair)} +#' ``\\rho = pressure / (Rd * Tair)`` #' #' # Value - \item{\eqn{\rho}}{air density (kg m-3)} + - ``\\rho``: air density (kg m-3) #' #' ```@example; output = false #' ``` #' # air density at 25degC and standard pressure (101.325kPa) #' air_density(25,101.325) #' -#' @references Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. +#' #References +#' Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. #' """ """ @@ -49,27 +50,29 @@ end #' - elev Elevation a_s_l. (m) #' - Tair Air temperature (deg C) #' - VPD Vapor pressure deficit (kPa); optional -#' - constants Kelvin- conversion degC to Kelvin \cr -#' pressure0 - reference atmospheric pressure at sea level (Pa) \cr -#' Rd - gas constant of dry air (J kg-1 K-1) \cr -#' g - gravitational acceleration (m s-2) \cr +#' - constants Kelvin- conversion degC to Kelvin +#' pressure0 - reference atmospheric pressure at sea level (Pa) +#' Rd - gas constant of dry air (J kg-1 K-1) +#' g - gravitational acceleration (m s-2) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details Atmospheric pressure is approximated by the hypsometric equation: #' -#' \deqn{pressure = pressure_0 / (exp(g * elevation / (Rd Temp)))} +#' ``pressure = pressure_0 / (exp(g * elevation / (Rd Temp)))`` #' -#' @note The hypsometric equation gives an estimate of the standard pressure +#' #Note +#' The hypsometric equation gives an estimate of the standard pressure #' at a given altitude. #' If VPD is provided, humidity correction is applied and the #' virtual temperature instead of air temperature is used. VPD is #' internally converted to specific humidity. #' #' # Value - \item{pressure -}{Atmospheric pressure (kPa)} + - pressure -: Atmospheric pressure (kPa) #' -#' @references Stull B., 1988: An Introduction to Boundary Layer Meteorology. +#' #References +#' Stull B., 1988: An Introduction to Boundary Layer Meteorology. #' Kluwer Academic Publishers, Dordrecht, Netherlands. #' #' ```@example; output = false @@ -115,25 +118,26 @@ end #' # Details Esat (kPa) is calculated using the Magnus equation: #' -#' \deqn{Esat = a * exp((b * Tair) / (c + Tair)) / 1000} +#' ``Esat = a * exp((b * Tair) / (c + Tair)) / 1000`` #' #' where the coefficients a, b, c take different values depending on the formula used. #' The default values are from Sonntag 1990 (a=611.2, b=17.62, c=243.12). This version #' of the Magnus equation is recommended by the WMO (WMO 2008; p1.4-29). Alternatively, #' parameter values determined by Alduchov & Eskridge 1996 or Allen et al. 1998 can be #' used (see references). -#' The slope of the Esat curve (\eqn{\Delta}) is calculated as the first derivative of the function: +#' The slope of the Esat curve (``\\Delta``) is calculated as the first derivative of the function: #' -#' \deqn{\Delta = dEsat / dTair} +#' ``\\Delta = dEsat / dTair`` #' #' which is solved using `\link[stats]{D`}. #' #' # Value A dataframe with the following columns: -#' \item{Esat}{Saturation vapor pressure (kPa)} -#' \item{Delta}{Slope of the saturation vapor pressure curve (kPa K-1)} +#' - Esat: Saturation vapor pressure (kPa) +#' - Delta: Slope of the saturation vapor pressure curve (kPa K-1) #' -#' @references Sonntag D. 1990: Important new values of the physical constants of 1986, vapor +#' #References +#' Sonntag D. 1990: Important new values of the physical constants of 1986, vapor #' pressure formulations based on the ITS-90 and psychrometric formulae. #' Zeitschrift fuer Meteorologie 70, 340-344. #' @@ -194,21 +198,22 @@ end #' #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) #' eps - ratio of the molecular weight of water vapor to dry air (-) #' #' # Details - The psychrometric constant (\eqn{\gamma}) is given as: + The psychrometric constant (``\\gamma``) is given as: #' -#' \deqn{\gamma = cp * pressure / (eps * \lambda)} +#' ``\\gamma = cp * pressure / (eps * \\lambda)`` #' -#' where \eqn{\lambda} is the latent heat of vaporization (J kg-1), -#' as calculated from `\link{latent_heat_vaporization`}. +#' where ``\\lambda`` is the latent heat of vaporization (J kg-1), +#' as calculated from [`latent_heat_vaporization`](@ref). #' #' # Value - \item{\eqn{\gamma} -}{the psychrometric constant (kPa K-1)} + - ``\\gamma`` -: the psychrometric constant (kPa K-1) #' -#' @references Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. +#' #References +#' Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. #' 3rd Edition. Academic Press, London. #' #' ```@example; output = false @@ -236,12 +241,13 @@ end #' # Details The following formula is used: #' -#' \deqn{\lambda = (2.501 - 0.00237*Tair)10^6} +#' ``\\lambda = (2.501 - 0.00237*Tair)10^6`` #' #' # Value - \item{\eqn{\lambda} -}{Latent heat of vaporization (J kg-1)} + - ``\\lambda`` -: Latent heat of vaporization (J kg-1) #' -#' @references Stull, B., 1988: An Introduction to Boundary Layer Meteorology (p.641) +#' #References +#' Stull, B., 1988: An Introduction to Boundary Layer Meteorology (p.641) #' Kluwer Academic Publishers, Dordrecht, Netherlands #' #' Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. @@ -276,10 +282,11 @@ end #' - accuracy Accuracy of the result (degC) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. +#' See [`Esat_slope`](@ref). #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' @note Arguments `accuracy` and `Esat_formula` are passed to this function by wetbulb_temp(). +#' #Note +#' Arguments `accuracy` and `Esat_formula` are passed to this function by wetbulb_temp(). #' #' @importFrom stats optimize #' @@ -303,24 +310,25 @@ end #' - accuracy Accuracy of the result (deg C) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air (-) \cr +#' See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air (-) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details Wet-bulb temperature (Tw) is calculated from the following expression: #' -#' \deqn{e = Esat(Tw) - gamma* (Tair - Tw)} +#' ``e = Esat(Tw) - gamma* (Tair - Tw)`` #' #' The equation is solved for Tw using `\link[stats]{optimize`}. -#' Actual vapor pressure e (kPa) is calculated from VPD using the function `\link{VPD_to_e`}. -#' The psychrometric constant gamma (kPa K-1) is calculated from `\link{psychrometric_constant`}. +#' Actual vapor pressure e (kPa) is calculated from VPD using the function [`VPD_to_e`](@ref). +#' The psychrometric constant gamma (kPa K-1) is calculated from [`psychrometric_constant`](@ref). #' #' # Value - \item{Tw -}{wet-bulb temperature (degC)} + - Tw -: wet-bulb temperature (degC) #' -#' @references Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. +#' #References +#' Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' #' ```@example; output = false @@ -375,10 +383,11 @@ end #' - accuracy Accuracy of the result (degC) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. +#' See [`Esat_slope`](@ref). #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' @note Arguments `accuracy` and `Esat_formula` are passed to this function by dew_point(). +#' #Note +#' Arguments `accuracy` and `Esat_formula` are passed to this function by dew_point(). #' #' @importFrom stats optimize #' @@ -402,21 +411,22 @@ end #' - accuracy Accuracy of the result (deg C) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. +#' See [`Esat_slope`](@ref). #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details Dew point temperature (Td) is defined by: #' -#' \deqn{e = Esat(Td)} +#' ``e = Esat(Td)`` #' #' where e is vapor pressure of the air and Esat is the vapor pressure deficit. #' This equation is solved for Td using `\link[stats]{optimize`}. #' #' # Value - \item{Td -}{dew point temperature (degC)} + - Td -: dew point temperature (degC) #' -#' @references Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. +#' #References +#' Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' #' ```@example; output = false @@ -464,22 +474,23 @@ end #' - VPD Vapor pressure deficit (kPa) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr +#' See [`Esat_slope`](@ref). +#' - constants Kelvin - conversion degree Celsius to Kelvin #' eps - ratio of the molecular weight of water vapor to dry air (-) #' #' # Details the virtual temperature is given by: #' -#' \deqn{Tv = Tair / (1 - (1 - eps) e/pressure)} +#' ``Tv = Tair / (1 - (1 - eps) e/pressure)`` #' #' where Tair is in Kelvin (converted internally). Likewise, VPD is converted -#' to actual vapor pressure (e in kPa) with `\link{VPD_to_e`} internally. +#' to actual vapor pressure (e in kPa) with [`VPD_to_e`](@ref) internally. #' #' # Value - \item{Tv -}{virtual temperature (deg C)} + - Tv -: virtual temperature (deg C) #' -#' @references Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. +#' #References +#' Monteith J_L., Unsworth M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' #' ```@example; output = false @@ -508,21 +519,22 @@ end #' #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' pressure0 - reference atmospheric pressure at sea level (Pa) \cr -#' Tair0 - reference air temperature (K) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' pressure0 - reference atmospheric pressure at sea level (Pa) +#' Tair0 - reference air temperature (K) #' kPa2Pa - conversion kilopascal (kPa) to pascal (Pa) #' #' # Details where v is the kinematic viscosity of the air (m2 s-1), #' given by (Massman 1999b): #' -#' \deqn{v = 1.327 * 10^-5(pressure0/pressure)(Tair/Tair0)^1.81} +#' ``v = 1.327 * 10^-5(pressure0/pressure)(Tair/Tair0)^1.81`` #' #' # Value - \item{v -}{kinematic viscosity of air (m2 s-1)} + - v -: kinematic viscosity of air (m2 s-1) #' -#' @references Massman, W_J., 1999b: Molecular diffusivities of Hg vapor in air, +#' #References +#' Massman, W_J., 1999b: Molecular diffusivities of Hg vapor in air, #' O2 and N2 near STP and the kinematic viscosity and thermal diffusivity #' of air near STP. Atmospheric Environment 33, 453-457. #' diff --git a/inst/fromR/potential_radiation.jl b/inst/fromR/potential_radiation.jl index 7d6243f..d5e59ff 100755 --- a/inst/fromR/potential_radiation.jl +++ b/inst/fromR/potential_radiation.jl @@ -49,7 +49,7 @@ end #' - timezone Time zone (hours) #' - useSolartime by default corrects hour (given in local winter time) #' for latitude to solar time (where noon is exactly at 12:00). -#' Set this to `FALSE` to directly use local winter time. +#' Set this to `false` to directly use local winter time. #' #' # Value vector of potential radiation (W m-2) @@ -60,7 +60,7 @@ end #' potRadApparentLocal = potential_radiation( #' 160, hour, 39.94, -5.77, timezone = +1) #' potRadTimezone = potential_radiation( -#' 160, hour, 39.94, -5.77, timezone = +1, useSolartime = FALSE) +#' 160, hour, 39.94, -5.77, timezone = +1, useSolartime = false) #' plot(potRadApparentLocal ~ hour, type = 'l' #' , ylab = 'potential radiation (W m-2)') #' lines(potRadTimezone ~ hour, col = "blue") diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 6475c0f..0a68405 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -11,28 +11,31 @@ #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) #' - H Sensible heat flux (W m-2) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' k - von Karman constant (-) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' k - von Karman constant (-) #' g - gravitational acceleration (m s-2) #' #' # Details The Monin-Obukhov length (L) is given by: #' -#' \deqn{L = - (\rho * cp * ustar^3 * Tair) / (k * g * H)} +#' ``L = - (\\rho * cp * ustar^3 * Tair) / (k * g * H)`` #' -#' where \eqn{rho} is air density (kg m-3). +#' where ``rho`` is air density (kg m-3). #' #' # Value - \item{L -}{Monin-Obukhov length (m)} + - L -: Monin-Obukhov length (m) #' -#' @note Note that L gets very small for very low ustar values with implications +#' #Note +#' Note that L gets very small for very low ustar values with implications #' for subsequent functions using L as input. It is recommended to filter #' data and exclude low ustar values (ustar < ~0.2) beforehand. #' -#' @references Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. +#' #References +#' Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. #' -#' @seealso `\link{stability_parameter`} +#' #See also +#' [`stability_parameter`](@ref) #' #' ```@example; output = false #' ``` @@ -66,22 +69,22 @@ end #' - H Sensible heat flux (W m-2) #' - zr Instrument (reference) height (m) #' - d Zero-plane displacement height (m) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' k - von Karman constant (-) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' k - von Karman constant (-) #' g - gravitational acceleration (m s-2) #' #' # Details - The stability parameter \eqn{\zeta} is given by: + The stability parameter ``\zeta`` is given by: #' -#' \deqn{\zeta = (zr - d) / L} +#' ``\zeta = (zr - d) / L`` #' #' where L is the Monin-Obukhov length (m), calculated from the function -#' `\link{Monin_Obukhov_length`}. The displacement height d can -#' be estimated from the function `\link{roughness_parameters`}. +#' [`Monin_Obukhov_length`](@ref). The displacement height d can +#' be estimated from the function [`roughness_parameters`](@ref). #' #' # Value - \item{\eqn{\zeta} - }{stability parameter (-)} + - ``\zeta`` - : stability parameter (-) #' #' ```@example; output = false #' ``` @@ -114,26 +117,27 @@ end #' #' # Details The functions give the integrated form of the universal functions. They -#' depend on the value of the stability parameter \eqn{\zeta}, -#' which can be calculated from the function `\link{stability_parameter`}. +#' depend on the value of the stability parameter ``\zeta``, +#' which can be calculated from the function [`stability_parameter`](@ref). #' The integration of the universal functions is: #' -#' \deqn{\psi = -x * zeta} +#' ``\psi = -x * zeta`` #' -#' for stable atmospheric conditions (\eqn{\zeta} >= 0), and +#' for stable atmospheric conditions (``\zeta`` >= 0), and #' -#' \deqn{\psi = 2 * log( (1 + y) / 2) } +#' ``\psi = 2 * log( (1 + y) / 2) `` #' -#' for unstable atmospheric conditions (\eqn{\zeta} < 0). +#' for unstable atmospheric conditions (``\zeta`` < 0). #' #' The different formulations differ in their value of x and y. #' #' # Value a DataFrame with the following columns: -#' \item{psi_h}{the value of the stability function for heat and water vapor (-)} -#' \item{psi_m}{the value of the stability function for momentum (-)} +#' - psi_h: the value of the stability function for heat and water vapor (-) +#' - psi_m: the value of the stability function for momentum (-) #' -#' @references Dyer, A_J., 1974: A review of flux-profile relationships. +#' #References +#' Dyer, A_J., 1974: A review of flux-profile relationships. #' Boundary-Layer Meteorology 7, 363-372. #' #' Dyer, A. J., Hicks, B_B., 1970: Flux-Gradient relationships in the diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index e3289ea..853c281 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -15,15 +15,15 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance for heat/water vapor (m s-1) -#' - calc_surface_CO2 Calculate surface CO2 concentration? Defaults to `FALSE`. +#' - calc_surface_CO2 Calculate surface CO2 concentration? Defaults to `false`. #' - Ca Atmospheric CO2 concentration (mol mol-1). Required if `calc_surface_CO2 = TRUE`. #' - NEE Net ecosystem exchange (umol m-2 s-1). Required if `calc_surface_CO2 = TRUE`. #' - Ga_CO2 Aerodynamic conductance for CO2 (m s-1). Required if `calc_surface_CO2 = TRUE`. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air (-) \cr +#' See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air (-) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' # Details @@ -32,44 +32,45 @@ #' the surface of the big-leaf (i.e. at height d + z0h; the apparent sink of sensible heat and water vapor). #' Aerodynamic canopy surface temperature is given by: #' -#' \deqn{Tsurf = Tair + H / (\rho * cp * Ga)} +#' ``Tsurf = Tair + H / (\\rho * cp * Ga)`` #' -#' where \eqn{\rho} is air density (kg m-3). +#' where ``\\rho`` is air density (kg m-3). #' Vapor pressure at the canopy surface is: #' -#' \deqn{esurf = e + (LE * \gamma)/(Ga * \rho * cp)} +#' ``esurf = e + (LE * \\gamma)/(Ga * \\rho * cp)`` #' -#' where \eqn{\gamma} is the psychrometric constant (kPa K-1). +#' where ``\\gamma`` is the psychrometric constant (kPa K-1). #' Vapor pressure deficit (VPD) at the canopy surface is calculated as: #' -#' \deqn{VPD_surf = Esat_surf - esurf} +#' ``VPD_surf = Esat_surf - esurf`` #' #' CO2 concentration at the canopy surface is given by: #' -#' \deqn{Ca_surf = Ca + NEE / Ga_CO2} +#' ``Ca_surf = Ca + NEE / Ga_CO2`` #' #' Note that Ga is assumed to be equal for water vapor and sensible heat. #' Ga is further assumed to be the inverse of the sum of the turbulent part #' and the canopy boundary layer conductance (1/Ga = 1/Ga_m + 1/Gb; -#' see `\link{aerodynamic_conductance`}). Ga_CO2, the aerodynamic conductance -#' for CO2 is also calculated by `\link{aerodynamic_conductance`}. +#' see [`aerodynamic_conductance`](@ref)). Ga_CO2, the aerodynamic conductance +#' for CO2 is also calculated by [`aerodynamic_conductance`](@ref). #' If Ga is replaced by Ga_m (i.e. only the turbulent conductance part), #' the results of the functions represent conditions outside the canopy #' boundary layer, i.e. in the canopy airspace. #' -#' @note The following sign convention for NEE is employed (relevant if +#' #Note +#' The following sign convention for NEE is employed (relevant if #' `calc_surface_CO2 = TRUE`): #' negative values of NEE denote net CO2 uptake by the ecosystem. #' #' # Value a DataFrame with the following columns: -#' \item{Tsurf}{Surface temperature (deg C)} \cr -#' \item{esat_surf}{Saturation vapor pressure at the surface (kPa)} \cr -#' \item{esurf}{vapor pressure at the surface (kPa)} \cr -#' \item{VPD_surf}{vapor pressure deficit at the surface (kPa)} \cr -#' \item{qsurf}{specific humidity at the surface (kg kg-1)} \cr -#' \item{rH_surf}{relative humidity at the surface (-)} \cr -#' \item{Ca_surf}{CO2 concentration at the surface (umol mol-1)} +#' - Tsurf: Surface temperature (deg C) +#' - esat_surf: Saturation vapor pressure at the surface (kPa) +#' - esurf: vapor pressure at the surface (kPa) +#' - VPD_surf: vapor pressure deficit at the surface (kPa) +#' - qsurf: specific humidity at the surface (kg kg-1) +#' - rH_surf: relative humidity at the surface (-) +#' - Ca_surf: CO2 concentration at the surface (umol mol-1) #' #' ```@example; output = false #' ``` @@ -82,7 +83,8 @@ #' surface_conditions(Tair=25,pressure=100,LE=100,H=200,VPD=1.2,Ga=c(0.02,0.05,0.1), #' Ca=400,Ga_CO2=c(0.02,0.05,0.1),NEE=-20,calc_surface_CO2=TRUE) #' -#' @references Knauer, J. et al., 2018: Towards physiologically meaningful water-use efficiency estimates +#' #References +#' Knauer, J. et al., 2018: Towards physiologically meaningful water-use efficiency estimates #' from eddy covariance data. Global Change Biology 24, 694-710. #' #' Blanken, P_D. & Black, T_A., 2004: The canopy conductance of a boreal aspen forest, @@ -93,7 +95,7 @@ #' #' @export function surface_conditions(data,Tair="Tair",pressure="pressure",LE="LE",H="H", - VPD="VPD",Ga="Ga_h",calc_surface_CO2=FALSE,Ca="Ca",Ga_CO2="Ga_CO2", + VPD="VPD",Ga="Ga_h",calc_surface_CO2=false,Ca="Ca",Ga_CO2="Ga_CO2", NEE="NEE",Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -141,17 +143,18 @@ end #' # Details CO2 concentration at the canopy surface is calculated as: #' -#' \deqn{Ca_surf = Ca + NEE / Ga_CO2} +#' ``Ca_surf = Ca + NEE / Ga_CO2`` #' #' Note that this equation can be used for any gas measured (with NEE #' replaced by the net exchange of the respective gas and Ga_CO2 by the Ga of #' that gas). #' -#' @note the following sign convention is employed: negative values of NEE denote +#' #Note +#' the following sign convention is employed: negative values of NEE denote #' net CO2 uptake by the ecosystem. #' #' # Value - \item{Ca_surf -}{CO2 concentration at the canopy surface (umol mol-1)} + - Ca_surf -: CO2 concentration at the canopy surface (umol mol-1) #' #' ```@example; output = false #' ``` @@ -180,18 +183,18 @@ end #' - LW_up Longwave upward radiation (W m-2) #' - LW_down Longwave downward radiation (W m-2) #' - emissivity Emissivity of the surface (-) -#' - constants sigma - Stefan-Boltzmann constant (W m-2 K-4) \cr +#' - constants sigma - Stefan-Boltzmann constant (W m-2 K-4) #' Kelvin - conversion degree Celsius to Kelvin #' #' # Details Radiometric surface temperature (Trad) is calculated as: #' -#' \deqn{Trad = ((LW_up - (1 - \epsilon)*LW_down) / (\sigma \epsilon))^(1/4)} +#' ``Trad = ((LW_up - (1 - \epsilon)*LW_down) / (\sigma \epsilon))^(1/4)`` #' #' # Value a DataFrame with the following columns: -#' \item{Trad_K}{Radiometric surface temperature (K)} \cr -#' \item{Trad_degC}{Radiometric surface temperature (degC)} +#' - Trad_K: Radiometric surface temperature (K) +#' - Trad_degC: Radiometric surface temperature (degC) #' #' ```@example; output = false #' ``` @@ -202,7 +205,8 @@ end #' Trad = radiometric_surface_temp(DE_Tha_Jun_2014,emissivity=0.98) #' summary(Trad) #' -#' @references Wang, W., Liang, S., Meyers, T. 2008: Validating MODIS land surface +#' #References +#' Wang, W., Liang, S., Meyers, T. 2008: Validating MODIS land surface #' temperature products using long-term nighttime ground measurements. #' Remote Sensing of Environment 112, 623-635. #' diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 783fc3a..180eb77 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -17,32 +17,32 @@ #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) #' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise they are set to 0. -#' Only used if `formulation = "Penman-Monteith"`. +#' Only used if `formulation = Val(:Penman-Monteith)`. #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise they are set to 0. -#' Only used if `formulation = "Penman-Monteith"`. -#' - formulation Formulation used. Either `"Penman-Monteith"` (the default) +#' Only used if `formulation = Val(:Penman-Monteith)`. +#' - formulation Formulation used. Either `Val(:Penman-Monteith)` (the default) #' using the inverted Penman-Monteith equation, or `"Flux-Gradient"`, #' for a simple flux-gradient approach requiring ET, pressure, and VPD only. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' Only used if `formulation = "Penman-Monteith"`. See `\link{Esat_slope`}. -#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' eps - ratio of the molecular weight of water vapor to dry air (-) \cr -#' Rd - gas constant of dry air (J kg-1 K-1) \cr -#' Rgas - universal gas constant (J mol-1 K-1) \cr -#' Kelvin - conversion degree Celsius to Kelvin \cr -#' Mw - molar mass of water vapor (kg mol-1) \cr +#' Only used if `formulation = Val(:Penman-Monteith)`. See [`Esat_slope`](@ref). +#' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) +#' eps - ratio of the molecular weight of water vapor to dry air (-) +#' Rd - gas constant of dry air (J kg-1 K-1) +#' Rgas - universal gas constant (J mol-1 K-1) +#' Kelvin - conversion degree Celsius to Kelvin +#' Mw - molar mass of water vapor (kg mol-1) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' #' # Details - If `formulation = "Penman-Monteith"` (the default), surface conductance (Gs) in m s-1 + If `formulation = Val(:Penman-Monteith)` (the default), surface conductance (Gs) in m s-1 #' is calculated from the inverted Penman-Monteith equation: #' -#' \deqn{Gs = ( LE * Ga * \gamma ) / ( \Delta * A + \rho * cp * Ga * VPD - LE * ( \Delta + \gamma ) )} +#' ``Gs = ( LE * Ga * \\gamma ) / ( \\Delta * A + \\rho * cp * Ga * VPD - LE * ( \\Delta + \\gamma ) )`` #' -#' Where \eqn{\gamma} is the psychrometric constant (kPa K-1), \eqn{\Delta} is the slope of the -#' saturation vapor pressure curve (kPa K-1), and \eqn{\rho} is air density (kg m-3). +#' Where ``\\gamma`` is the psychrometric constant (kPa K-1), ``\\Delta`` is the slope of the +#' saturation vapor pressure curve (kPa K-1), and ``\\rho`` is air density (kg m-3). #' Available energy (A) is defined as A = Rn - G - S. If G and/or S are not provided, A = Rn. #' #' By default, any missing data in G and S are set to 0. If `missing_S_as_NA = TRUE` @@ -50,30 +50,30 @@ #' #' If `formulation="Flux-Gradient"`, Gs (in mol m-2 s-1) is calculated from VPD and ET only: #' -#' \deqn{Gs = ET/pressure * VPD} +#' ``Gs = ET/pressure * VPD`` #' #' where ET is in mol m-2 s-1. Note that this formulation assumes fully coupled conditions (i.e. Ga = inf). #' This formulation is equivalent to the inverted form of Eq.6 in McNaughton & Black 1973: #' -#' \deqn{Gs = LE * \gamma / (\rho * cp * VPD)} +#' ``Gs = LE * \\gamma / (\\rho * cp * VPD)`` #' #' which gives Gs in m s-1. Note that Gs > Gc (canopy conductance) under conditions #' when a significant fraction of ET comes from interception or soil evaporation. #' #' If `pressure` is not available, it can be approximated by elevation using the -#' function `\link{pressure_from_elevation`} +#' function [`pressure_from_elevation`](@ref) #' #' # Value a dataframe with the following columns: -#' \item{Gs_ms}{Surface conductance in m s-1} -#' \item{Gs_mol}{Surface conductance in mol m-2 s-1} +#' - Gs_ms: Surface conductance in m s-1 +#' - Gs_mol: Surface conductance in mol m-2 s-1 #' #' ```@example; output = false #' ``` #' ## filter data to ensure that Gs is a meaningful proxy to canopy conductance (Gc) -#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=FALSE, +#' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=FALSE,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=TRUE, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), #' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, @@ -95,7 +95,7 @@ #' # Note that Ga is not added to the DataFrame 'DE_Tha_Jun_2014' #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation="Penman-Monteith") +#' formulation=Val(:Penman-Monteith)) #' summary(Gs_PM) #' #' @@ -103,11 +103,12 @@ #' DE_Tha_Jun_2014_2$Ga = Ga #' Gs_PM2 = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga="Ga", -#' formulation="Penman-Monteith") +#' formulation=Val(:Penman-Monteith)) #' # note the difference to the previous version (Ga="Ga") #' summary(Gs_PM2) #' -#' @references Monteith, J., 1965: Evaporation and environment. In Fogg, G. E. (Ed.), +#' #References +#' Monteith, J., 1965: Evaporation and environment. In Fogg, G. E. (Ed.), #' The state and movement of water in living organisms (pp.205-234). #' 19th Symp. Soc. Exp. Biol., Cambridge University Press, Cambridge #' @@ -118,8 +119,8 @@ """ """ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, - VPD="VPD",LE="LE",Ga="Ga_h",missing_G_as_NA=FALSE,missing_S_as_NA=FALSE, - formulation=c("Penman-Monteith","Flux-Gradient"), + VPD="VPD",LE="LE",Ga="Ga_h",missing_G_as_NA=false,missing_S_as_NA=false, + formulation=c(Val(:Penman-Monteith),"Flux-Gradient"), Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -132,7 +133,7 @@ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL Gs_mol = (LE_to_ET(LE,Tair)/constants[:Mw]) * pressure / VPD Gs_ms = mol_to_ms(Gs_mol,Tair,pressure) -else if (formulation == "Penman-Monteith") +else if (formulation == Val(:Penman-Monteith)) check_input(data,list(Tair,pressure,VPD,LE,Rn,Ga,G,S)) diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index 030ffe5..c6a2d34 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -10,21 +10,22 @@ #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) #' - z0m Roughness length (m) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' pressure0 - reference atmospheric pressure at sea level (Pa) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' pressure0 - reference atmospheric pressure at sea level (Pa) #' Tair0 - reference air temperature (K) #' #' # Details The Roughness Reynolds Number is calculated as in Massman 1999a: #' -#' \deqn{Re = z0m * ustar / v} +#' ``Re = z0m * ustar / v`` #' #' where `v` is the kinematic viscosity (m2 s-1). #' #' # Value - \item{Re -}{Roughness Reynolds Number (-)} + - Re -: Roughness Reynolds Number (-) #' -#' @references Massman, W_J., 1999a: A model study of kB H- 1 for vegetated surfaces using +#' #References +#' Massman, W_J., 1999a: A model study of kB H- 1 for vegetated surfaces using #' 'localized near-field' Lagrangian theory. Journal of Hydrology 223, 27-43. #' #' ```@example; output = false @@ -48,7 +49,7 @@ end #' A simple approximation of the two roughness parameters displacement height (d) #' and roughness length for momentum (z0m). #' -#' - method Method to use, one of `"canopy_height","canopy_height&LAI","wind_profile"` \cr +#' - method Method to use, one of `"canopy_height","canopy_height&LAI","wind_profile"` #' NOTE: if `method = "canopy_height"`, only the following three arguments #' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, #' and `hs` are required. @@ -72,10 +73,10 @@ end #' - stab_roughness Should stability correction be considered? Default is `TRUE`. #' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). #' Either `"Dyer_1970"` or `"Businger_1971"`. -#' - constants k - von-Karman constant (-) \cr -#' Kelvin - conversion degree Celsius to Kelvin \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr -#' g - gravitational acceleration (m s-2) \cr +#' - constants k - von-Karman constant (-) +#' Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' g - gravitational acceleration (m s-2) #' se_median - conversion standard error (SE) of the mean to SE of the median #' #' @@ -85,9 +86,9 @@ end #' empirical relationships with canopy height (zh). If `method = "canopy_height"`, #' the following formulas are used: #' -#' \deqn{d = frac_d * zh} +#' ``d = frac_d * zh`` #' -#' \deqn{z0m = frac_z0m * zh} +#' ``z0m = frac_z0m * zh`` #' #' where frac_d defaults to 0.7 and frac_z0m to 0.1. #' @@ -96,35 +97,37 @@ end #' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed #' the following semi-empirical relations: #' -#' \deqn{X = cd * LAI} +#' ``X = cd * LAI`` #' -#' \deqn{d = 1.1 * zh * ln(1 + X^(1/4))} +#' ``d = 1.1 * zh * ln(1 + X^(1/4))`` #' -#' \deqn{z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2} +#' ``z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2`` #' -#' \deqn{z0m = hs * zh * (1 - d/zh) for 0.2 < X} +#' ``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` #' #' If `method = "wind_profile"`, z0m is estimated by solving #' the wind speed profile for z0m: #' -#' \deqn{z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)} +#' ``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` #' #' By default, d in this equation is fixed to 0.7*zh, but can be set to any -#' other value. psi_m is 0 if `stab_roughness = FALSE`. +#' other value. psi_m is 0 if `stab_roughness = false`. #' #' # Value a DataFrame with the following columns: -#' \item{d}{Zero-plane displacement height (m)} -#' \item{z0m}{Roughness length for momentum (m)} -#' \item{z0m_se}{Only if `method = wind_profile`: Standard Error of the median for z0m (m)} +#' - d: Zero-plane displacement height (m) +#' - z0m: Roughness length for momentum (m) +#' - z0m_se: Only if `method = wind_profile`: Standard Error of the median for z0m (m) #' -#' @references Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat +#' #References +#' Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat #' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. #' #' Shaw, R. H., Pereira, A., 1982: Aerodynamic roughness of a plant canopy: #' a numerical experiment. Agricultural Meteorology, 26, 51-65. #' -#' @seealso `\link{wind_profile`} +#' #See also +#' [`wind_profile`](@ref) #' #' ```@example; output = false #' ``` @@ -221,18 +224,18 @@ end #' - d Zero-plane displacement height (-) #' - frac_d Fraction of displacement height on canopy height (-); #' only used if `d` is not available -#' - z0m Roughness length (m), optional; only used if `stab_correction = FALSE` (default=0.1) +#' - z0m Roughness length (m), optional; only used if `stab_correction = false` (default=0.1) #' - frac_z0m Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. #' Default is 0.1. #' - estimate_z0m Should `z0m` be estimated from the logarithmic wind profile? If `TRUE` (the default), #' arguments `z0m` and `frac_z0m` are ignored. -#' See `\link{roughness_parameters`} for details. +#' See [`roughness_parameters`](@ref) for details. #' - stab_correction Should stability correction be applied? Defaults to `TRUE` #' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). #' Either `"Dyer_1970"` or `"Businger_1971"`. -#' - constants k - von-Karman constant (-) \cr -#' Kelvin - conversion degree Celsius to Kelvin \cr -#' cp - specific heat of air for constant pressure (J K-1 kg-1) \cr +#' - constants k - von-Karman constant (-) +#' Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) #' g - gravitational acceleration (m s-2) #' #' # Details @@ -241,28 +244,31 @@ end #' according to the Monin-Obhukov similarity theory). #' In this case, the wind speed at a given height z is given by: #' -#' \deqn{u(z) = (ustar/k) * (ln((z - d) / z0m) - \psi{m}} +#' ``u(z) = (ustar/k) * (ln((z - d) / z0m) - \psi{m``} #' #' The roughness parameters zero-plane displacement height (d) and roughness length (z0m) -#' can be approximated from `\link{roughness_parameters`}. \eqn{\psi{m}} is omitted -#' if `stab_correction = FALSE` (not recommended). If `estimate_z0m = TRUE`, +#' can be approximated from [`roughness_parameters`](@ref). ``\psi{m``} is omitted +#' if `stab_correction = false` (not recommended). If `estimate_z0m = TRUE`, #' z0m is first estimated from the wind profile equation and then used in the equation #' above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). #' -#' @note Note that this equation is only valid for z >= d + z0m, and it is not +#' #Note +#' Note that this equation is only valid for z >= d + z0m, and it is not #' meaningful to calculate values closely above d + z0m. All values in `heights` #' smaller than d + z0m will return 0. #' #' # Value A vector of wind speed at heights `z`. #' -#' @references Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. +#' #References +#' Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. #' 3rd edition. Academic Press, London. #' #' Newman, J_F., Klein, P_M., 2014: The impacts of atmospheric stability on #' the accuracy of wind speed extrapolation methods. Resources 3, 81-105. #' -#' @seealso `\link{roughness_parameters`} +#' #See also +#' [`roughness_parameters`](@ref) #' #' ```@example; output = false #' ``` @@ -294,7 +300,7 @@ end if (is_null(z0m) & !estimate_z0m) if (is_null(frac_z0m)) - stop("Either 'z0m' or 'frac_z0m' must be specified if 'estimate_z0m' = FALSE") + stop("Either 'z0m' or 'frac_z0m' must be specified if 'estimate_z0m' = false") end z0m = frac_z0m * zh end diff --git a/inst/fromR/unit_conversions.jl b/inst/fromR/unit_conversions.jl index 66d1273..2349213 100755 --- a/inst/fromR/unit_conversions.jl +++ b/inst/fromR/unit_conversions.jl @@ -17,12 +17,12 @@ #' The conversions are given by: #' -#' \deqn{ET = LE/\lambda} +#' ``ET = LE/\\lambda`` #' -#' \deqn{LE = \lambda ET} +#' ``LE = \\lambda ET`` #' -#' where \eqn{\lambda} is the latent heat of vaporization (J kg-1) as calculated by -#' `\link{latent_heat_vaporization`}. +#' where ``\\lambda`` is the latent heat of vaporization (J kg-1) as calculated by +#' [`latent_heat_vaporization`](@ref). #' #' ```@example; output = false #' ``` @@ -64,21 +64,22 @@ end #' - G_mol Conductance (mol m-2 s-1) #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) -#' - constants Kelvin - conversion degree Celsius to Kelvin \cr -#' Rgas - universal gas constant (J mol-1 K-1) \cr +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' Rgas - universal gas constant (J mol-1 K-1) #' kPa2Pa - conversion kilopascal (kPa) to pascal (Pa) #' #' # Details #' The conversions are given by: #' -#' \deqn{G_mol = G_ms * pressure / (Rgas * Tair)} +#' ``G_mol = G_ms * pressure / (Rgas * Tair)`` #' -#' \deqn{G_ms = G_mol * (Rgas * Tair) / pressure} +#' ``G_ms = G_mol * (Rgas * Tair) / pressure`` #' #' where Tair is in Kelvin and pressure in Pa (converted from kPa internally) #' -#' @references Jones, H_G. 1992. Plants and microclimate: a quantitative approach to environmental plant physiology. +#' #References +#' Jones, H_G. 1992. Plants and microclimate: a quantitative approach to environmental plant physiology. #' 2nd Edition., Cambridge University Press, Cambridge. 428 p #' #' ```@example; output = false @@ -126,13 +127,14 @@ end #' - rH Relative humidity (-) #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' See `\link{Esat_slope`}. -#' - constants eps - ratio of the molecular weight of water vapor to dry air (-) \cr +#' See [`Esat_slope`](@ref). +#' - constants eps - ratio of the molecular weight of water vapor to dry air (-) #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' #' @family humidity conversion #' -#' @references Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. +#' #References +#' Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. #' """ """ @@ -271,7 +273,7 @@ end #' The conversion is given by: #' -#' \deqn{PPFD = Rg * frac_PAR * J_to_mol} +#' ``PPFD = Rg * frac_PAR * J_to_mol`` #' #' by default, the combined conversion factor (`frac_PAR * J_to_mol`) is 2.3 #' @@ -308,7 +310,7 @@ end #' #' - mass Numeric vector of mass in kg #' - molarMass Numeric vector of molar mass of the substance (kg mol-1) -#' e.g. as provided by `\link{bigleaf_constants`}()$H2Omol +#' e.g. as provided by [`bigleaf_constants`](@ref)()$H2Omol #' Default is molar mass of Water. #' #' # Value @@ -332,11 +334,11 @@ end #' #' - CO2_flux CO2 flux (umol CO2 m-2 s-1) #' - C_flux Carbon (C) flux (gC m-2 d-1) -#' - constants Cmol - molar mass of carbon (kg mol-1) \cr -#' umol2mol - conversion micromole (umol) to mol (mol) \cr -#' mol2umol - conversion mole (mol) to micromole (umol) \cr -#' kg2g - conversion kilogram (kg) to gram (g) \cr -#' g2kg - conversion gram (g) to kilogram (kg) \cr +#' - constants Cmol - molar mass of carbon (kg mol-1) +#' umol2mol - conversion micromole (umol) to mol (mol) +#' mol2umol - conversion mole (mol) to micromole (umol) +#' kg2g - conversion kilogram (kg) to gram (g) +#' g2kg - conversion gram (g) to kilogram (kg) #' days2seconds - seconds per day #' #' ```@example; output = false diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 0f2c735..85b7d67 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -11,7 +11,7 @@ that accept a DataFrame with columnnames corresponding to required arguments. We can demonstrate the usage with a simple example: ```julia -potential_ET(tha,Tair="Tair",pressure="pressure",Rn="Rn",VPD="VPD",approach="Priestley-Taylor") +potential_ET(tha,Tair="Tair",pressure="pressure",Rn="Rn",VPD="VPD",approach=Val(:Priestley-Taylor)) potential_ET(tha) potential_ET(tha,Tair=tha$Tair) potential_ET(tha,Tair=25) @@ -61,7 +61,7 @@ In the function call above, `vars_qc` lists the variables that should be filtere In the next example, we filter for meteorological conditions only, including growing season (`filter_growseas=TRUE`): ```julia -tha_filtered2 = filter_data(tha,quality_control=FALSE,filter_growseas=TRUE, +tha_filtered2 = filter_data(tha,quality_control=false,filter_growseas=TRUE, filter_vars=c("PPFD","ustar","LE","VPD"), filter_vals_min=c(200,0.2,0,0.01), filter_vals_max=c(NA,NA,NA,NA), NA_as_invalid = TRUE, @@ -78,7 +78,7 @@ In this case, it does not really make sense to filter for growing season, since As a last step we will filter for precipitation events. This is often meaningful for ecophysiological studies because data during and shortly after rainfall events do not contain much information on the physiological activity of the vegetation (i.e. they comprise significant fractions of evaporation from the soil and plant surfaces). The purpose of such a filter is mostly to minimize the fraction of soil and interception evaporation on the total water flux. This filter simply excludes periods following a precipitation event. A precipitation event is here defined as any time step with a recorded precipitation higher than `tprecip` (in mm per timestep). The function then filters all time periods following a precipitation event. The number of subsequent time periods excluded is controlled by the argument `precip_hours`. Here, we exclude rainfall events and the following 24 hours. ```julia -tha_filtered3 = filter_data(tha,quality_control=FALSE,filter_growseas=FALSE, +tha_filtered3 = filter_data(tha,quality_control=false,filter_growseas=false, filter_precip=TRUE,precip="precip",tprecip=0.02, records_per_hour=2,precip_hours=24) ``` @@ -267,8 +267,8 @@ Here, the points denote the mean wind speed and the bars denote the standard dev For many hydrological applications, it is relevant to get an estimate on the potential evapotranspiration (PET). At the moment, the `Bigleaf.jl` package contains two formulations for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: ```julia -summary(potential_ET(tha_filtered,G="G",approach="Priestley-Taylor")) -summary(potential_ET(tha_filtered,G="G",approach="Penman-Monteith"), +summary(potential_ET(tha_filtered,G="G",approach=Val(:Priestley-Taylor))) +summary(potential_ET(tha_filtered,G="G",approach=Val(:Penman-Monteith)), Gs_pot=quantile(tha_filtered$Gs_mol,0.95,na_rm=TRUE)) ``` @@ -480,7 +480,7 @@ unc_all = mapply(aerodynamic_conductance,Dl=Dl_sample,z0m=z0m_sample,LAI=LAI_sam ) # select "Ga_h" output variable and convert to matrix -unc_Ga_h = matrix(unlist(unc_all["Ga_h",]),ncol=n_pert,byrow=FALSE) +unc_Ga_h = matrix(unlist(unc_all["Ga_h",]),ncol=n_pert,byrow=false) # calculate 2.5th, 50th, and 97.5th quantile of the n_pert calculations for every timestep Ga_low = apply(unc_Ga_h,1,quantile,0.025,na_rm=T) diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 4387caa..83b7c99 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -8,7 +8,7 @@ using Dates, TimeZones using Pipe using AstroLib using Suppressor - +using Missings export toDataFrame, frac_hour @@ -23,6 +23,7 @@ export air_density, pressure_from_elevation, psychrometric_constant, wetbulb_temp_from_e_Tair_gamma, wetbulb_temp export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour +export potential_ET include("util.jl") include("bigleaf_constants.jl") @@ -30,5 +31,6 @@ include("unit_conversions.jl") include("meteorological_variables.jl") include("sun_position.jl") include("potential_radiation.jl") +include("evapotranspiration.jl") end diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl new file mode 100755 index 0000000..9fc7107 --- /dev/null +++ b/src/evapotranspiration.jl @@ -0,0 +1,223 @@ +""" +Potential Evapotranspiration + +Potential evapotranspiration according to Priestley & Taylor 1972 or + the Penman-Monteith equation with a prescribed surface conductance. + +# Arguments +- data: Data_frame or matrix containing all required variables; optional +- Tair: Air temperature (degC) +- pressure: Atmospheric pressure (kPa) +- Rn: Net radiation (W m-2) +- G: Ground heat flux (W m-2); optional +- S: Sum of all storage fluxes (W m-2); optional +- VPD: Vapor pressure deficit (kPa); only used if `approach = Val(:Penman-Monteith)`. +- Ga: Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = Val(:Penman-Monteith)`. +- approach: Approach used. Either `Val(:Priestley-Taylor)` (default), or `Val(:Penman-Monteith)`. +- alpha: Priestley-Taylor coefficient; only used if `approach = Val(:Priestley-Taylor)`. +- Gs_pot: Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; + only used if `approach = Val(:Penman-Monteith)`. +- missing_G_as_NA: if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. +- missing_S_as_NA: if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. +- Esat_formula: Optional: formula to be used for the calculation of esat and the slope of esat. + One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. + See [`Esat_slope`](@ref). +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - cp - specific heat of air for constant pressure (J K-1 kg-1) + - eps - ratio of the molecular weight of water vapor to dry air + - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) + - Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) + - Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) + - Kelvin - conversion degree Celsius to Kelvin (only used if `approach = Val(:Penman-Monteith)`) + +# Details +Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 +if `approach = Val(:Priestley-Taylor)` (the defau +``LE_pot,PT = (\\alpha * \\Delta * (Rn - G - S)) / (\\Delta + \\gamma)`` + +``\\alpha`` is the Priestley-Taylor coefficient, ``\\Delta`` is the slope +of the saturation vapor pressure curve (kPa K-1), and ``\\gamma`` is the +psychrometric constant (kPa K-1). +if `approach = Val(:Penman-Monteith)`, potential evapotranspiration is calculated according +to the Penman-Monteith equat + +``LE_pot,PM = (\\Delta * (Rn - G - S) + \\rho * cp * VPD * Ga) / (\\Delta + \\gamma * (1 + Ga/Gs_pot)`` + +where ``\\Delta`` is the slope of the saturation vapor pressure curve (kPa K-1), +``\\rho`` is the air density (kg m-3), and ``\\gamma`` is the psychrometric constant (kPa K-1). +The value of `Gs_pot` is typically a maximum value of Gs observed at the site, e.g. the 90th +percentile of Gs within the growing season. + +# Value +a DataFrame with the following columns: +- ET_pot: Potential evapotranspiration (kg m-2 s-1) +- LE_pot: Potential latent heat flux (W m-2) + +# Note +If the first argument `data` is provided (either a matrix or a DataFrame), +the following variables can be provided as character (in which case they are interpreted as +the column name of `data`) or as numeric vectors, in which case they are taken +directly for the calculations. If `data` is not provided, all input variables have to be +numeric vectors. + +# References +Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux +and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. + +Allen, R_G., Pereira L_S., Raes D., Smith M., 1998: Crop evapotranspiration - +Guidelines for computing crop water requirements - FAO Irrigation and drainage +paper 56. + +Novick, K_A., et al. 2016: The increasing importance of atmospheric demand +for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. + +# See also +[`surface_conductance`](@ref) + +```@example; output = false +# Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 +# using Priestley-Taylor: +potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach=Val(:Priestley-Taylor)) + +# Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) +# using Penman-Monteith: +LE_pot_PM = potential_ET(Gs_pot=0.5,Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400, + approach=Val(:Penman-Monteith))[,"LE_pot"] +LE_pot_PM + +# now cross-check with the inverted equation +surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) +``` +""" +function potential_ET(Tair,pressure,Rn, VPD,Ga; + G=missing,S=missing, approach=Val(:Priestley-Taylor), + alpha=1.26,Gs_pot=0.6, + missing_G_as_NA=false, missing_S_as_NA=false, + Esat_formula=Val(:Sonntag_1990), + constants=bigleaf_constants()) + + #TODO check_input(data,list(Tair,pressure,Rn,G,S)) + G,S = deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) + gamma = psychrometric_constant(Tair,pressure,constants) + Delta = Esat_from_Tair_deriv(Tair; formula = Esat_formula,constants) + # TODO replace by dispatch + if (approach == Val(:Priestley-Taylor)) + LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) + ET_pot = LE_to_ET(LE_pot,Tair) + elseif (approach == Val(:Penman-Monteith)) + #TODO check_input(data,list(Gs_pot,VPD,Ga)) + Gs_pot = mol_to_ms(Gs_pot,Tair,pressure;constants) + rho = air_density(Tair,pressure;constants) + LE_pot = (Delta * (Rn - G - S) + rho * constants[:cp] * VPD * Ga) / + (Delta + gamma * (1 + Ga / Gs_pot)) + ET_pot = LE_to_ET(LE_pot,Tair) + end + @suppress_err SLVector(ET_pot = ET_pot, LE_pot = LE_pot) +end + + +""" + TODO + +Evapotranspiration (ET) split up into imposed ET and equilibrium ET. + +- Tair: Air temperature (deg C) +- pressure: Atmospheric pressure (kPa) +- VPD: Air vapor pressure deficit (kPa) +- Gs: surface conductance to water vapor (m s-1) +- Rn: Net radiation (W m-2) +- G: Ground heat flux (W m-2); optional +- S: Sum of all storage fluxes (W m-2); optional +- missing_G_as_NA: if `TRUE`, missing G are treated as `NA`s, otherwise set 0. +- missing_S_as_NA: if `TRUE`, missing S are treated as `NA`s, otherwise set 0. +- Esat_formula: Optional: formula to be used for the calculation of esat and slope of esat. + One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, `"Allen_1998"`. + See [`Esat_slope`](@ref). +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + +- constants + - cp - specific heat of air for constant pressure (J K-1 kg-1) + - eps - ratio of the molecular weight of water vapor to dry (-) + - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) + +# Details +Total evapotranspiration can be written in the form (Jarvis & McNaughton 6): + +``ET = \\Omega ET_eq + (1 - \\Omega)ET_imp`` + +where ``\\Omega`` is the decoupling coefficient as calculated from +[`decoupling`](@ref). `ET_eq` is the equilibrium evapotranspiration e, +the ET rate that would occur under uncoupled conditions, where tbudget +is dominated by radiation (when Ga -> 0): + + ``ET_eq = (\\Delta * (Rn - G - S) * \\lambda) / (\\Del\\gamma) + +where ``\\Delta`` is the slope of the saturation vapor pressur(kPa K-1), +``\\lambda`` is the latent heat of vaporization (J kg-1), and \\gamma`` +is the psychrometric constant (kPa K-1). +`ET_imp` is the imposed evapotranspiration rate, the ET rate +that would occur under fully coupled conditions (when Ga -> inf): + + ``ET_imp = (\\rho * cp * VPD * Gs * \\lambda) / \\gamma`` + +where ``\\rho`` is the air density (kg m-3). + +#Note +Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) +Aerodynamic conductance (Ga) can be calculated using erodynamic_conductance`](@ref). + +# Value +A DataFrame with the following columns: +- ET_eq: Equilibrium ET (kg m-2 s-1) +- ET_imp: Imposed ET (kg m-2 s-1) +- LE_eq: Equilibrium LE (W m-2) +- LE_imp: Imposed LE (W m-2) + +#References +- Jarvis, P_G., McNaughton, K_G., 1986: Stomatal control of transpiration: +scaling up from leaf to region. Advances in Ecological Rese1-49. +- Monteith, J_L., Unsworth, M_H., 2008: Principles of ironmPhysics. +3rd edition. Academic Press, London. + +#See also +[`decoupling`](@ref) + +```@example; output = false +df = DataFrame(Tair=20,pressure=100,VPD=seq(0.5,4,0.5), + Gs_ms=seq(0.01,0.002,length_out=8),Rn=seq(50,400,50) +equilibrium_imposed_ET(df) +``` +""" +function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, + G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, + Esat_formula=Val(:Sonntag_1990), + constants=bigleaf_constants()) + # + #check_input(data,list(Tair,pressure,VPD,Rn,Gs,G,S)) + G,S = deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) + rho = air_density(Tair,pressure,constants) + gamma = psychrometric_constant(Tair,pressure,constants) + Delta = Esat_from_Tair_deriv(Tair,Esat_formula,constants) + LE_eq = (Delta * (Rn - G - S)) / (gamma + Delta) + LE_imp = (rho * constants[:cp] * Gs * VPD) / gamma + # + ET_imp = LE_to_ET(LE_imp,Tair) + ET_eq = LE_to_ET(LE_eq,Tair) + @suppress_err SLVector(ET_pot = ET_pot, LE_pot = LE_pot, LE_imp = LE_imp) +end + +function deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) + if ismissing(G) + @info("Ground heat flux G is not provided and set to 0.") + G = 0 + elseif missing_G_as_NA == false + G = coalesce(G, 0) + end + if ismissing(S) + @info("Energy storage fluxes S are not provided and set to 0.") + S = 0 + elseif missing_S_as_NA false + S = coalesce(S, 0) + end + G,S +end From e1de84a45b340b4fef73f8371af13f857c312f67 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Sun, 24 Oct 2021 20:30:09 +0200 Subject: [PATCH 06/43] return NamedTuple instead of SLVector --- docs/src/walkthrough.md | 2 +- src/evapotranspiration.jl | 4 ++-- src/sun_position.jl | 4 ++-- test/sun_position.jl | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 2d75d85..67e5216 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -157,7 +157,7 @@ lat,long = 51.0, 13.6 # Dresden Germany #deg2second = 24*3600/360 doy = 160 datetimes = DateTime(2021) .+Day(doy-1) .+ Hour.(hours) #.- Second(round(long*deg2second)) -res3 = @suppress_err @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDataFrame(_) +res3 = @suppress_err @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) @df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad") ``` diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index 9fc7107..77e4bd5 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -112,7 +112,7 @@ function potential_ET(Tair,pressure,Rn, VPD,Ga; (Delta + gamma * (1 + Ga / Gs_pot)) ET_pot = LE_to_ET(LE_pot,Tair) end - @suppress_err SLVector(ET_pot = ET_pot, LE_pot = LE_pot) + (ET_pot = ET_pot, LE_pot = LE_pot) end @@ -203,7 +203,7 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, # ET_imp = LE_to_ET(LE_imp,Tair) ET_eq = LE_to_ET(LE_eq,Tair) - @suppress_err SLVector(ET_pot = ET_pot, LE_pot = LE_pot, LE_imp = LE_imp) + (ET_pot = ET_pot, LE_pot = LE_pot, LE_imp = LE_imp) end function deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) diff --git a/src/sun_position.jl b/src/sun_position.jl index 6980c78..d04cfe2 100644 --- a/src/sun_position.jl +++ b/src/sun_position.jl @@ -25,7 +25,7 @@ function calc_sun_position_hor(datetime::DateTime, lat, long) pos_eq = calc_sun_position_MOD(jd) # precession is already account for in MOD pos_hor = eq2hor(pos_eq.α/deg2rad, pos_eq.δ/deg2rad, jd, lat, long; precession = false) .* deg2rad - @suppress_err SLVector(altitude = pos_hor[1], azimuth = pos_hor[2], hourangle = pos_hor[3]) + (altitude = pos_hor[1], azimuth = pos_hor[2], hourangle = pos_hor[3]) end @@ -132,7 +132,7 @@ function calc_sun_position_MOD(JD::Number) # declination δ = asin(sinϵ * sinλ_e) - @suppress_err S_MOD_rad = SLVector( + S_MOD_rad = ( λ = λ_e, β = 0.0, r = r, α = α, δ = δ, ϵ = ϵ diff --git a/test/sun_position.jl b/test/sun_position.jl index ac27605..2a8b00b 100644 --- a/test/sun_position.jl +++ b/test/sun_position.jl @@ -3,7 +3,7 @@ lat,long = 0.0,0.0 deg2second = 24*3600/360 datetimes = DateTime(2021,3,20) .+ Hour.(hours) .- Second(round(long*deg2second)) - res2 = @pipe calc_sun_position_MOD.(datetime2julian.(datetimes)) |> toDataFrame(_) + res2 = @pipe calc_sun_position_MOD.(datetime2julian.(datetimes)) |> DataFrame(_) # latitude over ecliptic of sun is zero @test all(res2.β .≈ 0.0) au2m = 149597870700.0 @@ -21,14 +21,14 @@ # @df res3 scatter(hours, cols(1:2), legend = :topleft, xlab="hours") # end # - res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDataFrame(_) + res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) # altitude (-pi, +pi) @test minimum(res3.altitude) ≈ -π/2 atol = 0.1 @test maximum(res3.altitude) ≈ +π/2 atol = 0.1 # lat,long = 45.0,0.0 datetimes = DateTime(2021,3,20) .+ Hour.(hours) .- Second(round(long*deg2second)) - res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDataFrame(_) + res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) # altitude (-pi, +pi) @test minimum(res3.altitude) ≈ -π/4 atol = 0.1 @test maximum(res3.altitude) ≈ +π/4 atol = 0.1 @@ -48,7 +48,7 @@ end # @df res3 scatter(hours, cols(1:2), legend = :topleft, xlab="hours") # end # - res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> toDataFrame(_) + res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) # maximum altitude at noon @test argmax(res3.altitude) == 1 + (length(hours)-1) ÷ 2 # azimuth increasing (aside edge cases) From a8b9982d1a1f67a9ada60acfe82e08f456ab2a7b Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 25 Oct 2021 22:51:25 +0200 Subject: [PATCH 07/43] implement DataFrame variants of potential_ET --- Project.toml | 1 + docs/make.jl | 2 + docs/src/evapotranspiration.md | 11 + docs/src/index.md | 5 + docs/src/walkthrough.md | 39 ++- inst/fromR/WUE_metrics.jl | 2 +- inst/fromR/aerodynamic_conductance.jl | 2 +- inst/fromR/bigleaf_physiology.jl | 10 +- inst/fromR/boundary_layer_conductance.jl | 4 +- inst/fromR/check_input.jl | 4 +- inst/fromR/datasets_description.jl | 6 +- inst/fromR/evapotranspiration.jl | 42 +-- inst/fromR/filter_data.jl | 2 +- inst/fromR/meteorological_variables.jl | 6 +- inst/fromR/stability_correction.jl | 2 +- inst/fromR/surface_conditions.jl | 4 +- inst/fromR/surface_conductance.jl | 18 +- inst/fromR/surface_roughness.jl | 2 +- inst/walkthrough_todo.jmd | 6 +- src/Bigleaf.jl | 7 +- src/evapotranspiration.jl | 318 +++++++++++++++++------ src/sun_position.jl | 4 +- src/util.jl | 24 +- test/evapotranspiration.jl | 46 ++++ test/runtests.jl | 3 + 25 files changed, 410 insertions(+), 160 deletions(-) create mode 100644 docs/src/evapotranspiration.md create mode 100644 test/evapotranspiration.jl diff --git a/Project.toml b/Project.toml index 2c4176b..468ddf1 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.1.0" AstroLib = "c7932e45-9af1-51e7-9da9-f004cd3a462b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" LabelledArrays = "2ee39098-c373-598a-b85f-a56591580800" Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" Optim = "429524aa-4258-5aef-a3af-852621145aeb" diff --git a/docs/make.jl b/docs/make.jl index 1c5b753..33dcbda 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -13,6 +13,7 @@ makedocs(; authors="Thomas Wutzler . Jürgen Knauer and contributors", repo="https://github.com/bgctw/Bigleaf.jl/blob/{commit}{path}#{line}", sitename="Bigleaf.jl", + doctestfilters=[r".*Info.*"], format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", canonical="https://bgctw.github.io/Bigleaf.jl", @@ -24,6 +25,7 @@ makedocs(; #"Unit conversions" => "unit_conversions.md", "Walkthrough" => "walkthrough.md", hide("metorological_variables.md"), + hide("evapotranspiration.md"), hide("global_radiation.md"), hide("unit_conversions.md"), hide("bigleaf_constants.md"), diff --git a/docs/src/evapotranspiration.md b/docs/src/evapotranspiration.md new file mode 100644 index 0000000..fc755c6 --- /dev/null +++ b/docs/src/evapotranspiration.md @@ -0,0 +1,11 @@ +## Evapotranspiration +```@index +Pages = ["evapotranspiration.md",] +``` + +```@docs +potential_ET +equilibrium_imposed_ET +fill_GS_missings! +surface_conductance +``` \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 6aaf4a9..2925b20 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -13,6 +13,11 @@ Pages = ["index.md",] Pages = ["metorological_variables.md",] ``` +## Evapotranspiration +```@index +Pages = ["evapotranspiration.md",] +``` + ## Global radiation ```@index Pages = ["global_radiation.md",] diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 67e5216..e6004f6 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -70,9 +70,44 @@ There are a few general guidelines that are important to consider when using the ## Units -It is imperative that variables are provided in the right units, as the plausibility of the input units is not checked in most cases. The required units of the input arguments can be found in the respective help file of the function. The good news is that units do not change across functions. For example, pressure is always required in kPa, and temperature always in °c. +It is imperative that variables are provided in the right units, as the plausibility of +the input units is not checked in most cases. The required units of the input arguments +can be found in the respective help file of the function. The good news is that units +do not change across functions. For example, pressure is always required in kPa, +and temperature always in °c. + +## Function arguments + +Most functions of `Bigleaf.jl` require a DataFrame, from which the required +variables are extracted. This is usually the first argument of a function. +Most functions further provide default values for their arguments, +such that in many cases it is not necessary to provide them explicitly. + +The column names in the DataFrame should correspond to the argument names +of the corresponding method that accespts each input individually. + +We can demonstrate the usage with a simple example: + +```@example doc +# explicit inputs +Tair, pressure, Rn, = 14.81, 97.71, 778.17 +potential_ET(Tair, pressure, Rn, Val(:PriestleyTaylor)) +# DataFrame +potential_ET(tha, Val(:PriestleyTaylor)) +# DataFrame with a few columns overwritten by user values +potential_ET(transform(tha, :Tair => x -> 25.0; renamecols=false), Val(:PriestleyTaylor)) +# varying one input only +Tair_vec = 10.0:1.0:20.0 +DataFrame(potential_ET.(Tair_vec, pressure, Rn, Val(:PriestleyTaylor); infoGS=false)) +nothing # hide +``` + +## Ground heat flux and storage fluxes + +Many functions require the available energy ($A$), which is defined as ($A = R_n - G - S$, all in $\text{W m}^{-2}$), where $R_n$ is the net radiation, $G$ is the ground heat flux, and $S$ is the sum of all storage fluxes of the ecosystem (see e.g. Leuning et al. 2012 for an overview). For some sites, $G$ is not available, and for most sites, only a few components of $S$ are measured. In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results might be (slightly) biased), but special options exist for the treatment of missing $S$ and $G$ values. If the options `missing_G_as_NA = TRUE` or `missing_S_as_NA = TRUE`, then the output variable is not calculated for that time period. Otherwise missing $S$ and $G$ values are set to O automatically. Please note that the default is to ignore $S$ and $G$ values. If $G$ and/or $S$ are available, they usually have to be added explicitly to the function call by explicitly using the optional arguments. The positional forms do not check for missing +values. -## TODO ## +# Function walkthrough # ## Meteorological variables diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index 227f4be..0c521c6 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -45,7 +45,7 @@ #' - IWUE: Inherent water-use efficiency (gC kPa (kg H20)-1) #' - uWUE: Underlying water-use efficiency (gC kPa^0.5 (kg H20)-1) #' -#' #Note +#' # Note #' Units for VPD can also be hPa. Units change accordingly. #' WUE_NEE is calculated based on the absolute value of NEE (the sign convention does not matter here). #' diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index bd6a739..fdbe33c 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -135,7 +135,7 @@ #' \item{Gb_Sc_name}{Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` and #' the respective `Sc` are provided} #' -#' #Note +#' # Note #' The roughness length for water and heat (z0h) is not returned by this function, but #' it can be calculated from the following relationship (e.g. Verma 1989): #' diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index b583b34..2007954 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -28,7 +28,7 @@ #' Ca can either be atmospheric CO2 concentration (as measured), or surface #' CO2 concentration as calculated from [`surface_CO2`](@ref). #' -#' #Note +#' # Note #' The equation is based on Fick's law of diffusion and is equivalent to the #' often used equation at leaf level (ci = ca - An/gs). #' Note that GPP and Gs have a different interpretation than An and gs. @@ -206,7 +206,7 @@ end #' The calculation of Jmax25 and Vcmax25 is identical to C3 photosynthesis #' as described above. #' -#' #Note +#' # Note #' The critical assumption is that bulk canopy photosynthesis is limited by #' one of the two limitation states. Incoming PPFD is assumed to determine #' the limitation states. Note however that the ranges (`PPFD_j` and `PPFD_c`) @@ -274,7 +274,7 @@ end #' # calculate Gs from the the inverted PM equation #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation=Val(:Penman-Monteith))[,"Gs_mol"] +#' formulation=Val(:PenmanMonteith))[,"Gs_mol"] #' #' # calculate Ci #' Ci = intercellular_CO2(DE_Tha_Jun_2014_2,Ca="Ca",GPP="GPP",Gs=Gs_PM) @@ -574,7 +574,7 @@ end #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation=Val(:Penman-Monteith))[,"Gs_mol"] +#' formulation=Val(:PenmanMonteith))[,"Gs_mol"] #' #' ### Estimate the stomatal slope parameter g1 using the USO model #' mod_USO = stomatal_slope(DE_Tha_Jun_2014_2,model="USO",GPP="GPP",Gs=Gs_PM, @@ -778,7 +778,7 @@ end #' `PPFD_ref` defaults to 2000 umol m-2 s-1, but other values can be used. For #' further details refer to Falge et al. 2001. #' -#' #Note +#' # Note #' Note the sign convention. Negative NEE indicates that carbon is taken up #' by the ecosystem. Reco has to be 0 or larger. #' diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index 29d62fb..b6ac2d0 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -140,7 +140,7 @@ end #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' #Note +#' # Note #' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile`. This function #' estimates a single `z0m` value for the entire time period! If a varying `z0m` value @@ -296,7 +296,7 @@ end #' #' where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). #' -#' #Note +#' # Note #' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile`. This function #' estimates a single `z0m` value for the entire time period! If a varying `z0m` value diff --git a/inst/fromR/check_input.jl b/inst/fromR/check_input.jl index 36242ae..d94461b 100755 --- a/inst/fromR/check_input.jl +++ b/inst/fromR/check_input.jl @@ -9,7 +9,7 @@ #' - data Input DataFrame or matrix #' - ... Input variables. Either a list or individual vectors #' -#' #Note +#' # Note #' This function can be run for named variables (in which case the return #' value will be named according to the given name), or for placeholder #' variables that are assigned and named according to e.g. entries of a character @@ -101,7 +101,7 @@ end #' #' - varlist List of variables for which the length has to be compared #' -#' #Note +#' # Note #' This function only plays a role if no input DataFrame or matrix are #' provided. In this case it ensures that provided vectors have the same #' length to avoid trouble later up the function call. diff --git a/inst/fromR/datasets_description.jl b/inst/fromR/datasets_description.jl index 5e627d4..44f21cd 100755 --- a/inst/fromR/datasets_description.jl +++ b/inst/fromR/datasets_description.jl @@ -40,7 +40,7 @@ #' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' #Note +#' # Note #' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). @@ -94,7 +94,7 @@ #' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' #Note +#' # Note #' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). @@ -145,7 +145,7 @@ #' - Reco: Ecosystem respiration from nighttime partitioning (umol m-2 s-1) [RECO_NT_VUT_USTAR50] #' } #' -#' #Note +#' # Note #' The original variable names as provided by the FLUXNET2015 dataset are #' given in squared brackets. Note that variable units have been converted #' in some cases (e.g. VPD from hPa to kPa). diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index 9c5beed..cab37f9 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -13,12 +13,12 @@ #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - VPD Vapor pressure deficit (kPa); only used if `approach = Val(:Penman-Monteith)`. -#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = Val(:Penman-Monteith)`. -#' - approach Approach used. Either `Val(:Priestley-Taylor)` (default), or `Val(:Penman-Monteith)`. -#' - alpha Priestley-Taylor coefficient; only used if `approach = Val(:Priestley-Taylor)`. +#' - VPD Vapor pressure deficit (kPa); only used if `approach = Val(:PenmanMonteith)`. +#' - Ga Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = Val(:PenmanMonteith)`. +#' - approach Approach used. Either `Val(:PriestleyTaylor)` (default), or `Val(:PenmanMonteith)`. +#' - alpha Priestley-Taylor coefficient; only used if `approach = Val(:PriestleyTaylor)`. #' - Gs_pot Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; -#' only used if `approach = Val(:Penman-Monteith)`. +#' only used if `approach = Val(:PenmanMonteith)`. #' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. @@ -27,20 +27,20 @@ #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) #' eps - ratio of the molecular weight of water vapor to dry air #' Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) -#' Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) -#' Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) -#' Kelvin - conversion degree Celsius to Kelvin (only used if `approach = Val(:Penman-Monteith)`) +#' Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = Val(:PenmanMonteith)`) +#' Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = Val(:PenmanMonteith)`) +#' Kelvin - conversion degree Celsius to Kelvin (only used if `approach = Val(:PenmanMonteith)`) #' #' # Details #' Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 -#' if `approach = Val(:Priestley-Taylor)` (the default): +#' if `approach = Val(:PriestleyTaylor)` (the default): #' #' ``LE_pot,PT = (\\alpha * \\Delta * (Rn - G - S)) / (\\Delta + \\gamma)`` #' #' ``\\alpha`` is the Priestley-Taylor coefficient, ``\\Delta`` is the slope #' of the saturation vapor pressure curve (kPa K-1), and ``\\gamma`` is the #' psychrometric constant (kPa K-1). -#' if `approach = Val(:Penman-Monteith)`, potential evapotranspiration is calculated according +#' if `approach = Val(:PenmanMonteith)`, potential evapotranspiration is calculated according #' to the Penman-Monteith equation: #' #' ``LE_pot,PM = (\\Delta * (Rn - G - S) + \\rho * cp * VPD * Ga) / (\\Delta + \\gamma * (1 + Ga/Gs_pot)`` @@ -55,7 +55,7 @@ #' - ET_pot: Potential evapotranspiration (kg m-2 s-1) #' - LE_pot: Potential latent heat flux (W m-2) #' -#' #Note +#' # Note #' If the first argument `data` is provided (either a matrix or a DataFrame), #' the following variables can be provided as character (in which case they are interpreted as #' the column name of `data`) or as numeric vectors, in which case they are taken @@ -80,12 +80,12 @@ #' ``` #' # Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 #' # using Priestley-Taylor: -#' potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach=Val(:Priestley-Taylor)) +#' potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach=Val(:PriestleyTaylor)) #' #' # Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) #' # using Penman-Monteith: #' LE_pot_PM = potential_ET(Gs_pot=0.5,Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400, -#' approach=Val(:Penman-Monteith))[,"LE_pot"] +#' approach=Val(:PenmanMonteith))[,"LE_pot"] #' LE_pot_PM #' #' # now cross-check with the inverted equation @@ -93,7 +93,7 @@ """ """ function potential_ET(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, - VPD="VPD",Ga="Ga_h",approach=c(Val(:Priestley-Taylor),Val(:Penman-Monteith)), + VPD="VPD",Ga="Ga_h",approach=c(Val(:PriestleyTaylor),Val(:PenmanMonteith)), alpha=1.26,Gs_pot=0.6,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -124,12 +124,12 @@ end Delta = Esat_slope(Tair,Esat_formula,constants)[,"Delta"] - if (approach == Val(:Priestley-Taylor)) + if (approach == Val(:PriestleyTaylor)) LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) ET_pot = LE_to_ET(LE_pot,Tair) -else if (approach == Val(:Penman-Monteith)) +else if (approach == Val(:PenmanMonteith)) check_input(data,list(Gs_pot,VPD,Ga)) @@ -152,7 +152,7 @@ end #' #' Reference evapotranspiration calculated from the Penman-Monteith #' equation with a prescribed surface conductance. -#' This function is deprecated. Use potential_ET(...,approach=Val(:Penman-Monteith)) instead. +#' This function is deprecated. Use potential_ET(...,approach=Val(:PenmanMonteith)) instead. #' #' - data Data_frame or matrix containing all required variables; optional #' - Gs_ref Reference surface conductance (m s-1); defaults to 0.0143 m s-1. @@ -170,9 +170,9 @@ end #' See [`Esat_slope`](@ref). #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) #' eps - ratio of the molecular weight of water vapor to dry air -#' Rd - gas constant of dry air (J kg-1 K-1) (only if `approach = Val(:Penman-Monteith)`) -#' Rgas - universal gas constant (J mol-1 K-1) (only if `approach = Val(:Penman-Monteith)`) -#' Kelvin - conversion degree Celsius to Kelvin (only if `approach = Val(:Penman-Monteith)`) +#' Rd - gas constant of dry air (J kg-1 K-1) (only if `approach = Val(:PenmanMonteith)`) +#' Rgas - universal gas constant (J mol-1 K-1) (only if `approach = Val(:PenmanMonteith)`) +#' Kelvin - conversion degree Celsius to Kelvin (only if `approach = Val(:PenmanMonteith)`) #' #' @export function reference_ET(data,Gs_ref=0.0143,Tair="Tair",pressure="pressure",VPD="VPD",Rn="Rn",Ga="Ga_h", @@ -231,7 +231,7 @@ end #' #' where ``\\rho`` is the air density (kg m-3). #' -#' #Note +#' # Note #' Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref). #' Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). #' diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index bba40e9..00b5566 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -75,7 +75,7 @@ #' column "valid", which indicates whether all the data of a time step fulfill the #' filtering criteria (1) or not (0). #' -#' #Note +#' # Note #' The thresholds set with `filter_vals_min` and `filter_vals_max` filter all data #' that are smaller than ("<"), or greater than (">") the specified thresholds. That means #' if a variable has exactly the same value as the threshold, it will not be filtered. Likewise, diff --git a/inst/fromR/meteorological_variables.jl b/inst/fromR/meteorological_variables.jl index ef9d59e..b5d1a17 100755 --- a/inst/fromR/meteorological_variables.jl +++ b/inst/fromR/meteorological_variables.jl @@ -61,7 +61,7 @@ end #' #' ``pressure = pressure_0 / (exp(g * elevation / (Rd Temp)))`` #' -#' #Note +#' # Note #' The hypsometric equation gives an estimate of the standard pressure #' at a given altitude. #' If VPD is provided, humidity correction is applied and the @@ -285,7 +285,7 @@ end #' See [`Esat_slope`](@ref). #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' #Note +#' # Note #' Arguments `accuracy` and `Esat_formula` are passed to this function by wetbulb_temp(). #' #' @importFrom stats optimize @@ -386,7 +386,7 @@ end #' See [`Esat_slope`](@ref). #' - constants Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) #' -#' #Note +#' # Note #' Arguments `accuracy` and `Esat_formula` are passed to this function by dew_point(). #' #' @importFrom stats optimize diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 0a68405..048d456 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -26,7 +26,7 @@ #' # Value - L -: Monin-Obukhov length (m) #' -#' #Note +#' # Note #' Note that L gets very small for very low ustar values with implications #' for subsequent functions using L as input. It is recommended to filter #' data and exclude low ustar values (ustar < ~0.2) beforehand. diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index 853c281..1e9b9b7 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -57,7 +57,7 @@ #' the results of the functions represent conditions outside the canopy #' boundary layer, i.e. in the canopy airspace. #' -#' #Note +#' # Note #' The following sign convention for NEE is employed (relevant if #' `calc_surface_CO2 = TRUE`): #' negative values of NEE denote net CO2 uptake by the ecosystem. @@ -149,7 +149,7 @@ end #' replaced by the net exchange of the respective gas and Ga_CO2 by the Ga of #' that gas). #' -#' #Note +#' # Note #' the following sign convention is employed: negative values of NEE denote #' net CO2 uptake by the ecosystem. #' diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 180eb77..acf6754 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -17,15 +17,15 @@ #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) #' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise they are set to 0. -#' Only used if `formulation = Val(:Penman-Monteith)`. +#' Only used if `formulation = Val(:PenmanMonteith)`. #' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise they are set to 0. -#' Only used if `formulation = Val(:Penman-Monteith)`. -#' - formulation Formulation used. Either `Val(:Penman-Monteith)` (the default) +#' Only used if `formulation = Val(:PenmanMonteith)`. +#' - formulation Formulation used. Either `Val(:PenmanMonteith)` (the default) #' using the inverted Penman-Monteith equation, or `"Flux-Gradient"`, #' for a simple flux-gradient approach requiring ET, pressure, and VPD only. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. -#' Only used if `formulation = Val(:Penman-Monteith)`. See [`Esat_slope`](@ref). +#' Only used if `formulation = Val(:PenmanMonteith)`. See [`Esat_slope`](@ref). #' - constants cp - specific heat of air for constant pressure (J K-1 kg-1) #' eps - ratio of the molecular weight of water vapor to dry air (-) #' Rd - gas constant of dry air (J kg-1 K-1) @@ -36,7 +36,7 @@ #' #' #' # Details - If `formulation = Val(:Penman-Monteith)` (the default), surface conductance (Gs) in m s-1 + If `formulation = Val(:PenmanMonteith)` (the default), surface conductance (Gs) in m s-1 #' is calculated from the inverted Penman-Monteith equation: #' #' ``Gs = ( LE * Ga * \\gamma ) / ( \\Delta * A + \\rho * cp * Ga * VPD - LE * ( \\Delta + \\gamma ) )`` @@ -95,7 +95,7 @@ #' # Note that Ga is not added to the DataFrame 'DE_Tha_Jun_2014' #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, -#' formulation=Val(:Penman-Monteith)) +#' formulation=Val(:PenmanMonteith)) #' summary(Gs_PM) #' #' @@ -103,7 +103,7 @@ #' DE_Tha_Jun_2014_2$Ga = Ga #' Gs_PM2 = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", #' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga="Ga", -#' formulation=Val(:Penman-Monteith)) +#' formulation=Val(:PenmanMonteith)) #' # note the difference to the previous version (Ga="Ga") #' summary(Gs_PM2) #' @@ -120,7 +120,7 @@ """ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, VPD="VPD",LE="LE",Ga="Ga_h",missing_G_as_NA=false,missing_S_as_NA=false, - formulation=c(Val(:Penman-Monteith),"Flux-Gradient"), + formulation=c(Val(:PenmanMonteith),"Flux-Gradient"), Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -133,7 +133,7 @@ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL Gs_mol = (LE_to_ET(LE,Tair)/constants[:Mw]) * pressure / VPD Gs_ms = mol_to_ms(Gs_mol,Tair,pressure) -else if (formulation == Val(:Penman-Monteith)) +else if (formulation == Val(:PenmanMonteith)) check_input(data,list(Tair,pressure,VPD,LE,Rn,Ga,G,S)) diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index c6a2d34..33f9ca1 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -252,7 +252,7 @@ end #' z0m is first estimated from the wind profile equation and then used in the equation #' above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). #' -#' #Note +#' # Note #' Note that this equation is only valid for z >= d + z0m, and it is not #' meaningful to calculate values closely above d + z0m. All values in `heights` #' smaller than d + z0m will return 0. diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 85b7d67..961846d 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -11,7 +11,7 @@ that accept a DataFrame with columnnames corresponding to required arguments. We can demonstrate the usage with a simple example: ```julia -potential_ET(tha,Tair="Tair",pressure="pressure",Rn="Rn",VPD="VPD",approach=Val(:Priestley-Taylor)) +potential_ET(tha,Tair="Tair",pressure="pressure",Rn="Rn",VPD="VPD",approach=Val(:PriestleyTaylor)) potential_ET(tha) potential_ET(tha,Tair=tha$Tair) potential_ET(tha,Tair=25) @@ -267,8 +267,8 @@ Here, the points denote the mean wind speed and the bars denote the standard dev For many hydrological applications, it is relevant to get an estimate on the potential evapotranspiration (PET). At the moment, the `Bigleaf.jl` package contains two formulations for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: ```julia -summary(potential_ET(tha_filtered,G="G",approach=Val(:Priestley-Taylor))) -summary(potential_ET(tha_filtered,G="G",approach=Val(:Penman-Monteith)), +summary(potential_ET(tha_filtered,G="G",approach=Val(:PriestleyTaylor))) +summary(potential_ET(tha_filtered,G="G",approach=Val(:PenmanMonteith)), Gs_pot=quantile(tha_filtered$Gs_mol,0.95,na_rm=TRUE)) ``` diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 83b7c99..b3b0eaa 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -2,7 +2,7 @@ module Bigleaf #using DocStringExtensions using Optim -using StaticArrays, LabelledArrays, RecursiveArrayTools +using FillArrays using DataFrames using Dates, TimeZones using Pipe @@ -11,7 +11,7 @@ using Suppressor using Missings -export toDataFrame, frac_hour +export frac_hour export bigleaf_constants export Esat_slope, Esat_from_Tair, Esat_from_Tair_deriv, LE_to_ET, ET_to_LE, ms_to_mol, mol_to_ms, VPD_to_rH, rH_to_VPD, @@ -23,7 +23,8 @@ export air_density, pressure_from_elevation, psychrometric_constant, wetbulb_temp_from_e_Tair_gamma, wetbulb_temp export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour -export potential_ET +export potential_ET, potential_ET!, fill_GS_missings!, equilibrium_imposed_ET, + surface_conductance include("util.jl") include("bigleaf_constants.jl") diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index 77e4bd5..f26b5d0 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -1,121 +1,203 @@ """ -Potential Evapotranspiration + potential_ET(Tair, pressure, Rn, ::Val{:PriestleyTaylor}; ...) + potential_ET(Tair, pressure, Rn, VPD, Ga, ::Val{:PenmanMonteith}; ...) + + potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; ...) + potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; ...) + + potential_ET(df, approach; ...) + potential_ET!(df, approach; ...) Potential evapotranspiration according to Priestley & Taylor 1972 or - the Penman-Monteith equation with a prescribed surface conductance. +the Penman-Monteith equation with a prescribed surface conductance. # Arguments -- data: Data_frame or matrix containing all required variables; optional - Tair: Air temperature (degC) - pressure: Atmospheric pressure (kPa) - Rn: Net radiation (W m-2) -- G: Ground heat flux (W m-2); optional -- S: Sum of all storage fluxes (W m-2); optional -- VPD: Vapor pressure deficit (kPa); only used if `approach = Val(:Penman-Monteith)`. -- Ga: Aerodynamic conductance to heat/water vapor (m s-1); only used if `approach = Val(:Penman-Monteith)`. -- approach: Approach used. Either `Val(:Priestley-Taylor)` (default), or `Val(:Penman-Monteith)`. -- alpha: Priestley-Taylor coefficient; only used if `approach = Val(:Priestley-Taylor)`. -- Gs_pot: Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; - only used if `approach = Val(:Penman-Monteith)`. -- missing_G_as_NA: if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. -- missing_S_as_NA: if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. -- Esat_formula: Optional: formula to be used for the calculation of esat and the slope of esat. - One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. - See [`Esat_slope`](@ref). +- VPD: Vapor pressure deficit (kPa) +- Ga: Aerodynamic conductance to heat/water vapor (m s-1) +- G: Ground heat flux (W m-2) +- S: Sum of all storage fluxes (W m-2) +- approach: Approach used. + Either `Val(:PriestleyTaylor)` (default), or `Val(:PenmanMonteith)`. +- df: Data_frame or matrix containing all required variables; optional +optional: +- `Esat_formula`: formula used in [`Esat_from_Tair`](@ref) - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - cp - specific heat of air for constant pressure (J K-1 kg-1) - eps - ratio of the molecular weight of water vapor to dry air - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) - - Rd - gas constant of dry air (J kg-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) - - Rgas - universal gas constant (J mol-1 K-1) (only used if `approach = Val(:Penman-Monteith)`) - - Kelvin - conversion degree Celsius to Kelvin (only used if `approach = Val(:Penman-Monteith)`) + - for :PenmanMonteith: + - Rd - gas constant of dry air (J kg-1 K-1) + - Rgas - universal gas constant (J mol-1 K-1) + - Kelvin - conversion degree Celsius to Kelvin +additional optional arguments if G and S are not specified by postition +and in the non-mutating DataFrame variant. +- G and S: as in the positional forms, but checked for missing or containing missings +- missing_G_as_NA: if `true`, missing optional G are treated as `NA`s, + otherwise set to 0. +- missing_S_as_NA: if `true`, missing optional S are treated as `NA`s, + otherwise set to 0. +additional optional for PriestleyTaylor: +- alpha: Priestley-Taylor coefficient +additional optional for PenmanMonteith: +- Gs_pot: Potential/maximum surface conductance (mol m-2 s-1); + defaults to 0.6 mol m-2 s-1; # Details Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 -if `approach = Val(:Priestley-Taylor)` (the defau -``LE_pot,PT = (\\alpha * \\Delta * (Rn - G - S)) / (\\Delta + \\gamma)`` +if `approach = Val(:PriestleyTaylor)` (the defau +``LE_{pot,PT} = (\\alpha * \\Delta * (Rn - G - S)) / (\\Delta + \\gamma)`` ``\\alpha`` is the Priestley-Taylor coefficient, ``\\Delta`` is the slope of the saturation vapor pressure curve (kPa K-1), and ``\\gamma`` is the psychrometric constant (kPa K-1). -if `approach = Val(:Penman-Monteith)`, potential evapotranspiration is calculated according +if `approach = Val(:PenmanMonteith)`, potential evapotranspiration is calculated according to the Penman-Monteith equat -``LE_pot,PM = (\\Delta * (Rn - G - S) + \\rho * cp * VPD * Ga) / (\\Delta + \\gamma * (1 + Ga/Gs_pot)`` +``LE_{pot,PM} = (\\Delta * (Rn - G - S) + \\rho * cp * VPD * Ga) / +(\\Delta + \\gamma * (1 + Ga/Gs_{pot})`` where ``\\Delta`` is the slope of the saturation vapor pressure curve (kPa K-1), -``\\rho`` is the air density (kg m-3), and ``\\gamma`` is the psychrometric constant (kPa K-1). +``\\rho`` is the air density (kg m-3), +and ``\\gamma`` is the psychrometric constant (kPa K-1). The value of `Gs_pot` is typically a maximum value of Gs observed at the site, e.g. the 90th percentile of Gs within the growing season. + +If keyword arguments `G` or `S` in the are set to `missing`, ground heat flux and storage flux +are assumed zero respectively. Note, that these keyword argument are not available in the +mutating form, where the caller has to add tree these columns before +(see [fill_GS_missings](@ref)). +Both methods are provided with several forms: +- all required inputs as positional arguments +- provideing G and S as positional arguments with handing of missing values +- providing a DataFrame with columns corresponding to required inputs + + # Value -a DataFrame with the following columns: -- ET_pot: Potential evapotranspiration (kg m-2 s-1) -- LE_pot: Potential latent heat flux (W m-2) +NamedTuple or DataFrame with the following entries: +- `ET_pot`: Potential evapotranspiration (kg m-2 s-1) +- `LE_pot`: Potential latent heat flux (W m-2) +For the mutating form, the original df with columns `ET_pot`, `LE_pot`, `G`, and `S` +updated or added. -# Note -If the first argument `data` is provided (either a matrix or a DataFrame), -the following variables can be provided as character (in which case they are interpreted as -the column name of `data`) or as numeric vectors, in which case they are taken -directly for the calculations. If `data` is not provided, all input variables have to be -numeric vectors. # References -Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux +- Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. - -Allen, R_G., Pereira L_S., Raes D., Smith M., 1998: Crop evapotranspiration - +- Allen, R_G., Pereira L_S., Raes D., Smith M., 1998: Crop evapotranspiration - Guidelines for computing crop water requirements - FAO Irrigation and drainage paper 56. - -Novick, K_A., et al. 2016: The increasing importance of atmospheric demand +- Novick, K_A., et al. 2016: The increasing importance of atmospheric demand for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. # See also [`surface_conductance`](@ref) - -```@example; output = false + +# Examples + +```jldoctest; output=false # Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 # using Priestley-Taylor: -potential_ET(Tair=30,pressure=100,Rn=500,alpha=1.26,approach=Val(:Priestley-Taylor)) - +Tair,pressure,Rn = 30.0,100.0,500.0 +ET_pot, LE_pot = potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor); + alpha=1.26, infoGS = false) +≈(ET_pot, 0.0002035969; rtol = 1e-5) +# output +true +``` +```@jldoctest; output=false # Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) # using Penman-Monteith: -LE_pot_PM = potential_ET(Gs_pot=0.5,Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400, - approach=Val(:Penman-Monteith))[,"LE_pot"] -LE_pot_PM - +Tair,pressure,Rn = 30.0,100.0,500.0 +VPD,Ga = 2.0, 0.1 +ET_pot,LE_pot = potential_ET(Tair,pressure,Rn,VPD, Ga, Val(:PenmanMonteith); + Gs_pot=0.5, infoGS=false) # now cross-check with the inverted equation -surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) +#Ga2 = surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) +#Ga2 ≈ GA +true +# output +true ``` """ -function potential_ET(Tair,pressure,Rn, VPD,Ga; - G=missing,S=missing, approach=Val(:Priestley-Taylor), - alpha=1.26,Gs_pot=0.6, - missing_G_as_NA=false, missing_S_as_NA=false, +function potential_ET(Tair, pressure, Rn, approach::Val{:PriestleyTaylor}; + G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, + infoGS=true, + kwargs...) + # + G_, S_ = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS) + potential_ET(Tair, pressure, Rn, G_, S_, approach; kwargs...) +end, +function potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; + alpha=1.26, Esat_formula=Val(:Sonntag_1990), constants=bigleaf_constants()) - - #TODO check_input(data,list(Tair,pressure,Rn,G,S)) - G,S = deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) - gamma = psychrometric_constant(Tair,pressure,constants) + # + gamma = psychrometric_constant(Tair,pressure;constants) + Delta = Esat_from_Tair_deriv(Tair; formula = Esat_formula,constants) + LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) + ET_pot = LE_to_ET(LE_pot,Tair) + (ET_pot = ET_pot, LE_pot = LE_pot) +end, +function potential_ET(Tair, pressure, Rn, VPD, Ga, approach::Val{:PenmanMonteith}; + G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, + infoGS=true, + kwargs...) + # + G_, S_ = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS) + G_tr, S_tr = 0,0 + potential_ET(Tair, pressure, Rn, VPD, Ga, G_tr, S_tr, approach; kwargs...) +end, +function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; + Gs_pot=0.6, + Esat_formula=Val(:Sonntag_1990), + constants=bigleaf_constants()) + # + gamma = psychrometric_constant(Tair,pressure;constants) Delta = Esat_from_Tair_deriv(Tair; formula = Esat_formula,constants) - # TODO replace by dispatch - if (approach == Val(:Priestley-Taylor)) - LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) - ET_pot = LE_to_ET(LE_pot,Tair) - elseif (approach == Val(:Penman-Monteith)) - #TODO check_input(data,list(Gs_pot,VPD,Ga)) - Gs_pot = mol_to_ms(Gs_pot,Tair,pressure;constants) - rho = air_density(Tair,pressure;constants) - LE_pot = (Delta * (Rn - G - S) + rho * constants[:cp] * VPD * Ga) / - (Delta + gamma * (1 + Ga / Gs_pot)) - ET_pot = LE_to_ET(LE_pot,Tair) - end + Gs_pot = mol_to_ms(Gs_pot,Tair,pressure;constants) + rho = air_density(Tair,pressure;constants) + LE_pot = (Delta * (Rn - G - S) + rho * constants[:cp] * VPD * Ga) / + (Delta + gamma * (1 + Ga / Gs_pot)) + ET_pot = LE_to_ET(LE_pot,Tair) (ET_pot = ET_pot, LE_pot = LE_pot) +end, +function potential_ET(df, approach::Val{:PriestleyTaylor}; + G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, infoGS=true, + kwargs...) + # + dfGS = fill_GS_missings(df, G,S, missing_G_as_NA, missing_S_as_NA; infoGS) + f(args...) = potential_ET(args..., approach; kwargs...) + select(hcat(select(df,:Tair, :pressure, :Rn), dfGS; copycols = false), + All() => ByRow(f) => AsTable + ) +end, +function potential_ET(df, approach::Val{:PenmanMonteith}; + G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, infoGS=true, + kwargs...) + # + dfGS = fill_GS_missings(df, G,S, missing_G_as_NA, missing_S_as_NA; infoGS) + f(args...) = potential_ET(args..., approach; kwargs...) + select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), + All() => ByRow(f) => AsTable + ) +end, +function potential_ET!(df, approach::Val{:PriestleyTaylor}; kwargs...) + f(args...) = potential_ET(args..., approach; kwargs...) + transform!(df, + [:Tair, :pressure, :Rn, :G, :S] => ByRow(f) => AsTable + ) +end, +function potential_ET!(df, approach::Val{:PenmanMonteith}; kwargs...) + f(args...) = potential_ET(args..., approach; kwargs...) + transform!(df, + [:Tair, :pressure, :Rn, :VPD, :Ga, :G, :S] => ByRow(f) => AsTable + ) end - """ TODO @@ -143,7 +225,7 @@ Evapotranspiration (ET) split up into imposed ET and equilibrium ET. # Details Total evapotranspiration can be written in the form (Jarvis & McNaughton 6): -``ET = \\Omega ET_eq + (1 - \\Omega)ET_imp`` +``ET = \\Omega ET_{eq} + (1 - \\Omega)ET_{imp}`` where ``\\Omega`` is the decoupling coefficient as calculated from [`decoupling`](@ref). `ET_eq` is the equilibrium evapotranspiration e, @@ -162,7 +244,7 @@ that would occur under fully coupled conditions (when Ga -> inf): where ``\\rho`` is the air density (kg m-3). -#Note +# Note Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) Aerodynamic conductance (Ga) can be calculated using erodynamic_conductance`](@ref). @@ -194,7 +276,7 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, constants=bigleaf_constants()) # #check_input(data,list(Tair,pressure,VPD,Rn,Gs,G,S)) - G,S = deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) + G,S = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) rho = air_density(Tair,pressure,constants) gamma = psychrometric_constant(Tair,pressure,constants) Delta = Esat_from_Tair_deriv(Tair,Esat_formula,constants) @@ -206,18 +288,84 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, (ET_pot = ET_pot, LE_pot = LE_pot, LE_imp = LE_imp) end -function deal_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) - if ismissing(G) - @info("Ground heat flux G is not provided and set to 0.") - G = 0 - elseif missing_G_as_NA == false - G = coalesce(G, 0) - end - if ismissing(S) - @info("Energy storage fluxes S are not provided and set to 0.") - S = 0 - elseif missing_S_as_NA false - S = coalesce(S, 0) - end - G,S + +function fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS=true) + # + G_ = ifelse( + ismissing(G), + (infoGS && @info("Ground heat flux G is not provided and set to 0."); 0.0), + ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) + ) + S_ = ifelse( + ismissing(S), + (infoGS && @info("Storage heat flux S is not provided and set to 0."); 0.0), + ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) + ) + G_,S_ +end + +function fill_GS_missings(df::DataFrame, G,S, missing_G_as_NA, missing_S_as_NA; infoGS=true) + nout = nrow(df) + G_ = ifelse( + ismissing(G), + (infoGS && @info("Ground heat flux G is not provided and set to 0."); Zeros(nout)), + @. ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) + ) + S_ = ifelse( + ismissing(S), + (infoGS && @info("Storage heat flux S is not provided and set to 0."); Zeros(nout)), + @. ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) + ) + DataFrame(G = G_, S = S_) +end + +""" + fill_GS_missings!(df::DataFrame, G=df.G, S=df.S, + missing_G_as_NA=false, missing_S_as_NA=false; infoGS=true) + +Fill missing values in ground heat flux and storage flux. + +# Arguments +- df: DataFrame where columns G and S should be updated +- G: Ground heat flux +- S: Storage heat flux +- missing_G_as_NA: set to true to not fill NAs in G to propagate to computations +- missing_S_as_NA: set to true to not fill NAs in S to propagate to computations +- infoGS: set to false to avoid log info message + +```@example +true +# using DataFrames +# df = DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0) +# fill_GS_missings!(df, missing, missing) +# all( df.S .== df.G .== 0.0) +``` +""" +function fill_GS_missings!(df::DataFrame, G=df.G, S=df.S, + missing_G_as_NA=false, missing_S_as_NA=false; infoGS=true + ) + nout = nrow(df) + df.G .= ifelse( + ismissing(G), + (infoGS && @info("Ground heat flux G is not provided and set to 0."); Zeros(nout)), + @. ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) + ) + df.S .= ifelse( + ismissing(S), + (infoGS && @info("Storage heat flux S is not provided and set to 0."); Zeros(nout)), + @. ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) + ) + df +end + +""" + TODO; implement surface_conductance. + +This stub is there to satisfy links im Help-pages. +""" +function surface_conductance() + error("not yet implemented.") end + + + diff --git a/src/sun_position.jl b/src/sun_position.jl index d04cfe2..4b3aa33 100644 --- a/src/sun_position.jl +++ b/src/sun_position.jl @@ -8,7 +8,7 @@ Compute the Sun position at given time and observer coordinates in horizontal co - `lat`, `long`: latitude and longitude in degree # Value -`SLVector`: sun position with entries +`NamedTuple`: sun position with entries - `altitude`: angle above the horizon [rad]. - `azimuth`: angle ange the horizon plane eastwards of north [rad] - `hourangle`: [rad] as output by AstroLib.eq2hor @@ -69,7 +69,7 @@ The algorithm was adapted from [Vallado 2013, p. 277-279]. - `JD`: time given as Julian Day . # Value -`SLVector`: sun position where +`NamedTuple`: sun position where - Ecliptic coordinates (1:3) - λ: Ecliptic longitude of the Sun [rad]. - β: Ecliptic latitude of the Sun [rad] is assumed 0. diff --git a/src/util.jl b/src/util.jl index 0e3c412..aece4e3 100644 --- a/src/util.jl +++ b/src/util.jl @@ -1,18 +1,16 @@ -export toDataFrame +# """ +# toDataFrame(x::AbstractVector{<:SLArray}) -""" - toDataFrame(x::AbstractVector{<:SLArray}) - -Convert an Vector{SLVector} to DataFrame. +# Convert an Vector{SLVector} to DataFrame. -Such objects are returned when broadcasting on a function -that retuns an SLVector. -""" -function toDataFrame(x::AbstractVector{<:SLArray}) - names = collect(keys(first(x))) - df = DataFrame(Tables.table(VectorOfArray(x)')) - rename!(df, names) -end +# Such objects are returned when broadcasting on a function +# that retuns an SLVector. +# """ +# function toDataFrame(x::AbstractVector{<:SLArray}) +# names = collect(keys(first(x))) +# df = DataFrame(Tables.table(VectorOfArray(x)')) +# rename!(df, names) +# end # function vec_tuple_to_DataFrame(data; names = string.(keys(first(data)))) # DataFrame(collect(map(idx -> getindex.(data, idx), eachindex(collect(first(data))))), collect(names)) diff --git a/test/evapotranspiration.jl b/test/evapotranspiration.jl new file mode 100644 index 0000000..455b842 --- /dev/null +++ b/test/evapotranspiration.jl @@ -0,0 +1,46 @@ +@testset "potential_ET scalars" begin + Tair,pressure,Rn = 30.0,100.0,500.0 + ET_pot, LE_pot = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor); alpha=1.26) + @test ≈(ET_pot, 0.0002035969; rtol = 1e-5) + @test ≈(LE_pot, 494.7202; rtol = 1e-5) + # + VPD,Ga = 2.0, 0.1 + ET_pot,LE_pot = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(Tair,pressure,Rn,VPD,Ga, Val(:PenmanMonteith)) + # + #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) +end + +@testset "potential_ET dataframe" begin + df = DataFrame( + Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0) + df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df, Val(:PriestleyTaylor); alpha=1.26) + # non-mutating + @test ncol(df) == 3 + @test nrow(df_ET) == nrow(df) + @test ≈(last(df_ET).ET_pot, 0.0002035969; rtol = 1e-5) + @test ≈(last(df_ET).LE_pot, 494.7202; rtol = 1e-5) + # + df2 = copy(df) + df2.VPD .= 2.0 + df2.Ga .= 0.1 + # df2 = transform(df, + # [] => ByRow(() -> 2.0) => :VPD, + # [] => ByRow(() -> 0.1) => :Ga, + # ) + df_ET2 = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df2, Val(:PenmanMonteith)) + @test ncol(df2) == 5 + @test nrow(df_ET2) == nrow(df2) + #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) + # + # mutating + dfm = copy(df) + @test_throws Exception potential_ET!( + dfm, Val(:PriestleyTaylor); alpha=1.26) + dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") hcat(dfm, Bigleaf.fill_GS_missings(dfm, missing, missing, false, false); copycols = false) + potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) + @test ncol(dfm) == 3+4 # four columns added: G,S, ET_pot, LE_pot + dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") fill_GS_missings!(copy(df2), missing, missing) + potential_ET!(dfm, Val(:PenmanMonteith)) + @test ncol(dfm) == ncol(df2)+4 # four columns added: G,S, ET_pot, LE_pot +end + diff --git a/test/runtests.jl b/test/runtests.jl index 93f6cf3..a51cbf8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,4 +12,7 @@ using Pipe, DataFrames, Dates @testset "sun_position" begin include("sun_position.jl") end + @testset "evapotranspiration" begin + include("evapotranspiration.jl") + end end From b85c328f2d81f49eb6dca5b4aa6ebc207f490ac6 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 10:05:42 +0200 Subject: [PATCH 08/43] modify handling of G and S defaults TOOD adapt docu --- src/evapotranspiration.jl | 121 ++++++++++++++++++++----------------- test/evapotranspiration.jl | 50 ++++++++++++--- 2 files changed, 104 insertions(+), 67 deletions(-) diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index f26b5d0..e34ff6e 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -1,9 +1,6 @@ """ potential_ET(Tair, pressure, Rn, ::Val{:PriestleyTaylor}; ...) - potential_ET(Tair, pressure, Rn, VPD, Ga, ::Val{:PenmanMonteith}; ...) - potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; ...) - potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; ...) potential_ET(df, approach; ...) potential_ET!(df, approach; ...) @@ -17,12 +14,12 @@ the Penman-Monteith equation with a prescribed surface conductance. - Rn: Net radiation (W m-2) - VPD: Vapor pressure deficit (kPa) - Ga: Aerodynamic conductance to heat/water vapor (m s-1) -- G: Ground heat flux (W m-2) -- S: Sum of all storage fluxes (W m-2) - approach: Approach used. Either `Val(:PriestleyTaylor)` (default), or `Val(:PenmanMonteith)`. - df: Data_frame or matrix containing all required variables; optional optional: +- G: Ground heat flux (W m-2). Defaults to zero. +- S: Sum of all storage fluxes (W m-2) . Defaults to zero. - `Esat_formula`: formula used in [`Esat_from_Tair`](@ref) - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - cp - specific heat of air for constant pressure (J K-1 kg-1) @@ -102,8 +99,7 @@ for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. # Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 # using Priestley-Taylor: Tair,pressure,Rn = 30.0,100.0,500.0 -ET_pot, LE_pot = potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor); - alpha=1.26, infoGS = false) +ET_pot, LE_pot = potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor)) ≈(ET_pot, 0.0002035969; rtol = 1e-5) # output true @@ -124,12 +120,9 @@ true ``` """ function potential_ET(Tair, pressure, Rn, approach::Val{:PriestleyTaylor}; - G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, - infoGS=true, - kwargs...) + G=zero(Tair),S=zero(Tair), kwargs...) # - G_, S_ = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS) - potential_ET(Tair, pressure, Rn, G_, S_, approach; kwargs...) + potential_ET(Tair, pressure, Rn, G, S, approach; kwargs...) end, function potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; alpha=1.26, @@ -143,13 +136,9 @@ function potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; (ET_pot = ET_pot, LE_pot = LE_pot) end, function potential_ET(Tair, pressure, Rn, VPD, Ga, approach::Val{:PenmanMonteith}; - G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, - infoGS=true, - kwargs...) + G=zero(Tair),S=zero(Tair), kwargs...) # - G_, S_ = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS) - G_tr, S_tr = 0,0 - potential_ET(Tair, pressure, Rn, VPD, Ga, G_tr, S_tr, approach; kwargs...) + potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, approach; kwargs...) end, function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; Gs_pot=0.6, @@ -166,10 +155,9 @@ function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; (ET_pot = ET_pot, LE_pot = LE_pot) end, function potential_ET(df, approach::Val{:PriestleyTaylor}; - G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, infoGS=true, - kwargs...) + G=missing,S=missing, infoGS=true, kwargs...) # - dfGS = fill_GS_missings(df, G,S, missing_G_as_NA, missing_S_as_NA; infoGS) + dfGS = get_df_GS(df, G,S; infoGS) f(args...) = potential_ET(args..., approach; kwargs...) select(hcat(select(df,:Tair, :pressure, :Rn), dfGS; copycols = false), All() => ByRow(f) => AsTable @@ -179,25 +167,74 @@ function potential_ET(df, approach::Val{:PenmanMonteith}; G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, infoGS=true, kwargs...) # - dfGS = fill_GS_missings(df, G,S, missing_G_as_NA, missing_S_as_NA; infoGS) + dfGS = get_df_GS(df, G,S; infoGS) f(args...) = potential_ET(args..., approach; kwargs...) select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), All() => ByRow(f) => AsTable ) end, -function potential_ET!(df, approach::Val{:PriestleyTaylor}; kwargs...) +function potential_ET!(df, approach::Val{:PriestleyTaylor}; + G=missing,S=missing, infoGS=true, kwargs...) + dfGS = get_df_GS(df, G,S; infoGS) + # temporarily add G and S to the DataFrame to mutate + df._tmp_G .= dfGS.G + df._tmp_S .= dfGS.S f(args...) = potential_ET(args..., approach; kwargs...) transform!(df, - [:Tair, :pressure, :Rn, :G, :S] => ByRow(f) => AsTable - ) + [:Tair, :pressure, :Rn, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable + ) + select!(df, Not([:_tmp_G, :_tmp_S])) end, -function potential_ET!(df, approach::Val{:PenmanMonteith}; kwargs...) +function potential_ET!(df, approach::Val{:PenmanMonteith}; + G=missing,S=missing, infoGS=true, kwargs...) + dfGS = get_df_GS(df, G,S; infoGS) + df._tmp_G .= dfGS.G + df._tmp_S .= dfGS.S f(args...) = potential_ET(args..., approach; kwargs...) transform!(df, - [:Tair, :pressure, :Rn, :VPD, :Ga, :G, :S] => ByRow(f) => AsTable + [:Tair, :pressure, :Rn, :VPD, :Ga, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable ) + select!(df, Not([:_tmp_G, :_tmp_S])) +end + +function get_df_GS(df, G, S; infoGS=true) + nout = nrow(df) + G_ = if ismissing(G) + infoGS && @info("Ground heat flux G is not provided and set to 0.") + Zeros(nout) + else + G + end + S_ = if ismissing(S) + infoGS && @info("Storage heat flux S is not provided and set to 0.") + Zeros(nout) + else + S + end + DataFrame(G = G_, S = S_) end +# function fill_GS_missings(df::DataFrame, G,S, missing_G_as_NA, missing_S_as_NA; infoGS=true) +# nout = nrow(df) +# G_ = ifelse( +# ismissing(G), +# (infoGS && @info("Ground heat flux G is not provided and set to 0."); Zeros(nout)), +# @. ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) +# ) +# S_ = ifelse( +# ismissing(S), +# (infoGS && @info("Storage heat flux S is not provided and set to 0."); Zeros(nout)), +# @. ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) +# ) +# DataFrame(G = G_, S = S_) +# end + +function fill_vec(G; is_replace_missing = true, fillvalue = zero(G)) + @. ifelse(is_replace_missing, coalesce(G, fillvalue), G) +end + + + """ TODO @@ -289,36 +326,6 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, end -function fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA; infoGS=true) - # - G_ = ifelse( - ismissing(G), - (infoGS && @info("Ground heat flux G is not provided and set to 0."); 0.0), - ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) - ) - S_ = ifelse( - ismissing(S), - (infoGS && @info("Storage heat flux S is not provided and set to 0."); 0.0), - ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) - ) - G_,S_ -end - -function fill_GS_missings(df::DataFrame, G,S, missing_G_as_NA, missing_S_as_NA; infoGS=true) - nout = nrow(df) - G_ = ifelse( - ismissing(G), - (infoGS && @info("Ground heat flux G is not provided and set to 0."); Zeros(nout)), - @. ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) - ) - S_ = ifelse( - ismissing(S), - (infoGS && @info("Storage heat flux S is not provided and set to 0."); Zeros(nout)), - @. ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) - ) - DataFrame(G = G_, S = S_) -end - """ fill_GS_missings!(df::DataFrame, G=df.G, S=df.S, missing_G_as_NA=false, missing_S_as_NA=false; infoGS=true) diff --git a/test/evapotranspiration.jl b/test/evapotranspiration.jl index 455b842..3cfe069 100644 --- a/test/evapotranspiration.jl +++ b/test/evapotranspiration.jl @@ -1,11 +1,11 @@ @testset "potential_ET scalars" begin Tair,pressure,Rn = 30.0,100.0,500.0 - ET_pot, LE_pot = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor); alpha=1.26) + ET_pot, LE_pot = potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor); alpha=1.26) @test ≈(ET_pot, 0.0002035969; rtol = 1e-5) @test ≈(LE_pot, 494.7202; rtol = 1e-5) # VPD,Ga = 2.0, 0.1 - ET_pot,LE_pot = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(Tair,pressure,Rn,VPD,Ga, Val(:PenmanMonteith)) + ET_pot,LE_pot = potential_ET(Tair,pressure,Rn,VPD,Ga, Val(:PenmanMonteith)) # #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) end @@ -34,13 +34,43 @@ end # # mutating dfm = copy(df) - @test_throws Exception potential_ET!( - dfm, Val(:PriestleyTaylor); alpha=1.26) - dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") hcat(dfm, Bigleaf.fill_GS_missings(dfm, missing, missing, false, false); copycols = false) - potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) - @test ncol(dfm) == 3+4 # four columns added: G,S, ET_pot, LE_pot - dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") fill_GS_missings!(copy(df2), missing, missing) - potential_ET!(dfm, Val(:PenmanMonteith)) - @test ncol(dfm) == ncol(df2)+4 # four columns added: G,S, ET_pot, LE_pot + #@test_throws Exception + @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) + #dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") hcat(dfm, Bigleaf.fill_GS_missings(dfm, missing, missing, false, false); copycols = false) + #potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) + @test ncol(dfm) == 3+2 # two columns added: ET_pot, LE_pot + dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET!(copy(df2), Val(:PenmanMonteith)) + @test ncol(dfm) == ncol(df2)+2 +end + +@testset "potential_ET dataframe missings in G" begin + df = @pipe DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0, G = 105.0) |> + allowmissing(_, Cols(:G)) + df.G[1] = missing + df_ET = @test_logs (:info,r"S is not provided") potential_ET(df, Val(:PriestleyTaylor); G = df.G) + # non-mutating + @test ncol(df) == 4 + @test nrow(df_ET) == nrow(df) + @test last(df_ET).ET_pot < 0.0002035969 # smaller because less energy available + @test ismissing(first(df_ET).LE_pot) + # + df2 = copy(df) + df2.VPD .= 2.0 + df2.Ga .= 0.1 + df_ET2 = @test_logs (:info,r"G is not provided") potential_ET(df2, Val(:PenmanMonteith), S = df.G) + @test ncol(df2) == 6 + @test nrow(df_ET2) == nrow(df2) + @test ismissing(first(df_ET).LE_pot) + #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) + # + # mutating + dfm = copy(df) + #@test_throws Exception + @test_logs (:info,r"G is not provided") potential_ET!(dfm, Val(:PriestleyTaylor); S = dfm.G) + #dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") hcat(dfm, Bigleaf.fill_GS_missings(dfm, missing, missing, false, false); copycols = false) + #potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) + @test ncol(dfm) == ncol(df)+2 # two columns added: ET_pot, LE_pot + dfm = @test_logs (:info,r"S is not provided") potential_ET!(copy(df2), Val(:PenmanMonteith); G = dfm.G) + @test ncol(dfm) == ncol(df2)+2 end From 970a375018c7be92e63a5bd706c47237195f7796 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 13:36:12 +0200 Subject: [PATCH 09/43] test and docu equilibrium_imposed_ET --- .gitignore | 1 + docs/src/evapotranspiration.md | 2 - docs/src/walkthrough.md | 22 +++- src/Bigleaf.jl | 4 +- src/evapotranspiration.jl | 222 ++++++++++++++++++--------------- test/evapotranspiration.jl | 25 ++++ 6 files changed, 165 insertions(+), 111 deletions(-) diff --git a/.gitignore b/.gitignore index a22dafe..9117cbf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /Manifest.toml /docs/build/ /tmp +docs/Manifest.toml diff --git a/docs/src/evapotranspiration.md b/docs/src/evapotranspiration.md index fc755c6..6dfa6ab 100644 --- a/docs/src/evapotranspiration.md +++ b/docs/src/evapotranspiration.md @@ -6,6 +6,4 @@ Pages = ["evapotranspiration.md",] ```@docs potential_ET equilibrium_imposed_ET -fill_GS_missings! -surface_conductance ``` \ No newline at end of file diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index e6004f6..e33020b 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -98,14 +98,30 @@ potential_ET(tha, Val(:PriestleyTaylor)) potential_ET(transform(tha, :Tair => x -> 25.0; renamecols=false), Val(:PriestleyTaylor)) # varying one input only Tair_vec = 10.0:1.0:20.0 -DataFrame(potential_ET.(Tair_vec, pressure, Rn, Val(:PriestleyTaylor); infoGS=false)) +DataFrame(potential_ET.(Tair_vec, pressure, Rn, Val(:PriestleyTaylor))) nothing # hide ``` ## Ground heat flux and storage fluxes -Many functions require the available energy ($A$), which is defined as ($A = R_n - G - S$, all in $\text{W m}^{-2}$), where $R_n$ is the net radiation, $G$ is the ground heat flux, and $S$ is the sum of all storage fluxes of the ecosystem (see e.g. Leuning et al. 2012 for an overview). For some sites, $G$ is not available, and for most sites, only a few components of $S$ are measured. In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results might be (slightly) biased), but special options exist for the treatment of missing $S$ and $G$ values. If the options `missing_G_as_NA = TRUE` or `missing_S_as_NA = TRUE`, then the output variable is not calculated for that time period. Otherwise missing $S$ and $G$ values are set to O automatically. Please note that the default is to ignore $S$ and $G$ values. If $G$ and/or $S$ are available, they usually have to be added explicitly to the function call by explicitly using the optional arguments. The positional forms do not check for missing -values. +Many functions require the available energy ($A$), which is defined as ($A = R_n - G - S$, +all in $\text{W m}^{-2}$), where $R_n$ is the net radiation, $G$ is the ground heat flux, +and $S$ is the sum of all storage fluxes of the ecosystem +(see e.g. Leuning et al. 2012 for an overview). For some sites, $G$ is not available, +and for most sites, only a few components of $S$ are measured. + +In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results might be (slightly) biased), but special options exist for the treatment of missing $S$ and $G$ values. + +Note that the default for G and S in the dataframe variant is missing (and assumed zero), +even if those columns are +present in the DataFrame. You need to explictly pass those columns with the optional +arguments: e.g. `potential_ET(df, Val(:PriestleyTaylor); G = df.G)` + +Note that in difference to the bigleaf R package missing entries in a provide +vector are not relaced by zero by default. +You need to explitly use coalesce when specifying a ground heat flux +for which missings should be replaced by zero: `;G = coalesce(df.G, zero(df.G))` + # Function walkthrough # diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index b3b0eaa..5cd056e 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -23,8 +23,8 @@ export air_density, pressure_from_elevation, psychrometric_constant, wetbulb_temp_from_e_Tair_gamma, wetbulb_temp export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour -export potential_ET, potential_ET!, fill_GS_missings!, equilibrium_imposed_ET, - surface_conductance +export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ET! +#export surface_conductance include("util.jl") include("bigleaf_constants.jl") diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index e34ff6e..b95f3be 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -29,13 +29,9 @@ optional: - Rd - gas constant of dry air (J kg-1 K-1) - Rgas - universal gas constant (J mol-1 K-1) - Kelvin - conversion degree Celsius to Kelvin -additional optional arguments if G and S are not specified by postition -and in the non-mutating DataFrame variant. -- G and S: as in the positional forms, but checked for missing or containing missings -- missing_G_as_NA: if `true`, missing optional G are treated as `NA`s, - otherwise set to 0. -- missing_S_as_NA: if `true`, missing optional S are treated as `NA`s, - otherwise set to 0. +additional optional arguments with data.frame variants +- infoGS=ture: sete to false to avoid infor log-message if G or S is not + specified additional optional for PriestleyTaylor: - alpha: Priestley-Taylor coefficient additional optional for PenmanMonteith: @@ -62,25 +58,27 @@ and ``\\gamma`` is the psychrometric constant (kPa K-1). The value of `Gs_pot` is typically a maximum value of Gs observed at the site, e.g. the 90th percentile of Gs within the growing season. -If keyword arguments `G` or `S` in the are set to `missing`, ground heat flux and storage flux -are assumed zero respectively. Note, that these keyword argument are not available in the -mutating form, where the caller has to add tree these columns before -(see [fill_GS_missings](@ref)). - +Ground heat flux and storage heat flux `G` or `S` are provided as optional +arguments. In the input-explicit variants, they default to zero. +In the data-frame arguments, they default to missing, which results +in assuming them to be zero which is displayed in a log-message. +Note that in difference ot the bigleaf R package, you explitly need to +care for missing values (see examples). + Both methods are provided with several forms: - all required inputs as positional arguments -- provideing G and S as positional arguments with handing of missing values - providing a DataFrame with columns corresponding to required inputs - + returning a DataFrame with only the result columns. +- a mutating DataFrame version that returns the original DataFrame with + result columns added or modified. # Value NamedTuple or DataFrame with the following entries: - `ET_pot`: Potential evapotranspiration (kg m-2 s-1) - `LE_pot`: Potential latent heat flux (W m-2) -For the mutating form, the original df with columns `ET_pot`, `LE_pot`, `G`, and `S` +For the mutating form, the original df with columns `ET_pot`, `LE_pot` updated or added. - # References - Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. @@ -94,23 +92,22 @@ for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. [`surface_conductance`](@ref) # Examples - +Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 +using Priestley-Taylor: ```jldoctest; output=false -# Calculate potential ET of a surface that receives a net radiation of 500 Wm-2 -# using Priestley-Taylor: Tair,pressure,Rn = 30.0,100.0,500.0 ET_pot, LE_pot = potential_ET(Tair,pressure,Rn, Val(:PriestleyTaylor)) ≈(ET_pot, 0.0002035969; rtol = 1e-5) # output true ``` -```@jldoctest; output=false -# Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) -# using Penman-Monteith: + +Calculate potential ET for a surface with known Gs (0.5 mol m-2 s-1) and Ga (0.1 m s-1) +using Penman-Monteith: +```jldoctest; output=false Tair,pressure,Rn = 30.0,100.0,500.0 -VPD,Ga = 2.0, 0.1 -ET_pot,LE_pot = potential_ET(Tair,pressure,Rn,VPD, Ga, Val(:PenmanMonteith); - Gs_pot=0.5, infoGS=false) +VPD, Ga = 2.0, 0.1 +ET_pot, LE_pot = potential_ET(Tair,pressure,Rn,VPD, Ga, Val(:PenmanMonteith); Gs_pot=0.5,) # now cross-check with the inverted equation #Ga2 = surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) #Ga2 ≈ GA @@ -118,6 +115,23 @@ true # output true ``` + +DataFrame variant with explicitly replacing missings: +```jldoctest; output=false +using DataFrames +df = DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0, G = 105.0, VPD = 2.0, Ga = 0.1) +allowmissing!(df, Cols(:G)); df.G[1] = missing +# +# need to provide G explicitly +df_ET = potential_ET(df, Val(:PriestleyTaylor); G = df.G, infoGS = false) +ismissing(df_ET.ET_pot[1]) +# +# use coalesce to replace missing values +df_ET = potential_ET(df, Val(:PriestleyTaylor); G = coalesce.(df.G, zero(df.G)), infoGS = false) +!ismissing(df_ET.ET_pot[1]) +# output +true +``` """ function potential_ET(Tair, pressure, Rn, approach::Val{:PriestleyTaylor}; G=zero(Tair),S=zero(Tair), kwargs...) @@ -164,11 +178,12 @@ function potential_ET(df, approach::Val{:PriestleyTaylor}; ) end, function potential_ET(df, approach::Val{:PenmanMonteith}; - G=missing,S=missing, missing_G_as_NA=false, missing_S_as_NA=false, infoGS=true, - kwargs...) + G=missing,S=missing, infoGS=true, kwargs...) # dfGS = get_df_GS(df, G,S; infoGS) - f(args...) = potential_ET(args..., approach; kwargs...) + function f(args...) + potential_ET(args..., approach; kwargs...) + end select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), All() => ByRow(f) => AsTable ) @@ -229,32 +244,28 @@ end # DataFrame(G = G_, S = S_) # end -function fill_vec(G; is_replace_missing = true, fillvalue = zero(G)) - @. ifelse(is_replace_missing, coalesce(G, fillvalue), G) -end - - +# function fill_vec(G; is_replace_missing = true, fillvalue = zero(G)) +# @. ifelse(is_replace_missing, coalesce(G, fillvalue), G) +# end """ - TODO + equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn; ...) + equilibrium_imposed_ET(df; ...) + equilibrium_imposed_ET!(df; ...) Evapotranspiration (ET) split up into imposed ET and equilibrium ET. +# Argumens - Tair: Air temperature (deg C) - pressure: Atmospheric pressure (kPa) - VPD: Air vapor pressure deficit (kPa) - Gs: surface conductance to water vapor (m s-1) - Rn: Net radiation (W m-2) +optional - G: Ground heat flux (W m-2); optional - S: Sum of all storage fluxes (W m-2); optional -- missing_G_as_NA: if `TRUE`, missing G are treated as `NA`s, otherwise set 0. -- missing_S_as_NA: if `TRUE`, missing S are treated as `NA`s, otherwise set 0. -- Esat_formula: Optional: formula to be used for the calculation of esat and slope of esat. - One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, `"Allen_1998"`. - See [`Esat_slope`](@ref). +- `Esat_formula`: formula used in [`Esat_from_Tair`](@ref) - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - -- constants - cp - specific heat of air for constant pressure (J K-1 kg-1) - eps - ratio of the molecular weight of water vapor to dry (-) - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) @@ -265,106 +276,102 @@ Total evapotranspiration can be written in the form (Jarvis & McNaughton 6): ``ET = \\Omega ET_{eq} + (1 - \\Omega)ET_{imp}`` where ``\\Omega`` is the decoupling coefficient as calculated from -[`decoupling`](@ref). `ET_eq` is the equilibrium evapotranspiration e, -the ET rate that would occur under uncoupled conditions, where tbudget +[`decoupling`](@ref). `ET_eq` is the equilibrium evapotranspiration i.e., +the ET rate that would occur under uncoupled conditions, where the budget is dominated by radiation (when Ga -> 0): - ``ET_eq = (\\Delta * (Rn - G - S) * \\lambda) / (\\Del\\gamma) +``ET_{eq} = (\\Delta * (Rn - G - S) * \\lambda) / ( \\Delta \\gamma)`` where ``\\Delta`` is the slope of the saturation vapor pressur(kPa K-1), -``\\lambda`` is the latent heat of vaporization (J kg-1), and \\gamma`` +``\\lambda`` is the latent heat of vaporization (J kg-1), and ``\\gamma`` is the psychrometric constant (kPa K-1). `ET_imp` is the imposed evapotranspiration rate, the ET rate that would occur under fully coupled conditions (when Ga -> inf): - ``ET_imp = (\\rho * cp * VPD * Gs * \\lambda) / \\gamma`` +``ET_{imp} = (\\rho * cp * VPD * Gs * \\lambda) / \\gamma`` where ``\\rho`` is the air density (kg m-3). # Note Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) -Aerodynamic conductance (Ga) can be calculated using erodynamic_conductance`](@ref). +Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). # Value -A DataFrame with the following columns: -- ET_eq: Equilibrium ET (kg m-2 s-1) -- ET_imp: Imposed ET (kg m-2 s-1) -- LE_eq: Equilibrium LE (W m-2) -- LE_imp: Imposed LE (W m-2) +A `NamedTuple` or `DataFrame` with the following columns: +- `ET_eq`: Equilibrium ET (kg m-2 s-1) +- `ET_imp`: Imposed ET (kg m-2 s-1) +- `LE_eq`: Equilibrium LE (W m-2) +- `LE_imp`: Imposed LE (W m-2) -#References +# References - Jarvis, P_G., McNaughton, K_G., 1986: Stomatal control of transpiration: -scaling up from leaf to region. Advances in Ecological Rese1-49. + scaling up from leaf to region. Advances in Ecological Rese1-49. - Monteith, J_L., Unsworth, M_H., 2008: Principles of ironmPhysics. -3rd edition. Academic Press, London. - -#See also -[`decoupling`](@ref) + 3rd edition. Academic Press, London. -```@example; output = false -df = DataFrame(Tair=20,pressure=100,VPD=seq(0.5,4,0.5), - Gs_ms=seq(0.01,0.002,length_out=8),Rn=seq(50,400,50) -equilibrium_imposed_ET(df) +# Examples +```jldoctest; output = false +Tair,pressure,Rn, VPD, Gs = 20.0,100.0,50.0, 0.5, 0.01 +ET_eq, ET_imp, LE_eq, LE_imp = equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn) +≈(ET_eq, 1.399424e-05; rtol = 1e-5) +# output +true ``` """ -function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, - G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, +function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn; + G=zero(Tair),S=zero(Tair), kwargs...) + equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; kwargs...) +end, +function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; Esat_formula=Val(:Sonntag_1990), constants=bigleaf_constants()) # - #check_input(data,list(Tair,pressure,VPD,Rn,Gs,G,S)) - G,S = fill_GS_missings(G,S,missing_G_as_NA, missing_S_as_NA) - rho = air_density(Tair,pressure,constants) - gamma = psychrometric_constant(Tair,pressure,constants) - Delta = Esat_from_Tair_deriv(Tair,Esat_formula,constants) + rho = air_density(Tair, pressure; constants) + gamma = psychrometric_constant(Tair, pressure; constants) + Delta = Esat_from_Tair_deriv(Tair; formula = Esat_formula, constants) LE_eq = (Delta * (Rn - G - S)) / (gamma + Delta) LE_imp = (rho * constants[:cp] * Gs * VPD) / gamma # ET_imp = LE_to_ET(LE_imp,Tair) ET_eq = LE_to_ET(LE_eq,Tair) - (ET_pot = ET_pot, LE_pot = LE_pot, LE_imp = LE_imp) + (;ET_eq, ET_imp, LE_eq, LE_imp) +end, +function equilibrium_imposed_ET(df; + G=missing,S=missing, infoGS=true, kwargs...) + # + dfGS = get_df_GS(df, G,S; infoGS) + function f(args...) + equilibrium_imposed_ET(args...; kwargs...) + end + dfb = hcat(select(df,:Tair, :pressure, :VPD, :Gs, :Rn), dfGS; copycols = false) + select(dfb, All() => ByRow(f) => AsTable ) +end, +function equilibrium_imposed_ET!(df; + G=missing,S=missing, infoGS=true, kwargs...) + # + dfGS = get_df_GS(df, G,S; infoGS) + # temporarily add G and S to the DataFrame to mutate + df._tmp_G .= dfGS.G + df._tmp_S .= dfGS.S + f(args...) = equilibrium_imposed_ET(args...; kwargs...) + transform!(df, + [:Tair, :pressure, :VPD, :Gs, :Rn, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable + ) + select!(df, Not([:_tmp_G, :_tmp_S])) end -""" - fill_GS_missings!(df::DataFrame, G=df.G, S=df.S, - missing_G_as_NA=false, missing_S_as_NA=false; infoGS=true) -Fill missing values in ground heat flux and storage flux. - -# Arguments -- df: DataFrame where columns G and S should be updated -- G: Ground heat flux -- S: Storage heat flux -- missing_G_as_NA: set to true to not fill NAs in G to propagate to computations -- missing_S_as_NA: set to true to not fill NAs in S to propagate to computations -- infoGS: set to false to avoid log info message +""" + TODO; implement decoupling. -```@example -true -# using DataFrames -# df = DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0) -# fill_GS_missings!(df, missing, missing) -# all( df.S .== df.G .== 0.0) -``` +This stub is there to satisfy links im Help-pages. """ -function fill_GS_missings!(df::DataFrame, G=df.G, S=df.S, - missing_G_as_NA=false, missing_S_as_NA=false; infoGS=true - ) - nout = nrow(df) - df.G .= ifelse( - ismissing(G), - (infoGS && @info("Ground heat flux G is not provided and set to 0."); Zeros(nout)), - @. ifelse(missing_G_as_NA, G, coalesce(G, 0.0)) - ) - df.S .= ifelse( - ismissing(S), - (infoGS && @info("Storage heat flux S is not provided and set to 0."); Zeros(nout)), - @. ifelse(missing_S_as_NA, G, coalesce(S, 0.0)) - ) - df +function decoupling() + error("not yet implemented.") end + """ TODO; implement surface_conductance. @@ -374,5 +381,12 @@ function surface_conductance() error("not yet implemented.") end +""" + TODO; implement aerodynamic_conductance. +This stub is there to satisfy links im Help-pages. +""" +function aerodynamic_conductance() + error("not yet implemented.") +end diff --git a/test/evapotranspiration.jl b/test/evapotranspiration.jl index 3cfe069..04680c1 100644 --- a/test/evapotranspiration.jl +++ b/test/evapotranspiration.jl @@ -74,3 +74,28 @@ end @test ncol(dfm) == ncol(df2)+2 end +@testset "equilibrium_imposed_ET scalars" begin + # regression from R package example + Tair,pressure,Rn, VPD, Gs = 20.0,100.0,50.0, 0.5, 0.01 + ET_eq, ET_imp, LE_eq, LE_imp = equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn) + @test ≈(ET_eq, 1.399424e-05; rtol = 1e-5) + @test ≈(ET_imp, 3.695727e-05; rtol = 1e-5) + @test ≈(LE_eq, 34.33628; rtol = 1e-5) + @test ≈(LE_imp, 90.67837; rtol = 1e-5) + # + df = DataFrame(Tair = 20.0:1.0:22.0, pressure = 100.0, Rn = 50.0, VPD = 0.5, Gs = 0.01) + ncoldf0 = ncol(df) + df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") equilibrium_imposed_ET(df) + @test ncol(df) == ncoldf0 + @test nrow(df_ET) == nrow(df) + @test ≈(first(df_ET).ET_eq, ET_eq) + @test ≈(first(df_ET).ET_imp, ET_imp) + # + dfm = copy(df) + dfm_ET = equilibrium_imposed_ET!(dfm; infoGS = false) + @test ncol(dfm) == ncoldf0 + 4 + @test nrow(dfm) == nrow(df) + @test ≈(first(dfm_ET).ET_eq, ET_eq) + @test ≈(first(dfm_ET).ET_imp, ET_imp) + #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) +end \ No newline at end of file From c0cfa0f9d5f8609c7cd78cc2a0a928986818fa8f Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 14:43:28 +0200 Subject: [PATCH 10/43] revert to old syntax of adding broadcasted columns to DataFrame. Tests failed in Julia 1..3 --- src/evapotranspiration.jl | 12 ++++++------ test/evapotranspiration.jl | 10 ++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index b95f3be..e2a30f7 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -192,8 +192,8 @@ function potential_ET!(df, approach::Val{:PriestleyTaylor}; G=missing,S=missing, infoGS=true, kwargs...) dfGS = get_df_GS(df, G,S; infoGS) # temporarily add G and S to the DataFrame to mutate - df._tmp_G .= dfGS.G - df._tmp_S .= dfGS.S + df[!, :_tmp_G] .= dfGS.G + df[!, :_tmp_S] .= dfGS.S f(args...) = potential_ET(args..., approach; kwargs...) transform!(df, [:Tair, :pressure, :Rn, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable @@ -203,8 +203,8 @@ end, function potential_ET!(df, approach::Val{:PenmanMonteith}; G=missing,S=missing, infoGS=true, kwargs...) dfGS = get_df_GS(df, G,S; infoGS) - df._tmp_G .= dfGS.G - df._tmp_S .= dfGS.S + df[!, :_tmp_G] .= dfGS.G + df[!, :_tmp_S] .= dfGS.S f(args...) = potential_ET(args..., approach; kwargs...) transform!(df, [:Tair, :pressure, :Rn, :VPD, :Ga, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable @@ -351,8 +351,8 @@ function equilibrium_imposed_ET!(df; # dfGS = get_df_GS(df, G,S; infoGS) # temporarily add G and S to the DataFrame to mutate - df._tmp_G .= dfGS.G - df._tmp_S .= dfGS.S + df[!, :_tmp_G] .= dfGS.G + df[!, :_tmp_S] .= dfGS.S f(args...) = equilibrium_imposed_ET(args...; kwargs...) transform!(df, [:Tair, :pressure, :VPD, :Gs, :Rn, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable diff --git a/test/evapotranspiration.jl b/test/evapotranspiration.jl index 04680c1..bb18b46 100644 --- a/test/evapotranspiration.jl +++ b/test/evapotranspiration.jl @@ -21,8 +21,10 @@ end @test ≈(last(df_ET).LE_pot, 494.7202; rtol = 1e-5) # df2 = copy(df) - df2.VPD .= 2.0 - df2.Ga .= 0.1 + # df2.VPD .= 2.0 # fail in older versions + # df2.Ga .= 0.1 + df2[!, :VPD] .= 2.0 + df2[!, :Ga] .= 0.1 # df2 = transform(df, # [] => ByRow(() -> 2.0) => :VPD, # [] => ByRow(() -> 0.1) => :Ga, @@ -55,8 +57,8 @@ end @test ismissing(first(df_ET).LE_pot) # df2 = copy(df) - df2.VPD .= 2.0 - df2.Ga .= 0.1 + df2[!, :VPD] .= 2.0 + df2[!, :Ga] .= 0.1 df_ET2 = @test_logs (:info,r"G is not provided") potential_ET(df2, Val(:PenmanMonteith), S = df.G) @test ncol(df2) == 6 @test nrow(df_ET2) == nrow(df2) From 446cfe2640072609eb5e6b3abe2f9555c466ac82 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 15:05:51 +0200 Subject: [PATCH 11/43] add tests for potential radiation --- .gitignore | 1 + Project.toml | 2 +- test/Manifest.toml | 265 ------------------------------------ test/Project.toml | 1 + test/potential_radiation.jl | 5 - test/runtests.jl | 8 +- test/util.jl | 4 + 7 files changed, 14 insertions(+), 272 deletions(-) delete mode 100644 test/Manifest.toml create mode 100644 test/util.jl diff --git a/.gitignore b/.gitignore index 9117cbf..cb7d064 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /docs/build/ /tmp docs/Manifest.toml +test/Manifest.toml diff --git a/Project.toml b/Project.toml index 468ddf1..868faf7 100644 --- a/Project.toml +++ b/Project.toml @@ -24,7 +24,7 @@ LabelledArrays = "1" Optim = "1" RecursiveArrayTools = "2" StaticArrays = "1" -julia = "1" +julia = "1.6, 1.7" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/Manifest.toml b/test/Manifest.toml deleted file mode 100644 index 616778b..0000000 --- a/test/Manifest.toml +++ /dev/null @@ -1,265 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.7.0-rc1" -manifest_format = "2.0" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.39.0" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" - -[[deps.Crayons]] -git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.0.4" - -[[deps.DataAPI]] -git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.9.0" - -[[deps.DataFrames]] -deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "d785f42445b63fc86caa08bb9a9351008be9b765" -uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.2.2" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.10" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" - -[[deps.Formatting]] -deps = ["Printf"] -git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.2" - -[[deps.Future]] -deps = ["Random"] -uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.InvertedIndices]] -git-tree-sha1 = "bee5f1ef5bf65df56bdd2e40447590b272a5471f" -uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.1.0" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" - -[[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.2" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" - -[[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" - -[[deps.Pipe]] -git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" -uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" -version = "1.3.0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[deps.PooledArrays]] -deps = ["DataAPI", "Future"] -git-tree-sha1 = "a193d6ad9c45ada72c14b731a318bedd3c2f00cf" -uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" -version = "1.3.0" - -[[deps.PrettyTables]] -deps = ["Crayons", "Formatting", "Markdown", "Reexport", "Tables"] -git-tree-sha1 = "d940010be611ee9d67064fe559edbb305f8cc0eb" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "1.2.3" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.1" - -[[deps.SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.Suppressor]] -git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787" -uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb" -version = "0.2.0" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] -git-tree-sha1 = "fed34d0e71b91734bf0a7e10eb1bb05296ddbcd0" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.6.0" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/test/Project.toml b/test/Project.toml index 41d023f..6ea5027 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,3 +4,4 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" diff --git a/test/potential_radiation.jl b/test/potential_radiation.jl index 840c4e7..283b032 100644 --- a/test/potential_radiation.jl +++ b/test/potential_radiation.jl @@ -1,8 +1,3 @@ -@testset "frac_hour" begin - p = frac_hour(1+1/60) - @test p == Hour(1) + Minute(1) -end - @testset "get_datetime_for_doy_hour summer" begin hours = [0,π,24] dts = get_datetime_for_doy_hour.(1, hours; year = 2021) diff --git a/test/runtests.jl b/test/runtests.jl index a51cbf8..ee4f73c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,11 @@ using Bigleaf using Test -using Pipe, DataFrames, Dates +using Pipe, DataFrames, Dates, TimeZones @testset "Bigleaf" begin + @testset "util" begin + include("util.jl") + end @testset "unit_conversions" begin include("unit_conversions.jl") end @@ -12,6 +15,9 @@ using Pipe, DataFrames, Dates @testset "sun_position" begin include("sun_position.jl") end + @testset "potential_radiation" begin + include("potential_radiation.jl") + end @testset "evapotranspiration" begin include("evapotranspiration.jl") end diff --git a/test/util.jl b/test/util.jl new file mode 100644 index 0000000..a28d2b3 --- /dev/null +++ b/test/util.jl @@ -0,0 +1,4 @@ +@testset "frac_hour" begin + @test frac_hour(Minute, 1+1/60) == Hour(1) + Minute(1) + @test frac_hour(1+1/60) == Hour(1) + Minute(1) +end From 7c1768ff53456450777ff76ace4f89cb8b12cfc7 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 15:19:31 +0200 Subject: [PATCH 12/43] remove suppress_err in setup block --- docs/src/walkthrough.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index e33020b..55b9990 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -28,7 +28,8 @@ using Latexify using DataDeps, Suppressor using RData import CodecBzip2, CodecXz -@suppress_err register(DataDep( +#@suppress_err # error in github-actions: GitHubActionsLogger has no field stream +register(DataDep( "DE_Tha_Jun_2014.rda", "downloading exampple dataset DE_Tha_Jun_2014 from bitbucket.org/juergenknauer/bigleaf", "https://bitbucket.org/juergenknauer/bigleaf/raw/0ebe11626b4409305951e8add9f6436703c82584/data/DE_Tha_Jun_2014.rda", From 41a234bdd1c2859d64701d531da77d7cd0811cec Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 15:33:40 +0200 Subject: [PATCH 13/43] remove suppress_err in example blocks --- docs/src/walkthrough.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 55b9990..e7de8d9 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -209,7 +209,7 @@ lat,long = 51.0, 13.6 # Dresden Germany #deg2second = 24*3600/360 doy = 160 datetimes = DateTime(2021) .+Day(doy-1) .+ Hour.(hours) #.- Second(round(long*deg2second)) -res3 = @suppress_err @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) +res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) @df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad") ``` @@ -219,7 +219,7 @@ about 55min ahead of local winter time. ```@example doc summernoon = DateTime(2021) +Day(doy-1) + Hour(12) -sunpos = @suppress_err calc_sun_position_hor(summernoon, lat, long) +sunpos = calc_sun_position_hor(summernoon, lat, long) sunpos.hourangle * 24*60/(2*π) # convert angle to minutes ``` From d266685994097fb52e99d7efeb65fac98e6e727e Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 26 Oct 2021 22:28:42 +0200 Subject: [PATCH 14/43] work on GPPd filter --- Project.toml | 1 + doc/Manifest.toml | 65 ----------------------------- doc/Project.toml | 2 - inst/tha.jl | 33 +++++++++++++++ src/Bigleaf.jl | 1 + src/filter_data.jl | 99 +++++++++++++++++++++++++++++++++++++++++++++ test/filter_data.jl | 3 ++ 7 files changed, 137 insertions(+), 67 deletions(-) delete mode 100644 doc/Manifest.toml delete mode 100644 doc/Project.toml create mode 100644 inst/tha.jl create mode 100755 src/filter_data.jl create mode 100644 test/filter_data.jl diff --git a/Project.toml b/Project.toml index 3976c06..33277b4 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ Optim = "429524aa-4258-5aef-a3af-852621145aeb" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" diff --git a/doc/Manifest.toml b/doc/Manifest.toml deleted file mode 100644 index 19ab490..0000000 --- a/doc/Manifest.toml +++ /dev/null @@ -1,65 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.7.0-rc1" -manifest_format = "2.0" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.Formatting]] -deps = ["Printf"] -git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.2" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.2.1" - -[[deps.Latexify]] -deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"] -git-tree-sha1 = "669315d963863322302137c4591ffce3cb5b8e68" -uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.15.8" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "5a5bc6bf062f0f95e62d0fe0a2d99699fed82dd9" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.8" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.Random]] -deps = ["Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.1.3" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/doc/Project.toml b/doc/Project.toml deleted file mode 100644 index 28825c2..0000000 --- a/doc/Project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[deps] -Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" diff --git a/inst/tha.jl b/inst/tha.jl new file mode 100644 index 0000000..871d8e1 --- /dev/null +++ b/inst/tha.jl @@ -0,0 +1,33 @@ +using DataFrames, Pipe, Missings + +using Latexify +using DataDeps, Suppressor +using RData +import CodecBzip2, CodecXz +#@suppress_err # error in github-actions: GitHubActionsLogger has no field stream +register(DataDep( + "DE_Tha_Jun_2014.rda", + "downloading exampple dataset DE_Tha_Jun_2014 from bitbucket.org/juergenknauer/bigleaf", + "https://bitbucket.org/juergenknauer/bigleaf/raw/0ebe11626b4409305951e8add9f6436703c82584/data/DE_Tha_Jun_2014.rda", + "395f02e1a1a2d175ac7499c200d9d48b1cb58ff4755dfd2d7fe96fd18258d73c" +)) +#println(datadep"DE_Tha_Jun_2014.rda") +ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download +DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) +nothing +tha = DE_Tha_Jun_2014 +tha.time = @. DateTime(tha.year) + Day(tha.doy-1) + frac_hour(Minute, tha.hour) +tmp = first(tha.time) + +dfGPPd = @pipe tha |> + transform(_, :time => ByRow(yearmonthday) => :ymd, copycols = false) |> + groupby(_, :ymd) |> + combine(_, :time => (x -> Date(first(x))) => :date, :GPP => mean => :GPPd, ) + #x -> x[!,2] + +show(dfGPPd.GPPd) + + +GPPd = [11.288497760271033, 13.013025930772224, 12.851774960756302, 11.996453734696843, 11.635422044472458, 11.155685572574535, 10.774393322790273, 10.181774605065584, 11.257192575993637, 12.9423423493281, 12.352468963712454, 13.402045020057509, 9.53826415212825, 12.071680051895479, 13.692589149111882, 12.845505638824156, 12.378533909407755, 11.672167064607493, 10.401156075240579, 10.705716138705611, 10.207347450816693, 11.052016352768987, 13.54435911634937, 12.060648361220956, 7.758974596237143, 9.869706534050541, 12.998054057980577, 10.627359564105669, 8.685295419767499, 10.874667977293333] +using Plots,StatsPlots +@df dfGPPd plot(:date, [:GPPd], xlab = "Date", ylab="GPP") diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 5cd056e..7f113df 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -9,6 +9,7 @@ using Pipe using AstroLib using Suppressor using Missings +using Statistics export frac_hour diff --git a/src/filter_data.jl b/src/filter_data.jl new file mode 100755 index 0000000..d2873ba --- /dev/null +++ b/src/filter_data.jl @@ -0,0 +1,99 @@ +""" +GPP-based Growing Season Filter + +Filters annual time series for growing season based on smoothed daily GPP data. + +# Arguments +- GPPd: daily GPP (any unit) +- tGPP: GPP threshold (fraction of 95th percentile of the GPP time series). + Takes values between 0 and 1. +- ws: window size used for GPP time series smoothing +- min_int: minimum time interval in days for a given state of growing season + +# Details +The basic idea behind the growing season filter is that vegetation is +considered to be active when its carbon uptake (GPP) is above a specified +threshold, which is defined relative to the peak GPP (95th percentile) +observed in the year. +The GPP-threshold is calculated as: + +``GPP_threshold = quantile(GPPd,0.95)*tGPP`` + +GPPd time series are smoothed with a moving average to avoid fluctuations +in the delineation of the growing season. The window size defaults to 15 +days, but depending on the ecosystem, other values can be appropriate. + +The argument `min_int` serves to avoid short fluctuations in the +status growing season vs. no growing season by defining a minimum length +of the status. If a time interval shorter than `min_int` is labeled +as growing season or non-growing season, it is changed to the status of +the neighboring values. + +# Value +a vector of type integer of the same length as the input GPPd in which 0 indicate + no growing season (dormant season) and 1 indicate growing season. +""" +function filter_growing_season(GPPd,tGPP,ws=15,min_int=5) + nday = length(GPPd) + if sum(ismissing(GPPd)) >= 0.5*nday + @warn "number of available GPPd data is less than half the total number of days " * + "per year. Filter is not applied!" + return(Trues(nday)) + end + growseas = repeat([true], nday) + GPP_threshold = quantile(skipmissing(GPPd), 0.95)*tGPP + + ## smooth GPP + GPPd_smoothed = filter(GPPd,method="convolution",filter=rep(1/ws,ws)) + + ## set values at the beginning and end of the time series to the mean of the original values + wsd = floor(ws/2) + GPPd_smoothed[1:wsd] = mean(GPPd[1:(2*wsd)],na_rm=TRUE) + GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] = mean(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)],na_rm=TRUE) + + # check for occurence of missing values and set them to mean of the values surrounding them + missing = which(is_na(GPPd_smoothed)) + if (length(missing) > 0) + if (length(missing) > 10){warning("Attention, there is a gap in 'GPPd' of length n = ",length(missing))} + replace_val = mean(GPPd_smoothed[max(1,missing[1] - 4):min((missing[length(missing)] + 4),length(GPPd_smoothed))],na_rm=TRUE) + GPPd_smoothed[missing] = replace_val + end + # filter daily GPP + growseas[GPPd_smoothed < GPP_threshold] = 0 + + ## change short intervals to the surrounding values to avoid 'wrong' fluctuations + intervals = rle(growseas) + short_int = which(intervals$lengths <= min_int) + + if (length(short_int) > 0) + start = numeric() + end = numeric() + + for (i in 1:length(short_int)) + + start[i] = sum(intervals$lengths[1:short_int[i]-1]) + 1 + end[i] = start[i]+intervals$lengths[short_int[i]] - 1 + + val = unique(growseas[start[i]:end[i]]) + + if (val == 0 & growseas[start[i]-1] == 1) + growseas[start[i]:end[i]] = 1 +else if (val == 1 & growseas[start[i]-1] == 0) + growseas[start[i]:end[i]] = 0 +end +end +end + + growseas = as_integer(growseas) + +else + + warning("number of available GPPd data is less than half the total number of days per year. Filter is not applied!") + growseas = as_integer(rep(1,length(GPPd))) + +end + + return(growseas) +end + + diff --git a/test/filter_data.jl b/test/filter_data.jl new file mode 100644 index 0000000..c307a89 --- /dev/null +++ b/test/filter_data.jl @@ -0,0 +1,3 @@ +@testset "GPPfilter" begin + GPPd = +end \ No newline at end of file From 93159e842d2e4d8e698731fd36c5228b2b25b2eb Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 27 Oct 2021 15:17:02 +0200 Subject: [PATCH 15/43] implement filter_growing_season --- Project.toml | 44 ++++++++++++--------- src/Bigleaf.jl | 8 +++- src/filter_data.jl | 93 +++++++++++++++++---------------------------- src/util.jl | 30 +++++++++++++++ test/Project.toml | 3 ++ test/filter_data.jl | 13 ++++++- test/runtests.jl | 6 ++- test/util.jl | 13 +++++++ 8 files changed, 128 insertions(+), 82 deletions(-) diff --git a/Project.toml b/Project.toml index 992f8c5..720e61b 100644 --- a/Project.toml +++ b/Project.toml @@ -4,35 +4,41 @@ authors = ["Thomas Wutzler and contributors"] version = "0.2.1-DEV" [deps] -TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" -Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" -Optim = "429524aa-4258-5aef-a3af-852621145aeb" +AstroLib = "c7932e45-9af1-51e7-9da9-f004cd3a462b" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" +Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" LabelledArrays = "2ee39098-c373-598a-b85f-a56591580800" Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Optim = "429524aa-4258-5aef-a3af-852621145aeb" +PaddedViews = "5432bcbf-9aad-5242-b902-cca2824c8663" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" -DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" -FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" -AstroLib = "c7932e45-9af1-51e7-9da9-f004cd3a462b" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [compat] -TimeZones = "1" -Suppressor = "0.2" -Optim = "1" +AstroLib = "0.4" +DataFrames = "1" +FillArrays = "0.12" LabelledArrays = "1" Missings = "1" -StaticArrays = "1" -julia = "1.6, 1.7" +Optim = "1" +PaddedViews = "0.5" Pipe = "1" -DataFrames = "1" RecursiveArrayTools = "2" -FillArrays = "0.12" -AstroLib = "0.4" +StaticArrays = "1" +StatsBase = "0.33" +Suppressor = "0.2" +TimeZones = "1" +julia = "1.6, 1.7" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"] diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 5cd056e..7fa63cf 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -9,9 +9,11 @@ using Pipe using AstroLib using Suppressor using Missings +using Statistics, StatsBase # mean, rle +using PaddedViews +using Infiltrator - -export frac_hour +export frac_hour, moving_average export bigleaf_constants export Esat_slope, Esat_from_Tair, Esat_from_Tair_deriv, LE_to_ET, ET_to_LE, ms_to_mol, mol_to_ms, VPD_to_rH, rH_to_VPD, @@ -24,6 +26,7 @@ export air_density, pressure_from_elevation, psychrometric_constant, export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ET! +export filter_growing_season #export surface_conductance include("util.jl") @@ -33,5 +36,6 @@ include("meteorological_variables.jl") include("sun_position.jl") include("potential_radiation.jl") include("evapotranspiration.jl") +include("filter_data.jl") end diff --git a/src/filter_data.jl b/src/filter_data.jl index d2873ba..5a90d21 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -1,5 +1,5 @@ """ -GPP-based Growing Season Filter + filter_growing_season(GPPd, tGPP; ws=15, min_int=5, warngap=true) Filters annual time series for growing season based on smoothed daily GPP data. @@ -7,8 +7,10 @@ Filters annual time series for growing season based on smoothed daily GPP data. - GPPd: daily GPP (any unit) - tGPP: GPP threshold (fraction of 95th percentile of the GPP time series). Takes values between 0 and 1. +optional - ws: window size used for GPP time series smoothing - min_int: minimum time interval in days for a given state of growing season +- warngap: set to false to suppress warning on too few non-missing data # Details The basic idea behind the growing season filter is that vegetation is @@ -27,73 +29,48 @@ The argument `min_int` serves to avoid short fluctuations in the status growing season vs. no growing season by defining a minimum length of the status. If a time interval shorter than `min_int` is labeled as growing season or non-growing season, it is changed to the status of -the neighboring values. +the neighboring values, i.e its opposite. # Value -a vector of type integer of the same length as the input GPPd in which 0 indicate - no growing season (dormant season) and 1 indicate growing season. +a `BitVector` of the same length as the input GPPd in which `false` indicate +no growing season (dormant season) and `true` indicate growing season. """ -function filter_growing_season(GPPd,tGPP,ws=15,min_int=5) +function filter_growing_season(GPPd,tGPP;ws=15,min_int=5,warngap=true) nday = length(GPPd) if sum(ismissing(GPPd)) >= 0.5*nday - @warn "number of available GPPd data is less than half the total number of days " * - "per year. Filter is not applied!" + @warn "number of available GPPd data is less than half the total number of days. " * + "Filter is not applied!" return(Trues(nday)) end - growseas = repeat([true], nday) GPP_threshold = quantile(skipmissing(GPPd), 0.95)*tGPP - - ## smooth GPP - GPPd_smoothed = filter(GPPd,method="convolution",filter=rep(1/ws,ws)) - - ## set values at the beginning and end of the time series to the mean of the original values - wsd = floor(ws/2) - GPPd_smoothed[1:wsd] = mean(GPPd[1:(2*wsd)],na_rm=TRUE) - GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] = mean(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)],na_rm=TRUE) - + # smooth GPP + #fromR: GPPd_smoothed = filter(GPPd,method="convolution",filter=rep(1/ws,ws)) + GPPd_smoothed = moving_average(GPPd, ws) + # set values at the beginning and end of the time series to the mean of the original values + wsd = floor(Int, ws/2) + GPPd_smoothed[1:wsd] .= mean(skipmissing(GPPd[1:(2*wsd)])) + GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] .= mean(skipmissing(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)])) # check for occurence of missing values and set them to mean of the values surrounding them - missing = which(is_na(GPPd_smoothed)) - if (length(missing) > 0) - if (length(missing) > 10){warning("Attention, there is a gap in 'GPPd' of length n = ",length(missing))} - replace_val = mean(GPPd_smoothed[max(1,missing[1] - 4):min((missing[length(missing)] + 4),length(GPPd_smoothed))],na_rm=TRUE) - GPPd_smoothed[missing] = replace_val - end + imissing = findall(ismissing, GPPd_smoothed) + if length(imissing) > 0 + warngap && length(imissing) > 10 && @warn "Attention, there is a gap in 'GPPd' of length n = $(length(imissing))" + #TODO check and correct for several gaps, see Impute package + replace_val = mean(skipmissing(GPPd_smoothed[max(1,imissing[1] - 4):min((imissing[length(imissing)] + 4),length(GPPd_smoothed))])) + GPPd_smoothed = coalesce.(GPPd_smoothed, replace_val) + end # filter daily GPP - growseas[GPPd_smoothed < GPP_threshold] = 0 - - ## change short intervals to the surrounding values to avoid 'wrong' fluctuations - intervals = rle(growseas) - short_int = which(intervals$lengths <= min_int) - - if (length(short_int) > 0) - start = numeric() - end = numeric() - - for (i in 1:length(short_int)) - - start[i] = sum(intervals$lengths[1:short_int[i]-1]) + 1 - end[i] = start[i]+intervals$lengths[short_int[i]] - 1 - - val = unique(growseas[start[i]:end[i]]) - - if (val == 0 & growseas[start[i]-1] == 1) - growseas[start[i]:end[i]] = 1 -else if (val == 1 & growseas[start[i]-1] == 0) - growseas[start[i]:end[i]] = 0 -end -end -end - - growseas = as_integer(growseas) - -else - - warning("number of available GPPd data is less than half the total number of days per year. Filter is not applied!") - growseas = as_integer(rep(1,length(GPPd))) - -end - + growseas = GPPd_smoothed .>= GPP_threshold + # change short intervals to the surrounding values to avoid 'wrong' fluctuations + # switch shortest interval successively and recompute interval lengths + intervals = rle(growseas)[2] + imin = argmin(intervals) + while intervals[imin] < min_int + end_int = cumsum(intervals) + start_int = vcat(0, end_int[1:end-1]) .+ 1 + growseas[start_int[imin]:end_int[imin]] .= !growseas[start_int[imin]] + intervals = rle(growseas)[2] + imin = argmin(intervals) + end return(growseas) end - diff --git a/src/util.jl b/src/util.jl index aece4e3..727c2bd 100644 --- a/src/util.jl +++ b/src/util.jl @@ -38,4 +38,34 @@ function frac_hour(period::Type{<:Period}, float::AbstractFloat) end frac_hour(float::AbstractFloat) = frac_hour(Nanosecond, float) + +""" + moving_average(vs,n; nmin = n ÷ 2) + +Compute the moving average over a vector allowing for missings. + +# Arguments +- vs: numeric vector to average over +- n: window size: number of items to average +- nmin: minimum number of non-missing records + +Values outside the edges are assumed missing. + +If the number of non-missing records within a window is smaller than nmin +then the averaged value is assumed missing. This avoids average at edges of +periods with many missings to be very sensitive to the edge values. +""" +function moving_average(vs,n; nmin = n ÷ 2) + kernel = -trunc(Int,(n-1)/2):ceil(Int,(n-1)/2) + #[mean(skipmissing(@view vs[i:(i+n-1)])) for i in 1:(length(vs)-(n-1))] + vsp = PaddedView(missing, vs, (-n:length(vs)+n,)) + fagg = function(x) + sum(.!ismissing.(x)) < nmin && return(missing) + mean(skipmissing(x)) + end + #ans = [fagg(@view vsp[i.+kernel]) for i in 100:101] + ans = [fagg(@view vsp[i.+kernel]) for i in 1:length(vs)] +end + + diff --git a/test/Project.toml b/test/Project.toml index 6ea5027..b4bda33 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,6 +2,9 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" diff --git a/test/filter_data.jl b/test/filter_data.jl index c307a89..d7d0d63 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -1,3 +1,12 @@ @testset "GPPfilter" begin - GPPd = -end \ No newline at end of file + rng = StableRNG(815) + GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) + GPPd[100:120] .= missing + #plot(1:365, GPPd) + growseas = @test_logs (:warn, r"gap in 'GPPd'") filter_growing_season(GPPd, 0.5) + #plot(growseas) + @test rle(growseas) == (Bool[0, 1, 0], [51, 258, 56]) + growseas = filter_growing_season(GPPd, 0.5; min_int = 56, warngap = false) + @test rle(growseas) == (Bool[1, 0], [309, 56]) +end + diff --git a/test/runtests.jl b/test/runtests.jl index ee4f73c..6d858a2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,7 @@ using Bigleaf -using Test +using Test, StableRNGs using Pipe, DataFrames, Dates, TimeZones +using Statistics, StatsBase @testset "Bigleaf" begin @testset "util" begin @@ -9,6 +10,9 @@ using Pipe, DataFrames, Dates, TimeZones @testset "unit_conversions" begin include("unit_conversions.jl") end + @testset "filter_data" begin + include("filter_data.jl") + end @testset "meteorological_variables" begin include("meteorological_variables.jl") end diff --git a/test/util.jl b/test/util.jl index a28d2b3..43c29da 100644 --- a/test/util.jl +++ b/test/util.jl @@ -2,3 +2,16 @@ @test frac_hour(Minute, 1+1/60) == Hour(1) + Minute(1) @test frac_hour(1+1/60) == Hour(1) + Minute(1) end + +@testset "moving_average" begin + GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(365) |> allowmissing(_) + GPPd[100:120] .= missing + GPPd_smooth = moving_average(GPPd, 15) + #plot(1:365, GPPd) + #plot!(1:365, GPPd_smooth) + @test length(GPPd_smooth) == length(GPPd) + @test GPPd_smooth[1] == mean(GPPd[1:8]) + @test GPPd_smooth[8] == mean(GPPd[1:15]) + @test isfinite(GPPd_smooth[100]) + @test ismissing(GPPd_smooth[101]) +end From ddc1a6bd7ee1dfefaddef28e8bb7e2a6d23e78a0 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 27 Oct 2021 16:01:35 +0200 Subject: [PATCH 16/43] filter_growing_seasonthrow error too few GPP data instead of warning --- Esat_abs.svg | 132 -------------------------------------------- src/filter_data.jl | 8 +-- test/filter_data.jl | 7 +++ 3 files changed, 11 insertions(+), 136 deletions(-) delete mode 100644 Esat_abs.svg diff --git a/Esat_abs.svg b/Esat_abs.svg deleted file mode 100644 index 8a08a99..0000000 --- a/Esat_abs.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/filter_data.jl b/src/filter_data.jl index 5a90d21..70c3e15 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -37,10 +37,10 @@ no growing season (dormant season) and `true` indicate growing season. """ function filter_growing_season(GPPd,tGPP;ws=15,min_int=5,warngap=true) nday = length(GPPd) - if sum(ismissing(GPPd)) >= 0.5*nday - @warn "number of available GPPd data is less than half the total number of days. " * - "Filter is not applied!" - return(Trues(nday)) + if sum(.!ismissing.(GPPd)) < 0.5*nday + error("Expected number of available GPPd data " * + "to be at least half the total number of days ($nday). " * + "But was only ($(sum(.!ismissing.(GPPd)))).") end GPP_threshold = quantile(skipmissing(GPPd), 0.95)*tGPP # smooth GPP diff --git a/test/filter_data.jl b/test/filter_data.jl index d7d0d63..73f5204 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -10,3 +10,10 @@ @test rle(growseas) == (Bool[1, 0], [309, 56]) end +@testset "GPPfilter with few data" begin + rng = StableRNG(815) + GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) + GPPd[1:200] .= missing + @test_throws ErrorException filter_growing_season(GPPd, 0.5) +end + From 42f964ea7856c72f2d7afa7214c6bb31e16c242f Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 28 Oct 2021 00:21:49 +0200 Subject: [PATCH 17/43] work on filtering methods --- docs/make.jl | 2 + docs/src/filter_data.md | 11 ++ docs/src/index.md | 10 ++ docs/src/surface_conductance.md | 10 ++ docs/src/walkthrough.md | 98 +++++++++++++- inst/fromR/filter_data.jl | 4 +- inst/tha.jl | 1 + inst/walkthrough_todo.jmd | 200 +++++++++++++++++----------- src/Bigleaf.jl | 6 +- src/evapotranspiration.jl | 29 ++-- src/filter_data.jl | 227 +++++++++++++++++++++++++++++--- test/Project.toml | 1 + test/filter_data.jl | 100 +++++++++++++- test/runtests.jl | 1 + 14 files changed, 582 insertions(+), 118 deletions(-) create mode 100644 docs/src/filter_data.md create mode 100644 docs/src/surface_conductance.md diff --git a/docs/make.jl b/docs/make.jl index 33dcbda..f0e884e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -26,9 +26,11 @@ makedocs(; "Walkthrough" => "walkthrough.md", hide("metorological_variables.md"), hide("evapotranspiration.md"), + hide("surface_conductance.md"), hide("global_radiation.md"), hide("unit_conversions.md"), hide("bigleaf_constants.md"), + hide("filter_data.md"), "Index" => "autodocs.md", ], ) diff --git a/docs/src/filter_data.md b/docs/src/filter_data.md new file mode 100644 index 0000000..d82b1a1 --- /dev/null +++ b/docs/src/filter_data.md @@ -0,0 +1,11 @@ +## Filtering data +```@index +Pages = ["filter_data.md",] +``` + +```@docs +setinvalid_qualityflag! +setinvalid_range! +setinvalid_nongrowingseason! +get_growingseason +``` \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 2925b20..e1320ef 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -18,6 +18,11 @@ Pages = ["metorological_variables.md",] Pages = ["evapotranspiration.md",] ``` +## Surface conductance +```@index +Pages = ["surface_conductance.md",] +``` + ## Global radiation ```@index Pages = ["global_radiation.md",] @@ -28,4 +33,9 @@ Pages = ["global_radiation.md",] Pages = ["unit_conversions.md","bigleaf_constants.md",] ``` +## Filtering data +```@index +Pages = ["filter_data.md",] +``` + diff --git a/docs/src/surface_conductance.md b/docs/src/surface_conductance.md new file mode 100644 index 0000000..ccfeb4c --- /dev/null +++ b/docs/src/surface_conductance.md @@ -0,0 +1,10 @@ +## Surface conductance +```@index +Pages = ["surface_conductance.md",] +``` + +```@docs +decoupling +surface_conductance +aerodynamic_conductance +``` \ No newline at end of file diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index e7de8d9..1a66a76 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -123,9 +123,105 @@ vector are not relaced by zero by default. You need to explitly use coalesce when specifying a ground heat flux for which missings should be replaced by zero: `;G = coalesce(df.G, zero(df.G))` - # Function walkthrough # +## Data filtering + +For most applications it is meaningful to filter your data. There are two main reasons +why we want to filter our data before we start calculating ecosystem properties. +The first one is to exclude datapoints that do not fulfill the requirements of the +EC technique or that are of bad quality due to e.g. instrument failure or gap-filling +with poor confidence. Note that the quality assessment of the EC data is not the purpose +of the `Bigleaf.jl` package. This is done by other packages (e.g. `REddyProc`), +which often provide quality control flags for the variables. These quality control +flags are used here to filter out bad-quality datapoints. + +A second reason for filtering our data is that some derived properties are only +meaningful if certain meteorological conditions are met. For instance, if we are +interested in properties related to plant gas exchange, it makes most sense to focus on +time periods when plants are photosynthetically active +(i.e. in the growing season and at daytime). + +`Bigleaf.jl` provides methods that update (or create) the :valid column in +a DataFrame. Records, i.e. rows, that contain non valid conditions are set to false. +If the valid column was false before, it stays at false. + +### `setinvalid_qualityflag!` +One can check quality flags. By default (argument `setvalmissing = true`) this also +replaces the non-valid values in the data-columns by `missing`. +```julia +thaf = copy(tha) # keep the original tha DataFrame +# if the :valid columns does not exist yet, it is created with all values true +setinvalid_qualityflag!(thaf; vars = [`LE`, "NEE"]) +sum(.!thaf.valid) # 7 records marked non-valid +sum(ismissing.(thaf.NEE)) # 7 NEE values set to missing +``` +In the function call above, `vars` lists the variables that should be filtered with +respect to their quality. Optional parameter `qc_suffix="_qc"` denotes the extension +of the variable name that identifies the column as a quality control indicator of a given +variable. The variables `LE` and `LE_qc`, for example, denote the variable itself +(latent heat flux), and the quality of the variable `LE`, respectively. The optional +argument `good_quality_threshold = 1.0` specifies the values of the quality column +below which the quality control to be considered as acceptable quality +(i.e. to not be filtered). For example with default value 1, +all `LE` values whose `LE_qc` variable is larger than 1 are set to `missing`. +The variable `missing_qc_as_bad` is required to decide what to do in +case of missing values in the quality control variable. By default this is (conservatively) +set to `TRUE`, i.e. all entries where the qc variable is missing is set invalid. + +### `setinvalid_range!` + +We can filter for meteorological conditions to be in acceptable ranges. +For each variable to check we supply the valid minimum and valid maxium as a two-tuple +as the second component of a pair. If their is no limit towards small or +large values, supply `-Inf` or `Inf` as the minimum or maximum respectively. +```julia +setinvalid_range!(thaf, + :PPFD => (200, Inf), + :ustar => (0.2, Inf), + :LE =>(0, Inf), + :VPD => (0.01, Inf) + ) +sum(.!thaf.valid) # many more records marked invalid +minimum(skipmissing(thaf.PPFD)) >= 200 # values outsides range some set to missing +sum(ismissing.(thaf.PPFD) +``` + +About half of the data were filtered because radiation was not high enough (night-time). +Another quarter was filtered because they showed negative LE values. +However, most of them occured during the night: +```julia +sum(ismissing.(thaf.PPFD)) / nrow(thaf) # 0.48 +sum(.!ismissing.(thaf.PPFD) .&& ismissing.(thaf.LE)) / nrow(thaf) # 0.05 +``` + +### `setinvalid_nongrowingseason!` + +A third method filters periods outside the growing season: +```julia +setinvalid_nongrowingseason!(thaf, 0.4) +sum(.!thaf2.valid) # tha dataset is all within growing season - no additional invalids +``` + +This function implements a simple growing season filter based on daily smoothed GPP time +series. +Arguments `tGPP` determines how high daily GPP has to be in relation to its peak value +within the year. In this case, the value of 0.4 denotes that smoothed GPP has to be at +least 40% of the 95th quantile. +Argument `ws` controls the degree of smoothing in the timeseries +and should be between 10-20 days. +The purpose of which is to minimize the high variation of GPP between days, +Argument `min_int` is a parameter that avoids that data are switching from +inside the growing season and out from one day to the next. +It determines the minimum number of days that a season should have. +The growing season filter is applicable to all sites, with one more more growing seasons, +but its advisable that site-specific parameter settings are used. + +In this example, it does not really make sense to filter for growing season, +since it uses only one month of data of which we know that vegetation is active at the site. +The algorithm realizes that and does not mark any additional data as invalid. + + ## Meteorological variables The `Bigleaf.jl` package provides calculation routines for a number of meteorological variables, which are basic to the calculation of many other variables. A few examples on their usage are given below: diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index 00b5566..7497a1b 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -190,7 +190,7 @@ end check_input(data,doy,year,GPP) date = strptime(paste0(year,"-",doy),format="%Y-%j") GPP_daily = aggregate(GPP,by=list(strftime(date)),mean,na_rm=TRUE)[,2] - growing_season = filter_growing_season(GPP_daily,tGPP=tGPP,ws=ws,min_int=min_int) + growing_season = filter_growingseason(GPP_daily,tGPP=tGPP,ws=ws,min_int=min_int) growseas_invalid = which(sapply(growing_season,rep,48) == 0) end @@ -315,7 +315,7 @@ end #' #' @importFrom stats quantile filter #' @export -function filter_growing_season(GPPd,tGPP,ws=15,min_int=5) +function filter_growingseason(GPPd,tGPP,ws=15,min_int=5) if(sum(is_na(GPPd)) < 0.5*length(GPPd)) diff --git a/inst/tha.jl b/inst/tha.jl index 871d8e1..6ee66b3 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -1,4 +1,5 @@ using DataFrames, Pipe, Missings +using Dates, TimeZones using Latexify using DataDeps, Suppressor diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 961846d..999f5b7 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -1,81 +1,133 @@ - - - -## Function arguments - -Most functions of the `Bigleaf.jl` package require an input matrix or (more common) a DataFrame, from which the required variables are extracted. This is usually the first argument of a function. Most functions further provide default values for their arguments, such that in many cases it is not necessary to provide them explicitly. - -Another convenient feature of the `Bigleaf.jl` package is that it supports functions -that accept a DataFrame with columnnames corresponding to required arguments. - -We can demonstrate the usage with a simple example: - -```julia -potential_ET(tha,Tair="Tair",pressure="pressure",Rn="Rn",VPD="VPD",approach=Val(:PriestleyTaylor)) -potential_ET(tha) -potential_ET(tha,Tair=tha$Tair) -potential_ET(tha,Tair=25) -potential_ET(Tair=25,pressure=100,Rn=200) -``` - -In the first line above, the input arguments are provided as the names of the DataFrame. In this case we do not need to provide them explicitly because the column names correspond to the default names of the function (i.e. the command can be written as in line 2). In the third example, we replace one variable name with a numeric vector. In the fourth row, we calculate PET for a constant temperature of 25°C, but take all other variables from the DataFrame. For some applications, in particular for exploratory or sensitivity analyses, application nr. 5 can be useful. In this case, we did not provide a DataFrame, but only numeric vectors of length one (or of any other length). This can be useful to see e.g. how sensitive the results of a given functions are with respect to one of the input variables. We could, for instance, investigate how potential ET as calculated with the Priestley-Taylor formulation changes when $R_n$ increases from 200 $\text{W m}^{-2}$ to 400 $\text{W m}^{-2}$ when all other variables are held constant: - -```julia -potential_ET(Tair=25,pressure=100,Rn=200) -potential_ET(Tair=25,pressure=100,Rn=400) +# Setup + +```julia +using Bigleaf +using DataFrames + +using Latexify +using DataDeps, Suppressor +using RData +import CodecBzip2, CodecXz +#@suppress_err # error in github-actions: GitHubActionsLogger has no field stream +register(DataDep( + "DE_Tha_Jun_2014.rda", + "downloading exampple dataset DE_Tha_Jun_2014 from bitbucket.org/juergenknauer/bigleaf", + "https://bitbucket.org/juergenknauer/bigleaf/raw/0ebe11626b4409305951e8add9f6436703c82584/data/DE_Tha_Jun_2014.rda", + "395f02e1a1a2d175ac7499c200d9d48b1cb58ff4755dfd2d7fe96fd18258d73c" +)) +#println(datadep"DE_Tha_Jun_2014.rda") +ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download +DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) +tha = DE_Tha_Jun_2014 +LAI = 7.6 # leaf area index +zh = 26.5 # average vegetation height (m) +zr = 42 # sensor height (m) +Dl = 0.01 # leaf characteristic dimension (m) ``` -When using your own data, it is not mandatory to use exactly the same variable names as here, but working with `Bigleaf.jl` is easier if you do so because then the variable names do not have to be specified when calling a function. - - -## Ground heat flux and storage fluxes - -Many functions require the available energy ($A$), which is defined as ($A = R_n - G - S$, all in $\text{W m}^{-2}$), where $R_n$ is the net radiation, $G$ is the ground heat flux, and $S$ is the sum of all storage fluxes of the ecosystem (see e.g. Leuning et al. 2012 for an overview). For some sites, $G$ is not available, and for most sites, only a few components of $S$ are measured. In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results might be (slightly) biased), but special options exist for the treatment of missing $S$ and $G$ values. If the options `missing_G_as_NA = TRUE` or `missing_S_as_NA = TRUE`, then the output variable is not calculated for that time period. Otherwise missing $S$ and $G$ values are set to O automatically. Please note that the default is to ignore $S$ and $G$ values. If $G$ and/or $S$ are available, they always have to be added explicitly to the function call (by providing the column name of $G$/$S$ or a vector). - - - - -\vspace{1cm} - - - -## Function walkthrough - -In the following, we explain how to use several of the package's key functions. Further information on the functions can be found on the respective function help pages and the references therein. - - ## Data filtering -For most applications it is meaningful to filter your data. There are two main reasons why we want to filter our data before we start calculating ecosystem properties. The first one is to exclude datapoints that do not fulfill the requirements of the EC technique or that are of bad quality due to e.g. instrument failure or gap-filling with poor confidence. Note that the quality assessment of the EC data is not the purpose of the `Bigleaf.jl` package. This is done by other packages (e.g. `REddyProc`), which often provide quality control flags for the variables. These quality control flags are used here to filter out bad-quality datapoints. - -A second reason for filtering our data is that some derived properties are only meaningful if certain meteorological conditions are met. For instance, if we are interested in properties related to plant gas exchange, it makes most sense to focus on time periods when plants are photosynthetically active (i.e. in the growing season and at daytime). - -The Bigleaf package provides the function `filter_data` that filters the data according to the criteria described above. We start with an example where the DataFrame is filtered only with respect to data quality (`quality_control=TRUE`): - -```julia -tha_filtered1 = filter_data(tha,quality_control=TRUE,vars_qc=c("LE","H","NEE","Tair","VPD","wind"), - quality_ext="_qc",good_quality = c(0,1),missing_qc_as_bad=TRUE) -``` - -In the function call above, `vars_qc` lists the variables that should be filtered with respect to their quality. This is usually a vector of type character that contains the column names of the variables that are to be filtered. `quality_ext` denotes the extension of the variable name that identifies the column as a quality control indicator of a given variable. The variables "LE" and "LE_qc", for example, denote the variable itself (latent heat flux), and the quality of the variable "LE", respectively. The argument `good_quality` specifies the values that the quality control indicator has to take in order to be considered as acceptable quality (i.e. to not be filtered). For example, if `good_quality=c(0,1)`, then all "LE" values whose "LE_qc" variable is larger than 1 are set to `NA`. The variable `missing_qc_as_bad` is required to decide what to do in case of missing values in the quality control variable. By default this is (conservatively) set to `TRUE`, i.e. all entries where the qc variable is missing is filtered out. The function prints some information on the amount of data filtered out. In this case, only a few values did not fulfill the quality criteria. -In the next example, we filter for meteorological conditions only, including growing season (`filter_growseas=TRUE`): - -```julia -tha_filtered2 = filter_data(tha,quality_control=false,filter_growseas=TRUE, - filter_vars=c("PPFD","ustar","LE","VPD"), - filter_vals_min=c(200,0.2,0,0.01), filter_vals_max=c(NA,NA,NA,NA), - NA_as_invalid = TRUE, - # arguments for growing season filter: - GPP="GPP",doy="doy",year="year",tGPP=0.4,ws=15,min_int=5) -``` - -The arguments `filter_vars`, `filter_vals_min`, and `filter_vals_max` control the variables to be filtered (corresponding to the column names of the DataFrame), the minimum and the maximum acceptable values, respectively. If there is no minimum or maximum, the respective entry can be set to `NA`. In this case we filter for time periods in which PPFD (photosynthetic photon flux density) has to be higher than 200 $\mu$mol m$^{-2}$ s$^{-1}$, but no maximum limit is considered. - -If `filter_growseas=TRUE`, the function implements a simple growing season filter based on daily smoothed GPP time series. The arguments `GPP`, `doy` and `year` are required at halfhourly/hourly time scale. GPP is aggregated to daily sums internally. The arguments `tGPP`, `ws`, and `min_int` determine how the growing season is filtered. `tGPP` determines how high daily GPP has to be in relation to its peak value within the year. In this case, the value of 0.4 denotes that smoothed GPP has to be at least 40% of the 95th quantile. `ws` controls the degree of smoothing in the timeseries (the purpose of which is to minimize the high variation of GPP between days), and should probably be between 10-20 days. `min_int` is a parameter that avoids that data are switching from inside the growing season and out from one day to the next, but determines the minimum number of days that the growing season should have. The growing season filter is applicable to all sites, with one more more growing seasons, but it's advisable that other parameter settings are used depending on the site. - -In this case, it does not really make sense to filter for growing season, since it's only one month of which we know that vegetation is active at the site. Luckily, the algorithm realizes that as well and does not filter out any data if `filter_growseas=TRUE` (same will happen at sites with a year-round growing season). In the function output we further see that almost half of the data were filtered because radiation was not high enough (night-time). Another 23.5% were filtered because they showed negative LE values. However, most of them occur during the night, and only 5.2% of them were not already filtered by the radiation filter (denoted as "additional data points" above). - -As a last step we will filter for precipitation events. This is often meaningful for ecophysiological studies because data during and shortly after rainfall events do not contain much information on the physiological activity of the vegetation (i.e. they comprise significant fractions of evaporation from the soil and plant surfaces). The purpose of such a filter is mostly to minimize the fraction of soil and interception evaporation on the total water flux. This filter simply excludes periods following a precipitation event. A precipitation event is here defined as any time step with a recorded precipitation higher than `tprecip` (in mm per timestep). The function then filters all time periods following a precipitation event. The number of subsequent time periods excluded is controlled by the argument `precip_hours`. Here, we exclude rainfall events and the following 24 hours. +For most applications it is meaningful to filter your data. There are two main reasons +why we want to filter our data before we start calculating ecosystem properties. +The first one is to exclude datapoints that do not fulfill the requirements of the +EC technique or that are of bad quality due to e.g. instrument failure or gap-filling +with poor confidence. Note that the quality assessment of the EC data is not the purpose +of the `Bigleaf.jl` package. This is done by other packages (e.g. `REddyProc`), +which often provide quality control flags for the variables. These quality control +flags are used here to filter out bad-quality datapoints. + +A second reason for filtering our data is that some derived properties are only +meaningful if certain meteorological conditions are met. For instance, if we are +interested in properties related to plant gas exchange, it makes most sense to focus on +time periods when plants are photosynthetically active +(i.e. in the growing season and at daytime). + +`Bigleaf.jl` provides methods that update (or create) the :valid column in +a DataFrame. Records, i.e. rows, that contain non valid conditions are set to false. +If the valid column was false before, it stays at false. + +One can check quality flags. By default (argument `setvalmissing = true`) this also +replaces the non-valid values in the data-columns by `missing`. +```julia +thaf = copy(tha) # keep the original tha DataFrame +# if the :valid columns does not exist yet, it is created with all values true +setinvalid_qualityflag!(thaf; vars = ["LE", "NEE"]) +sum(.!thaf.valid) # 7 records marked non-valid +sum(ismissing.(thaf.NEE)) # 7 NEE values set to missing +``` +In the function call above, `vars` lists the variables that should be filtered with +respect to their quality. Optional parameter `qc_suffix="_qc"` denotes the extension +of the variable name that identifies the column as a quality control indicator of a given +variable. The variables "LE" and "LE_qc", for example, denote the variable itself +(latent heat flux), and the quality of the variable "LE", respectively. The optional +argument `good_quality_threshold = 1.0` specifies the values of the quality column +below which the quality control to be considered as acceptable quality +(i.e. to not be filtered). For example with default value 1, +all "LE" values whose "LE_qc" variable is larger than 1 are set to `missing`. +The variable `missing_qc_as_bad` is required to decide what to do in +case of missing values in the quality control variable. By default this is (conservatively) +set to `TRUE`, i.e. all entries where the qc variable is missing is set invalid. + +We can filter for meteorological conditions to be in acceptable ranges. +For each variable to check we supply the valid minimum and valid maxium as a two-tuple +as the second component of a pair. If their is no limit towards small or +large values, supply `-Inf` or `Inf` as the minimum or maximum respectively. +```julia +setinvalid_range!(thaf, + :PPFD => (200, Inf), + :ustar => (0.2, Inf), + :LE =>(0, Inf), + :VPD => (0.01, Inf) + ) +sum(.!thaf.valid) # many more records marked invalid +minimum(skipmissing(thaf.PPFD)) >= 200 # values outsides range some set to missing +sum(ismissing.(thaf.PPFD) +``` + +About half of the data were filtered because radiation was not high enough (night-time). +Another quarter was filtered because they showed negative LE values. +However, most of them occured during the night: +```julia +sum(ismissing.(thaf.PPFD)) / nrow(thaf) +sum(.!ismissing.(thaf.PPFD) .&& ismissing.(thaf.LE)) / nrow(thaf) +``` + +A third method filters periods outside the growing season: +```julia +setinvalid_nongrowingseason!(thaf, 0.4) +sum(.!thaf2.valid) # tha dataset is all within growing season - no additional invalids +``` + +This function implements a simple growing season filter based on daily smoothed GPP time +series. +Arguments `tGPP` determines how high daily GPP has to be in relation to its peak value +within the year. In this case, the value of 0.4 denotes that smoothed GPP has to be at +least 40% of the 95th quantile. +Argument `ws` controls the degree of smoothing in the timeseries +and should be between 10-20 days. +The purpose of which is to minimize the high variation of GPP between days, +Argument `min_int` is a parameter that avoids that data are switching from +inside the growing season and out from one day to the next. +It determines the minimum number of days that a season should have. +The growing season filter is applicable to all sites, with one more more growing seasons, +but its advisable that site-specific parameter settings are used. + +In this example, it does not really make sense to filter for growing season, +since it uses only one month of data of which we know that vegetation is active at the site. +The algorithm realizes that and does not mark any additional data as invalid. + + +As a last step we will filter for precipitation events. +This is often meaningful for ecophysiological studies because data during and shortly +after rainfall events do not contain much information on the physiological activity +of the vegetation because they comprise significant fractions of evaporation from the +soil and plant surfaces. The purpose of such a filter is mostly to minimize the fraction +of soil and interception evaporation on the total water flux. This filter simply excludes +periods following a precipitation event. A precipitation event, here, is defined as any time +step with a recorded precipitation higher than `tprecip` (in mm per timestep). +The function then filters all time periods following a precipitation event. +The number of subsequent time periods excluded is controlled by the argument `precip_hours`. +Here, we exclude rainfall events and the following 24 hours. ```julia tha_filtered3 = filter_data(tha,quality_control=false,filter_growseas=false, diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 7fa63cf..271b5de 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -10,7 +10,7 @@ using AstroLib using Suppressor using Missings using Statistics, StatsBase # mean, rle -using PaddedViews +using PaddedViews, StaticArrays using Infiltrator export frac_hour, moving_average @@ -26,8 +26,8 @@ export air_density, pressure_from_elevation, psychrometric_constant, export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ET! -export filter_growing_season -#export surface_conductance +export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason +export decoupling, surface_conductance, aerodynamic_conductance include("util.jl") include("bigleaf_constants.jl") diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index e2a30f7..8b37d95 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -137,7 +137,7 @@ function potential_ET(Tair, pressure, Rn, approach::Val{:PriestleyTaylor}; G=zero(Tair),S=zero(Tair), kwargs...) # potential_ET(Tair, pressure, Rn, G, S, approach; kwargs...) -end, +end function potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; alpha=1.26, Esat_formula=Val(:Sonntag_1990), @@ -148,12 +148,12 @@ function potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) ET_pot = LE_to_ET(LE_pot,Tair) (ET_pot = ET_pot, LE_pot = LE_pot) -end, +end function potential_ET(Tair, pressure, Rn, VPD, Ga, approach::Val{:PenmanMonteith}; G=zero(Tair),S=zero(Tair), kwargs...) # potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, approach; kwargs...) -end, +end function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; Gs_pot=0.6, Esat_formula=Val(:Sonntag_1990), @@ -167,7 +167,7 @@ function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; (Delta + gamma * (1 + Ga / Gs_pot)) ET_pot = LE_to_ET(LE_pot,Tair) (ET_pot = ET_pot, LE_pot = LE_pot) -end, +end function potential_ET(df, approach::Val{:PriestleyTaylor}; G=missing,S=missing, infoGS=true, kwargs...) # @@ -176,7 +176,7 @@ function potential_ET(df, approach::Val{:PriestleyTaylor}; select(hcat(select(df,:Tair, :pressure, :Rn), dfGS; copycols = false), All() => ByRow(f) => AsTable ) -end, +end function potential_ET(df, approach::Val{:PenmanMonteith}; G=missing,S=missing, infoGS=true, kwargs...) # @@ -187,7 +187,7 @@ function potential_ET(df, approach::Val{:PenmanMonteith}; select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), All() => ByRow(f) => AsTable ) -end, +end function potential_ET!(df, approach::Val{:PriestleyTaylor}; G=missing,S=missing, infoGS=true, kwargs...) dfGS = get_df_GS(df, G,S; infoGS) @@ -199,7 +199,7 @@ function potential_ET!(df, approach::Val{:PriestleyTaylor}; [:Tair, :pressure, :Rn, :_tmp_G, :_tmp_S] => ByRow(f) => AsTable ) select!(df, Not([:_tmp_G, :_tmp_S])) -end, +end function potential_ET!(df, approach::Val{:PenmanMonteith}; G=missing,S=missing, infoGS=true, kwargs...) dfGS = get_df_GS(df, G,S; infoGS) @@ -291,11 +291,7 @@ that would occur under fully coupled conditions (when Ga -> inf): ``ET_{imp} = (\\rho * cp * VPD * Gs * \\lambda) / \\gamma`` where ``\\rho`` is the air density (kg m-3). - -# Note -Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) -Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). - + # Value A `NamedTuple` or `DataFrame` with the following columns: - `ET_eq`: Equilibrium ET (kg m-2 s-1) @@ -320,8 +316,11 @@ true """ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn; G=zero(Tair),S=zero(Tair), kwargs...) +# # Note +# Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) +# Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; kwargs...) -end, +end function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; Esat_formula=Val(:Sonntag_1990), constants=bigleaf_constants()) @@ -335,7 +334,7 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; ET_imp = LE_to_ET(LE_imp,Tair) ET_eq = LE_to_ET(LE_eq,Tair) (;ET_eq, ET_imp, LE_eq, LE_imp) -end, +end function equilibrium_imposed_ET(df; G=missing,S=missing, infoGS=true, kwargs...) # @@ -345,7 +344,7 @@ function equilibrium_imposed_ET(df; end dfb = hcat(select(df,:Tair, :pressure, :VPD, :Gs, :Rn), dfGS; copycols = false) select(dfb, All() => ByRow(f) => AsTable ) -end, +end function equilibrium_imposed_ET!(df; G=missing,S=missing, infoGS=true, kwargs...) # diff --git a/src/filter_data.jl b/src/filter_data.jl index 70c3e15..a9939aa 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -1,16 +1,203 @@ """ - filter_growing_season(GPPd, tGPP; ws=15, min_int=5, warngap=true) + setinvalid_qualityflag!(df; + vars=["LE","H","NEE","Tair","VPD","wind"], + qc_suffix="_qc", + good_quality_threshold = 1.0, + missing_qc_as_bad = true, + setvalmissing = true, + ) + +Set records with quality flags indicating problems to false in :valid column. + +# Arguments +- df: DataFrame with column :GPP +optional +- `vars=["LE","H","NEE","Tair","VPD","wind"]`: columns to theck for quality +- `qc_suffix="_qc"`: naming of the corresponding quality-flag column +- `good_quality_threshold = 1.0`: threshold in quality flag up to which + data is considered good quality +- `missing_qc_as_bad = true`: set to false to not mark records with missing + quality flag as invalid +- `setvalmissing = true`: set to false to prevent replacing values in value column + corresponding to problematic quality flag to missing. + +# Value +df with modified :valid and value columns. + +# Example +```jldoctest; output = false +using DataFrames +df = DataFrame( + NEE = 1:3, NEE_qc = [1,1,2], + GPP = 10:10:30, GPP_qc = [1,missing,1]) +setinvalid_qualityflag!(df; vars = ["NEE", "GPP"]) +df.valid == [true, false, false] +ismissing(df.GPP[2]) && ismissing(df.NEE[3]) +# output +true +``` +""" +function setinvalid_qualityflag!(df; setvalmissing = true, kwargs...) + if setvalmissing + set_badquality_missing!(df; kwargs...) + end + setinvalid_qualityflag!(df, Val(false); kwargs...) +end + +function set_badquality_missing!(df; + vars=["LE","H","NEE","Tair","VPD","wind"], + qc_suffix="_qc", + good_quality_threshold = 1.0, + missing_qc_as_bad = true + ) + fqc(x,xqc) = if missing_qc_as_bad + ifelse(!ismissing(xqc) && xqc <= good_quality_threshold, x, missing) + else + ifelse(ismissing(xqc) || xqc <= good_quality_threshold, x, missing) + end + tmp = map(vars) do var + qcvar = var * qc_suffix + [var, qcvar] => ByRow(fqc) => var + end + transform!(df, tmp...) +end + +function setinvalid_qualityflag!(df, setvalsmissing::Val{false}; + vars=["LE","H","NEE","Tair","VPD","wind"], + qc_suffix="_qc", + good_quality_threshold = 1.0, + missing_qc_as_bad=true + ) + vars_qc = vars .* qc_suffix + fvalid_var = missing_qc_as_bad ? valid_nonmissingvar_ : valid_missingvar_ + function fsel(valid, x...) + nvar = length(x) ÷ 2 + vs = x[1:nvar] + vs_qc = x[(nvar+1):end] + #valid = repeat(@MVector([true]), length(vs[1])) + for i = 1:nvar + @. valid = valid && fvalid_var(vs[i], vs_qc[i], good_quality_threshold) + end + valid + end + if !hasproperty(df, :valid) df[!,:valid] .= true; end + select!(df, :, Cols(vcat("valid", vars, vars_qc)) => fsel => :valid) +end + +function valid_nonmissingvar_(x, xqc, good_quality_threshold) + !ismissing(x) && !ismissing(xqc) && xqc <= good_quality_threshold +end +function valid_missingvar_(x, xqc, good_quality_threshold) + !ismissing(x) && (ismissing(xqc) || xqc <= good_quality_threshold) +end + +""" + setinvalid_range!(df, var_ranges...; setvalmissing = true, ...) + +Set records with values outside specified ranges to false in :valid column. + +If their is no limit towards small or +large values, supply `-Inf` or `Inf` as the minimum or maximum respectively. +If there were false values in the :value column before, they are kept false. +In addition, set values outside ranges to missing. + +# Arguments +- df: DataFrame with column :GPP +- var_ranges: Pair `Varname_symbol => (min,max)`: closed valid interval for + respective column +optional +- setvalmissing: set to false to prevent replacing values in value column outside ranges to missing. + +# Value +df with modified :valid and value columns. +```jldoctest; output = false +using DataFrames +df = DataFrame(NEE = [missing, 2,3], GPP = 10:10:30) +setinvalid_range!(df, :NEE => (-2.0,4.0), :GPP => (8.0,28.0)) +df.valid == [false, true, false] +ismissing(df.GPP[3]) +# output +true +``` +""" +function setinvalid_range!(df, var_ranges::Vararg{Pair,N}; setvalmissing = true, kwargs...) where N + if setvalmissing + set_badrange_missing!(df, var_ranges...; kwargs...) + end + setinvalid_range!(df, Val(false), var_ranges...; kwargs...) +end + +function set_badrange_missing!(df, var_ranges::Vararg{Pair,N}) where N + tmp = map(var_ranges) do p + var, (min, max) = p + var => (x -> @.(ifelse(!ismissing(x) && min <= x <= max, x, missing))) => var + end + transform!(df, tmp...) +end + +function setinvalid_range!(df, setvalsmissing::Val{false}, var_ranges::Vararg{Pair,N}) where N + function fval(valid, x...) + for (p,xi) in zip(var_ranges, x) + var, (min, max) = p + @. valid = valid && !ismissing(xi) && (min <= xi <= max) + end + valid + end + vars = map(x -> x.first, var_ranges) + if !hasproperty(df, :valid) df[!,:valid] .= true; end + select!(df, :, SA[:valid, vars...] => fval => :valid) +end + +""" + setinvalid_nongrowingseason!(df, tGPP; kwargs...) + +Set non-growseason to false in :valid column. + +# Arguments +- df: DataFrame with column :GPP +- tGPP: scalar threshold of daily GPP (see [`get_growingseason`](@ref)) +optional: +- `update_GPPd`: set to true additionally update GPPd_smoothed column to + results from [`get_growingseason`](@ref) +- and others passed to [`get_growingseason`](@ref) + +# Value +df with modified columns :valid and if :GPPd_smoothed, +where all non-growing season records are set to false. +""" +function setinvalid_nongrowingseason!(df, tGPP; update_GPPd_smoothed = false, kwargs...) + if !hasproperty(df, :valid) df[!,:valid] .= true; end + # non-copying dataframe where we can add grouping column __day + dft = transform(df, :time => ByRow(Date) => :__day, copycols = false) + function mean_nonmissing(x) + mx = mean(skipmissing(x)) + # if there is no non-missing record in a group, mean returns nan + ifelse(isnan(mx), missing, mx) + end + dfday = combine(groupby(dft, :__day), :GPP => mean_nonmissing => :GPPd, nrow) + transform!(dfday, + :GPPd => (x -> get_growingseason(x, tGPP; kwargs...)) => SA[:valid, :GPPd_smoothed]) + # rep(dfd.valid, each = df.nrow) + df[!,:valid] .= df.valid .&& vcat(fill.(dfday.valid, dfday.nrow)...) + if update_GPPd_smoothed + df[!,:GPPd_smoothed] .= vcat(fill.(dfday.GPPd_smoothed, dfday.nrow)...) + end + df +end + +""" + get_growingseason(GPPd, tGPP; ws=15, min_int=5, warngap=true) Filters annual time series for growing season based on smoothed daily GPP data. # Arguments -- GPPd: daily GPP (any unit) -- tGPP: GPP threshold (fraction of 95th percentile of the GPP time series). +- `GPPd`: daily GPP (any unit) +- `tGPP`: GPP threshold (fraction of 95th percentile of the GPP time series). Takes values between 0 and 1. optional -- ws: window size used for GPP time series smoothing -- min_int: minimum time interval in days for a given state of growing season -- warngap: set to false to suppress warning on too few non-missing data +- `ws`: window size used for GPP time series smoothing +- `min_int`: minimum time interval in days for a given state of growing season +- `warngap`: set to false to suppress warning on too few non-missing data # Details The basic idea behind the growing season filter is that vegetation is @@ -19,7 +206,7 @@ threshold, which is defined relative to the peak GPP (95th percentile) observed in the year. The GPP-threshold is calculated as: -``GPP_threshold = quantile(GPPd,0.95)*tGPP`` +``GPP_{threshold} = quantile(GPPd,0.95)*tGPP`` GPPd time series are smoothed with a moving average to avoid fluctuations in the delineation of the growing season. The window size defaults to 15 @@ -32,10 +219,12 @@ as growing season or non-growing season, it is changed to the status of the neighboring values, i.e its opposite. # Value -a `BitVector` of the same length as the input GPPd in which `false` indicate -no growing season (dormant season) and `true` indicate growing season. +A NamedTuple with entries +- `is_growingseason`: a `BitVector` of the same length as the input GPPd in which `false` + indicate no growing season (dormant season) and `true` indicate growing season. +- `GPPd_smoothed`: smoothed GPPd """ -function filter_growing_season(GPPd,tGPP;ws=15,min_int=5,warngap=true) +function get_growingseason(GPPd,tGPP;ws=15,min_int=5,warngap=true) nday = length(GPPd) if sum(.!ismissing.(GPPd)) < 0.5*nday error("Expected number of available GPPd data " * @@ -43,19 +232,23 @@ function filter_growing_season(GPPd,tGPP;ws=15,min_int=5,warngap=true) "But was only ($(sum(.!ismissing.(GPPd)))).") end GPP_threshold = quantile(skipmissing(GPPd), 0.95)*tGPP + #@show GPP_threshold # smooth GPP #fromR: GPPd_smoothed = filter(GPPd,method="convolution",filter=rep(1/ws,ws)) GPPd_smoothed = moving_average(GPPd, ws) - # set values at the beginning and end of the time series to the mean of the original values + # set values at the beginning and end of the time series to the mean of original values wsd = floor(Int, ws/2) GPPd_smoothed[1:wsd] .= mean(skipmissing(GPPd[1:(2*wsd)])) - GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] .= mean(skipmissing(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)])) - # check for occurence of missing values and set them to mean of the values surrounding them + GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] .= + mean(skipmissing(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)])) + # check for occurence of missing values and set to mean of the values surrounding them imissing = findall(ismissing, GPPd_smoothed) if length(imissing) > 0 - warngap && length(imissing) > 10 && @warn "Attention, there is a gap in 'GPPd' of length n = $(length(imissing))" + warngap && length(imissing) > 10 && + @warn "Attention, there is a gap in 'GPPd' of length n = $(length(imissing))" #TODO check and correct for several gaps, see Impute package - replace_val = mean(skipmissing(GPPd_smoothed[max(1,imissing[1] - 4):min((imissing[length(imissing)] + 4),length(GPPd_smoothed))])) + replace_val = mean(skipmissing(GPPd_smoothed[max(1,imissing[1] - 4):min( + (imissing[length(imissing)] + 4),length(GPPd_smoothed))])) GPPd_smoothed = coalesce.(GPPd_smoothed, replace_val) end # filter daily GPP @@ -71,6 +264,8 @@ function filter_growing_season(GPPd,tGPP;ws=15,min_int=5,warngap=true) intervals = rle(growseas)[2] imin = argmin(intervals) end - return(growseas) + (is_growingseason = growseas, GPPd_smoothed = GPPd_smoothed) end + + diff --git a/test/Project.toml b/test/Project.toml index b4bda33..749d32a 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -3,6 +3,7 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Pipe = "b98c9c47-44ae-5843-9183-064241ee97a0" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" diff --git a/test/filter_data.jl b/test/filter_data.jl index 73f5204..74fb290 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -1,19 +1,105 @@ -@testset "GPPfilter" begin +@testset "get_growingseason" begin rng = StableRNG(815) GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) GPPd[100:120] .= missing #plot(1:365, GPPd) - growseas = @test_logs (:warn, r"gap in 'GPPd'") filter_growing_season(GPPd, 0.5) + growseas = @test_logs (:warn, r"gap in 'GPPd'") get_growingseason(GPPd, 0.5) #plot(growseas) - @test rle(growseas) == (Bool[0, 1, 0], [51, 258, 56]) - growseas = filter_growing_season(GPPd, 0.5; min_int = 56, warngap = false) - @test rle(growseas) == (Bool[1, 0], [309, 56]) + @test rle(growseas.is_growingseason) == (Bool[0, 1, 0], [51, 258, 56]) + growseas = get_growingseason(GPPd, 0.5; min_int = 56, warngap = false) + @test rle(growseas.is_growingseason) == (Bool[1, 0], [309, 56]) end -@testset "GPPfilter with few data" begin +@testset "get_growingseason with few data" begin rng = StableRNG(815) GPPd = @pipe sin.((1:365).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) GPPd[1:200] .= missing - @test_throws ErrorException filter_growing_season(GPPd, 0.5) + @test_throws ErrorException get_growingseason(GPPd, 0.5) +end + +@testset "setinvalid_nongrowingseason!" begin + rng = StableRNG(815) + nday = 365 + GPPd = @pipe sin.((1:nday).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) + GPPd[100:120] .= missing + df = DataFrame(time = DateTime(2021) .+ Hour(1).+ Day.(0:nday-1), GPP = GPPd) + df2 = copy(df) + df2a = setinvalid_nongrowingseason!(df2, 0.5; warngap = false) + @test df2a === df2 + @test propertynames(df2a) == union(propertynames(df), SA[:valid]) + @test rle(df2a.valid) == (Bool[0, 1, 0], [51, 258, 56]) + # + df2.valid[80:90] .= false + df2a = setinvalid_nongrowingseason!(df2, 0.5; warngap = false, update_GPPd_smoothed = true) + @test df2a === df2 + @test propertynames(df2a) == union(propertynames(df), SA[:valid, :GPPd_smoothed]) + @test rle(df2a.valid) == (Bool[0, 1, 0, 1, 0], [51, 28, 11, 219, 56]) +end + +@testset "setinvalid_qualityflag!" begin + df = DataFrame( + NEE = 1:3, + GPP = 10:10:30, + NEE_qc = [1,1,2], + GPP_qc = [1,missing,1], + ) + df2 = copy(df) + df2a = setinvalid_qualityflag!(df2; vars = SA["NEE", "GPP"], setvalmissing = false) + @test df2a === df2 + @test df2a.valid == [true, false, false] + @test df2a.NEE == 1:3 # not set to missing + # test with existing :valid column, where already false entries are kept + df2.valid[1] = false + df2a = setinvalid_qualityflag!(df2; vars = SA["NEE", "GPP"], setvalmissing = false) + @test df2a === df2 + @test df2a.valid == [false, false, false] + @test df2a.NEE == 1:3 # not set to missing + # + # test with setting values to missing + df2 = copy(df) + df2a = setinvalid_qualityflag!(df2; vars = SA["NEE", "GPP"], setvalmissing = true) + @test df2a === df2 + @test df2a.valid == [true, false, false] + @test isequal(df2a.NEE, [1,2,missing]) + @test isequal(df2a.GPP, [10,missing,30]) +end + +@testset "setinvalid_range!" begin + df = DataFrame( + NEE = 1:3, + GPP = 10:10:30, + ) + allowmissing!(df, :NEE) + var_ranges = [:NEE => (-2.0,4.0), :GPP => (8.0,28.0)] + df2 = copy(df) + df3 = copy(df2) + df2a = setinvalid_range!(df2, var_ranges...; setvalmissing = false) + # @btime setinvalid_range!($df2, $(var_ranges)...; setvalmissing = false) + @test df2a === df2 + @test df2a.valid == [true, true, false] + df3a = setinvalid_range!(df3, var_ranges...) + @test df3a === df3 + @test isequal(df3a.valid, df2a.valid) + @test ismissing(df3a.GPP[3]) + @test all(.!ismissing.(df3a.GPP[1:2])) + # + df2.NEE[1] = missing + df3 = copy(df2) + df2b = setinvalid_range!(df2, var_ranges...; setvalmissing = false) + @test df2b === df2 + @test df2b.valid == [false, true, false] + df3a = setinvalid_range!(df3, var_ranges...) + @test isequal(df3a.valid, df2a.valid) + @test ismissing(df3a.GPP[3]) + @test all(.!ismissing.(df3a.GPP[1:2])) + @test all(.!ismissing.(df3a.NEE[2:3])) + # + df2.valid[2] = false + df3 = copy(df2) + df2b = setinvalid_range!(df2, var_ranges...; setvalmissing = false) + @test df2b === df2 + @test df2b.valid == [false, false, false] + df3a = setinvalid_range!(df3, var_ranges...) + @test isequal(df3a.valid, df2a.valid) end diff --git a/test/runtests.jl b/test/runtests.jl index 6d858a2..61d9f07 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,7 @@ using Bigleaf using Test, StableRNGs using Pipe, DataFrames, Dates, TimeZones +using StaticArrays using Statistics, StatsBase @testset "Bigleaf" begin From 0fe8b4d8e2cd971a037f8a930ddc0b03430b43a3 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 11:16:22 +0100 Subject: [PATCH 18/43] update compat --- .github/workflows/CI.yml | 3 ++- Project.toml | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1f4174e..5dec52b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,8 @@ jobs: matrix: version: - '1' - - '^1.7.0-0' + #- '^1.6' # currently covered by '1' + - '^1.7.0-0' os: - ubuntu-latest arch: diff --git a/Project.toml b/Project.toml index 720e61b..ca8804b 100644 --- a/Project.toml +++ b/Project.toml @@ -22,20 +22,20 @@ Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [compat] -AstroLib = "0.4" -DataFrames = "1" -FillArrays = "0.12" -LabelledArrays = "1" -Missings = "1" -Optim = "1" -PaddedViews = "0.5" -Pipe = "1" -RecursiveArrayTools = "2" -StaticArrays = "1" -StatsBase = "0.33" +AstroLib = "0.4.1" +DataFrames = "1.2.2" +FillArrays = "0.12.7" +LabelledArrays = "1.6.5" +Missings = "1.0.2" +Optim = "1.4.1" +PaddedViews = "0.5.10" +Pipe = "1.3" +RecursiveArrayTools = "2.20.0" +StaticArrays = "1.2.13" +StatsBase = "0.33.12" Suppressor = "0.2" -TimeZones = "1" -julia = "1.6, 1.7" +TimeZones = "1.6.1" +julia = "1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From d725052d79416a9e1d3400820cfdc209b4e95e89 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 11:47:07 +0100 Subject: [PATCH 19/43] revert @. with && to .&& --- docs/src/walkthrough.md | 4 ++-- src/filter_data.jl | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 1a66a76..70b744f 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -42,10 +42,10 @@ nothing ``` ```@example doc tha = DE_Tha_Jun_2014 -mdtable(select(describe(tha), :variable, :eltype, :min, :max), latex=false) # hide +# mdtable(select(describe(tha), :variable, :eltype, :min, :max), latex=false) # hide +nothing # hide ``` - And the first six rows of tha: ```@example doc mdtable(tha[1:6,:],latex=false) # hide diff --git a/src/filter_data.jl b/src/filter_data.jl index a9939aa..bb2e3f9 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -76,7 +76,8 @@ function setinvalid_qualityflag!(df, setvalsmissing::Val{false}; vs_qc = x[(nvar+1):end] #valid = repeat(@MVector([true]), length(vs[1])) for i = 1:nvar - @. valid = valid && fvalid_var(vs[i], vs_qc[i], good_quality_threshold) + # works only from 1.7 @. valid = valid && fvalid_var(vs[i], vs_qc[i], good_quality_threshold) + valid .= valid .&& fvalid_var.(vs[i], vs_qc[i], good_quality_threshold) end valid end @@ -102,8 +103,8 @@ If there were false values in the :value column before, they are kept false. In addition, set values outside ranges to missing. # Arguments -- df: DataFrame with column :GPP -- var_ranges: Pair `Varname_symbol => (min,max)`: closed valid interval for +- `df`: DataFrame with column :GPP +- `var_ranges`: Pair `Varname_symbol => (min,max)`: closed valid interval for respective column optional - setvalmissing: set to false to prevent replacing values in value column outside ranges to missing. @@ -157,12 +158,12 @@ Set non-growseason to false in :valid column. - df: DataFrame with column :GPP - tGPP: scalar threshold of daily GPP (see [`get_growingseason`](@ref)) optional: -- `update_GPPd`: set to true additionally update GPPd_smoothed column to +- `update_GPPd`: set to true additionally update `:GPPd_smoothed` column to results from [`get_growingseason`](@ref) - and others passed to [`get_growingseason`](@ref) # Value -df with modified columns :valid and if :GPPd_smoothed, +df with modified columns :valid and if `:GPPd_smoothed`, where all non-growing season records are set to false. """ function setinvalid_nongrowingseason!(df, tGPP; update_GPPd_smoothed = false, kwargs...) From 7b2f8d5dd1ae6c13ad6954f56227c5667a08bdf7 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 14:01:59 +0100 Subject: [PATCH 20/43] remove .&& - does not work with julia 1.6 --- src/filter_data.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/filter_data.jl b/src/filter_data.jl index bb2e3f9..69ea4e0 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -76,8 +76,8 @@ function setinvalid_qualityflag!(df, setvalsmissing::Val{false}; vs_qc = x[(nvar+1):end] #valid = repeat(@MVector([true]), length(vs[1])) for i = 1:nvar - # works only from 1.7 @. valid = valid && fvalid_var(vs[i], vs_qc[i], good_quality_threshold) - valid .= valid .&& fvalid_var.(vs[i], vs_qc[i], good_quality_threshold) + # TODO change when 1.7 is out which support .&&: @. valid = valid && fvalid_var(vs[i], vs_qc[i], good_quality_threshold) + @. valid = valid & fvalid_var(vs[i], vs_qc[i], good_quality_threshold) end valid end @@ -131,16 +131,19 @@ end function set_badrange_missing!(df, var_ranges::Vararg{Pair,N}) where N tmp = map(var_ranges) do p var, (min, max) = p - var => (x -> @.(ifelse(!ismissing(x) && min <= x <= max, x, missing))) => var + var => ByRow(x -> (ifelse(!ismissing(x) && min <= x <= max, x, missing))) => var end transform!(df, tmp...) end function setinvalid_range!(df, setvalsmissing::Val{false}, var_ranges::Vararg{Pair,N}) where N function fval(valid, x...) - for (p,xi) in zip(var_ranges, x) + for (p,xj) in zip(var_ranges, x) var, (min, max) = p - @. valid = valid && !ismissing(xi) && (min <= xi <= max) + #TODO simplify once 1.7 with .&& is out: @. valid = valid && !ismissing(xi) && (min <= xi <= max) + for i in eachindex(xj) + valid[i] = valid[i] && !ismissing(xj[i]) && (min <= xj[i] <= max) + end end valid end @@ -179,7 +182,8 @@ function setinvalid_nongrowingseason!(df, tGPP; update_GPPd_smoothed = false, kw transform!(dfday, :GPPd => (x -> get_growingseason(x, tGPP; kwargs...)) => SA[:valid, :GPPd_smoothed]) # rep(dfd.valid, each = df.nrow) - df[!,:valid] .= df.valid .&& vcat(fill.(dfday.valid, dfday.nrow)...) + #TODO simplify once 1.7 is out: df[!,:valid] .= df.valid .&& vcat(fill.(dfday.valid, dfday.nrow)...) + df[!,:valid] .= df.valid .& vcat(fill.(dfday.valid, dfday.nrow)...) if update_GPPd_smoothed df[!,:GPPd_smoothed] .= vcat(fill.(dfday.GPPd_smoothed, dfday.nrow)...) end From 579fd21ac092d84a9fbcd95cb3f18c2de4c4ffd5 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 14:25:31 +0100 Subject: [PATCH 21/43] readability of sun-position plot in walkthrough by rotating x axis --- docs/src/walkthrough.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 70b744f..ea9ea40 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -306,7 +306,7 @@ lat,long = 51.0, 13.6 # Dresden Germany doy = 160 datetimes = DateTime(2021) .+Day(doy-1) .+ Hour.(hours) #.- Second(round(long*deg2second)) res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) -@df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad") +@df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad", xrotation=6) ``` The hour-angle at noon represents the difference to From d2437585a1aae0082cf03c1c67c982c261a16240 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 16:22:35 +0100 Subject: [PATCH 22/43] implement setinvalid_afterprecip! --- docs/src/filter_data.md | 1 + docs/src/walkthrough.md | 23 ++++++++++++++- inst/tha.jl | 2 ++ inst/walkthrough_todo.jmd | 20 ++++--------- src/Bigleaf.jl | 5 ++-- src/filter_data.jl | 35 ++++++++++++++++++++++ src/util.jl | 61 ++++++++++++++++++++++++++++++++++++--- test/filter_data.jl | 17 +++++++++++ test/util.jl | 30 +++++++++++++++++++ 9 files changed, 172 insertions(+), 22 deletions(-) diff --git a/docs/src/filter_data.md b/docs/src/filter_data.md index d82b1a1..bcbeb4d 100644 --- a/docs/src/filter_data.md +++ b/docs/src/filter_data.md @@ -8,4 +8,5 @@ setinvalid_qualityflag! setinvalid_range! setinvalid_nongrowingseason! get_growingseason +setinvalid_afterprecip! ``` \ No newline at end of file diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index ea9ea40..44f8f17 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -17,7 +17,7 @@ The use of more detailed models is not within the scope of the `Bigleaf.jl` pack In this tutorial, we will work with a dataset from the eddy covariance site Tharandt (DE-Tha), a spruce forest in Eastern Germany. The DataFrame `DE_Tha_Jun_2014` is downloaded from the `bigleaf` [R package](https://bitbucket.org/juergenknauer/Bigleaf/) repository and contains half-hourly data of meteorological and flux measurements made in June 2014. For loading the RData into Julia, see the -[source](https://github.com/bgctw/Bigleaf.jl/blob/main/docs/src/walkthrough.md?plain=1#L26) of this file. We give the data.frame a shorter name here. +[source](https://github.com/bgctw/Bigleaf.jl/blob/main/docs/src/walkthrough.md?plain=1#L26) of this file. We give the data.frame a shorter name here and create a timestamp. ```@example doc using Bigleaf @@ -42,6 +42,7 @@ nothing ``` ```@example doc tha = DE_Tha_Jun_2014 +set_datetime_ydh!(tha) # mdtable(select(describe(tha), :variable, :eltype, :min, :max), latex=false) # hide nothing # hide ``` @@ -221,6 +222,26 @@ In this example, it does not really make sense to filter for growing season, since it uses only one month of data of which we know that vegetation is active at the site. The algorithm realizes that and does not mark any additional data as invalid. +### `setinvalid_afterprecip!` + +As a last step we will filter for precipitation events. +This is often meaningful for ecophysiological studies because data during and shortly +after rainfall events do not contain much information on the physiological activity +of the vegetation because they comprise significant fractions of evaporation from the +soil and plant surfaces. The purpose of such a filter is mostly to minimize the fraction +of soil and interception evaporation on the total water flux. This filter simply excludes +periods following a precipitation event. A precipitation event, here, is defined as any time +step with a recorded precipitation higher than `min_precip` (in mm per timestep). +The function then filters all time periods following a precipitation event. +The number of subsequent time periods excluded is controlled by the argument `precip_hours`. +Here, we exclude rainfall events and the following 24 hours. +The timestamps in the DataFrame must be sorted in increasing order. + +```julia +setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) +sum(.!thaf.valid) # some more invalids +``` + ## Meteorological variables diff --git a/inst/tha.jl b/inst/tha.jl index 6ee66b3..e6133b1 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -17,6 +17,8 @@ ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) nothing tha = DE_Tha_Jun_2014 + + tha.time = @. DateTime(tha.year) + Day(tha.doy-1) + frac_hour(Minute, tha.hour) tmp = first(tha.time) diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 999f5b7..08da8fc 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -19,6 +19,7 @@ register(DataDep( ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) tha = DE_Tha_Jun_2014 +set_datetime_ydh!(tha) LAI = 7.6 # leaf area index zh = 26.5 # average vegetation height (m) zr = 42 # sensor height (m) @@ -124,28 +125,17 @@ of the vegetation because they comprise significant fractions of evaporation fro soil and plant surfaces. The purpose of such a filter is mostly to minimize the fraction of soil and interception evaporation on the total water flux. This filter simply excludes periods following a precipitation event. A precipitation event, here, is defined as any time -step with a recorded precipitation higher than `tprecip` (in mm per timestep). +step with a recorded precipitation higher than `min_precip` (in mm per timestep). The function then filters all time periods following a precipitation event. The number of subsequent time periods excluded is controlled by the argument `precip_hours`. Here, we exclude rainfall events and the following 24 hours. +The timestamps in the DataFrame must be sorted in increasing order. ```julia -tha_filtered3 = filter_data(tha,quality_control=false,filter_growseas=false, - filter_precip=TRUE,precip="precip",tprecip=0.02, - records_per_hour=2,precip_hours=24) +setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) +sum(.!thaf.valid) # some more invalids ``` -We can also do all the steps described above with a single function call, which is also the intention of the function: - -```julia -tha_filtered = filter_data(tha,quality_control=TRUE,filter_growseas=TRUE, - filter_precip=TRUE, filter_vars=c("PPFD","ustar","LE","VPD"), - filter_vals_min=c(200,0.2,0,0.01),filter_vals_max=c(NA,NA,NA,NA), - NA_as_invalid = TRUE,vars_qc=c("GPP","LE","H","NEE","Tair","VPD","wind"), - quality_ext="_qc",good_quality = c(0,1),missing_qc_as_bad=TRUE, - GPP="GPP",doy="doy",year="year",tGPP=0.4,ws=15,min_int=5, - precip="precip",tprecip=0.02,records_per_hour=2,precip_hours=24) -``` When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to NA. (Note that this is the default case. If we add `filtered_data_to_NA=TRUE`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 271b5de..83c544d 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -13,7 +13,7 @@ using Statistics, StatsBase # mean, rle using PaddedViews, StaticArrays using Infiltrator -export frac_hour, moving_average +export frac_hour, moving_average, get_nonoverlapping_periods, set_datetime_ydh! export bigleaf_constants export Esat_slope, Esat_from_Tair, Esat_from_Tair_deriv, LE_to_ET, ET_to_LE, ms_to_mol, mol_to_ms, VPD_to_rH, rH_to_VPD, @@ -26,7 +26,8 @@ export air_density, pressure_from_elevation, psychrometric_constant, export calc_sun_position_MOD, calc_sun_position_hor export potential_radiation, extraterrestrial_radiation, get_datetime_for_doy_hour export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ET! -export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason +export setinvalid_range!, setinvalid_qualityflag!, + setinvalid_nongrowingseason!, get_growingseason, setinvalid_afterprecip! export decoupling, surface_conductance, aerodynamic_conductance include("util.jl") diff --git a/src/filter_data.jl b/src/filter_data.jl index 69ea4e0..843667a 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -272,5 +272,40 @@ function get_growingseason(GPPd,tGPP;ws=15,min_int=5,warngap=true) (is_growingseason = growseas, GPPd_smoothed = GPPd_smoothed) end +""" + setinvalid_afterprecip!(df; min_precip = 0.01, hours_after = 24.0) +Set records after precipitation to false in `:valid` column. +# Arguments +- df: DataFrame with columns `:datetime` and `:precip` sorted by `:datetime` + in increasing order. +optional: +- `min_precip` (in mm per timestep): minimum precip to be considered effective + precipitation. +- `hours_after`: time after the precipitation event to be considered invalid + +# Value +`df` with modified column `:valid`. +""" +function setinvalid_afterprecip!(df; min_precip = 0.01, hours_after = 24.0) + dfo = @pipe copy(df) |> + select!(_, :datetime, :precip) |> + subset!(_, :precip => ByRow(>=(min_precip)); skipmissing = true) |> + select!(_, + :datetime => :p_start, + :datetime => (x -> x .+ frac_hour(Second, hours_after)) => :p_end) + dfno = get_nonoverlapping_periods(dfo) + # + if !hasproperty(df, :valid) df[!,:valid] .= true; end + #ip = 1 + # since date.time is ordered we need to search for the next index only from last position + i_start = i_end = 1 + for ip in 1:nrow(dfno) + p_start, p_end = dfno.p_start[ip], dfno.p_end[ip] + i_start = i_end - 1 + findfirst(>=(p_start), df.datetime[i_end:end]) + i_end = i_start - 1 + findlast(<=(p_end), df.datetime[i_start:end]) + df.valid[i_start:i_end] .= false + end + df +end \ No newline at end of file diff --git a/src/util.jl b/src/util.jl index 727c2bd..7ca46f9 100644 --- a/src/util.jl +++ b/src/util.jl @@ -17,8 +17,8 @@ # end """ - frac_hour(float::AbstractFloat) - frac_hour(period::Type{<:Period}, float::AbstractFloat) + frac_hour(float::Number) + frac_hour(period::Type{<:Period}, float::Number) Create a period in given type (defaults to `Nanosecond`) from fractional hours. @@ -30,13 +30,13 @@ frac_hour(1+1/60) == Hour(1) + Minute(1) true ``` """ -function frac_hour(period::Type{<:Period}, float::AbstractFloat) +function frac_hour(period::Type{<:Period}, float::Number) #adapted from https://stackoverflow.com/a/51448499 full_hour, Δ = divrem(float, 1) partial = period(round(Dates.value(period(Hour(1))) * Δ)) Hour(full_hour) + partial end -frac_hour(float::AbstractFloat) = frac_hour(Nanosecond, float) +frac_hour(float::Number) = frac_hour(Nanosecond, float) """ @@ -67,5 +67,58 @@ function moving_average(vs,n; nmin = n ÷ 2) ans = [fagg(@view vsp[i.+kernel]) for i in 1:length(vs)] end +""" + get_nonoverlapping_periods(dfo::DataFrame) + +Get non-overlapping periods. + +# Arguments +- `dfo`: DataFrame with columns `p_start` and `p_end` sorted by increasing `p_start` + +# Value +- DataFrame with columns `p_start` and `p_end` with `p_start[i] > p_end[i-1]` +""" +function get_nonoverlapping_periods(dfo::DataFrame) + nrow(dfo) == 0 && return(dfo) + dfno = DataFrame() + p_start, p_end = dfo.p_start[1], dfo.p_end[1] + for i in 2:(nrow(dfo)-1) + if dfo.p_start[i] <= p_end + # if current row is in previous intervals, just extend the previous interval + p_end = max(p_end, dfo.p_end[i]) + else + # push the previous interval and take this row as interval + push!(dfno, (p_start = p_start, p_end = p_end)) + p_start, p_end = dfo.p_start[i], dfo.p_end[i] + end + end + if dfo.p_start[end] <= p_end + # last row start within previous interval: push extended interval + push!(dfno, (p_start = p_start, p_end = max(p_end, dfo.p_end[end]))) + else + # last row not in previous interval: push both interval and last row + push!(dfno, (p_start = p_start, p_end = p_end)) + push!(dfno, (p_start = dfo.p_start[end], p_end = dfo.p_end[end])) + end + dfno +end + +""" + set_datetime_ydh!(df, timezone = tz"UTC+0"; + year = df.year, doy = df.doy, hour = df.hour) + +Update column DateTime column datetime according to year, dayOfYear, and fractional hour. + +# Value +`df` with updated or added column `:datetime` moved to the first position. +""" +function set_datetime_ydh!(df, timezone = tz"UTC+0"; + year = df.year, doy = df.doy, hour = df.hour) + df[!,:datetime] = @. ZonedDateTime( + DateTime(year) + Day(doy-1) + frac_hour(Second, hour), timezone) + select!(df, :datetime, :) +end + + diff --git a/test/filter_data.jl b/test/filter_data.jl index 74fb290..0db24f2 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -103,3 +103,20 @@ end @test isequal(df3a.valid, df2a.valid) end +@testset "setinvalid_afterprecip!" begin + min_precip = 0.01 + hours_after = 1.0 + dfo = DataFrame(datetime = DateTime(2021) .+ Hour.(1:13), precip = 0.0) + dfo.precip[3] = 0.001 + dfo.precip[10:12] .= 2.0 + dfo.precip[6:8] .= 2.0 + allowmissing!(dfo, :precip); dfo.precip[2] = missing + # + df = copy(dfo) + df2 = setinvalid_afterprecip!(df; min_precip, hours_after) + @test df2 === df + @test all(.!df.valid[6:9]) + @test all(.!df.valid[10:13]) + @test all(df.valid[Not(vcat(6:9,10:13))]) +end + diff --git a/test/util.jl b/test/util.jl index 43c29da..34b72ca 100644 --- a/test/util.jl +++ b/test/util.jl @@ -15,3 +15,33 @@ end @test isfinite(GPPd_smooth[100]) @test ismissing(GPPd_smooth[101]) end + +@testset "get_nonoverlapping_periods" begin + dfo = DataFrame( + p_start = DateTime(2021) .+ Hour.(SA[0,4,5,10]), + p_end = DateTime(2021) .+ Hour.(SA[2,5,6,11]), + ) + dfno = get_nonoverlapping_periods(dfo) + @test dfno.p_start == DateTime(2021) .+ Hour.(SA[0,4,10]) + @test dfno.p_end == DateTime(2021) .+ Hour.(SA[2,6,11]) + # + # extend last row + dfo = DataFrame( + p_start = DateTime(2021) .+ Hour.(SA[0,4,5]), + p_end = DateTime(2021) .+ Hour.(SA[2,5,6]), + ) + dfno = get_nonoverlapping_periods(dfo) + @test dfno.p_start == DateTime(2021) .+ Hour.(SA[0,4]) + @test dfno.p_end == DateTime(2021) .+ Hour.(SA[2,6]) +end + +@testset "set_datetime_ydh!" begin + dfo = DataFrame(year = 2021, doy = repeat(1:3, inner = 48), hour = repeat(0.0:0.5:23.5, outer = 3)) + df = copy(dfo) + df2 = set_datetime_ydh!(df) + @test df2 === df + @test df2.datetime[1] == ZonedDateTime(DateTime(2021), tz"UTC") + @test all(Dates.year.(df2.datetime) .== dfo.year) + @test all(Dates.dayofyear.(df2.datetime) .== dfo.doy) + @test all(Dates.hour.(df2.datetime) .+ Dates.minute.(df2.datetime)/60 .== dfo.hour) +end From 2321dae07bcba0a97dde7e02f588537021351f9d Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 18:00:06 +0100 Subject: [PATCH 23/43] remove surface_conductance from first help page --- docs/src/index.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index e1320ef..2949027 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -18,11 +18,6 @@ Pages = ["metorological_variables.md",] Pages = ["evapotranspiration.md",] ``` -## Surface conductance -```@index -Pages = ["surface_conductance.md",] -``` - ## Global radiation ```@index Pages = ["global_radiation.md",] From 012b69f432e1068e3b002b92a5381bbec12255f0 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 18:05:47 +0100 Subject: [PATCH 24/43] update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23c0607..907046f 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ file an [issue](https://github.com/bgctw/Bigleaf.jl/issues) if you need a specif At the current state we ported - Meteorological variables +- Evapotranspiration +- Potential radiation - Unit conversions - -Upcoming: -- Computation of potential radiation +- Filtering data From 4dbce59245acebdb0cfa826179da6d50c624b924 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 1 Nov 2021 21:43:40 +0100 Subject: [PATCH 25/43] remove non-mutating variant of potential_ET --- docs/src/walkthrough.md | 55 ++++++++++---- inst/tha.jl | 19 ++++- inst/walkthrough_todo.jmd | 20 ++--- src/evapotranspiration.jl | 146 ++++++++++++++++++------------------- src/filter_data.jl | 6 +- test/evapotranspiration.jl | 48 +++++------- test/filter_data.jl | 2 +- 7 files changed, 159 insertions(+), 137 deletions(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 44f8f17..d645224 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -76,7 +76,7 @@ It is imperative that variables are provided in the right units, as the plausibi the input units is not checked in most cases. The required units of the input arguments can be found in the respective help file of the function. The good news is that units do not change across functions. For example, pressure is always required in kPa, -and temperature always in °c. +and temperature always in °C. ## Function arguments @@ -88,6 +88,11 @@ such that in many cases it is not necessary to provide them explicitly. The column names in the DataFrame should correspond to the argument names of the corresponding method that accespts each input individually. +Methods are usually provided with two forms: +- all required scalar inputs as positional arguments with outputing a NamedTuple +- a mutating DataFrame with columns corresponding to required inputs + where the output columns are added or modified. + We can demonstrate the usage with a simple example: ```@example doc @@ -95,10 +100,10 @@ We can demonstrate the usage with a simple example: Tair, pressure, Rn, = 14.81, 97.71, 778.17 potential_ET(Tair, pressure, Rn, Val(:PriestleyTaylor)) # DataFrame -potential_ET(tha, Val(:PriestleyTaylor)) +potential_ET!(copy(tha), Val(:PriestleyTaylor)) # DataFrame with a few columns overwritten by user values -potential_ET(transform(tha, :Tair => x -> 25.0; renamecols=false), Val(:PriestleyTaylor)) -# varying one input only +potential_ET!(transform(tha, :Tair => x -> 25.0; renamecols=false), Val(:PriestleyTaylor)) +# varying one input only: scalar form with dot-notation Tair_vec = 10.0:1.0:20.0 DataFrame(potential_ET.(Tair_vec, pressure, Rn, Val(:PriestleyTaylor))) nothing # hide @@ -112,14 +117,16 @@ and $S$ is the sum of all storage fluxes of the ecosystem (see e.g. Leuning et al. 2012 for an overview). For some sites, $G$ is not available, and for most sites, only a few components of $S$ are measured. -In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results might be (slightly) biased), but special options exist for the treatment of missing $S$ and $G$ values. +In `Bigleaf.jl` it is not a problem if $G$ and/or $S$ are missing (other than the results +might be (slightly) biased), but special options exist for the treatment of missing +$S$ and $G$ values. Note that the default for G and S in the dataframe variant is missing (and assumed zero), even if those columns are present in the DataFrame. You need to explictly pass those columns with the optional arguments: e.g. `potential_ET(df, Val(:PriestleyTaylor); G = df.G)` -Note that in difference to the bigleaf R package missing entries in a provide +Note that in difference to the bigleaf R package missing entries in an input vector are not relaced by zero by default. You need to explitly use coalesce when specifying a ground heat flux for which missings should be replaced by zero: `;G = coalesce(df.G, zero(df.G))` @@ -150,10 +157,10 @@ If the valid column was false before, it stays at false. ### `setinvalid_qualityflag!` One can check quality flags. By default (argument `setvalmissing = true`) this also replaces the non-valid values in the data-columns by `missing`. -```julia +```@example doc thaf = copy(tha) # keep the original tha DataFrame # if the :valid columns does not exist yet, it is created with all values true -setinvalid_qualityflag!(thaf; vars = [`LE`, "NEE"]) +setinvalid_qualityflag!(thaf; vars = ["LE", "NEE"]) sum(.!thaf.valid) # 7 records marked non-valid sum(ismissing.(thaf.NEE)) # 7 NEE values set to missing ``` @@ -176,7 +183,7 @@ We can filter for meteorological conditions to be in acceptable ranges. For each variable to check we supply the valid minimum and valid maxium as a two-tuple as the second component of a pair. If their is no limit towards small or large values, supply `-Inf` or `Inf` as the minimum or maximum respectively. -```julia +```@example doc setinvalid_range!(thaf, :PPFD => (200, Inf), :ustar => (0.2, Inf), @@ -185,13 +192,13 @@ setinvalid_range!(thaf, ) sum(.!thaf.valid) # many more records marked invalid minimum(skipmissing(thaf.PPFD)) >= 200 # values outsides range some set to missing -sum(ismissing.(thaf.PPFD) +sum(ismissing.(thaf.PPFD)) ``` About half of the data were filtered because radiation was not high enough (night-time). Another quarter was filtered because they showed negative LE values. However, most of them occured during the night: -```julia +```@example doc sum(ismissing.(thaf.PPFD)) / nrow(thaf) # 0.48 sum(.!ismissing.(thaf.PPFD) .&& ismissing.(thaf.LE)) / nrow(thaf) # 0.05 ``` @@ -199,9 +206,9 @@ sum(.!ismissing.(thaf.PPFD) .&& ismissing.(thaf.LE)) / nrow(thaf) # 0.05 ### `setinvalid_nongrowingseason!` A third method filters periods outside the growing season: -```julia +```@example doc setinvalid_nongrowingseason!(thaf, 0.4) -sum(.!thaf2.valid) # tha dataset is all within growing season - no additional invalids +sum(.!thaf.valid) # tha dataset is all within growing season - no additional invalids ``` This function implements a simple growing season filter based on daily smoothed GPP time @@ -237,7 +244,7 @@ The number of subsequent time periods excluded is controlled by the argument `pr Here, we exclude rainfall events and the following 24 hours. The timestamps in the DataFrame must be sorted in increasing order. -```julia +```@example doc setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) sum(.!thaf.valid) # some more invalids ``` @@ -309,6 +316,26 @@ The following figure compares them at absole scale and as difference to the ![](fig/Esat_rel.svg) +## Potential evapotranspiration + +For many hydrological applications, it is relevant to get an estimate on the potential +evapotranspiration (PET). At the moment, the `Bigleaf.jl` contains two formulations +for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: + +```@example doc +potential_ET!(thaf, Val(:PriestleyTaylor); G = thaf.G, infoGS = false) +# TODO need aerodynamci and surface conductance to compute Ga and Gs_mol before +# potential_ET!(thaf, Val(:PenmanMonteith); G = thaf.G, +# Gs_pot=quantile(skipmissing(thaf.Gs_mol),0.95)) +select(thaf[24:26,:], :datetime, :ET_pot, :LE_pot) +``` + +In the second calculation it is important to provide an estimate of aerodynamic +conductance Ga and ``Gs_{pot}``, the potential surface conductance under optimal conditions. +Here, we have approximated ``Gs_{pot}`` with the ``95^{\text{th}}`` percentile of all +``G_s`` values of the site. + + ## Global radiation Potential radiation for given time and latitude: diff --git a/inst/tha.jl b/inst/tha.jl index e6133b1..64f8771 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -1,5 +1,6 @@ using DataFrames, Pipe, Missings using Dates, TimeZones +using Statistics using Latexify using DataDeps, Suppressor @@ -17,15 +18,25 @@ ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) nothing tha = DE_Tha_Jun_2014 +set_datetime_ydh!(tha) + +thaf = copy(tha) +setinvalid_qualityflag!(thaf) +setinvalid_range!(thaf, + :PPFD => (200, Inf), + :ustar => (0.2, Inf), + :LE =>(0, Inf), + :VPD => (0.01, Inf) + ) +setinvalid_nongrowingseason!(thaf, 0.4) +setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) -tha.time = @. DateTime(tha.year) + Day(tha.doy-1) + frac_hour(Minute, tha.hour) -tmp = first(tha.time) dfGPPd = @pipe tha |> - transform(_, :time => ByRow(yearmonthday) => :ymd, copycols = false) |> + transform(_, :datetime => ByRow(yearmonthday) => :ymd, copycols = false) |> groupby(_, :ymd) |> - combine(_, :time => (x -> Date(first(x))) => :date, :GPP => mean => :GPPd, ) + combine(_, :datetime => (x -> Date(first(x))) => :date, :GPP => mean => :GPPd, ) #x -> x[!,2] show(dfGPPd.GPPd) diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 08da8fc..f7bfd5e 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -306,19 +306,21 @@ Here, the points denote the mean wind speed and the bars denote the standard dev ## Potential evapotranspiration -For many hydrological applications, it is relevant to get an estimate on the potential evapotranspiration (PET). At the moment, the `Bigleaf.jl` package contains two formulations for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: +For many hydrological applications, it is relevant to get an estimate on the potential +evapotranspiration (PET). At the moment, the `Bigleaf.jl` contains two formulations +for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: ```julia -summary(potential_ET(tha_filtered,G="G",approach=Val(:PriestleyTaylor))) -summary(potential_ET(tha_filtered,G="G",approach=Val(:PenmanMonteith)), - Gs_pot=quantile(tha_filtered$Gs_mol,0.95,na_rm=TRUE)) +potential_ET!(thaf, Val(:PriestleyTaylor); G = thaf.G, infoGS = false) +# TODO need surface conductance to compute Ga and Gs_mol before +# potential_ET!(thaf, Val(:PenmanMonteith); G = thaf.G, +# Gs_pot=quantile(skipmissing(thaf.Gs_mol),0.95)) ``` -In the second calculation it is important to provide an estimate of `Gs_pot`, which corresponds to the potential surface conductance under optimal conditions. Here, we have approximated `Gs_pot` with the 95$^{\text{th}}$ percentile of all $G_s$ values of the site. - - - - +In the second calculation it is important to provide an estimate of aerodynamic +conductance Ga and ``Gs_{pot}``, the potential surface conductance under optimal conditions. +Here, we have approximated ``Gs_{pot}`` with the ``95^{\text{th}}`` percentile of all +``G_s`` values of the site. ## Energy balance closure (EBC) diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index 8b37d95..dedeb85 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -2,41 +2,40 @@ potential_ET(Tair, pressure, Rn, ::Val{:PriestleyTaylor}; ...) potential_ET(Tair, pressure, Rn, G, S, ::Val{:PriestleyTaylor}; ...) - potential_ET(df, approach; ...) potential_ET!(df, approach; ...) Potential evapotranspiration according to Priestley & Taylor 1972 or the Penman-Monteith equation with a prescribed surface conductance. # Arguments -- Tair: Air temperature (degC) -- pressure: Atmospheric pressure (kPa) -- Rn: Net radiation (W m-2) -- VPD: Vapor pressure deficit (kPa) -- Ga: Aerodynamic conductance to heat/water vapor (m s-1) -- approach: Approach used. +- `Tair`: Air temperature (degC) +- `pressure`: Atmospheric pressure (kPa) +- `Rn`: Net radiation (W m-2) +- `VPD`: Vapor pressure deficit (kPa) +- `Ga`: Aerodynamic conductance to heat/water vapor (m s-1) +- `df`: Data_frame or matrix containing all required variables; optional +- `approach`: Approach used. Either `Val(:PriestleyTaylor)` (default), or `Val(:PenmanMonteith)`. -- df: Data_frame or matrix containing all required variables; optional optional: -- G: Ground heat flux (W m-2). Defaults to zero. -- S: Sum of all storage fluxes (W m-2) . Defaults to zero. +- `G`: Ground heat flux (W m-2). Defaults to zero. +- `S`: Sum of all storage fluxes (W m-2) . Defaults to zero. - `Esat_formula`: formula used in [`Esat_from_Tair`](@ref) - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - cp - specific heat of air for constant pressure (J K-1 kg-1) - - eps - ratio of the molecular weight of water vapor to dry air - - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) - - for :PenmanMonteith: - - Rd - gas constant of dry air (J kg-1 K-1) - - Rgas - universal gas constant (J mol-1 K-1) - - Kelvin - conversion degree Celsius to Kelvin + - `cp` - specific heat of air for constant pressure (J K-1 kg-1) + - `eps` - ratio of the molecular weight of water vapor to dry air + - `Pa2kPa` - conversion pascal (Pa) to kilopascal (kPa) + - for `PenmanMonteith`: + - `Rd` - gas constant of dry air (J kg-1 K-1) + - `Rgas` - universal gas constant (J mol-1 K-1) + - `Kelvin` - conversion degree Celsius to Kelvin additional optional arguments with data.frame variants -- infoGS=ture: sete to false to avoid infor log-message if G or S is not - specified +- `infoGS = true`: Set to false to avoid info-log-message if G or S is not + specified. additional optional for PriestleyTaylor: -- alpha: Priestley-Taylor coefficient +- `alpha = 1.26`: Priestley-Taylor coefficient additional optional for PenmanMonteith: -- Gs_pot: Potential/maximum surface conductance (mol m-2 s-1); - defaults to 0.6 mol m-2 s-1; +- `Gs_pot = 0.6`: Potential/maximum surface conductance (mol m-2 s-1); + # Details Potential evapotranspiration is calculated according to Priestley & Taylor, 1972 @@ -67,26 +66,22 @@ care for missing values (see examples). Both methods are provided with several forms: - all required inputs as positional arguments -- providing a DataFrame with columns corresponding to required inputs - returning a DataFrame with only the result columns. -- a mutating DataFrame version that returns the original DataFrame with - result columns added or modified. +- a mutating DataFrame version with columns corresponding to required inputs + where the output columns are added or modified. # Value NamedTuple or DataFrame with the following entries: - `ET_pot`: Potential evapotranspiration (kg m-2 s-1) - `LE_pot`: Potential latent heat flux (W m-2) -For the mutating form, the original df with columns `ET_pot`, `LE_pot` -updated or added. # References - Priestley, C_H_B., Taylor, R_J., 1972: On the assessment of surface heat flux -and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. + and evaporation using large-scale parameters. Monthly Weather Review 100, 81-92. - Allen, R_G., Pereira L_S., Raes D., Smith M., 1998: Crop evapotranspiration - -Guidelines for computing crop water requirements - FAO Irrigation and drainage -paper 56. + Guidelines for computing crop water requirements - FAO Irrigation and drainage + paper 56. - Novick, K_A., et al. 2016: The increasing importance of atmospheric demand -for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. + for ecosystem water and carbon fluxes. Nature Climate Change 6, 1023 - 1027. # See also [`surface_conductance`](@ref) @@ -123,11 +118,11 @@ df = DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0, G = 105.0, VPD allowmissing!(df, Cols(:G)); df.G[1] = missing # # need to provide G explicitly -df_ET = potential_ET(df, Val(:PriestleyTaylor); G = df.G, infoGS = false) +df_ET = potential_ET!(copy(df), Val(:PriestleyTaylor); G = df.G, infoGS = false) ismissing(df_ET.ET_pot[1]) # # use coalesce to replace missing values -df_ET = potential_ET(df, Val(:PriestleyTaylor); G = coalesce.(df.G, zero(df.G)), infoGS = false) +df_ET = potential_ET!(copy(df), Val(:PriestleyTaylor); G = coalesce.(df.G, zero(df.G)), infoGS = false) !ismissing(df_ET.ET_pot[1]) # output true @@ -168,26 +163,26 @@ function potential_ET(Tair, pressure, Rn, VPD, Ga, G, S, ::Val{:PenmanMonteith}; ET_pot = LE_to_ET(LE_pot,Tair) (ET_pot = ET_pot, LE_pot = LE_pot) end -function potential_ET(df, approach::Val{:PriestleyTaylor}; - G=missing,S=missing, infoGS=true, kwargs...) - # - dfGS = get_df_GS(df, G,S; infoGS) - f(args...) = potential_ET(args..., approach; kwargs...) - select(hcat(select(df,:Tair, :pressure, :Rn), dfGS; copycols = false), - All() => ByRow(f) => AsTable - ) -end -function potential_ET(df, approach::Val{:PenmanMonteith}; - G=missing,S=missing, infoGS=true, kwargs...) - # - dfGS = get_df_GS(df, G,S; infoGS) - function f(args...) - potential_ET(args..., approach; kwargs...) - end - select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), - All() => ByRow(f) => AsTable - ) -end +# function potential_ET(df, approach::Val{:PriestleyTaylor}; +# G=missing,S=missing, infoGS=true, kwargs...) +# # +# dfGS = get_df_GS(df, G,S; infoGS) +# f(args...) = potential_ET(args..., approach; kwargs...) +# select(hcat(select(df,:Tair, :pressure, :Rn), dfGS; copycols = false), +# All() => ByRow(f) => AsTable +# ) +# end +# function potential_ET(df, approach::Val{:PenmanMonteith}; +# G=missing,S=missing, infoGS=true, kwargs...) +# # +# dfGS = get_df_GS(df, G,S; infoGS) +# function f(args...) +# potential_ET(args..., approach; kwargs...) +# end +# select(hcat(select(df,:Tair, :pressure, :Rn, :VPD, :Ga), dfGS; copycols = false), +# All() => ByRow(f) => AsTable +# ) +# end function potential_ET!(df, approach::Val{:PriestleyTaylor}; G=missing,S=missing, infoGS=true, kwargs...) dfGS = get_df_GS(df, G,S; infoGS) @@ -250,25 +245,24 @@ end """ equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn; ...) - equilibrium_imposed_ET(df; ...) equilibrium_imposed_ET!(df; ...) Evapotranspiration (ET) split up into imposed ET and equilibrium ET. # Argumens -- Tair: Air temperature (deg C) -- pressure: Atmospheric pressure (kPa) -- VPD: Air vapor pressure deficit (kPa) -- Gs: surface conductance to water vapor (m s-1) -- Rn: Net radiation (W m-2) +- `Tair`: Air temperature (deg C) +- `pressure`: Atmospheric pressure (kPa) +- `VPD`: Air vapor pressure deficit (kPa) +- `Gs`: surface conductance to water vapor (m s-1) +- `Rn`: Net radiation (W m-2) optional -- G: Ground heat flux (W m-2); optional -- S: Sum of all storage fluxes (W m-2); optional +- `G`: Ground heat flux (W m-2); optional +- `S`: Sum of all storage fluxes (W m-2); optional - `Esat_formula`: formula used in [`Esat_from_Tair`](@ref) - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - cp - specific heat of air for constant pressure (J K-1 kg-1) - - eps - ratio of the molecular weight of water vapor to dry (-) - - Pa2kPa - conversion pascal (Pa) to kilopascal (kPa) + - `cp` - specific heat of air for constant pressure (J K-1 kg-1) + - `eps` - ratio of the molecular weight of water vapor to dry (-) + - `Pa2kPa` - conversion pascal (Pa) to kilopascal (kPa) # Details Total evapotranspiration can be written in the form (Jarvis & McNaughton 6): @@ -335,16 +329,16 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; ET_eq = LE_to_ET(LE_eq,Tair) (;ET_eq, ET_imp, LE_eq, LE_imp) end -function equilibrium_imposed_ET(df; - G=missing,S=missing, infoGS=true, kwargs...) - # - dfGS = get_df_GS(df, G,S; infoGS) - function f(args...) - equilibrium_imposed_ET(args...; kwargs...) - end - dfb = hcat(select(df,:Tair, :pressure, :VPD, :Gs, :Rn), dfGS; copycols = false) - select(dfb, All() => ByRow(f) => AsTable ) -end +# function equilibrium_imposed_ET(df; +# G=missing,S=missing, infoGS=true, kwargs...) +# # +# dfGS = get_df_GS(df, G,S; infoGS) +# function f(args...) +# equilibrium_imposed_ET(args...; kwargs...) +# end +# dfb = hcat(select(df,:Tair, :pressure, :VPD, :Gs, :Rn), dfGS; copycols = false) +# select(dfb, All() => ByRow(f) => AsTable ) +# end function equilibrium_imposed_ET!(df; G=missing,S=missing, infoGS=true, kwargs...) # @@ -359,8 +353,6 @@ function equilibrium_imposed_ET!(df; select!(df, Not([:_tmp_G, :_tmp_S])) end - - """ TODO; implement decoupling. diff --git a/src/filter_data.jl b/src/filter_data.jl index 843667a..04c5a19 100755 --- a/src/filter_data.jl +++ b/src/filter_data.jl @@ -158,8 +158,8 @@ end Set non-growseason to false in :valid column. # Arguments -- df: DataFrame with column :GPP -- tGPP: scalar threshold of daily GPP (see [`get_growingseason`](@ref)) +- `df`: DataFrame with columns `:GPP` and `:datetime` +- `tGPP`: scalar threshold of daily GPP (see [`get_growingseason`](@ref)) optional: - `update_GPPd`: set to true additionally update `:GPPd_smoothed` column to results from [`get_growingseason`](@ref) @@ -172,7 +172,7 @@ where all non-growing season records are set to false. function setinvalid_nongrowingseason!(df, tGPP; update_GPPd_smoothed = false, kwargs...) if !hasproperty(df, :valid) df[!,:valid] .= true; end # non-copying dataframe where we can add grouping column __day - dft = transform(df, :time => ByRow(Date) => :__day, copycols = false) + dft = transform(df, :datetime => ByRow(Date) => :__day, copycols = false) function mean_nonmissing(x) mx = mean(skipmissing(x)) # if there is no non-missing record in a group, mean returns nan diff --git a/test/evapotranspiration.jl b/test/evapotranspiration.jl index bb18b46..e3414f2 100644 --- a/test/evapotranspiration.jl +++ b/test/evapotranspiration.jl @@ -13,12 +13,12 @@ end @testset "potential_ET dataframe" begin df = DataFrame( Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0) - df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df, Val(:PriestleyTaylor); alpha=1.26) - # non-mutating - @test ncol(df) == 3 - @test nrow(df_ET) == nrow(df) - @test ≈(last(df_ET).ET_pot, 0.0002035969; rtol = 1e-5) - @test ≈(last(df_ET).LE_pot, 494.7202; rtol = 1e-5) + # df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df, Val(:PriestleyTaylor); alpha=1.26) + # # non-mutating + # @test ncol(df) == 3 + # @test nrow(df_ET) == nrow(df) + # @test ≈(last(df_ET).ET_pot, 0.0002035969; rtol = 1e-5) + # @test ≈(last(df_ET).LE_pot, 494.7202; rtol = 1e-5) # df2 = copy(df) # df2.VPD .= 2.0 # fail in older versions @@ -29,9 +29,9 @@ end # [] => ByRow(() -> 2.0) => :VPD, # [] => ByRow(() -> 0.1) => :Ga, # ) - df_ET2 = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df2, Val(:PenmanMonteith)) - @test ncol(df2) == 5 - @test nrow(df_ET2) == nrow(df2) + # df_ET2 = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") potential_ET(df2, Val(:PenmanMonteith)) + # @test ncol(df2) == 5 + # @test nrow(df_ET2) == nrow(df2) #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) # # mutating @@ -49,9 +49,8 @@ end df = @pipe DataFrame(Tair = 20.0:1.0:30.0,pressure = 100.0, Rn = 500.0, G = 105.0) |> allowmissing(_, Cols(:G)) df.G[1] = missing - df_ET = @test_logs (:info,r"S is not provided") potential_ET(df, Val(:PriestleyTaylor); G = df.G) - # non-mutating - @test ncol(df) == 4 + df_ET = @test_logs (:info,r"S is not provided") potential_ET!(copy(df), Val(:PriestleyTaylor); G = df.G) + @test ncol(df_ET) == ncol(df)+2 # two columns added: ET_pot, LE_pot @test nrow(df_ET) == nrow(df) @test last(df_ET).ET_pot < 0.0002035969 # smaller because less energy available @test ismissing(first(df_ET).LE_pot) @@ -59,21 +58,11 @@ end df2 = copy(df) df2[!, :VPD] .= 2.0 df2[!, :Ga] .= 0.1 - df_ET2 = @test_logs (:info,r"G is not provided") potential_ET(df2, Val(:PenmanMonteith), S = df.G) - @test ncol(df2) == 6 + df_ET2 = @test_logs (:info,r"G is not provided") potential_ET!(copy(df2), Val(:PenmanMonteith), S = df.G) + @test ncol(df_ET2) == ncol(df2)+2 # two columns added: ET_pot, LE_pot @test nrow(df_ET2) == nrow(df2) @test ismissing(first(df_ET).LE_pot) #TODO surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) - # - # mutating - dfm = copy(df) - #@test_throws Exception - @test_logs (:info,r"G is not provided") potential_ET!(dfm, Val(:PriestleyTaylor); S = dfm.G) - #dfm = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") hcat(dfm, Bigleaf.fill_GS_missings(dfm, missing, missing, false, false); copycols = false) - #potential_ET!(dfm, Val(:PriestleyTaylor); alpha=1.26) - @test ncol(dfm) == ncol(df)+2 # two columns added: ET_pot, LE_pot - dfm = @test_logs (:info,r"S is not provided") potential_ET!(copy(df2), Val(:PenmanMonteith); G = dfm.G) - @test ncol(dfm) == ncol(df2)+2 end @testset "equilibrium_imposed_ET scalars" begin @@ -87,14 +76,15 @@ end # df = DataFrame(Tair = 20.0:1.0:22.0, pressure = 100.0, Rn = 50.0, VPD = 0.5, Gs = 0.01) ncoldf0 = ncol(df) - df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") equilibrium_imposed_ET(df) - @test ncol(df) == ncoldf0 - @test nrow(df_ET) == nrow(df) - @test ≈(first(df_ET).ET_eq, ET_eq) - @test ≈(first(df_ET).ET_imp, ET_imp) + # df_ET = @test_logs (:info,r"G is not provided") (:info,r"S is not provided") equilibrium_imposed_ET(df) + # @test ncol(df) == ncoldf0 + # @test nrow(df_ET) == nrow(df) + # @test ≈(first(df_ET).ET_eq, ET_eq) + # @test ≈(first(df_ET).ET_imp, ET_imp) # dfm = copy(df) dfm_ET = equilibrium_imposed_ET!(dfm; infoGS = false) + @test dfm_ET === dfm @test ncol(dfm) == ncoldf0 + 4 @test nrow(dfm) == nrow(df) @test ≈(first(dfm_ET).ET_eq, ET_eq) diff --git a/test/filter_data.jl b/test/filter_data.jl index 0db24f2..bd46daa 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -22,7 +22,7 @@ end nday = 365 GPPd = @pipe sin.((1:nday).*π./365) .+ 0.5 .* rand(rng,365) |> allowmissing(_) GPPd[100:120] .= missing - df = DataFrame(time = DateTime(2021) .+ Hour(1).+ Day.(0:nday-1), GPP = GPPd) + df = DataFrame(datetime = DateTime(2021) .+ Hour(1).+ Day.(0:nday-1), GPP = GPPd) df2 = copy(df) df2a = setinvalid_nongrowingseason!(df2, 0.5; warngap = false) @test df2a === df2 From 805656fd1c7fa395db77ecd77c81dfbd4b8d7edb Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 2 Nov 2021 07:28:39 +0100 Subject: [PATCH 26/43] add test for setinvalid_qualityflag! with missing_qc_as_bad = false --- test/filter_data.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/filter_data.jl b/test/filter_data.jl index bd46daa..c3e6dff 100644 --- a/test/filter_data.jl +++ b/test/filter_data.jl @@ -62,6 +62,12 @@ end @test df2a.valid == [true, false, false] @test isequal(df2a.NEE, [1,2,missing]) @test isequal(df2a.GPP, [10,missing,30]) + df2 = copy(df) + df2a = setinvalid_qualityflag!(df2; vars = SA["NEE", "GPP"], setvalmissing = true, missing_qc_as_bad = false) + @test df2a === df2 + @test df2a.valid == [true, true, false] + @test isequal(df2a.NEE, [1,2,missing]) + @test isequal(df2a.GPP, [10,20,30]) end @testset "setinvalid_range!" begin From 8b7afe5dde52fd1d2cda2af0647d3f68ad6f3ba1 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 2 Nov 2021 13:01:03 +0100 Subject: [PATCH 27/43] working on wind profile --- docs/src/walkthrough.md | 2 +- inst/fromR/WUE_metrics.jl | 16 +- inst/fromR/aerodynamic_conductance.jl | 90 ++--- inst/fromR/bigleaf_physiology.jl | 114 +++--- inst/fromR/boundary_layer_conductance.jl | 52 +-- inst/fromR/check_input.jl | 24 +- inst/fromR/decoupling.jl | 4 +- inst/fromR/energy_balance.jl | 24 +- inst/fromR/evapotranspiration.jl | 44 +-- inst/fromR/filter_data.jl | 106 +++--- inst/fromR/meteorological_variables.jl | 16 +- inst/fromR/potential_radiation.jl | 2 +- inst/fromR/stability_correction.jl | 22 +- inst/fromR/surface_conditions.jl | 10 +- inst/fromR/surface_conductance.jl | 36 +- inst/fromR/surface_roughness.jl | 74 ++-- inst/fromR/unit_conversions.jl | 8 +- inst/walkthrough_todo.jmd | 44 +-- src/Bigleaf.jl | 2 + src/aerodynamic_conductance.jl | 299 +++++++++++++++ src/boundary_layer_conductance.jl | 452 +++++++++++++++++++++++ src/surface_roughness.jl | 350 ++++++++++++++++++ test/boundary_layer_conductance.jl | 33 ++ 23 files changed, 1480 insertions(+), 344 deletions(-) create mode 100755 src/aerodynamic_conductance.jl create mode 100755 src/boundary_layer_conductance.jl create mode 100755 src/surface_roughness.jl create mode 100644 test/boundary_layer_conductance.jl diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index d645224..f558f8b 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -175,7 +175,7 @@ below which the quality control to be considered as acceptable quality all `LE` values whose `LE_qc` variable is larger than 1 are set to `missing`. The variable `missing_qc_as_bad` is required to decide what to do in case of missing values in the quality control variable. By default this is (conservatively) -set to `TRUE`, i.e. all entries where the qc variable is missing is set invalid. +set to `true`, i.e. all entries where the qc variable is missing is set invalid. ### `setinvalid_range!` diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index 0c521c6..fb7bccc 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -36,7 +36,7 @@ #' ``uWUE= (GPP * sqrt(VPD)) / ET`` #' #' All metrics are calculated based on the median of all values. E_g. -#' WUE = median(GPP/ET,na_rm=TRUE) +#' WUE = median(GPP/ET,na_rm=true) #' #' # Value a named vector with the following elements: @@ -64,12 +64,12 @@ #' ## filter data for dry periods and daytime at DE-Tha in June 2014 #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' @@ -88,10 +88,10 @@ function WUE_metrics(data,GPP="GPP",NEE="NEE",LE="LE",VPD="VPD",Tair="Tair", GPP = (GPP * constants[:umol2mol] * constants[:Cmol]) * constants[:kg2g] # gC m-2 s-1 NEE = (NEE * constants[:umol2mol] * constants[:Cmol]) * constants[:kg2g] # gC m-2 s-1 - WUE = median(GPP/ET,na_rm=TRUE) - WUE_NEE = median(abs(NEE)/ET,na_rm=TRUE) - IWUE = median((GPP*VPD)/ET,na_rm=TRUE) - uWUE = median((GPP*sqrt(VPD))/ET,na_rm=TRUE) + WUE = median(GPP/ET,na_rm=true) + WUE_NEE = median(abs(NEE)/ET,na_rm=true) + IWUE = median((GPP*VPD)/ET,na_rm=true) + uWUE = median((GPP*sqrt(VPD))/ET,na_rm=true) return(c(WUE=WUE,WUE_NEE=WUE_NEE,IWUE=IWUE,uWUE=uWUE)) end diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index fdbe33c..d607cf2 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -17,23 +17,23 @@ #' - zh Canopy height (m) #' - d Zero-plane displacement height (m) #' - z0m Roughness length for momentum (m), optional; if not provided, it is estimated from `roughness_parameters` -#' (method="wind_profile"). Only used if `wind_profile = TRUE` and/or `Rb_model` = `"Su_2001"` or -#' `"Choudhury_1988"`. -#' - Dl Characteristic leaf dimension (m) (if `Rb_model` = `"Su_2001"`) -#' or leaf width (if `Rb_model` = `"Choudhury_1988"`); ignored otherwise. -#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if `Rb_model = "Su_2001"`. +#' (method=Val(:wind_profile)). Only used if `wind_profile = true` and/or `Rb_model` = `Val(:Su_2001)` or +#' `Val(:Choudhury_1988)`. +#' - Dl Characteristic leaf dimension (m) (if `Rb_model` = `Val(:Su_2001)`) +#' or leaf width (if `Rb_model` = `Val(:Choudhury_1988)`); ignored otherwise. +#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if `Rb_model = Val(:Su_2001)`. #' Defaults to 2. -#' - fc Fractional vegetation cover (-); only used if `Rb_model = "Su_2001"`. See Details. -#' - LAI One-sided leaf area index (m2 m-2); only used if `Rb_model` = `"Choudhury_1988"` or `"Su_2001"`. -#' - Cd Foliage drag coefficient (-); only used if `Rb_model = "Su_2001"`. -#' - hs Roughness length of bare soil (m); only used if `Rb_model = "Su_2001"`. +#' - fc Fractional vegetation cover (-); only used if `Rb_model = Val(:Su_2001)`. See Details. +#' - LAI One-sided leaf area index (m2 m-2); only used if `Rb_model` = `Val(:Choudhury_1988)` or `Val(:Su_2001)`. +#' - Cd Foliage drag coefficient (-); only used if `Rb_model = Val(:Su_2001)`. +#' - hs Roughness length of bare soil (m); only used if `Rb_model = Val(:Su_2001)`. #' - wind_profile Should Ga for momentum be calculated based on the logarithmic wind profile equation? #' Defaults to `false`. -#' - stab_correction Should stability correction be applied? Defaults to `TRUE`. Ignored if `wind_profile = false`. -#' - stab_formulation Stability correction function. Either `"Dyer_1970"` (default) or -#' `"Businger_1971"`. Ignored if `wind_profile = false` or if `stab_correction = false`. -#' - Rb_model Boundary layer resistance formulation. One of `"Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"`. -#' - kB_h kB-1 value for heat transfer; only used if `Rb_model = "constant_kB-1"` +#' - stab_correction Should stability correction be applied? Defaults to `true`. Ignored if `wind_profile = false`. +#' - stab_formulation Stability correction function. Either `Val(:Dyer_1970)` (default) or +#' `Val(:Businger_1971)`. Ignored if `wind_profile = false` or if `stab_correction = false`. +#' - Rb_model Boundary layer resistance formulation. One of `Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)`. +#' - kB_h kB-1 value for heat transfer; only used if `Rb_model = Val(:constant_kB1)` #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than #' `Sc_name` @@ -64,7 +64,7 @@ #' additional stability correction function. #' #' An alternative method to calculate Ra_m is provided -#' (calculated if `wind_profile = TRUE`): +#' (calculated if `wind_profile = true`): #' #' ``Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)`` #' @@ -75,18 +75,18 @@ #' where z = reference height, d the zero-plane displacement height, and L the Monin-Obukhov length, #' calculated with [`Monin_Obukhov_length`](@ref) #' The stability correction function is chosen by the argument `stab_formulation`. Options are -#' `"Dyer_1970"` and `"Businger_1971"`. +#' `Val(:Dyer_1970)` and `Val(:Businger_1971)`. #' #' The model used to determine the canopy boundary layer resistance for heat (Rb_h) is specified by #' the argument `Rb_model`. The following options are implemented: -#' `"Thom_1972"` is an empirical formulation based on the friction velocity (ustar) (Thom 1972): +#' `Val(:Thom_1972)` is an empirical formulation based on the friction velocity (ustar) (Thom 1972): #' #' ``Rb_h = 6.2ustar^-0.667`` #' -#' The model by Choudhury & Monteith 1988 (`Rb_model = "Choudhury_1988"`), +#' The model by Choudhury & Monteith 1988 (`Rb_model = Val(:Choudhury_1988)`), #' calculates Rb_h based on leaf width, LAI and ustar (Note that function argument `Dl` #' represents leaf width (w) and not characteristic leaf dimension (Dl) -#' if `Rb_model` = `"Choudhury_1988"`): +#' if `Rb_model` = `Val(:Choudhury_1988)`): #' #' ``Gb_h = LAI((0.02/\\alpha)*sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` #' @@ -94,7 +94,7 @@ #' u(zh) is wind speed at canopy height (calculated from [`wind_profile`](@ref)), #' and w is leaf width (m). See [`Gb_Choudhury`](@ref) for further details. #' -#' The option `Rb_model = "Su_2001"` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, +#' The option `Rb_model = Val(:Su_2001)` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, #' a simplification of the model developed by Massman 1999: #' #' ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` @@ -155,8 +155,8 @@ #' example in the function [`surface_conductance`](@ref). #' #' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated -#' from the function `roughness_parameters` within `wind_profile` if `wind_profile = TRUE` -#' and/or `Rb_model` = `"Su_2001"` or `"Choudhury_1988"` The `roughness_parameters` +#' from the function `roughness_parameters` within `wind_profile` if `wind_profile = true` +#' and/or `Rb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` #' function estimates a single `z0m` value for the entire time period! If a varying `z0m` value #' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' @@ -183,21 +183,21 @@ #' df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) #' #' # simple calculation of Ga -#' aerodynamic_conductance(df,Rb_model="Thom_1972") +#' aerodynamic_conductance(df,Rb_model=Val(:Thom_1972)) #' #' # calculation of Ga using a model derived from the logarithmic wind profile -#' aerodynamic_conductance(df,Rb_model="Thom_1972",zr=40,zh=25,d=17.5,z0m=2,wind_profile=TRUE) +#' aerodynamic_conductance(df,Rb_model=Val(:Thom_1972),zr=40,zh=25,d=17.5,z0m=2,wind_profile=true) #' #' # simple calculation of Ga, but a physically based canopy boundary layer model -#' aerodynamic_conductance(df,Rb_model="Su_2001",zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) +#' aerodynamic_conductance(df,Rb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) #' """ """ function aerodynamic_conductance(data,Tair="Tair",pressure="pressure",wind="wind",ustar="ustar",H="H", - zr,zh,d,z0m=NULL,Dl,N=2,fc=NULL,LAI,Cd=0.2,hs=0.01,wind_profile=false, - stab_correction=TRUE,stab_formulation=c("Dyer_1970","Businger_1971"), - Rb_model=c("Thom_1972","Choudhury_1988","Su_2001","constant_kB-1"), - kB_h=NULL,Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) + zr,zh,d,z0m=nothing,Dl,N=2,fc=nothing,LAI,Cd=0.2,hs=0.01,wind_profile=false, + stab_correction=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), + Rb_model=c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)), + kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants()) Rb_model = match_arg(Rb_model) stab_formulation = match_arg(stab_formulation) @@ -205,20 +205,20 @@ function aerodynamic_conductance(data,Tair="Tair",pressure="pressure",wind="wind check_input(data,list(Tair,pressure,wind,ustar,H)) ## calculate canopy boundary layer conductance (Gb) - if (Rb_model %in% c("Thom_1972","Choudhury_1988","Su_2001")) + if (Rb_model in c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001))) - if (Rb_model == "Thom_1972") + if (Rb_model == Val(:Thom_1972)) Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) -else if (Rb_model == "Choudhury_1988") +elseif (Rb_model == Val(:Choudhury_1988)) Gb_mod = Gb_Choudhury(data,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, constants=constants) -else if (Rb_model == "Su_2001") +elseif (Rb_model == Val(:Su_2001)) Gb_mod = Gb_Su(data=data,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, @@ -231,18 +231,18 @@ end Rb_h = Gb_mod[,"Rb_h"] Gb_h = Gb_mod[,"Gb_h"] Gb_x = DataFrame(Gb_mod[,grep(colnames(Gb_mod),pattern="Gb_")[-1]]) - colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=TRUE)[-1] + colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] -else if (Rb_model == "constant_kB-1") +elseif (Rb_model == Val(:constant_kB1)) - if(is_null(kB_h)) + if(isnothing(kB_h)) stop("value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") else Rb_h = kB_h/(constants[:k] * ustar) Gb_h = 1/Rb_h - if (!is_null(Sc) | !is_null(Sc_name)) + if (!isnothing(Sc) | !isnothing(Sc_name)) if (length(Sc) != length(Sc_name)) stop("arguments 'Sc' and 'Sc_name' must have the same length") end @@ -262,13 +262,13 @@ end ## calculate aerodynamic conductance for momentum (Ga_m) if (wind_profile) - if (is_null(z0m) & Rb_model %in% c("constant_kB-1","Thom_1972")) - stop("z0m must be provided if wind_profile=TRUE!") -else if (is_null(z0m) & Rb_model %in% c("Choudhury_1988","Su_2001")) + if (isnothing(z0m) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) + stop("z0m must be provided if wind_profile=true!") +elseif (isnothing(z0m) & Rb_model in c(Val(:Choudhury_1988),Val(:Su_2001))) # z0m estimated as in Choudhury_1988 or Su_2001 - z0m = roughness_parameters(method="wind_profile",zh=zh,zr=zr,d=d,data=data, + z0m = roughness_parameters(method=Val(:wind_profile),zh=zh,zr=zr,d=d,data=data, Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, - stab_roughness=TRUE,stab_formulation=stab_formulation, + stab_roughness=true,stab_formulation=stab_formulation, constants=constants)[,"z0m"] end @@ -277,7 +277,7 @@ end zeta = stability_parameter(data=data,Tair=Tair,pressure=pressure,ustar=ustar, H=H,zr=zr,d=d,constants=constants) - if (stab_formulation %in% c("Dyer_1970","Businger_1971")) + if (stab_formulation in c(Val(:Dyer_1970),Val(:Businger_1971))) psi_h = stability_correction(zeta,formulation=stab_formulation)[,"psi_h"] @@ -297,8 +297,8 @@ end else - if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model %in% c("constant_kB-1","Thom_1972")) - warning("Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") + if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) + @warn"Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") end Ra_m = wind / ustar^2 diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index 2007954..4ff053c 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -13,7 +13,7 @@ #' - GPP Gross primary productivity (umol CO2 m-2 s-1) #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) #' or set to 0 (`false`, the default)? #' - constants DwDc - Ratio of the molecular diffusivities for water vapor and CO2 (-) #' @@ -62,15 +62,15 @@ #' """ """ -function intercellular_CO2(data,Ca="Ca",GPP="GPP",Gs="Gs_mol",Rleaf=NULL, +function intercellular_CO2(data,Ca="Ca",GPP="GPP",Gs="Gs_mol",Rleaf=nothing, missing_Rleaf_as_NA=false,constants=bigleaf_constants()) check_input(data,list(Ca,GPP,Gs)) - if(!is_null(Rleaf)) - if(!missing_Rleaf_as_NA){Rleaf[is_na(Rleaf)] = 0 } + if(!isnothing(Rleaf)) + if(!missing_Rleaf_as_NA){Rleaf[ismissing(Rleaf)] = 0 } else - cat("Respiration from the leaves is ignored and set to 0.",fill=TRUE) + cat("Respiration from the leaves is ignored and set to 0.",fill=true) Rleaf = 0 end @@ -89,7 +89,7 @@ end #' CO2 concentration using the Farquhar et al. 1980 model for C3 photosynthesis. #' #' - data Data_Frame or matrix with all required columns -#' - C3 C3 vegetation (`TRUE`, the default) or C4 vegetation (`false`)? +#' - C3 C3 vegetation (`true`, the default) or C4 vegetation (`false`)? #' - Temp Surface (or air) temperature (degC) #' - GPP Gross primary productivity (umol m-2 s-1) #' - Ci Bulk canopy intercellular CO2 concentration (umol mol-1) @@ -115,11 +115,11 @@ end #' - Jmax_dS Entropy term for Jmax (kJ mol-1 K-1) #' - Theta Curvature term in the light response function of J (-) #' - alpha_canopy Canopy absorptance (-) -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) #' or set to 0 (`false`, the default)? #' - Ci_C4 intercellular CO2 concentration below which photosynthesis #' is considered to be CO2-limited (umol mol-1), ignored -#' if `C3 = TRUE`. +#' if `C3 = true`. #' - constants Kelvin - conversion degree Celsius to Kelvin #' Rgas - universal gas constant (J mol-1 K-1) #' kJ2J - conversion kilojoule (kJ) to joule (J) @@ -259,21 +259,21 @@ end #' ``` #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' #' # calculate Ga -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model="Thom_1972")[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # calculate Gs from the the inverted PM equation #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", -#' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, +#' Rn="Rn",G="G",S=nothing,VPD="VPD",Ga=Ga, #' formulation=Val(:PenmanMonteith))[,"Gs_mol"] #' #' # calculate Ci @@ -285,8 +285,8 @@ end #' @importFrom stats optimize #' #' @export -function photosynthetic_capacity(data,C3=TRUE,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD_j=c(200,500),PPFD_c=1000, - Rleaf=NULL,Oi=0.21,Kc25=404.9,Ko25=278.4,Gam25=42.75, +function photosynthetic_capacity(data,C3=true,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD_j=c(200,500),PPFD_c=1000, + Rleaf=nothing,Oi=0.21,Kc25=404.9,Ko25=278.4,Gam25=42.75, Kc_Ha=79.43,Ko_Ha=36.38,Gam_Ha=37.83,Vcmax_Ha=65.33,Vcmax_Hd=200, Vcmax_dS=0.635,Jmax_Ha=43.9,Jmax_Hd=200,Jmax_dS=0.640, Theta=0.7,alpha_canopy=0.8,missing_Rleaf_as_NA=false,Ci_C4=100, @@ -309,17 +309,17 @@ function photosynthetic_capacity(data,C3=TRUE,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD Ko = Ko * constants[:J2kJ] # basic filtering on Ci - Ci[Ci < 80 | is_na(Ci)] = NA + Ci[Ci < 80 | ismissing(Ci)] = NA # Presumed limitation states GPPc = GPPj = GPP - GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | is_na(PPFD)] = NA - GPPc[PPFD < PPFD_c | is_na(PPFD)] = NA + GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD)] = NA + GPPc[PPFD < PPFD_c | ismissing(PPFD)] = NA - if(!is_null(Rleaf)) - if(!missing_Rleaf_as_NA){Rleaf[is_na(Rleaf)] = 0 } + if(!isnothing(Rleaf)) + if(!missing_Rleaf_as_NA){Rleaf[ismissing(Rleaf)] = 0 } else - cat("Respiration from the leaves is ignored and set to 0.",fill=TRUE) + cat("Respiration from the leaves is ignored and set to 0.",fill=true) Rleaf = 0 end @@ -332,8 +332,8 @@ else { # C4 vegetation # Presumed limitation states (C4) GPPc = GPPj = GPP - GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | is_na(PPFD) | Ci < 0] = NA - GPPc[PPFD < PPFD_c | Ci < Ci_C4 | is_na(PPFD)] = NA + GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD) | Ci < 0] = NA + GPPc[PPFD < PPFD_c | Ci < Ci_C4 | ismissing(PPFD)] = NA Vcmax = GPPc J = 3 * GPPj / (1 - 0.5) @@ -355,7 +355,7 @@ end ) ) else - warning("Not enough observations to calculate Jmax!") + @warn"Not enough observations to calculate Jmax!") Jmax = NA end @@ -368,10 +368,10 @@ end # calculate medians and standard errors of the median - Vcmax25_Median = median(Vcmax25,na_rm=TRUE) - Vcmax25_SE = constants[:se_median] * sd(Vcmax25,na_rm=TRUE)/sqrt((sum(!is_na(Vcmax25)))) - Jmax25_Median = median(Jmax25,na_rm=TRUE) - Jmax25_SE = constants[:se_median] * sd(Jmax25,na_rm=TRUE)/sqrt((sum(!is_na(Jmax25)))) + Vcmax25_Median = median(Vcmax25,na_rm=true) + Vcmax25_SE = constants[:se_median] * sd(Vcmax25,na_rm=true)/sqrt((sum(!ismissing(Vcmax25)))) + Jmax25_Median = median(Jmax25,na_rm=true) + Jmax25_SE = constants[:se_median] * sd(Jmax25,na_rm=true)/sqrt((sum(!ismissing(Jmax25)))) return(c("Vcmax25"=round(Vcmax25_Median,2),"Vcmax25_SE"=round(Vcmax25_SE,2), "Jmax25"=round(Jmax25_Median,2),"Jmax25_SE"=round(Jmax25_SE,2))) @@ -441,17 +441,17 @@ function Arrhenius_temp_response(param,Temp,Ha,Hd,dS,constants=bigleaf_constants Hd = ifelse(missing(Hd),NA,Hd*constants[:kJ2J]) dS = ifelse(missing(dS),NA,dS*constants[:kJ2J]) - if (is_na(Ha)) + if (ismissing(Ha)) stop("Activation energy (Ha) has to be provided!") end - if (is_na(Hd) & is_na(dS)) + if (ismissing(Hd) & ismissing(dS)) param25 = param / exp(Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) -else if (!is_na(Hd) & !is_na(dS)) +elseif (!ismissing(Hd) & !ismissing(dS)) param25 = param / ( exp(Ha * (Temp - Tref) / (Tref*constants[:Rgas]*Temp)) * @@ -459,9 +459,9 @@ else if (!is_na(Hd) & !is_na(dS)) (1 + exp((Temp*dS - Hd) / (Temp * constants[:Rgas]))) ) -else if ((!is_na(Hd) & is_na(dS)) | (is_na(Hd) & !is_na(dS)) ) +elseif ((!ismissing(Hd) & ismissing(dS)) | (ismissing(Hd) & !ismissing(dS)) ) - warning("Both Hd and dS have to be provided for a temperature response + @warn"Both Hd and dS have to be provided for a temperature response that considers a temperature optimum and a deactivation term! Continue considering activation energy (Ha) only...") @@ -492,17 +492,17 @@ end #' - robust_nls Use robust nonlinear regression (`\link[robustbase]{nlrob`})? Default is `false`. #' - nmin Minimum number of data required to perform the fit; defaults to 40. #' - fitg0 Should g0 and g1 be fitted simultaneously? -#' - g0 Minimum stomatal conductance (mol m-2 s-1); ignored if `fitg0 = TRUE`. -#' - fitD0 Should D0 be fitted along with g1 (and g0 if `fitg0 = TRUE`)?; only used if `model = "Leuning"`. +#' - g0 Minimum stomatal conductance (mol m-2 s-1); ignored if `fitg0 = true`. +#' - fitD0 Should D0 be fitted along with g1 (and g0 if `fitg0 = true`)?; only used if `model = "Leuning"`. #' - D0 Stomatal sensitivity parameter to VPD; only used if `model = "Leuning"` and `fitD0 = false`. #' - Gamma Canopy CO2 compensation point (umol mol-1); only used if `model = "Leuning"`. #' Can be a constant or a variable. Defaults to 50 umol mol-1. #' - constants Kelvin - conversion degree Celsius to Kelvin #' Rgas - universal gas constant (J mol-1 K-1) #' DwDc - Ratio of the molecular diffusivities for water vapor and CO2 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`TRUE`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) #' or set to 0 (`false`, the default)? -#' - ... Additional arguments to `\link[stats]{nls`} or `\link[robustbase]{nlrob`} if `robust_nls = TRUE`. +#' - ... Additional arguments to `\link[stats]{nls`} or `\link[robustbase]{nlrob`} if `robust_nls = true`. #' #' # Details All stomatal models were developed at leaf-level, but its parameters @@ -525,7 +525,7 @@ end #' The equations above are valid at leaf-level. At ecosystem level, An is replaced by GPP (or GPP - Rleaf, #' where Rleaf is leaf respiration), and gs (stomatal conductance) by Gs (surface conductance). #' The parameters in the models are estimated using nonlinear regression (`\link[stats]{nls`}) if -#' `robust_nls = false` and weighted nonlinear regression if `robust_nls = TRUE`. +#' `robust_nls = false` and weighted nonlinear regression if `robust_nls = true`. #' The weights are calculated from `\link[robustbase]{nlrob`}, and `\link[stats]{nls`} #' is used for the actual fitting. #' Alternatively to measured VPD and Ca (i.e. conditions at instrument height), conditions at @@ -559,21 +559,21 @@ end #' ## filter data to ensure that Gs is a meaningful proxy to canopy conductance (Gc) #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' #' # calculate Gs from the the inverted PM equation -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model="Thom_1972")[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", -#' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, +#' Rn="Rn",G="G",S=nothing,VPD="VPD",Ga=Ga, #' formulation=Val(:PenmanMonteith))[,"Gs_mol"] #' #' ### Estimate the stomatal slope parameter g1 using the USO model @@ -582,7 +582,7 @@ end #' #' ### Use robust regression to minimize influence of outliers in Gs #' mod_USO = stomatal_slope(DE_Tha_Jun_2014_2,model="USO",GPP="GPP",Gs=Gs_PM, -#' robust_nls=TRUE,nmin=40,fitg0=false) +#' robust_nls=true,nmin=40,fitg0=false) #' #' ### Estimate the same parameter from the Ball&Berry model and prescribe g0 #' mod_BB = stomatal_slope(DE_Tha_Jun_2014_2,model="Ball&Berry",GPP="GPP", @@ -597,7 +597,7 @@ end #' #' @export function stomatal_slope(data,Tair="Tair",pressure="pressure",GPP="GPP",Gs="Gs_mol", - VPD="VPD",Ca="Ca",Rleaf=NULL,model=c("USO","Ball&Berry","Leuning"), + VPD="VPD",Ca="Ca",Rleaf=nothing,model=c("USO","Ball&Berry","Leuning"), robust_nls=false,nmin=40,fitg0=false,g0=0,fitD0=false, D0=1.5,Gamma=50,missing_Rleaf_as_NA=false, constants=bigleaf_constants(),...) @@ -617,10 +617,10 @@ end - if(!is_null(Rleaf)) - if(!missing_Rleaf_as_NA){Rleaf[is_na(Rleaf)] = 0 } + if(!isnothing(Rleaf)) + if(!missing_Rleaf_as_NA){Rleaf[ismissing(Rleaf)] = 0 } else - cat("Respiration from the leaves is ignored and set to 0.",fill=TRUE) + cat("Respiration from the leaves is ignored and set to 0.",fill=true) Rleaf = 0 end @@ -628,9 +628,9 @@ end if (model == "Leuning") - nr_data = sum(!is_na(GPP) & !is_na(Gs) & !is_na(VPD) & !is_na(Ca) & !is_na(Gamma)) + nr_data = sum(!ismissing(GPP) & !ismissing(Gs) & !ismissing(VPD) & !ismissing(Ca) & !ismissing(Gamma)) else - nr_data = sum(!is_na(GPP) & !is_na(Gs) & !is_na(VPD) & !is_na(Ca)) + nr_data = sum(!ismissing(GPP) & !ismissing(Gs) & !ismissing(VPD) & !ismissing(Ca)) end @@ -661,7 +661,7 @@ else end end -else if (model == "Leuning") +elseif (model == "Leuning") if (fitg0) if (fitD0) @@ -709,7 +709,7 @@ end end end -else if (model == "Ball&Berry") +elseif (model == "Ball&Berry") rH = VPD_to_rH(VPD,Tair) df$rH = rH @@ -849,7 +849,7 @@ function light_use_efficiency(GPP,PPFD) comp = complete_cases(GPP,PPFD) - LUE = sum(GPP[comp],na_rm=TRUE)/sum(PPFD[comp],na_rm=TRUE) + LUE = sum(GPP[comp],na_rm=true)/sum(PPFD[comp],na_rm=true) return(c("LUE"=LUE)) end @@ -896,14 +896,14 @@ end #' ``` #' ## calculate Ga, Gs, and the stomatal sensitivity to VPD for the site FR-Pue in #' ## May 2012. Data are filtered for daytime, sufficiently high ustar, etc. -#' FR_Pue_May_2012_2 = filter_data(FR_Pue_May_2012,quality_control=TRUE, +#' FR_Pue_May_2012_2 = filter_data(FR_Pue_May_2012,quality_control=true, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","VPD"), #' filter_vals_min=c(5,200,0.2,0.3), #' filter_vals_max=c(NA,NA,NA,NA), -#' NA_as_invalid=TRUE,quality_ext="_qc", -#' good_quality=c(0,1),missing_qc_as_bad=TRUE, +#' NA_as_invalid=true,quality_ext="_qc", +#' good_quality=c(0,1),missing_qc_as_bad=true, #' precip="precip",tprecip=0.1,precip_hours=24, #' records_per_hour=2) #' Ga = aerodynamic_conductance(FR_Pue_May_2012_2) diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index b6ac2d0..c442fdb 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -56,15 +56,15 @@ #' """ """ -function Gb_Thom(ustar,Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) +function Gb_Thom(ustar,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants()) - check_input(NULL,ustar) + check_input(nothing,ustar) Rb_h = 6.2*ustar^-0.667 Gb_h = 1/Rb_h kB_h = Rb_h*constants[:k]*ustar - if (!is_null(Sc) | !is_null(Sc_name)) + if (!isnothing(Sc) | !isnothing(Sc_name)) if (length(Sc) != length(Sc_name)) stop("arguments 'Sc' and 'Sc_name' must have the same length") end @@ -101,8 +101,8 @@ end #' - d Zero-plane displacement height (-), can be calculated using `roughness_parameters` #' - z0m Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` #' within `wind_profile` -#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). -#' Either `"Dyer_1970"` or `"Businger_1971"`. +#' - stab_formulation Stability correction function used (If `stab_correction = true`). +#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than #' `Sc_name` @@ -173,8 +173,8 @@ end #' #' @export function Gb_Choudhury(data,Tair="Tair",pressure="pressure",wind="wind",ustar="ustar",H="H", - leafwidth,LAI,zh,zr,d,z0m=NULL,stab_formulation=c("Dyer_1970","Businger_1971"), - Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) + leafwidth,LAI,zh,zr,d,z0m=nothing,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), + Sc=nothing,Sc_name=nothing,constants=bigleaf_constants()) stab_formulation = match_arg(stab_formulation) @@ -182,21 +182,21 @@ function Gb_Choudhury(data,Tair="Tair",pressure="pressure",wind="wind",ustar="us alpha = 4.39 - 3.97*exp(-0.258*LAI) - if (is_null(z0m)) - estimate_z0m = TRUE - z0m = NULL + if (isnothing(z0m)) + estimate_z0m = true + z0m = nothing else estimate_z0m = false end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, - zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=NULL, - stab_correction=TRUE,stab_formulation=stab_formulation) + zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, + stab_correction=true,stab_formulation=stab_formulation) ## avoid zero windspeed wind_zh = pmax(0.01,wind_zh) - if (!is_null(Sc) | !is_null(Sc_name)) + if (!isnothing(Sc) | !isnothing(Sc_name)) if (length(Sc) != length(Sc_name)) stop("arguments 'Sc' and 'Sc_name' must have the same length") end @@ -244,8 +244,8 @@ end #' - N Number of leaf sides participating in heat exchange (defaults to 2) #' - Cd Foliage drag coefficient (-) #' - hs Roughness height of the soil (m) -#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). -#' Either `"Dyer_1970"` or `"Businger_1971"`. +#' - stab_formulation Stability correction function used (If `stab_correction = true`). +#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additional quantities, has to be of same length than #' `Sc_name` @@ -334,32 +334,32 @@ end """ """ function Gb_Su(data,Tair="Tair",pressure="pressure",ustar="ustar",wind="wind", - H="H",zh,zr,d,z0m=NULL,Dl,fc=NULL,LAI=NULL,N=2,Cd=0.2,hs=0.01, - stab_formulation=c("Dyer_1970","Businger_1971"), - Sc=NULL,Sc_name=NULL,constants=bigleaf_constants()) + H="H",zh,zr,d,z0m=nothing,Dl,fc=nothing,LAI=nothing,N=2,Cd=0.2,hs=0.01, + stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), + Sc=nothing,Sc_name=nothing,constants=bigleaf_constants()) stab_formulation = match_arg(stab_formulation) check_input(data,list(Tair,pressure,ustar,wind,H)) - if (is_null(fc)) - if (is_null(LAI)) + if (isnothing(fc)) + if (isnothing(LAI)) stop("one of 'fc' or 'LAI' must be provided",call.=false) else fc = (1-exp(-LAI/2)) end end - if (is_null(z0m)) - estimate_z0m = TRUE - z0m = NULL + if (isnothing(z0m)) + estimate_z0m = true + z0m = nothing else estimate_z0m = false end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, - zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=NULL, - stab_correction=TRUE,stab_formulation=stab_formulation) + zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, + stab_correction=true,stab_formulation=stab_formulation) v = kinematic_viscosity(Tair,pressure,constants) Re = Reynolds_Number(Tair,pressure,ustar,hs,constants) @@ -371,7 +371,7 @@ end Rb_h = kB_h/(constants[:k]*ustar) Gb_h = 1/Rb_h - if (!is_null(Sc) | !is_null(Sc_name)) + if (!isnothing(Sc) | !isnothing(Sc_name)) if (length(Sc) != length(Sc_name)) stop("arguments 'Sc' and 'Sc_name' must have the same length") end diff --git a/inst/fromR/check_input.jl b/inst/fromR/check_input.jl index d94461b..ba43877 100755 --- a/inst/fromR/check_input.jl +++ b/inst/fromR/check_input.jl @@ -22,22 +22,22 @@ function check_input(data,...) vars = check_length(list(...)) if (missing(data)) - data = NULL + data = nothing end varlist = match_call()[-c(1:2)] varnames = c(unlist(sapply(varlist,as_character))) - varnames = varnames[!varnames %in% c("c","list")] + varnames = varnames[!varnames in c("c","list")] for (v in seq_along(vars)) var = vars[[v]] - varname = ifelse(varnames[v] %in% c("var","var_qc"),gsub("\"","",deparse(substitute(var))),varnames[v]) + varname = ifelse(varnames[v] in c("var","var_qc"),gsub("\"","",deparse(substitute(var))),varnames[v]) if (is_character(var)) - if (!missing(data) & !is_null(data)) + if (!missing(data) & !isnothing(data)) if (length(var) == 1) - if (var %in% colnames(data)) + if (var in colnames(data)) var = data[,var] if (is_numeric(var)) assign(varname,var,pos=sys_frame(-1)) @@ -51,8 +51,8 @@ else stop("name of variable '",varname,"' must have length 1",call.=false) end else - if ("data" %in% names(formals(sys_function(which=-1)))) - if (var %in% as_character(unlist(match_call(definition=sys_function(-1),call=sys_call(-1))[-1]))) + if ("data" in names(formals(sys_function(which=-1)))) + if (var in as_character(unlist(match_call(definition=sys_function(-1),call=sys_call(-1))[-1]))) stop("variable '",var,"' is of type character and interpreted as a column name, but no input matrix/DataFrame is provided. Provide '",var,"' as a numeric vector, or an input matrix/DataFrame with a column named '",var,"'",call.=false) else stop("variable '",var,"' is not provided",call.=false) @@ -63,25 +63,25 @@ end end else if (length(var) < 2) - if (is_null(var)) + if (isnothing(var)) assign(varname,var,pos=sys_frame(-1)) next -else if (is_na(var)) +elseif (ismissing(var)) assign(varname,var,pos=sys_frame(-1)) next end end - if (!missing(data) & !is_null(data)) + if (!missing(data) & !isnothing(data)) if (is_numeric(var) & length(var) == nrow(data)) assign(varname,var,envir=sys_frame(-1)) -else if (is_numeric(var) & length(var) != nrow(data)) +elseif (is_numeric(var) & length(var) != nrow(data)) if (length(var) == 1) var = rep(var,length=nrow(data)) assign(varname,var,envir=sys_frame(-1)) else stop("variable '",varname,"' must have the same length as the input matrix/DataFrame or length 1. Do NOT provide an input matrix/DataFrame if none of its variables are used!",call.=false) end -else if (!is_numeric(var)) +elseif (!is_numeric(var)) stop("variable '",varname,"' must be numeric",call.=false) end else diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index 5d50b4e..837de71 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -92,9 +92,9 @@ function decoupling(data,Tair="Tair",pressure="pressure",Ga="Ga_h",Gs="Gs_ms", Omega = (epsilon + 1) / (epsilon + 1 + Ga/Gs) -else if (approach == "Martin_1989") +elseif (approach == "Martin_1989") - if (is_null(LAI)) + if (isnothing(LAI)) stop("LAI is not provided!") diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index 472f516..f31a9c6 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -103,10 +103,10 @@ end #' - LE Latent heat flux (W m-2) #' - H Sensible heat flux (W m-2) #' - instantaneous should the energy balance be calculated at the time step -#' of the observations (`TRUE`), or over the entire time period +#' of the observations (`true`), or over the entire time period #' provided as input (`false`) -#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s ,otherwise set to 0. -#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `NA`s ,otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. #' #' #' # Details @@ -125,7 +125,7 @@ end #' - r_squared: r^2 of the OLS regression #' - EBR: energy balance ratio #' -#' if `instantaneous = TRUE`, only `EBR` is returned. +#' if `instantaneous = true`, only `EBR` is returned. #' #' #References #' Wilson K., et al. 2002: Energy balance closure at FLUXNET sites. @@ -137,28 +137,28 @@ end #' energy_closure(DE_Tha_Jun_2014,instantaneous=false) #' #' ## look at half-hourly closure -#' EBR_inst = energy_closure(DE_Tha_Jun_2014,instantaneous=TRUE) +#' EBR_inst = energy_closure(DE_Tha_Jun_2014,instantaneous=true) #' summary(EBR_inst) #' #' @importFrom stats complete_cases lm """ """ -function energy_closure(data,Rn="Rn",G=NULL,S=NULL,LE="LE",H="H",instantaneous=false, +function energy_closure(data,Rn="Rn",G=nothing,S=nothing,LE="LE",H="H",instantaneous=false, missing_G_as_NA=false,missing_S_as_NA=false) check_input(data,list(Rn,LE,H,G,S)) - if(!is_null(G)) - if (!missing_G_as_NA){G[is_na(G)] = 0} + if(!isnothing(G)) + if (!missing_G_as_NA){G[ismissing(G)] = 0} else - cat("Ground heat flux G is not provided and set to 0.",fill=TRUE) + cat("Ground heat flux G is not provided and set to 0.",fill=true) G = rep(0,nrow(data)) end - if(!is_null(S)) - if(!missing_S_as_NA){S[is_na(S)] = 0 } + if(!isnothing(S)) + if(!missing_S_as_NA){S[ismissing(S)] = 0 } else - cat("Energy storage fluxes S are not provided and set to 0.",fill=TRUE) + cat("Energy storage fluxes S are not provided and set to 0.",fill=true) S = rep(0,nrow(data)) end diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index cab37f9..d201736 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -19,8 +19,8 @@ #' - alpha Priestley-Taylor coefficient; only used if `approach = Val(:PriestleyTaylor)`. #' - Gs_pot Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; #' only used if `approach = Val(:PenmanMonteith)`. -#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -92,7 +92,7 @@ #' surface_conductance(Tair=20,pressure=100,VPD=2,Ga=0.1,Rn=400,LE=LE_pot_PM) """ """ -function potential_ET(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, +function potential_ET(data,Tair="Tair",pressure="pressure",Rn="Rn",G=nothing,S=nothing, VPD="VPD",Ga="Ga_h",approach=c(Val(:PriestleyTaylor),Val(:PenmanMonteith)), alpha=1.26,Gs_pot=0.6,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), @@ -102,21 +102,21 @@ function potential_ET(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL check_input(data,list(Tair,pressure,Rn,G,S)) - if(!is_null(G)) + if(!isnothing(G)) if (!missing_G_as_NA) - G[is_na(G)] = 0 + G[ismissing(G)] = 0 end else - cat("Ground heat flux G is not provided and set to 0.",fill=TRUE) + cat("Ground heat flux G is not provided and set to 0.",fill=true) G = 0 end - if(!is_null(S)) + if(!isnothing(S)) if(!missing_S_as_NA) - S[is_na(S)] = 0 + S[ismissing(S)] = 0 end else - cat("Energy storage fluxes S are not provided and set to 0.",fill=TRUE) + cat("Energy storage fluxes S are not provided and set to 0.",fill=true) S = 0 end @@ -129,7 +129,7 @@ end LE_pot = (alpha * Delta * (Rn - G - S)) / (Delta + gamma) ET_pot = LE_to_ET(LE_pot,Tair) -else if (approach == Val(:PenmanMonteith)) +elseif (approach == Val(:PenmanMonteith)) check_input(data,list(Gs_pot,VPD,Ga)) @@ -163,8 +163,8 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -176,7 +176,7 @@ end #' #' @export function reference_ET(data,Gs_ref=0.0143,Tair="Tair",pressure="pressure",VPD="VPD",Rn="Rn",Ga="Ga_h", - G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, + G=nothing,S=nothing,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) @@ -200,8 +200,8 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -261,23 +261,23 @@ end """ """ function equilibrium_imposed_ET(data,Tair="Tair",pressure="pressure",VPD="VPD",Gs="Gs_ms", - Rn="Rn",G=NULL,S=NULL,missing_G_as_NA=false,missing_S_as_NA=false, + Rn="Rn",G=nothing,S=nothing,missing_G_as_NA=false,missing_S_as_NA=false, Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) check_input(data,list(Tair,pressure,VPD,Rn,Gs,G,S)) - if(!is_null(G)) - if (!missing_G_as_NA){G[is_na(G)] = 0} + if(!isnothing(G)) + if (!missing_G_as_NA){G[ismissing(G)] = 0} else - cat("Ground heat flux G is not provided and set to 0.",fill=TRUE) + cat("Ground heat flux G is not provided and set to 0.",fill=true) G = 0 end - if(!is_null(S)) - if(!missing_S_as_NA){S[is_na(S)] = 0 } + if(!isnothing(S)) + if(!missing_S_as_NA){S[ismissing(S)] = 0 } else - cat("Energy storage fluxes S are not provided and set to 0.",fill=TRUE) + cat("Energy storage fluxes S are not provided and set to 0.",fill=true) S = 0 end diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index 7497a1b..eb51273 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -9,7 +9,7 @@ #' #' - data Data_frame or matrix containing all required input variables in #' half-hourly or hourly resolution. Including year, month, day information -#' - quality_control Should quality control be applied? Defaults to `TRUE`. +#' - quality_control Should quality control be applied? Defaults to `true`. #' - filter_growseas Should data be filtered for growing season? Defaults to `false`. #' - filter_precip Should precipitation filtering be applied? Defaults to `false`. #' - filter_vars Additional variables to be filtered. Vector of type character. @@ -17,7 +17,7 @@ #' the same length than `filter_vars`. Set to `NA` to be ignored. #' - filter_vals_max Maximum values of the variables to be filtered. Numeric vector of #' the same length than `filter_vars`. Set to `NA` to be ignored. -#' - NA_as_invalid If `TRUE` (the default) missing data are filtered out (applies to all variables). +#' - NA_as_invalid If `true` (the default) missing data are filtered out (applies to all variables). #' - vars_qc Character vector indicating the variables for which quality filter should #' be applied. Ignored if `quality_control = false`. #' - quality_ext The extension to the variables' names that marks them as @@ -25,7 +25,7 @@ #' - good_quality Which values indicate good quality (i.e. not to be filtered) #' in the quality control (qc) variables? Ignored if `quality_control = false`. #' - missing_qc_as_bad If quality control variable is `NA`, should the corresponding data point be -#' treated as bad quality? Defaults to `TRUE`. Ignored if `quality_control = false`. +#' treated as bad quality? Defaults to `true`. Ignored if `quality_control = false`. #' - precip Precipitation (mm time-1) #' - GPP Gross primary productivity (umol m-2 s-1); Ignored if `filter_growseas = false`. #' - doy Day of year; Ignored if `filter_growseas = false`. @@ -41,7 +41,7 @@ #' - precip_hours Number of hours removed following a precipitation event (h). #' Ignored if `filter_precip = false`. #' - records_per_hour Number of observations per hour. I_e. 2 for half-hourly data. -#' - filtered_data_to_NA Logical. If `TRUE` (the default), all variables in the input +#' - filtered_data_to_NA Logical. If `true` (the default), all variables in the input #' DataFrame/matrix are set to `NA` for the time step where ANY of the #' `filter_vars` were beyond their acceptable range (as #' determined by `filter_vals_min` and `filter_vals_max`). @@ -69,7 +69,7 @@ #' are set to `NA` for all variables. If a threshold is set to `NA`, it will be ignored. #' #' # Value - If `filtered_data_to_NA = TRUE` (default), the input DataFrame/matrix with + If `filtered_data_to_NA = true` (default), the input DataFrame/matrix with #' observations which did not fulfill the filter criteria set to `NA`. #' If `filtered_data_to_NA = false`, the input DataFrame/matrix with an additional #' column "valid", which indicates whether all the data of a time step fulfill the @@ -83,7 +83,7 @@ #' #' Variables considered of bad quality (as specified by the corresponding quality control variables) #' will be set to `NA` by this routine. Data that do not fulfill the filtering criteria are set to -#' `NA` if `filtered_data_to_NA = TRUE`. Note that with this option *all* variables of the same +#' `NA` if `filtered_data_to_NA = true`. Note that with this option *all* variables of the same #' time step are set to `NA`. Alternatively, if `filtered_data_to_NA = false` data are not set to `NA`, #' and a new column "valid" is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). @@ -93,29 +93,29 @@ #' ``` #' # Example of data filtering; data are for a month within the growing season, #' # hence growing season is not filtered. -#' # If filtered_data_to_NA=TRUE, all values of a row are set to NA if one filter +#' # If filtered_data_to_NA=true, all values of a row are set to NA if one filter #' # variable is beyond its bounds. #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2, -#' filtered_data_to_NA=TRUE) +#' filtered_data_to_NA=true) #' #' ## same, but with filtered_data_to_NA=false #' DE_Tha_Jun_2014_3 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2, #' filtered_data_to_NA=false) @@ -127,56 +127,56 @@ #' #' @importFrom stats aggregate #' @export -function filter_data(data,quality_control=TRUE,filter_growseas=false, - filter_precip=false,filter_vars=NULL, - filter_vals_min,filter_vals_max,NA_as_invalid=TRUE, - vars_qc=NULL,quality_ext="_qc",good_quality=c(0,1), - missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +function filter_data(data,quality_control=true,filter_growseas=false, + filter_precip=false,filter_vars=nothing, + filter_vals_min,filter_vals_max,NA_as_invalid=true, + vars_qc=nothing,quality_ext="_qc",good_quality=c(0,1), + missing_qc_as_bad=true,GPP="GPP",doy="doy", year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", tprecip=0.01,precip_hours=24,records_per_hour=2, - filtered_data_to_NA=TRUE,constants=bigleaf_constants()) + filtered_data_to_NA=true,constants=bigleaf_constants()) ### I) Quality control filter if (quality_control) - if (is_null(vars_qc)) - stop("quality_control (qc) is TRUE, but no qc variables are provided!") + if (isnothing(vars_qc)) + stop("quality_control (qc) is true, but no qc variables are provided!") end - if (any(!vars_qc %in% colnames(data))) + if (any(!vars_qc in colnames(data))) - missing_vars = vars_qc[which(!vars_qc %in% colnames(data))] + missing_vars = vars_qc[which(!vars_qc in colnames(data))] stop(paste("Variable ",missing_vars," is included in 'vars_qc', but does not exist in the input data!")) end vars_qc_qc = paste0(vars_qc,quality_ext) - if (any(!vars_qc_qc %in% colnames(data))) + if (any(!vars_qc_qc in colnames(data))) - missing_vars_qc = vars_qc_qc[which(!vars_qc_qc %in% colnames(data))] + missing_vars_qc = vars_qc_qc[which(!vars_qc_qc in colnames(data))] missing_vars2 = substr(missing_vars_qc,1,nchar(missing_vars_qc) - nchar(quality_ext)) stop(paste("Quality control for variable ",missing_vars2,"(",missing_vars_qc,") does not exist in the input data!")) end ## data quality - cat("Quality control:",fill=TRUE) + cat("Quality control:",fill=true) for (var in vars_qc) var_qc = paste0(var,quality_ext) check_input(data,var) check_input(data,var_qc) if (missing_qc_as_bad) - data[get(paste0(var,quality_ext)) > max(good_quality) | is_na(get(paste0(var,quality_ext))),var] = NA # exclude bad quality data or those where qc flag is not available - qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) | is_na(get(paste0(var,quality_ext)))) # count & report + data[get(paste0(var,quality_ext)) > max(good_quality) | ismissing(get(paste0(var,quality_ext))),var] = NA # exclude bad quality data or those where qc flag is not available + qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) | ismissing(get(paste0(var,quality_ext)))) # count & report else { # same, but consider missing quality flag variables as good - data[get(paste0(var,quality_ext)) > max(good_quality) & !is_na(get(paste0(var,quality_ext))),var] = NA - qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) & !is_na(get(paste0(var,quality_ext)))) + data[get(paste0(var,quality_ext)) > max(good_quality) & !ismissing(get(paste0(var,quality_ext))),var] = NA + qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) & !ismissing(get(paste0(var,quality_ext)))) end qc_invalid_perc = round((qc_invalid/nrow(data))*constants[:frac2percent],2) - cat(var,": ",qc_invalid," data points (",qc_invalid_perc,"%) set to NA",fill=TRUE,sep="") + cat(var,": ",qc_invalid," data points (",qc_invalid_perc,"%) set to NA",fill=true,sep="") end end @@ -189,7 +189,7 @@ end if(filter_growseas) check_input(data,doy,year,GPP) date = strptime(paste0(year,"-",doy),format="%Y-%j") - GPP_daily = aggregate(GPP,by=list(strftime(date)),mean,na_rm=TRUE)[,2] + GPP_daily = aggregate(GPP,by=list(strftime(date)),mean,na_rm=true)[,2] growing_season = filter_growingseason(GPP_daily,tGPP=tGPP,ws=ws,min_int=min_int) growseas_invalid = which(sapply(growing_season,rep,48) == 0) end @@ -199,25 +199,25 @@ end if (filter_precip) check_input(data,precip) if (NA_as_invalid) - precip_events = which(precip > tprecip | is_na(precip)) + precip_events = which(precip > tprecip | ismissing(precip)) else precip_events = which(precip > tprecip) end - precip_invalid = unique(as_numeric(unlist(sapply(precip_events, function(x) x:(min(x+precip_hours*records_per_hour,nrow(data),na_rm=TRUE)))))) + precip_invalid = unique(as_numeric(unlist(sapply(precip_events, function(x) x:(min(x+precip_hours*records_per_hour,nrow(data),na_rm=true)))))) end # 3) all other filter variables (as defined in filter_vars) invalids = list(growseas_invalid,precip_invalid) - if (!is_null(filter_vars)) + if (!isnothing(filter_vars)) for (var in filter_vars) v = which(filter_vars == var) vf = v + 2 check_input(data,var) if (NA_as_invalid) - invalids[[vf]] = which(get(var) < filter_vals_min[v] | get(var) > filter_vals_max[v] | is_na(get(var))) + invalids[[vf]] = which(get(var) < filter_vals_min[v] | get(var) > filter_vals_max[v] | ismissing(get(var))) else - invalids[[vf]] = which(get(var) < filter_vals_min[v] | get(var) > filter_vals_max[v] & !is_na(get(var))) + invalids[[vf]] = which(get(var) < filter_vals_min[v] | get(var) > filter_vals_max[v] & !ismissing(get(var))) end end end @@ -237,17 +237,17 @@ end var_names = c("growing season","precipitation",filter_vars) if (quality_control) - cat("-------------------------------------------------------------------",fill=TRUE) + cat("-------------------------------------------------------------------",fill=true) end - cat("Data filtering:",fill=TRUE) + cat("Data filtering:",fill=true) - cat(length(growseas_invalid)," data points (",invalids_perc[1],"%) excluded by growing season filter",fill=TRUE,sep="") + cat(length(growseas_invalid)," data points (",invalids_perc[1],"%) excluded by growing season filter",fill=true,sep="") invisible(sapply(c(1:(length(invalids)-1)), function(x) cat(additional_invalids[x]," additional data points (", additional_invalids_perc[x],"%) excluded by ",var_names[x+1], " filter (",length(unlist(invalids[x+1]))," data points = ", - invalids_perc[x+1]," % in total)",fill=TRUE,sep=""))) + invalids_perc[x+1]," % in total)",fill=true,sep=""))) invalid = unique(unlist(invalids)) @@ -255,8 +255,8 @@ end excl_perc = round((length(invalid)/nrow(data))*constants[:frac2percent],2) - cat(length(invalid)," data points (",excl_perc,"%) excluded in total",fill=TRUE,sep="") - cat(nrow(data) - length(invalid)," valid data points (",constants[:frac2percent]-excl_perc,"%) remaining.",fill=TRUE,sep="") + cat(length(invalid)," data points (",excl_perc,"%) excluded in total",fill=true,sep="") + cat(nrow(data) - length(invalid)," valid data points (",constants[:frac2percent]-excl_perc,"%) remaining.",fill=true,sep="") # 6) return input data frame with filtered time steps set to NA or an additional 'valid' column @@ -317,24 +317,24 @@ end #' @export function filter_growingseason(GPPd,tGPP,ws=15,min_int=5) - if(sum(is_na(GPPd)) < 0.5*length(GPPd)) + if(sum(ismissing(GPPd)) < 0.5*length(GPPd)) growseas = rep(1,length(GPPd)) - GPP_threshold = quantile(GPPd,probs=0.95,na_rm=TRUE)*tGPP + GPP_threshold = quantile(GPPd,probs=0.95,na_rm=true)*tGPP ## smooth GPP GPPd_smoothed = filter(GPPd,method="convolution",filter=rep(1/ws,ws)) ## set values at the beginning and end of the time series to the mean of the original values wsd = floor(ws/2) - GPPd_smoothed[1:wsd] = mean(GPPd[1:(2*wsd)],na_rm=TRUE) - GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] = mean(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)],na_rm=TRUE) + GPPd_smoothed[1:wsd] = mean(GPPd[1:(2*wsd)],na_rm=true) + GPPd_smoothed[(length(GPPd)-(wsd-1)):length(GPPd)] = mean(GPPd[(length(GPPd)-(2*wsd-1)):length(GPPd)],na_rm=true) # check for occurence of missing values and set them to mean of the values surrounding them - missing = which(is_na(GPPd_smoothed)) + missing = which(ismissing(GPPd_smoothed)) if (length(missing) > 0) - if (length(missing) > 10){warning("Attention, there is a gap in 'GPPd' of length n = ",length(missing))} - replace_val = mean(GPPd_smoothed[max(1,missing[1] - 4):min((missing[length(missing)] + 4),length(GPPd_smoothed))],na_rm=TRUE) + if (length(missing) > 10){@warn"Attention, there is a gap in 'GPPd' of length n = ",length(missing))} + replace_val = mean(GPPd_smoothed[max(1,missing[1] - 4):min((missing[length(missing)] + 4),length(GPPd_smoothed))],na_rm=true) GPPd_smoothed[missing] = replace_val end @@ -358,7 +358,7 @@ end if (val == 0 & growseas[start[i]-1] == 1) growseas[start[i]:end[i]] = 1 -else if (val == 1 & growseas[start[i]-1] == 0) +elseif (val == 1 & growseas[start[i]-1] == 0) growseas[start[i]:end[i]] = 0 end end @@ -368,7 +368,7 @@ end else - warning("number of available GPPd data is less than half the total number of days per year. Filter is not applied!") + @warn"number of available GPPd data is less than half the total number of days per year. Filter is not applied!") growseas = as_integer(rep(1,length(GPPd))) end diff --git a/inst/fromR/meteorological_variables.jl b/inst/fromR/meteorological_variables.jl index b5d1a17..924002c 100755 --- a/inst/fromR/meteorological_variables.jl +++ b/inst/fromR/meteorological_variables.jl @@ -81,11 +81,11 @@ end #' pressure_from_elevation(500,Tair=25,VPD=1) #' #' @export -function pressure_from_elevation(elev,Tair,VPD=NULL,constants=bigleaf_constants()) +function pressure_from_elevation(elev,Tair,VPD=nothing,constants=bigleaf_constants()) Tair = Tair + constants[:Kelvin] - if(is_null(VPD)) + if(isnothing(VPD)) pressure = constants[:pressure0] / exp(constants[:g] * elev / (constants[:Rd]*Tair)) @@ -169,11 +169,11 @@ function Esat_slope(Tair,formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), a = 611.2 b = 17.62 c = 243.12 -else if (formula == "Alduchov_1996") +elseif (formula == "Alduchov_1996") a = 610.94 b = 17.625 c = 243.04 -else if (formula == "Allen_1998") +elseif (formula == "Allen_1998") a = 610.8 b = 17.27 c = 237.3 @@ -351,8 +351,8 @@ end end # determine number of digits to print - ndigits = as_numeric(strsplit(format(accuracy,scientific = TRUE),"-")[[1]][2]) - ndigits = ifelse(is_na(ndigits),0,ndigits) + ndigits = as_numeric(strsplit(format(accuracy,scientific = true),"-")[[1]][2]) + ndigits = ifelse(ismissing(ndigits),0,ndigits) gamma = psychrometric_constant(Tair,pressure) @@ -448,8 +448,8 @@ end end # determine number of digits to print - ndigits = as_numeric(strsplit(format(accuracy,scientific = TRUE),"-")[[1]][2]) - ndigits = ifelse(is_na(ndigits),0,ndigits) + ndigits = as_numeric(strsplit(format(accuracy,scientific = true),"-")[[1]][2]) + ndigits = ifelse(ismissing(ndigits),0,ndigits) ea = VPD_to_e(VPD,Tair,Esat_formula) Td = sapply(seq_along(ea),function(i) round(dew_point_solver(ea[i],accuracy=accuracy, diff --git a/inst/fromR/potential_radiation.jl b/inst/fromR/potential_radiation.jl index d5e59ff..fe220e0 100755 --- a/inst/fromR/potential_radiation.jl +++ b/inst/fromR/potential_radiation.jl @@ -70,7 +70,7 @@ end #' @importFrom solartime computeSunPositionDoyHour """ """ -function potential_radiation(doy, hour, latDeg, longDeg, timezone, useSolartime = TRUE) +function potential_radiation(doy, hour, latDeg, longDeg, timezone, useSolartime = true) # Calculate potential radiation from solar elevation and extraterrestrial solar radiation solElevRad = computeSunPositionDoyHour( diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 048d456..5123670 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -112,8 +112,8 @@ end #' from the exponential wind profile under non-neutral conditions. #' #' - zeta Stability parameter zeta (-) -#' - formulation Formulation for the stability function. Either `"Dyer_1970"`, -#' or `"Businger_1971"` +#' - formulation Formulation for the stability function. Either `Val(:Dyer_1970)`, +#' or `Val(:Businger_1971)` #' #' # Details The functions give the integrated form of the universal functions. They @@ -121,11 +121,11 @@ end #' which can be calculated from the function [`stability_parameter`](@ref). #' The integration of the universal functions is: #' -#' ``\psi = -x * zeta`` +#' ``\\psi = -x * zeta`` #' #' for stable atmospheric conditions (``\zeta`` >= 0), and #' -#' ``\psi = 2 * log( (1 + y) / 2) `` +#' ``\\psi = 2 * log( (1 + y) / 2) `` #' #' for unstable atmospheric conditions (``\zeta`` < 0). #' @@ -157,24 +157,24 @@ end #' ``` #' zeta = seq(-2,0.5,0.05) #' stability_correction(zeta) -#' stability_correction(zeta,formulation="Businger_1971") +#' stability_correction(zeta,formulation=Val(:Businger_1971)) #' #' @export -function stability_correction(zeta,formulation=c("Dyer_1970","Businger_1971")) +function stability_correction(zeta,formulation=c(Val(:Dyer_1970),Val(:Businger_1971))) formulation = match_arg(formulation) - check_input(NULL,zeta) + check_input(nothing,zeta) psi_h = psi_m = numeric() # universal functions - if (formulation == "Businger_1971") + if (formulation == Val(:Businger_1971)) x_h = -7.8 x_m = -6 y_h = 0.95 * ( 1 - 11.6 * zeta)^0.5 y_m = (1 - 19.3*zeta)^0.25 -else if (formulation == "Dyer_1970") +elseif (formulation == Val(:Dyer_1970)) x_h = x_m = -5 y_h = (1 - 16 * zeta)^0.5 y_m = (1 - 16 * zeta)^0.25 @@ -182,11 +182,11 @@ end # integration of universal functions (after Paulson_1970 and Foken 2008) # stable - stable = zeta >= 0 | is_na(zeta) + stable = zeta >= 0 | ismissing(zeta) psi_h[stable] = x_h * zeta[stable] psi_m[stable] = x_m * zeta[stable] # unstable - unstable = zeta < 0 | is_na(zeta) + unstable = zeta < 0 | ismissing(zeta) psi_h[unstable] = 2 * log( (1 + y_h[unstable] ) / 2) psi_m[unstable] = 2 * log( (1 + y_m[unstable] ) / 2) + log( ( 1 + y_m[unstable]^2 ) / 2) diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index 1e9b9b7..2f7c0ad 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -16,9 +16,9 @@ #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance for heat/water vapor (m s-1) #' - calc_surface_CO2 Calculate surface CO2 concentration? Defaults to `false`. -#' - Ca Atmospheric CO2 concentration (mol mol-1). Required if `calc_surface_CO2 = TRUE`. -#' - NEE Net ecosystem exchange (umol m-2 s-1). Required if `calc_surface_CO2 = TRUE`. -#' - Ga_CO2 Aerodynamic conductance for CO2 (m s-1). Required if `calc_surface_CO2 = TRUE`. +#' - Ca Atmospheric CO2 concentration (mol mol-1). Required if `calc_surface_CO2 = true`. +#' - NEE Net ecosystem exchange (umol m-2 s-1). Required if `calc_surface_CO2 = true`. +#' - Ga_CO2 Aerodynamic conductance for CO2 (m s-1). Required if `calc_surface_CO2 = true`. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -59,7 +59,7 @@ #' #' # Note #' The following sign convention for NEE is employed (relevant if -#' `calc_surface_CO2 = TRUE`): +#' `calc_surface_CO2 = true`): #' negative values of NEE denote net CO2 uptake by the ecosystem. #' #' # Value @@ -81,7 +81,7 @@ #' #' # now calculate also surface CO2 concentration #' surface_conditions(Tair=25,pressure=100,LE=100,H=200,VPD=1.2,Ga=c(0.02,0.05,0.1), -#' Ca=400,Ga_CO2=c(0.02,0.05,0.1),NEE=-20,calc_surface_CO2=TRUE) +#' Ca=400,Ga_CO2=c(0.02,0.05,0.1),NEE=-20,calc_surface_CO2=true) #' #' #References #' Knauer, J. et al., 2018: Towards physiologically meaningful water-use efficiency estimates diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index acf6754..6c16737 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -16,9 +16,9 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) -#' - missing_G_as_NA if `TRUE`, missing G are treated as `NA`s, otherwise they are set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise they are set to 0. #' Only used if `formulation = Val(:PenmanMonteith)`. -#' - missing_S_as_NA if `TRUE`, missing S are treated as `NA`s, otherwise they are set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise they are set to 0. #' Only used if `formulation = Val(:PenmanMonteith)`. #' - formulation Formulation used. Either `Val(:PenmanMonteith)` (the default) #' using the inverted Penman-Monteith equation, or `"Flux-Gradient"`, @@ -45,8 +45,8 @@ #' saturation vapor pressure curve (kPa K-1), and ``\\rho`` is air density (kg m-3). #' Available energy (A) is defined as A = Rn - G - S. If G and/or S are not provided, A = Rn. #' -#' By default, any missing data in G and S are set to 0. If `missing_S_as_NA = TRUE` -#' or `missing_S_as_NA = TRUE`, Gs will give `NA` for these timesteps. +#' By default, any missing data in G and S are set to 0. If `missing_S_as_NA = true` +#' or `missing_S_as_NA = true`, Gs will give `NA` for these timesteps. #' #' If `formulation="Flux-Gradient"`, Gs (in mol m-2 s-1) is calculated from VPD and ET only: #' @@ -73,12 +73,12 @@ #' ## filter data to ensure that Gs is a meaningful proxy to canopy conductance (Gc) #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","VPD","H","LE"), -#' filter_growseas=false,filter_precip=TRUE, +#' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=TRUE, +#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), -#' missing_qc_as_bad=TRUE,GPP="GPP",doy="doy", +#' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' @@ -89,12 +89,12 @@ #' #' # calculate Gs from the the inverted PM equation (now Rn, and Ga are needed), #' # using a simple estimate of Ga based on Thom 1972 -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model="Thom_1972")[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' # Note that Ga is not added to the DataFrame 'DE_Tha_Jun_2014' #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", -#' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga=Ga, +#' Rn="Rn",G="G",S=nothing,VPD="VPD",Ga=Ga, #' formulation=Val(:PenmanMonteith)) #' summary(Gs_PM) #' @@ -102,7 +102,7 @@ #' # now add Ga to the DataFrame 'DE_Tha_Jun_2014' and repeat #' DE_Tha_Jun_2014_2$Ga = Ga #' Gs_PM2 = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", -#' Rn="Rn",G="G",S=NULL,VPD="VPD",Ga="Ga", +#' Rn="Rn",G="G",S=nothing,VPD="VPD",Ga="Ga", #' formulation=Val(:PenmanMonteith)) #' # note the difference to the previous version (Ga="Ga") #' summary(Gs_PM2) @@ -118,7 +118,7 @@ #' """ """ -function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL,S=NULL, +function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=nothing,S=nothing, VPD="VPD",LE="LE",Ga="Ga_h",missing_G_as_NA=false,missing_S_as_NA=false, formulation=c(Val(:PenmanMonteith),"Flux-Gradient"), Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), @@ -133,21 +133,21 @@ function surface_conductance(data,Tair="Tair",pressure="pressure",Rn="Rn",G=NULL Gs_mol = (LE_to_ET(LE,Tair)/constants[:Mw]) * pressure / VPD Gs_ms = mol_to_ms(Gs_mol,Tair,pressure) -else if (formulation == Val(:PenmanMonteith)) +elseif (formulation == Val(:PenmanMonteith)) check_input(data,list(Tair,pressure,VPD,LE,Rn,Ga,G,S)) - if(!is_null(G)) - if (!missing_G_as_NA){G[is_na(G)] = 0} + if(!isnothing(G)) + if (!missing_G_as_NA){G[ismissing(G)] = 0} else - cat("Ground heat flux G is not provided and set to 0.",fill=TRUE) + cat("Ground heat flux G is not provided and set to 0.",fill=true) G = 0 end - if(!is_null(S)) - if(!missing_S_as_NA){S[is_na(S)] = 0 } + if(!isnothing(S)) + if(!missing_S_as_NA){S[ismissing(S)] = 0 } else - cat("Energy storage fluxes S are not provided and set to 0.",fill=TRUE) + cat("Energy storage fluxes S are not provided and set to 0.",fill=true) S = 0 end diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index 33f9ca1..c2c548e 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -49,7 +49,7 @@ end #' A simple approximation of the two roughness parameters displacement height (d) #' and roughness length for momentum (z0m). #' -#' - method Method to use, one of `"canopy_height","canopy_height&LAI","wind_profile"` +#' - method Method to use, one of `"canopy_height","canopy_height&LAI",Val(:wind_profile)` #' NOTE: if `method = "canopy_height"`, only the following three arguments #' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, #' and `hs` are required. @@ -61,7 +61,7 @@ end #' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. #' Only needed if `method = "canopy_height&LAI"`. #' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` -#' The following arguments are only needed if `method = "wind_profile"`! +#' The following arguments are only needed if `method = Val(:wind_profile)`! #' - data Data_frame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) @@ -70,9 +70,9 @@ end #' - H Sensible heat flux (W m-2) #' - d Zero-plane displacement height (m); optional #' - z0m Roughness length for momentum (m); optional -#' - stab_roughness Should stability correction be considered? Default is `TRUE`. -#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). -#' Either `"Dyer_1970"` or `"Businger_1971"`. +#' - stab_roughness Should stability correction be considered? Default is `true`. +#' - stab_formulation Stability correction function used (If `stab_correction = true`). +#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. #' - constants k - von-Karman constant (-) #' Kelvin - conversion degree Celsius to Kelvin #' cp - specific heat of air for constant pressure (J K-1 kg-1) @@ -105,7 +105,7 @@ end #' #' ``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` #' -#' If `method = "wind_profile"`, z0m is estimated by solving +#' If `method = Val(:wind_profile)`, z0m is estimated by solving #' the wind speed profile for z0m: #' #' ``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` @@ -137,17 +137,17 @@ end #' #' # fix d to 0.7*zh and estimate z0m from the wind profile #' df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) -#' roughness_parameters(method="wind_profile",zh=25,zr=40,frac_d=0.7,data=df) +#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.7,data=df) #' #' # assume d = 0.8*zh -#' roughness_parameters(method="wind_profile",zh=25,zr=40,frac_d=0.8,data=df) +#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.8,data=df) #' #' @importFrom stats median sd complete_cases #' @export -function roughness_parameters(method=c("canopy_height","canopy_height&LAI","wind_profile"),zh, +function roughness_parameters(method=c("canopy_height","canopy_height&LAI",Val(:wind_profile)),zh, frac_d=0.7,frac_z0m=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", - wind="wind",ustar="ustar",H="H",d=NULL,z0m=NULL, - stab_roughness=TRUE,stab_formulation=c("Dyer_1970","Businger_1971"), + wind="wind",ustar="ustar",H="H",d=nothing,z0m=nothing, + stab_roughness=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), constants=bigleaf_constants()) method = match_arg(method) @@ -159,7 +159,7 @@ function roughness_parameters(method=c("canopy_height","canopy_height&LAI","wind z0m = frac_z0m*zh z0m_se = NA -else if (method == "canopy_height&LAI") +elseif (method == "canopy_height&LAI") X = cd * LAI d = 1.1 * zh * log(1 + X^(1/4)) @@ -171,11 +171,11 @@ else end z0m_se = NA -else if (method == "wind_profile") +elseif (method == Val(:wind_profile)) check_input(data,Tair,pressure,wind,ustar,H) - if (is_null(d)) + if (isnothing(d)) d = frac_d * zh @@ -196,8 +196,8 @@ end z0m_all[z0m_all > zh] = NA - z0m = median(z0m_all,na_rm=TRUE) - z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=TRUE) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) + z0m = median(z0m_all,na_rm=true) + z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=true) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) end @@ -218,7 +218,7 @@ end #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) #' - H Sensible heat flux (W m-2) -#' - wind Wind speed at height zr (m s-1); only used if `stab_correction = TRUE` +#' - wind Wind speed at height zr (m s-1); only used if `stab_correction = true` #' - zr Instrument (reference) height (m) #' - zh Canopy height (m) #' - d Zero-plane displacement height (-) @@ -227,12 +227,12 @@ end #' - z0m Roughness length (m), optional; only used if `stab_correction = false` (default=0.1) #' - frac_z0m Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. #' Default is 0.1. -#' - estimate_z0m Should `z0m` be estimated from the logarithmic wind profile? If `TRUE` (the default), +#' - estimate_z0m Should `z0m` be estimated from the logarithmic wind profile? If `true` (the default), #' arguments `z0m` and `frac_z0m` are ignored. #' See [`roughness_parameters`](@ref) for details. -#' - stab_correction Should stability correction be applied? Defaults to `TRUE` -#' - stab_formulation Stability correction function used (If `stab_correction = TRUE`). -#' Either `"Dyer_1970"` or `"Businger_1971"`. +#' - stab_correction Should stability correction be applied? Defaults to `true` +#' - stab_formulation Stability correction function used (If `stab_correction = true`). +#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. #' - constants k - von-Karman constant (-) #' Kelvin - conversion degree Celsius to Kelvin #' cp - specific heat of air for constant pressure (J K-1 kg-1) @@ -244,11 +244,11 @@ end #' according to the Monin-Obhukov similarity theory). #' In this case, the wind speed at a given height z is given by: #' -#' ``u(z) = (ustar/k) * (ln((z - d) / z0m) - \psi{m``} +#' ``u(z) = (ustar/k) * (ln((z - d) / z0m) - \\psi_m`` #' #' The roughness parameters zero-plane displacement height (d) and roughness length (z0m) -#' can be approximated from [`roughness_parameters`](@ref). ``\psi{m``} is omitted -#' if `stab_correction = false` (not recommended). If `estimate_z0m = TRUE`, +#' can be approximated from [`roughness_parameters`](@ref). ``\\psi_m`` is omitted +#' if `stab_correction = false` (not recommended). If `estimate_z0m = true`, #' z0m is first estimated from the wind profile equation and then used in the equation #' above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). #' @@ -282,8 +282,8 @@ end #' #' @export function wind_profile(data,z,Tair="Tair",pressure="pressure",ustar="ustar",H="H",wind="wind", - zr,zh,d=NULL,frac_d=0.7,z0m=NULL,frac_z0m=NULL,estimate_z0m=TRUE, - stab_correction=TRUE,stab_formulation=c("Dyer_1970","Businger_1971"), + zr,zh,d=nothing,frac_d=0.7,z0m=nothing,frac_z0m=nothing,estimate_z0m=true, + stab_correction=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), constants=bigleaf_constants()) stab_formulation = match_arg(stab_formulation) @@ -291,15 +291,15 @@ function wind_profile(data,z,Tair="Tair",pressure="pressure",ustar="ustar",H="H" check_input(data,ustar) ## determine roughness parameters - if (is_null(d)) - if (is_null(frac_d)) + if (isnothing(d)) + if (isnothing(frac_d)) stop("Either 'd' or 'frac_d' must be specified") end d = frac_d * zh end - if (is_null(z0m) & !estimate_z0m) - if (is_null(frac_z0m)) + if (isnothing(z0m) & !estimate_z0m) + if (isnothing(frac_z0m)) stop("Either 'z0m' or 'frac_z0m' must be specified if 'estimate_z0m' = false") end z0m = frac_z0m * zh @@ -308,21 +308,21 @@ end if (estimate_z0m) - if (!is_null(z0m) | !is_null(frac_z0m)) - cat("Note that arguments 'z0m' and 'frac_z0m' are ignored if 'estimate_z0m' = TRUE. z0m is - calculated from the logarithmic wind_profile equation.",fill=TRUE) + if (!isnothing(z0m) | !isnothing(frac_z0m)) + cat("Note that arguments 'z0m' and 'frac_z0m' are ignored if 'estimate_z0m' = true. z0m is + calculated from the logarithmic wind_profile equation.",fill=true) end check_input(data,Tair,pressure,wind,ustar,H) - z0m = roughness_parameters(method="wind_profile",zh=zh,zr=zr,d=d,data=data, + z0m = roughness_parameters(method=Val(:wind_profile),zh=zh,zr=zr,d=d,data=data, Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, - stab_roughness=TRUE,stab_formulation=stab_formulation, + stab_roughness=true,stab_formulation=stab_formulation, constants=constants)[,"z0m"] end - if ( any(z < (d + z0m) & !is_na(d + z0m)) ) - warning("function is only valid for heights above d + z0m! Wind speed for heights below d + z0m will return 0!") + if ( any(z < (d + z0m) & !ismissing(d + z0m)) ) + @warn"function is only valid for heights above d + z0m! Wind speed for heights below d + z0m will return 0!") end ## calculate wind speeds at given heights z diff --git a/inst/fromR/unit_conversions.jl b/inst/fromR/unit_conversions.jl index 2349213..7f38a59 100755 --- a/inst/fromR/unit_conversions.jl +++ b/inst/fromR/unit_conversions.jl @@ -154,8 +154,8 @@ end function rH_to_VPD(rH,Tair,Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_1998"), constants=bigleaf_constants()) - if(any(rH > 1 & !is_na(rH))) - warning("relative humidity (rH) has to be between 0 and 1.") + if(any(rH > 1 & !ismissing(rH))) + @warn"relative humidity (rH) has to be between 0 and 1.") end esat = Esat_slope(Tair,Esat_formula,constants)[,"Esat"] @@ -172,8 +172,8 @@ function e_to_rH(e,Tair,Esat_formula=c("Sonntag_1990","Alduchov_1996","Allen_199 esat = Esat_slope(Tair,Esat_formula,constants)[,"Esat"] - if (any(e > esat + .Machine$double_eps^0.5 & !is_na(e))) - warning("Provided vapour pressure that was higher than saturation. + if (any(e > esat + .Machine$double_eps^0.5 & !ismissing(e))) + @warn"Provided vapour pressure that was higher than saturation. Returning rH=1 for those cases.") end diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index f7bfd5e..5b82f04 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -67,7 +67,7 @@ below which the quality control to be considered as acceptable quality all "LE" values whose "LE_qc" variable is larger than 1 are set to `missing`. The variable `missing_qc_as_bad` is required to decide what to do in case of missing values in the quality control variable. By default this is (conservatively) -set to `TRUE`, i.e. all entries where the qc variable is missing is set invalid. +set to `true`, i.e. all entries where the qc variable is missing is set invalid. We can filter for meteorological conditions to be in acceptable ranges. For each variable to check we supply the valid minimum and valid maxium as a two-tuple @@ -137,7 +137,7 @@ sum(.!thaf.valid) # some more invalids ``` -When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to NA. (Note that this is the default case. If we add `filtered_data_to_NA=TRUE`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. +When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to NA. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. @@ -158,13 +158,13 @@ $G_a$ and in particular $G_b$ can be calculated with varying degrees of complexi summary(aerodynamic_conductance(tha_filtered)) ``` -Note that by not providing additional arguments, the default values are taken (type ?aerodynamic_conductance to see default values of the function arguments). We also do not need most of the arguments that can be provided to the function in this case (i.e. if `Rb_model="Thom_1972"`). These are only required if we use a more complex formulation of $G_b$. +Note that by not providing additional arguments, the default values are taken (type ?aerodynamic_conductance to see default values of the function arguments). We also do not need most of the arguments that can be provided to the function in this case (i.e. if `Rb_model=Val(:Thom_1972)`). These are only required if we use a more complex formulation of $G_b$. The output of the function is another DataFrame which contains separate columns for conductances and resistances of different scalars (momentum, heat, and CO$_2$ by default). For comparison, we now calculate a second estimate of $G_a$, where the calculation of $G_b$ is more physically-based (Su et al. 2001), and which requires more input variables compared to the first version. In particular, we now need LAI, the leaf characteristic dimension ($D_l$, assumed to be 1cm here), and information on sensor and canopy height ($z_r$ and $z_h$), as well as the displacement height (assumed to be 0.7*$z_h$): ```julia -Ga_Su = aerodynamic_conductance(tha_filtered,Rb_model="Su_2001",LAI=LAI,zh=zh,d=0.7*zh, +Ga_Su = aerodynamic_conductance(tha_filtered,Rb_model=Val(:Su_2001),LAI=LAI,zh=zh,d=0.7*zh, zr=zr,Dl=Dl) summary(Ga_Su) tha_filtered = cbind(tha_filtered,Ga_Su) @@ -185,12 +185,12 @@ where $T_a$ is air temperature, $H$ is the sensible heat flux ($\text{W m}^{-2}$ In `Bigleaf.jl`, the following function calculates conditions at the big-leaf surface: ```julia -surf = surface_conditions(tha_filtered,calc_surface_CO2=TRUE) +surf = surface_conditions(tha_filtered,calc_surface_CO2=true) summary(surf) tha_filtered = cbind(tha_filtered,surf) ``` -By default, the function calculates surface temperature and several humidity measures, including VPD and relative humidity. If we set `calc_surface_CO2=TRUE`, the CO$_2$ concentration at the surface is calculated additionally. Useful to know is that the expression "surface" depends on what kind of aerodynamic conductance we provide. If $G_a = G_{ah}$, we derive the conditions at the notional canopy surface (or the "big-leaf" surface). If $G_a = G_{am}$, we derive conditions in the intercanopy airspace (because $G_a$ does not account for the leaf boundary layer). +By default, the function calculates surface temperature and several humidity measures, including VPD and relative humidity. If we set `calc_surface_CO2=true`, the CO$_2$ concentration at the surface is calculated additionally. Useful to know is that the expression "surface" depends on what kind of aerodynamic conductance we provide. If $G_a = G_{ah}$, we derive the conditions at the notional canopy surface (or the "big-leaf" surface). If $G_a = G_{am}$, we derive conditions in the intercanopy airspace (because $G_a$ does not account for the leaf boundary layer). We can compare the surface and air temperature: @@ -239,18 +239,18 @@ With both $G_s$ and $G_a$ available, we can estimate the stomatal slope paramete ```julia ## stomatal slope from the USO model (Medlyn et al. 2011) -g1_USO = stomatal_slope(tha_filtered,model="USO",g0=0,robust_nls=TRUE) +g1_USO = stomatal_slope(tha_filtered,model="USO",g0=0,robust_nls=true) g1_USO ``` In this case, we have estimated $g_1$ from the USO (optimal stomatal optimization) model as described in Medlyn et al. 2011. The output is a model object that prints the model formulat that is used to estimate $g_1$, the estimated parameter value(s), as well as the weighted residual sum-of-squares. Further information on this model object can be obtained using the `summary` function. -In this case we have fixed the model intercept $g_0$ to 0 (this could also be any other value). We can also try to estimate $g_1$ and $g_0$ simultaneously (if we add `fitg0=TRUE` to the function call above), but note that the two parameters are usually correlated, and that the values of $g_0$ are not straightforward to interpret (especially at ecosystem level). The option `robust_nls=TRUE` specifies that $g_1$ is determined by a robust non-linear regression routine (from the `robustbase` package). We recommend to use this option since otherwise the parameter estimates are sensitive to outliers in $G_s$, which often occur even in filtered EC datasets. +In this case we have fixed the model intercept $g_0$ to 0 (this could also be any other value). We can also try to estimate $g_1$ and $g_0$ simultaneously (if we add `fitg0=true` to the function call above), but note that the two parameters are usually correlated, and that the values of $g_0$ are not straightforward to interpret (especially at ecosystem level). The option `robust_nls=true` specifies that $g_1$ is determined by a robust non-linear regression routine (from the `robustbase` package). We recommend to use this option since otherwise the parameter estimates are sensitive to outliers in $G_s$, which often occur even in filtered EC datasets. By default, the model takes VPD and atmospheric CO$_2$ concentration as measured at the tower as input. We can also calculate $g_1$ by taking the surface conditions, which are probably more relevant for plant physiological processes than those measured a certain distance above the canopy: ```julia ## stomatal slope from the USO model (Medlyn et al. 2011) stomatal_slope(tha_filtered,Tair="Tsurf",VPD="VPD_surf",Ca="Ca_surf",model="USO", - g0=0,robust_nls=TRUE) + g0=0,robust_nls=true) ``` which in this case, does not change our $g_1$ value significantly. @@ -258,11 +258,11 @@ We can also calculate $g_1$ using two different models. One is the long-standing ```julia ## Ball&Berry slope -stomatal_slope(tha_filtered,model="Ball&Berry",g0=0,robust_nls=TRUE) +stomatal_slope(tha_filtered,model="Ball&Berry",g0=0,robust_nls=true) ``` ```julia ## Leuning slope -stomatal_slope(tha_filtered,model="Leuning",g0=0,fitD0=TRUE,robust_nls=TRUE) +stomatal_slope(tha_filtered,model="Leuning",g0=0,fitD0=true,robust_nls=true) ``` Note that the absolute value of the $g_1$ parameter depends on the model. In the Leuning model, we have a third parameter $D_0$ that can again either be estimated (as in the example above) or fixed to a pre-defined value (by default 1.5 kPa). $D_0$ describes the stomatal sensitivity to VPD (higher values correspond to a lower stomatal sensitivity to VPD - note however that $g_1$ and $D_0$ are strongly correlated, which makes an independent estimates of $D_0$ difficult to achieve). @@ -288,16 +288,16 @@ In `Bigleaf.jl`, a wind profile can be calculated assuming an exponential increa ```julia wind_heights = seq(22,60,2) wp = wind_profile(tha_filtered,heights=wind_heights,zh=zh,zr=zr) -wp_means = colMeans(wp,na_rm=TRUE) -wp_sd = apply(wp,2,sd,na_rm=TRUE) +wp_means = colMeans(wp,na_rm=true) +wp_sd = apply(wp,2,sd,na_rm=true) plot(wind_heights ~ wp_means,xlab=expression("wind speed (m s"^{-1}*")"),ylab="height (m)", las=1,mgp=c(2.2,0.5,0),tcl=0.2,xlim=c(0,5)) arrows(wp_means-wp_sd,wind_heights,wp_means+wp_sd,wind_heights,angle=90, length=0.02,code=3) -points(x=mean(tha_filtered[,"wind"],na_rm=TRUE),y=zr,col="blue",pch=16) -arrows(mean(tha_filtered[,"wind"],na_rm=TRUE)-sd(tha_filtered[,"wind"],na_rm=TRUE), - zr,mean(tha_filtered[,"wind"],na_rm=TRUE)+sd(tha_filtered[,"wind"],na_rm=TRUE), +points(x=mean(tha_filtered[,"wind"],na_rm=true),y=zr,col="blue",pch=16) +arrows(mean(tha_filtered[,"wind"],na_rm=true)-sd(tha_filtered[,"wind"],na_rm=true), + zr,mean(tha_filtered[,"wind"],na_rm=true)+sd(tha_filtered[,"wind"],na_rm=true), zr,angle=90,length=0.02,code=3,col="blue") ``` Here, the points denote the mean wind speed and the bars denote the standard deviation. The blue point/bar represent the values that were measured at 42m. In this case we see that the wind speed as "back-calculated" from the wind profile agrees well with the actual measurements. @@ -421,8 +421,8 @@ invisible(capture_output(PET = potential_ET(Tair=25,pressure=100,Rn=200))) The `Bigleaf.jl` package contains a single list of constants (see `?Bigleaf_constants`). Whenever one or more constants are used in a function, this list is provided as a default argument, so the user does usually not need to interact with this list. However, should you wish to change a certain constant for the calculations (which could make sense in some cases, e.g. using a different value for the von-Karman constant (k)), individual constants can be changed within a function call. As an example, let's call a function with the `Bigleaf.jl` default value of k=0.41, and the alternative, often used value of k=0.4: ```julia -summary(aerodynamic_conductance(tha_filtered,wind_profile=TRUE,zr=zr,d=0.7*zh,z0m=2.65)[,"Ga_h"]) -summary(aerodynamic_conductance(tha_filtered,wind_profile=TRUE,zr=zr,d=0.7*zh,z0m=2.65, +summary(aerodynamic_conductance(tha_filtered,wind_profile=true,zr=zr,d=0.7*zh,z0m=2.65)[,"Ga_h"]) +summary(aerodynamic_conductance(tha_filtered,wind_profile=true,zr=zr,d=0.7*zh,z0m=2.65, constants=Bigleaf_constants(k=0.4))[,"Ga_h"]) ``` @@ -519,8 +519,8 @@ In the example above we create a parameter space that we use for the subsequent unc_all = mapply(aerodynamic_conductance,Dl=Dl_sample,z0m=z0m_sample,LAI=LAI_sample, MoreArgs=list(data=tha_filtered,zr=42,zh=26.5,d=0.7*26.5, N=2,stab_correction=T, - stab_formulation="Dyer_1970", - Rb_model="Su_2001") + stab_formulation=Val(:Dyer_1970), + Rb_model=Val(:Su_2001)) ) # select "Ga_h" output variable and convert to matrix @@ -550,7 +550,7 @@ ind = c(1:48) # first day plot(Ga_mean[ind],type="l",lwd=2,xlab="timestep",ylab=expression("G"["ah"]~"(m s"^{-1}*")"), las=1,mgp=c(2.2,0.5,0),tcl=-0.2,ylim=c(0.045,0.14)) -ok = which(!is_na(Ga_mean[ind])) +ok = which(!ismissing(Ga_mean[ind])) polygon(c(ok,rev(ok)),c(Ga_high[ind][ok],rev(Ga_low[ind][ok])), col="grey70",border=NA) points(Ga_mean[ind],type="l",lwd=2) @@ -559,7 +559,7 @@ points(Ga_mean[ind],type="l",lwd=2) plot(Gs_mean[ind],type="l",lwd=2,xlab="timestep",tcl=-0.2, ylab=expression("G"["sw"]~"(mol m"^{-2}~"s"^{-1}*")"),las=1,mgp=c(2.2,0.5,0)) -ok = which(!is_na(Gs_mean[ind])) +ok = which(!ismissing(Gs_mean[ind])) polygon(c(ok,rev(ok)),c(Gs_high[ind][ok],rev(Gs_low[ind][ok])), col="grey70",border=NA) points(Gs_mean[ind],type="l",lwd=2) diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 83c544d..121e93d 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -29,6 +29,7 @@ export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason, setinvalid_afterprecip! export decoupling, surface_conductance, aerodynamic_conductance +export compute_Gb, compute_Gb!, add_Gb, Gb_Thom include("util.jl") include("bigleaf_constants.jl") @@ -38,5 +39,6 @@ include("sun_position.jl") include("potential_radiation.jl") include("evapotranspiration.jl") include("filter_data.jl") +include("boundary_layer_conductance.jl") end diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl new file mode 100755 index 0000000..ae622e8 --- /dev/null +++ b/src/aerodynamic_conductance.jl @@ -0,0 +1,299 @@ +""" +Aerodynamic Conductance + +Bulk aerodynamic conductance, including options for the boundary layer conductance +formulation and stability correction functions. + +# Arguments +- `data` : Data_frame or matrix containing all required variables +- `Tair` : Air temperature (deg C) +- `pressure` : Atmospheric pressure (kPa) +- `wind` : Wind speed (m s-1) +- `ustar` : Friction velocity (m s-1) +- `H` : Sensible heat flux (W m-2) +- `zr` : Instrument (reference) height (m) +- `zh` : Canopy height (m) +- `d` : Zero-plane displacement height (m) +- `z0m` : Roughness length for momentum (m), optional; if not provided, + it is estimated from `roughness_parameters` + TODO : (method=Val(:wind_profile)). Only used if `wind_profile = true` and/or + `Rb_model` = `Val(:Su_2001)` or + : `Val(:Choudhury_1988)`. +- `Dl` : Characteristic leaf dimension (m) (if `Rb_model` = `Val(:Su_2001)`) + : or leaf width (if `Rb_model` = `Val(:Choudhury_1988)`); + ignored otherwise. +- `N` : Number of leaf sides participating in heat exchange (1 or 2); + only used if `Rb_model = Val(:Su_2001)`. + : Defaults to 2. +- `fc` : Fractional vegetation cover (-); only used if `Rb_model = Val(:Su_2001)`. + See Details. +- `LAI` : One-sided leaf area index (m2 m-2); only used if + `Rb_model` = `Val(:Choudhury_1988)` or `Val(:Su_2001)`. +- `Cd` : Foliage drag coefficient (-); only used if `Rb_model = Val(:Su_2001)`. +- `hs` : Roughness length of bare soil (m); only used if `Rb_model = Val(:Su_2001)`. +- `wind_profile` : Should Ga for momentum be calculated based on the logarithmic wind + profile equation? Defaults to `false`. +- `stab_correction` : Should stability correction be applied? Defaults to `true`. + Ignored if `wind_profile = false`. +- `stab_formulation` : Stability correction function. Either `Val(:Dyer_1970)` (default) or + : `Val(:Businger_1971)`. Ignored if `wind_profile = false` or if + `stab_correction = false`. +- `Rb_model` : Boundary layer resistance formulation. + One of `Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)`. +- `kB_h` : kB-1 value for heat transfer; only used if `Rb_model = Val(:constant_kB1)` +- `Sc` : Optional: Schmidt number of additional quantities to be calculated +- `Sc_name` : Optional: Name of the additonal quantities, has to be of same length than + `Sc_name` +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `k` - von Karman constant + - `cp` - specific heat of air for constant pressure (J K-1 kg-1) + - `Kelvin` - conversion degree Celsius to Kelvin + - `g` - gravitational acceleration (m s-2) + - `pressure0` - reference atmospheric pressure at sea level (Pa) + - `Tair0` - reference air temperature (K) + - `Sc_CO2` - Schmidt number for CO2 + - `Pr` - Prandtl number (if `Sc` is provided) + +# Details + +Aerodynamic conductance for heat (Ga_h) is calculated as: + +``Ga_h = 1 / (Ra_m + Rb_h)`` + +where ``Ra_m`` is the aerodynamic resistance for momentum and ``Rb`` the (quasi-laminar) +canopy boundary layer resistance ('excess resistance'). + +The aerodynamic resistance for momentum ``Ra_m`` is given by: + +``Ra_m = u/ustar^2`` + +Note that this formulation accounts for changes in atmospheric stability, and does not +require an additional stability correction function. + +An alternative method to calculate ``Ra_m`` is provided +(calculated if `wind_profile = true`): + +``Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)`` + +If the roughness parameters z0m and d are unknown, they can be estimated using +[`roughness_parameters`](@ref). The argument `stab_formulation` determines the stability +correction function used to account for the effect of atmospheric stability on Ra_m +(Ra_m is lower for unstable and higher for stable stratification). Stratification is based +on a stability parameter zeta (z-d/L), where z = reference height, d the zero-plane +displacement height, and L the Monin-Obukhov length, calculated with +[`Monin_Obukhov_length`](@ref) +The stability correction function is chosen by the argument `stab_formulation`. +Options are `Val(:Dyer_1970)` and `Val(:Businger_1971)`. + +The model used to determine the canopy boundary layer resistance for heat (``Rb_h``) is +specified by the argument `Rb_model`. The following options are implemented: +`Val(:Thom_1972)` is an empirical formulation based on the +friction velocity (ustar) (Thom 1972): + +``Rb_h = 6.2ustar^-0.667`` + +The model by Choudhury & Monteith 1988 (`Rb_model = Val(:Choudhury_1988)`), +calculates ``Rb_h`` based on leaf width, LAI and ustar (Note that function argument `Dl` +represents leaf width (w) and not characteristic leaf dimension (Dl) +if `Rb_model` = `Val(:Choudhury_1988)`): + +``Gb_h = LAI((0.02/\\alpha)*\\sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` + +where ``\\alpha`` is a canopy attenuation coefficient modeled in dependence on LAI, +u(zh) is wind speed at canopy height (calculated from [`wind_profile`](@ref)), +and w is leaf width (m). See [`Gb_Choudhury`](@ref) for further details. + +The option `Rb_model = Val(:Su_2001)` calculates ``Rb_h`` based on the physically-based +Rb model by Su et al. 2001, a simplification of the model developed by Massman 1999: + +``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` + +where Cd is a foliage drag coefficient (defaults to 0.2), fc is fractional +vegetation cover, Bs-1 is the inverse Stanton number for bare soil surface, +and Ct is a heat transfer coefficient. See [`Gb_Su`](@ref) for +details on the model. + +The models calculate the parameter kB-1, which is related to ``Rb_h``: + +``kB-1 = Rb_h * (k * ustar)`` + +Rb (and Gb) for water vapor and heat are assumed to be equal in this package. +Gb for other quantities x is calculated as (Hicks et al. 1987): + +``Gb_x = Gb / (Sc_x / Pr)^0.67`` + +where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Value +a dataframe with the following columns: +- `Ga_m`: Aerodynamic conductance for momentum transfer (m s-1) +- `Ra_m`: Aerodynamic resistance for momentum transfer (s m-1) +- `Ga_h`: Aerodynamic conductance for heat transfer (m s-1) +- `Ra_h`: Aerodynamic resistance for heat transfer (s m-1) +- `Gb_h`: Canopy boundary layer conductance for heat transfer (m s-1) +- `Rb_h`: Canopy boundary layer resistance for heat transfer (s m-1) +- `kB_h`: kB-1 parameter for heat transfer +- `zeta`: Stability parameter 'zeta' (NA if `wind_profile = false`) +- `psi_h`: Integrated stability correction function (NA if `wind_profile = false`) +- `Ra_CO2`: Aerodynamic resistance for CO2 transfer (s m-1) +- `Ga_CO2`: Aerodynamic conductance for CO2 transfer (m s-1) +- `Gb_CO2`: Canopy boundary layer conductance for CO2 transfer (m s-1) +- `Ga_Sc_name`: Aerodynamic conductance for `Sc_name` (m s-1). Only added if `Sc_name` and + the respective `Sc` are provided. +- `Gb_Sc_name`: Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` + and the respective `Sc` are provided. + +# Note +The roughness length for water and heat (z0h) is not returned by this function, but +it can be calculated from the following relationship (e.g. Verma 1989): + +``kB-1 = ln(z0m/z0h)`` + +it follows: + +``z0h = z0m / exp(kB-1)`` + +`kB-1` is an output of this function. + +Input variables such as LAI, Dl, or zh can be either constants, or +vary with time (i.e. vectors of the same length as `data`). + +Note that boundary layer conductance to water vapor transfer (Gb_w) is often +assumed to equal Gb_h. This assumption is also made in this R package, for +example in the function [`surface_conductance`](@ref). + +If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +from the function `roughness_parameters` within `wind_profile` if `wind_profile = true` +and/or `Rb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` +function estimates a single `z0m` value for the entire time period! If a varying `z0m` value +(e.g. across seasons or years) is required, `z0m` should be provided as input argument. + + +# References +- Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. + In: Estimation of areal evapotranspiration, IAHS Pub, 177, 13-20. +- Verhoef, A., De Bruin, H., Van Den Hurk, B., 1997: Some practical notes on the parameter kB-1 + for sparse vegetation. Journal of Applied Meteorology, 36, 560-572. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. +- Monteith, J_L., Unsworth, M_H., 2008: Principles of environmental physics. + Third Edition. Elsevier Academic Press, Burlington, USA. + +# See also +[`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref) for calculations of Rb / Gb only + +```@example; output = false +# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) + +# # simple calculation of Ga +# aerodynamic_conductance(df,Rb_model=Val(:Thom_1972)) + +# # calculation of Ga using a model derived from the logarithmic wind profile +# aerodynamic_conductance(df,Rb_model=Val(:Thom_1972),zr=40,zh=25,d=17.5,z0m=2,wind_profile=true) + +# # simple calculation of Ga, but a physically based canopy boundary layer model +# aerodynamic_conductance(df,Rb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) +``` +""" +function aerodynamic_conductance(df; + zr,zh,d,z0m=nothing,Dl,N=2,fc=nothing,LAI,Cd=0.2,hs=0.01,wind_profile=false, + stab_correction=true,stab_formulation=Val(:Dyer_1970), + Rb_model=c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)), + kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants() + ) + + ## calculate canopy boundary layer conductance (Gb) + if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] + if Rb_model == Val(:Thom_1972) + Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) + elseif Rb_model == Val(:Choudhury_1988) + Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, + H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, + stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + constants=constants) + elseif Rb_model == Val(:Su_2001) + Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, + H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, + stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + constants=constants) + end + kB_h = Gb_mod.kB_h + Rb_h = Gb_mod.Rb_h + Gb_h = Gb_mod.Gb_h + # TODO + # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) + # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] + elseif Rb_model == Val(:constant_kB1) + isnothing(kB_h) && error( + "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") + Rb_h = kB_h/(constants[:k] * ustar) + Gb_h = 1/Rb_h + if (!isnothing(Sc) || !isnothing(Sc_name)) + length(Sc) != length(Sc_name) && error( + "arguments 'Sc' and 'Sc_name' must have the same length") + !is_numeric(Sc) && error("argument 'Sc' must be numeric") + Sc = SA[constants[:Sc_CO2], Sc] + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) + colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + end + end + +# ## calculate aerodynamic conductance for momentum (Ga_m) +# if (wind_profile) +# if (isnothing(z0m) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) +# stop("z0m must be provided if wind_profile=true!") +# elseif (isnothing(z0m) & Rb_model in c(Val(:Choudhury_1988),Val(:Su_2001))) +# # z0m estimated as in Choudhury_1988 or Su_2001 +# z0m = roughness_parameters(method=Val(:wind_profile),zh=zh,zr=zr,d=d,data=df, +# Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, +# stab_roughness=true,stab_formulation=stab_formulation, +# constants=constants)[,"z0m"] +# end + +# if (stab_correction) + +# zeta = stability_parameter(data=df,Tair=Tair,pressure=pressure,ustar=ustar, +# H=H,zr=zr,d=d,constants=constants) + +# if (stab_formulation in c(Val(:Dyer_1970),Val(:Businger_1971))) + +# psi_h = stability_correction(zeta,formulation=stab_formulation)[,"psi_h"] + +# else +# stop("'stab_formulation' has to be one of 'Dyer_1970' or 'Businger_1971'. +# Choose 'stab_correction = false' if no stability correction should be applied.") +# end + +# Ra_m = pmax((log((zr - d)/z0m) - psi_h),0) / (constants[:k]*ustar) + +# else + +# Ra_m = pmax((log((zr - d)/z0m)),0) / (constants[:k]*ustar) +# zeta = psi_h = rep(NA_integer_,length=length(Ra_m)) + +# end + +# else + +# if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) +# @warn"Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") +# end + +# Ra_m = wind / ustar^2 +# zeta = psi_h = rep(NA_integer_,length=length(Ra_m)) + +# end + +# Ga_m = 1/Ra_m +# Ra_h = Ra_m + Rb_h +# Ga_h = 1/Ra_h +# Ga_x = 1/(Ra_m + 1/Gb_x) +# Ra_CO2 = 1/Ga_x[,1] +# colnames(Ga_x) = paste0("Ga_",c("CO2",Sc_name)) + +# Gab_x = cbind(Ga_x,Gb_x) +# Gab_x = Gab_x[rep(c(1,ncol(Gab_x)-(ncol(Gab_x)/2-1)),ncol(Gab_x)/2) + sort(rep(0:(ncol(Gab_x)/2-1),2))] # reorder columns + +# return(DataFrame(Ga_m,Ra_m,Ga_h,Ra_h,Gb_h,Rb_h,kB_h,zeta,psi_h,Ra_CO2,Gab_x)) +end \ No newline at end of file diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl new file mode 100755 index 0000000..36333c8 --- /dev/null +++ b/src/boundary_layer_conductance.jl @@ -0,0 +1,452 @@ +""" +Boundary Layer Conductance according to Thom 1972 + +An empirical formulation for the canopy boundary layer conductance +for heat transfer based on a simple ustar (friction velocity) dependency. + +# Arguments +- `ustar` : Friction velocity (m s-1) +optional +- `Sc` : `Pair{Symbol,Number}` Output name and Schmidt number of + additional quantities to be calculated +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `k` - von-Karman constant + - `Sc_CO2` - Schmidt number for CO2 + - `Pr` - Prandtl number (if `Sc` is provided) + +# Details +The empirical equation for Rb suggested by Thom 1972 is: + +``Rb = 6.2 ustar^{-0.67}`` + +Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. +Gb for other quantities x is calculated as (Hicks et al. 1987): + +``Gb_x = Gb / (Sc_x / Pr)^{0.67}`` + +where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Value +a NameTuple or DataFrame with the following columns: +- `Gb_h`: Boundary layer conductance for heat transfer (m s-1) +- `Rb_h`: Boundary layer resistance for heat transfer (s m-1) +- `kB_h`: kB-1 parameter for heat transfer +- `Gb_CO2`: Boundary layer conductance for CO2 (m s-1). +- ...: Boundary layer conductance for keys provided with argument `Sc` (m s-1). + +# References +- Thom, A., 1972: Momentum, mass and heat exchange of vegetation. + Quarterly Journal of the Royal Meteorological Society 98, 124-134. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. + +# See also +[`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) + +```@example; output = false +# Gb_Thom(seq(0.1,1.4,0.1)) + +# ## calculate Gb for SO2 as well +# Gb_Thom(seq(0.1,1.4,0.1),Sc=1.25,Sc_name="SO2") +``` +""" +function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) + compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols +end +function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) + compute_Gb_!(df, approach, SA[zh,zr,d]; kwargs...) # inputcols +end +function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) + compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols +end +function compute_Gb_!(df::AbstractDataFrame, approach, inputcols; + Sc::AbstractVector=SA[], kwargs... + ) + fGb(args...) = compute_Gb(approach, args...; kwargs...) + transform!(df, :ustar => ByRow(fGb) => SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]) + if length(Sc) != 0 + Sc_names = map(x -> x.first, Sc) + transform!(df, :Gb_h => ByRow(Gb_h -> add_Gb(Gb_h, Sc...)) => Sc_names) + end +end +compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) +compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) +compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) + +""" +Boundary Layer Conductance according to Thom 1972 + +An empirical formulation for the canopy boundary layer conductance +for heat transfer based on a simple ustar (friction velocity) dependency. + +# Arguments +- `ustar` : Friction velocity (m s-1) +optional +- `Sc` : `Pair{Symbol,Number}` Output name and Schmidt number of + additional quantities to be calculated +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `k` - von-Karman constant + - `Sc_CO2` - Schmidt number for CO2 + - `Pr` - Prandtl number (if `Sc` is provided) + +# Details +The empirical equation for Rb suggested by Thom 1972 is: + +``Rb = 6.2 ustar^{-0.67}`` + +Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. +Gb for other quantities x is calculated as (Hicks et al. 1987): + +``Gb_x = Gb / (Sc_x / Pr)^{0.67}`` + +where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Value +a NameTuple or DataFrame with the following columns: +- `Gb_h`: Boundary layer conductance for heat transfer (m s-1) +- `Rb_h`: Boundary layer resistance for heat transfer (s m-1) +- `kB_h`: kB-1 parameter for heat transfer +- `Gb_CO2`: Boundary layer conductance for CO2 (m s-1). +- ...: Boundary layer conductance for keys provided with argument `Sc` (m s-1). + +# References +- Thom, A., 1972: Momentum, mass and heat exchange of vegetation. + Quarterly Journal of the Royal Meteorological Society 98, 124-134. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. + +# See also +[`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) + +```@example; output = false +# Gb_Thom(seq(0.1,1.4,0.1)) + +# ## calculate Gb for SO2 as well +# Gb_Thom(seq(0.1,1.4,0.1),Sc=1.25,Sc_name="SO2") +``` +""" +function Gb_Thom(ustar::Union{Missing,Number}; constants=bigleaf_constants()) + Rb_h = 6.2*ustar^-0.667 + Gb_h = 1/Rb_h + kB_h = Rb_h*constants[:k]*ustar + Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 + (;Rb_h, Gb_h, kB_h, Gb_CO2) +end +# function Gb_Thom(ustar::Missing; constants=bigleaf_constants()) +# (Rb_h = missing, Gb_h = missing, kB_h = missing, Gb_CO2 = missing) +# end + + + + + +""" + add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants=bigleaf_constants()) + +compute additional boundary layer conductance quantities for given Schmidt-numbers + +# Arguments +- `Gb_h` : Boundary layer conductance for heat transfer (m s-1) +- `Sc` : several `Pair{Symbol,Number}` Output name and Schmidt number of + additional conductances to be calculated +optional +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `Pr` - Prandtl number + +# Value +a NameTuple with keys as in `Sc` and corresponding conductances (m s-1) +""" +function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; + constants=bigleaf_constants()) where N + Scn = ntuple(i -> Sc[i].first, N) + Scv = ntuple(i -> Sc[i].second, N) + Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 + Gbx = NamedTuple{Scn}(Gbxv) +end + + +""" +Boundary Layer Conductance according to Choudhury & Monteith 1988 + +A formulation for the canopy boundary layer conductance +for heat transfer according to Choudhury & Monteith 1988. + +# Arguments +- `df` : Data_frame or matrix containing all required variables +- `Tair` : Air temperature (degC) +- `pressure` : Atmospheric pressure (kPa) +- `wind` : Wind speed at sensor height (m s-1) +- `ustar` : Friction velocity (m s-1) +- `H` : Sensible heat flux (W m-2) +- `leafwidth` : Leaf width (m) +- `LAI` : One-sided leaf area index +- `zh` : Canopy height (m) +- `zr` : Instrument (reference) height (m) +- `d` : Zero-plane displacement height (-), can be calculated using `roughness_parameters` +- `z0m` : Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` + within `wind_profile` +- `stab_formulation` : Stability correction function used (If `stab_correction = true`). + Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +- `Sc` : Optional: Schmidt number of additional quantities to be calculated +- `Sc_name` : Optional: Name of the additonal quantities, has to be of same length than + `Sc_name` +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + _ k - von-Karman constant + _ Sc_CO2 - Schmidt number for CO2 + _ Pr - Prandtl number (if `Sc` is provided) + +# Value + A data frame with the following columns: + - Gb_h: Boundary layer conductance for heat transfer (m s-1) + - Rb_h: Boundary layer resistance for heat transfer (s m-1) + - kB_h: kB-1 parameter for heat transfer + +# Details +Boundary layer conductance according to Choudhury & Monteith 1988 is +given by: + +``Gb_h = LAI((2a/\\alpha)*\\sqrt(u(h)/w)*(1-exp(-\\alpha/2)))`` + +where u(zh) is the wind speed at the canopy surface, approximated from +measured wind speed at sensor height zr and a wind extinction coefficient ``\\alpha``: + +``u(zh) = u(zr) / (exp(\\alpha(zr/zh -1)))``. + +``\\alpha`` is modeled as an empirical relation to LAI (McNaughton & van den Hurk 1995): + +``\\alpha = 4.39 - 3.97*exp(-0.258*LAI)`` + +Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. +Gb for other quantities x is calculated as (Hicks et al. 1987): +``Gb_x = Gb / (Sc_x / Pr)^0.67`` +where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Note +If the roughness length for momentum (`z0m`) is not provided as input, it is estimated +from the function `roughness_parameters` within `wind_profile`. This function +estimates a single `z0m` value for the entire time period! If a varying `z0m` value +(e.g. across seasons or years) is required, `z0m` should be provided as input argument. + +# References +- Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat + budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. +- McNaughton, K. G., Van den Hurk, B_J_J_M., 1995: A 'Lagrangian' revision of + the resistors in the two-layer model for calculating the energy budget of a + plant canopy. Boundary-Layer Meteorology 74, 261-288. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. + +# See also +[`Gb_Thom`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) + +```@example; output = false +# ## bulk canopy boundary layer resistance for a closed canopy (LAI=5) +# ## with large leaves (leafwdith=0.1) +# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) +# Gb_Choudhury(data=df,leafwidth=0.1,LAI=5,zh=25,d=17.5,zr=40) + +# ## same conditions, but smaller leaves (leafwidth=0.01) +# Gb_Choudhury(data=df,leafwidth=0.01,LAI=5,zh=25,d=17.5,zr=40) +``` +""" +function Gb_Choudhury(leafwidth,LAI,zh,zr,d; + z0m=nothing, + stab_formulation=Val(:Dyer_1970), + constants=bigleaf_constants() + ) + error("Not implemented yet. Need windprofile") + # alpha = 4.39 - 3.97*exp(-0.258*LAI) + + # if isnothing(z0m) + # estimate_z0m = true + # z0m = nothing + # else + # estimate_z0m = false + # end +# wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, +# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, +# stab_correction=true,stab_formulation=stab_formulation) + +# ## avoid zero windspeed +# wind_zh = pmax(0.01,wind_zh) + +# if (!isnothing(Sc) | !isnothing(Sc_name)) +# if (length(Sc) != length(Sc_name)) +# stop("arguments 'Sc' and 'Sc_name' must have the same length") +# end +# if (!is_numeric(Sc)) +# stop("argument 'Sc' must be numeric") +# end +# end + +# Gb_h = LAI*((0.02/alpha)*sqrt(wind_zh/leafwidth)*(1-exp(-alpha/2))) +# Rb_h = 1/Gb_h +# kB_h = Rb_h*constants[:k]*ustar + +# Sc = c(constants[:Sc_CO2],Sc) +# Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) +# colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + + +# return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) +end + + + + + +""" +Boundary Layer Conductance according to Su et al. 2001 + +A physically based formulation for the canopy boundary layer conductance +to heat transfer according to Su et al. 2001. + +# Arguments +- data Data_frame or matrix containing all required variables +- Tair Air temperature (degC) +- pressure Atmospheric pressure (kPa) +- ustar Friction velocity (m s-1) +- wind Wind speed (m s-1) +- H Sensible heat flux (W m-2) +- zh Canopy height (m) +- zr Reference height (m) +- d Zero-plane displacement height (-), can be calculated using `roughness_parameters` +- z0m Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` + within `wind_profile` +- Dl Leaf characteristic dimension (m) +- fc Fractional vegetation cover [0-1] (if not provided, calculated from LAI) +- LAI One-sided leaf area index (-) +- N Number of leaf sides participating in heat exchange (defaults to 2) +- Cd Foliage drag coefficient (-) +- hs Roughness height of the soil (m) +- stab_formulation Stability correction function used (If `stab_correction = true`). + Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +- Sc Optional: Schmidt number of additional quantities to be calculated +- Sc_name Optional: Name of the additional quantities, has to be of same length than + `Sc_name` +- constants Kelvin - conversion degree Celsius to Kelvin + pressure0 - reference atmospheric pressure at sea level (Pa) + Tair0 - reference air temperature (K) + Sc_CO2 - Schmidt number for CO2 + Pr - Prandtl number (if `Sc` is provided) + +# Value + A DataFrame with the following columns: + - Gb_h: Boundary layer conductance for heat transfer (m s-1) + - Rb_h: Boundary layer resistance for heat transfer (s m-1) + - kB_h: kB-1 parameter for heat transfer + +# Details + The formulation is based on the kB-1 model developed by Massman 1999. + Su et al. 2001 derived the following approximation: + + ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` + + If fc (fractional vegetation cover) is missing, it is estimated from LAI: + + ``fc = 1 - exp(-LAI/2)`` + + The wind speed at the top of the canopy is calculated using function + [`wind_profile`](@ref). + + Ct is the heat transfer coefficient of the leaf (Massman 1999): + + ``Ct = Pr^-2/3 Reh^-1/2 N`` + + where Pr is the Prandtl number (set to 0.71), and Reh is the Reynolds number for leaves: + + ``Reh = Dl wind(zh) / v`` + + kBs-1, the kB-1 value for bare soil surface, is calculated according + to Su et al. 2001: + + ``kBs^-1 = 2.46(Re)^0.25 - ln(7.4)`` + + Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. + Gb for other quantities x is calculated as (Hicks et al. 1987): + + ``Gb_x = Gb / (Sc_x / Pr)^0.67`` + + where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Note +If the roughness length for momentum (`z0m`) is not provided as input, it is estimated + from the function `roughness_parameters` within `wind_profile`. This function + estimates a single `z0m` value for the entire time period! If a varying `z0m` value + (e.g. across seasons or years) is required, `z0m` should be provided as input argument. + + +# References +- Su, Z., Schmugge, T., Kustas, W. & Massman, W., 2001: An evaluation of + two models for estimation of the roughness height for heat transfer between + the land surface and the atmosphere. Journal of Applied Meteorology 40, 1933-1951. +- Massman, W., 1999: A model study of kB H- 1 for vegetated surfaces using + localized near-field' Lagrangian theory. Journal of Hydrology 223, 27-43. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. + +# See also +[`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`aerodynamic_conductance`](@ref) + +```@example; output = false +# # Canopy boundary layer resistance (and kB-1 parameter) for a set of meteorological conditions, +# # a leaf characteristic dimension of 1cm, and an LAI of 5 +# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) +# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.01,LAI=5) + +# # the same meteorological conditions, but larger leaves +# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.1,LAI=5) + +# # same conditions, large leaves, and sparse canopy cover (LAI = 1.5) +# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.1,LAI=1.5) +``` +""" +function Gb_Su(df,zh,zr,d; + z0m=nothing,Dl,fc=nothing,LAI=nothing,N=2,Cd=0.2,hs=0.01, + stab_formulation=Val(:Dyer_1970), + ) + if isnothing(fc) + isnothing(LAI) && error("one of 'fc' or 'LAI' must be provided") + fc = (1-exp(-LAI/2)) + end + error("not yet implemented, need windo profile") +# if (isnothing(z0m)) +# estimate_z0m = true +# z0m = nothing +# else +# estimate_z0m = false +# end + +# wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, +# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, +# stab_correction=true,stab_formulation=stab_formulation) + +# v = kinematic_viscosity(Tair,pressure,constants) +# Re = Reynolds_Number(Tair,pressure,ustar,hs,constants) +# kBs = 2.46 * (Re)^0.25 - log(7.4) +# Reh = Dl * wind_zh / v +# Ct = 1*constants[:Pr]^-0.6667*Reh^-0.5*N + +# kB_h = (constants[:k]*Cd)/(4*Ct*ustar/wind_zh)*fc^2 + kBs*(1 - fc)^2 +# Rb_h = kB_h/(constants[:k]*ustar) +# Gb_h = 1/Rb_h + +# if (!isnothing(Sc) | !isnothing(Sc_name)) +# if (length(Sc) != length(Sc_name)) +# stop("arguments 'Sc' and 'Sc_name' must have the same length") +# end +# if (!is_numeric(Sc)) +# stop("argument 'Sc' must be numeric") +# end +# end + +# Sc = c(constants[:Sc_CO2],Sc) +# Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) +# colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + +# return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) +end diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl new file mode 100755 index 0000000..fbc4eab --- /dev/null +++ b/src/surface_roughness.jl @@ -0,0 +1,350 @@ +#' Roughness Reynolds Number +#' +#' calculates the Roughness Reynolds Number. +#' +#' - Tair Air temperature (deg C) +#' - pressure Atmospheric pressure (kPa) +#' - ustar Friction velocity (m s-1) +#' - z0m Roughness length (m) +#' - constants Kelvin - conversion degree Celsius to Kelvin +#' pressure0 - reference atmospheric pressure at sea level (Pa) +#' Tair0 - reference air temperature (K) +#' +#' # Details +#' The Roughness Reynolds Number is calculated as in Massman 1999a: +#' +#' ``Re = z0m * ustar / v`` +#' +#' where `v` is the kinematic viscosity (m2 s-1). +#' +#' # Value +#' - Re -: Roughness Reynolds Number (-) +#' +#' #References +#' Massman, W_J., 1999a: A model study of kB H- 1 for vegetated surfaces using +#' 'localized near-field' Lagrangian theory. Journal of Hydrology 223, 27-43. +#' +#' ```@example; output = false +#' ``` +#' Reynolds_Number(25,100,0.5,z0m=0.5) +#' +# """ +# """ +# function Reynolds_Number(Tair,pressure,ustar,z0m,constants=bigleaf_constants()) + +# v = kinematic_viscosity(Tair,pressure,constants) +# Re = z0m*ustar/v + +# return(Re) +# end + + + +#' Roughness Parameters +#' +#' A simple approximation of the two roughness parameters displacement height (d) +#' and roughness length for momentum (z0m). +#' +#' - method Method to use, one of `"canopy_height","canopy_height&LAI",Val(:wind_profile)` +#' NOTE: if `method = "canopy_height"`, only the following three arguments +#' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, +#' and `hs` are required. +#' - zh Vegetation height (m) +#' - frac_d Fraction of displacement height on canopy height (-) +#' - frac_z0m Fraction of roughness length on canopy height (-) +#' - LAI Leaf area index (-) +#' - zr Instrument (reference) height (m) +#' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. +#' Only needed if `method = "canopy_height&LAI"`. +#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` +#' The following arguments are only needed if `method = Val(:wind_profile)`! +#' - data Data_frame or matrix containing all required variables +#' - Tair Air temperature (deg C) +#' - pressure Atmospheric pressure (kPa) +#' - wind Wind speed at height zr (m s-1) +#' - ustar Friction velocity (m s-1) +#' - H Sensible heat flux (W m-2) +#' - d Zero-plane displacement height (m); optional +#' - z0m Roughness length for momentum (m); optional +#' - stab_roughness Should stability correction be considered? Default is `true`. +#' - stab_formulation Stability correction function used (If `stab_correction = true`). +#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +#' - constants k - von-Karman constant (-) +#' Kelvin - conversion degree Celsius to Kelvin +#' cp - specific heat of air for constant pressure (J K-1 kg-1) +#' g - gravitational acceleration (m s-2) +#' se_median - conversion standard error (SE) of the mean to SE of the median +#' +#' +#' # Details +#' The two main roughness parameters, the displacement height (d) +#' and the roughness length for momentum (z0m) can be estimated from simple +#' empirical relationships with canopy height (zh). If `method = "canopy_height"`, +#' the following formulas are used: +#' +#' ``d = frac_d * zh`` +#' +#' ``z0m = frac_z0m * zh`` +#' +#' where frac_d defaults to 0.7 and frac_z0m to 0.1. +#' +#' Alternatively, d and z0m can be estimated from both canopy height and LAI +#' (If `method = "canopy_height&LAI"`). +#' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed +#' the following semi-empirical relations: +#' +#' ``X = cd * LAI`` +#' +#' ``d = 1.1 * zh * ln(1 + X^(1/4))`` +#' +#' ``z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2`` +#' +#' ``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` +#' +#' If `method = Val(:wind_profile)`, z0m is estimated by solving +#' the wind speed profile for z0m: +#' +#' ``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` +#' +#' By default, d in this equation is fixed to 0.7*zh, but can be set to any +#' other value. psi_m is 0 if `stab_roughness = false`. +#' +#' # Value +#' a DataFrame with the following columns: +#' - d: Zero-plane displacement height (m) +#' - z0m: Roughness length for momentum (m) +#' - z0m_se: Only if `method = wind_profile`: Standard Error of the median for z0m (m) +#' +#' #References +#' Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat +#' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. +#' +#' Shaw, R. H., Pereira, A., 1982: Aerodynamic roughness of a plant canopy: +#' a numerical experiment. Agricultural Meteorology, 26, 51-65. +#' +#' #See also +#' [`wind_profile`](@ref) +#' +#' ```@example; output = false +#' ``` +#' # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy +#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=5) +#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=2) +#' +#' # fix d to 0.7*zh and estimate z0m from the wind profile +#' df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) +#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.7,data=df) +#' +#' # assume d = 0.8*zh +#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.8,data=df) +#' +#' @importFrom stats median sd complete_cases +#' @export +# function roughness_parameters(method=c("canopy_height","canopy_height&LAI",Val(:wind_profile)),zh, +# frac_d=0.7,frac_z0m=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", +# wind="wind",ustar="ustar",H="H",d=nothing,z0m=nothing, +# stab_roughness=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), +# constants=bigleaf_constants()) + +# method = match_arg(method) +# stab_formulation = match_arg(stab_formulation) + +# if (method == "canopy_height") + +# d = frac_d*zh +# z0m = frac_z0m*zh +# z0m_se = NA + +# elseif (method == "canopy_height&LAI") + +# X = cd * LAI +# d = 1.1 * zh * log(1 + X^(1/4)) + +# if (X >= 0 & X <= 0.2) +# z0m = hs + 0.3 * X^(1/2) +# else +# z0m = 0.3 * zh * (1 - d/zh) +# end +# z0m_se = NA + +# elseif (method == Val(:wind_profile)) + +# check_input(data,Tair,pressure,wind,ustar,H) + +# if (isnothing(d)) + +# d = frac_d * zh + +# end + +# if (stab_roughness) + +# zeta = stability_parameter(data=data,Tair=Tair,pressure=pressure,ustar=ustar,H=H, +# zr=zr,d=d,constants=constants) +# psi_m = stability_correction(zeta,formulation=stab_formulation)[,"psi_m"] +# z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar - psi_m) + +# else + +# z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar) + +# end + +# z0m_all[z0m_all > zh] = NA + +# z0m = median(z0m_all,na_rm=true) +# z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=true) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) + +# end + +# return(DataFrame(d,z0m,z0m_se)) +# end + + + +""" +Wind Speed at Given Heights in the Surface Layer + +Wind speed at a given height above the canopy estimated from single-level +measurements of wind speed. + +# Arguments +- `data` : Data_frame or matrix containing all required variables +- `z` : Heights above ground for which wind speed is calculated. + Needs to be of same length as `data` or of length 1 +- `Tair` : Air temperature (deg C) +- `pressure` : Atmospheric pressure (kPa) +- `ustar` : Friction velocity (m s-1) +- `H` : Sensible heat flux (W m-2) +- `wind` : Wind speed at height zr (m s-1); only used if `stab_correction = true` +- `zr` : Instrument (reference) height (m) +- `zh` : Canopy height (m) +- `d` : Zero-plane displacement height (-) +- `frac_d` : Fraction of displacement height on canopy height (-); + : only used if `d` is not available +- `z0m` : Roughness length (m), optional; only used if `stab_correction = false` (default=0.1) +- `frac_z0m` : Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. + Default is 0.1. +- `estimate_z0m` : Should `z0m` be estimated from the logarithmic wind profile? If `true` (the default), + arguments `z0m` and `frac_z0m` are ignored. + See [`roughness_parameters`](@ref) for details. +- `stab_correction`: Should stability correction be applied? Defaults to `true` +- `stab_formulation`: Stability correction function used (If `stab_correction = true`). + Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `k` - von-Karman constant (-) + - `Kelvin` - conversion degree Celsius to Kelvin + - `cp` - specific heat of air for constant pressure (J K-1 kg-1) + - `g` - gravitational acceleration (m s-2) + +# Details +The underlying assumption is the existence of a logarithmic wind profile +above the height d + z0m (the height at which wind speed mathematically reaches zero +according to the Monin-Obhukov similarity theory). +In this case, the wind speed at a given height z is given by: + +``u(z) = (ustar/k) * (ln((z - d) / z0m) - \\psi_m`` + +The roughness parameters zero-plane displacement height (d) and roughness length (z0m) +can be approximated from [`roughness_parameters`](@ref). ``\\psi_m`` is omitted +if `stab_correction = false` (not recommended). If `estimate_z0m = true`, +z0m is first estimated from the wind profile equation and then used in the equation +above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). + +# Note +Note that this equation is only valid for z >= d + z0m, and it is not +meaningful to calculate values closely above d + z0m. All values in `heights` +smaller than d + z0m will return 0. + +Computing windspeed without stability correction, (besides d and z0m) only requires 'ustar'. +For stability correction, additionally requires 'Tair', 'pressure', and 'H'. +d and z0m can be specified directly as named arguments or as a fraction of zh. + + +# Value +A vector of wind speed at heights `z`. + +# References +- Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. + 3rd edition. Academic Press, London. +- Newman, J_F., Klein, P_M., 2014: The impacts of atmospheric stability on + the accuracy of wind speed extrapolation methods. Resources 3, 81-105. + +# See also +[`roughness_parameters`](@ref) + +```@example; output = false +# heights = seq(18,40,2) # heights above ground for which to calculate wind speed +# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) +# ws = DataFrame(matrix(NA,ncol=length(heights),nrow=nrow(df))) +# colnames(ws) = paste0(heights,"m") +# for (i in seq_along(heights)) +# ws[,i] = wind_profile(df,z=heights[i],zr=40,zh=25,d=16) +# } +``` +""" +function wind_profile(df, z; + zh=nothing, + d=nothing, frac_d=0.7, + z0m=nothing,frac_z0m=nothing, + stab_formulation=Val(:Dyer_1970), + constants=bigleaf_constants() + ) + ## determine roughness parameters + if (isnothing(d)) + (isnothing(frac_d) || isnothing(zh)) && error( + "Either 'd' or both 'zh' and 'frac_d' must be specified.") + d = frac_d * zh + end + if isnothing(z0m) + (isnothing(frac_z0m) || isnothing(zh)) && error( + "Either 'z0m' or both 'zh' and 'frac_z0m' must be specified.") + z0m = frac_z0m * zh + end + if any(@.(z < (d + z0m) & !ismissing(d + z0m))) + @warn("function is only valid for heights above d + z0m! Wind speed for heights " * + "below d + z0m will return 0!") + end + wind_profile_(df, stab_formulation, z, d, z0m; constants) +end + +function wind_profile(::Val{:no_stability_correction}, z, ustar, d, z0m; + constants=bigleaf_constants()) + wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m))) +end +function wind_profile(stab_formulation, ustar, Tair,pressure,H, d, z0m; + constants=bigleaf_constants()) + zeta = stability_parameter(Tair,pressure,ustar,H; zr=z,d,constants) + psi_m = stability_correction(stab_formulation, zeta).psi_m + wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) +end + +function wind_profile(df::AbstractDataFrame, stab_formulation::Val{:no_stability_correction}, + z, d, z0m; constants=bigleaf_constants()) + inputs = SA[:ustar] + fspeed(args...) = wind_profile(stab_formulation, z, args..., d, z0m; constants) + select(df, inputs => ByRows(fspeed) => :windz).windz +end + +function wind_profile(df::AbstractDataFrame, stab_formulation, z, d, z0m; + constants=bigleaf_constants()) + inputs = SA[:ustar, :Tair, :pressure, :H] + fspeed(args...) = wind_profile(stab_formulation, z, args..., d, z0m; constants) + select(df, inputs => ByRows(fspeed) => :windz).windz +end + + + + + +# function estimate_z0m( +# Tair,pressure,wind,ustar,H +# zh,zr,d) +# error("not yet implemented, need roughness_parameters") +# # z0m = roughness_parameters(Val(:wind_profile),zh=zh,zr=zr,d=d, +# # Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, +# # stab_roughness=true,stab_formulation=stab_formulation, +# # constants=constants)[,"z0m"] +# end + + diff --git a/test/boundary_layer_conductance.jl b/test/boundary_layer_conductance.jl new file mode 100644 index 0000000..a6f2cd8 --- /dev/null +++ b/test/boundary_layer_conductance.jl @@ -0,0 +1,33 @@ +@testset "add_Gb" begin + Gb_h = 0.0347 + @test add_Gb(Gb_h) == NamedTuple() + # + Gb = add_Gb(Gb_h, :Gb_N20 => 2, :Gb_CH4 => 4) + @test keys(Gb) == (:Gb_N20, :Gb_CH4) + @test ≈(Gb.Gb_N20, 0.0173, rtol = 1/100) + @test ≈(Gb.Gb_CH4, 0.0109, rtol = 1/100) + # + Gb = add_Gb(missing, :Gb_N20 => 2, :Gb_CH4 => 4) + @test keys(Gb) == (:Gb_N20, :Gb_CH4) + @test all(ismissing.(values(Gb))) +end + +@testset "Gb_Thom" begin + Gb = Gb_Thom(0.1) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test all(isapprox.(values(Gb), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) + Gb = Gb_Thom(missing) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test all(ismissing.(values(Gb))) + # + dfo = DataFrame(ustar = SA[0.1,missing,0.3]) + df = copy(dfo) + compute_Gb!(df, Val(:Thom_1972)) + @test propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2] + @test all(isapprox.(values(df[1,2:end]), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) + compute_Gb!(df, Val(:Thom_1972); Sc = SA[:Gb_N20 => 2, :Gb_CH4 => 4]) + @test propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2, + :Gb_N20, :Gb_CH4] + @test all(isapprox.(values(df[1,(end-1):end]), values((Gb_N20 = 0.0173, Gb_CH4 = 0.0109)), rtol = 1e-2)) +end + From 65cedda67a75ae75c30bda9feefb6c30cd2568f5 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 3 Nov 2021 07:53:18 +0100 Subject: [PATCH 28/43] work on wind_profile --- inst/fromR/WUE_metrics.jl | 2 +- inst/fromR/aerodynamic_conductance.jl | 2 +- inst/fromR/bigleaf_physiology.jl | 6 +- inst/fromR/boundary_layer_conductance.jl | 4 +- inst/fromR/decoupling.jl | 2 +- inst/fromR/energy_balance.jl | 4 +- inst/fromR/evapotranspiration.jl | 6 +- inst/fromR/filter_data.jl | 2 +- inst/fromR/stability_correction.jl | 16 +- inst/fromR/surface_conditions.jl | 4 +- inst/fromR/surface_conductance.jl | 2 +- inst/fromR/surface_roughness.jl | 4 +- inst/tha.jl | 23 +++ src/Bigleaf.jl | 7 +- src/aerodynamic_conductance.jl | 72 ++++----- src/boundary_layer_conductance.jl | 4 +- src/evapotranspiration.jl | 2 +- src/stability_correction.jl | 193 +++++++++++++++++++++++ src/surface_roughness.jl | 83 +++++----- test/runtests.jl | 19 +++ test/stability_correction.jl | 39 +++++ test/surface_roughness.jl | 20 +++ 22 files changed, 405 insertions(+), 111 deletions(-) create mode 100755 src/stability_correction.jl create mode 100644 test/stability_correction.jl create mode 100644 test/surface_roughness.jl diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index fb7bccc..14bb638 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -6,7 +6,7 @@ #' #' Calculation of various water use efficiency (WUE) metrics. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - GPP Gross primary productivity (umol CO2 m-2 s-1) #' - NEE Net ecosystem exchange (umol CO2 m-2 s-1) #' - LE Latent heat flux (W m-2) diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index d607cf2..b9ca1af 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -7,7 +7,7 @@ #' Bulk aerodynamic conductance, including options for the boundary layer conductance #' formulation and stability correction functions. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - wind Wind speed (m s-1) diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index 4ff053c..9920681 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -480,7 +480,7 @@ end #' Estimation of the intrinsic WUE metric "g1" (stomatal slope) #' from nonlinear regression. #' -#' - data Data_frame or matrix containing all required columns +#' - data DataFrame or matrix containing all required columns #' - Tair Air (or surface) temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - GPP Gross primary productivity (umol CO2 m-2 s-1) @@ -751,7 +751,7 @@ end #' calculates GPP_ref at a reference (usually saturating) PPFD and #' ecosystem quantum yield (alpha) using a rectangular light response curve. #' -#' - data Data_frame or matrix containing all required columns +#' - data DataFrame or matrix containing all required columns #' - NEE Net ecosystem exchange (umol CO2 m-2 s-1) #' - Reco Ecosystem respiration (umol CO2 m-2 s-1) #' - PPFD Photosynthetic photon flux density (umol m-2 s-1) @@ -861,7 +861,7 @@ end #' #' Sensitivity of surface conductance to vapor pressure deficit. #' -#' - data Data_frame or matrix containing all required columns +#' - data DataFrame or matrix containing all required columns #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - VPD Vapor pressure deficit (kPa) #' - ... Additional arguments to `\link[stats]{nls`} diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index c442fdb..346d97b 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -88,7 +88,7 @@ end #' A formulation for the canopy boundary layer conductance #' for heat transfer according to Choudhury & Monteith 1988. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (degC) #' - pressure Atmospheric pressure (kPa) #' - wind Wind speed at sensor height (m s-1) @@ -227,7 +227,7 @@ end #' A physically based formulation for the canopy boundary layer conductance #' to heat transfer according to Su et al. 2001. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (degC) #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index 837de71..2c02816 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -6,7 +6,7 @@ #' #' The canopy-atmosphere decoupling coefficient 'Omega'. #' -#' - data Data_frame or matrix containing all required input variables +#' - data DataFrame or matrix containing all required input variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index f31a9c6..8ad8cde 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -96,7 +96,7 @@ end #' Calculates the degree of the energy balance non-closure for the entire time span #' based on the ratio of two sums (energy balance ratio), and ordinary least squares (OLS). #' -#' - data Data_frame or matrix containing all required variables. +#' - data DataFrame or matrix containing all required variables. #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional @@ -191,7 +191,7 @@ end #' that the surface would receive if it had the same temperature than #' the air. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Rn Net radiation (W m-2) #' - Tair Air temperature (degC) #' - Tsurf Surface temperature (degC) diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index d201736..3782093 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -7,7 +7,7 @@ #' Potential evapotranspiration according to Priestley & Taylor 1972 or #' the Penman-Monteith equation with a prescribed surface conductance. #' -#' - data Data_frame or matrix containing all required variables; optional +#' - data DataFrame or matrix containing all required variables; optional #' - Tair Air temperature (degC) #' - pressure Atmospheric pressure (kPa) #' - Rn Net radiation (W m-2) @@ -154,7 +154,7 @@ end #' equation with a prescribed surface conductance. #' This function is deprecated. Use potential_ET(...,approach=Val(:PenmanMonteith)) instead. #' -#' - data Data_frame or matrix containing all required variables; optional +#' - data DataFrame or matrix containing all required variables; optional #' - Gs_ref Reference surface conductance (m s-1); defaults to 0.0143 m s-1. #' - Tair Air temperature (degC) #' - pressure Atmospheric pressure (kPa) @@ -192,7 +192,7 @@ end #' #' Evapotranspiration (ET) split up into imposed ET and equilibrium ET. #' -#' - data Data_frame or matrix containing all required input variables +#' - data DataFrame or matrix containing all required input variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - VPD Air vapor pressure deficit (kPa) diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index eb51273..8b0256c 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -7,7 +7,7 @@ #' Filters time series of EC data for high-quality values and specified #' meteorological conditions. #' -#' - data Data_frame or matrix containing all required input variables in +#' - data DataFrame or matrix containing all required input variables in #' half-hourly or hourly resolution. Including year, month, day information #' - quality_control Should quality control be applied? Defaults to `true`. #' - filter_growseas Should data be filtered for growing season? Defaults to `false`. diff --git a/inst/fromR/stability_correction.jl b/inst/fromR/stability_correction.jl index 5123670..f93aa0b 100755 --- a/inst/fromR/stability_correction.jl +++ b/inst/fromR/stability_correction.jl @@ -6,7 +6,7 @@ #' #' calculates the Monin-Obukhov length. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) @@ -62,7 +62,7 @@ end #' calculates "zeta", a parameter characterizing stratification in #' the lower atmosphere. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (degC) #' - pressure Atmospheric pressure (kPa) #' - ustar Friction velocity (m s-1) @@ -75,16 +75,16 @@ end #' g - gravitational acceleration (m s-2) #' #' # Details - The stability parameter ``\zeta`` is given by: + The stability parameter ``\\zeta`` is given by: #' -#' ``\zeta = (zr - d) / L`` +#' ``\\zeta = (zr - d) / L`` #' #' where L is the Monin-Obukhov length (m), calculated from the function #' [`Monin_Obukhov_length`](@ref). The displacement height d can #' be estimated from the function [`roughness_parameters`](@ref). #' #' # Value - - ``\zeta`` - : stability parameter (-) + - ``\\zeta`` - : stability parameter (-) #' #' ```@example; output = false #' ``` @@ -117,17 +117,17 @@ end #' #' # Details The functions give the integrated form of the universal functions. They -#' depend on the value of the stability parameter ``\zeta``, +#' depend on the value of the stability parameter ``\\zeta``, #' which can be calculated from the function [`stability_parameter`](@ref). #' The integration of the universal functions is: #' #' ``\\psi = -x * zeta`` #' -#' for stable atmospheric conditions (``\zeta`` >= 0), and +#' for stable atmospheric conditions (``\\zeta`` >= 0), and #' #' ``\\psi = 2 * log( (1 + y) / 2) `` #' -#' for unstable atmospheric conditions (``\zeta`` < 0). +#' for unstable atmospheric conditions (``\\zeta`` < 0). #' #' The different formulations differ in their value of x and y. #' diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index 2f7c0ad..d92cc0a 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -8,7 +8,7 @@ #' by inverting bulk transfer equations for water, energy, and carbon #' fluxes. #' -#' - data Data_frame or matrix containing all required input variables +#' - data DataFrame or matrix containing all required input variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - H Sensible heat flux (W m-2) @@ -179,7 +179,7 @@ end #' Radiometric surface temperature from longwave radiation #' measurements. #' -#' - data Data_frame or matrix containing all required input variables +#' - data DataFrame or matrix containing all required input variables #' - LW_up Longwave upward radiation (W m-2) #' - LW_down Longwave downward radiation (W m-2) #' - emissivity Emissivity of the surface (-) diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 6c16737..2a69fd6 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -7,7 +7,7 @@ #' Calculates surface conductance to water vapor from the inverted Penman-Monteith #' equation (by default) or from a simple flux-gradient approach. #' -#' - data Data_frame or matrix containing all required input variables +#' - data DataFrame or matrix containing all required input variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - Rn Net radiation (W m-2) diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index c2c548e..ba4e3a9 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -62,7 +62,7 @@ end #' Only needed if `method = "canopy_height&LAI"`. #' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` #' The following arguments are only needed if `method = Val(:wind_profile)`! -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - wind Wind speed at height zr (m s-1) @@ -211,7 +211,7 @@ end #' Wind speed at a given height above the canopy estimated from single-level #' measurements of wind speed. #' -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - z Heights above ground for which wind speed is calculated. #' Needs to be of same length as `data` or of length 1 #' - Tair Air temperature (deg C) diff --git a/inst/tha.jl b/inst/tha.jl index 64f8771..8ab1980 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -32,6 +32,28 @@ setinvalid_nongrowingseason!(thaf, 0.4) setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) +plot(first(tha, 48).ustar) + +tha48 = DataFrame( + datetime = ZonedDateTime("2014-06-01T00:00:00+00:00","yyyy-mm-ddTHH:MM:SSzzzz") .+ + (0:47) .* Minute(30), + ustar = Union{Missing, Float64}[0.5400000214576721, 0.4900000095367432, 0.4799999892711639, 0.449999988079071, 0.5099999904632568, 0.4600000083446503, 0.5400000214576721, 0.5199999809265137, 0.4600000083446503, 0.4099999964237213, 0.5299999713897705, 0.5099999904632568, 0.5199999809265137, 0.4099999964237213, 0.5400000214576721, 0.4600000083446503, 0.5099999904632568, 0.5, 0.6299999952316284, 0.5799999833106995, 0.6800000071525574, 0.6499999761581421, 0.7400000095367432, 0.7300000190734863, 0.7699999809265137, 0.7400000095367432, 0.7799999713897705, 0.8700000047683716, 0.7799999713897705, 0.6700000166893005, 0.5600000023841858, 0.6600000262260437, 0.7099999785423279, 0.6100000143051147, 0.5899999737739563, 0.6100000143051147, 0.5299999713897705, 0.449999988079071, 0.3400000035762787, 0.3300000131130219, 0.3300000131130219, 0.3700000047683716, 0.3799999952316284, 0.3799999952316284, 0.3600000143051147, 0.3700000047683716, 0.3600000143051147, 0.3400000035762787], + Tair = [11.88000011444092, 11.67000007629395, 11.1899995803833, 10.80000019073486, 10.67000007629395, 10.13000011444092, 9.779999732971191, 9.5, 9.09000015258789, 8.800000190734863, 8.6899995803833, 8.899999618530273, 9.430000305175781, 9.710000038146973, 10.55000019073486, 11.19999980926514, 11.72999954223633, 12.68000030517578, 13.1899995803833, 13.46000003814697, 14.1899995803833, 14.73999977111816, 14.65999984741211, 14.8100004196167, 15.02999973297119, 14.98999977111816, 14.77999973297119, 15.35000038146973, 15.5, 15.61999988555908, 15.47000026702881, 16.20000076293945, 16.13999938964844, 15.56999969482422, 15.14999961853027, 15.18000030517578, 14.81999969482422, 14.60000038146973, 14.10000038146973, 13.23999977111816, 12.65999984741211, 12.46000003814697, 12.09000015258789, 11.85999965667725, 11.72000026702881, 11.64000034332275, 11.52999973297119, 11.43000030517578], + pressure = [97.63999938964844, 97.62999725341797, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.62999725341797, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.69000244140625, 97.69999694824219, 97.70999908447266, 97.69999694824219, 97.7300033569336, 97.72000122070312, 97.70999908447266, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.70999908447266, 97.70999908447266, 97.72000122070312, 97.70999908447266, 97.70999908447266, 97.69999694824219, 97.68000030517578, 97.68000030517578, 97.66999816894531, 97.68000030517578, 97.66999816894531, 97.66999816894531, 97.66000366210938, 97.6500015258789, 97.63999938964844, 97.63999938964844, 97.6500015258789, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.68000030517578, 97.68000030517578, 97.69000244140625, 97.69000244140625, 97.69000244140625], + H = Union{Missing, Float64}[-68.18000030517578, -48.54000091552734, -59.09999847412109, -60.11000061035156, -59.40000152587891, -48.91999816894531, -53.52999877929688, -40.75, -29.28000068664551, -33.36999893188477, -9.050000190734863, 2.400000095367432, 38.29999923706055, 38.45000076293945, 85.08000183105469, 108.7799987792969, 176.6799926757812, 210.6900024414062, 230.0800018310547, 227.4199981689453, 321.6499938964844, 358.8999938964844, 329.9299926757812, 317.3099975585938, 375.1900024414062, 336.489990234375, 260.7099914550781, 360.6400146484375, 264.2200012207031, 185.3200073242188, 123.7699966430664, 236.0200042724609, 207.5200042724609, 128.3399963378906, 66.05999755859375, 79.6500015258789, 44.93999862670898, 16.22999954223633, -22.35000038146973, -29.10000038146973, -47.79999923706055, -63.61999893188477, -61.38000106811523, -65.11000061035156, -59.77000045776367, -60.52000045776367, -49.02999877929688, -53.45000076293945], +) + +tha_heights = ( + LAI = 7.6, # leaf area index + zh = 26.5, # average vegetation height (m) + zr = 42, # sensor height (m) + Dl = 0.01, # leaf characteristic dimension (m) +) + + + + +show(thaf.H[1:48]) dfGPPd = @pipe tha |> transform(_, :datetime => ByRow(yearmonthday) => :ymd, copycols = false) |> @@ -43,5 +65,6 @@ show(dfGPPd.GPPd) GPPd = [11.288497760271033, 13.013025930772224, 12.851774960756302, 11.996453734696843, 11.635422044472458, 11.155685572574535, 10.774393322790273, 10.181774605065584, 11.257192575993637, 12.9423423493281, 12.352468963712454, 13.402045020057509, 9.53826415212825, 12.071680051895479, 13.692589149111882, 12.845505638824156, 12.378533909407755, 11.672167064607493, 10.401156075240579, 10.705716138705611, 10.207347450816693, 11.052016352768987, 13.54435911634937, 12.060648361220956, 7.758974596237143, 9.869706534050541, 12.998054057980577, 10.627359564105669, 8.685295419767499, 10.874667977293333] + using Plots,StatsPlots @df dfGPPd plot(:date, [:GPPd], xlab = "Date", ylab="GPP") diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 121e93d..9b4f68a 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -30,6 +30,9 @@ export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason, setinvalid_afterprecip! export decoupling, surface_conductance, aerodynamic_conductance export compute_Gb, compute_Gb!, add_Gb, Gb_Thom +export wind_profile +export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, + stability_parameter!, stability_correction, stability_correction! include("util.jl") include("bigleaf_constants.jl") @@ -39,6 +42,8 @@ include("sun_position.jl") include("potential_radiation.jl") include("evapotranspiration.jl") include("filter_data.jl") -include("boundary_layer_conductance.jl") +include("stability_correction.jl") +include("surface_roughness.jl") +#include("boundary_layer_conductance.jl") end diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index ae622e8..a2a067b 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -5,7 +5,7 @@ Bulk aerodynamic conductance, including options for the boundary layer conductan formulation and stability correction functions. # Arguments -- `data` : Data_frame or matrix containing all required variables +- `data` : DataFrame or matrix containing all required variables - `Tair` : Air temperature (deg C) - `pressure` : Atmospheric pressure (kPa) - `wind` : Wind speed (m s-1) @@ -203,41 +203,41 @@ function aerodynamic_conductance(df; kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants() ) - ## calculate canopy boundary layer conductance (Gb) - if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] - if Rb_model == Val(:Thom_1972) - Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) - elseif Rb_model == Val(:Choudhury_1988) - Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, - H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, - stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - constants=constants) - elseif Rb_model == Val(:Su_2001) - Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, - H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, - stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - constants=constants) - end - kB_h = Gb_mod.kB_h - Rb_h = Gb_mod.Rb_h - Gb_h = Gb_mod.Gb_h - # TODO - # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) - # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] - elseif Rb_model == Val(:constant_kB1) - isnothing(kB_h) && error( - "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") - Rb_h = kB_h/(constants[:k] * ustar) - Gb_h = 1/Rb_h - if (!isnothing(Sc) || !isnothing(Sc_name)) - length(Sc) != length(Sc_name) && error( - "arguments 'Sc' and 'Sc_name' must have the same length") - !is_numeric(Sc) && error("argument 'Sc' must be numeric") - Sc = SA[constants[:Sc_CO2], Sc] - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) - colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - end - end + # ## calculate canopy boundary layer conductance (Gb) + # if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] + # if Rb_model == Val(:Thom_1972) + # Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) + # elseif Rb_model == Val(:Choudhury_1988) + # Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, + # H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, + # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + # constants=constants) + # elseif Rb_model == Val(:Su_2001) + # Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, + # H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, + # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + # constants=constants) + # end + # kB_h = Gb_mod.kB_h + # Rb_h = Gb_mod.Rb_h + # Gb_h = Gb_mod.Gb_h + # # TODO + # # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) + # # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] + # elseif Rb_model == Val(:constant_kB1) + # isnothing(kB_h) && error( + # "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") + # Rb_h = kB_h/(constants[:k] * ustar) + # Gb_h = 1/Rb_h + # if (!isnothing(Sc) || !isnothing(Sc_name)) + # length(Sc) != length(Sc_name) && error( + # "arguments 'Sc' and 'Sc_name' must have the same length") + # !is_numeric(Sc) && error("argument 'Sc' must be numeric") + # Sc = SA[constants[:Sc_CO2], Sc] + # Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) + # colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + # end + # end # ## calculate aerodynamic conductance for momentum (Ga_m) # if (wind_profile) diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index 36333c8..a3b516b 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -174,7 +174,7 @@ A formulation for the canopy boundary layer conductance for heat transfer according to Choudhury & Monteith 1988. # Arguments -- `df` : Data_frame or matrix containing all required variables +- `df` : DataFrame or matrix containing all required variables - `Tair` : Air temperature (degC) - `pressure` : Atmospheric pressure (kPa) - `wind` : Wind speed at sensor height (m s-1) @@ -305,7 +305,7 @@ A physically based formulation for the canopy boundary layer conductance to heat transfer according to Su et al. 2001. # Arguments -- data Data_frame or matrix containing all required variables +- data DataFrame or matrix containing all required variables - Tair Air temperature (degC) - pressure Atmospheric pressure (kPa) - ustar Friction velocity (m s-1) diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index dedeb85..972d342 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -13,7 +13,7 @@ the Penman-Monteith equation with a prescribed surface conductance. - `Rn`: Net radiation (W m-2) - `VPD`: Vapor pressure deficit (kPa) - `Ga`: Aerodynamic conductance to heat/water vapor (m s-1) -- `df`: Data_frame or matrix containing all required variables; optional +- `df`: DataFrame or matrix containing all required variables; optional - `approach`: Approach used. Either `Val(:PriestleyTaylor)` (default), or `Val(:PenmanMonteith)`. optional: diff --git a/src/stability_correction.jl b/src/stability_correction.jl new file mode 100755 index 0000000..75abcd2 --- /dev/null +++ b/src/stability_correction.jl @@ -0,0 +1,193 @@ +""" +Monin-Obukhov Length + +calculates the Monin-Obukhov length. + +# Arguments +- `Tair` Air temperature (degC) +- `pressure` Atmospheric pressure (kPa) +- `ustar` Friction velocity (m s-1) +- `H` Sensible heat flux (W m-2) +- df DataFrame containing all required variables +optional +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `Kelvin` - conversion degree Celsius to Kelvin + - `cp` - specific heat of air for constant pressure (J K-1 1) + - `k` - von Karman constant (-) + - `g` - gravitational acceleration (m s-2) + +# Details +The Monin-Obukhov length (L) is given by: + +``L = - (\\rho * cp * ustar^3 * Tair) / (k * g * H)`` + +where ``\\rho`` is air density (kg m-3). + +# Value +Monin-Obukhov length L (m) + +# Note +Note that L gets very small for very low ustar values with implications +for subsequent functions using L as input. It is recommended to filter +data and exclude low ustar values (ustar < ~0.2) beforehand. + +#References +Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. + +#See also +[`stability_parameter`](@ref) + +```@example; output = false +Monin_Obukhov_length(Tair=25,pressure=100,ustar=seq(0.2,1,0.1),H=seq(40,200,20)) +``` +""" +function Monin_Obukhov_length(Tair, pressure, ustar, H; constants=bigleaf_constants()) + rho = air_density(Tair, pressure; constants) + TairK = Tair + constants[:Kelvin] + MOL = (-rho*constants[:cp]*ustar^3*TairK) / (constants[:k]*constants[:g]*H) +end +function Monin_Obukhov_length!(df;constants=bigleaf_constants()) + ft(args...) = Monin_Obukhov_length(args...; constants) + transform!(df, SA[:Tair, :pressure, :ustar, :H] => ByRow(ft) => :MOL) +end +function Monin_Obukhov_length(df;constants=bigleaf_constants()) + Monin_Obukhov_length!(copy(df, copycols = false); + constants).MOL +end + +""" +Stability Parameter "zeta" + +calculates stability parameter "zeta", a parameter characterizing stratification in the +lower atmosphere. + +# Arguments +- `df` DataFrame or matrix containing all required variables +- `Tair` Air temperature (degC) +- `pressure` Atmospheric pressure (kPa) +- `ustar` Friction velocity (m s-1) +- `H` Sensible heat flux (W m-2) +- `zr` Instrument (reference) height (m) +- `d` Zero-plane displacement height (m) +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `Kelvin` - conversion degree Celsius to Kelvin + - `cp` - specific heat of air for constant pressure (J K-1 1) + - `k` - von Karman constant (-) + - `g` - gravitational acceleration (m s-2) + +# Details +The stability parameter ``\\zeta`` is given by: + + ``\\zeta = (zr - d) / L`` + +where L is the Monin-Obukhov length (m), calculated from the ction +[`Monin_Obukhov_length`](@ref). The displacement height can +be estimated from the function [`roughness_parameters`](@ref). + +# Value + - ``\\zeta`` - : stability parameter (-) + +```@example; output = false +df = DataFrame(Tair=25,pressure=100,ustar=seq(0.2,1,0.1),H=seq(40,200,20)) +stability_parameter(df,zr=40,d=15) +``` +""" +function stability_parameter(zr,d,MOL) + zeta = (zr - d) / MOL +end +function stability_parameter!(df::AbstractDataFrame; zr,d, + MOL = Monin_Obukhov_length(df)) + df[!,:zeta] .= stability_parameter(df; zr,d,MOL) + df +end +function stability_parameter(df::AbstractDataFrame; zr,d, + MOL = Monin_Obukhov_length(df)) + stability_parameter.(zr,d,MOL) +end + +""" +Integrated Stability Correction Functions for Heat and Momentum + +dimensionless stability functions needed to correct deviations +from the exponential wind profile under non-neutral conditions. + +# Arguments +- `zeta` : Stability parameter zeta (-) +- `stab_formulation` : Formulation for the stability function. Either `Val(:Dyer_1970)`, + or `Val(:Businger_1971)` +# Details +The functions give the integrated form of the universal functions. They +depend on the value of the stability parameter ``\\zeta``, +which can be calculated from the function [`stability_parameter`](@ref). +The integration of the universal functions is: + +``\\psi = -x * zeta`` + +for stable atmospheric conditions (``\\zeta`` >= 0), and + +``\\psi = 2 * log( (1 + y) / 2)`` + +for unstable atmospheric conditions (``\\zeta`` < 0). + +The different formulations differ in their value of x and y. + +# Value +a NamedTuple with the following columns: +- `psi_h`: the value of the stability function for heat and water vapor (-) +- `psi_m`: the value of the stability function for momentum (-) + +# References +- Dyer, A_J., 1974: A review of flux-profile relationships. + Boundary-Layer Meteorology 7, 363-372. +- Dyer, A. J., Hicks, B_B., 1970: Flux-Gradient relationships in the + constant flux layer. Quart. J. R. Meteorol. Soc. 96, 715-721. +- Businger, J_A., Wyngaard, J. C., Izumi, I., Bradley, E. F., 1971: + Flux-Profile relationships in the atmospheric surface layer. + J. Atmospheric Sci. 28, 181-189. +- Paulson, C_A., 1970: The mathematical representation of wind speed + and temperature profiles in the unstable atmospheric surface layer. + Journal of Applied Meteorology 9, 857-861. + Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. + +# Examples +```jldoctest; output = false +zeta = -2:0.5:0.5 +df2 = DataFrame(stability_correction.(zeta; stab_formulation=Val(:Businger_1971))) +propertynames(df2) == [:psi_h, :psi_m] +# output +true +``` +""" +function stability_correction(zeta; stab_formulation=Val(:Dyer_1970)) + # integration of universal functions (after Paulson_1970 and Foken 2008) + ismissing(zeta) && return((psi_h = missing, psi_m = missing)) + is_stable = zeta >= 0 + if is_stable + x_h, x_m = get_stability_coefs_stable(stab_formulation) + psi_h = x_h * zeta + psi_m = x_m * zeta + else + y_h, y_m = get_stability_coefs_unstable(stab_formulation, zeta) + psi_h = 2 * log( (1 + y_h ) / 2) + psi_m = 2 * log( (1 + y_m ) / 2) + + log( ( 1 + y_m^2 ) / 2) + -2 * atan(y_m) + pi/2 + end + (;psi_h, psi_m) +end + +get_stability_coefs_stable(::Val{:Businger_1971}) = (x_h = -7.8, x_m = -6) +function get_stability_coefs_unstable(::Val{:Businger_1971}, zeta) + y_h = 0.95 * ( 1 - 11.6 * zeta)^0.5 + y_m = (1 - 19.3*zeta)^0.25 + (;y_h, y_m) +end + +get_stability_coefs_stable(::Val{:Dyer_1970}) = (x_h = -5, x_m = -5) +function get_stability_coefs_unstable(::Val{:Dyer_1970}, zeta) + y_h = (1 - 16 * zeta)^0.5 + y_m = (1 - 16 * zeta)^0.25 + (;y_h, y_m) +end + + diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index fbc4eab..05974cf 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -58,7 +58,7 @@ #' Only needed if `method = "canopy_height&LAI"`. #' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` #' The following arguments are only needed if `method = Val(:wind_profile)`! -#' - data Data_frame or matrix containing all required variables +#' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) #' - pressure Atmospheric pressure (kPa) #' - wind Wind speed at height zr (m s-1) @@ -209,28 +209,26 @@ Wind speed at a given height above the canopy estimated from single-level measurements of wind speed. # Arguments -- `data` : Data_frame or matrix containing all required variables -- `z` : Heights above ground for which wind speed is calculated. - Needs to be of same length as `data` or of length 1 +- `z` : Height above ground for which wind speed is calculated. +- `ustar` : Friction velocity (m s-1) +- `d` : Zero-plane displacement height (-) +- `z0m` : Roughness length (m), optional; + TODO check in R: only used if `stab_correction = false` (default=0.1) +alternatively, in the DataFrame variant d and z0m can be specified as faction of zh +- `zh` : Canopy height (m) +- `frac_d` : Fraction of displacement height on canopy height (-) +- `frac_z0m` : Fraction of roughness length on canopy height (-) +required for stability correction - `Tair` : Air temperature (deg C) - `pressure` : Atmospheric pressure (kPa) -- `ustar` : Friction velocity (m s-1) - `H` : Sensible heat flux (W m-2) +required for z0m estimation - `wind` : Wind speed at height zr (m s-1); only used if `stab_correction = true` - `zr` : Instrument (reference) height (m) -- `zh` : Canopy height (m) -- `d` : Zero-plane displacement height (-) -- `frac_d` : Fraction of displacement height on canopy height (-); - : only used if `d` is not available -- `z0m` : Roughness length (m), optional; only used if `stab_correction = false` (default=0.1) -- `frac_z0m` : Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. - Default is 0.1. -- `estimate_z0m` : Should `z0m` be estimated from the logarithmic wind profile? If `true` (the default), - arguments `z0m` and `frac_z0m` are ignored. - See [`roughness_parameters`](@ref) for details. -- `stab_correction`: Should stability correction be applied? Defaults to `true` -- `stab_formulation`: Stability correction function used (If `stab_correction = true`). - Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +optional +- `stab_formulation = Val(:Dyer_1970)`: Stability correction function used + Either `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or + `Val(:no_stability_correction)`. - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - `k` - von-Karman constant (-) - `Kelvin` - conversion degree Celsius to Kelvin @@ -282,7 +280,20 @@ A vector of wind speed at heights `z`. # ws[,i] = wind_profile(df,z=heights[i],zr=40,zh=25,d=16) # } ``` -""" +""" +function wind_profile(::Val{:no_stability_correction}, z, ustar, d, z0m; + constants=bigleaf_constants()) + wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m))) +end +function wind_profile(stab_formulation, z, ustar, Tair,pressure,H, d, z0m; + constants=bigleaf_constants()) + # in order to comput psi_m need Monin_Obukhov_length with additional vars + MOL = Monin_Obukhov_length(Tair,pressure,ustar,H; constants) + zeta = stability_parameter(z,d,MOL) + psi_m = stability_correction(zeta; stab_formulation).psi_m + wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) +end + function wind_profile(df, z; zh=nothing, d=nothing, frac_d=0.7, @@ -305,36 +316,20 @@ function wind_profile(df, z; @warn("function is only valid for heights above d + z0m! Wind speed for heights " * "below d + z0m will return 0!") end - wind_profile_(df, stab_formulation, z, d, z0m; constants) + wind_profile_(df, z, d, z0m; stab_formulation, constants) end -function wind_profile(::Val{:no_stability_correction}, z, ustar, d, z0m; - constants=bigleaf_constants()) - wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m))) -end -function wind_profile(stab_formulation, ustar, Tair,pressure,H, d, z0m; - constants=bigleaf_constants()) - zeta = stability_parameter(Tair,pressure,ustar,H; zr=z,d,constants) - psi_m = stability_correction(stab_formulation, zeta).psi_m - wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) -end -function wind_profile(df::AbstractDataFrame, stab_formulation::Val{:no_stability_correction}, - z, d, z0m; constants=bigleaf_constants()) - inputs = SA[:ustar] - fspeed(args...) = wind_profile(stab_formulation, z, args..., d, z0m; constants) - select(df, inputs => ByRows(fspeed) => :windz).windz -end -function wind_profile(df::AbstractDataFrame, stab_formulation, z, d, z0m; - constants=bigleaf_constants()) - inputs = SA[:ustar, :Tair, :pressure, :H] - fspeed(args...) = wind_profile(stab_formulation, z, args..., d, z0m; constants) - select(df, inputs => ByRows(fspeed) => :windz).windz +function wind_profile(df::AbstractDataFrame, z, d, z0m; stab_formulation = Val(:Dyer_1970), + constants = bigleaf_constants()) + inputs = get_stab_inputs(stab_formulation) + # do not use ByRow because z0m may be a vector + fspeed(args...) = wind_profile.(stab_formulation, z, args..., d, z0m; constants) + select(df, inputs => fspeed => :windz).windz end - - - +get_stab_inputs(::Val{:no_stability_correction}) = SA[:ustar] +get_stab_inputs(::Union{Val{:Dyer_1970},Val{:Businger_1971}}) = SA[:ustar, :Tair, :pressure, :H] # function estimate_z0m( diff --git a/test/runtests.jl b/test/runtests.jl index 61d9f07..49f4150 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,22 @@ using Pipe, DataFrames, Dates, TimeZones using StaticArrays using Statistics, StatsBase +tha48 = DataFrame( + datetime = ZonedDateTime("2014-06-01T00:00:00+00:00","yyyy-mm-ddTHH:MM:SSzzzz") .+ + (0:47) .* Minute(30), + ustar = Union{Missing, Float64}[0.5400000214576721, 0.4900000095367432, 0.4799999892711639, 0.449999988079071, 0.5099999904632568, 0.4600000083446503, 0.5400000214576721, 0.5199999809265137, 0.4600000083446503, 0.4099999964237213, 0.5299999713897705, 0.5099999904632568, 0.5199999809265137, 0.4099999964237213, 0.5400000214576721, 0.4600000083446503, 0.5099999904632568, 0.5, 0.6299999952316284, 0.5799999833106995, 0.6800000071525574, 0.6499999761581421, 0.7400000095367432, 0.7300000190734863, 0.7699999809265137, 0.7400000095367432, 0.7799999713897705, 0.8700000047683716, 0.7799999713897705, 0.6700000166893005, 0.5600000023841858, 0.6600000262260437, 0.7099999785423279, 0.6100000143051147, 0.5899999737739563, 0.6100000143051147, 0.5299999713897705, 0.449999988079071, 0.3400000035762787, 0.3300000131130219, 0.3300000131130219, 0.3700000047683716, 0.3799999952316284, 0.3799999952316284, 0.3600000143051147, 0.3700000047683716, 0.3600000143051147, 0.3400000035762787], + Tair = [11.88000011444092, 11.67000007629395, 11.1899995803833, 10.80000019073486, 10.67000007629395, 10.13000011444092, 9.779999732971191, 9.5, 9.09000015258789, 8.800000190734863, 8.6899995803833, 8.899999618530273, 9.430000305175781, 9.710000038146973, 10.55000019073486, 11.19999980926514, 11.72999954223633, 12.68000030517578, 13.1899995803833, 13.46000003814697, 14.1899995803833, 14.73999977111816, 14.65999984741211, 14.8100004196167, 15.02999973297119, 14.98999977111816, 14.77999973297119, 15.35000038146973, 15.5, 15.61999988555908, 15.47000026702881, 16.20000076293945, 16.13999938964844, 15.56999969482422, 15.14999961853027, 15.18000030517578, 14.81999969482422, 14.60000038146973, 14.10000038146973, 13.23999977111816, 12.65999984741211, 12.46000003814697, 12.09000015258789, 11.85999965667725, 11.72000026702881, 11.64000034332275, 11.52999973297119, 11.43000030517578], + pressure = [97.63999938964844, 97.62999725341797, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.62999725341797, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.69000244140625, 97.69999694824219, 97.70999908447266, 97.69999694824219, 97.7300033569336, 97.72000122070312, 97.70999908447266, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.70999908447266, 97.70999908447266, 97.72000122070312, 97.70999908447266, 97.70999908447266, 97.69999694824219, 97.68000030517578, 97.68000030517578, 97.66999816894531, 97.68000030517578, 97.66999816894531, 97.66999816894531, 97.66000366210938, 97.6500015258789, 97.63999938964844, 97.63999938964844, 97.6500015258789, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.68000030517578, 97.68000030517578, 97.69000244140625, 97.69000244140625, 97.69000244140625], + H = Union{Missing, Float64}[-68.18000030517578, -48.54000091552734, -59.09999847412109, -60.11000061035156, -59.40000152587891, -48.91999816894531, -53.52999877929688, -40.75, -29.28000068664551, -33.36999893188477, -9.050000190734863, 2.400000095367432, 38.29999923706055, 38.45000076293945, 85.08000183105469, 108.7799987792969, 176.6799926757812, 210.6900024414062, 230.0800018310547, 227.4199981689453, 321.6499938964844, 358.8999938964844, 329.9299926757812, 317.3099975585938, 375.1900024414062, 336.489990234375, 260.7099914550781, 360.6400146484375, 264.2200012207031, 185.3200073242188, 123.7699966430664, 236.0200042724609, 207.5200042724609, 128.3399963378906, 66.05999755859375, 79.6500015258789, 44.93999862670898, 16.22999954223633, -22.35000038146973, -29.10000038146973, -47.79999923706055, -63.61999893188477, -61.38000106811523, -65.11000061035156, -59.77000045776367, -60.52000045776367, -49.02999877929688, -53.45000076293945], +) + +tha_heights = ( + LAI = 7.6, # leaf area index + zh = 26.5, # average vegetation height (m) + zr = 42, # sensor height (m) + Dl = 0.01, # leaf characteristic dimension (m) +) + @testset "Bigleaf" begin @testset "util" begin include("util.jl") @@ -23,6 +39,9 @@ using Statistics, StatsBase @testset "potential_radiation" begin include("potential_radiation.jl") end + @testset "stability_correction" begin + include("stability_correction.jl") + end @testset "evapotranspiration" begin include("evapotranspiration.jl") end diff --git a/test/stability_correction.jl b/test/stability_correction.jl new file mode 100644 index 0000000..58fd99d --- /dev/null +++ b/test/stability_correction.jl @@ -0,0 +1,39 @@ +@testset "Monin_Obukhov_length" begin + datetime, ustar, Tair, pressure, H = values(tha48[24,1:5]) + MOL24 = Monin_Obukhov_length(Tair, pressure, ustar, H) + @test ≈(MOL24, -104.3, rtol = 1/1000) + df = copy(tha48) + Monin_Obukhov_length!(df) + @test df.MOL[24] == MOL24 + MOL = Monin_Obukhov_length(df) + @test MOL == df.MOL +end + +@testset "stability_parameter" begin + df = copy(tha48) + MOL24 = first(Monin_Obukhov_length(df[SA[24],:])) + zr=40;d=15 + zeta = stability_parameter(zr,d,MOL24) + @test ≈(zeta , -0.240, rtol = 1/100) + # + stability_parameter!(df;zr,d) + @test df.zeta[24] == zeta + df = copy(tha48) + zetas = stability_parameter(df;zr,d) + @test df == tha48 # did not modify original df + @test zetas[24] == zeta +end + +@testset "stability_correction" begin + zeta = -2:0.5:0.5 + df2 = DataFrame(stability_correction.(zeta)) + @test all(isapprox.(df2.psi_h, SA[2.431,2.197,1.881,1.386,0,-2.5], rtol=1e-3)) + @test all(isapprox.(df2.psi_m, SA[2.275, 2.061, 1.772, 1.317, 0, -2.5], rtol=1e-3)) + df2 = DataFrame(stability_correction.(zeta; stab_formulation=Val(:Businger_1971))) + @test all(isapprox.(df2.psi_h, SA[2.085, 1.862, 1.564, 1.106,0, -3.9], rtol=1e-3)) + @test all(isapprox.(df2.psi_m, SA[2.418, 2.200, 1.904, 1.435,0, -3], rtol=1e-3)) + # + resm = stability_correction(missing) + @test keys(resm) == (:psi_h, :psi_m) + @test all(ismissing.(values(resm))) +end diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl new file mode 100644 index 0000000..61bc0d4 --- /dev/null +++ b/test/surface_roughness.jl @@ -0,0 +1,20 @@ +#@testset "wind_profile" begin + datetime, ustar, Tair, pressure, H = values(tha48[1,:]) + z = 30 + d=0.7*tha_heights.zh + z0m=2.65 + u30 = wind_profile(Val(:no_stability_correction), z, ustar, d, z0m) + @test ≈(u30, 1.93, rtol = 1/100 ) + # + u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) + @test ≈(u30c, 2.31, rtol = 1/100 ) + # + df = copy(tha48) + windz = wind_profile(df, z, d, z0m; stab_formulation = Val(:no_stability_correction)) + @test length(windz) == 48 + @test windz[1] == u30 + windzc = wind_profile(df, z, d, z0m; stab_formulation = Val(:Dyer_1970)) + @test windzc[1] == u30c + #plot(windz) + #plot!(windz2) +end \ No newline at end of file From f8a87eeaff8109dbed8ad7553fbdc847e8cf9e7c Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 3 Nov 2021 09:03:11 +0100 Subject: [PATCH 29/43] allo providing precomputed psi_m to wind_profile --- src/stability_correction.jl | 32 +++++++++++++++++++++++++++----- src/surface_roughness.jl | 25 ++++++++++++------------- test/stability_correction.jl | 22 ++++++++++++++++++++++ test/surface_roughness.jl | 5 ++++- 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/stability_correction.jl b/src/stability_correction.jl index 75abcd2..217b1e6 100755 --- a/src/stability_correction.jl +++ b/src/stability_correction.jl @@ -106,16 +106,23 @@ function stability_parameter(df::AbstractDataFrame; zr,d, end """ + stability_correction(zeta; + stab_formulation=Val(:Dyer_1970)) + stability_correction(Tair,pressure,ustar,H, z,d; constants, + stab_formulation=Val(:Dyer_1970)) + Integrated Stability Correction Functions for Heat and Momentum -dimensionless stability functions needed to correct deviations -from the exponential wind profile under non-neutral conditions. - # Arguments - `zeta` : Stability parameter zeta (-) -- `stab_formulation` : Formulation for the stability function. Either `Val(:Dyer_1970)`, - or `Val(:Businger_1971)` +- `stab_formulation` : Formulation for the stability function. Either + `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or `Val(:no_stability_correction)` +In the alternative computes `zeta` by [`stability_parameter`](@ref) and +[`Monin_Obukhov_length`](@ref) and requires respective arguments. + # Details +These dimensionless values are needed to correct deviations +from the exponential wind profile under non-neutral conditions. The functions give the integrated form of the universal functions. They depend on the value of the stability parameter ``\\zeta``, which can be calculated from the function [`stability_parameter`](@ref). @@ -160,6 +167,8 @@ true """ function stability_correction(zeta; stab_formulation=Val(:Dyer_1970)) # integration of universal functions (after Paulson_1970 and Foken 2008) + stab_formulation isa Val{:no_stability_correction} && return( + (psi_h = zero(zeta), psi_m = zero(zeta))) ismissing(zeta) && return((psi_h = missing, psi_m = missing)) is_stable = zeta >= 0 if is_stable @@ -190,4 +199,17 @@ function get_stability_coefs_unstable(::Val{:Dyer_1970}, zeta) (;y_h, y_m) end +function stability_correction(Tair,pressure,ustar,H, z,d; + stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) + stab_formulation isa Val{:no_stability_correction} && return( + (psi_h = zero(z), psi_m = zero(z))) + MOL = Monin_Obukhov_length(Tair,pressure,ustar,H; constants) + zeta = stability_parameter(z,d,MOL) + stability_correction(zeta; stab_formulation) +end +function stability_correction!(df, z, d; + stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) + ft(args...) = stability_correction(args..., z, d; stab_formulation, constants) + transform!(df, SA[:Tair,:pressure,:ustar,:H] => ByRow(ft) => AsTable) +end diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 05974cf..33d2ad9 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -281,17 +281,17 @@ A vector of wind speed at heights `z`. # } ``` """ -function wind_profile(::Val{:no_stability_correction}, z, ustar, d, z0m; - constants=bigleaf_constants()) - wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m))) +function wind_profile(z, ustar, d, z0m, psi_m = 0.0; constants=bigleaf_constants()) + wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) end + function wind_profile(stab_formulation, z, ustar, Tair,pressure,H, d, z0m; constants=bigleaf_constants()) # in order to comput psi_m need Monin_Obukhov_length with additional vars MOL = Monin_Obukhov_length(Tair,pressure,ustar,H; constants) zeta = stability_parameter(z,d,MOL) psi_m = stability_correction(zeta; stab_formulation).psi_m - wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) + wind_profile(z, ustar, d, z0m, psi_m) end function wind_profile(df, z; @@ -320,18 +320,17 @@ function wind_profile(df, z; end - function wind_profile(df::AbstractDataFrame, z, d, z0m; stab_formulation = Val(:Dyer_1970), - constants = bigleaf_constants()) - inputs = get_stab_inputs(stab_formulation) - # do not use ByRow because z0m may be a vector - fspeed(args...) = wind_profile.(stab_formulation, z, args..., d, z0m; constants) - select(df, inputs => fspeed => :windz).windz + constants = bigleaf_constants()) + psi_m = stability_correction!( + copy(df, copycols=false), z, d; stab_formulation, constants).psi_m + wind_profile.(df, z, d, z0m, psi_m; constants) end -get_stab_inputs(::Val{:no_stability_correction}) = SA[:ustar] -get_stab_inputs(::Union{Val{:Dyer_1970},Val{:Businger_1971}}) = SA[:ustar, :Tair, :pressure, :H] - +function wind_profile(df::AbstractDataFrame, z, d, z0m, psi_m::AbstractVector, + constants = bigleaf_constants()) + wind_profile.(z, df.ustar, d, z0m, psi_m; constants) +end # function estimate_z0m( # Tair,pressure,wind,ustar,H # zh,zr,d) diff --git a/test/stability_correction.jl b/test/stability_correction.jl index 58fd99d..c62aaf7 100644 --- a/test/stability_correction.jl +++ b/test/stability_correction.jl @@ -36,4 +36,26 @@ end resm = stability_correction(missing) @test keys(resm) == (:psi_h, :psi_m) @test all(ismissing.(values(resm))) + # + resm = stability_correction(first(zeta); + stab_formulation = Val(:no_stability_correction)) + @test resm == (psi_h = 0.0, psi_m = 0.0) end + +@testset "stability_correction from raw" begin + datetime, ustar, Tair, pressure, H = values(tha48[24,1:5]) + z=40.0;d=15.0 + resm = stability_correction(Tair,pressure,ustar,H, z,d) + @test resm.psi_h ≈ 0.940 rtol=1/1000 + @test resm.psi_m ≈ 0.902 rtol=1/1000 + # + resm0 = stability_correction(Tair,pressure,ustar,H, z,d; + stab_formulation = Val(:no_stability_correction)) + @test resm0 == (psi_h = 0.0, psi_m = 0.0) + # + df = copy(tha48) + df.ustar[3] = missing + stability_correction!(df,z,d) + @test all(ismissing.((df.psi_h[3], df.psi_m[3]))) + @test all(isapprox.((df.psi_h[24], df.psi_m[24]),values(resm))) +end \ No newline at end of file diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl index 61bc0d4..3c8cf26 100644 --- a/test/surface_roughness.jl +++ b/test/surface_roughness.jl @@ -3,7 +3,7 @@ z = 30 d=0.7*tha_heights.zh z0m=2.65 - u30 = wind_profile(Val(:no_stability_correction), z, ustar, d, z0m) + u30 = wind_profile(z, ustar, d, z0m) @test ≈(u30, 1.93, rtol = 1/100 ) # u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) @@ -17,4 +17,7 @@ @test windzc[1] == u30c #plot(windz) #plot!(windz2) + psi_m = stability_correction!(copy(df, copycols=false), z, d).psi_m + windzc2 = wind_profile(df, z, d, z0m, psi_m) + @test windzc2 == windzc end \ No newline at end of file From c6d339becfee764d679f71efbb83059f851acf13 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 3 Nov 2021 11:55:02 +0100 Subject: [PATCH 30/43] estimate z0m in wind_profile --- inst/fromR/WUE_metrics.jl | 2 +- inst/fromR/aerodynamic_conductance.jl | 4 +- inst/fromR/bigleaf_physiology.jl | 32 +-- inst/fromR/energy_balance.jl | 4 +- inst/fromR/evapotranspiration.jl | 12 +- inst/fromR/filter_data.jl | 38 ++-- inst/fromR/surface_conductance.jl | 8 +- inst/fromR/surface_roughness.jl | 26 +-- inst/tha.jl | 22 +- inst/walkthrough_todo.jmd | 8 +- src/Bigleaf.jl | 3 +- src/aerodynamic_conductance.jl | 4 +- src/stability_correction.jl | 5 +- src/surface_roughness.jl | 304 ++++++++++++-------------- test/runtests.jl | 4 + test/surface_roughness.jl | 42 +++- 16 files changed, 258 insertions(+), 260 deletions(-) diff --git a/inst/fromR/WUE_metrics.jl b/inst/fromR/WUE_metrics.jl index 14bb638..6cb7ecc 100755 --- a/inst/fromR/WUE_metrics.jl +++ b/inst/fromR/WUE_metrics.jl @@ -67,7 +67,7 @@ #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index b9ca1af..505eeba 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -125,8 +125,8 @@ #' - Gb_h: Canopy boundary layer conductance for heat transfer (m s-1) #' - Rb_h: Canopy boundary layer resistance for heat transfer (s m-1) #' - kB_h: kB-1 parameter for heat transfer -#' - zeta: Stability parameter 'zeta' (NA if `wind_profile = false`) -#' - psi_h: Integrated stability correction function (NA if `wind_profile = false`) +#' - zeta: Stability parameter 'zeta' (missing if `wind_profile = false`) +#' - psi_h: Integrated stability correction function (missing if `wind_profile = false`) #' - Ra_CO2: Aerodynamic resistance for CO2 transfer (s m-1) #' - Ga_CO2: Aerodynamic conductance for CO2 transfer (m s-1) #' - Gb_CO2: Canopy boundary layer conductance for CO2 transfer (m s-1) diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index 9920681..8176ee4 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -13,7 +13,7 @@ #' - GPP Gross primary productivity (umol CO2 m-2 s-1) #' - Gs Surface conductance to water vapor (mol m-2 s-1) #' - Rleaf Ecosystem respiration stemming from leaves (umol CO2 m-2 s-1); defaults to 0 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `missing` (`true`) #' or set to 0 (`false`, the default)? #' - constants DwDc - Ratio of the molecular diffusivities for water vapor and CO2 (-) #' @@ -115,7 +115,7 @@ end #' - Jmax_dS Entropy term for Jmax (kJ mol-1 K-1) #' - Theta Curvature term in the light response function of J (-) #' - alpha_canopy Canopy absorptance (-) -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `missing` (`true`) #' or set to 0 (`false`, the default)? #' - Ci_C4 intercellular CO2 concentration below which photosynthesis #' is considered to be CO2-limited (umol mol-1), ignored @@ -262,7 +262,7 @@ end #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", @@ -309,12 +309,12 @@ function photosynthetic_capacity(data,C3=true,Temp,GPP="GPP",Ci,PPFD="PPFD",PPFD Ko = Ko * constants[:J2kJ] # basic filtering on Ci - Ci[Ci < 80 | ismissing(Ci)] = NA + Ci[Ci < 80 | ismissing(Ci)] = missing # Presumed limitation states GPPc = GPPj = GPP - GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD)] = NA - GPPc[PPFD < PPFD_c | ismissing(PPFD)] = NA + GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD)] = missing + GPPc[PPFD < PPFD_c | ismissing(PPFD)] = missing if(!isnothing(Rleaf)) if(!missing_Rleaf_as_NA){Rleaf[ismissing(Rleaf)] = 0 } @@ -332,8 +332,8 @@ else { # C4 vegetation # Presumed limitation states (C4) GPPc = GPPj = GPP - GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD) | Ci < 0] = NA - GPPc[PPFD < PPFD_c | Ci < Ci_C4 | ismissing(PPFD)] = NA + GPPj[PPFD < PPFD_j[1] | PPFD > PPFD_j[2] | ismissing(PPFD) | Ci < 0] = missing + GPPc[PPFD < PPFD_c | Ci < Ci_C4 | ismissing(PPFD)] = missing Vcmax = GPPc J = 3 * GPPj / (1 - 0.5) @@ -351,12 +351,12 @@ end 4.0 * Theta * APPFD_PSII[i] * Jmax)) / (2.0 * Theta)))}, interval=c(0,1000),tol=1e-02)$minimum, - error=function(err){NA} + error=function(err){missing} ) ) else @warn"Not enough observations to calculate Jmax!") - Jmax = NA + Jmax = missing end # calculate Vcmax25 and Jmax25 @@ -437,9 +437,9 @@ function Arrhenius_temp_response(param,Temp,Ha,Hd,dS,constants=bigleaf_constants Temp = Temp + constants[:Kelvin] Tref = 25.0 + constants[:Kelvin] - Ha = ifelse(missing(Ha),NA,Ha*constants[:kJ2J]) - Hd = ifelse(missing(Hd),NA,Hd*constants[:kJ2J]) - dS = ifelse(missing(dS),NA,dS*constants[:kJ2J]) + Ha = ifelse(missing(Ha),missing,Ha*constants[:kJ2J]) + Hd = ifelse(missing(Hd),missing,Hd*constants[:kJ2J]) + dS = ifelse(missing(dS),missing,dS*constants[:kJ2J]) if (ismissing(Ha)) @@ -500,7 +500,7 @@ end #' - constants Kelvin - conversion degree Celsius to Kelvin #' Rgas - universal gas constant (J mol-1 K-1) #' DwDc - Ratio of the molecular diffusivities for water vapor and CO2 -#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `NA` (`true`) +#' - missing_Rleaf_as_NA if Rleaf is provided, should missing values be treated as `missing` (`true`) #' or set to 0 (`false`, the default)? #' - ... Additional arguments to `\link[stats]{nls`} or `\link[robustbase]{nlrob`} if `robust_nls = true`. #' @@ -562,7 +562,7 @@ end #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", @@ -901,7 +901,7 @@ end #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","VPD"), #' filter_vals_min=c(5,200,0.2,0.3), -#' filter_vals_max=c(NA,NA,NA,NA), +#' filter_vals_max=c(missing,missing,missing,missing), #' NA_as_invalid=true,quality_ext="_qc", #' good_quality=c(0,1),missing_qc_as_bad=true, #' precip="precip",tprecip=0.1,precip_hours=24, diff --git a/inst/fromR/energy_balance.jl b/inst/fromR/energy_balance.jl index 8ad8cde..a2dcd05 100755 --- a/inst/fromR/energy_balance.jl +++ b/inst/fromR/energy_balance.jl @@ -105,8 +105,8 @@ end #' - instantaneous should the energy balance be calculated at the time step #' of the observations (`true`), or over the entire time period #' provided as input (`false`) -#' - missing_G_as_NA if `true`, missing G are treated as `NA`s ,otherwise set to 0. -#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `missing`s ,otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `missing`s, otherwise set to 0. #' #' #' # Details diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index 3782093..1b11bc6 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -19,8 +19,8 @@ #' - alpha Priestley-Taylor coefficient; only used if `approach = Val(:PriestleyTaylor)`. #' - Gs_pot Potential/maximum surface conductance (mol m-2 s-1); defaults to 0.6 mol m-2 s-1; #' only used if `approach = Val(:PenmanMonteith)`. -#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `missing`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `missing`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -163,8 +163,8 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `missing`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `missing`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). @@ -200,8 +200,8 @@ end #' - Rn Net radiation (W m-2) #' - G Ground heat flux (W m-2); optional #' - S Sum of all storage fluxes (W m-2); optional -#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise set to 0. -#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `missing`s, otherwise set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `missing`s, otherwise set to 0. #' - Esat_formula Optional: formula to be used for the calculation of esat and the slope of esat. #' One of `"Sonntag_1990"` (Default), `"Alduchov_1996"`, or `"Allen_1998"`. #' See [`Esat_slope`](@ref). diff --git a/inst/fromR/filter_data.jl b/inst/fromR/filter_data.jl index 8b0256c..f29c012 100755 --- a/inst/fromR/filter_data.jl +++ b/inst/fromR/filter_data.jl @@ -14,9 +14,9 @@ #' - filter_precip Should precipitation filtering be applied? Defaults to `false`. #' - filter_vars Additional variables to be filtered. Vector of type character. #' - filter_vals_min Minimum values of the variables to be filtered. Numeric vector of -#' the same length than `filter_vars`. Set to `NA` to be ignored. +#' the same length than `filter_vars`. Set to `missing` to be ignored. #' - filter_vals_max Maximum values of the variables to be filtered. Numeric vector of -#' the same length than `filter_vars`. Set to `NA` to be ignored. +#' the same length than `filter_vars`. Set to `missing` to be ignored. #' - NA_as_invalid If `true` (the default) missing data are filtered out (applies to all variables). #' - vars_qc Character vector indicating the variables for which quality filter should #' be applied. Ignored if `quality_control = false`. @@ -24,7 +24,7 @@ #' quality control variables. Ignored if `quality_control = false`. #' - good_quality Which values indicate good quality (i.e. not to be filtered) #' in the quality control (qc) variables? Ignored if `quality_control = false`. -#' - missing_qc_as_bad If quality control variable is `NA`, should the corresponding data point be +#' - missing_qc_as_bad If quality control variable is `missing`, should the corresponding data point be #' treated as bad quality? Defaults to `true`. Ignored if `quality_control = false`. #' - precip Precipitation (mm time-1) #' - GPP Gross primary productivity (umol m-2 s-1); Ignored if `filter_growseas = false`. @@ -42,7 +42,7 @@ #' Ignored if `filter_precip = false`. #' - records_per_hour Number of observations per hour. I_e. 2 for half-hourly data. #' - filtered_data_to_NA Logical. If `true` (the default), all variables in the input -#' DataFrame/matrix are set to `NA` for the time step where ANY of the +#' DataFrame/matrix are set to `missing` for the time step where ANY of the #' `filter_vars` were beyond their acceptable range (as #' determined by `filter_vals_min` and `filter_vals_max`). #' If `false`, values are not filtered, and an additional column 'valid' @@ -58,7 +58,7 @@ #' the same name as the variable plus the extension as specified in `quality_ext` #' must be provided. For time steps where the value of the quality indicator is not included #' in the argument `good_quality`, i.e. the quality is not considered as 'good', -#' its value is set to `NA`. +#' its value is set to `missing`. #' #' 2) Meteorological filtering. Under certain conditions (e.g. low ustar), the assumptions #' of the EC method are not fulfilled. Further, some data analysis require certain meteorological @@ -66,11 +66,11 @@ #' The filter applied in this second step serves to exclude time periods that do not fulfill the criteria #' specified in the arguments. More specifically, time periods where one of the variables is higher #' or lower than the specified thresholds (`filter_vals_min` and `filter_vals_max`) -#' are set to `NA` for all variables. If a threshold is set to `NA`, it will be ignored. +#' are set to `missing` for all variables. If a threshold is set to `missing`, it will be ignored. #' #' # Value If `filtered_data_to_NA = true` (default), the input DataFrame/matrix with -#' observations which did not fulfill the filter criteria set to `NA`. +#' observations which did not fulfill the filter criteria set to `missing`. #' If `filtered_data_to_NA = false`, the input DataFrame/matrix with an additional #' column "valid", which indicates whether all the data of a time step fulfill the #' filtering criteria (1) or not (0). @@ -82,9 +82,9 @@ #' `tprecip` filters all data that are greater than `tprecip`. #' #' Variables considered of bad quality (as specified by the corresponding quality control variables) -#' will be set to `NA` by this routine. Data that do not fulfill the filtering criteria are set to -#' `NA` if `filtered_data_to_NA = true`. Note that with this option *all* variables of the same -#' time step are set to `NA`. Alternatively, if `filtered_data_to_NA = false` data are not set to `NA`, +#' will be set to `missing` by this routine. Data that do not fulfill the filtering criteria are set to +#' `missing` if `filtered_data_to_NA = true`. Note that with this option *all* variables of the same +#' time step are set to `missing`. Alternatively, if `filtered_data_to_NA = false` data are not set to `missing`, #' and a new column "valid" is added to the DataFrame/matrix, indicating if any value of a row #' did (1) or did not fulfill the filter criteria (0). #' @@ -93,14 +93,14 @@ #' ``` #' # Example of data filtering; data are for a month within the growing season, #' # hence growing season is not filtered. -#' # If filtered_data_to_NA=true, all values of a row are set to NA if one filter +#' # If filtered_data_to_NA=true, all values of a row are set to missing if one filter #' # variable is beyond its bounds. #' DE_Tha_Jun_2014_2 = filter_data(DE_Tha_Jun_2014,quality_control=false, #' vars_qc=c("Tair","precip","H","LE"), #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", @@ -113,7 +113,7 @@ #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar"), #' filter_vals_min=c(5,200,0.2), -#' filter_vals_max=c(NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", @@ -122,7 +122,7 @@ #' #' # note the additional column 'valid' in DE_Tha_Jun_2014_3. #' # To remove time steps marked as filtered out (i.e. 0 values in column 'valid'): -#' DE_Tha_Jun_2014_3[DE_Tha_Jun_2014_3["valid"] == 0,] = NA +#' DE_Tha_Jun_2014_3[DE_Tha_Jun_2014_3["valid"] == 0,] = missing #' #' #' @importFrom stats aggregate @@ -167,16 +167,16 @@ end check_input(data,var_qc) if (missing_qc_as_bad) - data[get(paste0(var,quality_ext)) > max(good_quality) | ismissing(get(paste0(var,quality_ext))),var] = NA # exclude bad quality data or those where qc flag is not available + data[get(paste0(var,quality_ext)) > max(good_quality) | ismissing(get(paste0(var,quality_ext))),var] = missing # exclude bad quality data or those where qc flag is not available qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) | ismissing(get(paste0(var,quality_ext)))) # count & report else { # same, but consider missing quality flag variables as good - data[get(paste0(var,quality_ext)) > max(good_quality) & !ismissing(get(paste0(var,quality_ext))),var] = NA + data[get(paste0(var,quality_ext)) > max(good_quality) & !ismissing(get(paste0(var,quality_ext))),var] = missing qc_invalid = sum(get(paste0(var,quality_ext)) > max(good_quality) & !ismissing(get(paste0(var,quality_ext)))) end qc_invalid_perc = round((qc_invalid/nrow(data))*constants[:frac2percent],2) - cat(var,": ",qc_invalid," data points (",qc_invalid_perc,"%) set to NA",fill=true,sep="") + cat(var,": ",qc_invalid," data points (",qc_invalid_perc,"%) set to missing",fill=true,sep="") end end @@ -259,10 +259,10 @@ end cat(nrow(data) - length(invalid)," valid data points (",constants[:frac2percent]-excl_perc,"%) remaining.",fill=true,sep="") - # 6) return input data frame with filtered time steps set to NA or an additional 'valid' column + # 6) return input data frame with filtered time steps set to missing or an additional 'valid' column if (filtered_data_to_NA) data_filtered = data - data_filtered[valid < 1,] = NA + data_filtered[valid < 1,] = missing else data_filtered = DataFrame(data,valid) end diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 2a69fd6..2f45bc6 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -16,9 +16,9 @@ #' - LE Latent heat flux (W m-2) #' - VPD Vapor pressure deficit (kPa) #' - Ga Aerodynamic conductance to heat/water vapor (m s-1) -#' - missing_G_as_NA if `true`, missing G are treated as `NA`s, otherwise they are set to 0. +#' - missing_G_as_NA if `true`, missing G are treated as `missing`s, otherwise they are set to 0. #' Only used if `formulation = Val(:PenmanMonteith)`. -#' - missing_S_as_NA if `true`, missing S are treated as `NA`s, otherwise they are set to 0. +#' - missing_S_as_NA if `true`, missing S are treated as `missing`s, otherwise they are set to 0. #' Only used if `formulation = Val(:PenmanMonteith)`. #' - formulation Formulation used. Either `Val(:PenmanMonteith)` (the default) #' using the inverted Penman-Monteith equation, or `"Flux-Gradient"`, @@ -46,7 +46,7 @@ #' Available energy (A) is defined as A = Rn - G - S. If G and/or S are not provided, A = Rn. #' #' By default, any missing data in G and S are set to 0. If `missing_S_as_NA = true` -#' or `missing_S_as_NA = true`, Gs will give `NA` for these timesteps. +#' or `missing_S_as_NA = true`, Gs will give `missing` for these timesteps. #' #' If `formulation="Flux-Gradient"`, Gs (in mol m-2 s-1) is calculated from VPD and ET only: #' @@ -76,7 +76,7 @@ #' filter_growseas=false,filter_precip=true, #' filter_vars=c("Tair","PPFD","ustar","LE"), #' filter_vals_min=c(5,200,0.2,0), -#' filter_vals_max=c(NA,NA,NA,NA),NA_as_invalid=true, +#' filter_vals_max=c(missing,missing,missing,missing),NA_as_invalid=true, #' quality_ext="_qc",good_quality=c(0,1), #' missing_qc_as_bad=true,GPP="GPP",doy="doy", #' year="year",tGPP=0.5,ws=15,min_int=5,precip="precip", diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index ba4e3a9..74198a8 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -49,9 +49,9 @@ end #' A simple approximation of the two roughness parameters displacement height (d) #' and roughness length for momentum (z0m). #' -#' - method Method to use, one of `"canopy_height","canopy_height&LAI",Val(:wind_profile)` +#' - method Method to use, one of `"canopy_height","canopy_height_LAI",Val(:wind_profile)` #' NOTE: if `method = "canopy_height"`, only the following three arguments -#' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, +#' are used. If `method = "canopy_height_LAI"`, only `zh, LAI, cd`, #' and `hs` are required. #' - zh Vegetation height (m) #' - frac_d Fraction of displacement height on canopy height (-) @@ -59,8 +59,8 @@ end #' - LAI Leaf area index (-) #' - zr Instrument (reference) height (m) #' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. -#' Only needed if `method = "canopy_height&LAI"`. -#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` +#' Only needed if `method = "canopy_height_LAI"`. +#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height_LAI"` #' The following arguments are only needed if `method = Val(:wind_profile)`! #' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) @@ -93,7 +93,7 @@ end #' where frac_d defaults to 0.7 and frac_z0m to 0.1. #' #' Alternatively, d and z0m can be estimated from both canopy height and LAI -#' (If `method = "canopy_height&LAI"`). +#' (If `method = "canopy_height_LAI"`). #' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed #' the following semi-empirical relations: #' @@ -132,8 +132,8 @@ end #' ```@example; output = false #' ``` #' # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy -#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=5) -#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=2) +#' roughness_parameters(method="canopy_height_LAI",zh=25,LAI=5) +#' roughness_parameters(method="canopy_height_LAI",zh=25,LAI=2) #' #' # fix d to 0.7*zh and estimate z0m from the wind profile #' df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) @@ -144,7 +144,7 @@ end #' #' @importFrom stats median sd complete_cases #' @export -function roughness_parameters(method=c("canopy_height","canopy_height&LAI",Val(:wind_profile)),zh, +function roughness_parameters(method=c("canopy_height","canopy_height_LAI",Val(:wind_profile)),zh, frac_d=0.7,frac_z0m=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", wind="wind",ustar="ustar",H="H",d=nothing,z0m=nothing, stab_roughness=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), @@ -157,9 +157,9 @@ function roughness_parameters(method=c("canopy_height","canopy_height&LAI",Val(: d = frac_d*zh z0m = frac_z0m*zh - z0m_se = NA + z0m_se = missing -elseif (method == "canopy_height&LAI") +elseif (method == "canopy_height_LAI") X = cd * LAI d = 1.1 * zh * log(1 + X^(1/4)) @@ -169,7 +169,7 @@ elseif (method == "canopy_height&LAI") else z0m = 0.3 * zh * (1 - d/zh) end - z0m_se = NA + z0m_se = missing elseif (method == Val(:wind_profile)) @@ -194,7 +194,7 @@ else end - z0m_all[z0m_all > zh] = NA + z0m_all[z0m_all > zh] = missing z0m = median(z0m_all,na_rm=true) z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=true) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) @@ -274,7 +274,7 @@ end #' ``` #' heights = seq(18,40,2) # heights above ground for which to calculate wind speed #' df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) -#' ws = DataFrame(matrix(NA,ncol=length(heights),nrow=nrow(df))) +#' ws = DataFrame(matrix(missing,ncol=length(heights),nrow=nrow(df))) #' colnames(ws) = paste0(heights,"m") #' for (i in seq_along(heights)) #' ws[,i] = wind_profile(df,z=heights[i],zr=40,zh=25,d=16) diff --git a/inst/tha.jl b/inst/tha.jl index 8ab1980..f7ab178 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -32,28 +32,10 @@ setinvalid_nongrowingseason!(thaf, 0.4) setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) -plot(first(tha, 48).ustar) +# tha48 and tha_heights see runtests.jl -tha48 = DataFrame( - datetime = ZonedDateTime("2014-06-01T00:00:00+00:00","yyyy-mm-ddTHH:MM:SSzzzz") .+ - (0:47) .* Minute(30), - ustar = Union{Missing, Float64}[0.5400000214576721, 0.4900000095367432, 0.4799999892711639, 0.449999988079071, 0.5099999904632568, 0.4600000083446503, 0.5400000214576721, 0.5199999809265137, 0.4600000083446503, 0.4099999964237213, 0.5299999713897705, 0.5099999904632568, 0.5199999809265137, 0.4099999964237213, 0.5400000214576721, 0.4600000083446503, 0.5099999904632568, 0.5, 0.6299999952316284, 0.5799999833106995, 0.6800000071525574, 0.6499999761581421, 0.7400000095367432, 0.7300000190734863, 0.7699999809265137, 0.7400000095367432, 0.7799999713897705, 0.8700000047683716, 0.7799999713897705, 0.6700000166893005, 0.5600000023841858, 0.6600000262260437, 0.7099999785423279, 0.6100000143051147, 0.5899999737739563, 0.6100000143051147, 0.5299999713897705, 0.449999988079071, 0.3400000035762787, 0.3300000131130219, 0.3300000131130219, 0.3700000047683716, 0.3799999952316284, 0.3799999952316284, 0.3600000143051147, 0.3700000047683716, 0.3600000143051147, 0.3400000035762787], - Tair = [11.88000011444092, 11.67000007629395, 11.1899995803833, 10.80000019073486, 10.67000007629395, 10.13000011444092, 9.779999732971191, 9.5, 9.09000015258789, 8.800000190734863, 8.6899995803833, 8.899999618530273, 9.430000305175781, 9.710000038146973, 10.55000019073486, 11.19999980926514, 11.72999954223633, 12.68000030517578, 13.1899995803833, 13.46000003814697, 14.1899995803833, 14.73999977111816, 14.65999984741211, 14.8100004196167, 15.02999973297119, 14.98999977111816, 14.77999973297119, 15.35000038146973, 15.5, 15.61999988555908, 15.47000026702881, 16.20000076293945, 16.13999938964844, 15.56999969482422, 15.14999961853027, 15.18000030517578, 14.81999969482422, 14.60000038146973, 14.10000038146973, 13.23999977111816, 12.65999984741211, 12.46000003814697, 12.09000015258789, 11.85999965667725, 11.72000026702881, 11.64000034332275, 11.52999973297119, 11.43000030517578], - pressure = [97.63999938964844, 97.62999725341797, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.62999725341797, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.69000244140625, 97.69999694824219, 97.70999908447266, 97.69999694824219, 97.7300033569336, 97.72000122070312, 97.70999908447266, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.70999908447266, 97.70999908447266, 97.72000122070312, 97.70999908447266, 97.70999908447266, 97.69999694824219, 97.68000030517578, 97.68000030517578, 97.66999816894531, 97.68000030517578, 97.66999816894531, 97.66999816894531, 97.66000366210938, 97.6500015258789, 97.63999938964844, 97.63999938964844, 97.6500015258789, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.68000030517578, 97.68000030517578, 97.69000244140625, 97.69000244140625, 97.69000244140625], - H = Union{Missing, Float64}[-68.18000030517578, -48.54000091552734, -59.09999847412109, -60.11000061035156, -59.40000152587891, -48.91999816894531, -53.52999877929688, -40.75, -29.28000068664551, -33.36999893188477, -9.050000190734863, 2.400000095367432, 38.29999923706055, 38.45000076293945, 85.08000183105469, 108.7799987792969, 176.6799926757812, 210.6900024414062, 230.0800018310547, 227.4199981689453, 321.6499938964844, 358.8999938964844, 329.9299926757812, 317.3099975585938, 375.1900024414062, 336.489990234375, 260.7099914550781, 360.6400146484375, 264.2200012207031, 185.3200073242188, 123.7699966430664, 236.0200042724609, 207.5200042724609, 128.3399963378906, 66.05999755859375, 79.6500015258789, 44.93999862670898, 16.22999954223633, -22.35000038146973, -29.10000038146973, -47.79999923706055, -63.61999893188477, -61.38000106811523, -65.11000061035156, -59.77000045776367, -60.52000045776367, -49.02999877929688, -53.45000076293945], -) -tha_heights = ( - LAI = 7.6, # leaf area index - zh = 26.5, # average vegetation height (m) - zr = 42, # sensor height (m) - Dl = 0.01, # leaf characteristic dimension (m) -) - - - - -show(thaf.H[1:48]) +show(thaf.wind[1:48]) dfGPPd = @pipe tha |> transform(_, :datetime => ByRow(yearmonthday) => :ymd, copycols = false) |> diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 5b82f04..c1c5555 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -137,7 +137,7 @@ sum(.!thaf.valid) # some more invalids ``` -When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to NA. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. +When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to missing. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. @@ -455,7 +455,7 @@ G1_bootstrap = function(dat,LoopNum,SampSizeRel) # dat = input DataFrame # LoopNum = number of iterations # SampSizeRel = fraction of data sampled for each iteration - dfout=DataFrame(matrix(NA,nrow = LoopNum,ncol = 0)) #Define output dataframe + dfout=DataFrame(matrix(missing,nrow = LoopNum,ncol = 0)) #Define output dataframe dat$RunNum=1:nrow(dat) SampSize=round(length(dat$RunNum)*SampSizeRel) #calculate number of data used for resampling @@ -552,7 +552,7 @@ plot(Ga_mean[ind],type="l",lwd=2,xlab="timestep",ylab=expression("G"["ah"]~"(m s ok = which(!ismissing(Ga_mean[ind])) polygon(c(ok,rev(ok)),c(Ga_high[ind][ok],rev(Ga_low[ind][ok])), - col="grey70",border=NA) + col="grey70",border=missing) points(Ga_mean[ind],type="l",lwd=2) @@ -561,7 +561,7 @@ plot(Gs_mean[ind],type="l",lwd=2,xlab="timestep",tcl=-0.2, ok = which(!ismissing(Gs_mean[ind])) polygon(c(ok,rev(ok)),c(Gs_high[ind][ok],rev(Gs_low[ind][ok])), - col="grey70",border=NA) + col="grey70",border=missing) points(Gs_mean[ind],type="l",lwd=2) ``` diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 9b4f68a..9e073de 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -32,7 +32,8 @@ export decoupling, surface_conductance, aerodynamic_conductance export compute_Gb, compute_Gb!, add_Gb, Gb_Thom export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, - stability_parameter!, stability_correction, stability_correction! + stability_parameter!, stability_correction, stability_correction!, + roughness_parameters include("util.jl") include("bigleaf_constants.jl") diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index a2a067b..abc79a0 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -133,8 +133,8 @@ a dataframe with the following columns: - `Gb_h`: Canopy boundary layer conductance for heat transfer (m s-1) - `Rb_h`: Canopy boundary layer resistance for heat transfer (s m-1) - `kB_h`: kB-1 parameter for heat transfer -- `zeta`: Stability parameter 'zeta' (NA if `wind_profile = false`) -- `psi_h`: Integrated stability correction function (NA if `wind_profile = false`) +- `zeta`: Stability parameter 'zeta' (missing if `wind_profile = false`) +- `psi_h`: Integrated stability correction function (missing if `wind_profile = false`) - `Ra_CO2`: Aerodynamic resistance for CO2 transfer (s m-1) - `Ga_CO2`: Aerodynamic conductance for CO2 transfer (m s-1) - `Gb_CO2`: Canopy boundary layer conductance for CO2 transfer (m s-1) diff --git a/src/stability_correction.jl b/src/stability_correction.jl index 217b1e6..a214ee6 100755 --- a/src/stability_correction.jl +++ b/src/stability_correction.jl @@ -117,7 +117,7 @@ Integrated Stability Correction Functions for Heat and Momentum - `zeta` : Stability parameter zeta (-) - `stab_formulation` : Formulation for the stability function. Either `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or `Val(:no_stability_correction)` -In the alternative computes `zeta` by [`stability_parameter`](@ref) and +In the alternative form computes `zeta` by [`stability_parameter`](@ref) and [`Monin_Obukhov_length`](@ref) and requires respective arguments. # Details @@ -125,6 +125,7 @@ These dimensionless values are needed to correct deviations from the exponential wind profile under non-neutral conditions. The functions give the integrated form of the universal functions. They depend on the value of the stability parameter ``\\zeta``, +a function of heigh `z`, which can be calculated from the function [`stability_parameter`](@ref). The integration of the universal functions is: @@ -132,7 +133,7 @@ The integration of the universal functions is: for stable atmospheric conditions (``\\zeta`` >= 0), and -``\\psi = 2 * log( (1 + y) / 2)`` +``\\psi = 2 * log( (1 + y(zeta)) / 2)`` for unstable atmospheric conditions (``\\zeta`` < 0). diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 33d2ad9..55447f7 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -40,168 +40,135 @@ -#' Roughness Parameters -#' -#' A simple approximation of the two roughness parameters displacement height (d) -#' and roughness length for momentum (z0m). -#' -#' - method Method to use, one of `"canopy_height","canopy_height&LAI",Val(:wind_profile)` -#' NOTE: if `method = "canopy_height"`, only the following three arguments -#' are used. If `method = "canopy_height&LAI"`, only `zh, LAI, cd`, -#' and `hs` are required. -#' - zh Vegetation height (m) -#' - frac_d Fraction of displacement height on canopy height (-) -#' - frac_z0m Fraction of roughness length on canopy height (-) -#' - LAI Leaf area index (-) -#' - zr Instrument (reference) height (m) -#' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. -#' Only needed if `method = "canopy_height&LAI"`. -#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height&LAI"` -#' The following arguments are only needed if `method = Val(:wind_profile)`! -#' - data DataFrame or matrix containing all required variables -#' - Tair Air temperature (deg C) -#' - pressure Atmospheric pressure (kPa) -#' - wind Wind speed at height zr (m s-1) -#' - ustar Friction velocity (m s-1) -#' - H Sensible heat flux (W m-2) -#' - d Zero-plane displacement height (m); optional -#' - z0m Roughness length for momentum (m); optional -#' - stab_roughness Should stability correction be considered? Default is `true`. -#' - stab_formulation Stability correction function used (If `stab_correction = true`). -#' Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. -#' - constants k - von-Karman constant (-) -#' Kelvin - conversion degree Celsius to Kelvin -#' cp - specific heat of air for constant pressure (J K-1 kg-1) -#' g - gravitational acceleration (m s-2) -#' se_median - conversion standard error (SE) of the mean to SE of the median -#' -#' -#' # Details -#' The two main roughness parameters, the displacement height (d) -#' and the roughness length for momentum (z0m) can be estimated from simple -#' empirical relationships with canopy height (zh). If `method = "canopy_height"`, -#' the following formulas are used: -#' -#' ``d = frac_d * zh`` -#' -#' ``z0m = frac_z0m * zh`` -#' -#' where frac_d defaults to 0.7 and frac_z0m to 0.1. -#' -#' Alternatively, d and z0m can be estimated from both canopy height and LAI -#' (If `method = "canopy_height&LAI"`). -#' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed -#' the following semi-empirical relations: -#' -#' ``X = cd * LAI`` -#' -#' ``d = 1.1 * zh * ln(1 + X^(1/4))`` -#' -#' ``z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2`` -#' -#' ``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` -#' -#' If `method = Val(:wind_profile)`, z0m is estimated by solving -#' the wind speed profile for z0m: -#' -#' ``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` -#' -#' By default, d in this equation is fixed to 0.7*zh, but can be set to any -#' other value. psi_m is 0 if `stab_roughness = false`. -#' -#' # Value -#' a DataFrame with the following columns: -#' - d: Zero-plane displacement height (m) -#' - z0m: Roughness length for momentum (m) -#' - z0m_se: Only if `method = wind_profile`: Standard Error of the median for z0m (m) -#' -#' #References -#' Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat -#' budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. -#' -#' Shaw, R. H., Pereira, A., 1982: Aerodynamic roughness of a plant canopy: -#' a numerical experiment. Agricultural Meteorology, 26, 51-65. -#' -#' #See also -#' [`wind_profile`](@ref) -#' -#' ```@example; output = false -#' ``` -#' # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy -#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=5) -#' roughness_parameters(method="canopy_height&LAI",zh=25,LAI=2) -#' -#' # fix d to 0.7*zh and estimate z0m from the wind profile -#' df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) -#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.7,data=df) -#' -#' # assume d = 0.8*zh -#' roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.8,data=df) -#' -#' @importFrom stats median sd complete_cases -#' @export -# function roughness_parameters(method=c("canopy_height","canopy_height&LAI",Val(:wind_profile)),zh, -# frac_d=0.7,frac_z0m=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", -# wind="wind",ustar="ustar",H="H",d=nothing,z0m=nothing, -# stab_roughness=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), -# constants=bigleaf_constants()) - -# method = match_arg(method) -# stab_formulation = match_arg(stab_formulation) - -# if (method == "canopy_height") - -# d = frac_d*zh -# z0m = frac_z0m*zh -# z0m_se = NA - -# elseif (method == "canopy_height&LAI") - -# X = cd * LAI -# d = 1.1 * zh * log(1 + X^(1/4)) - -# if (X >= 0 & X <= 0.2) -# z0m = hs + 0.3 * X^(1/2) -# else -# z0m = 0.3 * zh * (1 - d/zh) -# end -# z0m_se = NA - -# elseif (method == Val(:wind_profile)) - -# check_input(data,Tair,pressure,wind,ustar,H) - -# if (isnothing(d)) - -# d = frac_d * zh - -# end - -# if (stab_roughness) - -# zeta = stability_parameter(data=data,Tair=Tair,pressure=pressure,ustar=ustar,H=H, -# zr=zr,d=d,constants=constants) -# psi_m = stability_correction(zeta,formulation=stab_formulation)[,"psi_m"] -# z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar - psi_m) - -# else - -# z0m_all = (zr - d) * exp(-constants[:k]*wind / ustar) - -# end - -# z0m_all[z0m_all > zh] = NA - -# z0m = median(z0m_all,na_rm=true) -# z0m_se = constants[:se_median] * (sd(z0m_all,na_rm=true) / sqrt(length(z0m_all[complete_cases(z0m_all)]))) +""" +Roughness Parameters + +A simple approximation of the two roughness parameters displacement height (d) +and roughness length for momentum (z0m). + +# Arguments +- method Method to use, one of `"canopy_height","canopy_height_LAI",Val(:wind_profile)` + NOTE: if `method = "canopy_height"`, only the following three arguments + are used. If `method = "canopy_height_LAI"`, only `zh, LAI, cd`, + and `hs` are required. +- zh Vegetation height (m) +- frac_d Fraction of displacement height on canopy height (-) +- frac_z0m Fraction of roughness length on canopy height (-) +- LAI Leaf area index (-) +- zr Instrument (reference) height (m) +- cd Mean drag coefficient for individual leaves. Defaults to 0.2. + Only needed if `method = "canopy_height_LAI"`. +- hs roughness length of the soil surface (m). Only needed if `method = "canopy_height_LAI"` + The following arguments are only needed if `method = Val(:wind_profile)`! +- data DataFrame or matrix containing all required variables +- Tair Air temperature (deg C) +- pressure Atmospheric pressure (kPa) +- wind Wind speed at height zr (m s-1) +- ustar Friction velocity (m s-1) +- H Sensible heat flux (W m-2) +- d Zero-plane displacement height (m); optional +- z0m Roughness length for momentum (m); optional +- stab_roughness Should stability correction be considered? Default is `true`. +- stab_formulation Stability correction function used (If `stab_correction = true`). + Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. +- constants k - von-Karman constant (-) + Kelvin - conversion degree Celsius to Kelvin + cp - specific heat of air for constant pressure (J K-1 kg-1) + g - gravitational acceleration (m s-2) + se_median - conversion standard error (SE) of the mean to SE of the median + + +# Details +The two main roughness parameters, the displacement height (d) +and the roughness length for momentum (z0m) can be estimated from simple +empirical relationships with canopy height (zh). If `method = "canopy_height"`, +the following formulas are used: + +``d = frac_d * zh`` + +``z0m = frac_z0m * zh`` + +where frac_d defaults to 0.7 and frac_z0m to 0.1. + +Alternatively, d and z0m can be estimated from both canopy height and LAI +(If `method = "canopy_height_LAI"`). +Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed +the following semi-empirical relations: + +``X = cd * LAI`` + +``d = 1.1 * zh * ln(1 + X^(1/4))`` + +``z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2`` + +``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` + +If `method = Val(:wind_profile)`, z0m is estimated by solving +the wind speed profile for z0m: + +``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` + +By default, d in this equation is fixed to 0.7*zh, but can be set to any +other value. psi_m is 0 if `stab_roughness = false`. + +# Value +a NamedTuple with the following components: +- d: Zero-plane displacement height (m) +- z0m: Roughness length for momentum (m) +- z0m_se: Only if `method = wind_profile`: Standard Error of the median for z0m (m) + +# References +- Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat + budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. +- Shaw, R. H., Pereira, A., 1982: Aerodynamic roughness of a plant canopy: + a numerical experiment. Agricultural Meteorology, 26, 51-65. + +# See also +[`wind_profile`](@ref) -# end - -# return(DataFrame(d,z0m,z0m_se)) -# end +```@example; output = false +# # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy +# roughness_parameters(method="canopy_height_LAI",zh=25,LAI=5) +# roughness_parameters(method="canopy_height_LAI",zh=25,LAI=2) + +# # fix d to 0.7*zh and estimate z0m from the wind profile +# df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) +# roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.7,data=df) +# # assume d = 0.8*zh +# roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.8,data=df) +``` +""" +function roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_z0m=0.1) + d = frac_d*zh + z0m = frac_z0m*zh + z0m_se = missing + (;d, z0m, z0m_se) +end +function roughness_parameters(::Val{:canopy_height_LAI}, zh, LAI; cd=0.2, hs=0.01) + X = cd * LAI + d = 1.1 * zh * log(1 + X^(1/4)) + z0m = ifelse(0 <= X <= 0.2, hs + 0.3 * X^(1/2), 0.3 * zh * (1 - d/zh)) + z0m_se = missing + (;d, z0m, z0m_se) +end +# docu: supply psi_m = 0 for no stability correction, default method +function roughness_parameters(::Val{:wind_profile}, df, zh, zr; + d = 0.7*zh, psi_m = nothing, constants=bigleaf_constants() + ) + if isnothing(psi_m) + psi_m = stability_correction!(copy(df, copycols=false), zr, d; constants).psi_m + end + z0m_all = allowmissing(@. (zr - d) * exp(-constants[:k]*df.wind / df.ustar - psi_m)) + z0m_all[(z0m_all .> zh)] .= missing + nval = sum(.!ismissing.(z0m_all)) + z0m = median(skipmissing(z0m_all)) + z0m_se = constants[:se_median] * (std(skipmissing(z0m_all)) / sqrt(nval)) + (;d, z0m, z0m_se) +end + """ Wind Speed at Given Heights in the Surface Layer @@ -274,14 +241,14 @@ A vector of wind speed at heights `z`. ```@example; output = false # heights = seq(18,40,2) # heights above ground for which to calculate wind speed # df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) -# ws = DataFrame(matrix(NA,ncol=length(heights),nrow=nrow(df))) +# ws = DataFrame(matrix(missing,ncol=length(heights),nrow=nrow(df))) # colnames(ws) = paste0(heights,"m") # for (i in seq_along(heights)) # ws[,i] = wind_profile(df,z=heights[i],zr=40,zh=25,d=16) # } ``` """ -function wind_profile(z, ustar, d, z0m, psi_m = 0.0; constants=bigleaf_constants()) +function wind_profile(z::Number, ustar, d, z0m, psi_m = zero(z); constants=bigleaf_constants()) wind_heights = max(0,(ustar / constants[:k]) * (log(max(0,(z - d)) / z0m) - psi_m)) end @@ -319,16 +286,23 @@ function wind_profile(df, z; wind_profile_(df, z, d, z0m; stab_formulation, constants) end - -function wind_profile(df::AbstractDataFrame, z, d, z0m; stab_formulation = Val(:Dyer_1970), - constants = bigleaf_constants()) +function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; + zh = nothing, zr = nothing, + stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants()) psi_m = stability_correction!( copy(df, copycols=false), z, d; stab_formulation, constants).psi_m - wind_profile.(df, z, d, z0m, psi_m; constants) + if isnothing(z0m) + (isnothing(zh) || isnothing(zr)) && error( + "wind_profile: If z0m is not given, must specify both zh and zr optional arguments.") + z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = psi_m).z0m + @show z0m + end + wind_profile(df, z, d, z0m, psi_m; constants) end -function wind_profile(df::AbstractDataFrame, z, d, z0m, psi_m::AbstractVector, +function wind_profile(df::AbstractDataFrame, z, d, z0m, psi_m::AbstractVector; constants = bigleaf_constants()) + # when psi_m is given, df is not used any more, but keep for consistency wind_profile.(z, df.ustar, d, z0m, psi_m; constants) end # function estimate_z0m( diff --git a/test/runtests.jl b/test/runtests.jl index 49f4150..aec575c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,9 @@ using Pipe, DataFrames, Dates, TimeZones using StaticArrays using Statistics, StatsBase +isapproxm(args...; kwargs...) = isapprox(args...; kwargs...); +isapproxm(::Missing, ::Missing; kwargs...) = true + tha48 = DataFrame( datetime = ZonedDateTime("2014-06-01T00:00:00+00:00","yyyy-mm-ddTHH:MM:SSzzzz") .+ (0:47) .* Minute(30), @@ -11,6 +14,7 @@ tha48 = DataFrame( Tair = [11.88000011444092, 11.67000007629395, 11.1899995803833, 10.80000019073486, 10.67000007629395, 10.13000011444092, 9.779999732971191, 9.5, 9.09000015258789, 8.800000190734863, 8.6899995803833, 8.899999618530273, 9.430000305175781, 9.710000038146973, 10.55000019073486, 11.19999980926514, 11.72999954223633, 12.68000030517578, 13.1899995803833, 13.46000003814697, 14.1899995803833, 14.73999977111816, 14.65999984741211, 14.8100004196167, 15.02999973297119, 14.98999977111816, 14.77999973297119, 15.35000038146973, 15.5, 15.61999988555908, 15.47000026702881, 16.20000076293945, 16.13999938964844, 15.56999969482422, 15.14999961853027, 15.18000030517578, 14.81999969482422, 14.60000038146973, 14.10000038146973, 13.23999977111816, 12.65999984741211, 12.46000003814697, 12.09000015258789, 11.85999965667725, 11.72000026702881, 11.64000034332275, 11.52999973297119, 11.43000030517578], pressure = [97.63999938964844, 97.62999725341797, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.62999725341797, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.69000244140625, 97.69999694824219, 97.70999908447266, 97.69999694824219, 97.7300033569336, 97.72000122070312, 97.70999908447266, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.70999908447266, 97.70999908447266, 97.72000122070312, 97.70999908447266, 97.70999908447266, 97.69999694824219, 97.68000030517578, 97.68000030517578, 97.66999816894531, 97.68000030517578, 97.66999816894531, 97.66999816894531, 97.66000366210938, 97.6500015258789, 97.63999938964844, 97.63999938964844, 97.6500015258789, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.68000030517578, 97.68000030517578, 97.69000244140625, 97.69000244140625, 97.69000244140625], H = Union{Missing, Float64}[-68.18000030517578, -48.54000091552734, -59.09999847412109, -60.11000061035156, -59.40000152587891, -48.91999816894531, -53.52999877929688, -40.75, -29.28000068664551, -33.36999893188477, -9.050000190734863, 2.400000095367432, 38.29999923706055, 38.45000076293945, 85.08000183105469, 108.7799987792969, 176.6799926757812, 210.6900024414062, 230.0800018310547, 227.4199981689453, 321.6499938964844, 358.8999938964844, 329.9299926757812, 317.3099975585938, 375.1900024414062, 336.489990234375, 260.7099914550781, 360.6400146484375, 264.2200012207031, 185.3200073242188, 123.7699966430664, 236.0200042724609, 207.5200042724609, 128.3399963378906, 66.05999755859375, 79.6500015258789, 44.93999862670898, 16.22999954223633, -22.35000038146973, -29.10000038146973, -47.79999923706055, -63.61999893188477, -61.38000106811523, -65.11000061035156, -59.77000045776367, -60.52000045776367, -49.02999877929688, -53.45000076293945], + wind = Union{Missing, Float64}[4.210000038146973, 4.460000038146973, 4.539999961853027, 4.079999923706055, 3.950000047683716, 4.019999980926514, 3.269999980926514, 3.480000019073486, 3.240000009536743, 3.420000076293945, 3.440000057220459, 2.970000028610229, 3.349999904632568, 2.660000085830688, 2.779999971389771, 2.329999923706055, 2.220000028610229, 2.160000085830688, 2.519999980926514, 2.980000019073486, 2.359999895095825, 2.420000076293945, 2.920000076293945, 3.359999895095825, 2.759999990463257, 3.279999971389771, 3.410000085830688, 3.480000019073486, 3.039999961853027, 3.950000047683716, 2.700000047683716, 2.829999923706055, 2.730000019073486, 3.640000104904175, 2.75, 3.299999952316284, 2.630000114440918, 2.049999952316284, 1.879999995231628, 2.130000114440918, 2.400000095367432, 2.619999885559082, 2.640000104904175, 2.589999914169312, 2.589999914169312, 2.819999933242798, 2.779999971389771, 2.660000085830688], ) tha_heights = ( diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl index 3c8cf26..ba81c1c 100644 --- a/test/surface_roughness.jl +++ b/test/surface_roughness.jl @@ -1,8 +1,36 @@ -#@testset "wind_profile" begin +@testset "roughness_parameters" begin + zh = tha_heights.zh + zr = tha_heights.zr + LAI = tha_heights.LAI + keys_exp = (:d, :z0m, :z0m_se) + rp = roughness_parameters(Val(:canopy_height), zh) + #round.(values(rp); sigdigits = 4) + @test keys(rp) == keys_exp + @test all(isapproxm.(values(rp), (18.55, 2.65, missing), rtol=1e-3)) + # + rp = roughness_parameters(Val(:canopy_height_LAI), zh, LAI) + #round.(values(rp); sigdigits = 4) + @test keys(rp) == keys_exp + @test all(isapproxm.(values(rp), (21.77, 1.419, missing), rtol=1e-3)) + # + df = copy(tha48) + psi_m = stability_correction!(copy(df, copycols=false), zr, d; constants).psi_m + rp = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m) + #round.(values(rp); sigdigits = 4) + @test keys(rp) == keys_exp + #@test all(isapproxm.(values(rp), (18.55, 1.879, 0.3561), rtol=1e-3)) + #from R: + @test all(isapproxm.(values(rp), (18.55, 1.879402, 0.356108), rtol=1e-3)) + rp_psiauto = roughness_parameters(Val(:wind_profile), df, zh, zr) + @test propertynames(df) == propertynames(tha48) # not changed + @test rp_psiauto == rp +end + +@testset "wind_profile" begin datetime, ustar, Tair, pressure, H = values(tha48[1,:]) z = 30 d=0.7*tha_heights.zh - z0m=2.65 + z0m=2.14 #2.65 u30 = wind_profile(z, ustar, d, z0m) @test ≈(u30, 1.93, rtol = 1/100 ) # @@ -20,4 +48,12 @@ psi_m = stability_correction!(copy(df, copycols=false), z, d).psi_m windzc2 = wind_profile(df, z, d, z0m, psi_m) @test windzc2 == windzc -end \ No newline at end of file + # + # estimate z0m + # need to give zh and zr in addition to many variables in df + @test_throws Exception wind_profile(df, z, d) + windzc3 = wind_profile(df, z, d; zh=tha_heights.zh, zr=tha_heights.zr) + # may have used slightly different estimated z0m + @test all(isapprox.(windzc3, windzc, atol=0.01)) +end + From db0276d9c7923412e024a5f76c803d8a8d96adf4 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Wed, 3 Nov 2021 13:26:30 +0100 Subject: [PATCH 31/43] docstrings of surface_roughness --- docs/make.jl | 3 +- docs/src/index.md | 5 + docs/src/surface_roughness.md | 10 + inst/fromR/boundary_layer_conductance.jl | 4 +- inst/fromR/surface_roughness.jl | 50 ++-- src/Bigleaf.jl | 2 +- src/boundary_layer_conductance.jl | 4 +- src/surface_roughness.jl | 301 ++++++++++------------- test/surface_roughness.jl | 15 ++ 9 files changed, 194 insertions(+), 200 deletions(-) create mode 100644 docs/src/surface_roughness.md diff --git a/docs/make.jl b/docs/make.jl index f0e884e..39eb62a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -5,7 +5,7 @@ using Documenter, Latexify # https://discourse.julialang.org/t/generation-of-documentation-fails-qt-qpa-xcb-could-not-connect-to-display/60988/2 ENV["GKSwstype"] = "100" -DocMeta.setdocmeta!(Bigleaf, :DocTestSetup, :(using Bigleaf, Latexify); recursive=true, warn=false) +DocMeta.setdocmeta!(Bigleaf, :DocTestSetup, :(using Bigleaf, Latexify, DataFrames); recursive=true, warn=false) doctest(Bigleaf, manual = false) makedocs(; @@ -27,6 +27,7 @@ makedocs(; hide("metorological_variables.md"), hide("evapotranspiration.md"), hide("surface_conductance.md"), + hide("surface_roughness.md"), hide("global_radiation.md"), hide("unit_conversions.md"), hide("bigleaf_constants.md"), diff --git a/docs/src/index.md b/docs/src/index.md index 2949027..3fee7d9 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -12,12 +12,17 @@ Pages = ["index.md",] ```@index Pages = ["metorological_variables.md",] ``` +## Surface roughness +```@index +Pages = ["surface_roughness.md",] +``` ## Evapotranspiration ```@index Pages = ["evapotranspiration.md",] ``` + ## Global radiation ```@index Pages = ["global_radiation.md",] diff --git a/docs/src/surface_roughness.md b/docs/src/surface_roughness.md new file mode 100644 index 0000000..21178f0 --- /dev/null +++ b/docs/src/surface_roughness.md @@ -0,0 +1,10 @@ +## Surface roughness +```@index +Pages = ["surface_roughness.md",] +``` + +```@docs +Reynolds_Number +roughness_parameters +wind_profile +``` \ No newline at end of file diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index 346d97b..5f2ff72 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -190,7 +190,7 @@ else end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, - zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, + zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, stab_correction=true,stab_formulation=stab_formulation) ## avoid zero windspeed @@ -358,7 +358,7 @@ else end wind_zh = wind_profile(data=data,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, - zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, + zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, stab_correction=true,stab_formulation=stab_formulation) v = kinematic_viscosity(Tair,pressure,constants) diff --git a/inst/fromR/surface_roughness.jl b/inst/fromR/surface_roughness.jl index 74198a8..7a7a513 100755 --- a/inst/fromR/surface_roughness.jl +++ b/inst/fromR/surface_roughness.jl @@ -49,18 +49,18 @@ end #' A simple approximation of the two roughness parameters displacement height (d) #' and roughness length for momentum (z0m). #' -#' - method Method to use, one of `"canopy_height","canopy_height_LAI",Val(:wind_profile)` -#' NOTE: if `method = "canopy_height"`, only the following three arguments -#' are used. If `method = "canopy_height_LAI"`, only `zh, LAI, cd`, +#' - method Method to use, one of `Val(:canopy_height),Val(:canopy_height_LAI),Val(:wind_profile)` +#' NOTE: if `method = Val(:canopy_height)`, only the following three arguments +#' are used. If `method = Val(:canopy_height_LAI)`, only `zh, LAI, cd`, #' and `hs` are required. #' - zh Vegetation height (m) #' - frac_d Fraction of displacement height on canopy height (-) -#' - frac_z0m Fraction of roughness length on canopy height (-) +#' - frac_{z0m} Fraction of roughness length on canopy height (-) #' - LAI Leaf area index (-) #' - zr Instrument (reference) height (m) #' - cd Mean drag coefficient for individual leaves. Defaults to 0.2. -#' Only needed if `method = "canopy_height_LAI"`. -#' - hs roughness length of the soil surface (m). Only needed if `method = "canopy_height_LAI"` +#' Only needed if `method = Val(:canopy_height_LAI)`. +#' - hs roughness length of the soil surface (m). Only needed if `method = Val(:canopy_height_LAI)` #' The following arguments are only needed if `method = Val(:wind_profile)`! #' - data DataFrame or matrix containing all required variables #' - Tair Air temperature (deg C) @@ -83,17 +83,17 @@ end #' # Details The two main roughness parameters, the displacement height (d) #' and the roughness length for momentum (z0m) can be estimated from simple -#' empirical relationships with canopy height (zh). If `method = "canopy_height"`, +#' empirical relationships with canopy height (zh). If `method = Val(:canopy_height)`, #' the following formulas are used: #' #' ``d = frac_d * zh`` #' -#' ``z0m = frac_z0m * zh`` +#' ``z0m = frac_{z0m} * zh`` #' -#' where frac_d defaults to 0.7 and frac_z0m to 0.1. +#' where frac_d defaults to 0.7 and frac_{z0m} to 0.1. #' #' Alternatively, d and z0m can be estimated from both canopy height and LAI -#' (If `method = "canopy_height_LAI"`). +#' (If `method = Val(:canopy_height_LAI)`). #' Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed #' the following semi-empirical relations: #' @@ -132,8 +132,8 @@ end #' ```@example; output = false #' ``` #' # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy -#' roughness_parameters(method="canopy_height_LAI",zh=25,LAI=5) -#' roughness_parameters(method="canopy_height_LAI",zh=25,LAI=2) +#' roughness_parameters(method=Val(:canopy_height_LAI),zh=25,LAI=5) +#' roughness_parameters(method=Val(:canopy_height_LAI),zh=25,LAI=2) #' #' # fix d to 0.7*zh and estimate z0m from the wind profile #' df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) @@ -144,8 +144,8 @@ end #' #' @importFrom stats median sd complete_cases #' @export -function roughness_parameters(method=c("canopy_height","canopy_height_LAI",Val(:wind_profile)),zh, - frac_d=0.7,frac_z0m=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", +function roughness_parameters(method=c(Val(:canopy_height),Val(:canopy_height_LAI),Val(:wind_profile)),zh, + frac_d=0.7,frac_{z0m}=0.1,LAI,zr,cd=0.2,hs=0.01,data,Tair="Tair",pressure="pressure", wind="wind",ustar="ustar",H="H",d=nothing,z0m=nothing, stab_roughness=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), constants=bigleaf_constants()) @@ -153,13 +153,13 @@ function roughness_parameters(method=c("canopy_height","canopy_height_LAI",Val(: method = match_arg(method) stab_formulation = match_arg(stab_formulation) - if (method == "canopy_height") + if (method == Val(:canopy_height)) d = frac_d*zh - z0m = frac_z0m*zh + z0m = frac_{z0m}*zh z0m_se = missing -elseif (method == "canopy_height_LAI") +elseif (method == Val(:canopy_height_LAI)) X = cd * LAI d = 1.1 * zh * log(1 + X^(1/4)) @@ -225,10 +225,10 @@ end #' - frac_d Fraction of displacement height on canopy height (-); #' only used if `d` is not available #' - z0m Roughness length (m), optional; only used if `stab_correction = false` (default=0.1) -#' - frac_z0m Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. +#' - frac_{z0m} Fraction of roughness length on canopy height (-), optional; only used if `z0m` is not provided. #' Default is 0.1. #' - estimate_z0m Should `z0m` be estimated from the logarithmic wind profile? If `true` (the default), -#' arguments `z0m` and `frac_z0m` are ignored. +#' arguments `z0m` and `frac_{z0m}` are ignored. #' See [`roughness_parameters`](@ref) for details. #' - stab_correction Should stability correction be applied? Defaults to `true` #' - stab_formulation Stability correction function used (If `stab_correction = true`). @@ -282,7 +282,7 @@ end #' #' @export function wind_profile(data,z,Tair="Tair",pressure="pressure",ustar="ustar",H="H",wind="wind", - zr,zh,d=nothing,frac_d=0.7,z0m=nothing,frac_z0m=nothing,estimate_z0m=true, + zr,zh,d=nothing,frac_d=0.7,z0m=nothing,frac_{z0m}=nothing,estimate_z0m=true, stab_correction=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), constants=bigleaf_constants()) @@ -299,17 +299,17 @@ end end if (isnothing(z0m) & !estimate_z0m) - if (isnothing(frac_z0m)) - stop("Either 'z0m' or 'frac_z0m' must be specified if 'estimate_z0m' = false") + if (isnothing(frac_{z0m})) + stop("Either 'z0m' or 'frac_{z0m}' must be specified if 'estimate_z0m' = false") end - z0m = frac_z0m * zh + z0m = frac_{z0m} * zh end if (estimate_z0m) - if (!isnothing(z0m) | !isnothing(frac_z0m)) - cat("Note that arguments 'z0m' and 'frac_z0m' are ignored if 'estimate_z0m' = true. z0m is + if (!isnothing(z0m) | !isnothing(frac_{z0m})) + cat("Note that arguments 'z0m' and 'frac_{z0m}' are ignored if 'estimate_z0m' = true. z0m is calculated from the logarithmic wind_profile equation.",fill=true) end diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 9e073de..0f75c44 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -33,7 +33,7 @@ export compute_Gb, compute_Gb!, add_Gb, Gb_Thom export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, stability_parameter!, stability_correction, stability_correction!, - roughness_parameters + roughness_parameters, Reynolds_Number include("util.jl") include("bigleaf_constants.jl") diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index a3b516b..bf166b4 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -267,7 +267,7 @@ function Gb_Choudhury(leafwidth,LAI,zh,zr,d; # estimate_z0m = false # end # wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, -# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, +# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, # stab_correction=true,stab_formulation=stab_formulation) # ## avoid zero windspeed @@ -422,7 +422,7 @@ function Gb_Su(df,zh,zr,d; # end # wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, -# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_z0m=nothing, +# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, # stab_correction=true,stab_formulation=stab_formulation) # v = kinematic_viscosity(Tair,pressure,constants) diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 55447f7..014972c 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -1,107 +1,98 @@ -#' Roughness Reynolds Number -#' -#' calculates the Roughness Reynolds Number. -#' -#' - Tair Air temperature (deg C) -#' - pressure Atmospheric pressure (kPa) -#' - ustar Friction velocity (m s-1) -#' - z0m Roughness length (m) -#' - constants Kelvin - conversion degree Celsius to Kelvin -#' pressure0 - reference atmospheric pressure at sea level (Pa) -#' Tair0 - reference air temperature (K) -#' -#' # Details -#' The Roughness Reynolds Number is calculated as in Massman 1999a: -#' -#' ``Re = z0m * ustar / v`` -#' -#' where `v` is the kinematic viscosity (m2 s-1). -#' -#' # Value -#' - Re -: Roughness Reynolds Number (-) -#' -#' #References -#' Massman, W_J., 1999a: A model study of kB H- 1 for vegetated surfaces using -#' 'localized near-field' Lagrangian theory. Journal of Hydrology 223, 27-43. -#' -#' ```@example; output = false -#' ``` -#' Reynolds_Number(25,100,0.5,z0m=0.5) -#' -# """ -# """ -# function Reynolds_Number(Tair,pressure,ustar,z0m,constants=bigleaf_constants()) - -# v = kinematic_viscosity(Tair,pressure,constants) -# Re = z0m*ustar/v - -# return(Re) -# end +""" + Reynolds_Number(Tair,pressure,ustar,z0m; constants) + +calculates the Roughness Reynolds Number. + +# Arguments +- `Tair` : Air temperature (deg C) +- `pressure` : Atmospheric pressure (kPa) +- `ustar` : Friction velocity (m s-1) +- `z0m` : Roughness length (m) +optional +- `constants=`[`bigleaf_constants`](@ref)`()` + +# Details +The Roughness Reynolds Number is calculated as in Massman 1999a: +``Re = z0m * ustar / v``, where `v` is the kinematic viscosity (m2 s-1). + +# Value +Roughness Reynolds Number (-) +# References +Massman, W_J., 1999a: A model study of kB H- 1 for vegetated surfaces using +'localized near-field' Lagrangian theory. Journal of Hydrology 223, 27-43. + +```jldoctest; output = false +Tair,pressure,ustar,z0m = 25,100,0.5,0.5 +R = Reynolds_Number(Tair,pressure,ustar,z0m) +≈(R, 15870, rtol=1e-3) +# output +true +``` +""" +function Reynolds_Number(Tair,pressure,ustar,z0m;constants=bigleaf_constants()) + v = kinematic_viscosity(Tair,pressure;constants) + Re = z0m*ustar/v +end """ -Roughness Parameters + roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_{z0m}=0.1) + roughness_parameters(::Val{:canopy_height_LAI}, zh, LAI; cd=0.2, hs=0.01) + roughness_parameters(::Val{:wind_profile}, df, zh, zr; + d = 0.7*zh, psi_m = nothing, constants=bigleaf_constants()) A simple approximation of the two roughness parameters displacement height (d) and roughness length for momentum (z0m). # Arguments -- method Method to use, one of `"canopy_height","canopy_height_LAI",Val(:wind_profile)` - NOTE: if `method = "canopy_height"`, only the following three arguments - are used. If `method = "canopy_height_LAI"`, only `zh, LAI, cd`, - and `hs` are required. -- zh Vegetation height (m) -- frac_d Fraction of displacement height on canopy height (-) -- frac_z0m Fraction of roughness length on canopy height (-) -- LAI Leaf area index (-) -- zr Instrument (reference) height (m) -- cd Mean drag coefficient for individual leaves. Defaults to 0.2. - Only needed if `method = "canopy_height_LAI"`. -- hs roughness length of the soil surface (m). Only needed if `method = "canopy_height_LAI"` - The following arguments are only needed if `method = Val(:wind_profile)`! -- data DataFrame or matrix containing all required variables -- Tair Air temperature (deg C) -- pressure Atmospheric pressure (kPa) -- wind Wind speed at height zr (m s-1) -- ustar Friction velocity (m s-1) -- H Sensible heat flux (W m-2) -- d Zero-plane displacement height (m); optional -- z0m Roughness length for momentum (m); optional -- stab_roughness Should stability correction be considered? Default is `true`. -- stab_formulation Stability correction function used (If `stab_correction = true`). - Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. -- constants k - von-Karman constant (-) - Kelvin - conversion degree Celsius to Kelvin - cp - specific heat of air for constant pressure (J K-1 kg-1) - g - gravitational acceleration (m s-2) - se_median - conversion standard error (SE) of the mean to SE of the median - +- `zh` : Vegetation height (m) +- `constants=`[`bigleaf_constants`](@ref)`()`: +By canopy height: +- `frac_d` : Fraction of displacement height on canopy height (-) +- `frac_{z0m}` : Fraction of roughness length on canopy height (-) + +By canopy height and LAI +- `LAI` : Leaf area index (-) +- `cd` : Mean drag coefficient for individual leaves. Defaults to 0.2. +- `hs` : roughness length of the soil surface (m). + +By wind profile +- `df` : DataFrame or matrix containing all required variables + - `wind` : Wind speed at height zr (m s-1) + - `ustar` : Friction velocity (m s-1) + - further variables required by [`stability_correction!`](@ref) if `psi_m` is not given +- `zr` : Instrument (reference) height (m) +- `d` : Zero-plane displacement height (m) +- `psi_m` : value of the stability function for heat, see [`stability_correction!`](@ref) + Pass `psi_m = 0.0` to neglect stability correction. +- `z0m` : Roughness length for momentum (m) + # Details The two main roughness parameters, the displacement height (d) and the roughness length for momentum (z0m) can be estimated from simple -empirical relationships with canopy height (zh). If `method = "canopy_height"`, +empirical relationships with canopy height (zh). If `method = Val(:canopy_height)`, the following formulas are used: ``d = frac_d * zh`` -``z0m = frac_z0m * zh`` +``z0m = frac_{z0m} * zh`` -where frac_d defaults to 0.7 and frac_z0m to 0.1. +where ``frac_d`` defaults to 0.7 and ``frac_{z0m}`` to 0.1. Alternatively, d and z0m can be estimated from both canopy height and LAI -(If `method = "canopy_height_LAI"`). +(If `method = Val(:canopy_height_LAI)`). Based on data from Shaw & Pereira 1982, Choudhury & Monteith 1988 proposed the following semi-empirical relations: ``X = cd * LAI`` -``d = 1.1 * zh * ln(1 + X^(1/4))`` +``d = 1.1 * zh * ln(1 + X^{1/4})`` -``z0m = hs + 0.3 * zh * X^(1/2) for 0 <= X <= 0.2`` +``z0m = hs + 0.3 * zh * X^{1/2}`` for ``0 <= X <= 0.2`` -``z0m = hs * zh * (1 - d/zh) for 0.2 < X`` +``z0m = hs * zh * (1 - d/zh)`` for ``0.2 < X`` If `method = Val(:wind_profile)`, z0m is estimated by solving the wind speed profile for z0m: @@ -109,13 +100,13 @@ the wind speed profile for z0m: ``z0m = median((zr - d) * exp(-k*wind / ustar - psi_m)`` By default, d in this equation is fixed to 0.7*zh, but can be set to any -other value. psi_m is 0 if `stab_roughness = false`. +other value. # Value a NamedTuple with the following components: -- d: Zero-plane displacement height (m) -- z0m: Roughness length for momentum (m) -- z0m_se: Only if `method = wind_profile`: Standard Error of the median for z0m (m) +- `d`: Zero-plane displacement height (m) +- `z0m`: Roughness length for momentum (m) +- `z0m_se`: Only if `method = wind_profile`: Standard Error of the median for z0m (m) # References - Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat @@ -126,22 +117,26 @@ a NamedTuple with the following components: # See also [`wind_profile`](@ref) -```@example; output = false -# # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy -# roughness_parameters(method="canopy_height_LAI",zh=25,LAI=5) -# roughness_parameters(method="canopy_height_LAI",zh=25,LAI=2) +```jldoctest; output = false +# estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy +zh = 25.0 +roughness_parameters(Val(:canopy_height_LAI),zh,5) +roughness_parameters(Val(:canopy_height_LAI),zh,2) -# # fix d to 0.7*zh and estimate z0m from the wind profile -# df = DataFrame(Tair=c(25,25,25),pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=200) -# roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.7,data=df) - -# # assume d = 0.8*zh -# roughness_parameters(method=Val(:wind_profile),zh=25,zr=40,frac_d=0.8,data=df) +# fix d to 0.7*zh and estimate z0m from the wind profile +df = DataFrame(Tair=[25,25,25],pressure=100,wind=[3,4,5],ustar=[0.5,0.6,0.65],H=200) +roughness_parameters(Val(:wind_profile),df,zh,40;d=0.7*zh) + +# assume d = 0.8*zh +rp = roughness_parameters(Val(:wind_profile),df,zh,40;d=0.8*zh) +≈(rp.z0m, 0.55, rtol=0.1) +# output +true ``` """ -function roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_z0m=0.1) +function roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_{z0m}=0.1) d = frac_d*zh - z0m = frac_z0m*zh + z0m = frac_{z0m}*zh z0m_se = missing (;d, z0m, z0m_se) end @@ -155,6 +150,7 @@ function roughness_parameters(::Val{:canopy_height_LAI}, zh, LAI; cd=0.2, hs=0.0 end # docu: supply psi_m = 0 for no stability correction, default method +# if psi_m is given df only needs wind and ustar function roughness_parameters(::Val{:wind_profile}, df, zh, zr; d = 0.7*zh, psi_m = nothing, constants=bigleaf_constants() ) @@ -170,7 +166,11 @@ function roughness_parameters(::Val{:wind_profile}, df, zh, zr; end """ -Wind Speed at Given Heights in the Surface Layer + wind_profile(z::Number, ustar, d, z0m, psi_m = zero(z); constants) + wind_profile(df::AbstractDataFrame, z, d, z0m, psi_m::AbstractVector; constants) + wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; + zh = nothing, zr = nothing, + stab_formulation = Val(:Dyer_1970), constants) Wind speed at a given height above the canopy estimated from single-level measurements of wind speed. @@ -179,28 +179,20 @@ measurements of wind speed. - `z` : Height above ground for which wind speed is calculated. - `ustar` : Friction velocity (m s-1) - `d` : Zero-plane displacement height (-) -- `z0m` : Roughness length (m), optional; - TODO check in R: only used if `stab_correction = false` (default=0.1) -alternatively, in the DataFrame variant d and z0m can be specified as faction of zh -- `zh` : Canopy height (m) -- `frac_d` : Fraction of displacement height on canopy height (-) -- `frac_z0m` : Fraction of roughness length on canopy height (-) -required for stability correction -- `Tair` : Air temperature (deg C) -- `pressure` : Atmospheric pressure (kPa) -- `H` : Sensible heat flux (W m-2) -required for z0m estimation -- `wind` : Wind speed at height zr (m s-1); only used if `stab_correction = true` +- `z0m` : Roughness length (m) +- `constants=`[`bigleaf_constants`](@ref)`()` +For DataFrame variant with supplying stability_parameter +- `df`: : DataFrame with columns + - `ustar` : Friction velocity (m s-1) +- `psi_m` : value of the stability function for heat, see [`stability_correction!`](@ref) + Pass `psi_m = 0.0` to neglect stability correction. +For DataFrame varinat where psi_m and z0m are to be estimated +- `zh` : canopy height (m) - `zr` : Instrument (reference) height (m) -optional -- `stab_formulation = Val(:Dyer_1970)`: Stability correction function used - Either `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or - `Val(:no_stability_correction)`. -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `k` - von-Karman constant (-) - - `Kelvin` - conversion degree Celsius to Kelvin - - `cp` - specific heat of air for constant pressure (J K-1 kg-1) - - `g` - gravitational acceleration (m s-2) +- `df`: : DataFrame with columns + - `ustar` : Friction velocity (m s-1) + - `Tair`, `pressure`, `H` : see [`stability_correction!`](@ref) + - `wind` : see [`roughness_parameters(Val(:wind_profile), ...)`] # Details The underlying assumption is the existence of a logarithmic wind profile @@ -211,9 +203,11 @@ In this case, the wind speed at a given height z is given by: ``u(z) = (ustar/k) * (ln((z - d) / z0m) - \\psi_m`` The roughness parameters zero-plane displacement height (d) and roughness length (z0m) -can be approximated from [`roughness_parameters`](@ref). ``\\psi_m`` is omitted -if `stab_correction = false` (not recommended). If `estimate_z0m = true`, -z0m is first estimated from the wind profile equation and then used in the equation +can be approximated from [`roughness_parameters`](@ref). +``\\psi_m`` is the stability correction. Set it to zero (not recommended) to neglect +statbility correction. +If `z0m` is not provided, i.e. defaults to nothing, then +`z0m` is first estimated from the wind profile equation and then used in the equation above for the calculation of `u(z)` (see e.g. Newman & Klein 2014). # Note @@ -221,13 +215,9 @@ Note that this equation is only valid for z >= d + z0m, and it is not meaningful to calculate values closely above d + z0m. All values in `heights` smaller than d + z0m will return 0. -Computing windspeed without stability correction, (besides d and z0m) only requires 'ustar'. -For stability correction, additionally requires 'Tair', 'pressure', and 'H'. -d and z0m can be specified directly as named arguments or as a fraction of zh. - - + # Value -A vector of wind speed at heights `z`. +wind speed at given height `z`. # References - Monteith, J_L., Unsworth, M_H., 2008: Principles of Environmental Physics. @@ -238,14 +228,22 @@ A vector of wind speed at heights `z`. # See also [`roughness_parameters`](@ref) -```@example; output = false -# heights = seq(18,40,2) # heights above ground for which to calculate wind speed -# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) -# ws = DataFrame(matrix(missing,ncol=length(heights),nrow=nrow(df))) -# colnames(ws) = paste0(heights,"m") -# for (i in seq_along(heights)) -# ws[,i] = wind_profile(df,z=heights[i],zr=40,zh=25,d=16) -# } +```jldoctest; output = false +heights = 18:2:40 # heights above ground for which to calculate wind speed +df = DataFrame(Tair=25,pressure=100,wind=[3,4,5],ustar=[0.5,0.6,0.65],H=[200,230,250]) +zr=40;zh=25;d=16 +psi_m = stability_correction!(copy(df, copycols=false), zr, d).psi_m +z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m).z0m +ws = map(heights) do z + wind_profile(df,z,d,z0m,psi_m) +end +using Plots # plot wind profiles for the three rows in df +plot(heights, first.(ws), xlab = "height (m)", ylab = "wind speed (m/s)") +plot!(heights, getindex.(ws, 2)) +plot!(heights, getindex.(ws, 3)) +nothing +# output +nothing ``` """ function wind_profile(z::Number, ustar, d, z0m, psi_m = zero(z); constants=bigleaf_constants()) @@ -261,31 +259,6 @@ function wind_profile(stab_formulation, z, ustar, Tair,pressure,H, d, z0m; wind_profile(z, ustar, d, z0m, psi_m) end -function wind_profile(df, z; - zh=nothing, - d=nothing, frac_d=0.7, - z0m=nothing,frac_z0m=nothing, - stab_formulation=Val(:Dyer_1970), - constants=bigleaf_constants() - ) - ## determine roughness parameters - if (isnothing(d)) - (isnothing(frac_d) || isnothing(zh)) && error( - "Either 'd' or both 'zh' and 'frac_d' must be specified.") - d = frac_d * zh - end - if isnothing(z0m) - (isnothing(frac_z0m) || isnothing(zh)) && error( - "Either 'z0m' or both 'zh' and 'frac_z0m' must be specified.") - z0m = frac_z0m * zh - end - if any(@.(z < (d + z0m) & !ismissing(d + z0m))) - @warn("function is only valid for heights above d + z0m! Wind speed for heights " * - "below d + z0m will return 0!") - end - wind_profile_(df, z, d, z0m; stab_formulation, constants) -end - function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; zh = nothing, zr = nothing, stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants()) @@ -293,9 +266,9 @@ function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; copy(df, copycols=false), z, d; stab_formulation, constants).psi_m if isnothing(z0m) (isnothing(zh) || isnothing(zr)) && error( - "wind_profile: If z0m is not given, must specify both zh and zr optional arguments.") + "wind_profile: If 'z0m' is not given, must specify both 'zh' and 'zr' optional " * + "arguments to estimate 'z0m' from 'df.wind' and 'df.ustar' measured at height zr.") z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = psi_m).z0m - @show z0m end wind_profile(df, z, d, z0m, psi_m; constants) end @@ -305,14 +278,4 @@ function wind_profile(df::AbstractDataFrame, z, d, z0m, psi_m::AbstractVector; # when psi_m is given, df is not used any more, but keep for consistency wind_profile.(z, df.ustar, d, z0m, psi_m; constants) end -# function estimate_z0m( -# Tair,pressure,wind,ustar,H -# zh,zr,d) -# error("not yet implemented, need roughness_parameters") -# # z0m = roughness_parameters(Val(:wind_profile),zh=zh,zr=zr,d=d, -# # Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, -# # stab_roughness=true,stab_formulation=stab_formulation, -# # constants=constants)[,"z0m"] -# end - diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl index ba81c1c..6ed747b 100644 --- a/test/surface_roughness.jl +++ b/test/surface_roughness.jl @@ -1,3 +1,10 @@ +@testset "" begin + Tair,pressure,ustar,z0m = 25,100,0.5,0.5 + R = Reynolds_Number(Tair,pressure,ustar,z0m) + @test ≈(R, 15870, rtol=1e-3) +end + + @testset "roughness_parameters" begin zh = tha_heights.zh zr = tha_heights.zr @@ -21,6 +28,14 @@ #@test all(isapproxm.(values(rp), (18.55, 1.879, 0.3561), rtol=1e-3)) #from R: @test all(isapproxm.(values(rp), (18.55, 1.879402, 0.356108), rtol=1e-3)) + # + # no stability correction + rp0 = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = 0.0) + @test keys(rp0) == keys_exp + # same magnitude as with stability correction + @test all(isapproxm.(values(rp0), values(rp), rtol=0.5)) + # + # estimate psi rp_psiauto = roughness_parameters(Val(:wind_profile), df, zh, zr) @test propertynames(df) == propertynames(tha48) # not changed @test rp_psiauto == rp From c506251e5d1dd70d95059cc7420bbd983a1f75c3 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 4 Nov 2021 09:11:46 +0100 Subject: [PATCH 32/43] docstrings of stability_correction --- docs/make.jl | 5 +- docs/src/index.md | 6 ++ docs/src/stability_correction.md | 10 +++ docs/src/walkthrough.md | 56 +++++++++++++++-- inst/tha.jl | 5 ++ inst/walkthrough_todo.jmd | 102 +++++++++++++++++-------------- src/stability_correction.jl | 90 ++++++++++++++++----------- src/surface_roughness.jl | 20 +++--- 8 files changed, 193 insertions(+), 101 deletions(-) create mode 100644 docs/src/stability_correction.md diff --git a/docs/make.jl b/docs/make.jl index 39eb62a..6313022 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -25,9 +25,10 @@ makedocs(; #"Unit conversions" => "unit_conversions.md", "Walkthrough" => "walkthrough.md", hide("metorological_variables.md"), - hide("evapotranspiration.md"), - hide("surface_conductance.md"), + hide("stability_correction.md"), hide("surface_roughness.md"), + hide("surface_conductance.md"), + hide("evapotranspiration.md"), hide("global_radiation.md"), hide("unit_conversions.md"), hide("bigleaf_constants.md"), diff --git a/docs/src/index.md b/docs/src/index.md index 3fee7d9..8aef02d 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -12,6 +12,12 @@ Pages = ["index.md",] ```@index Pages = ["metorological_variables.md",] ``` + +## Stability correction +```@index +Pages = ["stability_correction.md",] +``` + ## Surface roughness ```@index Pages = ["surface_roughness.md",] diff --git a/docs/src/stability_correction.md b/docs/src/stability_correction.md new file mode 100644 index 0000000..0bec2ea --- /dev/null +++ b/docs/src/stability_correction.md @@ -0,0 +1,10 @@ +## Stability correction +```@index +Pages = ["stability_correction.md",] +``` + +```@docs +Monin_Obukhov_length +stability_parameter +stability_correction +``` \ No newline at end of file diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index f558f8b..c740fc7 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -58,10 +58,12 @@ For more information on the site see e.g. Grünwald & Bernhofer 2007. In addition, we will need some ancillary data for this site throughout this tutorial. To ensure consistency, we define them here at the beginning: ```@example doc -LAI = 7.6 # leaf area index -zh = 26.5 # average vegetation height (m) -zr = 42 # sensor height (m) -Dl = 0.01 # leaf characteristic dimension (m) +thal = ( + LAI = 7.6, # leaf area index + zh = 26.5, # average vegetation height (m) + zr = 42, # sensor height (m) + Dl = 0.01, # leaf characteristic dimension (m) +) nothing # hide ``` @@ -316,6 +318,49 @@ The following figure compares them at absole scale and as difference to the ![](fig/Esat_rel.svg) +## Wind profile + +The 'big-leaf' framework assumes that wind speed is zero at height d + $z_{0m}$ +(where $z_{0m}$ is the roughness length for momentum) and then increases exponentially with +height. The shape of the wind profile further depends on the stability conditions of the +air above the canopy. +In `Bigleaf.jl`, a wind profile can be calculated assuming an exponential increase with +height, which is affected by atmospheric stability. Here, we calculate wind speed at +heights of 22-60m in steps of 2m. As expected, the gradient in wind speed is strongest +close to the surface and weaker at greater heights: + +```@example doc +using Statistics +wind_heights = 22:2:60.0 +d = 0.7 * thal.zh +#psi_m = stability_correction!(copy(tha, copycols=false), thal.zr, d).psi_m +#z0m = roughness_parameters(Val(:wind_profile), tha, thal.zh, thal.zr; psi_m).z0m +wp = map(wind_heights) do z + wind_profile(tha,z,d; zh=thal.zh, zr=thal.zr) +end +nothing # hide +``` +```@setup doc +wp_means = map(x -> mean(skipmissing(x)), wp) +wp_sd = map(x -> std(skipmissing(x)), wp) +wr_mean = mean(skipmissing(tha.wind)) # measurements at reference height +wr_sd = std(skipmissing(tha.wind)) +using Plots # plot wind profiles for the three rows in df +plot(wp_means, wind_heights, ylab = "height (m)", xlab = "wind speed (m/s)", xerror=wp_sd, + label=nothing) +scatter!(wp_means, wind_heights, label = nothing) +``` +```@example doc +scatter!([wr_mean], [thal.zr], xerror = [wr_sd], markerstrokecolor=:blue, #hide + markerstrokewidth=2, label = nothing) # hide +``` + +Here, the points denote the mean wind speed and the bars denote the standard deviation +across time. The blue point/bar represent the values that were measured at zr = 42m. +In this case we see that the wind speed as "back-calculated" from the wind profile agrees +well with the actual measurements. + + ## Potential evapotranspiration For many hydrological applications, it is relevant to get an estimate on the potential @@ -354,7 +399,8 @@ lat,long = 51.0, 13.6 # Dresden Germany doy = 160 datetimes = DateTime(2021) .+Day(doy-1) .+ Hour.(hours) #.- Second(round(long*deg2second)) res3 = @pipe calc_sun_position_hor.(datetimes, lat, long) |> DataFrame(_) -@df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, xlab="Date and Time", ylab = "rad", xrotation=6) +@df res3 scatter(datetimes, cols([:altitude,:azimuth]), legend = :topleft, # hide + xlab="Date and Time", ylab = "rad", xrotation=6) # hide ``` The hour-angle at noon represents the difference to diff --git a/inst/tha.jl b/inst/tha.jl index f7ab178..4ee6226 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -1,3 +1,5 @@ +using Bigleaf + using DataFrames, Pipe, Missings using Dates, TimeZones using Statistics @@ -31,6 +33,9 @@ setinvalid_range!(thaf, setinvalid_nongrowingseason!(thaf, 0.4) setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) +function tmpf() + +end # tha48 and tha_heights see runtests.jl diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index c1c5555..63f4375 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -20,10 +20,12 @@ ENV["DATADEPS_ALWAYS_ACCEPT"]="true" # avoid question to download DE_Tha_Jun_2014 = first(values(load(joinpath(datadep"DE_Tha_Jun_2014.rda/DE_Tha_Jun_2014.rda")))) tha = DE_Tha_Jun_2014 set_datetime_ydh!(tha) -LAI = 7.6 # leaf area index -zh = 26.5 # average vegetation height (m) -zr = 42 # sensor height (m) -Dl = 0.01 # leaf characteristic dimension (m) +thal = ( + LAI = 7.6, # leaf area index + zh = 26.5, # average vegetation height (m) + zr = 42, # sensor height (m) + Dl = 0.01, # leaf characteristic dimension (m) +) ``` ## Data filtering @@ -137,7 +139,7 @@ sum(.!thaf.valid) # some more invalids ``` -When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (tha_filtered), in which all filtered timesteps are set to missing. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `tha_filtered`. +When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (thaf), in which all filtered timesteps are set to missing. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `thaf`. @@ -155,7 +157,7 @@ An important metric for many calculations in the `Bigleaf.jl` package is the aer $G_a$ and in particular $G_b$ can be calculated with varying degrees of complexity. We start with the simplest version, in which $G_b$ is calculated empirically based on the friction velocity ($u_*$) according to Thom 1972: ```julia -summary(aerodynamic_conductance(tha_filtered)) +summary(aerodynamic_conductance(thaf)) ``` Note that by not providing additional arguments, the default values are taken (type ?aerodynamic_conductance to see default values of the function arguments). We also do not need most of the arguments that can be provided to the function in this case (i.e. if `Rb_model=Val(:Thom_1972)`). These are only required if we use a more complex formulation of $G_b$. @@ -164,13 +166,13 @@ For comparison, we now calculate a second estimate of $G_a$, where the calculati ```julia -Ga_Su = aerodynamic_conductance(tha_filtered,Rb_model=Val(:Su_2001),LAI=LAI,zh=zh,d=0.7*zh, - zr=zr,Dl=Dl) +Ga_Su = aerodynamic_conductance(thaf,Rb_model=Val(:Su_2001),LAI=thal.zh,zh= thal.zh,d=0.7*zh, + zr= thal.zr,Dl=thal.Dl) summary(Ga_Su) -tha_filtered = cbind(tha_filtered,Ga_Su) +thaf = cbind(thaf,Ga_Su) ``` -We add the output of this function (`Ga_Su`) to our dataframe `tha_filtered`. We see that the values are different compared to the first, empirical estimate. This is because this formulation takes additional aerodynamically relevant properties (LAI, $D_l$) into account that were not considered by the simple empirical formulation. +We add the output of this function (`Ga_Su`) to our dataframe `thaf`. We see that the values are different compared to the first, empirical estimate. This is because this formulation takes additional aerodynamically relevant properties (LAI, $D_l$) into account that were not considered by the simple empirical formulation. @@ -185,9 +187,9 @@ where $T_a$ is air temperature, $H$ is the sensible heat flux ($\text{W m}^{-2}$ In `Bigleaf.jl`, the following function calculates conditions at the big-leaf surface: ```julia -surf = surface_conditions(tha_filtered,calc_surface_CO2=true) +surf = surface_conditions(thaf,calc_surface_CO2=true) summary(surf) -tha_filtered = cbind(tha_filtered,surf) +thaf = cbind(thaf,surf) ``` By default, the function calculates surface temperature and several humidity measures, including VPD and relative humidity. If we set `calc_surface_CO2=true`, the CO$_2$ concentration at the surface is calculated additionally. Useful to know is that the expression "surface" depends on what kind of aerodynamic conductance we provide. If $G_a = G_{ah}$, we derive the conditions at the notional canopy surface (or the "big-leaf" surface). If $G_a = G_{am}$, we derive conditions in the intercanopy airspace (because $G_a$ does not account for the leaf boundary layer). @@ -196,10 +198,10 @@ We can compare the surface and air temperature: ```julia par(mfrow=c(1,2),mar=c(5,4,2,0.5)) -plot(tha_filtered[,"Tair"] ~ tha_filtered[,"Tsurf"],xlim=c(8,35),ylim=c(8,35),las=1, +plot(thaf[,"Tair"] ~ thaf[,"Tsurf"],xlim=c(8,35),ylim=c(8,35),las=1, xlab="Tsurf (degC)",ylab="Tair (degC)",mgp=c(2.2,0.5,0),tcl=-0.2) abline(0,1) -plot(tha_filtered[,"VPD"] ~ tha_filtered[,"VPD_surf"],xlim=c(0,4),ylim=c(0,4),las=1, +plot(thaf[,"VPD"] ~ thaf[,"VPD_surf"],xlim=c(0,4),ylim=c(0,4),las=1, xlab="VPD surface (kPa)",ylab="VPD air (kPa)",mgp=c(2.2,0.5,0),tcl=-0.2) abline(0,1) ``` @@ -213,17 +215,17 @@ Both surface temperature and VPD are in most cases higher than the ones measured Knowledge on $G_a$ allows us to calculate the bulk surface conductance ($G_s$) of the site (In this case by inverting the Penman-Monteith equation). Gs represents the combined conductance of the vegetation and the soil to water vapor transfer (and as such it is not a purely physiological quantity). Calculating $G_s$ in `Bigleaf.jl` is simple: ```julia -summary(surface_conductance(tha_filtered)) +summary(surface_conductance(thaf)) ``` The function output is another DataFrame with two columns which only differ in the unit of $G_s$ (i.e. a hopeless attempt to make both physicists and physiologists happy). One in m s$^{-1}$ and one in mol m$^{-2}$ s$^{-1}$. In this function we have ignored the ground heat flux ($G$) and the storage fluxes ($S$), and the function politely reminds us of this omission by printing the first two lines of the output (it also tells us what it does, it assumes they are 0 in each time step). In this case we do not have information on the storage fluxes, but we have measurements on the ground heat flux, which we should add to the function call: ```julia -Gs = surface_conductance(tha_filtered,G="G") +Gs = surface_conductance(thaf,G="G") summary(Gs) -tha_filtered = cbind(tha_filtered,Gs) +thaf = cbind(thaf,Gs) ``` -Again, we have added the two output columns to our DataFrame `tha_filtered`. +Again, we have added the two output columns to our DataFrame `thaf`. @@ -239,7 +241,7 @@ With both $G_s$ and $G_a$ available, we can estimate the stomatal slope paramete ```julia ## stomatal slope from the USO model (Medlyn et al. 2011) -g1_USO = stomatal_slope(tha_filtered,model="USO",g0=0,robust_nls=true) +g1_USO = stomatal_slope(thaf,model="USO",g0=0,robust_nls=true) g1_USO ``` @@ -249,7 +251,7 @@ By default, the model takes VPD and atmospheric CO$_2$ concentration as measured ```julia ## stomatal slope from the USO model (Medlyn et al. 2011) -stomatal_slope(tha_filtered,Tair="Tsurf",VPD="VPD_surf",Ca="Ca_surf",model="USO", +stomatal_slope(thaf,Tair="Tsurf",VPD="VPD_surf",Ca="Ca_surf",model="USO", g0=0,robust_nls=true) ``` which in this case, does not change our $g_1$ value significantly. @@ -258,11 +260,11 @@ We can also calculate $g_1$ using two different models. One is the long-standing ```julia ## Ball&Berry slope -stomatal_slope(tha_filtered,model="Ball&Berry",g0=0,robust_nls=true) +stomatal_slope(thaf,model="Ball&Berry",g0=0,robust_nls=true) ``` ```julia ## Leuning slope -stomatal_slope(tha_filtered,model="Leuning",g0=0,fitD0=true,robust_nls=true) +stomatal_slope(thaf,model="Leuning",g0=0,fitD0=true,robust_nls=true) ``` Note that the absolute value of the $g_1$ parameter depends on the model. In the Leuning model, we have a third parameter $D_0$ that can again either be estimated (as in the example above) or fixed to a pre-defined value (by default 1.5 kPa). $D_0$ describes the stomatal sensitivity to VPD (higher values correspond to a lower stomatal sensitivity to VPD - note however that $g_1$ and $D_0$ are strongly correlated, which makes an independent estimates of $D_0$ difficult to achieve). @@ -270,9 +272,9 @@ Note that the absolute value of the $g_1$ parameter depends on the model. In the We can visualize the $g_1$ parameter by plotting $G_s$ against the "stomatal index": ```julia -stomatal_index = tha_filtered[,"GPP"] / (tha_filtered[,"Ca"] * sqrt(tha_filtered[,"VPD"])) +stomatal_index = thaf[,"GPP"] / (thaf[,"Ca"] * sqrt(thaf[,"VPD"])) -plot(tha_filtered[,"Gs_mol"] ~ stomatal_index,las=1, +plot(thaf[,"Gs_mol"] ~ stomatal_index,las=1, xlab=expression("GPP / (C"["a"]~sqrt("D"["a"])*")"), ylab=expression("G"["sw"]~"(mol m"^{-2}~"s"^{-1}*")"), tcl=0.2,mgp=c(2.2,0.5,0),xlim=c(0,0.12)) @@ -286,22 +288,28 @@ The 'big-leaf' framework assumes that wind speed is zero at height d + $z_{0m}$ In `Bigleaf.jl`, a wind profile can be calculated assuming an exponential increase with height, which is affected by atmospheric stability. Here, we calculate wind speed at heights of 22-60m in steps of 2m. As expected, the gradient in wind speed is strongest close to the surface and weaker at greater heights: ```julia -wind_heights = seq(22,60,2) -wp = wind_profile(tha_filtered,heights=wind_heights,zh=zh,zr=zr) -wp_means = colMeans(wp,na_rm=true) -wp_sd = apply(wp,2,sd,na_rm=true) +using Statistics +wind_heights = 22:2:60.0 +d = 0.7 * thal.zh +#psi_m = stability_correction!(copy(tha, copycols=false), thal.zr, d).psi_m +#z0m = roughness_parameters(Val(:wind_profile), tha, thal.zh, thal.zr; psi_m).z0m +wp = map(wind_heights) do z + wind_profile(tha,z,d; zh=thal.zh, zr=thal.zr) +end +wp_means = map(x -> mean(skipmissing(x)), wp) +wp_sd = map(x -> std(skipmissing(x)), wp) +wr_mean = mean(skipmissing(tha.wind)) # measurements at reference height +wr_sd = std(skipmissing(tha.wind)) -plot(wind_heights ~ wp_means,xlab=expression("wind speed (m s"^{-1}*")"),ylab="height (m)", - las=1,mgp=c(2.2,0.5,0),tcl=0.2,xlim=c(0,5)) -arrows(wp_means-wp_sd,wind_heights,wp_means+wp_sd,wind_heights,angle=90, - length=0.02,code=3) -points(x=mean(tha_filtered[,"wind"],na_rm=true),y=zr,col="blue",pch=16) -arrows(mean(tha_filtered[,"wind"],na_rm=true)-sd(tha_filtered[,"wind"],na_rm=true), - zr,mean(tha_filtered[,"wind"],na_rm=true)+sd(tha_filtered[,"wind"],na_rm=true), - zr,angle=90,length=0.02,code=3,col="blue") +using Plots # plot wind profiles for the three rows in df +plot(wp_means, wind_heights, ylab = "height (m)", xlab = "wind speed (m/s)", xerror=wp_sd, label=nothing) +scatter!(wp_means, wind_heights, label = nothing) +scatter!([wr_mean], [thal.zr], xerror = [wr_sd], markerstrokecolor=:blue, markerstrokewidth=2, label = nothing) ``` -Here, the points denote the mean wind speed and the bars denote the standard deviation. The blue point/bar represent the values that were measured at 42m. In this case we see that the wind speed as "back-calculated" from the wind profile agrees well with the actual measurements. - +Here, the points denote the mean wind speed and the bars denote the standard deviation +across time. The blue point/bar represent the values that were measured at zr = 42m. +In this case we see that the wind speed as "back-calculated" from the wind profile agrees +well with the actual measurements. ## Potential evapotranspiration @@ -421,8 +429,8 @@ invisible(capture_output(PET = potential_ET(Tair=25,pressure=100,Rn=200))) The `Bigleaf.jl` package contains a single list of constants (see `?Bigleaf_constants`). Whenever one or more constants are used in a function, this list is provided as a default argument, so the user does usually not need to interact with this list. However, should you wish to change a certain constant for the calculations (which could make sense in some cases, e.g. using a different value for the von-Karman constant (k)), individual constants can be changed within a function call. As an example, let's call a function with the `Bigleaf.jl` default value of k=0.41, and the alternative, often used value of k=0.4: ```julia -summary(aerodynamic_conductance(tha_filtered,wind_profile=true,zr=zr,d=0.7*zh,z0m=2.65)[,"Ga_h"]) -summary(aerodynamic_conductance(tha_filtered,wind_profile=true,zr=zr,d=0.7*zh,z0m=2.65, +summary(aerodynamic_conductance(thaf,wind_profile=true,zr= thal.zr,d=0.7*zh,z0m=2.65)[,"Ga_h"]) +summary(aerodynamic_conductance(thaf,wind_profile=true,zr= thal.zr,d=0.7*zh,z0m=2.65, constants=Bigleaf_constants(k=0.4))[,"Ga_h"]) ``` @@ -435,8 +443,8 @@ We see that in this case, small changes in k have an effect on the calculated va By default, the function `aerodynamic_conductance` returns the (quasi-laminar) canopy boundary layer ($G_{b}$) for heat and water vapor (which are assumed to be equal in the `Bigleaf.jl` package), as well as for CO$_2$. The function further provides the possibility to calculate $G_b$ for other trace gases, provided that the respective Schmidt number is known. Further, if we are only interested in $G_b$ (or the kB$^{-1}$ parameter) we can use one of the following functions: `Gb_Thom`, `Gb_Choudhury`, `Gb_Su`. These functions are integrated in the main function `aerodynamic_conductance`, but the modular design of the package allows them to be called separately. We can demonstrate the calculation of $G_b$ for methane (CH$_4$) with a simple example: ```julia -summary(Gb_Thom(tha_filtered$ustar)) -summary(Gb_Thom(tha_filtered$ustar,Sc=0.99,Sc_name="CH4")) +summary(Gb_Thom(thaf$ustar)) +summary(Gb_Thom(thaf$ustar,Sc=0.99,Sc_name="CH4")) ``` In the first line we get the standard output of the function, whereas in the second line we get in addition the $G_b$ for methane. @@ -481,7 +489,7 @@ We can use this function with our data: ```{r, results="hide"} # 300 times resampling; each time 75 % of the data: -tha_G1BT = G1_bootstrap(dat = tha_filtered,LoopNum = 300,SampSizeRel = 0.75) +tha_G1BT = G1_bootstrap(dat = thaf,LoopNum = 300,SampSizeRel = 0.75) # estimate using all data: g1_mean = summary(g1_USO)$coef[1,1] g1_se = summary(g1_USO)$coef[1,2] @@ -517,7 +525,7 @@ In the example above we create a parameter space that we use for the subsequent ```julia unc_all = mapply(aerodynamic_conductance,Dl=Dl_sample,z0m=z0m_sample,LAI=LAI_sample, - MoreArgs=list(data=tha_filtered,zr=42,zh=26.5,d=0.7*26.5, + MoreArgs=list(data=thaf,zr=42,zh=26.5,d=0.7*26.5, N=2,stab_correction=T, stab_formulation=Val(:Dyer_1970), Rb_model=Val(:Su_2001)) @@ -535,9 +543,9 @@ summary(Ga) # calculate the Gs for the three Ga estimates -Gs_low = surface_conductance(tha_filtered,Ga=Ga[,"Ga_low"],G="G")[,"Gs_mol"] -Gs_mean = surface_conductance(tha_filtered,Ga=Ga[,"Ga_mean"],G="G")[,"Gs_mol"] -Gs_high = surface_conductance(tha_filtered,Ga=Ga[,"Ga_high"],G="G")[,"Gs_mol"] +Gs_low = surface_conductance(thaf,Ga=Ga[,"Ga_low"],G="G")[,"Gs_mol"] +Gs_mean = surface_conductance(thaf,Ga=Ga[,"Ga_mean"],G="G")[,"Gs_mol"] +Gs_high = surface_conductance(thaf,Ga=Ga[,"Ga_high"],G="G")[,"Gs_mol"] Gs = cbind(Gs_low,Gs_mean,Gs_high) summary(Gs) ``` diff --git a/src/stability_correction.jl b/src/stability_correction.jl index a214ee6..356206e 100755 --- a/src/stability_correction.jl +++ b/src/stability_correction.jl @@ -1,14 +1,16 @@ """ -Monin-Obukhov Length + Monin_Obukhov_length(Tair, pressure, ustar, H; constants) + Monin_Obukhov_length!(df;constants=bigleaf_constants()) + Monin_Obukhov_length(df;constants=bigleaf_constants()) calculates the Monin-Obukhov length. # Arguments -- `Tair` Air temperature (degC) -- `pressure` Atmospheric pressure (kPa) -- `ustar` Friction velocity (m s-1) -- `H` Sensible heat flux (W m-2) -- df DataFrame containing all required variables +- `Tair` : Air temperature (degC) +- `pressure` : Atmospheric pressure (kPa) +- `ustar` : Friction velocity (m s-1) +- `H` : Sensible heat flux (W m-2) +- `df` : DataFrame containing the above variables optional - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - `Kelvin` - conversion degree Celsius to Kelvin @@ -23,22 +25,26 @@ The Monin-Obukhov length (L) is given by: where ``\\rho`` is air density (kg m-3). -# Value -Monin-Obukhov length L (m) - # Note Note that L gets very small for very low ustar values with implications for subsequent functions using L as input. It is recommended to filter data and exclude low ustar values (ustar < ~0.2) beforehand. -#References +# Value +Monin-Obukhov length L (m). The non-mutating DataFrame variant returns a vector, +the mutating variant add or modifies column `:MOL`. + + +# References Foken, T, 2008: Micrometeorology. Springer, Berlin, Germany. -#See also +# See also [`stability_parameter`](@ref) ```@example; output = false -Monin_Obukhov_length(Tair=25,pressure=100,ustar=seq(0.2,1,0.1),H=seq(40,200,20)) +Monin_Obukhov_length( + Tair=25,pressure=100, + ustar=seq(0.2,1,0.1),H=seq(40,200,20)) ``` """ function Monin_Obukhov_length(Tair, pressure, ustar, H; constants=bigleaf_constants()) @@ -56,52 +62,56 @@ function Monin_Obukhov_length(df;constants=bigleaf_constants()) end """ -Stability Parameter "zeta" + stability_parameter(zr,d,MOL) + stability_parameter!(df::AbstractDataFrame; zr,d, MOL=nothing, constants) + stability_parameter(df::AbstractDataFrame; zr,d, MOL=nothing, constants) calculates stability parameter "zeta", a parameter characterizing stratification in the lower atmosphere. # Arguments -- `df` DataFrame or matrix containing all required variables -- `Tair` Air temperature (degC) -- `pressure` Atmospheric pressure (kPa) -- `ustar` Friction velocity (m s-1) -- `H` Sensible heat flux (W m-2) -- `zr` Instrument (reference) height (m) -- `d` Zero-plane displacement height (m) -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `Kelvin` - conversion degree Celsius to Kelvin - - `cp` - specific heat of air for constant pressure (J K-1 1) - - `k` - von Karman constant (-) - - `g` - gravitational acceleration (m s-2) +- `zr` : Instrument (reference) height (m) +- `d` : Zero-plane displacement height (m) +- `MOL` : Monin-Obukhov-length L (m) +- `df` : DataFrame containting the variables required by [`Monin_Obukhov_length`](@ref) +optional +- `constants=`[`bigleaf_constants`](@ref)`()` + +If `MOL=nothing` in the DataFrame variants, it is computed by +[`Monin_Obukhov_length`](@ref) using `df` and `constants`. # Details The stability parameter ``\\zeta`` is given by: - ``\\zeta = (zr - d) / L`` +``\\zeta = (zr - d) / L`` where L is the Monin-Obukhov length (m), calculated from the ction [`Monin_Obukhov_length`](@ref). The displacement height can be estimated from the function [`roughness_parameters`](@ref). # Value - - ``\\zeta`` - : stability parameter (-) +``\\zeta``: stability parameter (-). The nonmutainting DataFrame variant returns a vector. +The mutating variant modifies or adds column [`:zeta`]. -```@example; output = false -df = DataFrame(Tair=25,pressure=100,ustar=seq(0.2,1,0.1),H=seq(40,200,20)) -stability_parameter(df,zr=40,d=15) +```jldoctest; output = false +df = DataFrame(Tair=25, pressure=100, ustar=0.2:0.1:1.0, H=40:20:200) +zeta = stability_parameter(df;zr=40,d=15) +all(zeta .< 0) +# output +true ``` """ function stability_parameter(zr,d,MOL) zeta = (zr - d) / MOL end -function stability_parameter!(df::AbstractDataFrame; zr,d, - MOL = Monin_Obukhov_length(df)) - df[!,:zeta] .= stability_parameter(df; zr,d,MOL) +function stability_parameter!(df::AbstractDataFrame; zr,d, MOL=nothing, + constants=bigleaf_constants()) + df[!,:zeta] .= stability_parameter(df; zr,d,MOL,constants) df end -function stability_parameter(df::AbstractDataFrame; zr,d, - MOL = Monin_Obukhov_length(df)) +function stability_parameter(df::AbstractDataFrame; zr,d, MOL=nothing, + constants=bigleaf_constants()) + if isnothing(MOL) MOL = Monin_Obukhov_length(df; constants); end stability_parameter.(zr,d,MOL) end @@ -110,14 +120,20 @@ end stab_formulation=Val(:Dyer_1970)) stability_correction(Tair,pressure,ustar,H, z,d; constants, stab_formulation=Val(:Dyer_1970)) + stability_correction!(df, z, d; + stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) Integrated Stability Correction Functions for Heat and Momentum # Arguments - `zeta` : Stability parameter zeta (-) - `stab_formulation` : Formulation for the stability function. Either - `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or `Val(:no_stability_correction)` -In the alternative form computes `zeta` by [`stability_parameter`](@ref) and + `Val(:Dyer_1970)`, or `Val(:Businger_1971)` or `Val(:no_stability_correction)` +- `Tair`,`pressure`,`ustar`,`H` : see [`Monin_Obukhov_length`](@ref) +- `z`,`d` : see [`stability_parameter`](@ref) +- `df` : DataFrame containting the variables required by [`Monin_Obukhov_length`](@ref) + +In the second and third form computes `zeta` by [`stability_parameter`](@ref) and [`Monin_Obukhov_length`](@ref) and requires respective arguments. # Details diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 014972c..5a24df0 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -37,7 +37,7 @@ end """ - roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_{z0m}=0.1) + roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_z0m=0.1) roughness_parameters(::Val{:canopy_height_LAI}, zh, LAI; cd=0.2, hs=0.01) roughness_parameters(::Val{:wind_profile}, df, zh, zr; d = 0.7*zh, psi_m = nothing, constants=bigleaf_constants()) @@ -51,7 +51,7 @@ and roughness length for momentum (z0m). By canopy height: - `frac_d` : Fraction of displacement height on canopy height (-) -- `frac_{z0m}` : Fraction of roughness length on canopy height (-) +- `frac_z0m` : Fraction of roughness length on canopy height (-) By canopy height and LAI - `LAI` : Leaf area index (-) @@ -62,10 +62,10 @@ By wind profile - `df` : DataFrame or matrix containing all required variables - `wind` : Wind speed at height zr (m s-1) - `ustar` : Friction velocity (m s-1) - - further variables required by [`stability_correction!`](@ref) if `psi_m` is not given + - further variables required by [`stability_correction`](@ref) if `psi_m` is not given - `zr` : Instrument (reference) height (m) - `d` : Zero-plane displacement height (m) -- `psi_m` : value of the stability function for heat, see [`stability_correction!`](@ref) +- `psi_m` : value of the stability function for heat, see [`stability_correction`](@ref) Pass `psi_m = 0.0` to neglect stability correction. - `z0m` : Roughness length for momentum (m) @@ -134,9 +134,9 @@ rp = roughness_parameters(Val(:wind_profile),df,zh,40;d=0.8*zh) true ``` """ -function roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_{z0m}=0.1) +function roughness_parameters(::Val{:canopy_height}, zh; frac_d=0.7, frac_z0m=0.1) d = frac_d*zh - z0m = frac_{z0m}*zh + z0m = frac_z0m*zh z0m_se = missing (;d, z0m, z0m_se) end @@ -158,7 +158,8 @@ function roughness_parameters(::Val{:wind_profile}, df, zh, zr; psi_m = stability_correction!(copy(df, copycols=false), zr, d; constants).psi_m end z0m_all = allowmissing(@. (zr - d) * exp(-constants[:k]*df.wind / df.ustar - psi_m)) - z0m_all[(z0m_all .> zh)] .= missing + #z0m_all[(z0m_all .> zh)] .= missing # problems with missings + replace!(x -> !ismissing(x) && x > zh ? missing : x, z0m_all) nval = sum(.!ismissing.(z0m_all)) z0m = median(skipmissing(z0m_all)) z0m_se = constants[:se_median] * (std(skipmissing(z0m_all)) / sqrt(nval)) @@ -184,14 +185,14 @@ measurements of wind speed. For DataFrame variant with supplying stability_parameter - `df`: : DataFrame with columns - `ustar` : Friction velocity (m s-1) -- `psi_m` : value of the stability function for heat, see [`stability_correction!`](@ref) +- `psi_m` : value of the stability function for heat, see [`stability_correction`](@ref) Pass `psi_m = 0.0` to neglect stability correction. For DataFrame varinat where psi_m and z0m are to be estimated - `zh` : canopy height (m) - `zr` : Instrument (reference) height (m) - `df`: : DataFrame with columns - `ustar` : Friction velocity (m s-1) - - `Tair`, `pressure`, `H` : see [`stability_correction!`](@ref) + - `Tair`, `pressure`, `H` : see [`stability_correction`](@ref) - `wind` : see [`roughness_parameters(Val(:wind_profile), ...)`] # Details @@ -243,7 +244,6 @@ plot!(heights, getindex.(ws, 2)) plot!(heights, getindex.(ws, 3)) nothing # output -nothing ``` """ function wind_profile(z::Number, ustar, d, z0m, psi_m = zero(z); constants=bigleaf_constants()) From fd3875d9a7b9fd1feb9b69a08cd5ea703a366e3f Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 4 Nov 2021 10:50:08 +0100 Subject: [PATCH 33/43] test fixing DocTest error by using DataFrames in make.jl in addition to using DataFrames in DocTestSetup --- docs/make.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 6313022..8724fa2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,16 +1,17 @@ using Bigleaf -using Documenter, Latexify +using Documenter, Latexify, DataFrames # allow plot to work without display # https://discourse.julialang.org/t/generation-of-documentation-fails-qt-qpa-xcb-could-not-connect-to-display/60988/2 ENV["GKSwstype"] = "100" -DocMeta.setdocmeta!(Bigleaf, :DocTestSetup, :(using Bigleaf, Latexify, DataFrames); recursive=true, warn=false) +DocMeta.setdocmeta!(Bigleaf, :DocTestSetup, :(using Bigleaf, Latexify, DataFrames); + recursive=true, warn=false) doctest(Bigleaf, manual = false) makedocs(; modules=[Bigleaf], - authors="Thomas Wutzler . Jürgen Knauer and contributors", + authors="Thomas Wutzler , Jürgen Knauer and contributors", repo="https://github.com/bgctw/Bigleaf.jl/blob/{commit}{path}#{line}", sitename="Bigleaf.jl", doctestfilters=[r".*Info.*"], From 1ea0ce8217eb3a8903379128f673ccae0cefc8c4 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 4 Nov 2021 16:55:17 +0100 Subject: [PATCH 34/43] implement Gs_Choudhury and Gs_Su --- inst/tha.jl | 2 +- src/Bigleaf.jl | 7 +- src/aerodynamic_conductance.jl | 73 ++++----- src/boundary_layer_conductance.jl | 234 ++++++++++++----------------- src/surface_roughness.jl | 3 +- test/boundary_layer_conductance.jl | 62 +++++++- test/runtests.jl | 10 +- test/surface_roughness.jl | 20 ++- 8 files changed, 217 insertions(+), 194 deletions(-) diff --git a/inst/tha.jl b/inst/tha.jl index 4ee6226..c1a831c 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -37,7 +37,7 @@ function tmpf() end -# tha48 and tha_heights see runtests.jl +# tha48 and thal see runtests.jl show(thaf.wind[1:48]) diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 0f75c44..61ed393 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -29,11 +29,12 @@ export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason, setinvalid_afterprecip! export decoupling, surface_conductance, aerodynamic_conductance -export compute_Gb, compute_Gb!, add_Gb, Gb_Thom +export compute_Gb, compute_Gb!, add_Gb, add_Gb!, Gb_Thom, Gb_Choudhury, Gb_Su export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, stability_parameter!, stability_correction, stability_correction!, - roughness_parameters, Reynolds_Number + roughness_parameters, Reynolds_Number + include("util.jl") include("bigleaf_constants.jl") @@ -45,6 +46,6 @@ include("evapotranspiration.jl") include("filter_data.jl") include("stability_correction.jl") include("surface_roughness.jl") -#include("boundary_layer_conductance.jl") +include("boundary_layer_conductance.jl") end diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index abc79a0..5c0c769 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -203,41 +203,41 @@ function aerodynamic_conductance(df; kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants() ) - # ## calculate canopy boundary layer conductance (Gb) - # if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] - # if Rb_model == Val(:Thom_1972) - # Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) - # elseif Rb_model == Val(:Choudhury_1988) - # Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, - # H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, - # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - # constants=constants) - # elseif Rb_model == Val(:Su_2001) - # Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, - # H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, - # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - # constants=constants) - # end - # kB_h = Gb_mod.kB_h - # Rb_h = Gb_mod.Rb_h - # Gb_h = Gb_mod.Gb_h - # # TODO - # # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) - # # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] - # elseif Rb_model == Val(:constant_kB1) - # isnothing(kB_h) && error( - # "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") - # Rb_h = kB_h/(constants[:k] * ustar) - # Gb_h = 1/Rb_h - # if (!isnothing(Sc) || !isnothing(Sc_name)) - # length(Sc) != length(Sc_name) && error( - # "arguments 'Sc' and 'Sc_name' must have the same length") - # !is_numeric(Sc) && error("argument 'Sc' must be numeric") - # Sc = SA[constants[:Sc_CO2], Sc] - # Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) - # colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - # end - # end + ## calculate canopy boundary layer conductance (Gb) + if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] + if Rb_model == Val(:Thom_1972) + Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) + elseif Rb_model == Val(:Choudhury_1988) + Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, + H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, + stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + constants=constants) + elseif Rb_model == Val(:Su_2001) + Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, + H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, + stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + constants=constants) + end + kB_h = Gb_mod.kB_h + Rb_h = Gb_mod.Rb_h + Gb_h = Gb_mod.Gb_h + # TODO + # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) + # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] + elseif Rb_model == Val(:constant_kB1) + isnothing(kB_h) && error( + "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") + Rb_h = kB_h/(constants[:k] * ustar) + Gb_h = 1/Rb_h + if (!isnothing(Sc) || !isnothing(Sc_name)) + length(Sc) != length(Sc_name) && error( + "arguments 'Sc' and 'Sc_name' must have the same length") + !is_numeric(Sc) && error("argument 'Sc' must be numeric") + Sc = SA[constants[:Sc_CO2], Sc] + Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) + colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + end + end # ## calculate aerodynamic conductance for momentum (Ga_m) # if (wind_profile) @@ -296,4 +296,5 @@ function aerodynamic_conductance(df; # Gab_x = Gab_x[rep(c(1,ncol(Gab_x)-(ncol(Gab_x)/2-1)),ncol(Gab_x)/2) + sort(rep(0:(ncol(Gab_x)/2-1),2))] # reorder columns # return(DataFrame(Ga_m,Ra_m,Ga_h,Ra_h,Gb_h,Rb_h,kB_h,zeta,psi_h,Ra_CO2,Gab_x)) -end \ No newline at end of file +end + diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index bf166b4..cd441d0 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -5,14 +5,10 @@ An empirical formulation for the canopy boundary layer conductance for heat transfer based on a simple ustar (friction velocity) dependency. # Arguments +For `Val(:Thom_1972)` - `ustar` : Friction velocity (m s-1) optional -- `Sc` : `Pair{Symbol,Number}` Output name and Schmidt number of - additional quantities to be calculated - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `k` - von-Karman constant - - `Sc_CO2` - Schmidt number for CO2 - - `Pr` - Prandtl number (if `Sc` is provided) # Details The empirical equation for Rb suggested by Thom 1972 is: @@ -54,25 +50,20 @@ a NameTuple or DataFrame with the following columns: function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols end -function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) - compute_Gb_!(df, approach, SA[zh,zr,d]; kwargs...) # inputcols +function compute_Gb!(df::AbstractDataFrame, approach::Val{:Choudhury_1988}; kwargs...) + Gb_Choudhury!(df; kwargs...) end -function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) - compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols +function compute_Gb!(df::AbstractDataFrame, approach::Val{:Su_2001}; kwargs...) + Gb_Su!(df; kwargs...) end + function compute_Gb_!(df::AbstractDataFrame, approach, inputcols; Sc::AbstractVector=SA[], kwargs... ) fGb(args...) = compute_Gb(approach, args...; kwargs...) transform!(df, :ustar => ByRow(fGb) => SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]) - if length(Sc) != 0 - Sc_names = map(x -> x.first, Sc) - transform!(df, :Gb_h => ByRow(Gb_h -> add_Gb(Gb_h, Sc...)) => Sc_names) - end end compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) -compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) -compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) """ Boundary Layer Conductance according to Thom 1972 @@ -144,6 +135,7 @@ end """ add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants=bigleaf_constants()) + add_Gb(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) compute additional boundary layer conductance quantities for given Schmidt-numbers @@ -151,12 +143,13 @@ compute additional boundary layer conductance quantities for given Schmidt-numbe - `Gb_h` : Boundary layer conductance for heat transfer (m s-1) - `Sc` : several `Pair{Symbol,Number}` Output name and Schmidt number of additional conductances to be calculated +- `df` : DataFrame to add output columns optional - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - `Pr` - Prandtl number # Value -a NameTuple with keys as in `Sc` and corresponding conductances (m s-1) +a NameTuple or `df` with keys as in `Sc` and corresponding conductances (m s-1) """ function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants=bigleaf_constants()) where N @@ -165,6 +158,13 @@ function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 Gbx = NamedTuple{Scn}(Gbxv) end +function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N + N == 0 && return(df) + ft(Gb_h) = add_Gb(Gb_h, Sc...; kwargs...) + transform!(df, :Gb_h => ByRow(ft) => AsTable) +end + + """ @@ -207,16 +207,17 @@ for heat transfer according to Choudhury & Monteith 1988. Boundary layer conductance according to Choudhury & Monteith 1988 is given by: -``Gb_h = LAI((2a/\\alpha)*\\sqrt(u(h)/w)*(1-exp(-\\alpha/2)))`` +``Gb_h = LAI((2a/\\alpha)*\\sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` -where u(zh) is the wind speed at the canopy surface, approximated from -measured wind speed at sensor height zr and a wind extinction coefficient ``\\alpha``: +where ``\\alpha`` is modeled as an empirical relation to LAI (McNaughton & van den Hurk 1995): -``u(zh) = u(zr) / (exp(\\alpha(zr/zh -1)))``. +``\\alpha = 4.39 - 3.97*exp(-0.258*LAI)`` -``\\alpha`` is modeled as an empirical relation to LAI (McNaughton & van den Hurk 1995): +``w`` is leafwidth and ``u(zh)`` is the wind speed at the canopy surface. -``\\alpha = 4.39 - 3.97*exp(-0.258*LAI)`` +It can be approximated from measured wind speed at sensor height zr and a wind extinction +coefficient ``\\alpha``: ``u(zh) = u(zr) / (exp(\\alpha(zr/zh -1)))``. +However, here it is computed from [`wind_profile`](@ref) Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. Gb for other quantities x is calculated as (Hicks et al. 1987): @@ -225,7 +226,9 @@ where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0. # Note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated -from the function `roughness_parameters` within `wind_profile`. This function +in the DataFrame variant from the function [`roughness_parameters`](@ref) within +[`wind_profile`(@ref)]. +This function estimates a single `z0m` value for the entire time period! If a varying `z0m` value (e.g. across seasons or years) is required, `z0m` should be provided as input argument. @@ -252,52 +255,29 @@ estimates a single `z0m` value for the entire time period! If a varying `z0m` va # Gb_Choudhury(data=df,leafwidth=0.01,LAI=5,zh=25,d=17.5,zr=40) ``` """ -function Gb_Choudhury(leafwidth,LAI,zh,zr,d; - z0m=nothing, +function Gb_Choudhury(ustar; leafwidth, LAI, wind_zh, constants=bigleaf_constants()) + alpha = 4.39 - 3.97*exp(-0.258*LAI) + wind_zh = max(0.01, wind_zh) ## avoid zero windspeed + Gb_h = LAI*((0.02/alpha)*sqrt(wind_zh/leafwidth)*(1-exp(-alpha/2))) + # TODO facture out derving 3 others form Gb_h? + Rb_h = 1/Gb_h + kB_h = Rb_h*constants[:k]*ustar + Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 + (;Rb_h, Gb_h, kB_h, Gb_CO2) +end + +function Gb_Choudhury!(df::AbstractDataFrame; leafwidth, LAI, zh, zr, d=0.7*zh, + z0m = nothing, stab_formulation=Val(:Dyer_1970), constants=bigleaf_constants() ) - error("Not implemented yet. Need windprofile") - # alpha = 4.39 - 3.97*exp(-0.258*LAI) - - # if isnothing(z0m) - # estimate_z0m = true - # z0m = nothing - # else - # estimate_z0m = false - # end -# wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, -# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, -# stab_correction=true,stab_formulation=stab_formulation) - -# ## avoid zero windspeed -# wind_zh = pmax(0.01,wind_zh) - -# if (!isnothing(Sc) | !isnothing(Sc_name)) -# if (length(Sc) != length(Sc_name)) -# stop("arguments 'Sc' and 'Sc_name' must have the same length") -# end -# if (!is_numeric(Sc)) -# stop("argument 'Sc' must be numeric") -# end -# end - -# Gb_h = LAI*((0.02/alpha)*sqrt(wind_zh/leafwidth)*(1-exp(-alpha/2))) -# Rb_h = 1/Gb_h -# kB_h = Rb_h*constants[:k]*ustar - -# Sc = c(constants[:Sc_CO2],Sc) -# Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) -# colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - - -# return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) + wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) + # Broadcasting does not work over keyword arguments, need to pass as positional + fwind(ustar, wind_zh; kwargs...) = Gb_Choudhury(ustar;wind_zh, kwargs...) + ft(ustar) = fwind.(ustar, wind_zh; leafwidth, LAI, constants) + transform!(df, :ustar => ft => AsTable) end - - - - """ Boundary Layer Conductance according to Su et al. 2001 @@ -340,43 +320,40 @@ to heat transfer according to Su et al. 2001. - kB_h: kB-1 parameter for heat transfer # Details - The formulation is based on the kB-1 model developed by Massman 1999. - Su et al. 2001 derived the following approximation: - - ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` - - If fc (fractional vegetation cover) is missing, it is estimated from LAI: - - ``fc = 1 - exp(-LAI/2)`` - - The wind speed at the top of the canopy is calculated using function - [`wind_profile`](@ref). - - Ct is the heat transfer coefficient of the leaf (Massman 1999): - - ``Ct = Pr^-2/3 Reh^-1/2 N`` - - where Pr is the Prandtl number (set to 0.71), and Reh is the Reynolds number for leaves: - - ``Reh = Dl wind(zh) / v`` - - kBs-1, the kB-1 value for bare soil surface, is calculated according - to Su et al. 2001: - - ``kBs^-1 = 2.46(Re)^0.25 - ln(7.4)`` - - Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. - Gb for other quantities x is calculated as (Hicks et al. 1987): +The formulation is based on the kB-1 model developed by Massman 1999. +Su et al. 2001 derived the following approximation: - ``Gb_x = Gb / (Sc_x / Pr)^0.67`` +``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` + +If fc (fractional vegetation cover) is missing, it is estimated from LAI: +``fc = 1 - exp(-LAI/2)`` + +The wind speed at the top of the canopy is calculated using function +[`wind_profile`](@ref). + +Ct is the heat transfer coefficient of the leaf (Massman 1999): + +``Ct = Pr^-2/3 Reh^-1/2 N`` + +where Pr is the Prandtl number (set to 0.71), and Reh is the Reynolds number for leaves: + +``Reh = Dl wind(zh) / v`` - where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). +kBs-1, the kB-1 value for bare soil surface, is calculated according +to Su et al. 2001: + +``kBs^-1 = 2.46(Re)^0.25 - ln(7.4)`` + +Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. +Gb for other quantities x is calculated as (Hicks et al. 1987): + ``Gb_x = Gb / (Sc_x / Pr)^0.67`` +where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). # Note If the roughness length for momentum (`z0m`) is not provided as input, it is estimated - from the function `roughness_parameters` within `wind_profile`. This function - estimates a single `z0m` value for the entire time period! If a varying `z0m` value - (e.g. across seasons or years) is required, `z0m` should be provided as input argument. +from the function `roughness_parameters` within `wind_profile`. This function +estimates a single `z0m` value for the entire time period! If a varying `z0m` value +(e.g. across seasons or years) is required, `z0m` should be provided as input argument. # References @@ -405,48 +382,35 @@ If the roughness length for momentum (`z0m`) is not provided as input, it is est # Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.1,LAI=1.5) ``` """ -function Gb_Su(df,zh,zr,d; - z0m=nothing,Dl,fc=nothing,LAI=nothing,N=2,Cd=0.2,hs=0.01, - stab_formulation=Val(:Dyer_1970), +function Gb_Su(Tair,pressure,ustar; zh, wind_zh, Dl, fc, N=2, Cd=0.2, hs=0.01, + constants=bigleaf_constants() + ) + wind_zh = max(0.01,wind_zh) ## avoid zero windspeed + v = kinematic_viscosity(Tair,pressure;constants) + Re = Reynolds_Number(Tair,pressure,ustar,hs; constants) + kBs = 2.46 * (Re)^0.25 - log(7.4) + Reh = Dl * wind_zh / v + Ct = 1*constants[:Pr]^-0.6667*Reh^-0.5*N + # + kB_h = (constants[:k]*Cd)/(4*Ct*ustar/wind_zh)*fc^2 + kBs*(1 - fc)^2 + Rb_h = kB_h/(constants[:k]*ustar) + Gb_h = 1/Rb_h + Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 + (;Rb_h, Gb_h, kB_h, Gb_CO2) +end + +function Gb_Su!(df::AbstractDataFrame; Dl, fc=nothing, N=2, Cd=0.2, hs=0.01, + z0m = nothing, zh, zr, d = 0.7*zh, LAI, + stab_formulation=Val(:Dyer_1970), constants=bigleaf_constants() ) + wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) if isnothing(fc) isnothing(LAI) && error("one of 'fc' or 'LAI' must be provided") fc = (1-exp(-LAI/2)) end - error("not yet implemented, need windo profile") -# if (isnothing(z0m)) -# estimate_z0m = true -# z0m = nothing -# else -# estimate_z0m = false -# end - -# wind_zh = wind_profile(data=df,z=zh,Tair=Tair,pressure=pressure,ustar=ustar,H=H, -# zr=zr,estimate_z0m=estimate_z0m,zh=zh,d=d,z0m=z0m,frac_{z0m}=nothing, -# stab_correction=true,stab_formulation=stab_formulation) - -# v = kinematic_viscosity(Tair,pressure,constants) -# Re = Reynolds_Number(Tair,pressure,ustar,hs,constants) -# kBs = 2.46 * (Re)^0.25 - log(7.4) -# Reh = Dl * wind_zh / v -# Ct = 1*constants[:Pr]^-0.6667*Reh^-0.5*N - -# kB_h = (constants[:k]*Cd)/(4*Ct*ustar/wind_zh)*fc^2 + kBs*(1 - fc)^2 -# Rb_h = kB_h/(constants[:k]*ustar) -# Gb_h = 1/Rb_h - -# if (!isnothing(Sc) | !isnothing(Sc_name)) -# if (length(Sc) != length(Sc_name)) -# stop("arguments 'Sc' and 'Sc_name' must have the same length") -# end -# if (!is_numeric(Sc)) -# stop("argument 'Sc' must be numeric") -# end -# end - -# Sc = c(constants[:Sc_CO2],Sc) -# Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) -# colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - -# return(DataFrame(Gb_h,Rb_h,kB_h,Gb_x)) + inputcols = SA[:Tair,:pressure,:ustar] + # Broadcasting does not work over keyword arguments, need to pass as positional + fwind(wind_zh, args...; kwargs...) = Gb_Su(args...; wind_zh, kwargs...) + ft(args...) = fwind.(wind_zh, args...; zh, Dl, fc, N=2, Cd=0.2, hs=0.01, constants) + transform!(df, inputcols => ft => AsTable) end diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 5a24df0..e0f5b4a 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -261,7 +261,8 @@ end function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; zh = nothing, zr = nothing, - stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants()) + stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants() + ) psi_m = stability_correction!( copy(df, copycols=false), z, d; stab_formulation, constants).psi_m if isnothing(z0m) diff --git a/test/boundary_layer_conductance.jl b/test/boundary_layer_conductance.jl index a6f2cd8..2fc3875 100644 --- a/test/boundary_layer_conductance.jl +++ b/test/boundary_layer_conductance.jl @@ -7,12 +7,22 @@ @test ≈(Gb.Gb_N20, 0.0173, rtol = 1/100) @test ≈(Gb.Gb_CH4, 0.0109, rtol = 1/100) # - Gb = add_Gb(missing, :Gb_N20 => 2, :Gb_CH4 => 4) - @test keys(Gb) == (:Gb_N20, :Gb_CH4) - @test all(ismissing.(values(Gb))) + Gbm = add_Gb(missing, :Gb_N20 => 2, :Gb_CH4 => 4) + @test keys(Gbm) == (:Gb_N20, :Gb_CH4) + @test all(ismissing.(values(Gbm))) + # + # DataFrame variant + dfo = DataFrame(Gb_h=SA[Gb_h, missing, 0.055]) + df = copy(dfo) + df2 = add_Gb!(df) + @test isequal(df2, dfo) + df2 = add_Gb!(df, :Gb_N20 => 2, :Gb_CH4 => 4) + @test df2 === df # mutating + @test propertynames(df2)[end-1:end] == [:Gb_N20, :Gb_CH4] + @test df2.Gb_N20[1] == Gb.Gb_N20 end -@testset "Gb_Thom" begin +@testset "compute_Gb Gb_Thom" begin Gb = Gb_Thom(0.1) @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) @test all(isapprox.(values(Gb), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) @@ -20,14 +30,50 @@ end @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) @test all(ismissing.(values(Gb))) # + # DataFrame variant dfo = DataFrame(ustar = SA[0.1,missing,0.3]) df = copy(dfo) compute_Gb!(df, Val(:Thom_1972)) @test propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2] @test all(isapprox.(values(df[1,2:end]), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) - compute_Gb!(df, Val(:Thom_1972); Sc = SA[:Gb_N20 => 2, :Gb_CH4 => 4]) - @test propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2, - :Gb_N20, :Gb_CH4] - @test all(isapprox.(values(df[1,(end-1):end]), values((Gb_N20 = 0.0173, Gb_CH4 = 0.0109)), rtol = 1e-2)) +end + +@testset "compute_Gb Gb_Choudhury" begin + zh, zr, LAI = thal.zh, thal.zr, thal.LAI + leafwidth=0.1 + wind_zh = 1.2 + Gb = Gb_Choudhury(tha48.ustar[24]; leafwidth, LAI, wind_zh) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test Gb.Rb_h ≈ 8.533 rtol=1e-3 # regression to first implementation + Gbm = Gb_Choudhury(missing; leafwidth, LAI, wind_zh) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test Gbm.Rb_h == Gb.Rb_h + # + # DataFrame variant + df = copy(tha48) + compute_Gb!(df, Val(:Choudhury_1988); leafwidth, LAI, zh, zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + all(isapprox.(values(df[24,SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]]), + (7.534, 0.1327, 2.255, 0.1008), rtol=1e-3)) +end + +@testset "compute_Gb Gb_Su" begin + zh, zr, LAI = thal.zh, thal.zr, thal.LAI + Dl=0.01; leafwidth=0.1; fc = (1-exp(-LAI/2)) + wind_zh = 1.2 + Tair, pressure, ustar = values(tha48[24, SA[:Tair, :pressure, :ustar]]) + Gb = Gb_Su(Tair, pressure, ustar; zh, wind_zh, Dl, fc) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test Gb.Rb_h ≈ 1.221 rtol=1e-3 # regression to first implementation + Gb = Gb_Su(missing, pressure, ustar; zh, wind_zh, Dl, fc) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test all(ismissing.(values(Gb))) + # + # DataFrame variant + df = copy(tha48) + compute_Gb!(df, Val(:Su_2001); Dl, LAI, zh, zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + all(isapprox.(values(df[24,SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]]), + (1.767, 0.5659, 0.5289, 0.4299), rtol=1e-3)) # regression to first implementation end diff --git a/test/runtests.jl b/test/runtests.jl index aec575c..11bc149 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,9 +15,9 @@ tha48 = DataFrame( pressure = [97.63999938964844, 97.62999725341797, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.61000061035156, 97.62999725341797, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.69000244140625, 97.69999694824219, 97.70999908447266, 97.69999694824219, 97.7300033569336, 97.72000122070312, 97.70999908447266, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.69999694824219, 97.70999908447266, 97.70999908447266, 97.72000122070312, 97.70999908447266, 97.70999908447266, 97.69999694824219, 97.68000030517578, 97.68000030517578, 97.66999816894531, 97.68000030517578, 97.66999816894531, 97.66999816894531, 97.66000366210938, 97.6500015258789, 97.63999938964844, 97.63999938964844, 97.6500015258789, 97.66000366210938, 97.66999816894531, 97.69000244140625, 97.68000030517578, 97.68000030517578, 97.69000244140625, 97.69000244140625, 97.69000244140625], H = Union{Missing, Float64}[-68.18000030517578, -48.54000091552734, -59.09999847412109, -60.11000061035156, -59.40000152587891, -48.91999816894531, -53.52999877929688, -40.75, -29.28000068664551, -33.36999893188477, -9.050000190734863, 2.400000095367432, 38.29999923706055, 38.45000076293945, 85.08000183105469, 108.7799987792969, 176.6799926757812, 210.6900024414062, 230.0800018310547, 227.4199981689453, 321.6499938964844, 358.8999938964844, 329.9299926757812, 317.3099975585938, 375.1900024414062, 336.489990234375, 260.7099914550781, 360.6400146484375, 264.2200012207031, 185.3200073242188, 123.7699966430664, 236.0200042724609, 207.5200042724609, 128.3399963378906, 66.05999755859375, 79.6500015258789, 44.93999862670898, 16.22999954223633, -22.35000038146973, -29.10000038146973, -47.79999923706055, -63.61999893188477, -61.38000106811523, -65.11000061035156, -59.77000045776367, -60.52000045776367, -49.02999877929688, -53.45000076293945], wind = Union{Missing, Float64}[4.210000038146973, 4.460000038146973, 4.539999961853027, 4.079999923706055, 3.950000047683716, 4.019999980926514, 3.269999980926514, 3.480000019073486, 3.240000009536743, 3.420000076293945, 3.440000057220459, 2.970000028610229, 3.349999904632568, 2.660000085830688, 2.779999971389771, 2.329999923706055, 2.220000028610229, 2.160000085830688, 2.519999980926514, 2.980000019073486, 2.359999895095825, 2.420000076293945, 2.920000076293945, 3.359999895095825, 2.759999990463257, 3.279999971389771, 3.410000085830688, 3.480000019073486, 3.039999961853027, 3.950000047683716, 2.700000047683716, 2.829999923706055, 2.730000019073486, 3.640000104904175, 2.75, 3.299999952316284, 2.630000114440918, 2.049999952316284, 1.879999995231628, 2.130000114440918, 2.400000095367432, 2.619999885559082, 2.640000104904175, 2.589999914169312, 2.589999914169312, 2.819999933242798, 2.779999971389771, 2.660000085830688], -) +); -tha_heights = ( +thal = ( LAI = 7.6, # leaf area index zh = 26.5, # average vegetation height (m) zr = 42, # sensor height (m) @@ -46,6 +46,12 @@ tha_heights = ( @testset "stability_correction" begin include("stability_correction.jl") end + @testset "surface_roughness" begin + include("surface_roughness.jl") + end + @testset "boundary_layer_conductance" begin + include("boundary_layer_conductance.jl") + end @testset "evapotranspiration" begin include("evapotranspiration.jl") end diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl index 6ed747b..adce8db 100644 --- a/test/surface_roughness.jl +++ b/test/surface_roughness.jl @@ -1,4 +1,4 @@ -@testset "" begin +@testset "Reynolds_Number" begin Tair,pressure,ustar,z0m = 25,100,0.5,0.5 R = Reynolds_Number(Tair,pressure,ustar,z0m) @test ≈(R, 15870, rtol=1e-3) @@ -6,9 +6,9 @@ end @testset "roughness_parameters" begin - zh = tha_heights.zh - zr = tha_heights.zr - LAI = tha_heights.LAI + zh = thal.zh + zr = thal.zr + LAI = thal.LAI keys_exp = (:d, :z0m, :z0m_se) rp = roughness_parameters(Val(:canopy_height), zh) #round.(values(rp); sigdigits = 4) @@ -21,7 +21,8 @@ end @test all(isapproxm.(values(rp), (21.77, 1.419, missing), rtol=1e-3)) # df = copy(tha48) - psi_m = stability_correction!(copy(df, copycols=false), zr, d; constants).psi_m + d=0.7*zh + psi_m = stability_correction!(copy(df, copycols=false), zr, d).psi_m rp = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m) #round.(values(rp); sigdigits = 4) @test keys(rp) == keys_exp @@ -44,14 +45,17 @@ end @testset "wind_profile" begin datetime, ustar, Tair, pressure, H = values(tha48[1,:]) z = 30 - d=0.7*tha_heights.zh - z0m=2.14 #2.65 + d=0.7*thal.zh + z0m= 2.65 u30 = wind_profile(z, ustar, d, z0m) @test ≈(u30, 1.93, rtol = 1/100 ) # u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) @test ≈(u30c, 2.31, rtol = 1/100 ) # + z0m=2.14 #2.65 + u30 = wind_profile(z, ustar, d, z0m) # used below + u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) df = copy(tha48) windz = wind_profile(df, z, d, z0m; stab_formulation = Val(:no_stability_correction)) @test length(windz) == 48 @@ -67,7 +71,7 @@ end # estimate z0m # need to give zh and zr in addition to many variables in df @test_throws Exception wind_profile(df, z, d) - windzc3 = wind_profile(df, z, d; zh=tha_heights.zh, zr=tha_heights.zr) + windzc3 = wind_profile(df, z, d; zh=thal.zh, zr=thal.zr) # may have used slightly different estimated z0m @test all(isapprox.(windzc3, windzc, atol=0.01)) end From c5efe5429b48cd04dab312b122898c14288dab7b Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 4 Nov 2021 18:30:19 +0100 Subject: [PATCH 35/43] document boundary_layer_conductance --- docs/make.jl | 1 + docs/src/boundary_layer_conductance.md | 12 + docs/src/index.md | 5 + docs/src/walkthrough.md | 14 + inst/walkthrough_todo.jmd | 15 +- src/boundary_layer_conductance.jl | 363 +++++++++++-------------- src/stability_correction.jl | 2 + src/surface_roughness.jl | 2 + 8 files changed, 201 insertions(+), 213 deletions(-) create mode 100644 docs/src/boundary_layer_conductance.md diff --git a/docs/make.jl b/docs/make.jl index 8724fa2..660a9d2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -28,6 +28,7 @@ makedocs(; hide("metorological_variables.md"), hide("stability_correction.md"), hide("surface_roughness.md"), + hide("boundary_layer_conductance.md"), hide("surface_conductance.md"), hide("evapotranspiration.md"), hide("global_radiation.md"), diff --git a/docs/src/boundary_layer_conductance.md b/docs/src/boundary_layer_conductance.md new file mode 100644 index 0000000..ac18d79 --- /dev/null +++ b/docs/src/boundary_layer_conductance.md @@ -0,0 +1,12 @@ +## Boundary Layer Conductance +```@index +Pages = ["boundary_layer_conductance.md",] +``` + +```@docs +compute_Gb! +add_Gb +Gb_Thom +Gb_Choudhury +Gb_Su +``` \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 8aef02d..de2a1e6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -23,6 +23,11 @@ Pages = ["stability_correction.md",] Pages = ["surface_roughness.md",] ``` +## Boundary Layer Conductance +```@index +Pages = ["boundary_layer_conductance.md",] +``` + ## Evapotranspiration ```@index Pages = ["evapotranspiration.md",] diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index c740fc7..497369c 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -318,6 +318,20 @@ The following figure compares them at absole scale and as difference to the ![](fig/Esat_rel.svg) +## Boundary layer conductance for trace gases + +By default, the function `aerodynamic_conductance` (calling `compute_Gb!`) returns the +(quasi-laminar) canopy boundary layer ($G_{b}$) for heat and water vapor +(which are assumed to be equal in the `Bigleaf.jl`), as well as for CO$_2$. +Functin `add_Gb` calculates $G_b$ for other trace gases, provided that the respective Schmidt +number is known. + +```@example doc +compute_Gb!(thaf, Val(:Thom_1972)) # adds/modifies column Gb_h and Gb_CO2 +add_Gb!(thaf, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) # adds Gb_O2 and Gb_CH4 +select(first(thaf,3), r"Gb_") +``` + ## Wind profile The 'big-leaf' framework assumes that wind speed is zero at height d + $z_{0m}$ diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 63f4375..20f499c 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -440,15 +440,18 @@ We see that in this case, small changes in k have an effect on the calculated va ## Boundary layer conductance for trace gases -By default, the function `aerodynamic_conductance` returns the (quasi-laminar) canopy boundary layer ($G_{b}$) for heat and water vapor (which are assumed to be equal in the `Bigleaf.jl` package), as well as for CO$_2$. The function further provides the possibility to calculate $G_b$ for other trace gases, provided that the respective Schmidt number is known. Further, if we are only interested in $G_b$ (or the kB$^{-1}$ parameter) we can use one of the following functions: `Gb_Thom`, `Gb_Choudhury`, `Gb_Su`. These functions are integrated in the main function `aerodynamic_conductance`, but the modular design of the package allows them to be called separately. We can demonstrate the calculation of $G_b$ for methane (CH$_4$) with a simple example: +By default, the functions `aerodynamic_conductance` (calling `compute_Gb!`) returns the +(quasi-laminar) canopy boundary layer ($G_{b}$) for heat and water vapor +(which are assumed to be equal in the `Bigleaf.jl` package), as well as for CO$_2$. +Functin `add_Gb` calculates $G_b$ for other trace gases, provided that the respective Schmidt +number is known. -```julia -summary(Gb_Thom(thaf$ustar)) -summary(Gb_Thom(thaf$ustar,Sc=0.99,Sc_name="CH4")) +```@example doc +compute_Gb!(thaf, Val(:Thom_1972)) # adds/modifies column Gb_h and Gb_CO2 +add_Gb!(thaf, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) # adds Gb_O2 and Gb_CH4 +select(first(thaf,3), r"Gb_") ``` -In the first line we get the standard output of the function, whereas in the second line we get in addition the $G_b$ for methane. - ## Dealing with uncertainties diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index cd441d0..b946564 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -1,50 +1,42 @@ """ -Boundary Layer Conductance according to Thom 1972 + compute_Gb!(df::AbstractDataFrame, approach; kwargs...) -An empirical formulation for the canopy boundary layer conductance -for heat transfer based on a simple ustar (friction velocity) dependency. +Estimate boundary layer conductance. # Arguments For `Val(:Thom_1972)` -- `ustar` : Friction velocity (m s-1) -optional -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - -# Details -The empirical equation for Rb suggested by Thom 1972 is: - -``Rb = 6.2 ustar^{-0.67}`` - -Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. -Gb for other quantities x is calculated as (Hicks et al. 1987): - -``Gb_x = Gb / (Sc_x / Pr)^{0.67}`` - -where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). +- `df` : DataFrame with required variables (depend on approach) +- `approach` : one of + - `Val(:Thom_1972)`: see [`Gb_Thom`](@ref) + - `Val(:Choudhury_1988)`: see [`Gb_Choudhury`](@ref) + - `Val(:Su_2001)`: see [`Gb_Su`](@ref) +The different approaches required different variables present in `df` and +different keyword arguments. + # Value -a NameTuple or DataFrame with the following columns: +updated DataFrame `df` with the following columns: - `Gb_h`: Boundary layer conductance for heat transfer (m s-1) - `Rb_h`: Boundary layer resistance for heat transfer (s m-1) - `kB_h`: kB-1 parameter for heat transfer - `Gb_CO2`: Boundary layer conductance for CO2 (m s-1). -- ...: Boundary layer conductance for keys provided with argument `Sc` (m s-1). - -# References -- Thom, A., 1972: Momentum, mass and heat exchange of vegetation. - Quarterly Journal of the Royal Meteorological Society 98, 124-134. -- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: - A preliminary multiple resistance routine for deriving dry deposition velocities - from measured quantities. Water, Air, and Soil Pollution 36, 311-330. - -# See also -[`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) -```@example; output = false -# Gb_Thom(seq(0.1,1.4,0.1)) +To subsequently compute conductances for other species with different +Schmidt numbers see [`add_Gb`](@ref). -# ## calculate Gb for SO2 as well -# Gb_Thom(seq(0.1,1.4,0.1),Sc=1.25,Sc_name="SO2") +# See also +[`Gb_Thom`](@ref), `Gb_Choudhury`](@ref), [`Gb_Su`](@ref), `Gb_Choudhury`](@ref), +[`aerodynamic_conductance`](@ref) + +```jldoctest; output = false +using DataFrames +df = DataFrame(Tair=25,pressure=100,wind=[3,4,5], + ustar=[0.5,0.6,0.65],H=[200,230,250]) +leafwidth=0.01;LAI=5;zh=25;zr=40 +compute_Gb!(df, Val(:Choudhury_1988); leafwidth, LAI, zh, zr) +≈(df.Gb_h[1], 0.329, rtol=1e-3) +# output +true ``` """ function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) @@ -66,20 +58,65 @@ end compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) """ -Boundary Layer Conductance according to Thom 1972 + add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants) + add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) -An empirical formulation for the canopy boundary layer conductance -for heat transfer based on a simple ustar (friction velocity) dependency. +compute additional boundary layer conductance quantities for given Schmidt-numbers # Arguments -- `ustar` : Friction velocity (m s-1) +- `Gb_h` : Boundary layer conductance for heat transfer (m s-1) +- `Sc` : several `Pair{Symbol,Number}` Output name and Schmidt number of + additional conductances to be calculated +- `df` : DataFrame to add output columns optional -- `Sc` : `Pair{Symbol,Number}` Output name and Schmidt number of - additional quantities to be calculated - `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `k` - von-Karman constant - - `Sc_CO2` - Schmidt number for CO2 - - `Pr` - Prandtl number (if `Sc` is provided) + - `Pr` - Prandtl number + +# Details +Boundary layer conductance for other quantities x is calculated +based on boundary layer for heat transfer as (Hicks et al. 1987): + + ``Gb_x = Gb_h / (Sc_x / Pr)^{0.67}`` + +where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). + +# Value +a NameTuple or `df` with keys as in `Sc` and corresponding conductances (m s-1) + +# Examples +```jldoctest; output=false +using DataFrames +df = DataFrame(Gb_h=[0.02, missing, 0.055]) +add_Gb!(df, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) +propertynames(df)[2:3] == [:Gb_O2, :Gb_CH4] +# output +true +``` +""" +function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; + constants=bigleaf_constants()) where N + Scn = ntuple(i -> Sc[i].first, N) + Scv = ntuple(i -> Sc[i].second, N) + Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 + Gbx = NamedTuple{Scn}(Gbxv) +end +function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N + N == 0 && return(df) + ft(Gb_h) = add_Gb(Gb_h, Sc...; kwargs...) + transform!(df, :Gb_h => ByRow(ft) => AsTable) +end + +""" + Gb_Thom(ustar; constants) + compute_Gb!(df, Val{:Thom_1972}) + +Boundary Layer Conductance according to Thom 1972, an empirical formulation for the +for heat transfer based on a simple ustar (friction velocity) dependency. + +# Arguments +- `ustar` : Friction velocity (m s-1) +- `df` : DataFrame with above variables +- `constants=`[`bigleaf_constants`](@ref)`()` # Details The empirical equation for Rb suggested by Thom 1972 is: @@ -87,19 +124,9 @@ The empirical equation for Rb suggested by Thom 1972 is: ``Rb = 6.2 ustar^{-0.67}`` Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. -Gb for other quantities x is calculated as (Hicks et al. 1987): - -``Gb_x = Gb / (Sc_x / Pr)^{0.67}`` - -where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). - + # Value -a NameTuple or DataFrame with the following columns: -- `Gb_h`: Boundary layer conductance for heat transfer (m s-1) -- `Rb_h`: Boundary layer resistance for heat transfer (s m-1) -- `kB_h`: kB-1 parameter for heat transfer -- `Gb_CO2`: Boundary layer conductance for CO2 (m s-1). -- ...: Boundary layer conductance for keys provided with argument `Sc` (m s-1). +see [`compute_Gb!`](@ref) # References - Thom, A., 1972: Momentum, mass and heat exchange of vegetation. @@ -108,14 +135,11 @@ a NameTuple or DataFrame with the following columns: A preliminary multiple resistance routine for deriving dry deposition velocities from measured quantities. Water, Air, and Soil Pollution 36, 311-330. -# See also -[`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) - ```@example; output = false -# Gb_Thom(seq(0.1,1.4,0.1)) - -# ## calculate Gb for SO2 as well -# Gb_Thom(seq(0.1,1.4,0.1),Sc=1.25,Sc_name="SO2") +using DataFrames +df = DataFrame(ustar = SA[0.1,missing,0.3]) +compute_Gb!(df, Val(:Thom_1972)) +propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2] ``` """ function Gb_Thom(ustar::Union{Missing,Number}; constants=bigleaf_constants()) @@ -125,83 +149,40 @@ function Gb_Thom(ustar::Union{Missing,Number}; constants=bigleaf_constants()) Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 (;Rb_h, Gb_h, kB_h, Gb_CO2) end -# function Gb_Thom(ustar::Missing; constants=bigleaf_constants()) -# (Rb_h = missing, Gb_h = missing, kB_h = missing, Gb_CO2 = missing) -# end - - - - - -""" - add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants=bigleaf_constants()) - add_Gb(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) - -compute additional boundary layer conductance quantities for given Schmidt-numbers - -# Arguments -- `Gb_h` : Boundary layer conductance for heat transfer (m s-1) -- `Sc` : several `Pair{Symbol,Number}` Output name and Schmidt number of - additional conductances to be calculated -- `df` : DataFrame to add output columns -optional -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `Pr` - Prandtl number - -# Value -a NameTuple or `df` with keys as in `Sc` and corresponding conductances (m s-1) -""" -function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; - constants=bigleaf_constants()) where N - Scn = ntuple(i -> Sc[i].first, N) - Scv = ntuple(i -> Sc[i].second, N) - Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 - Gbx = NamedTuple{Scn}(Gbxv) -end -function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N - N == 0 && return(df) - ft(Gb_h) = add_Gb(Gb_h, Sc...; kwargs...) - transform!(df, :Gb_h => ByRow(ft) => AsTable) -end - - """ -Boundary Layer Conductance according to Choudhury & Monteith 1988 + Gb_Choudhury(ustar; leafwidth, LAI, wind_zh, constants) + alpha = 4.39 - 3.97*exp(-0.258*LAI) + Gb_Choudhury!(df::AbstractDataFrame; leafwidth, LAI, wind_zh=nothing, zh, zr, d=0.7*zh, + z0m = nothing, stab_formulation=Val(:Dyer_1970), constants) -A formulation for the canopy boundary layer conductance +Estimate the canopy boundary layer conductance for heat transfer according to Choudhury & Monteith 1988. # Arguments -- `df` : DataFrame or matrix containing all required variables - `Tair` : Air temperature (degC) - `pressure` : Atmospheric pressure (kPa) - `wind` : Wind speed at sensor height (m s-1) - `ustar` : Friction velocity (m s-1) - `H` : Sensible heat flux (W m-2) +- `df` : DataFrame or matrix containing the above variables - `leafwidth` : Leaf width (m) - `LAI` : One-sided leaf area index +- `wind_zh` : Wind speed at canopy heihgt (m s-1), if not given then computed by + [`wind_profile`](@ref) using the arguments below - `zh` : Canopy height (m) - `zr` : Instrument (reference) height (m) -- `d` : Zero-plane displacement height (-), can be calculated using `roughness_parameters` -- `z0m` : Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` - within `wind_profile` -- `stab_formulation` : Stability correction function used (If `stab_correction = true`). - Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. -- `Sc` : Optional: Schmidt number of additional quantities to be calculated -- `Sc_name` : Optional: Name of the additonal quantities, has to be of same length than - `Sc_name` -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - _ k - von-Karman constant - _ Sc_CO2 - Schmidt number for CO2 - _ Pr - Prandtl number (if `Sc` is provided) +- `d` : Zero-plane displacement height (-), can be calculated using + `roughness_parameters` +- `z0m` : Roughness length for momentum (m). If not provided, calculated + from `roughness_parameters` within `wind_profile` using zh and zr +- `stab_formulation` : Stability correction function used, + see [`stability_correction`](@ref). +- `constants=`[`bigleaf_constants`](@ref)`()` # Value - A data frame with the following columns: - - Gb_h: Boundary layer conductance for heat transfer (m s-1) - - Rb_h: Boundary layer resistance for heat transfer (s m-1) - - kB_h: kB-1 parameter for heat transfer +see [`compute_Gb!`](@ref) # Details Boundary layer conductance according to Choudhury & Monteith 1988 is @@ -217,21 +198,13 @@ where ``\\alpha`` is modeled as an empirical relation to LAI (McNaughton & van d It can be approximated from measured wind speed at sensor height zr and a wind extinction coefficient ``\\alpha``: ``u(zh) = u(zr) / (exp(\\alpha(zr/zh -1)))``. -However, here it is computed from [`wind_profile`](@ref) +However, here (if not explicitly given) it is estimated by [`wind_profile`](@ref) Gb (=1/Rb) for water vapor and heat are assumed to be equal in this package. Gb for other quantities x is calculated as (Hicks et al. 1987): ``Gb_x = Gb / (Sc_x / Pr)^0.67`` where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). -# Note -If the roughness length for momentum (`z0m`) is not provided as input, it is estimated -in the DataFrame variant from the function [`roughness_parameters`](@ref) within -[`wind_profile`(@ref)]. -This function -estimates a single `z0m` value for the entire time period! If a varying `z0m` value -(e.g. across seasons or years) is required, `z0m` should be provided as input argument. - # References - Choudhury, B. J., Monteith J_L., 1988: A four-layer model for the heat budget of homogeneous land surfaces. Q. J. R. Meteorol. Soc. 114, 373-398. @@ -241,22 +214,11 @@ estimates a single `z0m` value for the entire time period! If a varying `z0m` va - Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: A preliminary multiple resistance routine for deriving dry deposition velocities from measured quantities. Water, Air, and Soil Pollution 36, 311-330. - -# See also -[`Gb_Thom`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) - -```@example; output = false -# ## bulk canopy boundary layer resistance for a closed canopy (LAI=5) -# ## with large leaves (leafwdith=0.1) -# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) -# Gb_Choudhury(data=df,leafwidth=0.1,LAI=5,zh=25,d=17.5,zr=40) - -# ## same conditions, but smaller leaves (leafwidth=0.01) -# Gb_Choudhury(data=df,leafwidth=0.01,LAI=5,zh=25,d=17.5,zr=40) -``` """ function Gb_Choudhury(ustar; leafwidth, LAI, wind_zh, constants=bigleaf_constants()) alpha = 4.39 - 3.97*exp(-0.258*LAI) + # (ismissing(wind_zh) || isnothing(wind_zh)) && return( + # (Rb_h=missing, Gb_h=missing, kB_h=missing, Gb_CO2=missing)) wind_zh = max(0.01, wind_zh) ## avoid zero windspeed Gb_h = LAI*((0.02/alpha)*sqrt(wind_zh/leafwidth)*(1-exp(-alpha/2))) # TODO facture out derving 3 others form Gb_h? @@ -265,13 +227,13 @@ function Gb_Choudhury(ustar; leafwidth, LAI, wind_zh, constants=bigleaf_constant Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 (;Rb_h, Gb_h, kB_h, Gb_CO2) end - -function Gb_Choudhury!(df::AbstractDataFrame; leafwidth, LAI, zh, zr, d=0.7*zh, - z0m = nothing, - stab_formulation=Val(:Dyer_1970), +function Gb_Choudhury!(df::AbstractDataFrame; leafwidth, LAI, wind_zh=nothing, + zh, zr, d=0.7*zh, z0m = nothing, stab_formulation=Val(:Dyer_1970), constants=bigleaf_constants() ) - wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) + if isnothing(wind_zh) + wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) + end # Broadcasting does not work over keyword arguments, need to pass as positional fwind(ustar, wind_zh; kwargs...) = Gb_Choudhury(ustar;wind_zh, kwargs...) ft(ustar) = fwind.(ustar, wind_zh; leafwidth, LAI, constants) @@ -279,45 +241,41 @@ function Gb_Choudhury!(df::AbstractDataFrame; leafwidth, LAI, zh, zr, d=0.7*zh, end """ -Boundary Layer Conductance according to Su et al. 2001 + Gb_Su(Tair,pressure,ustar; zh, wind_zh, Dl, fc, N=2, Cd=0.2, hs=0.01, constants) + Gb_Su!(df; wind_zh=nothing, Dl, fc=nothing, N=2, Cd=0.2, hs=0.01, + z0m = nothing, zh, zr, d = 0.7*zh, LAI, stab_formulation=Val(:Dyer_1970), constants) -A physically based formulation for the canopy boundary layer conductance -to heat transfer according to Su et al. 2001. +Estimate Boundary Layer Conductance to heat transfer using the physically based +formulation according to Su et al. 2001. # Arguments -- data DataFrame or matrix containing all required variables -- Tair Air temperature (degC) -- pressure Atmospheric pressure (kPa) -- ustar Friction velocity (m s-1) -- wind Wind speed (m s-1) -- H Sensible heat flux (W m-2) -- zh Canopy height (m) -- zr Reference height (m) -- d Zero-plane displacement height (-), can be calculated using `roughness_parameters` -- z0m Roughness length for momentum (m). If not provided, calculated from `roughness_parameters` - within `wind_profile` -- Dl Leaf characteristic dimension (m) -- fc Fractional vegetation cover [0-1] (if not provided, calculated from LAI) -- LAI One-sided leaf area index (-) -- N Number of leaf sides participating in heat exchange (defaults to 2) -- Cd Foliage drag coefficient (-) -- hs Roughness height of the soil (m) -- stab_formulation Stability correction function used (If `stab_correction = true`). - Either `Val(:Dyer_1970)` or `Val(:Businger_1971)`. -- Sc Optional: Schmidt number of additional quantities to be calculated -- Sc_name Optional: Name of the additional quantities, has to be of same length than - `Sc_name` -- constants Kelvin - conversion degree Celsius to Kelvin - pressure0 - reference atmospheric pressure at sea level (Pa) - Tair0 - reference air temperature (K) - Sc_CO2 - Schmidt number for CO2 - Pr - Prandtl number (if `Sc` is provided) +- `Tair` : Air temperature (degC) +- `pressure` : Atmospheric pressure (kPa) +- `ustar` : Friction velocity (m s-1) +- `H` : Sensible heat flux (W m-2) +- `wind` : Wind speed at sensor height (m s-1) +- `df` : DataFrame or matrix containing the above variables +- `Dl` : Leaf characteristic dimension (m) +- `fc` : Fractional vegetation cover [0-1] (if not provided, calculated from LAI) +- `LAI` : One-sided leaf area index (-) +- `N` : Number of leaf sides participating in heat exchange (defaults to 2) +- `Cd` : Foliage drag coefficient (-) +- `hs` : Roughness height of the soil (m) +- `LAI` : One-sided leaf area index +- `wind_zh` : Wind speed at canopy heihgt (m s-1) (if not given then computed by + [`wind_profile`](@ref) using the arguments below) +- `zh` : Canopy height (m) +- `zr` : Instrument (reference) height (m) +- `d` : Zero-plane displacement height (-), can be calculated using + `roughness_parameters` +- `z0m` : Roughness length for momentum (m). If not provided, calculated + from `roughness_parameters` within `wind_profile` using zh and zr +- `stab_formulation` : Stability correction function used, + see [`stability_correction`](@ref). +- `constants=`[`bigleaf_constants`](@ref)`()` # Value - A DataFrame with the following columns: - - Gb_h: Boundary layer conductance for heat transfer (m s-1) - - Rb_h: Boundary layer resistance for heat transfer (s m-1) - - kB_h: kB-1 parameter for heat transfer +see [`compute_Gb!`](@ref) # Details The formulation is based on the kB-1 model developed by Massman 1999. @@ -349,13 +307,6 @@ Gb for other quantities x is calculated as (Hicks et al. 1987): ``Gb_x = Gb / (Sc_x / Pr)^0.67`` where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). -# Note -If the roughness length for momentum (`z0m`) is not provided as input, it is estimated -from the function `roughness_parameters` within `wind_profile`. This function -estimates a single `z0m` value for the entire time period! If a varying `z0m` value -(e.g. across seasons or years) is required, `z0m` should be provided as input argument. - - # References - Su, Z., Schmugge, T., Kustas, W. & Massman, W., 2001: An evaluation of two models for estimation of the roughness height for heat transfer between @@ -366,20 +317,17 @@ estimates a single `z0m` value for the entire time period! If a varying `z0m` va A preliminary multiple resistance routine for deriving dry deposition velocities from measured quantities. Water, Air, and Soil Pollution 36, 311-330. -# See also -[`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`aerodynamic_conductance`](@ref) - -```@example; output = false -# # Canopy boundary layer resistance (and kB-1 parameter) for a set of meteorological conditions, -# # a leaf characteristic dimension of 1cm, and an LAI of 5 -# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) -# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.01,LAI=5) - -# # the same meteorological conditions, but larger leaves -# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.1,LAI=5) - -# # same conditions, large leaves, and sparse canopy cover (LAI = 1.5) -# Gb_Su(data=df,zh=25,zr=40,d=17.5,Dl=0.1,LAI=1.5) +```jldoctest; output = false +using DataFrames +df = DataFrame(Tair=25,pressure=100,wind=[3,4,5],ustar=[0.5,0.6,0.65],H=[200,230,250]) +compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.01,LAI=5) +# the same meteorological conditions, but larger leaves +compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.1,LAI=5) +# same conditions, large leaves, and sparse canopy cover (LAI = 1.5) +compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.1,LAI=1.5) +≈(df.Gb_h[1], 0.0746, rtol=1e-3) +# output +true ``` """ function Gb_Su(Tair,pressure,ustar; zh, wind_zh, Dl, fc, N=2, Cd=0.2, hs=0.01, @@ -398,12 +346,13 @@ function Gb_Su(Tair,pressure,ustar; zh, wind_zh, Dl, fc, N=2, Cd=0.2, hs=0.01, Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 (;Rb_h, Gb_h, kB_h, Gb_CO2) end - -function Gb_Su!(df::AbstractDataFrame; Dl, fc=nothing, N=2, Cd=0.2, hs=0.01, - z0m = nothing, zh, zr, d = 0.7*zh, LAI, +function Gb_Su!(df::AbstractDataFrame; wind_zh=nothing, Dl, fc=nothing, + N=2, Cd=0.2, hs=0.01, z0m = nothing, zh, zr, d = 0.7*zh, LAI, stab_formulation=Val(:Dyer_1970), constants=bigleaf_constants() ) - wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) + if isnothing(wind_zh) + wind_zh = wind_profile(df, zh, d, z0m; zh, zr, stab_formulation, constants) + end if isnothing(fc) isnothing(LAI) && error("one of 'fc' or 'LAI' must be provided") fc = (1-exp(-LAI/2)) diff --git a/src/stability_correction.jl b/src/stability_correction.jl index 356206e..1f18b03 100755 --- a/src/stability_correction.jl +++ b/src/stability_correction.jl @@ -94,6 +94,7 @@ be estimated from the function [`roughness_parameters`](@ref). The mutating variant modifies or adds column [`:zeta`]. ```jldoctest; output = false +using DataFrames df = DataFrame(Tair=25, pressure=100, ustar=0.2:0.1:1.0, H=40:20:200) zeta = stability_parameter(df;zr=40,d=15) all(zeta .< 0) @@ -175,6 +176,7 @@ a NamedTuple with the following columns: # Examples ```jldoctest; output = false +using DataFrames zeta = -2:0.5:0.5 df2 = DataFrame(stability_correction.(zeta; stab_formulation=Val(:Businger_1971))) propertynames(df2) == [:psi_h, :psi_m] diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index e0f5b4a..cd5b83b 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -118,6 +118,7 @@ a NamedTuple with the following components: [`wind_profile`](@ref) ```jldoctest; output = false +using DataFrames # estimate d and z0m from canopy height for a dense (LAI=5) and open (LAI=2) canopy zh = 25.0 roughness_parameters(Val(:canopy_height_LAI),zh,5) @@ -230,6 +231,7 @@ wind speed at given height `z`. [`roughness_parameters`](@ref) ```jldoctest; output = false +using DataFrames heights = 18:2:40 # heights above ground for which to calculate wind speed df = DataFrame(Tair=25,pressure=100,wind=[3,4,5],ustar=[0.5,0.6,0.65],H=[200,230,250]) zr=40;zh=25;d=16 From a9961678c3282877b42388ee9c24a4d9c745ea61 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 4 Nov 2021 18:43:10 +0100 Subject: [PATCH 36/43] commenting out aerodynami_conductance for now --- src/aerodynamic_conductance.jl | 68 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index 5c0c769..31c3422 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -204,40 +204,40 @@ function aerodynamic_conductance(df; ) ## calculate canopy boundary layer conductance (Gb) - if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] - if Rb_model == Val(:Thom_1972) - Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) - elseif Rb_model == Val(:Choudhury_1988) - Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, - H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, - stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - constants=constants) - elseif Rb_model == Val(:Su_2001) - Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, - H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, - stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - constants=constants) - end - kB_h = Gb_mod.kB_h - Rb_h = Gb_mod.Rb_h - Gb_h = Gb_mod.Gb_h - # TODO - # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) - # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] - elseif Rb_model == Val(:constant_kB1) - isnothing(kB_h) && error( - "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") - Rb_h = kB_h/(constants[:k] * ustar) - Gb_h = 1/Rb_h - if (!isnothing(Sc) || !isnothing(Sc_name)) - length(Sc) != length(Sc_name) && error( - "arguments 'Sc' and 'Sc_name' must have the same length") - !is_numeric(Sc) && error("argument 'Sc' must be numeric") - Sc = SA[constants[:Sc_CO2], Sc] - Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) - colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - end - end + # if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] + # if Rb_model == Val(:Thom_1972) + # Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) + # elseif Rb_model == Val(:Choudhury_1988) + # Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, + # H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, + # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + # constants=constants) + # elseif Rb_model == Val(:Su_2001) + # Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, + # H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, + # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, + # constants=constants) + # end + # kB_h = Gb_mod.kB_h + # Rb_h = Gb_mod.Rb_h + # Gb_h = Gb_mod.Gb_h + # # TODO + # # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) + # # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] + # elseif Rb_model == Val(:constant_kB1) + # isnothing(kB_h) && error( + # "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") + # Rb_h = kB_h/(constants[:k] * ustar) + # Gb_h = 1/Rb_h + # if (!isnothing(Sc) || !isnothing(Sc_name)) + # length(Sc) != length(Sc_name) && error( + # "arguments 'Sc' and 'Sc_name' must have the same length") + # !is_numeric(Sc) && error("argument 'Sc' must be numeric") + # Sc = SA[constants[:Sc_CO2], Sc] + # Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) + # colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) + # end + # end # ## calculate aerodynamic conductance for momentum (Ga_m) # if (wind_profile) From 293ee69de8812d16c8d3bdebb78553efd964d50d Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 5 Nov 2021 09:49:37 +0100 Subject: [PATCH 37/43] implement Gb_constant_kB1 --- src/Bigleaf.jl | 3 ++- src/boundary_layer_conductance.jl | 24 ++++++++++++++++++++++++ test/boundary_layer_conductance.jl | 18 ++++++++++++++++++ test/runtests.jl | 3 +++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index 61ed393..d17c021 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -29,7 +29,8 @@ export potential_ET, potential_ET!, equilibrium_imposed_ET, equilibrium_imposed_ export setinvalid_range!, setinvalid_qualityflag!, setinvalid_nongrowingseason!, get_growingseason, setinvalid_afterprecip! export decoupling, surface_conductance, aerodynamic_conductance -export compute_Gb, compute_Gb!, add_Gb, add_Gb!, Gb_Thom, Gb_Choudhury, Gb_Su +export compute_Gb, compute_Gb!, add_Gb, add_Gb!, + Gb_Thom, Gb_Choudhury, Gb_Su, Gb_constant_kB1 export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, stability_parameter!, stability_correction, stability_correction!, diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index b946564..2d834e7 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -10,6 +10,7 @@ For `Val(:Thom_1972)` - `Val(:Thom_1972)`: see [`Gb_Thom`](@ref) - `Val(:Choudhury_1988)`: see [`Gb_Choudhury`](@ref) - `Val(:Su_2001)`: see [`Gb_Su`](@ref) + - `Val(:constant_kB1)`: see [`Gb_constant_kB1`](@ref) The different approaches required different variables present in `df` and different keyword arguments. @@ -42,6 +43,9 @@ true function compute_Gb!(df::AbstractDataFrame, approach::Val{:Thom_1972}; kwargs...) compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols end +function compute_Gb!(df::AbstractDataFrame, approach::Val{:constant_kB1}; kwargs...) + compute_Gb_!(df, approach, :ustar; kwargs...) # inputcols +end function compute_Gb!(df::AbstractDataFrame, approach::Val{:Choudhury_1988}; kwargs...) Gb_Choudhury!(df; kwargs...) end @@ -56,6 +60,7 @@ function compute_Gb_!(df::AbstractDataFrame, approach, inputcols; transform!(df, :ustar => ByRow(fGb) => SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]) end compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) +compute_Gb(::Val{:constant_kB1}, args...; kB_h) = Gb_constant_kB1(args..., kB_h) """ add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants) @@ -150,6 +155,25 @@ function Gb_Thom(ustar::Union{Missing,Number}; constants=bigleaf_constants()) (;Rb_h, Gb_h, kB_h, Gb_CO2) end +""" + Gb_constant_kB1(ustar; constants) + compute_Gb!(df, Val{:Thom_1972}) + +Boundary Layer Conductance using constant XX + +# Arguments +- `kB_h` : kB-1 value for heat transfer +- `constants=`[`bigleaf_constants`](@ref)`()` + +# Details +Rb_h computed by ``kB_h/(k * ustar)``, where k is the von Karman constant. +""" +function Gb_constant_kB1(ustar, kB_h; constants=bigleaf_constants()) + Rb_h = kB_h/(constants[:k] * ustar) + Gb_h = 1/Rb_h + Gb_CO2 = Gb_h / (constants[:Sc_CO2]/constants[:Pr])^0.67 + (;Rb_h, Gb_h, kB_h, Gb_CO2) +end """ Gb_Choudhury(ustar; leafwidth, LAI, wind_zh, constants) diff --git a/test/boundary_layer_conductance.jl b/test/boundary_layer_conductance.jl index 2fc3875..7ea610a 100644 --- a/test/boundary_layer_conductance.jl +++ b/test/boundary_layer_conductance.jl @@ -22,6 +22,24 @@ @test df2.Gb_N20[1] == Gb.Gb_N20 end +@testset "compute_Gb Gb_constant_kB1" begin + kB_h = 1.18 + Gb = Gb_constant_kB1(0.1, kB_h) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test all(isapprox.(values(Gb), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) + Gb = Gb_constant_kB1(missing, kB_h) + @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) + @test Gb.kB_h == kB_h + @test all(ismissing.(values(Gb[SA[:Rb_h, :Gb_h, :Gb_CO2]]))) + # + # DataFrame variant + dfo = DataFrame(ustar = SA[0.1,missing,0.3]) + df = copy(dfo) + compute_Gb!(df, Val(:constant_kB1); kB_h) + @test propertynames(df) == [:ustar, :Rb_h, :Gb_h, :kB_h, :Gb_CO2] + @test all(isapprox.(values(df[1,2:end]), values((Rb_h = 28.8, Gb_h = 0.0347, kB_h = 1.18, Gb_CO2 = 0.0264)), rtol = 1e-2)) +end + @testset "compute_Gb Gb_Thom" begin Gb = Gb_Thom(0.1) @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) diff --git a/test/runtests.jl b/test/runtests.jl index 11bc149..ecd32ae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,6 +24,9 @@ thal = ( Dl = 0.01, # leaf characteristic dimension (m) ) + + + @testset "Bigleaf" begin @testset "util" begin include("util.jl") From e4abc540c7bd1704b9729093cbd3ef1ce3c2b02e Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 8 Nov 2021 10:34:47 +0100 Subject: [PATCH 38/43] implement add_Ga --- src/Bigleaf.jl | 5 +- src/aerodynamic_conductance.jl | 196 ++++++++++++++--------------- src/boundary_layer_conductance.jl | 33 +++-- src/stability_correction.jl | 13 ++ src/surface_roughness.jl | 7 +- test/aerodynamic_conductance.jl | 68 ++++++++++ test/boundary_layer_conductance.jl | 6 +- test/runtests.jl | 3 +- test/stability_correction.jl | 20 +++ 9 files changed, 234 insertions(+), 117 deletions(-) create mode 100644 test/aerodynamic_conductance.jl diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index d17c021..b086ac4 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -34,8 +34,8 @@ export compute_Gb, compute_Gb!, add_Gb, add_Gb!, export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, stability_parameter!, stability_correction, stability_correction!, - roughness_parameters, Reynolds_Number - + roughness_parameters, Reynolds_Number +export aerodynamic_conductance!, add_Ga, add_Ga! include("util.jl") include("bigleaf_constants.jl") @@ -48,5 +48,6 @@ include("filter_data.jl") include("stability_correction.jl") include("surface_roughness.jl") include("boundary_layer_conductance.jl") +include("aerodynamic_conductance.jl") end diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index 31c3422..91a3e5d 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -167,7 +167,10 @@ from the function `roughness_parameters` within `wind_profile` if `wind_profile and/or `Rb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` function estimates a single `z0m` value for the entire time period! If a varying `z0m` value (e.g. across seasons or years) is required, `z0m` should be provided as input argument. - + + +TODO +For adding aerodynamic conductance for other species see [`add_Ga!`](@ref). # References - Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. @@ -196,105 +199,102 @@ function estimates a single `z0m` value for the entire time period! If a varying # aerodynamic_conductance(df,Rb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) ``` """ -function aerodynamic_conductance(df; - zr,zh,d,z0m=nothing,Dl,N=2,fc=nothing,LAI,Cd=0.2,hs=0.01,wind_profile=false, - stab_correction=true,stab_formulation=Val(:Dyer_1970), - Rb_model=c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)), - kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants() +function aerodynamic_conductance!(df, Rb_model = Val(:Thom_1972); + zr,zh=nothing,d = 0.7*zh ,z0m=nothing,Dl=nothing,N=2,fc=nothing,LAI=nothing,Cd=0.2,hs=0.01, + leafwidth=nothing, + wind_profile=false, + stab_formulation=Val(:Dyer_1970), + kB_h=nothing,constants=bigleaf_constants() ) + # add zeta, psi_h and compute z0m + if !(stab_formulation isa Val{:no_stability_correction}) + stability_parameter!(df::AbstractDataFrame; zr,d, constants) + else + df[!,:zeta] .= missing + end + # adds columns psi_m and psi_h, (:no_stability_correction: just add 0s without using zeta) + stability_correction!(df; stab_formulation, constants) + if isnothing(z0m) + z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = df.psi_m).z0m + end + # calculate canopy boundary layer conductance (Gb) + Rb_model isa Val{:Thom_1972} && compute_Gb!(df, Rb_model; constants) + Rb_model isa Val{:constant_kB1} && compute_Gb!(df, Rb_model; kB_h, constants) + Rb_model isa Val{:Choudhury_1988} && compute_Gb!(df, Rb_model; + leafwidth, LAI, zh, zr, d, z0m, stab_formulation, constants) + Rb_model isa Val{:Su_2001} && compute_Gb!(df, Rb_model; + Dl, fc, N, Cd, hs, z0m, zh, zr, d, LAI, stab_formulation, constants) + # calculate aerodynamic conductance for momentum (Ga_m) + # TODO factor out to own function + df[!,:Ra_m] .= @. max((log((zr - d)/z0m) - df.psi_h),0) / (constants[:k]*ustar) + df[!,:Ga_m] .= 1/df.Ra_m + df[!,:Ra_h] .= df.Ra_m + df.Rb_h + df[!,:Ga_h] .= 1/df.Ra_h + df[!,:GA_CO2] .= 1/(df.Ra_m + 1/df.Gb_CO2) + df[!,:Ra_CO2] = 1/df.Ga_CO2 + # TODO add Ga for other species: + # Ga_x <- 1/(Ra_m + 1/Gb_x) + df +# return(DataFrame(Ga_m,Ra_m,Ga_h,Ra_h,Gb_h,Rb_h,kB_h,zeta,psi_h,Ra_CO2,Gab_x)) +end - ## calculate canopy boundary layer conductance (Gb) - # if Rb_model in SA[Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001)] - # if Rb_model == Val(:Thom_1972) - # Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) - # elseif Rb_model == Val(:Choudhury_1988) - # Gb_mod = Gb_Choudhury(df,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, - # H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, - # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - # constants=constants) - # elseif Rb_model == Val(:Su_2001) - # Gb_mod = Gb_Su(data=df,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, - # H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, - # stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, - # constants=constants) - # end - # kB_h = Gb_mod.kB_h - # Rb_h = Gb_mod.Rb_h - # Gb_h = Gb_mod.Gb_h - # # TODO - # # Gb_x = DataFrame(Gb_mod[,grep(names(Gb_mod),pattern="Gb_")[-1]]) - # # colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] - # elseif Rb_model == Val(:constant_kB1) - # isnothing(kB_h) && error( - # "value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") - # Rb_h = kB_h/(constants[:k] * ustar) - # Gb_h = 1/Rb_h - # if (!isnothing(Sc) || !isnothing(Sc_name)) - # length(Sc) != length(Sc_name) && error( - # "arguments 'Sc' and 'Sc_name' must have the same length") - # !is_numeric(Sc) && error("argument 'Sc' must be numeric") - # Sc = SA[constants[:Sc_CO2], Sc] - # Gb_x = DataFrame(lapply(Sc,function(x) Gb_h / (x/constants[:Pr])^0.67)) - # colnames(Gb_x) = paste0("Gb_",c("CO2",Sc_name)) - # end - # end - -# ## calculate aerodynamic conductance for momentum (Ga_m) -# if (wind_profile) -# if (isnothing(z0m) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) -# stop("z0m must be provided if wind_profile=true!") -# elseif (isnothing(z0m) & Rb_model in c(Val(:Choudhury_1988),Val(:Su_2001))) -# # z0m estimated as in Choudhury_1988 or Su_2001 -# z0m = roughness_parameters(method=Val(:wind_profile),zh=zh,zr=zr,d=d,data=df, -# Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, -# stab_roughness=true,stab_formulation=stab_formulation, -# constants=constants)[,"z0m"] -# end - -# if (stab_correction) - -# zeta = stability_parameter(data=df,Tair=Tair,pressure=pressure,ustar=ustar, -# H=H,zr=zr,d=d,constants=constants) - -# if (stab_formulation in c(Val(:Dyer_1970),Val(:Businger_1971))) - -# psi_h = stability_correction(zeta,formulation=stab_formulation)[,"psi_h"] - -# else -# stop("'stab_formulation' has to be one of 'Dyer_1970' or 'Businger_1971'. -# Choose 'stab_correction = false' if no stability correction should be applied.") -# end - -# Ra_m = pmax((log((zr - d)/z0m) - psi_h),0) / (constants[:k]*ustar) - -# else - -# Ra_m = pmax((log((zr - d)/z0m)),0) / (constants[:k]*ustar) -# zeta = psi_h = rep(NA_integer_,length=length(Ra_m)) - -# end - -# else - -# if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) -# @warn"Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") -# end - -# Ra_m = wind / ustar^2 -# zeta = psi_h = rep(NA_integer_,length=length(Ra_m)) - -# end - -# Ga_m = 1/Ra_m -# Ra_h = Ra_m + Rb_h -# Ga_h = 1/Ra_h -# Ga_x = 1/(Ra_m + 1/Gb_x) -# Ra_CO2 = 1/Ga_x[,1] -# colnames(Ga_x) = paste0("Ga_",c("CO2",Sc_name)) - -# Gab_x = cbind(Ga_x,Gb_x) -# Gab_x = Gab_x[rep(c(1,ncol(Gab_x)-(ncol(Gab_x)/2-1)),ncol(Gab_x)/2) + sort(rep(0:(ncol(Gab_x)/2-1),2))] # reorder columns +""" + add_Ga(Gb_h, Ga_m, Sc::Vararg{Pair,N}; constants) + add_Ga!(df::AbstractDataFrame, Sc; Gb_h = df.Gb_h, Ga_m = df.Ga.m, kwargs...) -# return(DataFrame(Ga_m,Ra_m,Ga_h,Ra_h,Gb_h,Rb_h,kB_h,zeta,psi_h,Ra_CO2,Gab_x)) +compute additional aerodynamic conductance quantities for given Schmidt-numbers + +# Arguments +- `Gb_h` : Boundary layer conductance for heat transfer (m s-1) +- `Ga_m` : Aerodynamic conductance for momentum (m s-1) +- `Sc` : several `Pair{Symbol,Number}` Output name and Schmidt number of + additional conductances to be calculated +- `df` : DataFrame to add output columns +optional +- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries + - `Pr` - Prandtl number + +# Details +Aerodynamic conductance is calculated as + +``Ga_x = 1/(1/Ga_m + 1/Gb_x)`` + +where Gb_x is the Boundary layer conductance for other quantities x is calculated +based on boundary layer for heat transfer, Schmidt-Number, and Prantl number, +as documented in [`add_Gb`](@ref). + +# Value +a NameTuple or `df` with keys `Ga_x` where `x` are the keys in `Sc` and +corresponding aerodynamic conductances (m s-1). + +# Examples +```jldoctest; output=false +using DataFrames +df = DataFrame(Gb_h=[0.02, missing, 0.055], Ga_m = [0.03, 0.03, 0.03]) +add_Ga!(df, :O2 => 0.84, :CH4 => 0.99) +propertynames(df)[3:4] == [:Ga_O2, :Ga_CH4] +# output +true +``` +""" +function add_Ga(Gb_h::Union{Missing,Number}, Ga_m::Union{Missing,Number}, + Sc::Vararg{Pair,N}; kwargs...) where N + Scn, Scv = get_names_and_values("Ga_", Sc...) + add_Ga_(Gb_h, Ga_m, Scn, Scv; kwargs...) +end +function add_Ga_(Gb_h::Union{Missing,Number}, Ga_m::Union{Missing,Number}, + Scn::NTuple{N,Symbol}, Scv::NTuple{N}; constants=bigleaf_constants()) where N + Gbx = add_Gb_(Gb_h, Scn, Scv; constants) + Gaxv = ntuple(i -> 1/(1/Ga_m + 1/Gbx[i]), length(Gbx)) + Gax = NamedTuple{Scn}(Gaxv) end +function add_Ga!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, Ga_m = df.Ga_m, kwargs...) where N + N == 0 && return(df) + Scn, Scv = get_names_and_values("Ga_", Sc...) + ft() = add_Ga_.(Gb_h, Ga_m, Ref(Scn), Ref(Scv); kwargs...) + transform!(df, [] => ft => AsTable) +end + + + diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index 2d834e7..39b0d80 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -60,7 +60,7 @@ function compute_Gb_!(df::AbstractDataFrame, approach, inputcols; transform!(df, :ustar => ByRow(fGb) => SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]) end compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) -compute_Gb(::Val{:constant_kB1}, args...; kB_h) = Gb_constant_kB1(args..., kB_h) +compute_Gb(::Val{:constant_kB1}, args...; kB_h, constants) = Gb_constant_kB1(args..., kB_h; constants) """ add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants) @@ -86,29 +86,38 @@ based on boundary layer for heat transfer as (Hicks et al. 1987): where `Sc_x` is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). # Value -a NameTuple or `df` with keys as in `Sc` and corresponding conductances (m s-1) +a NameTuple or `df` with keys `Gb_x` where `x` are the keys in `Sc` and +corresponding boundary layer conductances (m s-1). # Examples ```jldoctest; output=false using DataFrames df = DataFrame(Gb_h=[0.02, missing, 0.055]) -add_Gb!(df, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) +add_Gb!(df, :O2 => 0.84, :CH4 => 0.99) propertynames(df)[2:3] == [:Gb_O2, :Gb_CH4] # output true ``` """ -function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; - constants=bigleaf_constants()) where N - Scn = ntuple(i -> Sc[i].first, N) - Scv = ntuple(i -> Sc[i].second, N) - Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 - Gbx = NamedTuple{Scn}(Gbxv) +function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; kwargs...) where N + Scn, Scv = get_names_and_values("Gb_", Sc...) + add_Gb_(Gb_h, Scn, Scv; kwargs...) +end +function add_Gb_(Gb_h::Union{Missing,Number}, Scn::NTuple{N,Symbol}, Scv::NTuple{N}; + constants=bigleaf_constants()) where N + Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 + Gbx = NamedTuple{Scn}(Gbxv) end function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N N == 0 && return(df) - ft(Gb_h) = add_Gb(Gb_h, Sc...; kwargs...) - transform!(df, :Gb_h => ByRow(ft) => AsTable) + Scn, Scv = get_names_and_values("Gb_", Sc...) + ft() = add_Gb_.(Gb_h, Ref(Scn), Ref(Scv); kwargs...) + transform!(df, [] => ft => AsTable) +end +function get_names_and_values(prefix::AbstractString, Sc::Vararg{Pair,N}) where N + Scn = ntuple(i -> Symbol(prefix * string(Sc[i].first)), N) + Scv = ntuple(i -> Sc[i].second, N) + Scn, Scv end """ @@ -381,6 +390,8 @@ function Gb_Su!(df::AbstractDataFrame; wind_zh=nothing, Dl, fc=nothing, isnothing(LAI) && error("one of 'fc' or 'LAI' must be provided") fc = (1-exp(-LAI/2)) end + isnothing(Dl) && error( + "need to provide keyword argument Dl with :Su_2001 method") inputcols = SA[:Tair,:pressure,:ustar] # Broadcasting does not work over keyword arguments, need to pass as positional fwind(wind_zh, args...; kwargs...) = Gb_Su(args...; wind_zh, kwargs...) diff --git a/src/stability_correction.jl b/src/stability_correction.jl index 1f18b03..580cdcc 100755 --- a/src/stability_correction.jl +++ b/src/stability_correction.jl @@ -218,6 +218,7 @@ function get_stability_coefs_unstable(::Val{:Dyer_1970}, zeta) (;y_h, y_m) end +#TODO z or zr here? function stability_correction(Tair,pressure,ustar,H, z,d; stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) stab_formulation isa Val{:no_stability_correction} && return( @@ -227,6 +228,18 @@ function stability_correction(Tair,pressure,ustar,H, z,d; stability_correction(zeta; stab_formulation) end +function stability_correction!(df; zeta=df.zeta, + stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) + # cannot dispatch on keyword argument, hence need if-clause + if stab_formulation isa Val{:no_stability_correction} + df[!,:psi_h] .= 0.0 + df[!,:psi_m] .= 0.0 + return(df) + end + ft() = stability_correction.(zeta; stab_formulation) + transform!(df, [] => ft => AsTable) +end + function stability_correction!(df, z, d; stab_formulation=Val(:Dyer_1970), constants = bigleaf_constants()) ft(args...) = stability_correction(args..., z, d; stab_formulation, constants) diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index cd5b83b..07f71dd 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -153,10 +153,12 @@ end # docu: supply psi_m = 0 for no stability correction, default method # if psi_m is given df only needs wind and ustar function roughness_parameters(::Val{:wind_profile}, df, zh, zr; - d = 0.7*zh, psi_m = nothing, constants=bigleaf_constants() + d = 0.7*zh, psi_m = nothing, stab_formulation=Val(:Dyer_1970), + constants=bigleaf_constants() ) if isnothing(psi_m) - psi_m = stability_correction!(copy(df, copycols=false), zr, d; constants).psi_m + psi_m = stability_correction!(copy(df, copycols=false), zr, d; + stab_formulation, constants).psi_m end z0m_all = allowmissing(@. (zr - d) * exp(-constants[:k]*df.wind / df.ustar - psi_m)) #z0m_all[(z0m_all .> zh)] .= missing # problems with missings @@ -265,6 +267,7 @@ function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; zh = nothing, zr = nothing, stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants() ) + # TODO providingn z or zr to stabiity correction? psi_m = stability_correction!( copy(df, copycols=false), z, d; stab_formulation, constants).psi_m if isnothing(z0m) diff --git a/test/aerodynamic_conductance.jl b/test/aerodynamic_conductance.jl new file mode 100644 index 0000000..225baf3 --- /dev/null +++ b/test/aerodynamic_conductance.jl @@ -0,0 +1,68 @@ +@testset "add_Ga" begin + Gb_h = 0.0347 + Ga_m = 0.3 + @test add_Ga(Gb_h, Ga_m) == NamedTuple() + # + Ga = add_Ga(Gb_h, Ga_m, :N20 => 2, :CH4 => 4) + @test keys(Ga) == (:Ga_N20, :Ga_CH4) + @test ≈(Ga.Ga_N20, 0.0164, rtol = 1/100) + @test ≈(Ga.Ga_CH4, 0.0105, rtol = 1/100) + # + Gam = add_Ga(Gb_h, missing, :N20 => 2, :CH4 => 4) + @test keys(Gam) == (:Ga_N20, :Ga_CH4) + @test all(ismissing.(values(Gam))) + # + # DataFrame variant + dfo = DataFrame(Gb_h=SA[Gb_h, missing, 0.055], Ga_m = SA[0.3,0.3,0.3]) + df = copy(dfo) + df2 = add_Gb!(df) + @test isequal(df2, dfo) + df2 = add_Ga!(df, :N20 => 2, :CH4 => 4) + @test df2 === df # mutating + @test propertynames(df2)[end-1:end] == [:Ga_N20, :Ga_CH4] + @test df2.Ga_N20[1] == Ga.Ga_N20 +end + + + + +@testset "compute_Gb :no_stability_correction" begin + dfo = DataFrame(ustar = SA[0.1,missing,0.3], wind = [3.4, 2.8, 3.3]) + df = copy(dfo) + aerodynamic_conductance!(df, Val(:Thom_1972); zh = thal.zh, zr = thal.zr, + stab_formulation = Val(:no_stability_correction)) + @test all(iszero.(df.psi_h)) + @test all(iszero.(df.psi_m)) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] +end + + +@testset "compute_Gb Gb_Thom" begin + df = copy(tha48) + aerodynamic_conductance!(df, Val(:Thom_1972); zh = thal.zh, zr = thal.zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] +end + +@testset "constant_kB1" begin + kB_h = 1.18 + # DataFrame variant + df = copy(tha48) + aerodynamic_conductance!(df, Val(:constant_kB1); kB_h, zh = thal.zh, zr = thal.zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] +end + +@testset "compute_Gb Gb_Choudhury" begin + leafwidth=0.1 + df = copy(tha48) + aerodynamic_conductance!(df, Val(:Choudhury_1988); + leafwidth, LAI=thal.LAI, zh = thal.zh, zr = thal.zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] +end + +@testset "compute_Gb Gb_Su" begin + Dl=0.01 + df = copy(tha48) + aerodynamic_conductance!(df, Val(:Su_2001); Dl, LAI=thal.LAI, zh=thal.zh, zr=thal.zr) + @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] +end + diff --git a/test/boundary_layer_conductance.jl b/test/boundary_layer_conductance.jl index 7ea610a..04c0c68 100644 --- a/test/boundary_layer_conductance.jl +++ b/test/boundary_layer_conductance.jl @@ -2,12 +2,12 @@ Gb_h = 0.0347 @test add_Gb(Gb_h) == NamedTuple() # - Gb = add_Gb(Gb_h, :Gb_N20 => 2, :Gb_CH4 => 4) + Gb = add_Gb(Gb_h, :N20 => 2, :CH4 => 4) @test keys(Gb) == (:Gb_N20, :Gb_CH4) @test ≈(Gb.Gb_N20, 0.0173, rtol = 1/100) @test ≈(Gb.Gb_CH4, 0.0109, rtol = 1/100) # - Gbm = add_Gb(missing, :Gb_N20 => 2, :Gb_CH4 => 4) + Gbm = add_Gb(missing, :N20 => 2, :CH4 => 4) @test keys(Gbm) == (:Gb_N20, :Gb_CH4) @test all(ismissing.(values(Gbm))) # @@ -16,7 +16,7 @@ df = copy(dfo) df2 = add_Gb!(df) @test isequal(df2, dfo) - df2 = add_Gb!(df, :Gb_N20 => 2, :Gb_CH4 => 4) + df2 = add_Gb!(df, :N20 => 2, :CH4 => 4) @test df2 === df # mutating @test propertynames(df2)[end-1:end] == [:Gb_N20, :Gb_CH4] @test df2.Gb_N20[1] == Gb.Gb_N20 diff --git a/test/runtests.jl b/test/runtests.jl index ecd32ae..26a5aca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,7 @@ using Bigleaf using Test, StableRNGs -using Pipe, DataFrames, Dates, TimeZones +using Pipe: @pipe +using DataFrames, Dates, TimeZones using StaticArrays using Statistics, StatsBase diff --git a/test/stability_correction.jl b/test/stability_correction.jl index c62aaf7..87fd35b 100644 --- a/test/stability_correction.jl +++ b/test/stability_correction.jl @@ -42,6 +42,26 @@ end @test resm == (psi_h = 0.0, psi_m = 0.0) end +@testset "stability_correction DataFrame variant" begin + zr=40;d=15 + dfo = DataFrame(Tair=25, pressure=100, ustar=0.2:0.1:1.0, H=40:20:200) + df = copy(dfo) + stability_correction!(df, zr, d) + propertynames(df)[(end-1):end] == SA[:psi_h, :psi_m] + # + dfm = copy(dfo) + stability_correction!(dfm, zr, d; stab_formulation = Val(:no_stability_correction)) + propertynames(dfm)[(end-1):end] == SA[:psi_h, :psi_m] + @test all(iszero.(dfm.psi_h)) + @test all(iszero.(dfm.psi_m)) + # + df2 = copy(dfo) + stability_parameter!(df2; zr, d) # adds zeta + stability_correction!(df2) + @test df2.psi_h == df.psi_h + @test df2.psi_m == df.psi_m +end + @testset "stability_correction from raw" begin datetime, ustar, Tair, pressure, H = values(tha48[24,1:5]) z=40.0;d=15.0 From 0799b84b4a4aa891d01429c067163cdfd04a1afa Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 8 Nov 2021 22:06:31 +0100 Subject: [PATCH 39/43] implement and document aerodynamic_conductance! --- docs/make.jl | 1 + docs/src/aerodynamic_conductance.md | 11 + docs/src/boundary_layer_conductance.md | 2 +- docs/src/index.md | 5 + docs/src/surface_conductance.md | 1 - docs/src/walkthrough.md | 57 ++++ inst/fromR/aerodynamic_conductance.jl | 58 ++-- inst/fromR/bigleaf_physiology.jl | 4 +- inst/fromR/boundary_layer_conductance.jl | 6 +- inst/fromR/decoupling.jl | 2 +- inst/fromR/evapotranspiration.jl | 2 +- inst/fromR/surface_conditions.jl | 4 +- inst/fromR/surface_conductance.jl | 2 +- inst/tha.jl | 14 +- inst/walkthrough_todo.jmd | 52 ++- src/Bigleaf.jl | 2 +- src/aerodynamic_conductance.jl | 398 ++++++++++++----------- src/boundary_layer_conductance.jl | 28 +- src/evapotranspiration.jl | 11 +- test/aerodynamic_conductance.jl | 56 +++- test/runtests.jl | 3 + 21 files changed, 423 insertions(+), 296 deletions(-) create mode 100644 docs/src/aerodynamic_conductance.md diff --git a/docs/make.jl b/docs/make.jl index 660a9d2..6c642e4 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -30,6 +30,7 @@ makedocs(; hide("surface_roughness.md"), hide("boundary_layer_conductance.md"), hide("surface_conductance.md"), + hide("aerodynamic_conductance.md"), hide("evapotranspiration.md"), hide("global_radiation.md"), hide("unit_conversions.md"), diff --git a/docs/src/aerodynamic_conductance.md b/docs/src/aerodynamic_conductance.md new file mode 100644 index 0000000..80d7a96 --- /dev/null +++ b/docs/src/aerodynamic_conductance.md @@ -0,0 +1,11 @@ +## Aerodynamic Conductance +```@index +Pages = ["aerodynamic_conductance.md",] +``` + +```@docs +aerodynamic_conductance! +compute_Ram +roughness_z0h +add_Ga! +``` \ No newline at end of file diff --git a/docs/src/boundary_layer_conductance.md b/docs/src/boundary_layer_conductance.md index ac18d79..2002f09 100644 --- a/docs/src/boundary_layer_conductance.md +++ b/docs/src/boundary_layer_conductance.md @@ -5,7 +5,7 @@ Pages = ["boundary_layer_conductance.md",] ```@docs compute_Gb! -add_Gb +add_Gb! Gb_Thom Gb_Choudhury Gb_Su diff --git a/docs/src/index.md b/docs/src/index.md index de2a1e6..16280b8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -28,6 +28,11 @@ Pages = ["surface_roughness.md",] Pages = ["boundary_layer_conductance.md",] ``` +## Aerodynamic Conductance +```@index +Pages = ["aerodynamic_conductance.md",] +``` + ## Evapotranspiration ```@index Pages = ["evapotranspiration.md",] diff --git a/docs/src/surface_conductance.md b/docs/src/surface_conductance.md index ccfeb4c..64e042c 100644 --- a/docs/src/surface_conductance.md +++ b/docs/src/surface_conductance.md @@ -6,5 +6,4 @@ Pages = ["surface_conductance.md",] ```@docs decoupling surface_conductance -aerodynamic_conductance ``` \ No newline at end of file diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 497369c..56169d7 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -251,6 +251,12 @@ setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) sum(.!thaf.valid) # some more invalids ``` +```@example doc +thas = subset(thaf, :valid) +``` + + + ## Meteorological variables @@ -318,6 +324,57 @@ The following figure compares them at absole scale and as difference to the ![](fig/Esat_rel.svg) +## Aerodynamic conductance + +An important metric for many calculations in the `Bigleaf.jl` package is the aerodynamic +conductance ($G_a$) between the land surface and the measurement height. $G_a$ +characterizes how efficiently mass and energy is transferred between the land surface +and the atmosphere. $G_a$ consists of two parts: $G_{a_m}$, the aerodynamic conductance +for momentum, and $G_b$, the canopy boundary layer (or quasi-laminar) conductance. +$G_a$ can be defined as + + $G_a = 1/(1/G_{a_m} + 1/G_b)$. + +In this tutorial we will focus on +how to use the function [`aerodynamic_conductance!`](@ref). +For further details on the equations, +the reader is directed to the publication of the Bigleaf package (Knauer et al. 2018) and +the references therein. A good overview is provided by e.g. Verma 1989. + + $G_a$ and in particular $G_b$ can be calculated with varying degrees of complexity. +We start with the simplest version, in which $G_b$ is calculated empirically based on +the friction velocity ($u_*$) according to Thom 1972: + +```@example doc +aerodynamic_conductance!(thas) +thas[1:3, Cols(:datetime,Between(:zeta,:Ga_CO2))] +``` + +Note that by not providing additional arguments, the default values are taken. +We also do not need most of the arguments that can be provided to the function in this case +(i.e. with `Gb_model=Val(:Thom_1972)`). These are only required if we use a more complex +formulation of $G_b$. +The output of the function is another DataFrame which contains separate columns for +conductances and resistances of different scalars (momentum, heat, and $CO_2$ by default). + +For comparison, we now calculate a second estimate of $G_a$, where the calculation of +$G_b$ is more physically-based (Su et al. 2001), and which requires more input variables +compared to the first version. In particular, we now need LAI, the leaf characteristic +dimension ($D_l$, assumed to be 1cm here), and information on sensor and canopy height +($z_r$ and $z_h$), as well as the displacement height (assumed to be 0.7*$z_h$): + + +```@example doc +aerodynamic_conductance!(thas;Gb_model=Val(:Su_2001), + LAI=thal.zh, zh=thal.zh, d=0.7*thal.zh, zr=thal.zr,Dl=thal.Dl) +thas[1:3, Cols(:datetime,Between(:zeta,:Ga_CO2))] +``` + +We see that the values are different compared to the first, empirical estimate. +This is because this formulation takes additional aerodynamically relevant properties +(LAI, $D_l$) into account that were not considered by the simple empirical formulation. + + ## Boundary layer conductance for trace gases By default, the function `aerodynamic_conductance` (calling `compute_Gb!`) returns the diff --git a/inst/fromR/aerodynamic_conductance.jl b/inst/fromR/aerodynamic_conductance.jl index 505eeba..1e14578 100755 --- a/inst/fromR/aerodynamic_conductance.jl +++ b/inst/fromR/aerodynamic_conductance.jl @@ -17,23 +17,23 @@ #' - zh Canopy height (m) #' - d Zero-plane displacement height (m) #' - z0m Roughness length for momentum (m), optional; if not provided, it is estimated from `roughness_parameters` -#' (method=Val(:wind_profile)). Only used if `wind_profile = true` and/or `Rb_model` = `Val(:Su_2001)` or +#' (method=Val(:wind_profile)). Only used if `wind_profile = true` and/or `Gb_model` = `Val(:Su_2001)` or #' `Val(:Choudhury_1988)`. -#' - Dl Characteristic leaf dimension (m) (if `Rb_model` = `Val(:Su_2001)`) -#' or leaf width (if `Rb_model` = `Val(:Choudhury_1988)`); ignored otherwise. -#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if `Rb_model = Val(:Su_2001)`. +#' - Dl Characteristic leaf dimension (m) (if `Gb_model` = `Val(:Su_2001)`) +#' or leaf width (if `Gb_model` = `Val(:Choudhury_1988)`); ignored otherwise. +#' - N Number of leaf sides participating in heat exchange (1 or 2); only used if `Gb_model = Val(:Su_2001)`. #' Defaults to 2. -#' - fc Fractional vegetation cover (-); only used if `Rb_model = Val(:Su_2001)`. See Details. -#' - LAI One-sided leaf area index (m2 m-2); only used if `Rb_model` = `Val(:Choudhury_1988)` or `Val(:Su_2001)`. -#' - Cd Foliage drag coefficient (-); only used if `Rb_model = Val(:Su_2001)`. -#' - hs Roughness length of bare soil (m); only used if `Rb_model = Val(:Su_2001)`. +#' - fc Fractional vegetation cover (-); only used if `Gb_model = Val(:Su_2001)`. See Details. +#' - LAI One-sided leaf area index (m2 m-2); only used if `Gb_model` = `Val(:Choudhury_1988)` or `Val(:Su_2001)`. +#' - Cd Foliage drag coefficient (-); only used if `Gb_model = Val(:Su_2001)`. +#' - hs Roughness length of bare soil (m); only used if `Gb_model = Val(:Su_2001)`. #' - wind_profile Should Ga for momentum be calculated based on the logarithmic wind profile equation? #' Defaults to `false`. #' - stab_correction Should stability correction be applied? Defaults to `true`. Ignored if `wind_profile = false`. #' - stab_formulation Stability correction function. Either `Val(:Dyer_1970)` (default) or #' `Val(:Businger_1971)`. Ignored if `wind_profile = false` or if `stab_correction = false`. -#' - Rb_model Boundary layer resistance formulation. One of `Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)`. -#' - kB_h kB-1 value for heat transfer; only used if `Rb_model = Val(:constant_kB1)` +#' - Gb_model Boundary layer resistance formulation. One of `Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)`. +#' - kB_h kB-1 value for heat transfer; only used if `Gb_model = Val(:constant_kB1)` #' - Sc Optional: Schmidt number of additional quantities to be calculated #' - Sc_name Optional: Name of the additonal quantities, has to be of same length than #' `Sc_name` @@ -78,15 +78,15 @@ #' `Val(:Dyer_1970)` and `Val(:Businger_1971)`. #' #' The model used to determine the canopy boundary layer resistance for heat (Rb_h) is specified by -#' the argument `Rb_model`. The following options are implemented: +#' the argument `Gb_model`. The following options are implemented: #' `Val(:Thom_1972)` is an empirical formulation based on the friction velocity (ustar) (Thom 1972): #' #' ``Rb_h = 6.2ustar^-0.667`` #' -#' The model by Choudhury & Monteith 1988 (`Rb_model = Val(:Choudhury_1988)`), +#' The model by Choudhury & Monteith 1988 (`Gb_model = Val(:Choudhury_1988)`), #' calculates Rb_h based on leaf width, LAI and ustar (Note that function argument `Dl` #' represents leaf width (w) and not characteristic leaf dimension (Dl) -#' if `Rb_model` = `Val(:Choudhury_1988)`): +#' if `Gb_model` = `Val(:Choudhury_1988)`): #' #' ``Gb_h = LAI((0.02/\\alpha)*sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` #' @@ -94,7 +94,7 @@ #' u(zh) is wind speed at canopy height (calculated from [`wind_profile`](@ref)), #' and w is leaf width (m). See [`Gb_Choudhury`](@ref) for further details. #' -#' The option `Rb_model = Val(:Su_2001)` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, +#' The option `Gb_model = Val(:Su_2001)` calculates Rb_h based on the physically-based Rb model by Su et al. 2001, #' a simplification of the model developed by Massman 1999: #' #' ``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` @@ -156,7 +156,7 @@ #' #' If the roughness length for momentum (`z0m`) is not provided as input, it is estimated #' from the function `roughness_parameters` within `wind_profile` if `wind_profile = true` -#' and/or `Rb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` +#' and/or `Gb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` #' function estimates a single `z0m` value for the entire time period! If a varying `z0m` value #' (e.g. across seasons or years) is required, `z0m` should be provided as input argument. #' @@ -183,42 +183,42 @@ #' df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) #' #' # simple calculation of Ga -#' aerodynamic_conductance(df,Rb_model=Val(:Thom_1972)) +#' aerodynamic_conductance(df,Gb_model=Val(:Thom_1972)) #' #' # calculation of Ga using a model derived from the logarithmic wind profile -#' aerodynamic_conductance(df,Rb_model=Val(:Thom_1972),zr=40,zh=25,d=17.5,z0m=2,wind_profile=true) +#' aerodynamic_conductance(df,Gb_model=Val(:Thom_1972),zr=40,zh=25,d=17.5,z0m=2,wind_profile=true) #' #' # simple calculation of Ga, but a physically based canopy boundary layer model -#' aerodynamic_conductance(df,Rb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) +#' aerodynamic_conductance(df,Gb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) #' """ """ function aerodynamic_conductance(data,Tair="Tair",pressure="pressure",wind="wind",ustar="ustar",H="H", zr,zh,d,z0m=nothing,Dl,N=2,fc=nothing,LAI,Cd=0.2,hs=0.01,wind_profile=false, stab_correction=true,stab_formulation=c(Val(:Dyer_1970),Val(:Businger_1971)), - Rb_model=c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)), + Gb_model=c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)), kB_h=nothing,Sc=nothing,Sc_name=nothing,constants=bigleaf_constants()) - Rb_model = match_arg(Rb_model) + Gb_model = match_arg(Gb_model) stab_formulation = match_arg(stab_formulation) check_input(data,list(Tair,pressure,wind,ustar,H)) ## calculate canopy boundary layer conductance (Gb) - if (Rb_model in c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001))) + if (Gb_model in c(Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001))) - if (Rb_model == Val(:Thom_1972)) + if (Gb_model == Val(:Thom_1972)) Gb_mod = Gb_Thom(ustar=ustar,Sc=Sc,Sc_name=Sc_name,constants=constants) -elseif (Rb_model == Val(:Choudhury_1988)) +elseif (Gb_model == Val(:Choudhury_1988)) Gb_mod = Gb_Choudhury(data,Tair=Tair,pressure=pressure,wind=wind,ustar=ustar, H=H,leafwidth=Dl,LAI=LAI,zh=zh,zr=zr,d=d,z0m=z0m, stab_formulation=stab_formulation,Sc=Sc,Sc_name=Sc_name, constants=constants) -elseif (Rb_model == Val(:Su_2001)) +elseif (Gb_model == Val(:Su_2001)) Gb_mod = Gb_Su(data=data,Tair=Tair,pressure=pressure,ustar=ustar,wind=wind, H=H,zh=zh,zr=zr,d=d,z0m=z0m,Dl=Dl,N=N,fc=fc,LAI=LAI,Cd=Cd,hs=hs, @@ -234,10 +234,10 @@ end colnames(Gb_x) = grep(colnames(Gb_mod),pattern="Gb_",value=true)[-1] -elseif (Rb_model == Val(:constant_kB1)) +elseif (Gb_model == Val(:constant_kB1)) if(isnothing(kB_h)) - stop("value of kB-1 has to be specified if Rb_model is set to 'constant_kB-1'!") + stop("value of kB-1 has to be specified if Gb_model is set to 'constant_kB-1'!") else Rb_h = kB_h/(constants[:k] * ustar) Gb_h = 1/Rb_h @@ -262,9 +262,9 @@ end ## calculate aerodynamic conductance for momentum (Ga_m) if (wind_profile) - if (isnothing(z0m) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) + if (isnothing(z0m) & Gb_model in c(Val(:constant_kB1),Val(:Thom_1972))) stop("z0m must be provided if wind_profile=true!") -elseif (isnothing(z0m) & Rb_model in c(Val(:Choudhury_1988),Val(:Su_2001))) +elseif (isnothing(z0m) & Gb_model in c(Val(:Choudhury_1988),Val(:Su_2001))) # z0m estimated as in Choudhury_1988 or Su_2001 z0m = roughness_parameters(method=Val(:wind_profile),zh=zh,zr=zr,d=d,data=data, Tair=Tair,pressure=pressure,wind=wind,ustar=ustar,H=H, @@ -297,7 +297,7 @@ end else - if ((!missing(zr) | !missing(d) | !missing(z0m)) & Rb_model in c(Val(:constant_kB1),Val(:Thom_1972))) + if ((!missing(zr) | !missing(d) | !missing(z0m)) & Gb_model in c(Val(:constant_kB1),Val(:Thom_1972))) @warn"Provided roughness length parameters (zr,d,z0m) are not used if 'wind_profile = false' (the default). Ra_m is calculated as wind / ustar^2") end diff --git a/inst/fromR/bigleaf_physiology.jl b/inst/fromR/bigleaf_physiology.jl index 8176ee4..15b92b5 100755 --- a/inst/fromR/bigleaf_physiology.jl +++ b/inst/fromR/bigleaf_physiology.jl @@ -269,7 +269,7 @@ end #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' #' # calculate Ga -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Gb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # calculate Gs from the the inverted PM equation #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", @@ -569,7 +569,7 @@ end #' tprecip=0.1,precip_hours=24,records_per_hour=2) #' #' # calculate Gs from the the inverted PM equation -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Gb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' Gs_PM = surface_conductance(DE_Tha_Jun_2014_2,Tair="Tair",pressure="pressure", diff --git a/inst/fromR/boundary_layer_conductance.jl b/inst/fromR/boundary_layer_conductance.jl index 5f2ff72..1d5037b 100755 --- a/inst/fromR/boundary_layer_conductance.jl +++ b/inst/fromR/boundary_layer_conductance.jl @@ -45,7 +45,7 @@ #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' #' #See also -#' [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) +#' [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance!`](@ref) #' #' ```@example; output = false #' ``` @@ -159,7 +159,7 @@ end #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' #' #See also -#' [`Gb_Thom`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance`](@ref) +#' [`Gb_Thom`](@ref), [`Gb_Su`](@ref), [`aerodynamic_conductance!`](@ref) #' #' ```@example; output = false #' ``` @@ -316,7 +316,7 @@ end #' from measured quantities. Water, Air, and Soil Pollution 36, 311-330. #' #' #See also -#' [`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`aerodynamic_conductance`](@ref) +#' [`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`aerodynamic_conductance!`](@ref) #' #' ```@example; output = false #' ``` diff --git a/inst/fromR/decoupling.jl b/inst/fromR/decoupling.jl index 2c02816..618c39e 100755 --- a/inst/fromR/decoupling.jl +++ b/inst/fromR/decoupling.jl @@ -59,7 +59,7 @@ #' vegetation and the atmosphere. Agricultural and Forest Meteorology 49, 45-53. #' #' #See also -#' [`aerodynamic_conductance`](@ref), [`surface_conductance`](@ref), +#' [`aerodynamic_conductance!`](@ref), [`surface_conductance`](@ref), #' [`equilibrium_imposed_ET`](@ref) #' #' ```@example; output = false diff --git a/inst/fromR/evapotranspiration.jl b/inst/fromR/evapotranspiration.jl index 1b11bc6..9758871 100755 --- a/inst/fromR/evapotranspiration.jl +++ b/inst/fromR/evapotranspiration.jl @@ -233,7 +233,7 @@ end #' #' # Note #' Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref). -#' Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). +#' Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance!`](@ref). #' #' # Value A DataFrame with the following columns: diff --git a/inst/fromR/surface_conditions.jl b/inst/fromR/surface_conditions.jl index d92cc0a..ec08643 100755 --- a/inst/fromR/surface_conditions.jl +++ b/inst/fromR/surface_conditions.jl @@ -51,8 +51,8 @@ #' Note that Ga is assumed to be equal for water vapor and sensible heat. #' Ga is further assumed to be the inverse of the sum of the turbulent part #' and the canopy boundary layer conductance (1/Ga = 1/Ga_m + 1/Gb; -#' see [`aerodynamic_conductance`](@ref)). Ga_CO2, the aerodynamic conductance -#' for CO2 is also calculated by [`aerodynamic_conductance`](@ref). +#' see [`aerodynamic_conductance!`](@ref)). Ga_CO2, the aerodynamic conductance +#' for CO2 is also calculated by [`aerodynamic_conductance!`](@ref). #' If Ga is replaced by Ga_m (i.e. only the turbulent conductance part), #' the results of the functions represent conditions outside the canopy #' boundary layer, i.e. in the canopy airspace. diff --git a/inst/fromR/surface_conductance.jl b/inst/fromR/surface_conductance.jl index 2f45bc6..7e95d7c 100755 --- a/inst/fromR/surface_conductance.jl +++ b/inst/fromR/surface_conductance.jl @@ -89,7 +89,7 @@ #' #' # calculate Gs from the the inverted PM equation (now Rn, and Ga are needed), #' # using a simple estimate of Ga based on Thom 1972 -#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Rb_model=Val(:Thom_1972))[,"Ga_h"] +#' Ga = aerodynamic_conductance(DE_Tha_Jun_2014_2,Gb_model=Val(:Thom_1972))[,"Ga_h"] #' #' # if G and/or S are available, don't forget to indicate (they are ignored by default). #' # Note that Ga is not added to the DataFrame 'DE_Tha_Jun_2014' diff --git a/inst/tha.jl b/inst/tha.jl index c1a831c..c43d9e7 100644 --- a/inst/tha.jl +++ b/inst/tha.jl @@ -22,16 +22,20 @@ nothing tha = DE_Tha_Jun_2014 set_datetime_ydh!(tha) -thaf = copy(tha) -setinvalid_qualityflag!(thaf) +thaf = copy(tha); +setinvalid_qualityflag!(thaf); setinvalid_range!(thaf, :PPFD => (200, Inf), :ustar => (0.2, Inf), :LE =>(0, Inf), :VPD => (0.01, Inf) - ) -setinvalid_nongrowingseason!(thaf, 0.4) -setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) + ); +setinvalid_nongrowingseason!(thaf, 0.4); +setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24); + +thas = subset(thaf, :valid) + + function tmpf() diff --git a/inst/walkthrough_todo.jmd b/inst/walkthrough_todo.jmd index 20f499c..6526dae 100644 --- a/inst/walkthrough_todo.jmd +++ b/inst/walkthrough_todo.jmd @@ -137,6 +137,10 @@ The timestamps in the DataFrame must be sorted in increasing order. setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) sum(.!thaf.valid) # some more invalids ``` +```julia +thas = subset(thaf, :valid) +``` + When looking at the function output we see that we these settings, we exclude in total 1013 data points (70.35% of the data). In total, 29.65% of all data remained. The output of the `filter_data` function is another DataFrame (thaf), in which all filtered timesteps are set to missing. (Note that this is the default case. If we add `filtered_data_to_NA=true`, the data are left untouched, but an additional column "valid" is added to the DataFrame that specifies whether the time points fulfull the criteria or not). In the following examples we will work mostly with the filtered DataFrame `thaf`. @@ -152,29 +156,49 @@ When looking at the function output we see that we these settings, we exclude in ## Aerodynamic conductance -An important metric for many calculations in the `Bigleaf.jl` package is the aerodynamic conductance ($G_a$) between the land surface and the measurement height. $G_a$ characterizes how efficiently mass and energy is transferred between the land surface and the atmosphere. $G_a$ consists of two parts: $G_{am}$, the aerodynamic conductance for momentum, and $G_b$, the canopy boundary layer (or quasi-laminar) conductance. $G_a$ can be defined as $G_a = 1/(1/G_{am} + 1/G_b)$. In this tutorial we will focus on how to use the function `aerodynamic_conductance`. For further details on the equations, the reader is directed to the publication of the Bigleaf package (Knauer et al. 2018) and the references therein. A good overview is provided by e.g. Verma 1989. +An important metric for many calculations in the `Bigleaf.jl` package is the aerodynamic +conductance ($G_a$) between the land surface and the measurement height. $G_a$ +characterizes how efficiently mass and energy is transferred between the land surface +and the atmosphere. $G_a$ consists of two parts: $G_{a_m}$, the aerodynamic conductance +for momentum, and $G_b$, the canopy boundary layer (or quasi-laminar) conductance. +$G_a$ can be defined as $G_a = 1/(1/G_{a_m} + 1/G_b)$. In this tutorial we will focus on +how to use the function [`aerodynamic_conductance!`](@ref). +For further details on the equations, +the reader is directed to the publication of the Bigleaf package (Knauer et al. 2018) and +the references therein. A good overview is provided by e.g. Verma 1989. -$G_a$ and in particular $G_b$ can be calculated with varying degrees of complexity. We start with the simplest version, in which $G_b$ is calculated empirically based on the friction velocity ($u_*$) according to Thom 1972: +$G_a$ and in particular $G_b$ can be calculated with varying degrees of complexity. +We start with the simplest version, in which $G_b$ is calculated empirically based on +the friction velocity ($u_*$) according to Thom 1972: ```julia -summary(aerodynamic_conductance(thaf)) +aerodynamic_conductance!(thas) +thas[1:6, Cols(:datetime,Between(:zeta,:Ga_CO2))] ``` -Note that by not providing additional arguments, the default values are taken (type ?aerodynamic_conductance to see default values of the function arguments). We also do not need most of the arguments that can be provided to the function in this case (i.e. if `Rb_model=Val(:Thom_1972)`). These are only required if we use a more complex formulation of $G_b$. -The output of the function is another DataFrame which contains separate columns for conductances and resistances of different scalars (momentum, heat, and CO$_2$ by default). -For comparison, we now calculate a second estimate of $G_a$, where the calculation of $G_b$ is more physically-based (Su et al. 2001), and which requires more input variables compared to the first version. In particular, we now need LAI, the leaf characteristic dimension ($D_l$, assumed to be 1cm here), and information on sensor and canopy height ($z_r$ and $z_h$), as well as the displacement height (assumed to be 0.7*$z_h$): +Note that by not providing additional arguments, the default values are taken. +We also do not need most of the arguments that can be provided to the function in this case +(i.e. with `Gb_model=Val(:Thom_1972)`). These are only required if we use a more complex +formulation of $G_b$. +The output of the function is another DataFrame which contains separate columns for +conductances and resistances of different scalars (momentum, heat, and $CO_2$ by default). + +For comparison, we now calculate a second estimate of $G_a$, where the calculation of +$G_b$ is more physically-based (Su et al. 2001), and which requires more input variables +compared to the first version. In particular, we now need LAI, the leaf characteristic +dimension ($D_l$, assumed to be 1cm here), and information on sensor and canopy height +($z_r$ and $z_h$), as well as the displacement height (assumed to be 0.7*$z_h$): ```julia -Ga_Su = aerodynamic_conductance(thaf,Rb_model=Val(:Su_2001),LAI=thal.zh,zh= thal.zh,d=0.7*zh, - zr= thal.zr,Dl=thal.Dl) -summary(Ga_Su) -thaf = cbind(thaf,Ga_Su) +aerodynamic_conductance!(thas;Gb_model=Val(:Su_2001), + LAI=thal.zh, zh=thal.zh, d=0.7*thal.zh, zr=thal.zr,Dl=thal.Dl) +thas[1:6, Cols(:datetime,Between(:zeta,:Ga_CO2))] ``` -We add the output of this function (`Ga_Su`) to our dataframe `thaf`. We see that the values are different compared to the first, empirical estimate. This is because this formulation takes additional aerodynamically relevant properties (LAI, $D_l$) into account that were not considered by the simple empirical formulation. - - +We see that the values are different compared to the first, empirical estimate. +This is because this formulation takes additional aerodynamically relevant properties +(LAI, $D_l$) into account that were not considered by the simple empirical formulation. ## Surface conditions @@ -531,7 +555,7 @@ unc_all = mapply(aerodynamic_conductance,Dl=Dl_sample,z0m=z0m_sample,LAI=LAI_sam MoreArgs=list(data=thaf,zr=42,zh=26.5,d=0.7*26.5, N=2,stab_correction=T, stab_formulation=Val(:Dyer_1970), - Rb_model=Val(:Su_2001)) + Gb_model=Val(:Su_2001)) ) # select "Ga_h" output variable and convert to matrix diff --git a/src/Bigleaf.jl b/src/Bigleaf.jl index b086ac4..52f2403 100644 --- a/src/Bigleaf.jl +++ b/src/Bigleaf.jl @@ -35,7 +35,7 @@ export wind_profile export Monin_Obukhov_length, Monin_Obukhov_length!, stability_parameter, stability_parameter!, stability_correction, stability_correction!, roughness_parameters, Reynolds_Number -export aerodynamic_conductance!, add_Ga, add_Ga! +export aerodynamic_conductance!, add_Ga, add_Ga!, compute_Ram, compute_Ram!, roughness_z0h include("util.jl") include("bigleaf_constants.jl") diff --git a/src/aerodynamic_conductance.jl b/src/aerodynamic_conductance.jl index 91a3e5d..b6df2b9 100755 --- a/src/aerodynamic_conductance.jl +++ b/src/aerodynamic_conductance.jl @@ -1,243 +1,244 @@ """ -Aerodynamic Conductance + aerodynamic_conductance!(df; + Gb_model = Val(:Thom_1972), Ram_model = Val(:wind_zr), + zr=nothing,zh=nothing, d = isnothing(zh) ? nothing : 0.7*zh, + ... + ) Bulk aerodynamic conductance, including options for the boundary layer conductance formulation and stability correction functions. # Arguments -- `data` : DataFrame or matrix containing all required variables -- `Tair` : Air temperature (deg C) -- `pressure` : Atmospheric pressure (kPa) -- `wind` : Wind speed (m s-1) -- `ustar` : Friction velocity (m s-1) -- `H` : Sensible heat flux (W m-2) -- `zr` : Instrument (reference) height (m) -- `zh` : Canopy height (m) -- `d` : Zero-plane displacement height (m) -- `z0m` : Roughness length for momentum (m), optional; if not provided, - it is estimated from `roughness_parameters` - TODO : (method=Val(:wind_profile)). Only used if `wind_profile = true` and/or - `Rb_model` = `Val(:Su_2001)` or - : `Val(:Choudhury_1988)`. -- `Dl` : Characteristic leaf dimension (m) (if `Rb_model` = `Val(:Su_2001)`) - : or leaf width (if `Rb_model` = `Val(:Choudhury_1988)`); - ignored otherwise. -- `N` : Number of leaf sides participating in heat exchange (1 or 2); - only used if `Rb_model = Val(:Su_2001)`. - : Defaults to 2. -- `fc` : Fractional vegetation cover (-); only used if `Rb_model = Val(:Su_2001)`. - See Details. -- `LAI` : One-sided leaf area index (m2 m-2); only used if - `Rb_model` = `Val(:Choudhury_1988)` or `Val(:Su_2001)`. -- `Cd` : Foliage drag coefficient (-); only used if `Rb_model = Val(:Su_2001)`. -- `hs` : Roughness length of bare soil (m); only used if `Rb_model = Val(:Su_2001)`. -- `wind_profile` : Should Ga for momentum be calculated based on the logarithmic wind - profile equation? Defaults to `false`. -- `stab_correction` : Should stability correction be applied? Defaults to `true`. - Ignored if `wind_profile = false`. -- `stab_formulation` : Stability correction function. Either `Val(:Dyer_1970)` (default) or - : `Val(:Businger_1971)`. Ignored if `wind_profile = false` or if - `stab_correction = false`. -- `Rb_model` : Boundary layer resistance formulation. - One of `Val(:Thom_1972),Val(:Choudhury_1988),Val(:Su_2001),Val(:constant_kB1)`. -- `kB_h` : kB-1 value for heat transfer; only used if `Rb_model = Val(:constant_kB1)` -- `Sc` : Optional: Schmidt number of additional quantities to be calculated -- `Sc_name` : Optional: Name of the additonal quantities, has to be of same length than - `Sc_name` -- `constants=`[`bigleaf_constants`](@ref)`()`: Dictionary with entries - - `k` - von Karman constant - - `cp` - specific heat of air for constant pressure (J K-1 kg-1) - - `Kelvin` - conversion degree Celsius to Kelvin - - `g` - gravitational acceleration (m s-2) - - `pressure0` - reference atmospheric pressure at sea level (Pa) - - `Tair0` - reference air temperature (K) - - `Sc_CO2` - Schmidt number for CO2 - - `Pr` - Prandtl number (if `Sc` is provided) +- `df`: DataFrame with columns + - `ustar` : Friction velocity (m s-1) + - `wind` : Wind speed at sensor height (m s-1) +- `Gb_model` : model for computing boundary layer conductance (see [`compute_Gb!`](@ref)) +- `Ram_model` : model for computing aerodynamic resistance (see [`compute_Ram`](@ref)) +- `zh` : canopy height (m) +- `zr` : Instrument (reference) height (m) + +Further required columns of `df` and keyword argument depend on `Gb_model` +(see [`compute_Gb!`](@ref)) and `Ram_model` (see [`compute_Ram`](@ref)). + +If only columns `ustar` and `wind` are available, use default models +(`Val(:Thom_1972)` and `Val(:wind_zr)`). # Details Aerodynamic conductance for heat (Ga_h) is calculated as: ``Ga_h = 1 / (Ra_m + Rb_h)`` - -where ``Ra_m`` is the aerodynamic resistance for momentum and ``Rb`` the (quasi-laminar) -canopy boundary layer resistance ('excess resistance'). - -The aerodynamic resistance for momentum ``Ra_m`` is given by: - -``Ra_m = u/ustar^2`` - -Note that this formulation accounts for changes in atmospheric stability, and does not -require an additional stability correction function. - -An alternative method to calculate ``Ra_m`` is provided -(calculated if `wind_profile = true`): - -``Ra_m = (ln((zr - d)/z0m) - psi_h) / (k ustar)`` - -If the roughness parameters z0m and d are unknown, they can be estimated using -[`roughness_parameters`](@ref). The argument `stab_formulation` determines the stability -correction function used to account for the effect of atmospheric stability on Ra_m -(Ra_m is lower for unstable and higher for stable stratification). Stratification is based -on a stability parameter zeta (z-d/L), where z = reference height, d the zero-plane -displacement height, and L the Monin-Obukhov length, calculated with -[`Monin_Obukhov_length`](@ref) -The stability correction function is chosen by the argument `stab_formulation`. -Options are `Val(:Dyer_1970)` and `Val(:Businger_1971)`. - -The model used to determine the canopy boundary layer resistance for heat (``Rb_h``) is -specified by the argument `Rb_model`. The following options are implemented: -`Val(:Thom_1972)` is an empirical formulation based on the -friction velocity (ustar) (Thom 1972): - -``Rb_h = 6.2ustar^-0.667`` -The model by Choudhury & Monteith 1988 (`Rb_model = Val(:Choudhury_1988)`), -calculates ``Rb_h`` based on leaf width, LAI and ustar (Note that function argument `Dl` -represents leaf width (w) and not characteristic leaf dimension (Dl) -if `Rb_model` = `Val(:Choudhury_1988)`): - -``Gb_h = LAI((0.02/\\alpha)*\\sqrt(u(zh)/w)*(1-exp(-\\alpha/2)))`` - -where ``\\alpha`` is a canopy attenuation coefficient modeled in dependence on LAI, -u(zh) is wind speed at canopy height (calculated from [`wind_profile`](@ref)), -and w is leaf width (m). See [`Gb_Choudhury`](@ref) for further details. - -The option `Rb_model = Val(:Su_2001)` calculates ``Rb_h`` based on the physically-based -Rb model by Su et al. 2001, a simplification of the model developed by Massman 1999: - -``kB-1 = (k Cd fc^2) / (4Ct ustar/u(zh)) + kBs-1(1 - fc)^2`` - -where Cd is a foliage drag coefficient (defaults to 0.2), fc is fractional -vegetation cover, Bs-1 is the inverse Stanton number for bare soil surface, -and Ct is a heat transfer coefficient. See [`Gb_Su`](@ref) for -details on the model. - -The models calculate the parameter kB-1, which is related to ``Rb_h``: +where ``Ra_m`` is the aerodynamic resistance for momentum and ``Rb_h = 1/Gb_h`` the +(quasi-laminar) canopy boundary layer resistance ('excess resistance') for heat. -``kB-1 = Rb_h * (k * ustar)`` +`Ra_m` is computed and described with [`compute_Ram`](@ref) using model `Ram_model`. -Rb (and Gb) for water vapor and heat are assumed to be equal in this package. -Gb for other quantities x is calculated as (Hicks et al. 1987): +`Rb_h` is computed and described with 1/[`compute_Gb!`](@ref) using a given `Gb_model`. -``Gb_x = Gb / (Sc_x / Pr)^0.67`` -where Sc_x is the Schmidt number of quantity x, and Pr is the Prandtl number (0.71). - # Value -a dataframe with the following columns: -- `Ga_m`: Aerodynamic conductance for momentum transfer (m s-1) +combined results of [`compute_Gb!`](@ref) and - `Ra_m`: Aerodynamic resistance for momentum transfer (s m-1) +- `Ga_m`: Aerodynamic conductance for momentum transfer (m s-1) - `Ga_h`: Aerodynamic conductance for heat transfer (m s-1) - `Ra_h`: Aerodynamic resistance for heat transfer (s m-1) -- `Gb_h`: Canopy boundary layer conductance for heat transfer (m s-1) -- `Rb_h`: Canopy boundary layer resistance for heat transfer (s m-1) -- `kB_h`: kB-1 parameter for heat transfer -- `zeta`: Stability parameter 'zeta' (missing if `wind_profile = false`) -- `psi_h`: Integrated stability correction function (missing if `wind_profile = false`) -- `Ra_CO2`: Aerodynamic resistance for CO2 transfer (s m-1) - `Ga_CO2`: Aerodynamic conductance for CO2 transfer (m s-1) -- `Gb_CO2`: Canopy boundary layer conductance for CO2 transfer (m s-1) -- `Ga_Sc_name`: Aerodynamic conductance for `Sc_name` (m s-1). Only added if `Sc_name` and - the respective `Sc` are provided. -- `Gb_Sc_name`: Boundary layer conductance for `Sc_name` (m s-1). Only added if `Sc_name` - and the respective `Sc` are provided. # Note -The roughness length for water and heat (z0h) is not returned by this function, but -it can be calculated from the following relationship (e.g. Verma 1989): - -``kB-1 = ln(z0m/z0h)`` - -it follows: - -``z0h = z0m / exp(kB-1)`` - -`kB-1` is an output of this function. +The roughness length for water and heat (z0h) can be computed by [`roughness_z0h`](@ref). +TODO check Input variables such as LAI, Dl, or zh can be either constants, or -vary with time (i.e. vectors of the same length as `data`). +vary with time, i.e. are vectors of the same length as `df`. -Note that boundary layer conductance to water vapor transfer (Gb_w) is often -assumed to equal Gb_h. This assumption is also made in this R package, for +Note that boundary layer conductance to water vapor transfer (`Gb_w`) is often +assumed to equal `Gb_h`. This assumption is also made in `Bigleaf.jl`, for example in the function [`surface_conductance`](@ref). If the roughness length for momentum (`z0m`) is not provided as input, it is estimated -from the function `roughness_parameters` within `wind_profile` if `wind_profile = true` -and/or `Rb_model` = `Val(:Su_2001)` or `Val(:Choudhury_1988)` The `roughness_parameters` -function estimates a single `z0m` value for the entire time period! If a varying `z0m` value +using [`roughness_parameters`](@ref), which estimates a single `z0m` +value for the entire time period. If a varying `z0m` value (e.g. across seasons or years) is required, `z0m` should be provided as input argument. - -TODO -For adding aerodynamic conductance for other species see [`add_Ga!`](@ref). - -# References -- Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. - In: Estimation of areal evapotranspiration, IAHS Pub, 177, 13-20. -- Verhoef, A., De Bruin, H., Van Den Hurk, B., 1997: Some practical notes on the parameter kB-1 - for sparse vegetation. Journal of Applied Meteorology, 36, 560-572. -- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: - A preliminary multiple resistance routine for deriving dry deposition velocities - from measured quantities. Water, Air, and Soil Pollution 36, 311-330. -- Monteith, J_L., Unsworth, M_H., 2008: Principles of environmental physics. - Third Edition. Elsevier Academic Press, Burlington, USA. - -# See also -[`Gb_Thom`](@ref), [`Gb_Choudhury`](@ref), [`Gb_Su`](@ref) for calculations of Rb / Gb only - -```@example; output = false -# df = DataFrame(Tair=25,pressure=100,wind=c(3,4,5),ustar=c(0.5,0.6,0.65),H=c(200,230,250)) - -# # simple calculation of Ga -# aerodynamic_conductance(df,Rb_model=Val(:Thom_1972)) - -# # calculation of Ga using a model derived from the logarithmic wind profile -# aerodynamic_conductance(df,Rb_model=Val(:Thom_1972),zr=40,zh=25,d=17.5,z0m=2,wind_profile=true) - -# # simple calculation of Ga, but a physically based canopy boundary layer model -# aerodynamic_conductance(df,Rb_model=Val(:Su_2001),zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) +# Examples +```jldoctest; output = false +using DataFrames +df = DataFrame(Tair=25,pressure=100,wind=[3,4,5], + ustar=[0.5,0.6,0.65],H=[200,230,250]) +# simple calculation of Ga +aerodynamic_conductance!(df;Gb_model=Val(:Thom_1972)) +# calculation of Ram using a model derived from the logarithmic wind profile +aerodynamic_conductance!(df;Gb_model=Val(:Thom_1972),Ram_model = Val(:wind_profile), + zr=40,zh=25,d=17.5,z0m=2) +# simple calculation of Ga, but a physically based canopy boundary layer model +aerodynamic_conductance!(df,Gb_model=Val(:Su_2001), + zr=40,zh=25,d=17.5,Dl=0.05,N=2,fc=0.8) +all(isfinite.(df.psi_h)) +# output +true ``` """ -function aerodynamic_conductance!(df, Rb_model = Val(:Thom_1972); - zr,zh=nothing,d = 0.7*zh ,z0m=nothing,Dl=nothing,N=2,fc=nothing,LAI=nothing,Cd=0.2,hs=0.01, +function aerodynamic_conductance!(df; Gb_model = Val(:Thom_1972), Ram_model = Val(:wind_zr), + zr=nothing,zh=nothing, d = isnothing(zh) ? nothing : 0.7*zh , + z0m=nothing,Dl=nothing,N=2,fc=nothing,LAI=nothing,Cd=0.2,hs=0.01, leafwidth=nothing, wind_profile=false, stab_formulation=Val(:Dyer_1970), kB_h=nothing,constants=bigleaf_constants() ) - # add zeta, psi_h and compute z0m - if !(stab_formulation isa Val{:no_stability_correction}) + # add zeta + if !isnothing(zr) && !isnothing(d) && !(stab_formulation isa Val{:no_stability_correction}) stability_parameter!(df::AbstractDataFrame; zr,d, constants) else df[!,:zeta] .= missing end # adds columns psi_m and psi_h, (:no_stability_correction: just add 0s without using zeta) stability_correction!(df; stab_formulation, constants) - if isnothing(z0m) + # pre-estimate z0m to use it in both Gb and Ga + needs_windprofile = + Gb_model isa Union{Val{:Choudhury_1988}, Val{:Su_2001}} || + Ram_model isa Val{:wind_profile} + if isnothing(z0m) && needs_windprofile z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = df.psi_m).z0m end # calculate canopy boundary layer conductance (Gb) - Rb_model isa Val{:Thom_1972} && compute_Gb!(df, Rb_model; constants) - Rb_model isa Val{:constant_kB1} && compute_Gb!(df, Rb_model; kB_h, constants) - Rb_model isa Val{:Choudhury_1988} && compute_Gb!(df, Rb_model; + Gb_model isa Val{:Thom_1972} && compute_Gb!(df, Gb_model; constants) + Gb_model isa Val{:constant_kB1} && compute_Gb!(df, Gb_model; kB_h, constants) + Gb_model isa Val{:Choudhury_1988} && compute_Gb!(df, Gb_model; leafwidth, LAI, zh, zr, d, z0m, stab_formulation, constants) - Rb_model isa Val{:Su_2001} && compute_Gb!(df, Rb_model; + Gb_model isa Val{:Su_2001} && compute_Gb!(df, Gb_model; Dl, fc, N, Cd, hs, z0m, zh, zr, d, LAI, stab_formulation, constants) - # calculate aerodynamic conductance for momentum (Ga_m) - # TODO factor out to own function - df[!,:Ra_m] .= @. max((log((zr - d)/z0m) - df.psi_h),0) / (constants[:k]*ustar) - df[!,:Ga_m] .= 1/df.Ra_m - df[!,:Ra_h] .= df.Ra_m + df.Rb_h - df[!,:Ga_h] .= 1/df.Ra_h - df[!,:GA_CO2] .= 1/(df.Ra_m + 1/df.Gb_CO2) - df[!,:Ra_CO2] = 1/df.Ga_CO2 - # TODO add Ga for other species: - # Ga_x <- 1/(Ra_m + 1/Gb_x) - df -# return(DataFrame(Ga_m,Ra_m,Ga_h,Ra_h,Gb_h,Rb_h,kB_h,zeta,psi_h,Ra_CO2,Gab_x)) + # calculate aerodynamic risistance for momentum (Ra_m) + Ram_model isa Val{:wind_profile} && compute_Ram!(df, Ram_model; zr, d, z0m, constants) + Ram_model isa Val{:wind_zr} && compute_Ram!(df, Ram_model) + function ft(Ra_m, Gb_h, Gb_CO2) + Ga_m = 1/Ra_m + Ra_h = Ra_m + 1/Gb_h + Ga_h = 1/Ra_h + Ga_CO2 = 1/(Ra_m + 1/Gb_CO2) + (;Ga_m, Ga_h, Ra_h, Ga_CO2) + end + transform!(df, [:Ra_m, :Gb_h, :Gb_CO2] => ByRow(ft) => AsTable) +end + + +""" + roughness_z0h(z0m, kB_h) + +# Arguments +- `z0m` : Roughness length for momentum (m). Can be calculated + by [`roughness_parameters`](@ref). +- `kB_h` : kB-1 parameter, Output of [`aerodynamic_conductance!`](@ref) + +# Details +The roughness length for water and heat (z0h) is calculated from the +relationship (e.g. Verma 1989): + +``{k_B}_h = ln(z_{0m}/z_{0h})`` + +it follows: + +``z_{0h} = z_{0m} / e^{k_{B_h}}`` + +# References +Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. + In: Estimation of areal evapotranspiration, IAHS Pub, 177, 13-20. +""" +roughness_z0h(z0m, kB_h) = z0m / exp(kB_h) + + +""" + compute_Ram(::Val{:wind_profile}, ustar; + zr, d, z0m, psi_h, constants=bigleaf_constants()) + compute_Ram!(df, method::Val{:wind_profile}; + zr, d, z0m, psi_h = df.psi_h, kwargs...) + + compute_Ram(::Val{:wind_zr}, ustar, wind) + compute_Ram!(df, method::Val{:wind_zr}; kwargs...) + +Estimate bulk aerodynamic conductance. + +# Arguments +- `ustar` : Friction velocity (m s-1) +- `wind` : wind speed at measurement height (m s-1) +- `df` : DataFrame with above columns +- `zr` : Instrument (reference) height (m) +- `d` : Zero-plane displacement height (-), can be estimated using + [`roughness_parameters`](@ref) +- `z0m` : Roughness length for momentum (m). Can be estimated using + from [`roughness_parameters`](@ref) +- `psi_h` : the value of the stability function for heat and water vapor (-) + see [`stability_correction`](@ref) + +# Details + +The aerodynamic resistance for momentum ``R_{a_m}`` is given by (`Ram_method = Val(:wind_zr)`): + +``R_{a_m} = u/{u^*}^2`` + +Where u is the horizontal wind velocity. +Note that this formulation accounts for changes in atmospheric stability, and does not +require an additional stability correction function. + +An alternative method to calculate ``Ra_m`` is provided (`Ram_method = Val(:wind_profile)`): + +``R_{a_m} = (ln((z_r - d)/z_{0m}) - \\psi_h) / (k \\, u^*)`` + +If the roughness parameters `z0m` and `d` are unknown, they can be estimated using +[`roughness_parameters`](@ref). The argument `stab_formulation` determines the stability +correction function used to account for the effect of atmospheric stability on `Ra_m` +(`Ra_m` is lower for unstable and higher for stable stratification). Stratification is based +on a stability parameter `zeta` ``\\zeta=(z-d/L)``, where `z` is the height, +`d` the zero-plane displacement height, and `L` the Monin-Obukhov length, calculated with +[`Monin_Obukhov_length`](@ref) +The stability correction function is chosen by the argument `stab_formulation`. +Options are `Val(:Dyer_1970)` and `Val(:Businger_1971)` and `Val(:no_stability_correction)`. + +# Note +For adding aerodynamic conductance for other species see [`add_Ga!`](@ref). + +# Value +Aerodynamic resistance for momentum transfer (s m-1) (``Ra_m``) + +# References +- Verma, S., 1989: Aerodynamic resistances to transfers of heat, mass and momentum. + In: Estimation of areal evapotranspiration, IAHS Pub, 177, 13-20. +- Verhoef, A., De Bruin, H., Van Den Hurk, B., 1997: Some practical notes on the parameter kB-1 + for sparse vegetation. Journal of Applied Meteorology, 36, 560-572. +- Hicks, B_B., Baldocchi, D_D., Meyers, T_P., Hosker, J_R., Matt, D_R., 1987: + A preliminary multiple resistance routine for deriving dry deposition velocities + from measured quantities. Water, Air, and Soil Pollution 36, 311-330. +- Monteith, J_L., Unsworth, M_H., 2008: Principles of environmental physics. + Third Edition. Elsevier Academic Press, Burlington, USA. + +# See also +[`aerodynamic_conductance!`](@ref), [`add_Ga!`](@ref) +""" +function compute_Ram(::Val{:wind_profile}, ustar::Union{Missing,Number}; + zr, d, z0m, psi_h, constants=bigleaf_constants() + ) + Ra_m = max((log((zr - d)/z0m) - psi_h),0) / (constants[:k]*ustar) +end +function compute_Ram!(df, method::Val{:wind_profile}; + zr, d, z0m, psi_h = df.psi_h, kwargs... + ) + # put keyword arguments to positional arguments for proper broadcast + ftpos(ustar, zr, d, z0m, psi_h) = + compute_Ram(method, ustar; zr, d, z0m, psi_h, kwargs...) + ft(ustar) = ftpos.(ustar, zr, d, z0m, psi_h) + transform!(df, :ustar => ft => :Ra_m) +end + +function compute_Ram(::Val{:wind_zr}, ustar::Union{Missing,Number}, wind) + Ra_m = wind / ustar^2 +end +function compute_Ram!(df, method::Val{:wind_zr}; kwargs...) + ft(ustar, wind) = compute_Ram(method, ustar, wind; kwargs...) + transform!(df, SA[:ustar, :wind] => ByRow(ft) => :Ra_m) end + """ add_Ga(Gb_h, Ga_m, Sc::Vararg{Pair,N}; constants) add_Ga!(df::AbstractDataFrame, Sc; Gb_h = df.Gb_h, Ga_m = df.Ga.m, kwargs...) @@ -257,11 +258,11 @@ optional # Details Aerodynamic conductance is calculated as -``Ga_x = 1/(1/Ga_m + 1/Gb_x)`` +``G_{a_x} = 1/(1/G_{a_m} + 1/G_{b_x})`` -where Gb_x is the Boundary layer conductance for other quantities x is calculated +where `Gb_x` is the Boundary layer conductance for other quantities x is calculated based on boundary layer for heat transfer, Schmidt-Number, and Prantl number, -as documented in [`add_Gb`](@ref). +as documented in [`add_Gb!`](@ref). # Value a NameTuple or `df` with keys `Ga_x` where `x` are the keys in `Sc` and @@ -270,13 +271,20 @@ corresponding aerodynamic conductances (m s-1). # Examples ```jldoctest; output=false using DataFrames -df = DataFrame(Gb_h=[0.02, missing, 0.055], Ga_m = [0.03, 0.03, 0.03]) +df = DataFrame(Gb_h=[0.02, missing, 0.055], Ga_m = 1 ./ [0.03, 0.03, 0.03]) add_Ga!(df, :O2 => 0.84, :CH4 => 0.99) propertynames(df)[3:4] == [:Ga_O2, :Ga_CH4] # output true ``` """ +function add_Ga!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; + Gb_h = df.Gb_h, Ga_m = df.Ga_m, kwargs...) where N + N == 0 && return(df) + Scn, Scv = get_names_and_values("Ga_", Sc...) + ft() = add_Ga_.(Gb_h, Ga_m, Ref(Scn), Ref(Scv); kwargs...) + transform!(df, [] => ft => AsTable) +end function add_Ga(Gb_h::Union{Missing,Number}, Ga_m::Union{Missing,Number}, Sc::Vararg{Pair,N}; kwargs...) where N Scn, Scv = get_names_and_values("Ga_", Sc...) @@ -288,12 +296,6 @@ function add_Ga_(Gb_h::Union{Missing,Number}, Ga_m::Union{Missing,Number}, Gaxv = ntuple(i -> 1/(1/Ga_m + 1/Gbx[i]), length(Gbx)) Gax = NamedTuple{Scn}(Gaxv) end -function add_Ga!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, Ga_m = df.Ga_m, kwargs...) where N - N == 0 && return(df) - Scn, Scv = get_names_and_values("Ga_", Sc...) - ft() = add_Ga_.(Gb_h, Ga_m, Ref(Scn), Ref(Scv); kwargs...) - transform!(df, [] => ft => AsTable) -end diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index 39b0d80..2caa9f6 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -23,11 +23,11 @@ updated DataFrame `df` with the following columns: - `Gb_CO2`: Boundary layer conductance for CO2 (m s-1). To subsequently compute conductances for other species with different -Schmidt numbers see [`add_Gb`](@ref). +Schmidt numbers see [`add_Gb!`](@ref). # See also [`Gb_Thom`](@ref), `Gb_Choudhury`](@ref), [`Gb_Su`](@ref), `Gb_Choudhury`](@ref), -[`aerodynamic_conductance`](@ref) +[`aerodynamic_conductance!`](@ref) ```jldoctest; output = false using DataFrames @@ -60,7 +60,7 @@ function compute_Gb_!(df::AbstractDataFrame, approach, inputcols; transform!(df, :ustar => ByRow(fGb) => SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2]) end compute_Gb(::Val{:Thom_1972}, args...; kwargs...) = Gb_Thom(args...; kwargs...) -compute_Gb(::Val{:constant_kB1}, args...; kB_h, constants) = Gb_constant_kB1(args..., kB_h; constants) +compute_Gb(::Val{:constant_kB1}, args...; kB_h, kwargs...) = Gb_constant_kB1(args..., kB_h; kwargs...) """ add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; constants) @@ -99,6 +99,12 @@ propertynames(df)[2:3] == [:Gb_O2, :Gb_CH4] true ``` """ +function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N + N == 0 && return(df) + Scn, Scv = get_names_and_values("Gb_", Sc...) + ft() = add_Gb_.(Gb_h, Ref(Scn), Ref(Scv); kwargs...) + transform!(df, [] => ft => AsTable) +end function add_Gb(Gb_h::Union{Missing,Number}, Sc::Vararg{Pair,N}; kwargs...) where N Scn, Scv = get_names_and_values("Gb_", Sc...) add_Gb_(Gb_h, Scn, Scv; kwargs...) @@ -108,12 +114,6 @@ function add_Gb_(Gb_h::Union{Missing,Number}, Scn::NTuple{N,Symbol}, Scv::NTuple Gbxv = @. Gb_h / (Scv/constants[:Pr])^0.67 Gbx = NamedTuple{Scn}(Gbxv) end -function add_Gb!(df::AbstractDataFrame, Sc::Vararg{Pair,N}; Gb_h = df.Gb_h, kwargs...) where N - N == 0 && return(df) - Scn, Scv = get_names_and_values("Gb_", Sc...) - ft() = add_Gb_.(Gb_h, Ref(Scn), Ref(Scv); kwargs...) - transform!(df, [] => ft => AsTable) -end function get_names_and_values(prefix::AbstractString, Sc::Vararg{Pair,N}) where N Scn = ntuple(i -> Symbol(prefix * string(Sc[i].first)), N) Scv = ntuple(i -> Sc[i].second, N) @@ -165,13 +165,15 @@ function Gb_Thom(ustar::Union{Missing,Number}; constants=bigleaf_constants()) end """ - Gb_constant_kB1(ustar; constants) - compute_Gb!(df, Val{:Thom_1972}) + Gb_constant_kB1(ustar, kB_h; constants) + compute_Gb!(df, Val{:constant_kB1}) -Boundary Layer Conductance using constant XX +Boundary Layer Conductance using constant kB-1 value for heat transfer. # Arguments -- `kB_h` : kB-1 value for heat transfer +- `ustar` : Friction velocity (m s-1) +- `df` : DataFrame with above variables +- `kB_h` : kB-1 value for heat transfer - `constants=`[`bigleaf_constants`](@ref)`()` # Details diff --git a/src/evapotranspiration.jl b/src/evapotranspiration.jl index 972d342..bd26dc6 100755 --- a/src/evapotranspiration.jl +++ b/src/evapotranspiration.jl @@ -312,7 +312,7 @@ function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn; G=zero(Tair),S=zero(Tair), kwargs...) # # Note # Surface conductance (Gs) can be calculated with [`surface_conductance`](@ref) -# Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance`](@ref). +# Aerodynamic conductance (Ga) can be calculated using [`aerodynamic_conductance!`](@ref). equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; kwargs...) end function equilibrium_imposed_ET(Tair,pressure,VPD,Gs, Rn, G, S; @@ -372,12 +372,3 @@ function surface_conductance() error("not yet implemented.") end -""" - TODO; implement aerodynamic_conductance. - -This stub is there to satisfy links im Help-pages. -""" -function aerodynamic_conductance() - error("not yet implemented.") -end - diff --git a/test/aerodynamic_conductance.jl b/test/aerodynamic_conductance.jl index 225baf3..c2b6cf6 100644 --- a/test/aerodynamic_conductance.jl +++ b/test/aerodynamic_conductance.jl @@ -23,46 +23,74 @@ @test df2.Ga_N20[1] == Ga.Ga_N20 end +@testset "compute_Ram" begin + zr, zh, d = thal.zr, thal.zh, 0.7*thal.zh + ustar, wind = tha48[24, [:ustar, :wind]] + df = copy(tha48) + stability_parameter!(df; zr, d) + stability_correction!(df) + z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = df.psi_m).z0m + Ram_r = compute_Ram(Val(:wind_zr), ustar, wind) + @test Ram_r ≈ 6.31 rtol = 1/100 # TODO check with R + Ram_p = compute_Ram(Val(:wind_profile), ustar; zr, d, z0m, psi_h = df.psi_h[24]) + @test Ram_p ≈ 5.41 rtol = 1/100 # TODO check with R + #Gb_h, Gb_CO2 = compute_Gb!(df, Val(:Thom_1972))[24, [:Gb_h, :Gb_CO2]] + compute_Ram!(df, Val(:wind_zr)) + #@test all(propertynames(df)[(end+1-length(Ram)):end] .== keys(Ram)) + @test propertynames(df)[end] == :Ra_m + @test df.Ra_m[24] == Ram_r + compute_Ram!(df, Val(:wind_profile); zr, d, z0m) + @test df.Ra_m[24] == Ram_p +end - -@testset "compute_Gb :no_stability_correction" begin +@testset "aerodynamic_conductance! only ustar and wind" begin + # using only ustar and wind dfo = DataFrame(ustar = SA[0.1,missing,0.3], wind = [3.4, 2.8, 3.3]) df = copy(dfo) - aerodynamic_conductance!(df, Val(:Thom_1972); zh = thal.zh, zr = thal.zr, - stab_formulation = Val(:no_stability_correction)) + aerodynamic_conductance!(df; Gb_model=Val(:Thom_1972)) + # missing due to missing zr + @test all(ismissing.(df.psi_h)) + @test all(ismissing.(df.psi_m)) + aerodynamic_conductance!(df; Gb_model=Val(:Thom_1972), zr = thal.zr, zh = thal.zh + ,stab_formulation = Val(:no_stability_correction)) @test all(iszero.(df.psi_h)) @test all(iszero.(df.psi_m)) - @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + @test propertynames(df)[(end-8):end] == + SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] + # TODO compare with R end - @testset "compute_Gb Gb_Thom" begin df = copy(tha48) - aerodynamic_conductance!(df, Val(:Thom_1972); zh = thal.zh, zr = thal.zr) - @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + aerodynamic_conductance!(df; Gb_model=Val(:Thom_1972), zh = thal.zh, zr = thal.zr) + @test propertynames(df)[(end-8):end] == + SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end @testset "constant_kB1" begin kB_h = 1.18 # DataFrame variant df = copy(tha48) - aerodynamic_conductance!(df, Val(:constant_kB1); kB_h, zh = thal.zh, zr = thal.zr) - @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + aerodynamic_conductance!(df; Gb_model=Val(:constant_kB1), kB_h, zh = thal.zh, zr = thal.zr) + @test propertynames(df)[(end-8):end] == + SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end @testset "compute_Gb Gb_Choudhury" begin leafwidth=0.1 df = copy(tha48) - aerodynamic_conductance!(df, Val(:Choudhury_1988); + aerodynamic_conductance!(df; Gb_model=Val(:Choudhury_1988), leafwidth, LAI=thal.LAI, zh = thal.zh, zr = thal.zr) - @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + @test propertynames(df)[(end-8):end] == + SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end @testset "compute_Gb Gb_Su" begin Dl=0.01 df = copy(tha48) - aerodynamic_conductance!(df, Val(:Su_2001); Dl, LAI=thal.LAI, zh=thal.zh, zr=thal.zr) - @test propertynames(df)[(end-3):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2] + aerodynamic_conductance!(df; Gb_model=Val(:Su_2001), Dl, LAI=thal.LAI, zh=thal.zh, zr=thal.zr) + @test propertynames(df)[(end-8):end] == + SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end diff --git a/test/runtests.jl b/test/runtests.jl index 26a5aca..feb745c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -56,6 +56,9 @@ thal = ( @testset "boundary_layer_conductance" begin include("boundary_layer_conductance.jl") end + @testset "aerodynamic_conductance" begin + include("aerodynamic_conductance.jl") + end @testset "evapotranspiration" begin include("evapotranspiration.jl") end From 888056d9d3061dc87b61036e20fc8d628d433e94 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 8 Nov 2021 22:25:37 +0100 Subject: [PATCH 40/43] subset for valid data in walkthrough --- docs/src/walkthrough.md | 26 ++++++++++++-------------- src/surface_roughness.jl | 8 +++++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/src/walkthrough.md b/docs/src/walkthrough.md index 56169d7..bd88b57 100644 --- a/docs/src/walkthrough.md +++ b/docs/src/walkthrough.md @@ -251,13 +251,12 @@ setinvalid_afterprecip!(thaf; min_precip=0.02, hours_after=24) sum(.!thaf.valid) # some more invalids ``` +In this walkthrough we use the data as filtered above: ```@example doc thas = subset(thaf, :valid) ``` - - ## Meteorological variables The `Bigleaf.jl` package provides calculation routines for a number of meteorological variables, which are basic to the calculation of many other variables. A few examples on their usage are given below: @@ -384,9 +383,9 @@ Functin `add_Gb` calculates $G_b$ for other trace gases, provided that the respe number is known. ```@example doc -compute_Gb!(thaf, Val(:Thom_1972)) # adds/modifies column Gb_h and Gb_CO2 -add_Gb!(thaf, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) # adds Gb_O2 and Gb_CH4 -select(first(thaf,3), r"Gb_") +compute_Gb!(thas, Val(:Thom_1972)) # adds/modifies column Gb_h and Gb_CO2 +add_Gb!(thas, :Gb_O2 => 0.84, :Gb_CH4 => 0.99) # adds Gb_O2 and Gb_CH4 +select(first(thas,3), r"Gb_") ``` ## Wind profile @@ -404,18 +403,17 @@ close to the surface and weaker at greater heights: using Statistics wind_heights = 22:2:60.0 d = 0.7 * thal.zh -#psi_m = stability_correction!(copy(tha, copycols=false), thal.zr, d).psi_m -#z0m = roughness_parameters(Val(:wind_profile), tha, thal.zh, thal.zr; psi_m).z0m +z0m = roughness_parameters(Val(:wind_profile), thas, thal.zh, thal.zr).z0m wp = map(wind_heights) do z - wind_profile(tha,z,d; zh=thal.zh, zr=thal.zr) + wind_profile(thas,z,d, z0m; zh=thal.zh, zr=thal.zr) end nothing # hide ``` ```@setup doc wp_means = map(x -> mean(skipmissing(x)), wp) wp_sd = map(x -> std(skipmissing(x)), wp) -wr_mean = mean(skipmissing(tha.wind)) # measurements at reference height -wr_sd = std(skipmissing(tha.wind)) +wr_mean = mean(skipmissing(thas.wind)) # measurements at reference height +wr_sd = std(skipmissing(thas.wind)) using Plots # plot wind profiles for the three rows in df plot(wp_means, wind_heights, ylab = "height (m)", xlab = "wind speed (m/s)", xerror=wp_sd, label=nothing) @@ -439,11 +437,11 @@ evapotranspiration (PET). At the moment, the `Bigleaf.jl` contains two formulati for the estimate of PET: the Priestley-Taylor equation, and the Penman-Monteith equation: ```@example doc -potential_ET!(thaf, Val(:PriestleyTaylor); G = thaf.G, infoGS = false) +potential_ET!(thas, Val(:PriestleyTaylor); G = thas.G, infoGS = false) # TODO need aerodynamci and surface conductance to compute Ga and Gs_mol before -# potential_ET!(thaf, Val(:PenmanMonteith); G = thaf.G, -# Gs_pot=quantile(skipmissing(thaf.Gs_mol),0.95)) -select(thaf[24:26,:], :datetime, :ET_pot, :LE_pot) +# potential_ET!(thas, Val(:PenmanMonteith); G = thas.G, +# Gs_pot=quantile(skipmissing(thas.Gs_mol),0.95)) +select(thas[24:26,:], :datetime, :ET_pot, :LE_pot) ``` In the second calculation it is important to provide an estimate of aerodynamic diff --git a/src/surface_roughness.jl b/src/surface_roughness.jl index 07f71dd..f36b16f 100755 --- a/src/surface_roughness.jl +++ b/src/surface_roughness.jl @@ -268,14 +268,16 @@ function wind_profile(df::AbstractDataFrame, z, d, z0m = nothing; stab_formulation = Val(:Dyer_1970), constants = bigleaf_constants() ) # TODO providingn z or zr to stabiity correction? - psi_m = stability_correction!( - copy(df, copycols=false), z, d; stab_formulation, constants).psi_m if isnothing(z0m) (isnothing(zh) || isnothing(zr)) && error( "wind_profile: If 'z0m' is not given, must specify both 'zh' and 'zr' optional " * "arguments to estimate 'z0m' from 'df.wind' and 'df.ustar' measured at height zr.") - z0m = roughness_parameters(Val(:wind_profile), df, zh, zr; psi_m = psi_m).z0m + # uses psi_m at zr + z0m = roughness_parameters(Val(:wind_profile), df, zh, zr).z0m end + psi_m = stability_correction!( + copy(df, copycols=false), z, d; stab_formulation, constants).psi_m + #wind_profile(df, z, d, z0m, psi_m; constants) wind_profile(df, z, d, z0m, psi_m; constants) end From d006e723bfe25650ab27f28a1bd7738ae158aab9 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 8 Nov 2021 22:59:26 +0100 Subject: [PATCH 41/43] adapt tests to updated wind_profile --- src/boundary_layer_conductance.jl | 4 ++-- test/surface_roughness.jl | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/boundary_layer_conductance.jl b/src/boundary_layer_conductance.jl index 2caa9f6..51802a8 100755 --- a/src/boundary_layer_conductance.jl +++ b/src/boundary_layer_conductance.jl @@ -35,7 +35,7 @@ df = DataFrame(Tair=25,pressure=100,wind=[3,4,5], ustar=[0.5,0.6,0.65],H=[200,230,250]) leafwidth=0.01;LAI=5;zh=25;zr=40 compute_Gb!(df, Val(:Choudhury_1988); leafwidth, LAI, zh, zr) -≈(df.Gb_h[1], 0.329, rtol=1e-3) +≈(df.Gb_h[1], 0.379, rtol=1e-3) # output true ``` @@ -360,7 +360,7 @@ compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.01,LAI=5) compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.1,LAI=5) # same conditions, large leaves, and sparse canopy cover (LAI = 1.5) compute_Gb!(df,Val(:Su_2001), zh=25,zr=40,Dl=0.1,LAI=1.5) -≈(df.Gb_h[1], 0.0746, rtol=1e-3) +≈(df.Gb_h[1], 0.0638, rtol=1e-3) # output true ``` diff --git a/test/surface_roughness.jl b/test/surface_roughness.jl index adce8db..264dbfc 100644 --- a/test/surface_roughness.jl +++ b/test/surface_roughness.jl @@ -53,7 +53,7 @@ end u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) @test ≈(u30c, 2.31, rtol = 1/100 ) # - z0m=2.14 #2.65 + z0m=1.9 #2.14 #2.65 u30 = wind_profile(z, ustar, d, z0m) # used below u30c = wind_profile(Val(:Dyer_1970), z, ustar, Tair,pressure, H, d, z0m) df = copy(tha48) @@ -73,6 +73,7 @@ end @test_throws Exception wind_profile(df, z, d) windzc3 = wind_profile(df, z, d; zh=thal.zh, zr=thal.zr) # may have used slightly different estimated z0m - @test all(isapprox.(windzc3, windzc, atol=0.01)) + #windzc3 - windzc + @test all(isapprox.(windzc3, windzc, atol=0.1)) end From 2295bab01ce2e65bf7e5097bcb196e4d085b9280 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 8 Nov 2021 23:21:23 +0100 Subject: [PATCH 42/43] adapt NamedTuple indexing by Symbol Vector to 1.6 --- test/boundary_layer_conductance.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boundary_layer_conductance.jl b/test/boundary_layer_conductance.jl index 04c0c68..9bc0756 100644 --- a/test/boundary_layer_conductance.jl +++ b/test/boundary_layer_conductance.jl @@ -30,7 +30,7 @@ end Gb = Gb_constant_kB1(missing, kB_h) @test keys(Gb) == (:Rb_h, :Gb_h, :kB_h, :Gb_CO2) @test Gb.kB_h == kB_h - @test all(ismissing.(values(Gb[SA[:Rb_h, :Gb_h, :Gb_CO2]]))) + @test all(ismissing.(values(getindex.(Ref(Gb), SA[:Rb_h, :Gb_h, :Gb_CO2])))) # # DataFrame variant dfo = DataFrame(ustar = SA[0.1,missing,0.3]) From d991cb62ac5155fe42dd071142ebc0ffc8dc43de Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 9 Nov 2021 07:35:05 +0100 Subject: [PATCH 43/43] add test for roughness_z0h --- test/aerodynamic_conductance.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/aerodynamic_conductance.jl b/test/aerodynamic_conductance.jl index c2b6cf6..5b25c33 100644 --- a/test/aerodynamic_conductance.jl +++ b/test/aerodynamic_conductance.jl @@ -61,14 +61,14 @@ end # TODO compare with R end -@testset "compute_Gb Gb_Thom" begin +@testset "aerodynamic_conductance! Gb_Thom" begin df = copy(tha48) aerodynamic_conductance!(df; Gb_model=Val(:Thom_1972), zh = thal.zh, zr = thal.zr) @test propertynames(df)[(end-8):end] == SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end -@testset "constant_kB1" begin +@testset "aerodynamic_conductance! constant_kB1" begin kB_h = 1.18 # DataFrame variant df = copy(tha48) @@ -77,7 +77,7 @@ end SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end -@testset "compute_Gb Gb_Choudhury" begin +@testset "aerodynamic_conductance! Gb_Choudhury" begin leafwidth=0.1 df = copy(tha48) aerodynamic_conductance!(df; Gb_model=Val(:Choudhury_1988), @@ -86,7 +86,7 @@ end SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end -@testset "compute_Gb Gb_Su" begin +@testset "aerodynamic_conductance! Gb_Su" begin Dl=0.01 df = copy(tha48) aerodynamic_conductance!(df; Gb_model=Val(:Su_2001), Dl, LAI=thal.LAI, zh=thal.zh, zr=thal.zr) @@ -94,3 +94,9 @@ end SA[:Rb_h, :Gb_h, :kB_h, :Gb_CO2, :Ra_m, :Ga_m, :Ga_h, :Ra_h, :Ga_CO2] end +@testset "roughness_z0h" begin + z0m, kB_h = 1.2, 3.4 + z0h = roughness_z0h(z0m, kB_h) + @test z0h ≈ 0.0400 rtol = 1e-2 +end +