Skip to content

Commit 2619e3d

Browse files
committed
Merge branch 'main' into graph-visualization
2 parents 46ad56f + c2254a8 commit 2619e3d

25 files changed

Lines changed: 135 additions & 130 deletions

.github/workflows/Benchmarks.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ jobs:
2222
with:
2323
julia-version: ${{ matrix.version }}
2424
bench-on: ${{ github.event.pull_request.head.sha }}
25+
extra-pkgs: |
26+
https://github.com/PalmStudio/XPalm.jl#main
27+
https://github.com/VEZY/PlantBiophysics.jl#master

README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -233,52 +233,52 @@ The package is designed to be easily scalable, and can be used to simulate model
233233

234234
```julia
235235
mapping = ModelMapping(
236-
"Scene" => ToyDegreeDaysCumulModel(),
237-
"Plant" => (
236+
:Scene => ToyDegreeDaysCumulModel(),
237+
:Plant => (
238238
MultiScaleModel(
239239
model=ToyLAIModel(),
240240
mapped_variables=[
241-
:TT_cu => "Scene",
241+
:TT_cu => :Scene,
242242
],
243243
),
244244
Beer(0.6),
245245
MultiScaleModel(
246246
model=ToyAssimModel(),
247-
mapped_variables=[:soil_water_content => "Soil"],
247+
mapped_variables=[:soil_water_content => :Soil],
248248
),
249249
MultiScaleModel(
250250
model=ToyCAllocationModel(),
251251
mapped_variables=[
252-
:carbon_demand => ["Leaf", "Internode"],
253-
:carbon_allocation => ["Leaf", "Internode"]
252+
:carbon_demand => [:Leaf, :Internode],
253+
:carbon_allocation => [:Leaf, :Internode]
254254
],
255255
),
256256
MultiScaleModel(
257257
model=ToyPlantRmModel(),
258-
mapped_variables=[:Rm_organs => ["Leaf" => :Rm, "Internode" => :Rm],],
258+
mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],
259259
),
260260
),
261-
"Internode" => (
261+
:Internode => (
262262
MultiScaleModel(
263263
model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0),
264-
mapped_variables=[:TT => "Scene",],
264+
mapped_variables=[:TT => :Scene,],
265265
),
266266
MultiScaleModel(
267267
model=ToyInternodeEmergence(TT_emergence=20.0),
268-
mapped_variables=[:TT_cu => "Scene"],
268+
mapped_variables=[:TT_cu => :Scene],
269269
),
270270
ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004),
271271
Status(carbon_biomass=1.0)
272272
),
273-
"Leaf" => (
273+
:Leaf => (
274274
MultiScaleModel(
275275
model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0),
276-
mapped_variables=[:TT => "Scene",],
276+
mapped_variables=[:TT => :Scene,],
277277
),
278278
ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025),
279279
Status(carbon_biomass=1.0)
280280
),
281-
"Soil" => (
281+
:Soil => (
282282
ToySoilWaterModel(),
283283
),
284284
);
@@ -305,11 +305,11 @@ And run the simulation:
305305

306306
```julia
307307
out_vars = ModelMapping(
308-
"Scene" => (:TT_cu,),
309-
"Plant" => (:carbon_allocation, :carbon_assimilation, :soil_water_content, :aPPFD, :TT_cu, :LAI),
310-
"Leaf" => (:carbon_demand, :carbon_allocation),
311-
"Internode" => (:carbon_demand, :carbon_allocation),
312-
"Soil" => (:soil_water_content,),
308+
:Scene => (:TT_cu,),
309+
:Plant => (:carbon_allocation, :carbon_assimilation, :soil_water_content, :aPPFD, :TT_cu, :LAI),
310+
:Leaf => (:carbon_demand, :carbon_allocation),
311+
:Internode => (:carbon_demand, :carbon_allocation),
312+
:Soil => (:soil_water_content,),
313313
)
314314

315315
out = run!(mtg, mapping, meteo, outputs=out_vars, executor=SequentialEx());

benchmark/benchmarks.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ SUITE[suite_name]["XPalm_setup"] = @benchmarkable xpalm_default_param_create() s
4444
palm, models, out_vars, meteo = xpalm_default_param_create()
4545
sim_outputs = xpalm_default_param_run(palm, models, out_vars, meteo)
4646

47-
SUITE[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run($palm, $models, $out_vars, $meteo) seconds = 120
48-
SUITE[suite_name]["XPalm_convert_outputs"] = @benchmarkable xpalm_default_param_convert_outputs($sim_outputs) seconds = 120
47+
SUITE[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run(palm, models, out_vars, meteo) setup = ((palm, models, out_vars, meteo) = xpalm_default_param_create())
48+
SUITE[suite_name]["XPalm_convert_outputs"] = @benchmarkable xpalm_default_param_convert_outputs($sim_outputs)
4949

5050
#tune!(SUITE)
5151
#results = run(SUITE, verbose=true)

benchmark/test-PSE-benchmark.jl

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, mete
2828

2929
if length(MultiScaleTreeGraph.children(status.node)) == 1 && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence
3030

31-
status_new_internode = add_organ!(status.node, sim_object, "<", "Internode", 2, index=1)
32-
add_organ!(status_new_internode.node, sim_object, "+", "Leaf", 2, index=1)
31+
status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1)
32+
add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1)
3333
status_new_internode.TT_cu_emergence = status.TT_cu
3434
elseif (length(MultiScaleTreeGraph.children(status.node)) >= 2 && length(MultiScaleTreeGraph.children(status.node)) < 7) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence
35-
status_new_internode = add_organ!(status.node, sim_object, "<", "Internode", 2, index=1)
36-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=4)
37-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=5)
35+
status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1)
36+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=4)
37+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=5)
3838
status_new_internode.TT_cu_emergence = status.TT_cu
3939
elseif (length(MultiScaleTreeGraph.children(status.node)) >= 7 && length(MultiScaleTreeGraph.children(status.node)) < 30) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence
40-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=6)
41-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=7)
42-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=8)
43-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=9)
44-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=10)
45-
add_organ!(status.node, sim_object, "+", "Leaf", 2, index=11)
40+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=6)
41+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=7)
42+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=8)
43+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=9)
44+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=10)
45+
add_organ!(status.node, sim_object, "+", :Leaf, 2, index=11)
4646

4747
end
4848

@@ -60,62 +60,62 @@ function do_benchmark_on_heavier_mtg()
6060

6161
#similar to the mtg growth test but with a much lower emergence threshold
6262
mapping = ModelMapping(
63-
"Scene" => ToyDegreeDaysCumulModel(),
64-
"Plant" => (
63+
:Scene => ToyDegreeDaysCumulModel(),
64+
:Plant => (
6565
MultiScaleModel(
6666
model=ToyLAIModel(),
6767
mapped_variables=[
68-
:TT_cu => "Scene",
68+
:TT_cu => :Scene,
6969
],
7070
),
7171
PlantSimEngine.Examples.Beer(0.6),
7272
MultiScaleModel(
7373
model=ToyCAllocationModel(),
7474
mapped_variables=[
75-
:carbon_assimilation => ["Leaf"],
76-
:carbon_demand => ["Leaf", "Internode"],
77-
:carbon_allocation => ["Leaf", "Internode"]
75+
:carbon_assimilation => [:Leaf],
76+
:carbon_demand => [:Leaf, :Internode],
77+
:carbon_allocation => [:Leaf, :Internode]
7878
],
7979
),
8080
MultiScaleModel(
8181
model=ToyPlantRmModel(),
82-
mapped_variables=[:Rm_organs => ["Leaf" => :Rm, "Internode" => :Rm],],
82+
mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],
8383
),
8484
),
85-
"Internode" => (
85+
:Internode => (
8686
MultiScaleModel(
8787
model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0),
88-
mapped_variables=[:TT => "Scene",],
88+
mapped_variables=[:TT => :Scene,],
8989
),
9090
MultiScaleModel(
9191
model=ToyInternodeCrazyEmergence(TT_emergence=1.0),
92-
mapped_variables=[:TT_cu => "Scene"],
92+
mapped_variables=[:TT_cu => :Scene],
9393
),
9494
ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004),
9595
Status(carbon_biomass=1.0)
9696
),
97-
"Leaf" => (
97+
:Leaf => (
9898
MultiScaleModel(
9999
model=ToyAssimModel(),
100-
mapped_variables=[:soil_water_content => "Soil", :aPPFD => "Plant"],
100+
mapped_variables=[:soil_water_content => :Soil, :aPPFD => :Plant],
101101
),
102102
MultiScaleModel(
103103
model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0),
104-
mapped_variables=[:TT => "Scene",],
104+
mapped_variables=[:TT => :Scene,],
105105
),
106106
ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025),
107107
Status(carbon_biomass=1.0)
108108
),
109-
"Soil" => (
109+
:Soil => (
110110
ToySoilWaterModel(),
111111
),
112112
)
113113

114114
out_vars = Dict(
115-
"Leaf" => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation),
116-
"Internode" => (:carbon_allocation, :TT_cu_emergence),
117-
"Plant" => (:carbon_allocation,),
118-
"Soil" => (:soil_water_content,),
115+
:Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation),
116+
:Internode => (:carbon_allocation, :TT_cu_emergence),
117+
:Plant => (:carbon_allocation,),
118+
:Soil => (:soil_water_content,),
119119
)
120120

121121
out = run!(mtg, mapping, meteo_day, tracked_outputs=out_vars, executor=SequentialEx())

benchmark/test-multirate-buffer-benchmark.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ function PlantSimEngine.run!(::MRBenchConsumer24Model, models, status, meteo, co
3131
end
3232

3333
function _build_multirate_benchmark_mtg(nleaves::Int)
34-
mtg = Node(MultiScaleTreeGraph.NodeMTG("/", "Scene", 1, 0))
35-
plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", "Plant", 1, 1))
36-
internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", "Internode", 1, 2))
34+
mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0))
35+
plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1))
36+
internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2))
3737

3838
for i in 1:nleaves
39-
Node(internode, MultiScaleTreeGraph.NodeMTG("+", "Leaf", i, 2))
39+
Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 2))
4040
end
4141

4242
return mtg
@@ -46,30 +46,30 @@ function setup_multirate_buffer_benchmark(; nleaves=2000, ndays=30)
4646
mtg = _build_multirate_benchmark_mtg(nleaves)
4747

4848
mapping = ModelMapping(
49-
"Leaf" => (
49+
:Leaf => (
5050
ModelSpec(MRBenchSourceModel(Ref(0))) |> TimeStepModel(1.0),
5151
),
52-
"Plant" => (
52+
:Plant => (
5353
ModelSpec(MRBenchConsumer4Model()) |>
54-
MultiScaleModel([:X => ["Leaf"]]) |>
54+
MultiScaleModel([:X => [:Leaf]]) |>
5555
TimeStepModel(ClockSpec(4.0, 1.0)) |>
56-
InputBindings(; X=(process=:mrbenchsource, var=:X, scale="Leaf", policy=Integrate())),
56+
InputBindings(; X=(process=:mrbenchsource, var=:X, scale=:Leaf, policy=Integrate())),
5757
ModelSpec(MRBenchConsumer24Model()) |>
58-
MultiScaleModel([:X => ["Leaf"]]) |>
58+
MultiScaleModel([:X => [:Leaf]]) |>
5959
TimeStepModel(ClockSpec(24.0, 1.0)) |>
60-
InputBindings(; X=(process=:mrbenchsource, var=:X, scale="Leaf", policy=Integrate())),
60+
InputBindings(; X=(process=:mrbenchsource, var=:X, scale=:Leaf, policy=Integrate())),
6161
),
6262
)
6363

6464
nsteps = 24 * ndays
6565
meteo = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], nsteps))
6666

6767
reqs = [
68-
OutputRequest("Leaf", :X; name=:x_hourly, process=:mrbenchsource, policy=HoldLast()),
69-
OutputRequest("Leaf", :X; name=:x_daily_sum, process=:mrbenchsource, policy=Integrate(), clock=ClockSpec(24.0, 1.0)),
68+
OutputRequest(:Leaf, :X; name=:x_hourly, process=:mrbenchsource, policy=HoldLast()),
69+
OutputRequest(:Leaf, :X; name=:x_daily_sum, process=:mrbenchsource, policy=Integrate(), clock=ClockSpec(24.0, 1.0)),
7070
]
7171

72-
tracked = Dict("Plant" => (:Y4, :Y24), "Leaf" => (:X,))
72+
tracked = Dict(:Plant => (:Y4, :Y24), :Leaf => (:X,))
7373
return mtg, mapping, meteo, reqs, tracked, nsteps
7474
end
7575

benchmark/test-xpalm.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ function xpalm_default_param_convert_outputs(sim_outputs)
4747
end
4848

4949

50+
println(Pkg.status("XPalm"))
51+
5052
#=@testset "XPalm simple test" begin
5153
# default number of seconds is 5
5254
b_XP = @benchmark xpalm_default_param_run() seconds = 120

docs/src/API/API_public.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ Scope selection detail:
7070
### Exporting variables at requested rates
7171

7272
```julia
73-
req_hold = OutputRequest("Leaf", :A; name=:A_hourly, process=:assim, policy=HoldLast())
74-
req_day = OutputRequest("Leaf", :A; name=:A_daily_sum, process=:assim, policy=Integrate(), clock=ClockSpec(24.0, 1.0))
73+
req_hold = OutputRequest(:Leaf, :A; name=:A_hourly, process=:assim, policy=HoldLast())
74+
req_day = OutputRequest(:Leaf, :A; name=:A_daily_sum, process=:assim, policy=Integrate(), clock=ClockSpec(24.0, 1.0))
7575
run!(sim, meteo; tracked_outputs=[req_hold, req_day], executor=SequentialEx())
7676
out = collect_outputs(sim; sink=DataFrame)
7777

@@ -126,23 +126,23 @@ Use `Integrate` for accumulation semantics and `Aggregate` for summary-statistic
126126
```julia
127127
ModelSpec(DailyModel()) |>
128128
TimeStepModel(ClockSpec(24.0, 1.0)) |>
129-
InputBindings(; a=(process=:hourly_assim, var=:A, scale="Leaf", policy=Integrate(SumReducer())))
129+
InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(SumReducer())))
130130

131131
ModelSpec(DailyModel()) |>
132132
TimeStepModel(ClockSpec(24.0, 1.0)) |>
133-
InputBindings(; a=(process=:hourly_assim, var=:A, scale="Leaf", policy=Aggregate(MaxReducer())))
133+
InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Aggregate(MaxReducer())))
134134

135135
ModelSpec(DailyModel()) |>
136136
TimeStepModel(ClockSpec(24.0, 1.0)) |>
137-
InputBindings(; a=(process=:hourly_assim, var=:A, scale="Leaf", policy=Integrate(vals -> maximum(vals) - minimum(vals))))
137+
InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(vals -> maximum(vals) - minimum(vals))))
138138

139139
ModelSpec(DailyModel()) |>
140140
TimeStepModel(ClockSpec(24.0, 1.0)) |>
141-
InputBindings(; a=(process=:hourly_assim, var=:A, scale="Leaf", policy=Integrate((vals, durations) -> sum(vals .* durations))))
141+
InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate((vals, durations) -> sum(vals .* durations))))
142142

143143
ModelSpec(DailyModel()) |>
144144
TimeStepModel(ClockSpec(24.0, 1.0)) |>
145-
InputBindings(; a=(process=:hourly_assim, var=:A, scale="Leaf", policy=Integrate(PlantMeteo.DurationSumReducer())))
145+
InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(PlantMeteo.DurationSumReducer())))
146146
```
147147

148148
Built-in reducer types are:

docs/src/model_execution.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ aggregates over that civil day (including later timesteps from that day when ava
142142

143143
```julia
144144
mapping = ModelMapping(
145-
"Leaf" => (
145+
:Leaf => (
146146
ModelSpec(LeafSourceModel()) |> TimeStepModel(1.0),
147147
ModelSpec(LeafConsumerModel()) |>
148148
TimeStepModel(ClockSpec(2.0, 1.0)) |>
@@ -155,13 +155,13 @@ mapping = ModelMapping(
155155

156156
```julia
157157
mapping = ModelMapping(
158-
"Leaf" => (
158+
:Leaf => (
159159
ModelSpec(HourlyAssimModel()) |> TimeStepModel(1.0),
160160
),
161-
"Plant" => (
161+
:Plant => (
162162
ModelSpec(DailyCarbonOfferModel()) |>
163163
TimeStepModel(ClockSpec(24.0, 1.0)) |>
164-
InputBindings(; A=(process=:hourlyassim, var=:A, scale="Leaf", policy=Integrate())),
164+
InputBindings(; A=(process=:hourlyassim, var=:A, scale=:Leaf, policy=Integrate())),
165165
),
166166
)
167167
```
@@ -170,7 +170,7 @@ mapping = ModelMapping(
170170

171171
```julia
172172
mapping = ModelMapping(
173-
"Leaf" => (
173+
:Leaf => (
174174
ModelSpec(SlowSourceModel()) |> TimeStepModel(ClockSpec(2.0, 1.0)),
175175
ModelSpec(FastConsumerModel()) |>
176176
TimeStepModel(1.0) |>
@@ -195,7 +195,7 @@ on each `ModelSpec`.
195195
You can export selected variables at a requested rate from temporal streams:
196196

197197
```julia
198-
req = OutputRequest("Leaf", :carbon_assimilation;
198+
req = OutputRequest(:Leaf, :carbon_assimilation;
199199
name=:A_daily,
200200
process=:toyassim,
201201
policy=Integrate(),

docs/src/multiscale/multiscale_considerations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Some models, like the ones we've seen in single-scale simulations, work on a ver
4444

4545
More fine-grained models can be tied to a specific plant organ.
4646

47-
For instance, a model computing a leaf's surface area depending on its age would operate at the "leaf" scale, and be called **for every leaf** at every timestep. On the other hand, a model computing the plant's total leaf area only needs to be run once per timestep, and can be run at the :Plant scale.
47+
For instance, a model computing a leaf's surface area depending on its age would operate at the `:Leaf` scale, and be called **for every leaf** at every timestep. On the other hand, a model computing the plant's total leaf area only needs to be run once per timestep, and can be run at the `:Plant` scale.
4848

4949
This is a major difference between a single-scale simulation and a multi-scale one. By default, any model in a single-scale simulation will only run **once** per timestep. However, in multi-scale, if a plant has several instances of an organ type -say it has a hundred leaves- then any model operating at the :Leaf scale will by default run one hundred times per timestep, unless it is explicitely controlled by another model (which can happen in hard dependency configurations).
5050

docs/src/multiscale/single_to_multiscale.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ outputs_singlescale = run!(models_singlescale, meteo_day)
5151
outputs_singlescale[1:3,:] # show the first 3 rows of the output
5252
```
5353

54-
Those models all operate on a simplified model of a single plant, without any organ-local information. We can therefore consider them to be working at the 'whole plant' scale. Their variables also operate at that "plant" scale, so there is no need to map any variable to other scales.
54+
Those models all operate on a simplified model of a single plant, without any organ-local information. We can therefore consider them to be working at the 'whole plant' scale. Their variables also operate at that `:Plant` scale, so there is no need to map any variable to other scales.
5555

5656
We can therefore convert this into the following mapping:
5757

0 commit comments

Comments
 (0)