@@ -124,32 +124,18 @@ function run!(
124124 executor= ThreadedEx ()
125125) where {T<: Union{AbstractArray,AbstractDict} ,A}
126126
127- obj_parallelizable = all ([object_parallelizable (dep (obj)) for obj in collect (values (object))])
128- # Check if the simulation can be parallelized over objects:
129- if ! obj_parallelizable && executor != SequentialEx ()
130- is_obj_parallel = Set {AbstractModel} ()
131- for obj_par in [which_object_parallelizable (dep (obj)) for obj in collect (values (object))]
132- for mod in obj_par[findall (x -> x. second. second == false , obj_par)]
133- push! (is_obj_parallel, mod. second. first)
134- end
135- end
136-
137- mods_not_parallel = join (is_obj_parallel, " ; " )
138-
139- check && @warn string (
140- " A parallel executor was provided (`executor=$(executor) `) but some models cannot be run in parallel over objects: $mods_not_parallel . " ,
141- " The simulation will be run sequentially. Use `executor=SequentialEx()` to remove this warning."
127+ if executor != SequentialEx ()
128+ @warn string (
129+ " Parallelisation over objects was removed, (but may be reintroduced in the future). Parallelisation will only occur over timesteps."
142130 ) maxlog = 1
143- executor_obj = SequentialEx ()
144- else
145- executor_obj = executor
146131 end
147132
148- @floop executor_obj for obj in collect (values (object))
133+ for obj in collect (values (object))
149134 run! (obj, meteo, constants, extra, check= check, executor= executor)
150135 end
151136end
152137
138+
153139# 2- one object, one time-step
154140function run! (
155141 :: SingletonAlike ,
@@ -160,10 +146,11 @@ function run!(
160146 extra= nothing ;
161147 check= true
162148)
163- run! (object, dep (object), 1 , status (object, 1 ), meteo, constants, extra)
149+ run! (object, Weather[ meteo] , constants, extra; check )
164150end
165151
166152# 3- one object, one meteo time-step, several status time-steps (rare case but possible)
153+ # Also occurs when meteo is nothing
167154function run! (
168155 :: TableAlike ,
169156 :: SingletonAlike ,
@@ -199,11 +186,16 @@ function run!(
199186 # the variables the user provided for all time-steps.
200187 for (i, row) in enumerate (sim_rows)
201188 i > 1 && propagate_values! (sim_rows[i- 1 ], row, object. vars_not_propagated)
202- run! (object, dep_graph, i, row, meteo, constants, extra)
203- end
189+ roots = collect (dep_graph. roots)
190+ for (process, node) in roots
191+ run_node! (object, node, i, row, meteo, constants, extra)
192+ end end
204193 else
205194 @floop executor for (i, row) in enumerate (sim_rows)
206- run! (object, dep_graph, i, row, meteo, constants, extra)
195+ local roots = collect (dep_graph. roots)
196+ for (process, node) in roots
197+ run_node! (object, node, i, row, meteo, constants, extra)
198+ end
207199 end
208200 end
209201end
@@ -248,14 +240,21 @@ function run!(
248240 # Not parallelizable over time-steps, it means some values depend on the previous value.
249241 # In this case we propagate the values of the variables from one time-step to the other, except for
250242 # the variables the user provided for all time-steps.
243+ roots = collect (dep_graph. roots)
244+
251245 for (i, meteo_i) in enumerate (meteo_rows)
252246 i > 1 && propagate_values! (object[i- 1 ], object[i], object. vars_not_propagated)
253- run! (object, dep_graph, i, object[i], meteo_i, constants, extra)
247+ for (process, node) in roots
248+ run_node! (object, node, i, object[i], meteo_i, constants, extra)
249+ end
254250 end
255251 else
256252 # Computing time-steps in parallel:
257253 @floop executor for (i, meteo_i) in enumerate (meteo_rows)
258- run! (object, dep_graph, i, object[i], meteo_i, constants, extra)
254+ local roots = collect (dep_graph. roots)
255+ for (process, node) in roots
256+ run_node! (object, node, i, object[i], meteo_i, constants, extra)
257+ end
259258 end
260259 end
261260end
@@ -276,25 +275,14 @@ function run!(
276275 obj_parallelizable = all ([object_parallelizable (graph) for graph in dep_graphs])
277276
278277 # Check if the simulation can be parallelized over objects:
279- if ! obj_parallelizable && executor != SequentialEx ()
280- is_obj_parallel = Set {AbstractModel} ()
281- for graph in dep_graphs
282- obj_par = which_object_parallelizable (graph)
283- for mod in obj_par[findall (x -> x. second. second == false , obj_par)]
284- push! (is_obj_parallel, mod. second. first)
285- end
286- end
287-
288- mods_not_parallel = join (is_obj_parallel, " ; " )
289-
290- check && @warn string (
291- " A parallel executor was provided (`executor=$(executor) `) but some models cannot be run in parallel over objects: $mods_not_parallel . " ,
292- " The simulation will be run sequentially. Use `executor=SequentialEx()` to remove this warning."
278+ if executor != SequentialEx ()
279+ @warn string (
280+ " Parallelisation over objects was removed, (but may be reintroduced in the future). Parallelisation will only occur over timesteps."
293281 ) maxlog = 1
294- executor = SequentialEx ()
295282 end
296283 # Each object:
297- @floop executor for (i, obj) in enumerate (collect (values (object)))
284+ for (i, obj) in enumerate (collect (values (object)))
285+
298286 if check
299287 # Check if the meteo data and the status have the same length (or length 1)
300288 check_dimensions (obj, meteo)
@@ -307,13 +295,49 @@ function run!(
307295 end
308296 end
309297
310- run! (obj, dep_graphs[i], 1 , status (obj)[1 ], meteo, constants, extra)
298+ roots_i = collect (dep_graphs[i]. roots)
299+ for (process_i, node_i) in roots_i
300+ run_node! (obj, node_i, 1 , status (obj)[1 ], meteo, constants, extra)
301+ end
302+ end
303+ end
304+
305+
306+
307+ # for each dependency node in the graph (always one time-step, one object), actual workhorse:
308+ function run_node! (
309+ object:: T ,
310+ node:: SoftDependencyNode ,
311+ i, # time-step to index into the dependency node (to know if the model has been called already)
312+ st,
313+ meteo,
314+ constants,
315+ extra
316+ ) where {T<: ModelList }
317+
318+ # Check if all the parents have been called before the child:
319+ if ! AbstractTrees. isroot (node) && any ([p. simulation_id[i] <= node. simulation_id[i] for p in node. parent])
320+ # If not, this node should be called via another parent
321+ return nothing
322+ end
323+
324+ # Actual call to the model:
325+ run! (node. value, object. models, st, meteo, constants, extra)
326+ node. simulation_id[i] += 1 # increment the simulation id, to know if the model has been called already
327+
328+ # Recursively visit the children (soft dependencies only, hard dependencies are handled by the model itself):
329+ for child in node. children
330+ # ! check if we can run this safely in a @floop loop. I would say no,
331+ # ! because we are running a parallel computation above already, modifying the node.simulation_id,
332+ # ! which is not thread-safe.
333+ run_node! (object, child, i, st, meteo, constants, extra)
311334 end
312335end
313336
314- # 6- Compatibility with MTG:
315337
316- # 6.1: if we pass an MTG and a mapping, then we use them to compute a GraphSimulation object
338+ # Compatibility with MTG:
339+
340+ # If we pass an MTG and a mapping, then we use them to compute a GraphSimulation object
317341# that we use with the first method in this file.
318342function run! (
319343 object:: MultiScaleTreeGraph.Node ,
@@ -341,7 +365,6 @@ function run!(
341365 return sim
342366end
343367
344- # 6.2: if we pass a TreeAlike object (e.g. a GraphSimulation):
345368function run! (
346369 :: TreeAlike ,
347370 :: SingletonAlike ,
@@ -352,16 +375,9 @@ function run!(
352375 check= true ,
353376 executor= ThreadedEx ()
354377)
355- models = get_models (object)
356-
357- # Run the simulation of each soft-coupled model in the dependency graph:
358- # Note: hard-coupled processes handle themselves already
359- @floop executor for (process_key, dependency_node) in collect (dep (object). roots)
360- run! (object, dependency_node, 1 , models, meteo, constants, object, check, executor)
361- end
378+ run! (object, Weather[meteo], constants, extra, check, executor)
362379end
363380
364- # 6.2 bis, over several time-steps
365381function run! (
366382 :: TreeAlike ,
367383 :: TableAlike ,
@@ -379,65 +395,20 @@ function run!(
379395
380396 ! isnothing (extra) && error (" Extra parameters are not allowed for the simulation of an MTG (already used for statuses)." )
381397
382- # Note: The object is not thread safe here, because we write all meteo time-steps into the same Status (for each node)
383- # This is because the number of nodes is usually higher than the number of cores anyway, so we don't gain much by parallelizing over
384- # meteo time-steps in addition. This way we also reduce the memory footprint that can grow very large if we have many time-steps.
385398 for (i, meteo_i) in enumerate (Tables. rows (meteo))
386- # In parallel over dependency root, i.e. for independant computations:
387- @floop executor for (process_key, dependency_node) in collect (dep_graph . roots)
399+ roots = collect (dep_graph . roots)
400+ for (process_key, dependency_node) in roots
388401 # Note: parallelization over objects is handled by the run! method below
389- run ! (object, dependency_node, i, models, meteo_i, constants, object, check, executor)
402+ run_node_multiscale ! (object, dependency_node, i, models, meteo_i, constants, object, check, executor)
390403 end
391404 # At the end of the time-step, we save the results of the simulation in the object:
392405 save_results! (object, i)
393406 end
394407end
395408
396- # ! Actual calls to the model:
397- # Running the simulation on the dependency graph (always one time-step, one object):
398- function run! (object:: T , dep_graph:: DependencyGraph{Dict{Symbol,N}} , i, st, meteo, constants, extra; executor= ThreadedEx ()) where {
399- T<: ModelList ,
400- N<: Union{HardDependencyNode,SoftDependencyNode}
401- }
402- # Run the simulation of each soft-coupled model in the dependency graph:
403- # Note: hard-coupled processes handle themselves already
404- @floop executor for (process, node) in collect (dep_graph. roots)
405- run! (object, node, i, st, meteo, constants, extra)
406- end
407- end
408-
409- # for each dependency node in the graph (always one time-step, one object), actual workhorse:
410- function run! (
411- object:: T ,
412- node:: SoftDependencyNode ,
413- i, # time-step to index into the dependency node (to know if the model has been called already)
414- st,
415- meteo,
416- constants,
417- extra
418- ) where {T<: ModelList }
419-
420- # Check if all the parents have been called before the child:
421- if ! AbstractTrees. isroot (node) && any ([p. simulation_id[i] <= node. simulation_id[i] for p in node. parent])
422- # If not, this node should be called via another parent
423- return nothing
424- end
425-
426- # Actual call to the model:
427- run! (node. value, object. models, st, meteo, constants, extra)
428- node. simulation_id[i] += 1 # increment the simulation id, to know if the model has been called already
429-
430- # Recursively visit the children (soft dependencies only, hard dependencies are handled by the model itself):
431- for child in node. children
432- # ! check if we can run this safely in a @floop loop. I would say no,
433- # ! because we are running a parallel computation above already, modifying the node.simulation_id,
434- # ! which is not thread-safe.
435- run! (object, child, i, st, meteo, constants, extra)
436- end
437- end
438409
439410# For a tree-alike object:
440- function run ! (
411+ function run_node_multiscale ! (
441412 object:: T ,
442413 node:: SoftDependencyNode ,
443414 i, # time-step to index into the dependency node (to know if the model has been called already)
@@ -469,7 +440,7 @@ function run!(
469440 executor = SequentialEx ()
470441 end
471442
472- @floop executor for st in node_statuses # for each node status at the current scale (potentially in parallel over nodes)
443+ for st in node_statuses # for each node status at the current scale (potentially in parallel over nodes)
473444 # Actual call to the model:
474445 run! (node. value, models_at_scale, st, meteo, constants, extra)
475446 end
@@ -481,6 +452,6 @@ function run!(
481452 # ! check if we can run this safely in a @floop loop. I would say no,
482453 # ! because we are running a parallel computation above already, modifying the node.simulation_id,
483454 # ! which is not thread-safe yet.
484- run ! (object, child, i, models, meteo, constants, extra, check, executor)
455+ run_node_multiscale ! (object, child, i, models, meteo, constants, extra, check, executor)
485456 end
486457end
0 commit comments