Skip to content

Commit 6069ebf

Browse files
authored
Merge pull request #150 from VirtualPlantLab/fix-printing
Fix printing of ModelList
2 parents af81844 + 11504b2 commit 6069ebf

5 files changed

Lines changed: 129 additions & 65 deletions

File tree

src/component_models/ModelList.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,6 @@ function ModelList(
187187
ts_kwargs = homogeneous_ts_kwargs(status)
188188
ts_kwargs = add_model_vars(ts_kwargs, mods, type_promotion)
189189

190-
191-
192190
model_list = ModelList(
193191
mods,
194192
ts_kwargs,
@@ -469,9 +467,10 @@ function convert_vars!(mapped_vars::Dict{String,Dict{Symbol,Any}}, type_promotio
469467
end
470468
end
471469

472-
function Base.show(io::IO, ::MIME"text/plain", t::ModelList)
473-
print(io, dep(t))
474-
print(io, status(t))
470+
function Base.show(io::IO, m::MIME"text/plain", t::ModelList)
471+
show(io, m, dep(t))
472+
println(io, "")
473+
show(io, m, status(t))
475474
end
476475

477476
# Short form printing (e.g. inside another object)

src/dependencies/dependency_graph.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,18 @@ AbstractTrees.printnode(io::IO, node::HardDependencyNode{T}) where {T} = print(i
6464
AbstractTrees.printnode(io::IO, node::SoftDependencyNode{T}) where {T} = print(io, T)
6565
Base.show(io::IO, t::AbstractDependencyNode) = AbstractTrees.print_tree(io, t)
6666
Base.length(t::AbstractDependencyNode) = length(collect(AbstractTrees.PreOrderDFS(t)))
67+
Base.length(t::DependencyGraph) = length(traverse_dependency_graph(t))
68+
AbstractTrees.children(t::DependencyGraph) = collect(t.roots)
6769

68-
function Base.show(io::IO, t::DependencyGraph{Dict{Pair{String,Symbol},PlantSimEngine.SoftDependencyNode}})
70+
# Long form printing
71+
function Base.show(io::IO, ::MIME"text/plain", t::DependencyGraph)
6972
# If the graph is cyclic, we print the cycle because we can't print indefinitely:
7073
iscyclic, cycle_vec = is_graph_cyclic(t; warn=false, full_stack=true)
7174
if iscyclic
7275
print(io, "⚠ Cyclic dependency graph: \n $(print_cycle(cycle_vec))")
7376
return nothing
7477
else
7578
draw_dependency_graph(io, t)
76-
print(io, "The dependency graph is acyclic.")
7779
end
7880
end
7981

src/dependencies/printing.jl

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
function draw_dependency_graph(
22
io,
3-
graphs::DependencyGraph{Dict{Pair{String,Symbol},PlantSimEngine.SoftDependencyNode}};
4-
title="Dependency graph",
3+
graphs::DependencyGraph;
4+
title=string("Dependency graph (", length(graphs), " models)"),
5+
max_depth::Int=1,
56
title_style::String="#FFA726 italic",
67
guides_style::String="#42A5F5",
78
dep_graph_guides=(space=" ", vline="", branch="", leaf="", hline="")
89
)
910

1011
dep_graph_guides = map((g) -> Term.apply_style("{$guides_style}$g{/$guides_style}"), dep_graph_guides)
1112

13+
max_depth_reached = Ref(max_depth)
14+
1215
graph_panel = []
1316
for (p, graph) in graphs.roots
17+
if max_depth_reached[] <= 0
18+
push!(graph_panel, "...")
19+
break
20+
end
1421
node = []
1522
# p = :process2; graph = graphs.roots[p]
1623
# typeof(deps[:process4].children[1].hard_dependency.children[1])
17-
draw_panel(node, graph, "", dep_graph_guides, graph; title="Main model")
24+
draw_panel(node, graph, "", dep_graph_guides, graph, max_depth_reached; title="Main model")
1825
push!(graph_panel, Term.Panel(node...; fit=true, title=string(p), style="green dim"))
26+
max_depth_reached[] -= 1
1927
end
2028

2129
print(
@@ -34,7 +42,12 @@ end
3442
3543
Draw the panels for all dependencies
3644
"""
37-
function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-coupled model")
45+
function draw_panel(node, graph, prefix, dep_graph_guides, parent, max_depth_reached=Ref(5); title="Soft-coupled model")
46+
47+
if max_depth_reached[] <= 0
48+
push!(node, "...")
49+
return nothing
50+
end
3851

3952
# If the node has a sibling, draw a branching guide + a horizontal line:
4053
if length(parent.children) <= 1
@@ -65,7 +78,8 @@ function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-c
6578
if graph isa SoftDependencyNode
6679
# draw a branching guide if there's more soft dependencies after this one:
6780
for child in graph.hard_dependency
68-
draw_panel(node, child, panel_hright, dep_graph_guides, graph; title="Hard-coupled model")
81+
draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title="Hard-coupled model")
82+
max_depth_reached[] -= 1
6983
end
7084
elseif isa(parent, SoftDependencyNode) && length(parent.children) > 0
7185
# The current node is a hard dependency of a soft dependency.
@@ -75,7 +89,8 @@ function draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-c
7589

7690
# Recursive call:
7791
for child in AbstractTrees.children(graph)
78-
draw_panel(node, child, panel_hright, dep_graph_guides, graph; title=title_panel(child))
92+
draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title=title_panel(child))
93+
max_depth_reached[] -= 1
7994
end
8095
end
8196

@@ -90,7 +105,7 @@ function draw_model_panel(i::SoftDependencyNode{T}; title=nothing) where {T}
90105
"Model: $(T)\n",
91106
"Dep: $(i.parent_vars)"
92107
);
93-
fit=true,
108+
fit=false,
94109
style="blue dim"
95110
)
96111
end

src/dependencies/soft_dependencies.jl

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
4040
d_vars = Dict{Symbol,Vector{Pair{Symbol,NamedTuple}}}()
4141
for (procname, node) in d.roots
4242
var = Pair{Symbol,NamedTuple}[]
43-
traverse_dependency_graph!(node, variables, var)
43+
nodes_visited = Set{AbstractDependencyNode}()
44+
traverse_dependency_graph!(node, variables, var; node_visited=nodes_visited)
4445
push!(d_vars, procname => var)
4546
end
4647

@@ -138,7 +139,7 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}},
138139
end
139140

140141
# For multiscale mapping:
141-
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}
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}
142143
mapped_vars = mapped_variables(mapping, soft_dep_graphs_roots, verbose=false)
143144
rev_mapping = reverse_mapping(mapped_vars, all=false)
144145

@@ -151,11 +152,11 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
151152

152153
# Remove the hard dependencies from the soft dependencies:
153154
soft_deps_not_hard = drop_process(soft_deps, [hd.process for hd in i.hard_dependency])
154-
155+
155156
hard_dependencies_from_other_scale = [hd for hd in i.hard_dependency if hd.scale != i.scale]
156157

157158
# NB: if a node is already a hard dependency of the node, it cannot be a soft dependency
158-
159+
159160
# Check if the process has soft dependencies at other scales:
160161
soft_deps_multiscale = search_inputs_in_multiscale_output(proc, organ, ins, soft_dep_graphs_roots.roots, rev_mapping, hard_dependencies_from_other_scale)
161162
# Example output: "Soil" => Dict(:soil_water=>[:soil_water_content]), which means that the variable :soil_water_content
@@ -171,37 +172,37 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
171172
# If the process has soft dependencies, then it is not independant
172173
# and we need to add its parent(s) to the node, and the node as a child
173174
for (parent_soft_dep, soft_dep_vars) in pairs(soft_deps_not_hard)
174-
175+
175176
# if the parent isn't registered as a soft dependency, it likely means the soft dependecy should be to an internal hard dependency to the parent
176-
if(!haskey(soft_dep_graph, parent_soft_dep))
177+
if (!haskey(soft_dep_graph, parent_soft_dep))
177178

178179
roots_at_given_scale = soft_dep_graphs_roots.roots[i.scale][:soft_dep_graph]
179-
if !(parent_soft_dep in keys(roots_at_given_scale))
180+
if !(parent_soft_dep in keys(roots_at_given_scale))
180181
master_node = ()
181-
for ((hd_key, hd_scale), hd) in hard_dep_dict
182-
if parent_soft_dep == hd_key
183-
master_node = hd
182+
for ((hd_key, hd_scale), hd) in hard_dep_dict
183+
if parent_soft_dep == hd_key
184+
master_node = hd
184185
depth = 0
185186
# A cleaner way of preventing cycles or infinite loops would be more desirable
186187
while !isa(master_node, SoftDependencyNode) && depth < 50
187188
master_node.parent === nothing && error("Finalised hard dependency has no parent")
188189
master_node = master_node.parent
189190
depth += 1
190-
end
191-
191+
end
192+
192193
break
193194
end
194-
end
195-
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
196-
end
195+
end
196+
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
197+
end
197198
# NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ?
198199
parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process]
199200
else
200201
parent_node = soft_dep_graph[parent_soft_dep]
201202
end
202-
203-
204-
203+
204+
205+
205206
# preventing a cyclic dependency
206207
if parent_soft_dep == proc
207208
error("Cyclic model dependency detected for process $proc from organ $organ.")
@@ -247,33 +248,33 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
247248
# If the node has soft dependencies at other scales, add it as child of the other scale (and add its parent too):
248249
if length(soft_deps_multiscale) > 0
249250
for org in keys(soft_deps_multiscale)
250-
for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org]
251-
251+
for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org]
252+
252253
# if the node has a soft dependency on a node that is a nested hard dependency,
253254
# have it point to the master node of that hard dependency instead of the internal node
254255
# This check is meant in case the organ at the inspected scale is part of a hard dependency,
255256
# and therefore already absent from the roots
256257

257-
roots_at_given_scale = soft_dep_graphs_roots.roots[org][:soft_dep_graph]
258-
if !(parent_soft_dep in keys(roots_at_given_scale))
258+
roots_at_given_scale = soft_dep_graphs_roots.roots[org][:soft_dep_graph]
259+
if !(parent_soft_dep in keys(roots_at_given_scale))
259260
master_node = ()
260-
for ((hd_key, hd_scale), hd) in hard_dep_dict
261-
if parent_soft_dep == hd_key
262-
master_node = hd
261+
for ((hd_key, hd_scale), hd) in hard_dep_dict
262+
if parent_soft_dep == hd_key
263+
master_node = hd
263264
depth = 0
264265
# A cleaner way of preventing cycles or infinite loops would be more desirable
265266
while !isa(master_node, SoftDependencyNode) && depth < 50
266267
master_node.parent === nothing && error("Finalised hard dependency has no parent")
267268
master_node = master_node.parent
268269
depth += 1
269-
end
270-
270+
end
271+
271272
break
272273
end
273-
end
274+
end
274275

275276
master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies")
276-
277+
277278
# NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ?
278279
parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process]
279280
else
@@ -298,13 +299,13 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
298299
)
299300
end
300301

301-
302+
302303
if !(i in parent_node.children) # && error("Cyclic dependency detected for process $proc from organ $organ.")
303304

304305
# Add the current node as a child of the node on which it depends:
305306
push!(parent_node.children, i)
306307
end
307-
# Add the node on which the current node depends as a parent
308+
# Add the node on which the current node depends as a parent
308309
if i.parent === nothing
309310
# If the node had no parent already, it is nothing, so we change into a vector
310311
i.parent = [parent_node]
@@ -315,7 +316,7 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic
315316
end
316317

317318
# Add the multiscale soft dependencies variables of the parent to the current node
318-
i.parent_vars = NamedTuple(Symbol(k) => NamedTuple(v) for (k, v) in soft_deps_multiscale)
319+
i.parent_vars = NamedTuple(Symbol(k) => NamedTuple(v) for (k, v) in soft_deps_multiscale)
319320
end
320321
end
321322
end
@@ -488,9 +489,9 @@ function search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_gra
488489
# Avoid collecting variables at other scales if they come from a hard dependency
489490
# They are handled internally by the hard dep, so if a hard dependency contains that variable, don't add it
490491
# (This only needs to be done one level beneath the soft dependency nodes, any hard dependencies internal to another one don't expose their variables here)
491-
492+
492493
in_hard_dep::Bool = false
493-
hd_os_current_scale = filter(x -> x.scale == org, hard_dependencies_from_other_scale)
494+
hd_os_current_scale = filter(x -> x.scale == org, hard_dependencies_from_other_scale)
494495
for hd_os in hd_os_current_scale
495496
hd_os_output_vars = [first(p) for p in pairs(hd_os.outputs)]
496497
in_hard_dep |= length(filter(x -> x == var, hd_os_output_vars)) > 0

0 commit comments

Comments
 (0)