Skip to content

Commit cb77476

Browse files
committed
Many changes : new orchestrator attempt, many more fixes. Multiple timesteps handled, but bad meshing with refvalue/refvectors cause overwrites if we use multiscale mappings, and unexplored issues if we avoid them. Also, many-node to many-node issues if multiscale mapping. Some TODO comments, structs, commented out code and notes are outdated. More exploration and cleanup required, but current state is much more interesting than the previous commit.
1 parent a3c8973 commit cb77476

15 files changed

Lines changed: 887 additions & 64 deletions

src/Abstract_model_structs.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,16 @@ end
5050

5151
"""
5252
timestep_valid(tsr::TimestepRange)
53-
54-
Checks whether a TimestepRange
5553
"""
5654
timestep_valid(tsr::TimestepRange) = tsr.lower_bound <= tsr.upper_bound
5755

58-
function model_timestep_range_compatible_with_timestep(tsr::TimestepRange, p::Period)
56+
function is_timestep_in_range(tsr::TimestepRange, p::Period)
5957
if !timestep_valid(tsr)
6058
return false
6159
end
6260

6361
# 0 means any timestep is valid, no timestep constraints
64-
if tsr.upper_bound == Seconds(0)
62+
if tsr.upper_bound == Second(0)
6563
return true
6664
end
6765

src/PlantSimEngine.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ include("component_models/get_status.jl")
5757
# Transform into a dataframe:
5858
include("dataframe.jl")
5959

60+
# Timesteps. :
61+
include("timestep/timestep_mapping.jl")
62+
6063
# Computing model dependencies:
6164
include("dependencies/soft_dependencies.jl")
6265
include("dependencies/hard_dependencies.jl")
@@ -66,9 +69,6 @@ include("dependencies/printing.jl")
6669
include("dependencies/dependencies.jl")
6770
include("dependencies/get_model_in_dependency_graph.jl")
6871

69-
# Timesteps. :
70-
include("timestep/timestep_mapping.jl")
71-
7272
# MTG compatibility:
7373
include("mtg/GraphSimulation.jl")
7474
include("mtg/mapping/getters.jl")
@@ -107,7 +107,7 @@ include("examples_import.jl")
107107
export PreviousTimeStep
108108
export AbstractModel
109109
export ModelList, MultiScaleModel
110-
export Orchestrator
110+
export Orchestrator, Orchestrator2, TimestepRange, Var_to, Var_from, ModelTimestepMapping
111111
export RMSE, NRMSE, EF, dr
112112
export Status, TimeStepTable, status
113113
export init_status!

src/dependencies/dependencies.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,16 @@ function dep(m::NamedTuple, nsteps=1; verbose::Bool=true)
100100
dep(nsteps; verbose=verbose, m...)
101101
end
102102

103-
function dep(mapping::Dict{String,T}; verbose::Bool=true) where {T}
103+
function dep(mapping::Dict{String,T}; verbose::Bool=true, orchestrator=Orchestrator2()) where {T}
104104
# First step, get the hard-dependency graph and create SoftDependencyNodes for each hard-dependency root. In other word, we want
105105
# only the nodes that are not hard-dependency of other nodes. These nodes are taken as roots for the soft-dependency graph because they
106106
# are independant.
107-
soft_dep_graphs_roots, hard_dep_dict = hard_dependencies(mapping; verbose=verbose)
107+
soft_dep_graphs_roots, hard_dep_dict = hard_dependencies(mapping; verbose=verbose, orchestrator=Orchestrator2())
108108
# Second step, compute the soft-dependency graph between SoftDependencyNodes computed in the first step. To do so, we search the
109109
# inputs of each process into the outputs of the other processes, at the same scale, but also between scales. Then we keep only the
110110
# nodes that have no soft-dependencies, and we set them as root nodes of the soft-dependency graph. The other nodes are set as children
111111
# of the nodes that they depend on.
112-
dep_graph = soft_dependencies_multiscale(soft_dep_graphs_roots, mapping, hard_dep_dict)
112+
dep_graph = soft_dependencies_multiscale(soft_dep_graphs_roots, mapping, hard_dep_dict, orchestrator=orchestrator)
113113
# During the building of the soft-dependency graph, we identified the inputs and outputs of each dependency node,
114114
# and also defined **inputs** as MappedVar if they are multiscale, i.e. if they take their values from another scale.
115115
# What we are missing is that we need to also define **outputs** as multiscale if they are needed by another scale.

src/dependencies/dependency_graph.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ mutable struct HardDependencyNode{T} <: AbstractDependencyNode
1212
children::Vector{HardDependencyNode}
1313
end
1414

15+
mutable struct TimestepMapping
16+
variable_from::Symbol
17+
variable_to::Symbol
18+
node_to # SoftDependencyNode causes a circular reference # TODO could it be a harddependencynode... ?
19+
mapping_function::Function
20+
mapping_data_template
21+
mapping_data::Dict{Int, Any} # TODO Any's type is the variable's type, also, is Int good here ? Prob not
22+
end
23+
24+
# can hard dependency nodes also handle timestep mapped variables... ?
1525
mutable struct SoftDependencyNode{T} <: AbstractDependencyNode
1626
value::T
1727
process::Symbol
@@ -23,6 +33,8 @@ mutable struct SoftDependencyNode{T} <: AbstractDependencyNode
2333
parent_vars::Union{Nothing,NamedTuple}
2434
children::Vector{SoftDependencyNode}
2535
simulation_id::Vector{Int} # id of the simulation
36+
timestep::Period
37+
timestep_mapping_data::Union{Nothing, Vector{TimestepMapping}} # TODO : this approach might not play too well with parallelisation over MTG nodes
2638
end
2739

2840
# Add methods to check if a node is parallelizable:

src/dependencies/hard_dependencies.jl

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ end
112112

113113

114114
# When we use a mapping (multiscale), we return the set of soft-dependencies (we put the hard-dependencies as their children):
115-
function hard_dependencies(mapping::Dict{String,T}; verbose::Bool=true) where {T}
115+
function hard_dependencies(mapping::Dict{String,T}; verbose::Bool=true, orchestrator::Orchestrator2=Orchestrator2()) where {T}
116116
full_vars_mapping = Dict(first(mod) => Dict(get_mapped_variables(last(mod))) for mod in mapping)
117117
soft_dep_graphs = Dict{String,Any}()
118118
not_found = Dict{Symbol,DataType}()
@@ -226,6 +226,42 @@ function hard_dependencies(mapping::Dict{String,T}; verbose::Bool=true) where {T
226226
end
227227
end
228228

229+
#=
230+
# TODO check whether this is a bit late in the game
231+
# maybe the timestep mapping should be done before we enter this function
232+
if length(orchestrator.non_default_timestep_data_per_scale) > 0
233+
if haskey(orchestrator.non_default_timestep_data_per_scale, symbol(node))
234+
tvm = orchestrator.non_default_timestep_data_per_scale[symbol(node)].timestep_variable_mapping
235+
if haskey(twm, var)
236+
237+
end
238+
end
239+
end
240+
error("Variable `$(var)` is not computed by any model, not mapped from a different scale or timestep not initialised by the user in the status, and not found in the MTG at scale $(symbol(node)) (checked for MTG node $(node_id(node))).")
241+
=#
242+
243+
244+
# Once multiscale mapping has been dealt with, check if any variable has a timestep mapping
245+
# Which will add potential new dependencies
246+
#=if !isempty(orchestrator.non_default_timestep_data_per_scale)
247+
# TODO the user can get away with not declaring the model, only the scale if necessary
248+
# a prepass that recomputes everything might simplify code here and make the simulation require less variable digging
249+
for (scale, tsh) in non_default_timestep_data_per_scale
250+
# TODO find which model the variable is pulled from
251+
# TODO check the variable exists
252+
for (model, timestep) in tsh.model_timesteps
253+
# TODO check the timestep is within the model's accepted timestep range
254+
# TODO recover the right variables
255+
end
256+
257+
for (variable, tvm) in tsh.timestep_variable_mapping
258+
# TODO check the variable isn't already mapped
259+
# If it is, ensure there are no name conflicts
260+
# and the model of the variable it is taken from has the expected timestep
261+
# If it isn't, create a new link
262+
end
263+
end
264+
end=#
229265
for (organ, model) in mapping
230266
soft_dep_graph = Dict(
231267
process_ => SoftDependencyNode(
@@ -238,7 +274,9 @@ function hard_dependencies(mapping::Dict{String,T}; verbose::Bool=true) where {T
238274
nothing,
239275
nothing,
240276
SoftDependencyNode[],
241-
[0] # Vector of zeros of length = number of time-steps
277+
[0], # Vector of zeros of length = number of time-steps
278+
orchestrator.default_timestep,
279+
nothing
242280
)
243281
for (process_, soft_dep_vars) in hard_deps[organ].roots # proc_ = :carbon_assimilation ; soft_dep_vars = hard_deps.roots[proc_]
244282
)

src/dependencies/soft_dependencies.jl

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
7070
nothing,
7171
nothing,
7272
SoftDependencyNode[],
73-
fill(0, nsteps)
73+
fill(0, nsteps),
74+
Day(1), # TODO
75+
nothing
7476
)
7577
for (process_, soft_dep_vars) in d.roots
7678
)
@@ -138,8 +140,18 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
138140
return DependencyGraph(independant_process_root, d.not_found)
139141
end
140142

143+
function timestep_mapped_variables(orchestrator)
144+
145+
#=struct SimulationTimestepHandler#{W,V}
146+
model_timesteps::Dict{Any, Period} # where {W <: AbstractModel} # if a model isn't in there, then it follows the default, todo check if the given timestep respects the model's range
147+
timestep_variable_mapping::Dict{Any, TimestepMapper} #where {V}
148+
end
149+
non_default_timestep_data_per_scale::Dict{String, SimulationTimestepHandler}
150+
=#
151+
end
152+
141153
# For multiscale mapping:
142-
function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{String,Any}}, mapping::Dict{String,A}, hard_dep_dict::Dict{Pair{Symbol,String},HardDependencyNode}) where {A<:Any}
154+
function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{String,Any}}, mapping::Dict{String,A}, hard_dep_dict::Dict{Pair{Symbol,String},HardDependencyNode}; orchestrator::Orchestrator2=Orchestrator2()) where {A<:Any}
143155
mapped_vars = mapped_variables(mapping, soft_dep_graphs_roots, verbose=false)
144156
rev_mapping = reverse_mapping(mapped_vars, all=false)
145157

@@ -324,10 +336,93 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
324336
end
325337
end
326338

327-
return DependencyGraph(independant_process_root, soft_dep_graphs_roots.not_found)
339+
dep_graph = DependencyGraph(independant_process_root, soft_dep_graphs_roots.not_found)
340+
traverse_dependency_graph!(x -> set_non_default_timestep_in_node(x, orchestrator), dep_graph, visit_hard_dep=false)
341+
traverse_dependency_graph!(x -> add_timestep_data_to_node(x, orchestrator), dep_graph, visit_hard_dep=false)
342+
343+
return dep_graph
344+
end
345+
346+
# # set the timestep for everyone first, else we might not use the correct timestep when looking at the parents later
347+
function set_non_default_timestep_in_node(soft_dependency_node, orchestrator::Orchestrator2)
348+
for mtsm in orchestrator.non_default_timestep_mapping
349+
if mtsm.scale == soft_dependency_node.scale && (mtsm.model) == typeof(model_(soft_dependency_node.value))
350+
soft_dependency_node.timestep = mtsm.timestep
351+
end
352+
end
353+
end
354+
355+
function add_timestep_data_to_node(soft_dependency_node, orchestrator::Orchestrator2)
356+
357+
# now we can create the mapping
358+
for mtsm in orchestrator.non_default_timestep_mapping
359+
if mtsm.scale == soft_dependency_node.scale && (mtsm.model) == typeof(model_(soft_dependency_node.value))
360+
for (var_to, var_from) in mtsm.var_to_var
361+
if !isnothing(soft_dependency_node.parent)
362+
parent = nothing
363+
variable_mapping = nothing
364+
for parent_node in soft_dependency_node.parent
365+
if typeof(parent_node.value) == var_from.model && parent_node.scale == var_from.scale
366+
parent = parent_node
367+
variable_mapping = create_timestep_mapping(soft_dependency_node, parent, var_to, var_from)
368+
break
369+
end
370+
end
371+
if isnothing(parent)
372+
#error
373+
end
374+
if isnothing(parent.timestep_mapping_data)
375+
parent.timestep_mapping_data = Vector{TimestepMapping}()
376+
end
377+
push!(parent.timestep_mapping_data, variable_mapping)
378+
else
379+
# Error
380+
end
381+
end
382+
end
383+
end
328384
end
329385

386+
# TODO this is incorrect, there may be multiple variables mapped between the two nodes
387+
function create_timestep_mapping(node::SoftDependencyNode, parent::SoftDependencyNode, var_to::Var_to, var_from::Var_from)
388+
389+
@assert parent.timestep != 0 "Error : node timestep internally set to 0"
390+
391+
timestep_ratio = node.timestep / parent.timestep
392+
393+
# Keeping things simple for now, only integers allowed
394+
@assert timestep_ratio == trunc(timestep_ratio) "Error : non-integer timestep ratio"
395+
396+
# TODO ensure type compatibility between var_to and var_from
397+
# Simplification probably possible by doing the check earlier
398+
399+
# TODO test previoustimestep
400+
var_type = DataType
401+
402+
for (symbol, var_dump) in node.inputs
403+
for var in var_dump
404+
if isa(var, MappedVar)
405+
# check the source variable, because the sink one might be a vector...?
406+
# TODO multinode mapping
407+
if var.source_variable == var_from.name
408+
# This should be a fixed size array, ideally
409+
var_type = eltype(mapped_default(var))
410+
break
411+
end
412+
else
413+
if var.symbol == var_from.name
414+
@assert "untested"
415+
var_type = eltype(mapped_default(var))
416+
break
417+
end
418+
end
419+
end
420+
end
330421

422+
mapping_data_template = Vector{var_type}(undef, convert(Int64, timestep_ratio))
423+
# TODO : type shouldn't be Any but Vector{var_type}
424+
return TimestepMapping(var_from.name, var_to.name, node, var_from.mapping_function, mapping_data_template, Dict{MultiScaleTreeGraph.NodeMTG, Any}())
425+
end
331426
"""
332427
drop_process(proc_vars, process)
333428

src/mtg/GraphSimulation.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ struct GraphSimulation{T,S,U,O,V}
2525
var_need_init::Dict{String,V}
2626
dependency_graph::DependencyGraph
2727
models::Dict{String,U}
28-
orchestrator::Orchestrator
2928
outputs::Dict{String,O}
29+
outputs_index::Dict{String, Int}
30+
orchestrator::Orchestrator2
31+
3032
end
3133

32-
function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false, orchestrator=Orchestrator())
33-
GraphSimulation(init_simulation(graph, mapping; nsteps=nsteps, outputs=outputs, type_promotion=type_promotion, check=check, verbose=verbose)..., orchestrator)
34+
function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false, orchestrator=Orchestrator2())
35+
GraphSimulation(init_simulation(graph, mapping; nsteps=nsteps, outputs=outputs, type_promotion=type_promotion, check=check, verbose=verbose, orchestrator=orchestrator)...)
3436
end
3537

3638
dep(g::GraphSimulation) = g.dependency_graph

src/mtg/add_organ.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,10 @@ function add_organ!(node::MultiScaleTreeGraph.Node, sim_object, link, symbol, sc
3333
new_node = MultiScaleTreeGraph.Node(id, node, MultiScaleTreeGraph.NodeMTG(link, symbol, index, scale), attributes)
3434
st = init_node_status!(new_node, sim_object.statuses, sim_object.status_templates, sim_object.reverse_multiscale_mapping, sim_object.var_need_init, check=check)
3535

36+
# TODO add the node to the timestep mappings
37+
# TODO initialise the MTG nodes in the timestep mappings
38+
# NOTE : this isn't ideal, as it constrains the add_organ! function usage
39+
init_timestep_mapping_data(new_node, sim_object.dependency_graph)
40+
3641
return st
3742
end

0 commit comments

Comments
 (0)