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{