diff --git a/.Rbuildignore b/.Rbuildignore index b82695cf..e150a797 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -9,3 +9,4 @@ ^.*\.Rproj$ ^\.Rproj\.user$ ^data-raw$ +^\.idea$ diff --git a/R/generate_rabimo_area.R b/R/generate_rabimo_area.R index 80898d40..1c306ee1 100644 --- a/R/generate_rabimo_area.R +++ b/R/generate_rabimo_area.R @@ -4,11 +4,13 @@ #' #' All default values can be overridden by entering new key-value pairs. #' -#' @param code identifier of area +#' @param code vector of unique area identifiers. If NULL, default codes are +#' created: area_1, area_2, ... +#' @param n number of areas to be created. Default: length of code vector (1 if code is NULL) #' @param \dots key = value pairs overriding the default column values #' @param column_info data frame as returned by \code{\link{read_column_info}} #' @export -generate_rabimo_area <- function(code, ..., column_info = read_column_info()) +generate_rabimo_area <- function(code = NULL, ..., column_info = read_column_info()) { #kwb.utils::assignPackageObjects("kwb.rabimo");column_info=read_column_info();`%>%`<-magrittr::`%>%` @@ -39,7 +41,12 @@ generate_rabimo_area <- function(code, ..., column_info = read_column_info()) result <- do.call(data.frame, args) - # Add column "code" - result["code"] <- code + # Overwrite column "code" + result["code"] <- if (is.null(code)) { + paste0("area_", seq_len(nrow(result))) + } else { + code + } + result } diff --git a/R/reconfigure.R b/R/reconfigure.R new file mode 100644 index 00000000..01d0dffc --- /dev/null +++ b/R/reconfigure.R @@ -0,0 +1,63 @@ +if (FALSE) +{ + config_2020 <- kwb.rabimo::rabimo_inputs_2020$config + config_2025 <- kwb.rabimo::rabimo_inputs_2025$config + + str(config_2020) + str(kwb.rabimo:::reconfigure(config = config_2020)) + + str(config_2025) + str(kwb.rabimo:::reconfigure(config = config_2025)) + + config$green_roof <- list( + list(roof_fraction_column = "green_roof", bagrov_value = 0.5) + ) + + config$green_roof <- list( + list(roof_fraction_column = "green_roof_ext", bagrov_value = 0.5), + list(roof_fraction_column = "green_roof_int", bagrov_value = 0.5) + ) +} + +# reconfigure ------------------------------------------------------------------ +reconfigure <- function(config) +{ + #config <- config_2020 + + # Provide vector of Bagrov values + bagrov_values <- config$bagrov_values + + # Remove element "green_roof" from vector of Bagrov values + config$bagrov_values <- bagrov_values[names(bagrov_values) != "green_roof"] + + # Provide evaporation factor for infiltration method + evaporation_factor <- config$swale[["swale_evaporation_factor"]] + + # Remove config$swale + config$swale <- NULL + + config$measures <- list( + green_roof = list( + list( + roof_fraction_column = "green_roof", + bagrov_value = bagrov_values[["green_roof"]] + ) + ), + infiltration = list( + list( + area_fraction_column = "to_swale", + evaporation_factor = evaporation_factor + #, overflow_rate = 0 # not yet supported! + ) + # , list( + # name = "rigole", + # area_fraction_column = "to_inf_rigole", + # evaporation_factor = 0, + # overflow_rate = 0 + # #, rigole_specific_factor = 1 + # ) + ) + ) + + config +} diff --git a/R/run_rabimo.R b/R/run_rabimo.R index fca94ac6..a43aed5d 100644 --- a/R/run_rabimo.R +++ b/R/run_rabimo.R @@ -36,7 +36,7 @@ #' #' plot(results_2025[, -1L]) run_rabimo <- function( - data, config, controls = define_controls(), silent = FALSE + data, config, controls = define_controls(), silent = FALSE ) { # Provide functions and variables for debugging @@ -44,41 +44,58 @@ run_rabimo <- function( if (FALSE) { kwb.utils::assignPackageObjects("kwb.rabimo") - data <- kwb.rabimo::rabimo_inputs_2025$data - config <- kwb.rabimo::rabimo_inputs_2025$config + data <- kwb.utils::removeColumns(kwb.rabimo::rabimo_inputs_2025$data, "to_swale") + config <- reconfigure(kwb.rabimo::rabimo_inputs_2025$config) + config$measures$green_roof[[2]] <- list( + roof_fraction_column = "green_roof_int", + bagrov_value = 0.7 + ) + config$measures$infiltration[[1]]$overflow_factor <- 0.2 + config$measures$infiltration[[2]] <- list( + area_fraction_column = "to_swale_2", + evaporation_factor = 0.2, + overflow_factor = 0.15 + ) + #str(config$measures$infiltration) controls <- define_controls() + silent <- FALSE `%>%` <- magrittr::`%>%` } - + data <- remove_geo_column_if_required(data) # Save geometry data that may have stored in attribute "geometry" geometry <- attr(data, "geometry") - + + # if config is provided in old format, convert to new format + if (is.null(config$measures)) { + config <- reconfigure(config) + } + # If road-area-specific columns are missing, create them - data <- handle_missing_columns(data) - + data <- handle_missing_columns(data, silent = silent, measures = config$measures) + # Provide function to access the list of controls control <- create_accessor(controls) - + # Check whether data and config have the expected structures if (isTRUE(control("check"))) { - stop_on_invalid_data(data) stop_on_invalid_config(config) + stop_on_invalid_data(data, measures = config$measures) } - + # Get climate data climate <- cat_and_run( dbg = !silent, "Collecting climate related data", get_climate(data) ) - + # Create access functions to data columns and config elements fetch_data <- create_accessor(data) fetch_config <- create_accessor(config) fetch_climate <- create_accessor(climate) - + # Prepare soil properties for all rows. They are required to calculate the # actual evapotranspiration of unsealed areas. In the case of water bodies, # all values are 0.0. (hsonne: really?) @@ -94,12 +111,26 @@ run_rabimo <- function( dbg = FALSE ) ) - + # Precalculate actual evapotranspirations for impervious areas + # Here we expect the new config format (config$measures must exist!) + green_roof_columns <- sapply( + config$measures$green_roof, "[[", "roof_fraction_column" + ) + + # - Bagrov values are stored within config$measures$green_roof + bagrov_values <- c( + fetch_config("bagrov_values"), + stats::setNames( + sapply(config$measures$green_roof, "[[", "bagrov_value"), + green_roof_columns + ) + ) + evaporation_sealed <- cat_and_run( dbg = !silent, "Precalculating actual evapotranspirations for impervious areas", - expr = fetch_config("bagrov_values") %>% + expr = bagrov_values %>% lapply(function(x) { real_evapo_transpiration( potential_evaporation = fetch_climate("epot_yr"), @@ -110,7 +141,7 @@ run_rabimo <- function( }) %>% do.call(what = data.frame) ) - + # Precalculate actual evapotranspirations for waterbodies or pervious areas evaporation_unsealed <- cat_and_run( dbg = !silent, @@ -127,150 +158,175 @@ run_rabimo <- function( use_abimo_algorithm = control("use_abimo_bagrov_solver") ) ) - + runoff_all <- fetch_climate("prec_yr") - cbind( evaporation_sealed, unsealed = evaporation_unsealed ) - + # Runoff for all sealed areas (including roofs) - + # Calculate roof related variables - + # total runoff of roof areas # (total runoff, contains both surface runoff and infiltration components) runoff_roof <- select_columns(runoff_all, "roof") - runoff_green_roof <- select_columns(runoff_all, "green_roof") - + + # Selection of green-roof related columns (fractions of the roof area) + runoff_green_roof <- select_columns(runoff_all, green_roof_columns, drop = FALSE) + fractions_green_roof <- fetch_data(green_roof_columns, drop = FALSE) + # Provide runoff coefficients for impervious surfaces runoff_factors <- fetch_config("runoff_factors") - + # actual runoff from roof surface (area based, with no infiltration) - runoff_roof_actual <- with( - data, - main_frac * roof * (1 - green_roof) * swg_roof - ) * runoff_factors[["roof"]] * runoff_roof - + non_green_roof <- (1 - rowSums(fractions_green_roof)) + runoff_roof_actual <- with(data, main_frac * roof * swg_roof) * + non_green_roof * + runoff_factors[["roof"]] * + runoff_roof + # actual runoff from green roof surface (area based, with no infiltration) - runoff_green_roof_actual <- with( - data, - main_frac * roof * green_roof * swg_roof - ) * runoff_factors[["roof"]] * runoff_green_roof - + runoff_green_roof_actual <- with(data, main_frac * roof * swg_roof) * + fractions_green_roof * + runoff_factors[["roof"]] * + runoff_green_roof + # actual infiltration from roof surface (area based, with no runoff) - infiltration_roof_actual <- with( - data, main_frac * roof * (1-green_roof) * (1-swg_roof) - ) * runoff_roof - + infiltration_roof_actual <- with(data, main_frac * roof * (1 - swg_roof)) * + non_green_roof * + runoff_roof + # actual infiltration from green_roof surface (area based, with no runoff) - infiltration_green_roof_actual <- with( - data, - main_frac * roof * green_roof * (1-swg_roof) - ) * runoff_green_roof - + infiltration_green_roof_actual <- with(data, main_frac * roof * (1 - swg_roof)) * + fractions_green_roof * + runoff_green_roof + # Calculate runoff for all surface classes at once # (contains both surface runoff and infiltration components) - + # Identify active surface class columns in input data surface_cols_no_rd <- matching_names(data, pattern_no_roads()) surface_cols_rd <- matching_names(data, pattern_roads()) digits <- gsub("\\D", "", surface_cols_no_rd) - surface_class_names <- paste0("surface",digits) - + surface_class_names <- paste0("surface", digits) + # choose columns related to surface classes runoff_sealed <- select_columns(runoff_all, surface_class_names) # head(runoff_sealed) - + # Runoff from the actual partial areas that are sealed and connected # (road and non-road) areas (for all surface classes at once) - + runoff_factor_matrix <- expand_to_matrix( x = runoff_factors[surface_class_names], nrow = nrow(data) ) - + unbuilt_surface_fractions <- fetch_data(surface_cols_no_rd) road_surface_fractions <- fetch_data(surface_cols_rd) - + # add an empty column in road_surface_fraction to match dimension if needed if (!identical(length(surface_cols_no_rd), length(surface_cols_rd))) { road_surface_fractions$srf5_pvd_r <- 0 } - + runoff_sealed_actual <- runoff_sealed * ( with(data, main_frac * pvd * swg_pvd) * unbuilt_surface_fractions + with(data, road_frac * pvd_r * swg_pvd_r) * road_surface_fractions ) * runoff_factor_matrix - + # infiltration of sealed surfaces # (road and non-road) areas (for all surface classes at once) infiltration_sealed_actual <- runoff_sealed * ( with(data, main_frac * pvd) * unbuilt_surface_fractions + with(data, road_frac * pvd_r) * road_surface_fractions) - runoff_sealed_actual - + # Total Runoff of unsealed surfaces (unsealedSurface_RUV) - runoff_unsealed <- fetch_climate("prec_yr") - as.numeric(evaporation_unsealed) # why as.numeric()? - + # as.numeric() removes attribute "bagrovUnsealed" with intermediate values + runoff_unsealed <- fetch_climate("prec_yr") - as.numeric(evaporation_unsealed) + # Infiltration of road (unsealed areas) infiltration_unsealed_roads <- with(data, road_frac * (1 - pvd_r)) * runoff_sealed[, ncol(runoff_sealed)] # last (less sealed) surface class - + fraction_unsealed <- with( data, ifelse(control("reproduce_abimo_error"), 1, main_frac) * (1 - (roof + pvd)) ) - + infiltration_unsealed_surfaces <- fraction_unsealed * runoff_unsealed - + # Calculate runoff 'ROW' for entire block area (FLGES + STR_FLGES) (mm/a) - total_surface_runoff <- ( - runoff_roof_actual + runoff_green_roof_actual + - #orig.: runoff_unsealed_roads <- was set to zero in the master branch - rowSums(runoff_sealed_actual)) - + total_surface_runoff <- runoff_roof_actual + + rowSums(runoff_green_roof_actual) + + #orig.: runoff_unsealed_roads <- was set to zero in the master branch + rowSums(runoff_sealed_actual) + # Calculate infiltration rate 'RI' for entire block partial area (mm/a) - total_infiltration <- - (infiltration_roof_actual + - infiltration_green_roof_actual + - infiltration_unsealed_surfaces + - infiltration_unsealed_roads + - rowSums(infiltration_sealed_actual)) - + total_infiltration <- infiltration_roof_actual + + rowSums(infiltration_green_roof_actual) + + infiltration_unsealed_surfaces + + infiltration_unsealed_roads + + rowSums(infiltration_sealed_actual) + + # Here we expect the new config format! + # Provide information on the infiltration measure(s) + infiltration_configs <- config$measures$infiltration + + deltas <- lapply(infiltration_configs, function(pars) { + #pars <- infiltration_configs[[1L]] + # check for all required elements + pars <- select_elements(pars, c( + "area_fraction_column", + "evaporation_factor", + "overflow_factor" + )) + area_fraction_connected <- fetch_data(pars$area_fraction_column) + total_surface_runoff * (1 - pars$overflow_factor) * data.frame( + surface_runoff = area_fraction_connected * (-1), + infiltration = area_fraction_connected * (1 - pars$evaporation_factor) + ) + }) + + # name the entries according to the fraction columns, just for convenience + names(deltas) <- sapply(infiltration_configs, `[[`, "area_fraction_column") + + deltas_surface_runoff <- do.call(cbind, lapply(deltas, `[[`, "surface_runoff")) + deltas_infiltration <- do.call(cbind, lapply(deltas, `[[`, "infiltration")) + # Correct Surface Runoff and Infiltration if area has an infiltration swale - swale_delta <- total_surface_runoff * (fetch_data("to_swale")) - total_surface_runoff <- total_surface_runoff - swale_delta - total_infiltration <- total_infiltration + - swale_delta * (1 - fetch_config("swale")[["swale_evaporation_factor"]]) - + total_surface_runoff <- total_surface_runoff + rowSums(deltas_surface_runoff) + total_infiltration <- total_infiltration + rowSums(deltas_infiltration) + # Calculate "total system losses" 'R' due to runoff and infiltration # for entire block partial area total_runoff <- total_surface_runoff + total_infiltration - + # Calculate evaporation 'VERDUNST' by subtracting 'R', the sum of - # runoff and infiltration from precipitation of entire year, - # multiplied by precipitation correction factor + # runoff and infiltration from (corrected) precipitation of entire year total_evaporation <- climate[["prec_yr"]] - total_runoff - + # Provide total area for calculation of "flows" total_area <- fetch_data("total_area") - + # Calculate volume 'rowvol' from runoff (qcm/s) surface_runoff_flow <- yearly_height_to_volume_flow( total_surface_runoff, total_area ) - + # Calculate volume 'rivol' from infiltration rate (qcm/s) infiltration_flow <- yearly_height_to_volume_flow( total_infiltration, total_area ) - + # Calculate volume of "system losses" 'rvol' due to surface runoff and # infiltration total_runoff_flow <- surface_runoff_flow + infiltration_flow - + # Provide mapping between local variable names and ABIMO-output columns name_mapping <- list( code = "CODE", @@ -283,23 +339,23 @@ run_rabimo <- function( total_area = "FLAECHE", total_evaporation = "VERDUNSTUN" ) - + # Compose result data frame. Use mget() to get the result vectors from the # local environment and put them into the data frame result_data_raw <- cbind( fetch_data("code", drop = FALSE), mget(names(name_mapping)[-1L]) ) - + output_format <- control("output_format") - + result_data <- if (output_format == "abimo") { - + # Provide the same columns as Abimo does rename_columns(result_data_raw, name_mapping) - + } else if (output_format == "rabimo") { - + data.frame( code = result_data_raw$code, area = result_data_raw$total_area, @@ -307,23 +363,23 @@ run_rabimo <- function( infiltr = result_data_raw$total_infiltration, evapor = result_data_raw$total_evaporation ) - + } else { - + clean_stop("controls$output_format must be either 'abimo' or 'rabimo'.") } - + # Round all columns to three digits (skip first column: "code") result_data[-1L] <- lapply(result_data[-1L], round, 3L) - + result_data <- restore_geo_column_if_required( result_data, geometry = geometry ) - + if (isFALSE(control("intermediates"))) { return(result_data) } - + # Return intermediate results as attributes structure( result_data, @@ -356,25 +412,55 @@ run_rabimo <- function( } # handle_missing_columns ------------------------------------------------------- -handle_missing_columns <- function(data) +handle_missing_columns <- function(data, silent = FALSE, measures = NULL) { - road_specific_columns <- c( - "road_frac", "pvd_r", "swg_pvd_r", - "srf1_pvd_r", "srf2_pvd_r", "srf3_pvd_r", "srf4_pvd_r" - ) - - missing_road_columns <- setdiff(road_specific_columns, names(data)) - - if (length(missing_road_columns)) { - for (column in missing_road_columns) { - data[[column]] <- 0 + init_column <- function(data, column, default) { + if (!silent) { + message(sprintf("Initialising new column '%s' with %0.1f", column, default)) } + data[[column]] <- default + data } - - if (! "main_frac" %in% names(data)) { - data$main_frac <- 1 + + defaults <- list( + # road_specific_columns + road_frac = 0, + pvd_r = 0, + swg_pvd_r = 0, + srf1_pvd_r = 0, + srf2_pvd_r = 0, + srf3_pvd_r = 0, + srf4_pvd_r = 0, + # (non-road) fraction + main_frac = 1 + ) + + for (column in names(defaults)) { + if (!column %in% names(data)) { + data <- init_column(data, column, defaults[[column]]) + } } - + + # measures + # $green_roof + # [[1]] + # $roof_fraction_column = "green_roof_ext" + # [[2]] + # $roof_fraction_column = "green_roof_int" + # $infiltration + # [[1]] + # $area_fraction_column = "to_swale" + + if (!is.null(measures)) { + columns_green_roof <- sapply(measures$green_roof, "[[", "roof_fraction_column") + columns_infiltration <- sapply(measures$infiltration, "[[", "area_fraction_column") + for (column in c(columns_green_roof, columns_infiltration)) { + if (! column %in% names(data)) { + data <- init_column(data, column, 0) + } + } + } + data } @@ -382,9 +468,9 @@ handle_missing_columns <- function(data) get_climate <- function(input) { climate <- select_columns(input, c("prec_yr", "prec_s", "epot_yr", "epot_s")) - + climate[["x_ratio"]] <- climate[["prec_yr"]] / climate[["epot_yr"]] - + climate } diff --git a/R/stop_on_invalid_config.R b/R/stop_on_invalid_config.R index 533b37bf..336eac2f 100644 --- a/R/stop_on_invalid_config.R +++ b/R/stop_on_invalid_config.R @@ -15,10 +15,38 @@ stop_on_invalid_config <- function(config) bagrov_values <- select_elements(config, "bagrov_values") runoff_factors <- select_elements(config, "runoff_factors") - x <- config$bagrov_values - check_values_for_surface_types(x) - - x <- config$runoff_factors - check_values_for_surface_types(x) + check_values_for_surface_types(x = bagrov_values) + check_values_for_surface_types(x = runoff_factors) + + if (is_new_format <- !is.null(config$measures)) { + green_roof_configs <- select_elements(config$measures, "green_roof") + infiltration_configs <- select_elements(config$measures, "infiltration") + columns_green_roof <- sapply( + green_roof_configs, + FUN = select_elements, + elements = "roof_fraction_column" + ) + columns_infiltration <- sapply( + infiltration_configs, + FUN = select_elements, + elements = "area_fraction_column" + ) + if (length(columns_green_roof) != length(unique(columns_green_roof))) { + kwb.utils::stopFormatted( + "The s in config$measures$green_roof (%s) are not unique as expected.", + kwb.utils::stringList(columns_green_roof) + ) + } + if (length(columns_infiltration) != length(unique(columns_infiltration))) { + kwb.utils::stopFormatted( + "The s in config$measures$infiltration (%s) are not unique as expected.", + kwb.utils::stringList(columns_infiltration) + ) + } + + } else { + stopifnot("green_roof" %in% names(bagrov_values)) + } + } diff --git a/R/stop_on_invalid_data.R b/R/stop_on_invalid_data.R index 7de7ce08..046be049 100644 --- a/R/stop_on_invalid_data.R +++ b/R/stop_on_invalid_data.R @@ -1,7 +1,7 @@ # stop_on_invalid_data --------------------------------------------------------- #' @importFrom rlang .data #' @importFrom kwb.utils stopFormatted -stop_on_invalid_data <- function(data) +stop_on_invalid_data <- function(data, measures = NULL) { # Read information on column names and types column_info <- read_column_info() @@ -40,7 +40,7 @@ stop_on_invalid_data <- function(data) convert = FALSE ) - # Do not accept any NA + # Do not accept any NA in required columns of type numeric check_columns( data = data, columns = names(data) %>% @@ -81,6 +81,23 @@ stop_on_invalid_data <- function(data) if (length(columns <- matching_names(data, pattern_roads()))) { check_sum_up_to_1_or_0(data, columns) } + + # If measures are given, check that related fractions do not sum up to + # value above 1 + if (!is.null(measures)) { + columns_green_roof <- sapply( + select_elements(measures, "green_roof"), + FUN = select_elements, + "roof_fraction_column" + ) + columns_infiltration <- sapply( + select_elements(measures, "infiltration"), + FUN = select_elements, + "area_fraction_column" + ) + check_sum_is_below_1(data, columns_green_roof) + check_sum_is_below_1(data, columns_infiltration) + } } # get_expected_data_type ------------------------------------------------------- @@ -104,6 +121,20 @@ get_expected_data_type <- function(columns = NULL) type_info[intersect(names(type_info), columns)] } +# stop_on_non_numeric_columns -------------------------------------------------- +stop_on_non_numeric_columns <- function(data) +{ + is_numeric <- sapply(data, is.numeric) + + if (any(!is_numeric)) { + kwb.utils::stopFormatted( + "There are non-numeric columns in %s: %s", + deparse(substitute(data)), + kwb.utils::stringList(names(data)[!is_numeric]) + ) + } +} + # check_sum_up_to_1_or_0 ------------------------------------------------------- #' @importFrom kwb.utils stopFormatted stringList check_sum_up_to_1_or_0 <- function(data, columns, tolerance = 0.005) @@ -115,14 +146,7 @@ check_sum_up_to_1_or_0 <- function(data, columns, tolerance = 0.005) column_data <- select_columns(data, columns, drop = FALSE) - # Check for non-numeric columns - is_numeric <- sapply(column_data, is.numeric) - if (any(!is_numeric)) { - clean_stop( - "There are non-numeric columns in check_sum_up_to_1_or_0(): ", - kwb.utils::stringList(columns[!is_numeric]) - ) - } + stop_on_non_numeric_columns(column_data) sums <- rowSums(column_data) ok <- equals(sums, 0) | equals(sums, 1) @@ -142,3 +166,34 @@ check_sum_up_to_1_or_0 <- function(data, columns, tolerance = 0.005) "(see above). The tolerance was: %f" )) } + +# check_sum_is_below_1 ------------------------------------------------------- +check_sum_is_below_1 <- function(data, columns) +{ + select_columns <- kwb.utils::selectColumns + + column_data <- select_columns(data, columns, drop = FALSE) + + stop_on_non_numeric_columns(column_data) + + sums <- rowSums(column_data) + ok <- sums < 1 + + if (all(ok)) { + return() + } + + cat("(First) invalid rows:\n") + + select_columns(data, c("code", columns))[!ok, ] %>% + utils::head() %>% + print() + + kwb.utils::stopFormatted( + paste( + "The sum of columns %s is not less than or equal to 1 in each row", + "as expected (see above)." + ), + kwb.utils::stringList(columns) + ) +} diff --git a/inst/extdata/column-names.csv b/inst/extdata/column-names.csv index 6c86f4cb..c0a438ca 100644 --- a/inst/extdata/column-names.csv +++ b/inst/extdata/column-names.csv @@ -8,7 +8,6 @@ district,BEZIRK,,Specific to Berlin: identifier of city district,-,,character,0 total_area,,,Total block area,m2,required,numeric,100 main_frac,,,Non-road fraction of total_area,0..1,,numeric,1.0 roof,PROBAU,x,Roof fraction of non-road built area,0..1,required,numeric,0.2 -green_roof,,,Green roof fraction of roof area,0..1,required,numeric,0.0 swg_roof,KAN_BEB,x,Fraction of roof area connected to the sewer ,0..1,required,numeric,1.0 pvd,PROVGU,x, Paved fraction of non-road area,0..1,required,numeric,0.6 swg_pvd,KAN_VGU,x,Fraction of paved area connected to the sewer,0..1,required,numeric,0.7 @@ -24,7 +23,6 @@ srf1_pvd_r,STR_BELAG1,x,Fraction of road area belonging to surface class 1,0..1, srf2_pvd_r,STR_BELAG2,x,Fraction of road area belonging to surface class 2,0..1,,numeric,0.1 srf3_pvd_r,STR_BELAG3,x,Fraction of road area belonging to surface class 3,0..1,,numeric,0.0 srf4_pvd_r,STR_BELAG4,x,Fraction of road area belonging to surface class 4,0..1,,numeric,0.0 -to_swale,,,Fraction of total area connected to an infiltration swale,0..1,required,numeric,0.0 gw_dist,FLUR,,Depth to the water table,m,required,numeric,3.0 ufc30,FELD_30,,Usable field capacity 0..30 cm,% by volume,required,numeric,13.0 ufc150,FELD_150,,Usable field capacity 0..150 cm,% by volume,required,numeric,13.0 diff --git a/man/generate_rabimo_area.Rd b/man/generate_rabimo_area.Rd index c378d581..26e8679d 100644 --- a/man/generate_rabimo_area.Rd +++ b/man/generate_rabimo_area.Rd @@ -4,14 +4,17 @@ \alias{generate_rabimo_area} \title{Generate an area in R-Abimo format with default values} \usage{ -generate_rabimo_area(code, ..., column_info = read_column_info()) +generate_rabimo_area(code = NULL, ..., column_info = read_column_info()) } \arguments{ -\item{code}{identifier of area} +\item{code}{vector of unique area identifiers. If NULL, default codes are +created: area_1, area_2, ...} \item{\dots}{key = value pairs overriding the default column values} \item{column_info}{data frame as returned by \code{\link{read_column_info}}} + +\item{n}{number of areas to be created. Default: length of code vector (1 if code is NULL)} } \description{ All default values can be overridden by entering new key-value pairs. diff --git a/tests/testthat/test-function-generate_rabimo_area.R b/tests/testthat/test-function-generate_rabimo_area.R index 41f30c23..d7443d5a 100644 --- a/tests/testthat/test-function-generate_rabimo_area.R +++ b/tests/testthat/test-function-generate_rabimo_area.R @@ -4,12 +4,13 @@ test_that("generate_rabimo_area() works", { f <- kwb.rabimo::generate_rabimo_area - expect_error(f()) + expect_no_error(data <- f()) - expect_no_error(expect_output(kwb.rabimo::run_rabimo( - data = f(code = "a_code"), - config = kwb.rabimo::rabimo_inputs_2020$config, + expect_no_error(kwb.rabimo::run_rabimo( + silent = TRUE, + data = data, + config = kwb.rabimo::rabimo_inputs_2025$config, controls = kwb.rabimo::define_controls() - ))) + )) }) diff --git a/tests/testthat/test-function-handle_missing_columns.R b/tests/testthat/test-function-handle_missing_columns.R index 608a920a..6120c6fc 100644 --- a/tests/testthat/test-function-handle_missing_columns.R +++ b/tests/testthat/test-function-handle_missing_columns.R @@ -1,5 +1,18 @@ # library(testthat) test_that("handle_missing_columns() works", { - f <- kwb.rabimo:::handle_missing_columns - expect_error(f()) + + handle_missing <- kwb.rabimo:::handle_missing_columns + + expect_error(handle_missing()) + + area_with_missing <- kwb.utils::removeColumns( + kwb.rabimo::generate_rabimo_area("code"), + columns = c("main_frac") + ) + + expect_message(result_1 <- handle_missing(area_with_missing)) + expect_silent(result_2 <- handle_missing(area_with_missing, silent = TRUE)) + + expect_identical(result_1$main_frac, 1) + expect_identical(result_2$main_frac, 1) }) diff --git a/tests/testthat/test-function-run_rabimo.R b/tests/testthat/test-function-run_rabimo.R index 2fa190e5..d18a5bfb 100644 --- a/tests/testthat/test-function-run_rabimo.R +++ b/tests/testthat/test-function-run_rabimo.R @@ -11,12 +11,12 @@ test_that("run_rabimo() reproduces previous results", { test_that("run_rabimo() works", { - f <- kwb.rabimo::run_rabimo + run <- kwb.rabimo::run_rabimo - expect_error(f()) + expect_error(run()) data <- data.frame( - code = "a", + code = "area_1", land_type = "a", prec_yr = 100L, prec_s = 100L, @@ -74,10 +74,10 @@ test_that("run_rabimo() works", { ) expect_output( - result_1 <- f(data, config, controls = define_controls()) + result_1 <- run(data, config, controls = define_controls()) ) expect_silent( - result_2 <- f(data, config, controls = define_controls(), silent = TRUE) + result_2 <- run(data, config, controls = define_controls(), silent = TRUE) ) expect_s3_class(result_1, "data.frame") @@ -96,20 +96,33 @@ test_that("run_rabimo() keeps geometry if data inherits from 'sf'", { inputs <- kwb.rabimo::rabimo_inputs_2025 data <- inputs$data[sample(nrow(inputs$data), 10L), ] expect_true("sf" %in% class(data)) - expect_output(result <- kwb.rabimo::run_rabimo(data, config = inputs$config)) + expect_output(suppressMessages( + result <- kwb.rabimo::run_rabimo(data, config = inputs$config) + )) expect_true("sf" %in% class(result)) }) test_that("Full connection to swales results in zero runoff", { generate <- kwb.rabimo::generate_rabimo_area data <- rbind( - generate("area_0"), - generate("all_swale", to_swale = 1), + generate("area_0", green_roof = 0, to_swale = 0), + generate("all_swale", green_roof = 0, to_swale = 1), generate("all_swale_plus_green_roof", green_roof = 1, to_swale = 1), - generate("all_swale_plus_green_roof", pvd = 0, to_swale = 1), - kwb.rabimo::generate_rabimo_area("all_swale_plus_both", pvd = 0, green_roof = 1, to_swale = 1) + generate("all_swale_plus_green_roof", green_roof = 0, to_swale = 1, pvd = 0), + generate("all_swale_plus_both", green_roof = 1, to_swale = 1, pvd = 0) ) config <- kwb.rabimo::rabimo_inputs_2025$config result <- kwb.rabimo::run_rabimo(data, config, silent = TRUE) expect_true(all(result$runoff[startsWith(result$code, "all_swale")] == 0)) }) + +test_that("Abimo can simulate intensive green roofs", { + # generate <- kwb.rabimo::generate_rabimo_area + # data <- rbind( + # generate("area_0"), + # generate("area_1") + # ) + # config <- kwb.rabimo::rabimo_inputs_2025$config + # result <- kwb.rabimo::run_rabimo(data, config, silent = TRUE) + # expect_true(all(result$runoff[startsWith(result$code, "all_swale")] == 0)) +}) diff --git a/tests/testthat/test-function-run_rabimo_with_measures.R b/tests/testthat/test-function-run_rabimo_with_measures.R index 0aaac858..5e744035 100644 --- a/tests/testthat/test-function-run_rabimo_with_measures.R +++ b/tests/testthat/test-function-run_rabimo_with_measures.R @@ -109,11 +109,12 @@ test_that("run_rabimo_with_measures(old_version = TRUE) works", { expect_error(RUN()) sample_size <- 100L + seeds <- sample(1e10, 5) - for (seed in sample(1e10, 5)) { + for (seed in seeds) { #seed <- seeds[1L] - writeLines(paste("seed:", seed)) + #writeLines(paste("seed:", seed)) DATASETS <- lapply( X = list( diff --git a/tests/testthat/test-function-stop_on_invalid_config.R b/tests/testthat/test-function-stop_on_invalid_config.R index 113041f4..42762dee 100644 --- a/tests/testthat/test-function-stop_on_invalid_config.R +++ b/tests/testthat/test-function-stop_on_invalid_config.R @@ -1,17 +1,57 @@ -# -# This file was generated by kwb.test::create_test_files(), -# launched by hsonne on 2024-03-07 19:06:24.082509. -# Please modify the dummy functions so that real cases are -# tested. Then, delete this comment. -# - +#library(testthat) test_that("stop_on_invalid_config() works", { - + f <- kwb.rabimo:::stop_on_invalid_config - + + expect_error(f()) + expect_error(f(list())) + + base_config <- list( + bagrov_values = c( + roof = 1, + surface1 = 1, + surface2 = 1, + surface3 = 1, + surface4 = 1, + surface5 = 1 + ), + runoff_factors = c( + roof = 1, + surface1 = 1, + surface2 = 1, + surface3 = 1, + surface4 = 1, + surface5 = 1 + ) + ) + + expect_error( + f(c(base_config, list( + measures = list( + green_roof = list( + list(roof_fraction_column = "column-1"), + list(roof_fraction_column = "column-1") + ), + infiltration = list() + ) + ))), + "roof_fraction_column.*are not unique as expected" + ) + expect_error( - f() - # Argument "config" fehlt (ohne Standardwert) + f(c(base_config, list( + measures = list( + green_roof = list( + list(roof_fraction_column = "column-1"), + list(roof_fraction_column = "column-2") + ), + infiltration = list( + list(area_fraction_column = "column-1"), + list(area_fraction_column = "column-1") + ) + ) + ))), + "area_fraction_column.*are not unique" ) - + }) diff --git a/vignettes/tutorial.Rmd b/vignettes/tutorial.Rmd index 02c06f78..f88dd71f 100644 --- a/vignettes/tutorial.Rmd +++ b/vignettes/tutorial.Rmd @@ -210,7 +210,10 @@ art_blocks <- kwb.rabimo::generate_rabimo_area( art_blocks # Run R-Abimo on the block areas -art_water_balance <- kwb.rabimo::run_rabimo(art_blocks, config = abimo_inputs$config) +art_water_balance <- kwb.rabimo::run_rabimo( + data = art_blocks, + config = abimo_inputs$config +) # How does the roof area influence the runoff? plot(art_blocks$roof, art_water_balance$runoff)