diff --git a/DESCRIPTION b/DESCRIPTION index b2b5b55..c4d05dc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: growR Type: Package -Version: 1.0.9.9000 +Version: 1.0.9.9001 Date: 2023-09-27 Authors@R: person( given = "Kevin", diff --git a/NAMESPACE b/NAMESPACE index be86731..66e6ce0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,8 @@ export(add_lines) export(analyze_parameter_scan) export(atmospheric_CO2) export(box_smooth) +export(browse) +export(browse_end) export(build_functional_group) export(create_combinations) export(create_example_environment) diff --git a/NEWS.md b/NEWS.md index 1b56472..1024a25 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# growR 1.0.1 +# growR 1.1.0 ## Added @@ -16,6 +16,11 @@ * S3 dispatch for plot method of `ModvegeSite` objects -> `plot(mvs)` is now possible if `mvs` is a `ModvegeSite` instance. +* `ParameterData` input checking: throws error on duplicate input parameter + name. + +* Debug utility conveniences `browse` and `browse_end`. + ## Changed * Input data CSV files are now actual CSV files, instead of @@ -33,6 +38,9 @@ * autocut: `get_annual_gross_yield` was incorrectly hardcoded to return 1. +* `ParameterData$set_parameters` now updates initial condition values, if + applicable. + ## Removed * Removed superfluous weather inputs. @@ -41,6 +49,8 @@ * Redundant argument *store_results* in `growR_run_loop`. +* `SEA` symmetrization around 1 is not enforced anymore. + # growR 1.0.0 * Initial CRAN submission. diff --git a/R/environment.R b/R/environment.R index e0dfaec..f2e91bd 100644 --- a/R/environment.R +++ b/R/environment.R @@ -78,8 +78,7 @@ ModvegeEnvironment = R6Class( input_dir = NULL) { # Set instance variables self$site_name = site_name - self$run_name = run_name - self$run_name_in_filename = self$make_filename_for_run(run_name) + self$set_run_name(run_name) self$years = years # Revert to defaults for the not provided values. if (param_file == "-") { @@ -107,6 +106,16 @@ ModvegeEnvironment = R6Class( self$load_inputs() }, + #' @description + #' Set run name and update *run_name_in_filename*. + #' + #' @param run_name Str. New value of `self$run_name`. + #' + set_run_name = function(run_name) { + self$run_name = run_name + self$run_name_in_filename = self$make_filename_for_run(run_name) + }, + #' @description Load simulation inputs. #' #' Stores parameters, management and weather data from files specified in diff --git a/R/modvegesite.R b/R/modvegesite.R index 00e497a..1b16371 100644 --- a/R/modvegesite.R +++ b/R/modvegesite.R @@ -94,7 +94,7 @@ ModvegeSite = R6Class( #-Public-attributes------------------------------------------------------------- #' @field time_step Used time step in the model in days (untested). time_step = 1., -#' @field state_variable_namse Vector containing the names of the model's +#' @field state_variable_names Vector containing the names of the model's #' state variables. state_variable_names = NULL, #' @field n_state_variables Number of state variables. @@ -538,9 +538,6 @@ ModvegeSite = R6Class( self[["OMDGV"]][1] = P$OMDGV0 self[["OMDGR"]][1] = P$OMDGR0 - # (minSEA + maxSEA)/2 = 1 - self$parameters[["minSEA"]] = 2 - P[["maxSEA"]] - # Management: # Initialize a pointer that indicates whether there has been a cut # during reproductive growth. In this case, reproductive growth is diff --git a/R/parameter_scan.R b/R/parameter_scan.R index 77d8247..1303739 100644 --- a/R/parameter_scan.R +++ b/R/parameter_scan.R @@ -187,8 +187,6 @@ analyze_parameter_scan = function(parameter_scan_results, datafile = "", } # Reduce to relevant years relevant_data = measured_data[measured_data$year %in% years, ] - # Construct a selector for the DOYs present in relevant data - mask = relevant_data$DOY + 365 * (relevant_data$year - relevant_data$year[1]) # Calculate measured cBM_end measured_cBM_ends = c() for (year in years) { @@ -206,10 +204,11 @@ analyze_parameter_scan = function(parameter_scan_results, datafile = "", cBM_end = c() for (i_year in 1:n_years) { mv = modvegesites[[i_year]] - cBM = c(cBM, mv$cBM) + mask = relevant_data$DOY[relevant_data$year == years[i_year]] + cBM = c(cBM, mv$cBM[mask]) cBM_end = c(cBM_end, mv$cBM[length(mv$cBM)]) smoothed = box_smooth(mv$dBM, box_width = smooth_interval) - dBM = c(dBM, smoothed) + dBM = c(dBM, smoothed[mask]) } results[[combination]][["cBM"]] = list() results[[combination]][["dBM"]] = list() @@ -217,10 +216,10 @@ analyze_parameter_scan = function(parameter_scan_results, datafile = "", # Employ performance metrics for (metric in metrics_to_use) { # cBM - m_cBM = metric_map[[metric]][["func"]](cBM[mask], relevant_data$cBM) + m_cBM = metric_map[[metric]][["func"]](cBM, relevant_data$cBM) results[[combination]][["cBM"]][[metric]] = m_cBM # dBM - m_dBM = metric_map[[metric]][["func"]](dBM[mask], relevant_data$dBM) + m_dBM = metric_map[[metric]][["func"]](dBM, relevant_data$dBM) results[[combination]][["dBM"]][[metric]] = m_dBM # cBM_end m_cBM_end = metric_map[[metric]][["func"]](cBM_end, measured_cBM_ends) diff --git a/R/parameters.R b/R/parameters.R index bc13d31..edafc65 100644 --- a/R/parameters.R +++ b/R/parameters.R @@ -118,6 +118,9 @@ ModvegeParameters = R6Class( #' @field fg_parameter_names Names of vegetation parameters defined by the #' functional group composition. fg_parameter_names = NULL, +#' @field initial_condition_names Names of initial conditions. + initial_condition_names = c(initial_condition_names, + "WHC", "maxOMDGV", "maxOMDGR"), #' @field param_file Name of the parameter file from which initial parameter #' values were read. param_file = NULL, @@ -179,17 +182,7 @@ ModvegeParameters = R6Class( # Update the functional group parameters in P. self$update_functional_group() - # Set initial conditions - for (name in initial_condition_names) { - old_name = name - new_name = paste0(old_name, "0") - self[[new_name]] = self[[old_name]] - } - - # Set some more initial values - self[["WR0"]] = self[["WHC"]] - self[["OMDGV0"]] = self[["maxOMDGV"]] - self[["OMDGR0"]] = self[["maxOMDGR"]] + private$update_initial_conditions() }, #' @description Savely update the given parameters @@ -205,11 +198,17 @@ ModvegeParameters = R6Class( for (i in 1:length(params)) { name = param_names[[i]] self[[name]] = params[[i]] + # :TODO: Also update initial values XXX0. } # Check if FG composition needs to be updated if (any(param_names %in% c("w_FGA", "w_FGB", "w_FGC", "w_FGD"))) { self$update_functional_group() } + + # Check if initial conditions need updating + if (any(param_names %in% self$initial_condition_names)) { + private$update_initial_conditions() + } }, #' @description Update functional group parameters @@ -230,7 +229,8 @@ ModvegeParameters = R6Class( } }, - #' @description Parameter Sanity Check + #' @description + #' Parameter Sanity Check #' Ensure that the supplied *params* are valid ModVege parameters and, #' if requested, check that all required parameters are present. #' Issues a warning for any invalid parameters and throws an error if @@ -247,6 +247,11 @@ ModvegeParameters = R6Class( #' #' @md check_parameters = function(param_names, check_for_completeness = TRUE) { + # Check for duplicate param names + if (length(param_names) != length(unique(param_names))) { + logger("Non-unique parameter names in supplied parameters.", + level = ERROR) + } param_file = self$param_file if (check_for_completeness) { # Give error if an argument is missing. @@ -271,6 +276,22 @@ ModvegeParameters = R6Class( return(not_known) } ) + ), + + private = list( + ## Set initial conditions + update_initial_conditions = function() { + for (name in initial_condition_names) { + old_name = name + new_name = paste0(old_name, "0") + self[[new_name]] = self[[old_name]] + } + + # Set some more initial values + self[["WR0"]] = self[["WHC"]] + self[["OMDGV0"]] = self[["maxOMDGV"]] + self[["OMDGR0"]] = self[["maxOMDGR"]] + } ) ) diff --git a/R/utilities.R b/R/utilities.R index a982f77..4b10957 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -41,14 +41,14 @@ check_for_package = function(package, stop = TRUE) { return(TRUE) } -#' Constants representing different debug levels to be used internally. +## Constants representing different debug levels to be used internally. ERROR = 1 WARNING = 2 INFO = 3 DEBUG = 4 TRACE = 5 -#' Names of debug levels. +## Names of debug levels. DEBUG_LEVELS = c("ERROR", "WARNING", "INFO", "DEBUG", "TRACE") #' Set verbosity of growR output. @@ -373,3 +373,67 @@ ensure_table_columns = function(required, data, data_name = "the data table") { } } +#' Create unique DOY + year identifier +#' +#' Return numbers of the form YYYYDDD where YYYY is the year and DDD the DOY. +#' +#' @param years int vector. +#' @param DOYs int vector of same length as *years*. +#' +#' @return int vector of same length as *years* containing numbers of the +#' form YYYYDDD, where the first four digits represent a year and the last +#' four represent a DOY. +make_yearDOY = function(years, DOYs) { + return(1000 * years + DOYs) +} + +#' Debugging utilities +#' +#' @description +#' Debug specified function *func* by entering a [browser()] right at the +#' beginning ([browse()]) or end ([browse_end()]) of the function. +#' +#' @details +#' These are convenience shorthands for R's builtin debug tools, like +#' [debugonce()] and the [trace()]/[untrace()] combination. +#' +#' @param func An R function to be browsed. +#' @param ... Arguments to the function *func* that is to be browsed. +#' +#' @return Returns the result of `func(...)`. Enters a [browser()]. +#' +#' @examplesIf interactive() +#' # Define a simple function for this example +#' my_func = function(a) { for (i in 1:5) { a = a + i }; return(a) } +#' +#' # Enter a browser at the beginning of the function +#' browse(my_func, 0) +#' +#' # Enter a browser at the end of the function. This allows us to inspect +#' # the function's local variables without having to go through the whole loop. +#' browse_end(my_func, 0) +#' +#' @seealso [browser()], [debugonce()], [trace()] +#' +#' @md +#' @export +browse = function(func, ...) { + debugonce(func) + func(...) +} + +#' Enter browser at end of function execution +#' +#' @describeIn browse +#' Enter [browser()] at the end of the function call to `func(...)`. This +#' only works, if the function can execute without error until its end. +#' Otherwise, the error will be thrown. +#' +#' @seealso [trace()] +#' @md +#' @export +browse_end = function(func, ...) { + trace(func, exit = browser, where = environment()) + func(...) +} + diff --git a/_pkgdown.yml b/_pkgdown.yml index ca1213b..6bcedfe 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -56,9 +56,14 @@ reference: - contents: - PscanPlotter - Combinator +- title: Debug Tools + desc: Utilities that may be of help if things go wrong. +- contents: + - browse + - browse_end + - logger - title: Other - contents: - - DEBUG_LEVELS - ERROR - SEA - aCO2_inverse @@ -82,7 +87,6 @@ reference: - get_relative_cut_contribution - get_site_name - growR_package_options - - logger - management_parameters - metric_map - parse_year_strings diff --git a/man/DEBUG_LEVELS.Rd b/man/DEBUG_LEVELS.Rd deleted file mode 100644 index 47a6df4..0000000 --- a/man/DEBUG_LEVELS.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utilities.R -\docType{data} -\name{DEBUG_LEVELS} -\alias{DEBUG_LEVELS} -\title{Names of debug levels.} -\format{ -An object of class \code{character} of length 5. -} -\usage{ -DEBUG_LEVELS -} -\description{ -Names of debug levels. -} -\keyword{datasets} diff --git a/man/ERROR.Rd b/man/ERROR.Rd deleted file mode 100644 index 376f67a..0000000 --- a/man/ERROR.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utilities.R -\docType{data} -\name{ERROR} -\alias{ERROR} -\title{Constants representing different debug levels to be used internally.} -\format{ -An object of class \code{numeric} of length 1. -} -\usage{ -ERROR -} -\description{ -Constants representing different debug levels to be used internally. -} -\keyword{datasets} diff --git a/man/ModvegeEnvironment.Rd b/man/ModvegeEnvironment.Rd index dad2059..2fdd66f 100644 --- a/man/ModvegeEnvironment.Rd +++ b/man/ModvegeEnvironment.Rd @@ -59,6 +59,7 @@ files are searched for. Defaults to `getOption("growR.input_dir").} \subsection{Public methods}{ \itemize{ \item \href{#method-ModvegeEnvironment-new}{\code{ModvegeEnvironment$new()}} +\item \href{#method-ModvegeEnvironment-set_run_name}{\code{ModvegeEnvironment$set_run_name()}} \item \href{#method-ModvegeEnvironment-load_inputs}{\code{ModvegeEnvironment$load_inputs()}} \item \href{#method-ModvegeEnvironment-make_filename_for_run}{\code{ModvegeEnvironment$make_filename_for_run()}} \item \href{#method-ModvegeEnvironment-get_environment_for_year}{\code{ModvegeEnvironment$get_environment_for_year()}} @@ -108,6 +109,23 @@ Defaults to \code{getOption("growR.input_dir")}.} } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ModvegeEnvironment-set_run_name}{}}} +\subsection{Method \code{set_run_name()}}{ +Set run name and update \emph{run_name_in_filename}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ModvegeEnvironment$set_run_name(run_name)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{run_name}}{Str. New value of \code{self$run_name}.} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ModvegeEnvironment-load_inputs}{}}} \subsection{Method \code{load_inputs()}}{ diff --git a/man/ModvegeParameters.Rd b/man/ModvegeParameters.Rd index 60abd3a..bc0b762 100644 --- a/man/ModvegeParameters.Rd +++ b/man/ModvegeParameters.Rd @@ -109,6 +109,8 @@ vegetation parameters.} \item{\code{fg_parameter_names}}{Names of vegetation parameters defined by the functional group composition.} +\item{\code{initial_condition_names}}{Names of initial conditions.} + \item{\code{param_file}}{Name of the parameter file from which initial parameter values were read.} } diff --git a/man/ModvegeSite.Rd b/man/ModvegeSite.Rd index 5368832..fec1a30 100644 --- a/man/ModvegeSite.Rd +++ b/man/ModvegeSite.Rd @@ -98,7 +98,7 @@ Part of ENV due to water limitation. Dimensionless. \describe{ \item{\code{time_step}}{Used time step in the model in days (untested).} -\item{\code{state_variable_namse}}{Vector containing the names of the model's +\item{\code{state_variable_names}}{Vector containing the names of the model's state variables.} \item{\code{n_state_variables}}{Number of state variables.} @@ -173,14 +173,6 @@ the autocut routine will be employed.} } \if{html}{\out{}} } -\section{Active bindings}{ -\if{html}{\out{
}} -\describe{ -\item{\code{state_variable_namse}}{Vector containing the names of the model's -state variables.} -} -\if{html}{\out{
}} -} \section{Methods}{ \subsection{Public methods}{ \itemize{ diff --git a/man/browse.Rd b/man/browse.Rd new file mode 100644 index 0000000..17d7025 --- /dev/null +++ b/man/browse.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities.R +\name{browse} +\alias{browse} +\alias{browse_end} +\title{Debugging utilities} +\usage{ +browse(func, ...) + +browse_end(func, ...) +} +\arguments{ +\item{func}{An R function to be browsed.} + +\item{...}{Arguments to the function \emph{func} that is to be browsed.} +} +\value{ +Returns the result of \code{func(...)}. Enters a \code{\link[=browser]{browser()}}. +} +\description{ +Debug specified function \emph{func} by entering a \code{\link[=browser]{browser()}} right at the +beginning (\code{\link[=browse]{browse()}}) or end (\code{\link[=browse_end]{browse_end()}}) of the function. +} +\details{ +These are convenience shorthands for R's builtin debug tools, like +\code{\link[=debugonce]{debugonce()}} and the \code{\link[=trace]{trace()}}/\code{\link[=untrace]{untrace()}} combination. +} +\section{Functions}{ +\itemize{ +\item \code{browse_end()}: Enter \code{\link[=browser]{browser()}} at the end of the function call to \code{func(...)}. This +only works, if the function can execute without error until its end. +Otherwise, the error will be thrown. + +}} +\examples{ +\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +# Define a simple function for this example +my_func = function(a) { for (i in 1:5) { a = a + i }; return(a) } + +# Enter a browser at the beginning of the function +browse(my_func, 0) + +# Enter a browser at the end of the function. This allows us to inspect +# the function's local variables without having to go through the whole loop. +browse_end(my_func, 0) +\dontshow{\}) # examplesIf} +} +\seealso{ +\code{\link[=browser]{browser()}}, \code{\link[=debugonce]{debugonce()}}, \code{\link[=trace]{trace()}} + +\code{\link[=trace]{trace()}} +} diff --git a/man/make_yearDOY.Rd b/man/make_yearDOY.Rd new file mode 100644 index 0000000..209bcc2 --- /dev/null +++ b/man/make_yearDOY.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities.R +\name{make_yearDOY} +\alias{make_yearDOY} +\title{Create unique DOY + year identifier} +\usage{ +make_yearDOY(years, DOYs) +} +\arguments{ +\item{years}{int vector.} + +\item{DOYs}{int vector of same length as *years*.} +} +\value{ +int vector of same length as *years* containing numbers of the + form YYYYDDD, where the first four digits represent a year and the last + four represent a DOY. +} +\description{ +Return numbers of the form YYYYDDD where YYYY is the year and DDD the DOY. +}