-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tidy up leap year implementation, and other date-related cleanup #1623
Conversation
New function `PEcAn.utils::days_in_year(year)` calculates the days in a year given a year (or vector of years). I used this function to replace many occurrences of `ifelse(leap_year(year), 366, 355)` many places in the code. I also simplified many statements related to leap year if-else clauses, where it was simple enough to do so. Also, new function `PEcAn.data.atmsphere::eccentricity_obliquity(doy)` does the appropriate math. This was refactored out of several places that had this fairly complex equation duplicated. Also, all of the files I touched have had trailing whitespace removed (courtesy of RStudio), which extends the review diff, but is good coding practice.
For completeness, even though it doesn't actually do anything yet.
models/ed/R/met2model.ED2.R
Outdated
f <- pi/180 * (279.5 + 0.9856 * doy) | ||
et <- (-104.7 * sin(f) + 596.2 * sin(2 * f) + 4.3 * sin(4 * f) - 429.3 * | ||
cos(f) - 2 * cos(2 * f) + 19.3 * cos(3 * f)) / 3600 # equation of time -> eccentricity and obliquity | ||
et <- PEcAn.data.atmosphere::eccentricity_obliquity(doy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's great to encapsulate this, but you could probably go even further and encapsulate the whole solar geometry calculation of solar zenith angle as well (i.e. everything up to cosz below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was thinking that too. I went through it quickly and pulled this out because it was really obvious and I saw it in a few places. But yeah, I'll go ahead and do that, so wait to merge this until I do.
@ashiklom you've got a ROxygen fail on models/jules/man/detect.timestep.Rd |
models/dalec/R/met2model.DALEC.R
Outdated
dt <- (366 * 24 * 60 * 60) / length(sec), # leap year | ||
dt <- (365 * 24 * 60 * 60) / length(sec)) # non-leap year | ||
diy <- PEcAn.utils::days_in_year(year) | ||
dt <- diy * 24 * 60 * 60 / length(sec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't the convention be to use
udunits::ud.convert(diy, 'days', 'seconds') / length(sec)
models/ed/R/met2model.ED2.R
Outdated
dtmp <- rep(1:366, each = 86400 / dt) | ||
} | ||
diy <- PEcAn.utils::days_in_year(y) | ||
ytmp <- rep(y, diy * 86400 / dt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another case where seconds / day is hardcoded. Better to use ud.convert?
models/ed/R/met2model.ED2.R
Outdated
@@ -146,7 +145,7 @@ met2model.ED2 <- function(in.path, in.prefix, outfolder, start_date, end_date, l | |||
if (useCO2) { | |||
CO2 <- c(rep(CO2[1], toff), CO2)[1:slen] | |||
} | |||
|
|||
## build time variables (year, month, day of year) | |||
skip <- FALSE | |||
nyr <- floor(length(sec) / 86400 / 365 * dt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like a kludge. Could ud.convert work here too?
models/ed/R/met2model.ED2.R
Outdated
|
||
|
||
diy <- PEcAn.utils::days_in_year(year) | ||
dt <- diy * 24 * 60 * 60 / length(sec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use ud.convert? This pattern seems to be repeated a lot. Maybe we need another function seconds_in_year
?
models/ed/R/met2model.ED2.R
Outdated
cosz <- sin(lat * pi/180) * sin(dec) + cos(lat * pi/180) * cos(dec) * cos(h) | ||
cosz[cosz < 0] <- 0 | ||
|
||
cosz <- PEcAn.data.atmosphere::solar_angle(doy, lat, lon, dt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this function return solar_angle, or cos(zenith angle)? I would expect that solar_angle to return zenith_angle, and perhaps function should be named cos_solar_zenith_angle???
@@ -628,7 +628,8 @@ detect.timestep <- function(met.dir,met.regexp,start_date){ | |||
tlen <- grep("time =", met.header) | |||
if (length(tlen) > 0) { | |||
tlen <- as.numeric(gsub(pattern = "[^[:digit:]]", "", met.header[tlen])) | |||
dt <- 86400 / round(tlen/(365 + lubridate::leap_year(as.Date(start_date)))) | |||
diy <- PEcAn.utils::days_in_year(lubridate::year(as.Date(start_date))) | |||
dt <- 86400 / round(tlen/(diy)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another repeated could this be wrapped in a function called dt
?
VPD <- VPD * 0.01 # convert to Pascal | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these transformations should also use ud.convert
models/sipnet/R/met2model.SIPNET.R
Outdated
366 * 24 * 60 * 60 / length(sec), # leap year | ||
365 * 24 * 60 * 60 / length(sec)) # non-leap year | ||
|
||
dt <- diy * 24 * 60 * 60 / length(sec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!!!!!
modules/assim.batch/R/get.da.data.R
Outdated
@@ -43,33 +43,33 @@ calculate.nee.L <- function(yeardoytime, model.i.nee, observed.flux, be, bu) { | |||
get.da.data <- function(out.dir, ameriflux.dir, years, be, bu, ensemble.size = 199) { | |||
ensemble.size <- 500 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this hard-coded value of ensemble size a bug?
ntime <- ifelse(year %% 4 == 0, 2923, 2919) # should use lubridate::leap_year? | ||
|
||
ntime <- ifelse(lubridate::leap_year(year), 2923, 2919) ## ANS: Where do these numbers come from? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these numbers are the number of 3 hour intervals in a year -1 (365*8=2920)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps can be replaced by days_in_year(year) * 8 - 1
?
#' @param doy Day of year | ||
#' @param lat Latitude | ||
#' @param lon Longitude | ||
#' @param dt Timestep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could be better documented.
- What is the return value?
- cite a canonical source of the equations used
- add description
Should the name be cos_solar_zenith_angle to be more explicit?
#' @author Alexey Shiklomanov | ||
#' @param doy Day of year | ||
#' @export | ||
eccentricity_obliquity <- function(doy) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ibid
eccentricity * | ||
(cos(declination) * | ||
cos(deg2rad(LAT)) * | ||
radiation = 1367.0 * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
magic number !!!also uses =
instead of <-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an old script (not a package function definition) that hasn't been touched in a while, so I'm just gonna leave it. There's a lot more that's wrong with it, which I don't feel like spending time fixing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a good start
I made a few comments - it seems that there is an opportunity to clean up some more magic numbers as seconds/ day is repeatedly calculated or given as a magic number. dt <- diy * 24 * 60 * 60 / length(sec)
I found a few bits of code that you didn't explicitly touch but are worth looking at.
Also not sure what the current thinking on roll-your-own vs. use existing packages, but there is already the insol package that calculates solar zenith angle and potential radiation
Per @dlebauer recommendations.
Use it in all the `met2model` code instead of hard-coded math.
Also, Rstudio removed trailing whitespace from a bunch of files.
Also, some more whitespace fixes by RStudio.
@dlebauer I addressed many of your comments, but to address all of the cases where we have had-coded date-time conversions would take a while. I definitely agree that all of our met processing and met2model code could use some serious cleanup and refactoring, but I don't have the time to sift through every line to figure out what the conversions are (especially since I wrote none of this original code, so it's often hard for me to divine exactly what's going on). I think this PR makes decent progress on a lot of the required cleanup, and it's worth merging now and continuing the process in later pull requests. If you or @mdietze disagree, you can request additional changes to the PR, but it'll be a little while before I get to them since I have a lot of other stuff to catch up on. |
|
||
## Format/convert inputs | ||
ppfd <- tapply(PPFD, doy, mean, na.rm = TRUE) # Find the mean for the day | ||
tair <- udunits2::ud.convert(tapply(Tair, doy, mean, na.rm = TRUE), "kelvin", "celsius") # Convert Kelvin to Celcius | ||
vpd <- udunits2::ud.convert(tapply(VPD, doy, mean, na.rm = TRUE), "Pa", "kPa") # pascal to kila pascal | ||
precip <- tapply(Precip, doy, sum, na.rm = TRUE) # Sum to daily precipitation | ||
co2 <- tapply(CO2, doy, mean) # need daily average, so sum up day | ||
co2 <- co2 / 1e+06 # convert to ppm | ||
co2 <- co2 / 1e+06 # convert to ppm. ANS: Convert from what? Mole-fraction to ppm is multiplying by 1e6, not dividing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an error that is currently causing invalid calculations? If so, who can resolve it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tonygardella wrote this coupler, so I think he can check and resolve this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix it!
Description
New function
PEcAn.utils::days_in_year(year)
calculates the days in a year given a year (or vector of years). I used this function to replace many occurrences ofifelse(leap_year(year), 366, 355)
many places in the code. I also simplified many statements related to leap year if-else clauses, where it was simple enough to do so.Also, new function
PEcAn.data.atmsphere::eccentricity_obliquity(doy)
does the appropriate math. This was refactored out of several places that had this fairly complex equation duplicated.Also, all of the files I touched have had trailing whitespace removed (courtesy of RStudio), which extends the review diff, but is good coding practice.
Motivation and Context
See issue #801.
Types of changes
Checklist: