Skip to content

Commit 0eec039

Browse files
committed
Update tutorials to latest version
1 parent cbab473 commit 0eec039

8 files changed

Lines changed: 148 additions & 140 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ PlantGraphs = "615ad455-9aac-4340-9247-71ef610f781a"
1010
PlantRayTracer = "78485975-e4aa-407d-b3bb-ded5a4265d05"
1111
PlantViz = "358bd95d-d12c-439f-94b7-04b17e500c7f"
1212
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
13+
SkyDomes = "1838625c-7cf7-40c6-898b-904883e4b556"
1314

1415
[compat]
1516
PlantGeomPrimitives = "0.0.3"
@@ -18,4 +19,5 @@ PlantGraphs = "0.0.2"
1819
PlantRayTracer = " 0.0.6"
1920
PlantViz = "0.0.6"
2021
Reexport = "1.2.2"
21-
julia = "1.9"
22+
SkyDomes = "0.1.6"
23+
julia = "1.11"

nice_trees.png

655 KB
Loading

test/forest.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function VirtualPlantLab.feed!(turtle::Turtle, i::TreeTypes.Internode, data)
5252
## Rotate turtle around the head to implement elliptical phyllotaxis
5353
rh!(turtle, data.phyllotaxis)
5454
HollowCylinder!(turtle, length = i.length, height = i.length/15, width = i.length/15,
55-
move = true, color = RGB(0.5,0.4,0.0))
55+
move = true, colors = RGB(0.5,0.4,0.0))
5656
return nothing
5757
end
5858

@@ -62,7 +62,7 @@ function VirtualPlantLab.feed!(turtle::Turtle, l::TreeTypes.Leaf, data)
6262
ra!(turtle, -data.leaf_angle)
6363
## Generate the leaf
6464
Ellipse!(turtle, length = l.length, width = l.width, move = false,
65-
color = RGB(0.2,0.6,0.2))
65+
colors = RGB(0.2,0.6,0.2))
6666
## Rotate turtle back to original direction
6767
ra!(turtle, data.leaf_angle)
6868
return nothing
@@ -233,7 +233,7 @@ newforest = deepcopy(forest)
233233
@threads for i in eachindex(forest)
234234
newforest[i] = simulate(forest[i], getInternode, 6)
235235
end
236-
render(Scene(newforest), parallel = true)
236+
render(Scene(newforest, parallel = true))
237237
#=
238238
239239
An alternative way to perform the simulation is to have an outer loop for each timestep and an internal loop over the different trees. Although this approach is not required for this simple model, most FSP models will probably need such a scheme as growth of each individual plant will depend on competition for resources with neighbouring plants. In this case, this approach would look as follows:
@@ -245,7 +245,7 @@ for step in 1:15
245245
newforest[i] = simulate(newforest[i], getInternode, 1)
246246
end
247247
end
248-
render(Scene(newforest), parallel = true)
248+
render(Scene(newforest, parallel = true))
249249
#=
250250
251251
# Customizing the scene
@@ -276,7 +276,7 @@ VirtualPlantLab.translate!(soil, Vec(0.0, 10.5, 0.0))
276276
We can now add the `soil` to the `scene` object with the `add!` function.
277277
278278
=#
279-
VirtualPlantLab.add!(scene, mesh = soil, color = RGB(1,1,0))
279+
VirtualPlantLab.add!(scene, mesh = soil, colors = RGB(1,1,0))
280280
#=
281281
282282
We can now render the scene that combines the random forest of binary trees and a yellow soil. Notice that
@@ -297,5 +297,5 @@ compute the resolution from a physical width and height in cm and a dpi (e.g., u
297297
298298
=#
299299
res = calculate_resolution(width = 16.0, height = 16.0, dpi = 1_000)
300-
output = render(scene, axes = false, resolution = res)
300+
output = render(scene, axes = false, size = res)
301301
export_scene(scene = output, filename = "nice_trees.png")

test/growthforest.jl

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
#=
2+
23
# Growth forest
34
4-
Alejandro Morales
5+
Alejandro Morales & Ana Ernst
56
67
Centre 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
918
In this example we extend the binary forest example to have more complex, time-
1019
dependent development and growth based on carbon allocation. For simplicity, the
@@ -24,6 +33,7 @@ import Random
2433
using FastGaussQuadrature
2534
using Distributions
2635
Random.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
4353
module 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
98108
The methods for creating the geometry and color of the tree are the same as in
99109
the previous example.
110+
Create geometry + color for the internodes
100111
101112
=#
102-
## Create geometry + color for the internodes
103113
function 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
109119
end
110120

111-
## Create geometry + color for the leaves
121+
# Create geometry + color for the leaves
112122
function 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
121131
end
122132

123-
## Insertion angle for the bud nodes
133+
# Insertion angle for the bud nodes
124134
function 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)
127137
end
128138
#=
@@ -131,10 +141,10 @@ end
131141
132142
The meristem rule is now parameterized by the initial states of the leaves and
133143
internodes 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)
138148
function 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
155165
length 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
159169
function 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
185195
end
186196

187-
## Branch rule parameterized by initial states of internodes
197+
# Branch rule parameterized by initial states of internodes
188198
function create_branch_rule(vint)
189199
branch_rule = Rule(TreeTypes.Bud,
190200
lhs = prob_break,
@@ -196,7 +206,6 @@ function create_branch_rule(vint)
196206
end
197207
#=
198208
199-
200209
### Growth
201210
202211
We 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
237246
plot(0:1:50, x -> sink_strength(TreeTypes.Leaf(age = x), TreeTypes.treeparams()),
238247
xlabel = "Age", ylabel = "Sink strength", label = "Leaf")
239-
#=
240248

241-
=#
242249
sink_strength(int) = pdf(int.sink, int.age)
243250
plot!(0:1:50, x -> sink_strength(TreeTypes.Internode(age = x)), label = "Internode")
244251
#=
@@ -275,19 +282,19 @@ strength.
275282
276283
=#
277284
function 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.
325332
get_meristems(tree) = apply(tree, Query(TreeTypes.Meristem))
326333
function 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
342349
end
@@ -350,30 +357,27 @@ orientation and RGR:
350357
=#
351358
RGRs = rand(Normal(0.3,0.01), 10, 10)
352359
histogram(vec(RGRs))
353-
#=
354360

355-
=#
356361
orientations = [rand()*360.0 for i = 1:2.0:20.0, j = 1:2.0:20.0]
357362
histogram(vec(orientations))
358-
#=
359363

360-
=#
361364
origins = [Vec(i,j,0) for i = 1:2.0:20.0, j = 1:2.0:20.0];
365+
nothing #hide
362366
#=
363367
364368
The following initalizes a tree based on the origin, orientation and RGR:
365369
366370
=#
367371
function 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)
387391
end
388392
#=
389393
390-
391394
## Visualization
392395
393396
As 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
402405
end
403406
function 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))
405408
end
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
408411
soil = Scene(Graph(axiom = soil_graph));
409412
render(soil, axes = false)
410413
#=
@@ -415,13 +418,12 @@ or a function):
415418
416419
=#
417420
function 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)
421424
end
422425
#=
423426
424-
425427
## Retrieving canopy-level data
426428
427429
We 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
=#
458460
get_LAI(forest)
461+
#=

0 commit comments

Comments
 (0)