Define Paths and Scenarios
library(kwb.raindrop)
path_list <- list(
root_path = "C:/kwb/projects/raindrop/2025-12-19_Raindrop_Daten",
dir_base = "<root_path>/Optimierungsfall",
dir_exe = "<root_path>/Berechnungskern",
dir_input = "<root_path>/Optimierungsfall/models/120min_40mm/input",
dir_output = "<root_path>/Optimierungsfall/models/120min_40mm/output",
dir_target_output = "<dir_output>/<dir_target>",
file_base = "120min_40mm.h5",
file_errors_hdf5 = "Fehlerprotokoll.h5",
file_exe = "Regenwasserbewirtschaftung.exe",
file_results_hdf5_element = "Mulde_Rigole.h5",
file_results_hdf5_flaeche = "Dach.h5",
file_results_hdf5_verschaltungen = "<dir_target>_Verschaltungen.h5",
file_results_txt = "Mulde_Rigole_RAINDROP.txt",
file_results_txt_multilayer = "Mulde_Rigole_RAINDROP_multi_layer.txt",
file_target = "<dir_target>.h5",
path_base = "<dir_base>/<file_base>",
path_exe = "<dir_exe>/<file_exe>",
path_errors_hdf5 = "<dir_target_output>/<file_errors_hdf5>",
path_results_hdf5_element = "<dir_target_output>/<file_results_hdf5_element>",
path_results_hdf5_flaeche = "<dir_target_output>/<file_results_hdf5_flaeche>",
path_results_hdf5_verschaltungen = "<dir_target_output>/<file_results_hdf5_verschaltungen>",
path_results_txt = "<dir_target_output>/<file_results_txt>",
path_results_txt_multilayer = "<dir_target_output>/<file_results_txt_multilayer>",
path_target_input = "<dir_input>/<file_target>"
)
parameters <- tibble::tibble(
para_nama_short = c(
"connected_area",
"mulde_area",
"mulde_height",
"filter_hydraulicconductivity",
# "filter_height",
"storage_height"#,
# "bottom_hydraulicconductivity"
),
para_name_long = c(
"/Massnahmenelemente/Dach/Allgemein/Flaeche",
"/Massnahmenelemente/Mulde_Rigole/Allgemein/Flaeche",
"/Massnahmenelemente/Mulde_Rigole/Eigenschaften_Oberflaeche/Ueberlaufhoehe",
"Bodenarten/Bodenfilter/Ks_HydraulicConductivity",
#"/Massnahmenelemente/Mulde_Rigole/Bodenschichtung/Schichtdicken",
"/Massnahmenelemente/Mulde_Rigole/Bodenschichtung/Schichtdicken"#,
# "/Massnahmenelemente/Mulde_Rigole/Allgemein/Endversickerungsrate"
),
index = c(1L,
1L,
1L,
1L,
# 1L,
2#,
#1L
)
)
DT::datatable(parameters)
connected_area <- 1000
mulde_area <- c(25, 50, 100, 200)
mulde_height <- c(100, 200, 300)
filter_hydraulicconductivity <- c(36, 180, 360)
filter_height <- 300
storage_height <- seq(100, 500, 1000)
rain_factor <- 1
bottom_hydraulicconductivity <- 12 #c(1,5,10,20,45,90,180,270,360,1860,3600)
# Alle Kombinationen erzeugen
param_grid_all_combinations <- expand.grid(
connected_area = connected_area,
mulde_area = mulde_area,
mulde_height = mulde_height,
filter_hydraulicconductivity = filter_hydraulicconductivity,
filter_height = filter_height,
storage_height = storage_height,
bottom_hydraulicconductivity = bottom_hydraulicconductivity,
rain_factor = rain_factor
)
param_grid_all_combinations <- param_grid_all_combinations %>%
dplyr::bind_cols(tibble::tibble(scenario_name = sprintf("s%05d",
seq_len(nrow(param_grid_all_combinations)))))
ref_scenario <- param_grid_all_combinations %>%
dplyr::filter(connected_area == min(unique(param_grid_all_combinations$connected_area)),
mulde_area == min(unique(param_grid_all_combinations$mulde_area)),
filter_height == min(filter_height),
filter_hydraulicconductivity == min(param_grid_all_combinations$filter_hydraulicconductivity),
bottom_hydraulicconductivity == min(unique(param_grid_all_combinations$bottom_hydraulicconductivity)),
mulde_height == min(param_grid_all_combinations$mulde_height),
storage_height == min(param_grid_all_combinations$storage_height)) %>%
dplyr::pull(scenario_name)
stopifnot(length(ref_scenario)==1)
scenarios_with_single_parameter_variation <- kwb.raindrop::find_single_param_variations(
data = param_grid_all_combinations,
ref_scenario = ref_scenario
) %>%
dplyr::pull(scenario_name) %>% unique()
#> Rows with exactly one differing parameter: 7 of 36
#> Single-parameter variations per parameter: connected_area=0, mulde_area=3, mulde_height=2, filter_hydraulicconductivity=2, filter_height=0, storage_height=0, bottom_hydraulicconductivity=0, rain_factor=0
param_grid <- param_grid_all_combinations %>%
dplyr::filter(scenario_name %in% scenarios_with_single_parameter_variation)
DT::datatable(param_grid)
htmlwidgets::saveWidget(DT::datatable(parameters), "parameters.html")
htmlwidgets::saveWidget(DT::datatable(param_grid), "param_grid.html")
psi_s_mm <- function(kf_mmh) (3.237 * (kf_mmh/25.4)^(-0.328)) * 25.4Run Model
# Number of cores for parallel processing (or: automatic)
#future::plan(future::multisession, workers = parallel::detectCores() - 1)
lapply(seq_len(nrow(param_grid)), function(i) {
param_grid_tmp <- param_grid[i, ]
paths <- kwb.utils::resolve(path_list, dir_target = param_grid_tmp$scenario_name)
fs::dir_create(paths$dir_input, recurse = TRUE)
fs::dir_create(paths$dir_output, recurse = TRUE)
fs::dir_create(paths$dir_target_output, recurse = TRUE)
fs::file_copy(path = paths$path_base,
new_path = paths$path_target_input,
overwrite = TRUE)
# "a" = read/write (legt an, falls nicht da); alternativ "r+" = read/write, aber nicht neu anlegen
h5 <- hdf5r::H5File$new(paths$path_target_input, mode = "a")
new_path <- stringr::str_c(normalizePath(fs::path_abs(paths$dir_target_output)),
"\\")
# 2) Alle Werte lesen (als named list, Keys = absolute Pfade)
vals <- kwb.raindrop::h5_read_values(h5)
vals$`//Berechnungsparameter/Ergebnispfad` <- new_path
vals$`//Berechnungsparameter/R-Plots` <- 0
vals$`//Berechnungsparameter/Evapotranspiration_aktiv` <- 1
vals$`//Massnahmenelemente/Dach/Berechnungsparameter/Evapotranspiration_aktiv`
vals$`//Massnahmenelemente/Mulde_Rigole/Berechnungsparameter/Evapotranspiration_aktiv` <- 1
vals$`//Massnahmenelemente/Dach/Allgemein/Flaeche` <- param_grid_tmp$connected_area
vals$`//Massnahmenelemente/Mulde_Rigole/Allgemein/Flaeche` <- param_grid_tmp$mulde_area
vals$`//Massnahmenelemente/Mulde_Rigole/Eigenschaften_Oberflaeche/Ueberlaufhoehe` <- param_grid_tmp$mulde_height
vals$`//Bodenarten/Bodenfilter/Ks_HydraulicConductivity` <- param_grid_tmp$filter_hydraulicconductivity
vals$`//Bodenarten/Bodenfilter/Psi_Saugspannung_CapillarySuction` <- psi_s_mm(param_grid_tmp$filter_hydraulicconductivity)
#vals$`//Massnahmenelemente/Mulde_Rigole/Bodenschichtung/Startwerte_theta_ActualSoilMoisture` <- c(0.3, 0)
vals$`//Massnahmenelemente/Mulde_Rigole/Bodenschichtung/Schichtdicken` <- c(param_grid_tmp$filter_height,
param_grid_tmp$storage_height)
vals$`//Massnahmenelemente/Mulde_Rigole/Allgemein/Endversickerungsrate` <- param_grid_tmp$bottom_hydraulicconductivity
# Timeseries (2×N) als tibble?
if (is.data.frame(vals[["//Kurven/Regen"]])) {
vals[["//Kurven/Regen"]]$value <- vals[["//Kurven/Regen"]]$value * param_grid_tmp$rain_factor
}
kwb.raindrop::h5_write_values(h5, vals, resize = TRUE, scalar_strategy = "error", verbose = FALSE)
h5$close_all()
kwb.raindrop::run_model(path_exe = paths$path_exe,
path_input = paths$path_target_input)
})
### Read results for first run
paths <- kwb.utils::resolve(path_list, dir_target = sprintf("s%05d", i = 1))
#simulation_names <- basename(fs::dir_ls(paths$dir_output))
simulation_names <- scenarios_with_single_parameter_variation
debug <- TRUE
errors_df <- lapply(simulation_names, function(s_name) {
s_id <- s_name %>% stringr::str_remove("s") %>% as.integer()
paths <- kwb.utils::resolve(path_list, dir_target = s_name)
if(fs::file_exists(paths$path_errors_hdf5)) {
kwb.utils::catAndRun(messageText = sprintf("Reading error file '%s'",
paths$path_errors_hdf5),
expr = {
error_hdf <- hdf5r::H5File$new(paths$path_errors_hdf5, mode = "r")
tibble::tibble(id = s_id,
path = paths$path_errors_hdf5,
number_of_errors = error_hdf[["AnzahlFehler"]]$read()
)
},
dbg = debug)
}
}) %>%
dplyr::bind_rows()Analyse Results
import_results_from_rds <- FALSE
debug <- TRUE
paths <- kwb.utils::resolve(path_list, dir_target = sprintf("s%05d", i = 1))
#simulation_names <- basename(fs::dir_ls(paths$dir_output))
simulation_names <- scenarios_with_single_parameter_variation
simulation_results <- if(import_results_from_rds == FALSE) {
message(sprintf("Reading results files ('%s') for %d model runs",
paste0(c(paths$file_results_hdf5_element, paths$file_results_hdf5_flaeche,
stringr::str_replace(paths$file_results_hdf5_verschaltungen,
"[0-9]+", "xxxxx")), collapse = "|"),
length(simulation_names)))
stats::setNames(lapply(simulation_names, function(s_name) {
s_id <- s_name %>% stringr::str_remove("s") %>% as.integer()
paths <- kwb.utils::resolve(path_list, dir_target = s_name)
if(all(file.exists(c(paths$path_results_hdf5_verschaltungen,
paths$path_results_hdf5_element,
paths$path_results_hdf5_flaeche)))) {
kwb.utils::catAndRun(messageText = sprintf("(%d/%d)) Reading results files for model run %s",
which(simulation_names == s_name),
length(simulation_names),
paths$dir_target_output),
expr = {
# "a" = read/write (legt an, falls nicht da); alternativ "r+" = read/write, aber nicht neu anlegen
res_hdf5_element <- hdf5r::H5File$new(paths$path_results_hdf5_element, mode = "r")
res_hdf5_flaeche <- hdf5r::H5File$new(paths$path_results_hdf5_flaeche, mode = "r")
res_hdf5_verschaltungen <- hdf5r::H5File$new(paths$path_results_hdf5_verschaltungen, mode = "r")
hdf5_results <- list(
element = list(
meta = kwb.raindrop::read_hdf5_scalars(res_hdf5_element[["Metainfo"]], numeric_only = FALSE),
rates = kwb.raindrop::read_hdf5_timeseries(res_hdf5_element[["Raten"]]),
water_balance = kwb.raindrop::read_hdf5_scalars(res_hdf5_element[["Wasserbilanz"]]),
additional_evapotranspiration = kwb.raindrop::read_hdf5_timeseries(res_hdf5_element[["Zusaetzliche Variablen Evapotranspiration"]]),
additional_infiltration = kwb.raindrop::read_hdf5_timeseries(res_hdf5_element[["Zusaetzliche Variablen Infiltration"]]),
states = kwb.raindrop::read_hdf5_timeseries(res_hdf5_element[["Zustandsvariablen"]])),
connected_area = list(
meta = kwb.raindrop::read_hdf5_scalars(res_hdf5_flaeche[["Metainfo"]], numeric_only = FALSE),
rates = kwb.raindrop::read_hdf5_timeseries(res_hdf5_flaeche[["Raten"]]),
water_balance = kwb.raindrop::read_hdf5_scalars(res_hdf5_flaeche[["Wasserbilanz"]]),
additional_evapotranspiration = kwb.raindrop::read_hdf5_timeseries(res_hdf5_flaeche[["Zusaetzliche Variablen Evapotranspiration"]]),
additional_infiltration = kwb.raindrop::read_hdf5_timeseries(res_hdf5_flaeche[["Zusaetzliche Variablen Infiltration"]]),
states = kwb.raindrop::read_hdf5_timeseries(res_hdf5_flaeche[["Zustandsvariablen"]])),
connections = kwb.raindrop::read_hdf5_connections(res_hdf5_verschaltungen)
)
hdf5_results
},
dbg = debug)}}), nm = simulation_names)
} else {
readRDS(file = "../simulation_results.Rds")
}
pdff <- "simulation_results_per_scenario_120min-40mm.pdf"
kwb.utils::preparePdf(pdff)
lapply(scenarios_with_single_parameter_variation, function(s_name) {
selected_scenario <- param_grid %>%
dplyr::filter(scenario_name == s_name)
simulation_results[[s_name]]$element$states %>%
dplyr::bind_rows(simulation_results[[s_name]]$element$rates) %>%
dplyr::filter(variable %in% c("h_pond", "fPI_InfiltrationRate", "f_Endversickerungsrate") | stringr::str_starts(variable, pattern = "fPI_InfiltrationRate_deeperLayers")) %>%
ggplot2::ggplot(ggplot2::aes(x = time, y = value)) +
ggplot2::geom_point() +
ggplot2::facet_wrap(~ variable, nrow = 3, ncol = 2, scales = "free_y") +
ggplot2::labs(title = sprintf("Scenario ID: %s",
s_name),
subtitle = sprintf("connected area: %.1f m2, mulde: area %.1f m2, height: %.1f mm; filter: kf: %.1f mm/h, height: %.1f mm;\nstorage_height: %.1f mm, bottom_kf: %.1f mm/h; rain_factor: %.2f",
selected_scenario$connected_area,
selected_scenario$mulde_area,
selected_scenario$mulde_height,
selected_scenario$filter_hydraulicconductivity,
selected_scenario$filter_height,
selected_scenario$storage_height,
selected_scenario$bottom_hydraulicconductivity,
selected_scenario$rain_factor)) +
ggplot2::theme_bw()
})
kwb.utils::finishAndShowPdf(pdff)
simulation_results_h_pond_list <- stats::setNames(lapply(names(simulation_results), function(s_name) {
simulation_results[[s_name]]$element$states %>%
dplyr::filter(variable == "h_pond") %>%
dplyr::summarise(h_pond_max = max(value),
h_pond_mean = mean(value))
}), names(simulation_results))
simulation_results_h_pond <- simulation_results_h_pond_list %>%
dplyr::bind_rows(, .id = "scenario_name") %>%
dplyr::left_join(param_grid,
by = "scenario_name")
### Plot results
pdff <- "simulation_results_h_pond_max_120min-40mm.pdf"
kwb.utils::preparePdf(pdff)
kwb.raindrop::plot_hpond_vs_ref(data = simulation_results_h_pond,
response = "h_pond_max",
ref_scenario = ref_scenario,
diff = "abs")
kwb.utils::finishAndShowPdf(pdff)
pdff <- "simulation_results_h_pond_mean_120min-40mm.pdf"
kwb.utils::preparePdf(pdff)
kwb.raindrop::plot_hpond_vs_ref(data = simulation_results_h_pond,
response = "h_pond_mean",
ref_scenario = ref_scenario,
diff = "abs")
kwb.utils::finishAndShowPdf(pdff)