11#=
2+
23# Growth forest
34
4- Alejandro Morales
5+ Alejandro Morales & Ana Ernst
56
67Centre for Crop Systems Analysis - Wageningen University
78
9+ > ## TL;DR
10+ > Now we want to implement a more extended functionality of our [Forest]()!
11+ > - Growth rules, based on information stored in organs (dimensions, carbon assimilation)
12+ > - Update dimensions in function of assimilation
13+ > - Compute sink strength
14+ > - Merge Scenes
15+ > - Generate forest on grid and retrieve canopy-level data (e.g., LAI)
16+ >
817
918In this example we extend the binary forest example to have more complex, time-
1019dependent development and growth based on carbon allocation. For simplicity, the
@@ -24,6 +33,7 @@ import Random
2433using FastGaussQuadrature
2534using Distributions
2635Random. seed! (123456789 )
36+ import GLMakie
2737#=
2838
2939## Model definition
@@ -39,41 +49,41 @@ module. The differences with respect to the previous example are:
3949 - Bud break probability is a function of distance to apical meristem
4050
4151=#
42- # # Data types
52+ # Data types
4353module TreeTypes
4454 using VirtualPlantLab
4555 using Distributions
46- # # Meristem
56+ # Meristem
4757 Base. @kwdef mutable struct Meristem <: VirtualPlantLab.Node
48- age:: Int64 = 0 # # Age of the meristem
58+ age:: Int64 = 0 # Age of the meristem
4959 end
50- # # Bud
60+ # Bud
5161 struct Bud <: VirtualPlantLab.Node end
52- # # Node
62+ # Node
5363 struct Node <: VirtualPlantLab.Node end
54- # # BudNode
64+ # BudNode
5565 struct BudNode <: VirtualPlantLab.Node end
56- # # Internode (needs to be mutable to allow for changes over time)
66+ # Internode (needs to be mutable to allow for changes over time)
5767 Base. @kwdef mutable struct Internode <: VirtualPlantLab.Node
5868 age:: Int64 = 0 # # Age of the internode
5969 biomass:: Float64 = 0.0 # # Initial biomass
6070 length:: Float64 = 0.0 # # Internodes
6171 width:: Float64 = 0.0 # # Internodes
6272 sink:: Exponential{Float64} = Exponential (5 )
6373 end
64- # # Leaf
74+ # Leaf
6575 Base. @kwdef mutable struct Leaf <: VirtualPlantLab.Node
6676 age:: Int64 = 0 # # Age of the leaf
6777 biomass:: Float64 = 0.0 # # Initial biomass
6878 length:: Float64 = 0.0 # # Leaves
6979 width:: Float64 = 0.0 # # Leaves
7080 sink:: Beta{Float64} = Beta (2 ,5 )
7181 end
72- # # Graph-level variables -> mutable because we need to modify them during growth
82+ # Graph-level variables -> mutable because we need to modify them during growth
7383 Base. @kwdef mutable struct treeparams
74- # # Variables
84+ # Variables
7585 biomass:: Float64 = 2e-3 # # Current total biomass (g)
76- # # Parameters
86+ # Parameters
7787 RGR:: Float64 = 1.0 # # Relative growth rate (1/d)
7888 IB0:: Float64 = 1e-3 # # Initial biomass of an internode (g)
7989 SIW:: Float64 = 0.1e6 # # Specific internode weight (g/m3)
@@ -97,32 +107,32 @@ import .TreeTypes
97107
98108The methods for creating the geometry and color of the tree are the same as in
99109the previous example.
110+ Create geometry + color for the internodes
100111
101112=#
102- # # Create geometry + color for the internodes
103113function VirtualPlantLab. feed! (turtle:: Turtle , i:: TreeTypes.Internode , vars)
104- # # Rotate turtle around the head to implement elliptical phyllotaxis
114+ # Rotate turtle around the head to implement elliptical phyllotaxis
105115 rh! (turtle, vars. phyllotaxis)
106116 HollowCylinder! (turtle, length = i. length, height = i. width, width = i. width,
107- move = true , color = RGB (0.5 ,0.4 ,0.0 ))
117+ move = true , colors = RGB (0.5 ,0.4 ,0.0 ))
108118 return nothing
109119end
110120
111- # # Create geometry + color for the leaves
121+ # Create geometry + color for the leaves
112122function VirtualPlantLab. feed! (turtle:: Turtle , l:: TreeTypes.Leaf , vars)
113- # # Rotate turtle around the arm for insertion angle
123+ # Rotate turtle around the arm for insertion angle
114124 ra! (turtle, - vars. leaf_angle)
115- # # Generate the leaf
125+ # Generate the leaf
116126 Ellipse! (turtle, length = l. length, width = l. width, move = false ,
117- color = RGB (0.2 ,0.6 ,0.2 ))
118- # # Rotate turtle back to original direction
127+ colors = RGB (0.2 ,0.6 ,0.2 ))
128+ # Rotate turtle back to original direction
119129 ra! (turtle, vars. leaf_angle)
120130 return nothing
121131end
122132
123- # # Insertion angle for the bud nodes
133+ # Insertion angle for the bud nodes
124134function VirtualPlantLab. feed! (turtle:: Turtle , b:: TreeTypes.BudNode , vars)
125- # # Rotate turtle around the arm for insertion angle
135+ # Rotate turtle around the arm for insertion angle
126136 ra! (turtle, - vars. branch_angle)
127137end
128138#=
@@ -131,10 +141,10 @@ end
131141
132142The meristem rule is now parameterized by the initial states of the leaves and
133143internodes and will only be triggered every X days where X is the plastochron.
144+ Create right side of the growth rule (parameterized by the initial states
145+ of the leaves and internodes)
134146
135147=#
136- # # Create right side of the growth rule (parameterized by the initial states
137- # # of the leaves and internodes)
138148function create_meristem_rule (vleaf, vint)
139149 meristem_rule = Rule (TreeTypes. Meristem,
140150 lhs = mer -> mod (data (mer). age, graph_data (mer). plastochron) == 0 ,
@@ -155,36 +165,36 @@ rather than the number of internodes. An adhoc traversal is used to compute this
155165length of the main branch a bud belongs to (ignoring the lateral branches).
156166
157167=#
158- # # Compute the probability that a bud breaks as function of distance to the meristem
168+ # Compute the probability that a bud breaks as function of distance to the meristem
159169function prob_break (bud)
160- # # We move to parent node in the branch where the bud was created
170+ # We move to parent node in the branch where the bud was created
161171 node = parent (bud)
162- # # Extract the first internode
172+ # Extract the first internode
163173 child = filter (x -> data (x) isa TreeTypes. Internode, children (node))[1 ]
164174 data_child = data (child)
165- # # We measure the length of the branch until we find the meristem
175+ # We measure the length of the branch until we find the meristem
166176 distance = 0.0
167177 while ! isa (data_child, TreeTypes. Meristem)
168- # # If we encounter an internode, store the length and move to the next node
178+ # If we encounter an internode, store the length and move to the next node
169179 if data_child isa TreeTypes. Internode
170180 distance += data_child. length
171181 child = children (child)[1 ]
172182 data_child = data (child)
173- # # If we encounter a node, extract the next internode
183+ # If we encounter a node, extract the next internode
174184 elseif data_child isa TreeTypes. Node
175185 child = filter (x -> data (x) isa TreeTypes. Internode, children (child))[1 ]
176186 data_child = data (child)
177187 else
178188 error (" Should be Internode, Node or Meristem" )
179189 end
180190 end
181- # # Compute the probability of bud break as function of distance and
182- # # make stochastic decision
191+ # Compute the probability of bud break as function of distance and
192+ # make stochastic decision
183193 prob = min (1.0 , distance* graph_data (bud). budbreak)
184194 return rand () < prob
185195end
186196
187- # # Branch rule parameterized by initial states of internodes
197+ # Branch rule parameterized by initial states of internodes
188198function create_branch_rule (vint)
189199 branch_rule = Rule (TreeTypes. Bud,
190200 lhs = prob_break,
@@ -196,7 +206,6 @@ function create_branch_rule(vint)
196206end
197207#=
198208
199-
200209### Growth
201210
202211We need some functions to compute the length and width of a leaf or internode
@@ -236,9 +245,7 @@ sink_strength(leaf, vars) = leaf.age > vars.leaf_expansion ? 0.0 :
236245 pdf (leaf. sink, leaf. age/ vars. leaf_expansion)/ 100.0
237246plot (0 : 1 : 50 , x -> sink_strength (TreeTypes. Leaf (age = x), TreeTypes. treeparams ()),
238247 xlabel = " Age" , ylabel = " Sink strength" , label = " Leaf" )
239- #=
240248
241- =#
242249sink_strength (int) = pdf (int. sink, int. age)
243250plot! (0 : 1 : 50 , x -> sink_strength (TreeTypes. Internode (age = x)), label = " Internode" )
244251#=
@@ -275,19 +282,19 @@ strength.
275282
276283=#
277284function grow! (tree, all_leaves, all_internodes)
278- # # Compute total biomass increment
285+ # Compute total biomass increment
279286 tvars = data (tree)
280287 ΔB = tvars. RGR* tvars. biomass
281288 tvars. biomass += ΔB
282- # # Total sink strength
289+ # Total sink strength
283290 total_sink = 0.0
284291 for leaf in all_leaves
285292 total_sink += sink_strength (leaf, tvars)
286293 end
287294 for int in all_internodes
288295 total_sink += sink_strength (int)
289296 end
290- # # Allocate biomass to leaves and internodes
297+ # Allocate biomass to leaves and internodes
291298 for leaf in all_leaves
292299 leaf. biomass += ΔB* sink_strength (leaf, tvars)/ total_sink
293300 end
@@ -325,18 +332,18 @@ parallel.
325332get_meristems (tree) = apply (tree, Query (TreeTypes. Meristem))
326333function daily_step! (forest)
327334 @threads for tree in forest
328- # # Retrieve all the relevant organs
335+ # Retrieve all the relevant organs
329336 all_leaves = get_leaves (tree)
330337 all_internodes = get_internodes (tree)
331338 all_meristems = get_meristems (tree)
332- # # Update the age of the organs
339+ # Update the age of the organs
333340 age! (all_leaves, all_internodes, all_meristems)
334- # # Grow the tree
341+ # Grow the tree
335342 grow! (tree, all_leaves, all_internodes)
336343 tvars = data (tree)
337344 size_leaves! (all_leaves, tvars)
338345 size_internodes! (all_internodes, tvars)
339- # # Developmental rules
346+ # Developmental rules
340347 rewrite! (tree)
341348 end
342349end
@@ -350,30 +357,27 @@ orientation and RGR:
350357=#
351358RGRs = rand (Normal (0.3 ,0.01 ), 10 , 10 )
352359histogram (vec (RGRs))
353- #=
354360
355- =#
356361orientations = [rand ()* 360.0 for i = 1 : 2.0 : 20.0 , j = 1 : 2.0 : 20.0 ]
357362histogram (vec (orientations))
358- #=
359363
360- =#
361364origins = [Vec (i,j,0 ) for i = 1 : 2.0 : 20.0 , j = 1 : 2.0 : 20.0 ];
365+ nothing # hide
362366#=
363367
364368The following initalizes a tree based on the origin, orientation and RGR:
365369
366370=#
367371function create_tree (origin, orientation, RGR)
368- # # Initial state and parameters of the tree
372+ # Initial state and parameters of the tree
369373 vars = TreeTypes. treeparams (RGR = RGR)
370- # # Initial states of the leaves
374+ # Initial states of the leaves
371375 leaf_length, leaf_width = leaf_dims (vars. LB0, vars)
372376 vleaf = (biomass = vars. LB0, length = leaf_length, width = leaf_width)
373- # # Initial states of the internodes
377+ # Initial states of the internodes
374378 int_length, int_width = int_dims (vars. LB0, vars)
375379 vint = (biomass = vars. IB0, length = int_length, width = int_width)
376- # # Growth rules
380+ # Growth rules
377381 meristem_rule = create_meristem_rule (vleaf, vint)
378382 branch_rule = create_branch_rule (vint)
379383 axiom = T (origin) + RH (orientation) +
@@ -387,7 +391,6 @@ function create_tree(origin, orientation, RGR)
387391end
388392#=
389393
390-
391394## Visualization
392395
393396As in the previous example, it makes sense to visualize the forest with a soil
@@ -401,10 +404,10 @@ Base.@kwdef struct Soil <: VirtualPlantLab.Node
401404 width:: Float64
402405end
403406function VirtualPlantLab. feed! (turtle:: Turtle , s:: Soil , vars)
404- Rectangle! (turtle, length = s. length, width = s. width, color = RGB (255 / 255 , 236 / 255 , 179 / 255 ))
407+ Rectangle! (turtle, length = s. length, width = s. width, colors = RGB (255 / 255 , 236 / 255 , 179 / 255 ))
405408end
406- soil_graph = RA (- 90.0 ) + T (Vec (0.0 , 10.0 , 0.0 )) + # Moves into position
407- Soil (length = 20.0 , width = 20.0 ) # Draws the soil tile
409+ soil_graph = RA (- 90.0 ) + T (Vec (0.0 , 10.0 , 0.0 )) + # # Moves into position
410+ Soil (length = 20.0 , width = 20.0 ) # # Draws the soil tile
408411soil = Scene (Graph (axiom = soil_graph));
409412render (soil, axes = false )
410413#=
@@ -415,13 +418,12 @@ or a function):
415418
416419=#
417420function render_forest (forest, soil)
418- scene = Scene (vec (forest)) # create scene from forest
419- scene = Scene ([scene, soil]) # merges the two scenes
421+ scene = Scene (vec (forest)) # # create scene from forest
422+ scene = Scene ([scene, soil]) # # merges the two scenes
420423 render (scene)
421424end
422425#=
423426
424-
425427## Retrieving canopy-level data
426428
427429We may want to extract some information at the canopy level such as LAI. This is
@@ -456,3 +458,4 @@ And compute the leaf area index:
456458
457459=#
458460get_LAI (forest)
461+ #=
0 commit comments