1+ # ' Read Raindrop optimisation simulation results from HDF5
2+ # ' (parallel via future.apply + progress)
3+ # '
4+ # ' Parallel variant of \code{get_simulation_results_optim()} using
5+ # ' \code{future.apply::future_lapply()} including progress reporting
6+ # ' via \code{progressr}.
7+ # '
8+ # ' @inheritParams get_simulation_results_optim
9+ # ' @param workers Optional number of parallel workers. If not NULL,
10+ # ' a temporary \code{future::multisession} plan is set.
11+ # ' @param show_progress Logical (default TRUE).
12+ # ' @param future_seed Passed to \code{future.apply::future_lapply()}.
13+ # '
14+ # ' @return Named list (see \code{\link{get_simulation_results_optim}}).
15+ # '
16+ # ' @export
17+ # ' @importFrom stats setNames
18+ # ' @importFrom hdf5r H5File
19+ # ' @importFrom future plan multisession
20+ # ' @importFrom future.apply future_lapply
21+ # ' @importFrom progressr with_progress progressor handlers
22+ # ' @importFrom kwb.utils resolve
23+ get_simulation_results_optim_parallel <- function (paths ,
24+ path_list ,
25+ simulation_names ,
26+ debug = TRUE ,
27+ workers = NULL ,
28+ show_progress = TRUE ,
29+ future_seed = TRUE ) {
30+
31+ if (! requireNamespace(" future.apply" , quietly = TRUE )) {
32+ stop(" Package 'future.apply' is required." )
33+ }
34+ if (! requireNamespace(" future" , quietly = TRUE )) {
35+ stop(" Package 'future' is required." )
36+ }
37+
38+ message(sprintf(
39+ " Reading results files in parallel ('%s') for %d model runs%s" ,
40+ paste0(c(paths $ file_results_hdf5_element ,
41+ paths $ file_results_hdf5_flaeche ), collapse = " |" ),
42+ length(simulation_names ),
43+ if (! is.null(workers )) sprintf(" (workers=%d)" , workers ) else " "
44+ ))
45+
46+ # Optional temporary future plan
47+ old_plan <- NULL
48+ if (! is.null(workers )) {
49+ old_plan <- future :: plan()
50+ on.exit(future :: plan(old_plan ), add = TRUE )
51+ future :: plan(future :: multisession , workers = workers )
52+ }
53+
54+ n <- length(simulation_names )
55+
56+ # ---- progressr integration ----
57+ if (show_progress ) {
58+ if (! requireNamespace(" progressr" , quietly = TRUE )) {
59+ stop(" Package 'progressr' required for progress reporting." )
60+ }
61+ progressr :: handlers(global = TRUE )
62+ }
63+
64+ res_list <- if (show_progress ) {
65+
66+ progressr :: with_progress({
67+
68+ p <- progressr :: progressor(steps = n )
69+
70+ future.apply :: future_lapply(
71+ X = seq_along(simulation_names ),
72+ FUN = function (i ) {
73+
74+ s_name <- simulation_names [[i ]]
75+
76+ p(sprintf(" Reading %s (%d/%d)" , s_name , i , n ))
77+
78+ run_paths <- kwb.utils :: resolve(path_list , dir_target = s_name )
79+
80+ if (! all(file.exists(c(run_paths $ path_results_hdf5_element ,
81+ run_paths $ path_results_hdf5_flaeche )))) {
82+ if (isTRUE(debug )) {
83+ message(sprintf(" Missing files for %s -> returning NULL" , s_name ))
84+ }
85+ return (NULL )
86+ }
87+
88+ if (isTRUE(debug )) {
89+ message(sprintf(" Reading HDF5 for %s (%s)" ,
90+ s_name , run_paths $ dir_target_output ))
91+ }
92+
93+ res_hdf5_element <- hdf5r :: H5File $ new(
94+ run_paths $ path_results_hdf5_element , mode = " r"
95+ )
96+ res_hdf5_flaeche <- hdf5r :: H5File $ new(
97+ run_paths $ path_results_hdf5_flaeche , mode = " r"
98+ )
99+
100+ on.exit({
101+ try(res_hdf5_element $ close_all(), silent = TRUE )
102+ try(res_hdf5_flaeche $ close_all(), silent = TRUE )
103+ }, add = TRUE )
104+
105+ list (
106+ element = list (
107+ meta = read_hdf5_scalars(
108+ res_hdf5_element [[" Metainfo" ]], numeric_only = FALSE
109+ ),
110+ rates = read_hdf5_timeseries(
111+ res_hdf5_element [[" Raten" ]]
112+ ),
113+ water_balance = read_hdf5_scalars(
114+ res_hdf5_element [[" Wasserbilanz" ]]
115+ ),
116+ states = read_hdf5_timeseries(
117+ res_hdf5_element [[" Zustandsvariablen" ]]
118+ )
119+ ),
120+ connected_area = list (
121+ meta = read_hdf5_scalars(
122+ res_hdf5_flaeche [[" Metainfo" ]], numeric_only = FALSE
123+ ),
124+ rates = read_hdf5_timeseries(
125+ res_hdf5_flaeche [[" Raten" ]]
126+ ),
127+ water_balance = read_hdf5_scalars(
128+ res_hdf5_flaeche [[" Wasserbilanz" ]]
129+ ),
130+ states = read_hdf5_timeseries(
131+ res_hdf5_flaeche [[" Zustandsvariablen" ]]
132+ )
133+ )
134+ )
135+ },
136+ future.seed = future_seed
137+ )
138+ })
139+
140+ } else {
141+
142+ future.apply :: future_lapply(
143+ X = seq_along(simulation_names ),
144+ FUN = function (i ) {
145+ s_name <- simulation_names [[i ]]
146+
147+ run_paths <- kwb.utils :: resolve(path_list , dir_target = s_name )
148+
149+ if (! all(file.exists(c(run_paths $ path_results_hdf5_element ,
150+ run_paths $ path_results_hdf5_flaeche )))) {
151+ return (NULL )
152+ }
153+
154+ res_hdf5_element <- hdf5r :: H5File $ new(
155+ run_paths $ path_results_hdf5_element , mode = " r"
156+ )
157+ res_hdf5_flaeche <- hdf5r :: H5File $ new(
158+ run_paths $ path_results_hdf5_flaeche , mode = " r"
159+ )
160+
161+ on.exit({
162+ try(res_hdf5_element $ close_all(), silent = TRUE )
163+ try(res_hdf5_flaeche $ close_all(), silent = TRUE )
164+ }, add = TRUE )
165+
166+ list (
167+ element = list (
168+ meta = read_hdf5_scalars(res_hdf5_element [[" Metainfo" ]], numeric_only = FALSE ),
169+ rates = read_hdf5_timeseries(res_hdf5_element [[" Raten" ]]),
170+ water_balance = read_hdf5_scalars(res_hdf5_element [[" Wasserbilanz" ]]),
171+ states = read_hdf5_timeseries(res_hdf5_element [[" Zustandsvariablen" ]])
172+ ),
173+ connected_area = list (
174+ meta = read_hdf5_scalars(res_hdf5_flaeche [[" Metainfo" ]], numeric_only = FALSE ),
175+ rates = read_hdf5_timeseries(res_hdf5_flaeche [[" Raten" ]]),
176+ water_balance = read_hdf5_scalars(res_hdf5_flaeche [[" Wasserbilanz" ]]),
177+ states = read_hdf5_timeseries(res_hdf5_flaeche [[" Zustandsvariablen" ]])
178+ )
179+ )
180+ },
181+ future.seed = future_seed
182+ )
183+ }
184+
185+ stats :: setNames(res_list , simulation_names )
186+ }
0 commit comments