Skip to content

Commit 7e959cb

Browse files
authored
Merge pull request #3597 from dlebauer/saturated_vapor_pressure
Consolidate methods and standardize units for calculating saturated vapor pressure deficit
2 parents 8a77ccd + 75cf90d commit 7e959cb

8 files changed

Lines changed: 195 additions & 50 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ section for the next release.
1111

1212
* Add function `clip_and_save_raster_file()` for subsetting rasters to match a polygon of interest (#3537).
1313
* Add CH4 and N2O to standard_vars in PEcAn.utils
14-
14+
* New function `sat_vapor_pressure()` added for computing saturation vapor pressure from temperature using various methods.
15+
*
1516
## [1.9.0] - 2025-05-25
1617

1718
### Added

modules/data.atmosphere/NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export(process_gridded_noaa_download)
9494
export(qair2rh)
9595
export(read.register)
9696
export(rh2qair)
97+
export(sat_vapor_pressure)
9798
export(save.betas)
9899
export(save.model)
99100
export(site.lst)

modules/data.atmosphere/NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
* `ERA5_met_process()` can now process ensemble data efficiently in parallel using new option `n_cores`
44
* Dependency `ggplot2` is now suggested rather than required. It is used in two vignettes and for optional diagnostic plots from `debias_met_regression`.
5-
5+
* New function `sat_vapor_pressure()` added for computing saturation vapor pressure from temperature using various methods.
66

77
# PEcAn.data.atmosphere 1.9.0
88

modules/data.atmosphere/R/metutils.R

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,32 +69,94 @@ get.vpd <- function(rh, temp) {
6969
## calculate saturation vapor pressure
7070
es <- get.es(temp)
7171
## calculate vapor pressure deficit
72-
return(((100 - rh)/100) * es)
72+
return(((100 - rh) / 100) * es)
7373
} # get.vpd
7474

75-
##' Calculate saturation vapor pressure
76-
##'
77-
##' @title get es
78-
##' @param temp temperature in degrees C
79-
##' @return saturation vapor pressure in mb
80-
##' @export
81-
##' @author David LeBauer
82-
##' @examples
83-
##' temp <- -30:30
84-
##' plot(temp, get.es(temp))
85-
get.es <- function(temp) {
86-
return(6.11 * exp((2500000/461) * (1/273 - 1/(273 + temp))))
87-
} # get.es
75+
#' Saturation vapor pressure (t2es)
76+
#'
77+
#' Compute saturation vapor pressure from temperature using one of the
78+
#' following methods:
79+
#' - (Default) Clausius–Clapeyron (FAO-56 style) — Recommended for most applications.
80+
#' Commonly used approximation for terrestrial ecosystem models, consistent with Penman-Monteith
81+
#' and FAO-56 (Allen et al, 1998).
82+
#' - Magnus — More accurate in the range −40 to +50 C. Coefficients as in Alduchov & Eskridge (1996).
83+
#' - Goff–Gratch - Highest accuracy; use when following WMO-style recommendations. Goff–Gratch 1946; WMO, 2014.
84+
#'
85+
#' Each method uses different units internally, users can specify units
86+
#' for both inputs and outputs, with defaults "degC" and "kPa", respectively.
87+
#'
88+
#' @param temp numeric vector of temperatures
89+
#' @param method one of "Magnus","ClausiusClapeyron" (default), or "GoffGratch".
90+
#' See details for references.
91+
#' @param temp_units input temperature units ("degC","K","degF"), default "degC"
92+
#' @param out_units output pressure units ("kPa","hPa","Pa","mb"), default "kPa"
93+
#' @return numeric vector in `out_units`
94+
#' @aliases t2es
95+
#'
96+
#' @references
97+
#' Alduchov, O. A., & Eskridge, R. E. (1996). Improved Magnus Form Approximation of Saturation Vapor Pressure. J. Appl. Meteor.*, 35(4), 601–609. <doi:10.1175/1520-0450(1996)035<0601:IMFAOS>2.0.CO;2>
98+
#'
99+
#' 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.
100+
#'
101+
#' Goff, J. A., & Gratch, S. (1946). Low-pressure properties of water from −160 to 212F. Trans. ASHVE, 52, 95–122.
102+
#'
103+
#' WMO (2014) Guide to Instruments and Methods of Observation (WMO-No. 8), ch. 4.
104+
#' @md
105+
#' @author David LeBauer
106+
#' @export
107+
sat_vapor_pressure <- function(
108+
temp,
109+
temp_units = "degC",
110+
out_units = "kPa",
111+
method = c("ClausiusClapeyron", "Magnus", "GoffGratch")) {
112+
method <- match.arg(method)
113+
# normalize common alias
114+
if (tolower(out_units) == "mb") out_units <- "hPa"
115+
116+
if (method == "Magnus") {
117+
# canonical temp: degC; canonical pressure: kPa
118+
Tc <- units::ud_convert(temp, temp_units, "degC")
119+
es_kPa <- 0.61078 * exp((17.27 * Tc) / (Tc + 237.3))
120+
return(units::ud_convert(es_kPa, "kPa", out_units))
121+
}
122+
123+
if (method == "ClausiusClapeyron") {
124+
# canonical temp: degC; canonical pressure: hPa
125+
Tc <- units::ud_convert(temp, temp_units, "degC")
126+
L <- 2.5e6 # J kg^-1
127+
Rv <- 461 # J kg^-1 K^-1
128+
es_hPa <- 6.11 * exp((L / Rv) * (1 / 273 - 1 / (273 + Tc)))
129+
return(units::ud_convert(es_hPa, "hPa", out_units))
130+
}
88131

89-
## TODO: merge SatVapPress with get.es; add option to choose method
90-
SatVapPres <- function(T) {
91-
# /estimates saturation vapor pressure (kPa) Goff-Gratch 1946 /input: T = absolute temperature
92-
T_st <- 373.15 ##steam temperature (K)
93-
e_st <- 1013.25 ##/saturation vapor pressure at steam temp (hPa)
94-
return(0.1 * exp(-7.90298 * (T_st/T - 1) + 5.02808 * log(T_st/T) - 1.3816e-07 * (10^(11.344 * (1 - T/T_st)) -
95-
1) + 0.0081328 * (10^(-3.49149 * (T_st/T - 1)) - 1) + log(e_st)))
96-
} # SatVapPres
132+
if (method == "GoffGratch") {
133+
# canonical temp: K; canonical pressure: hPa
134+
Tk <- units::ud_convert(temp, temp_units, "K")
135+
Tst <- 373.15 # K
136+
est <- 1013.246 # hPa at steam point
137+
lg10 <- function(z) log10(z)
138+
log10_es <- -7.90298 * (Tst / Tk - 1) +
139+
5.02808 * lg10(Tst / Tk) -
140+
1.3816e-7 * (10^(11.344 * (1 - Tk / Tst)) - 1) +
141+
8.1328e-3 * (10^(-3.49149 * (Tst / Tk - 1)) - 1) +
142+
lg10(est)
143+
es_hPa <- 10^log10_es
144+
return(units::ud_convert(es_hPa, "hPa", out_units))
145+
}
146+
}
97147

148+
# ---- Aliases for backward-compatibility ----
149+
150+
#' @rdname sat_vapor_pressure
151+
#' @export
152+
get.es <- function(temp) {
153+
sat_vapor_pressure(
154+
temp = temp,
155+
method = "ClausiusClapeyron",
156+
temp_units = "degC",
157+
out_units = "hPa"
158+
)
159+
}
98160

99161
##' Calculate RH from temperature and dewpoint
100162
##'
@@ -201,7 +263,7 @@ sw2ppfd <- function(sw) {
201263
##' Campbell and Norman (1998). Introduction to Environmental Biophysics. pg 151 'the energy content of solar radiation in the PAR waveband is 2.35 x 10^5 J/mol'
202264
##' See also the chapter radiation basics (10)
203265
##' Here the input is the total solar radiation so to obtain in the PAR spectrum need to multiply by 0.486
204-
##' This last value 0.486 is based on the approximation that PAR is 0.45-0.50 of the total radiation
266+
##' This is based on the approximation that PAR is 0.45-0.50 of the total radiation
205267
##' This means that 1e6 / (2.35e6) * 0.486 = 2.07
206268
##' 1e6 converts from mol to mu mol
207269
##' 1/3600 divides the values in hours to seconds

modules/data.atmosphere/man/get.es.Rd

Lines changed: 0 additions & 24 deletions
This file was deleted.

modules/data.atmosphere/man/sat_vapor_pressure.Rd

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/data.atmosphere/man/solarMJ2ppfd.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/data.atmosphere/tests/testthat/test.metutils.R

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,52 @@ test_that("get.rh RH from dewpoint",{
3131
expect_equal(getrhtest(25, 10), 38.82, tolerance = 0.2)
3232
expect_equal(getrhtest(0, -5), 69, tolerance = 0.2)
3333
})
34+
35+
test_that("different methods of sat_vapor_pressure work correctly", {
36+
expect_equal(
37+
sat_vapor_pressure(c(-10, 10), method = "Magnus"),
38+
c(0.286, 1.228),
39+
tolerance = 0.001
40+
)
41+
expect_equal(
42+
sat_vapor_pressure(c(-10, 10), method = "ClausiusClapeyron"),
43+
c(0.287, 1.233),
44+
tolerance = 0.001
45+
)
46+
expect_equal(
47+
sat_vapor_pressure(c(-10, 10), method = "GoffGratch"),
48+
c(0.286, 1.227),
49+
tolerance = 0.001
50+
)
51+
})
52+
53+
test_that("sat_vapor_pressure works with different units", {
54+
expect_equal(
55+
sat_vapor_pressure(283.15,
56+
method = "GoffGratch",
57+
temp_units = "K",
58+
out_units = "mb"
59+
),
60+
12.27,
61+
tolerance = 0.01
62+
)
3463

64+
expect_equal(
65+
sat_vapor_pressure(283.15,
66+
method = "ClausiusClapeyron",
67+
temp_units = "K",
68+
out_units = "kPa"
69+
),
70+
1.227,
71+
tolerance = 0.01
72+
)
73+
expect_equal(
74+
sat_vapor_pressure(283.15,
75+
method = "Magnus",
76+
temp_units = "K",
77+
out_units = "Pa"
78+
),
79+
1227,
80+
tolerance = 1
81+
)
82+
})

0 commit comments

Comments
 (0)