Skip to content

Commit 4c46ee8

Browse files
committed
Turn off code execution
1 parent b1d0c2c commit 4c46ee8

10 files changed

Lines changed: 341 additions & 445 deletions

File tree

docs/src/howto/Materials.md

Lines changed: 8 additions & 140 deletions
Large diffs are not rendered by default.

docs/src/tutorials/from_tree_forest/forest.md

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11

22

3-
````@example Forest
4-
#= Forest
3+
# Forest
54

6-
Alejandro Morales
5+
Alejandro Morales & Ana Ernst
76

87
Centre for Crop Systems Analysis - Wageningen University
98

9+
> ## TL;DR
10+
> Similar in functionality to [Tree](https://virtualplantlab.com/dev/tutorials/from_tree_forest/tree/) tutorial with separate graphs for each tree
11+
> - Modify tree parameters for each tree
12+
> - Multithreaded simulation (grow trees in parallel)
13+
> - Scene customization (e.g., add soil)
14+
> - Export Scenes
15+
>
16+
1017
In this example we extend the tree example into a forest, where
1118
each tree is described by a separate graph object and parameters driving the
1219
growth of these trees vary across individuals following a predefined distribution.
@@ -15,7 +22,7 @@ This tutorial requires using the Distributions.jl package:
1522
The data types, rendering methods and growth rules are the same as in the binary
1623
tree example:
1724

18-
=#
25+
```julia
1926
using VirtualPlantLab
2027
using Distributions, Plots, ColorTypes
2128
import GLMakie
@@ -104,7 +111,7 @@ end
104111
branch_rule = Rule(TreeTypes.Bud,
105112
lhs = prob_break,
106113
rhs = bud -> TreeTypes.BudNode() + TreeTypes.Internode() + TreeTypes.Meristem())
107-
````
114+
```
108115

109116
The main difference with respect to the tree is that several of the parameters
110117
will vary per TreeTypes. Also, the location of the tree and initial orientation of
@@ -116,19 +123,19 @@ of each tree and rotates it.
116123
(ii) Wrap the axiom, rules and the creation of the graph into a function that
117124
takes the required parameters as inputs.
118125

119-
````@example Forest
126+
```julia
120127
function create_tree(origin, growth, budbreak, orientation)
121128
axiom = T(origin) + RH(orientation) + TreeTypes.Internode() + TreeTypes.Meristem()
122129
tree = Graph(axiom = axiom, rules = (meristem_rule, branch_rule),
123130
data = TreeTypes.treeparams(growth = growth, budbreak = budbreak))
124131
return tree
125132
end
126-
````
133+
```
127134

128135
The code for elongating the internodes to simulate growth remains the same as for
129136
the binary tree example
130137

131-
````@example Forest
138+
```julia
132139
getInternode = Query(TreeTypes.Internode)
133140

134141
function elongate!(tree, query)
@@ -149,53 +156,53 @@ function simulate(tree, query, nsteps)
149156
end
150157
return new_tree
151158
end
152-
````
159+
```
153160

154161
Let's simulate a forest of 10 x 10 trees with a distance between (and within) rows
155162
of 2 meters. First we generate the original positions of the trees. For the
156163
position we just need to pass a `Vec` object with the x, y, and z coordinates of
157164
the location of each TreeTypes. The code below will generate a matrix with the coordinates:
158165

159-
````@example Forest
166+
```julia
160167
origins = [Vec(i,j,0) for i = 1:2.0:20.0, j = 1:2.0:20.0]
161-
````
168+
```
162169

163170
We may assume that the initial orientation is uniformly distributed between 0 and 360 degrees:
164171

165-
````@example Forest
172+
```julia
166173
orientations = [rand()*360.0 for i = 1:2.0:20.0, j = 1:2.0:20.0]
167-
````
174+
```
168175

169176
For the `growth` and `budbreak` parameters we will assumed that they follow a
170177
LogNormal and Beta distribution, respectively. We can generate random
171178
values from these distributions using the `Distributions` package. For the
172179
relative growth rate:
173180

174-
````@example Forest
181+
```julia
175182
growths = rand(LogNormal(-2, 0.3), 10, 10)
176183
histogram(vec(growths))
177184
savefig("growths.png") ## hide
178-
````
185+
```
179186

180187
![](growths.png)
181188

182189
And for the budbreak parameter:
183190

184-
````@example Forest
191+
```julia
185192
budbreaks = rand(Beta(2.0, 10), 10, 10)
186193
histogram(vec(budbreaks))
187194
savefig("budbreaks.png") ## hide
188-
````
195+
```
189196

190197
![](budbreaks.png)
191198

192199
Now we can create our forest by calling the `create_tree` function we defined earlier
193200
with the correct inputs per tree:
194201

195-
````@example Forest
202+
```julia
196203
forest = vec(create_tree.(origins, growths, budbreaks, orientations));
197204
nothing #hide
198-
````
205+
```
199206

200207
By vectorizing `create_tree()` over the different arrays, we end up with an array
201208
of trees. Each tree is a different Graph, with its own nodes, rewriting rules
@@ -210,29 +217,29 @@ as you may need to change some settings in your computer).
210217
We can simulate the growth of each tree by applying the method `simulate` to each
211218
tree, creating a new version of the forest (the code below is an array comprehension)
212219

213-
````@example Forest
220+
```julia
214221
newforest = [simulate(tree, getInternode, 2) for tree in forest];
215222
nothing #hide
216-
````
223+
```
217224

218225
And we can render the forest with the function `render` as in the binary tree
219226
example but passing the whole forest at once
220227

221-
````@example Forest
228+
```julia
222229
pl = render(Scene(newforest))
223230
GLMakie.save("newforest1.png", pl) ## hide
224-
````
231+
```
225232

226233
![](newforest1.png)
227234

228235
If we iterate 4 more iterations we will start seeing the different individuals
229236
diverging in size due to the differences in growth rates
230237

231-
````@example Forest
238+
```julia
232239
newforest = [simulate(tree, getInternode, 15) for tree in newforest];
233240
pl = render(Scene(newforest))
234241
GLMakie.save("newforest2.png", pl) ## hide
235-
````
242+
```
236243

237244
![](newforest2.png)
238245

@@ -245,21 +252,21 @@ and execute the iterations of the loop in multiple threads using the macro `@thr
245252
Note that the rendering function can also be ran in parallel (i.e. the geometry will be
246253
generated separately for each plant and the merge together):
247254

248-
````@example Forest
255+
```julia
249256
using Base.Threads
250257
newforest = deepcopy(forest)
251258
@threads for i in 1:length(forest)
252259
newforest[i] = simulate(forest[i], getInternode, 6)
253260
end
254261
pl = render(Scene(newforest, parallel = true))
255262
GLMakie.save("newforest3.png", pl) ## hide
256-
````
263+
```
257264

258265
![](newforest3.png)
259266

260267
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:
261268

262-
````@example Forest
269+
```julia
263270
newforest = deepcopy(forest)
264271
for step in 1:15
265272
@threads for i in 1:length(newforest)
@@ -268,7 +275,7 @@ for step in 1:15
268275
end
269276
pl = render(Scene(newforest, parallel = true))
270277
GLMakie.save("newforest4.png", pl) ## hide
271-
````
278+
```
272279

273280
![](newforest4.png)
274281

@@ -279,10 +286,10 @@ tweaking the 3D representation. When we want to combine plants generated from gr
279286
geometric element it is best to combine all these geometries in a `GLScene` object. We can start the scene
280287
with the `newforest` generated in the above:
281288

282-
````@example Forest
289+
```julia
283290
scene = Scene(newforest);
284291
nothing #hide
285-
````
292+
```
286293

287294
We can create the soil tile directly, without having to create a graph. The simplest approach is two use
288295
a special constructor `Rectangle` where one species a corner of the rectangle and two vectors defining the
@@ -292,28 +299,28 @@ above when we determined the origin of each plant. VPL offers some shortcuts: `O
292299
passing the desired length as input. Below, a rectangle is created on the XY plane with the origin as a
293300
corner and each side being 11 units long:
294301

295-
````@example Forest
302+
```julia
296303
soil = Rectangle(length = 21.0, width = 21.0)
297304
rotatey!(soil, pi/2)
298305
VirtualPlantLab.translate!(soil, Vec(0.0, 10.5, 0.0))
299-
````
306+
```
300307

301308
We can now add the `soil` to the `scene` object with the `add!` function.
302309

303-
````@example Forest
310+
```julia
304311
VirtualPlantLab.add!(scene, mesh = soil, colors = RGB(1,1,0))
305-
````
312+
```
306313

307314
We can now render the scene that combines the random forest of binary trees and a yellow soil. Notice that
308315
in all previous figures, a coordinate system with grids was being depicted. This is helpful for debugging
309316
your code but also to help setup the scene (e.g. if you are not sure how big the soil tile should be).
310317
Howver, it may be distracting for the visualization. It turns out that we can turn that off with
311318
`axes = false`:
312319

313-
````@example Forest
320+
```julia
314321
pl = render(scene, axes = false)
315322
GLMakie.save("newforest5.png", pl) ## hide
316-
````
323+
```
317324

318325
![](newforest5.png)
319326

@@ -323,11 +330,11 @@ we can run the `save_scene` function on the object returned from `render`. The a
323330
`render` to increase the number of pixels in the final image. A helper function `calculate_resolution` is provided to
324331
compute the resolution from a physical width and height in cm and a dpi (e.g., useful for publications and posters):
325332

326-
````@example Forest
333+
```julia
327334
res = calculate_resolution(width = 16.0, height = 16.0, dpi = 1_000)
328335
output = render(scene, axes = false, size = res)
329336
export_scene(scene = output, filename = "nice_trees.png")
330-
````
337+
```
331338

332339
---
333340

0 commit comments

Comments
 (0)