Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/Benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
uses: actions/checkout@v7
with:
repository: VEZY/PlantBiophysics.jl
ref: multi-plant
ref: master
path: downstream/PlantBiophysics

- name: Set up Julia
Expand Down Expand Up @@ -106,6 +106,6 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.full_performance }}
uses: ./.github/workflows/FullPerformance.yml
with:
xpalm_ref: codex/xpalm-release-regression
plantbiophysics_ref: multi-plant
plantgeom_ref: plantsimengine-multi-plant
xpalm_ref: main
plantbiophysics_ref: master
plantgeom_ref: main
6 changes: 3 additions & 3 deletions .github/workflows/FullPerformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
plantgeom_ref:
description: PlantGeom branch, tag, or commit
required: true
default: plantsimengine-multi-plant
default: main
type: string
workflow_call:
inputs:
Expand All @@ -38,7 +38,7 @@ on:
plantgeom_ref:
description: PlantGeom branch, tag, or commit
required: false
default: plantsimengine-multi-plant
default: main
type: string

permissions:
Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
uses: actions/checkout@v7
with:
repository: VEZY/PlantGeom.jl
ref: ${{ inputs.plantgeom_ref || 'plantsimengine-multi-plant' }}
ref: ${{ inputs.plantgeom_ref || 'main' }}
path: downstream/PlantGeom

- name: Set up Julia
Expand Down
21 changes: 16 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ API.
- `@process` defines the abstract process type.
- `process(model)` identifies the process.
- `inputs_(model)` and `outputs_(model)` declare status variables.
- In `outputs_`, `Distributed(Default(value))` or `Distributed(Required(T))`
declares an output on selected destination objects. Ordinary values stay local.
- `environment_inputs_(model)` and `environment_outputs_(model)` declare environment
variables.
- `dep(model)` optionally returns model-author defaults using `Input(...)` and
Expand All @@ -27,8 +29,9 @@ Read the current model's parameters directly from `model`. `context` is a

- `CompositeModel` owns a `ObjectRegistry`, model applications, instances, and an
environment.
- `Object` is one runtime entity with stable `ObjectId`, labels, parent,
geometry, and `Status`.
- `Object` is one runtime entity with a unique, stable `ObjectId`, optional
display `name`, group labels, parent, geometry, and `Status`. Display names
need not be unique and never select objects; the UI shows the ID by default.
- Plant architecture is not prescribed. Users choose scales and topology.
- `CompositeModelTemplate` and `ObjectInstance` reuse the same model definitions across
several plants or objects.
Expand All @@ -47,6 +50,10 @@ ModelSpec(model; name=:application, on=selector, inputs=(...), calls=(...), ever

- `on` selects where the model runs.
- `inputs` declares value dependencies.
- `outputs_to` is a tuple of anonymous `OutputTo(selector; vars=...)` entries.
One entry can omit `vars` to bind all distributed outputs; several entries
must explicitly partition their names. Kernels request columns with
`output_targets(context, (:variable,))`.
- `calls` declares manually executable hard dependencies.
- `Updates(:x; after=:producer)` orders intentional duplicate writers.
- `output_routing=(x=:stream_only,)` excludes an output from canonical
Expand All @@ -67,11 +74,15 @@ Scope and topology:
- `Subtree()`: the current object and its descendants
- `SelfPlant()`: the current plant instance/root
- `Ancestor(...)`
- `Scope(name)`
- `Scope(:instance_name)`: an instance's root and descendants
- `Scope(ObjectId(root_id))`: a root chosen directly by object ID and its descendants
- `Relation(...)`

Use keyword criteria for object labels: `kind=:plant`, `species=:oil_palm`,
`scale=:Leaf`, and `name=:leaf_1`.
Use `id=:leaf_1` to select a particular object. Preserve the ID's value type,
including numeric MTG IDs. Use keyword criteria for group labels:
`kind=:plant`, `species=:oil_palm`, and `scale=:Leaf`.
`ModelSpec.name` and `ObjectInstance.name` remain identifiers for applications
and instances; do not treat them as object display names.

`Self()` never means the model, species, or plant unless the current object is
itself that plant.
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## Unreleased

- Declare outputs written to selected objects inside `outputs_` with
`Distributed(Default(value))` or `Distributed(Required(T))`. These variables
are now visible when inspecting a model before building a scenario.
- `outputs_to` now takes an anonymous tuple of `OutputTo` entries. One entry
can omit `vars` to bind all distributed outputs; multiple entries must
explicitly partition variable names. Defaults and types belong to the model.
- Request destination columns with `output_targets(context, (:x, :y))`.
Missing, duplicate, and incompatible destination declarations fail before
initialization. Named output groups and schemas in `OutputTo.vars` were removed.

## v0.15.0

### Summary
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
PlantSimEngineGraphEditorExt = "HTTP"

[compat]
CSV = "0.10"
CSV = "0.10, 1.0"
DataFrames = "1"
Dates = "1.10"
HTTP = "1, 2.0"
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ ModelSpec(
This is the package API for multiscale, multi-plant, soil, microclimate, and
model-scale simulations.

An object needs a unique ID, for example `Object(:leaf_1)`. Select it with
`One(id=:leaf_1)`, or add `scale=:Leaf` to select it as part of
`Many(scale=:Leaf)`. An optional `name="Sunlit leaf"` changes its display
label; it does not change the object's identity or connections. Model
application names identify the configured uses of models.

## Installation

The examples on this development branch use `CompositeModel`. Registered
Expand Down
4 changes: 0 additions & 4 deletions benchmark/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
MultiScaleTreeGraph = "dd4a991b-8a45-4075-bede-262ee62d5583"
PlantBiophysics = "7ae8fcfa-76ad-4ec6-9ea7-5f8f5e2d6ec9"
PlantMeteo = "4630fe09-e0fb-4da5-a846-781cb73437b6"
PlantSimEngine = "9a576370-710b-4269-adf9-4f603a9c6423"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
XPalm = "6b523e1e-d512-416c-8e51-a8fbef0064e7"

[sources]
PlantSimEngine = {path = ".."}
XPalm = {rev = "main", url = "https://github.com/PalmStudio/XPalm.jl"}
PlantBiophysics = {rev = "master", url = "https://github.com/VEZY/PlantBiophysics.jl"}
20 changes: 20 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Benchmarks

`Project.toml` contains the dependencies for the PlantSimEngine benchmarks.
The default suite runs only these benchmarks, both locally and in CI.
AirspeedVelocity resolves this project before loading `benchmarks.jl`, so
optional downstream packages must stay out of its dependencies and sources.

The dedicated PlantBiophysics and full downstream workflows add their checked-out
packages with `Pkg.develop` after running the corresponding helper in
`prepare_full_performance_project.jl`. The full workflow develops XPalm,
PlantBiophysics, PlantGeom, and the PlantSimEngine checkout together, so XPalm
uses the compatible PlantGeom source instead of an older registry release.

For a local downstream run, follow the environment setup in
`.github/workflows/FullPerformance.yml`, then set
`PSE_BENCHMARK_INCLUDE_DOWNSTREAM=true` when loading `benchmarks.jl`.
The flag enables the downstream suite; it does not install its dependencies.
`test/runtests.jl` uses the same default. Passing a test-name pattern explicitly
selects those tests, as the dedicated downstream CI jobs do.
The pinned release comparisons in `release_baselines/` use separate projects.
2 changes: 1 addition & 1 deletion benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const SUITE = BenchmarkGroup()
const INCLUDE_DOWNSTREAM_BENCHMARKS = get(
ENV,
"PSE_BENCHMARK_INCLUDE_DOWNSTREAM",
get(ENV, "GITHUB_ACTIONS", "false") == "true" ? "false" : "true",
"false",
) == "true"
_supports_composite_object_benchmarks(engine) =
isdefined(engine, :CompositeModel) &&
Expand Down
2 changes: 1 addition & 1 deletion benchmark/initial_status_preparation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function readout_binding(name, j, width)
return name => One(within=Self(), var=Symbol("seed_", mod1(j, width)))
elseif kind == 2
return PreviousTimeStep(name) => One(
scale=:Source, name=:source_1, within=Subtree(),
scale=:Source, id=:source_1, within=Subtree(),
application=:signal_source, var=:signal,
)
end
Expand Down
5 changes: 5 additions & 0 deletions benchmark/prepare_full_performance_project.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ using TOML
function prepare_full_performance_project!(project_path)
project = TOML.parsefile(project_path)
pop!(project, "sources", nothing)
# Downstream packages are opt-in: AirspeedVelocity resolves the core project
# before benchmarks.jl can inspect PSE_BENCHMARK_INCLUDE_DOWNSTREAM.
project["deps"]["XPalm"] = "6b523e1e-d512-416c-8e51-a8fbef0064e7"
project["deps"]["PlantBiophysics"] = "7ae8fcfa-76ad-4ec6-9ea7-5f8f5e2d6ec9"
open(project_path, "w") do io
TOML.print(io, project)
end
Expand All @@ -13,6 +17,7 @@ function prepare_plantbiophysics_performance_project!(project_path)
project = TOML.parsefile(project_path)
pop!(project, "sources", nothing)
pop!(project["deps"], "XPalm", nothing)
project["deps"]["PlantBiophysics"] = "7ae8fcfa-76ad-4ec6-9ea7-5f8f5e2d6ec9"
open(project_path, "w") do io
TOML.print(io, project)
end
Expand Down
42 changes: 21 additions & 21 deletions benchmark/test-distributed-output-benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ struct DistributedOutputBenchmarkBoundInputModel <:
AbstractDistributed_Output_Benchmark_Bound_InputModel end
struct DistributedOutputBenchmarkStatusInputModel <:
AbstractDistributed_Output_Benchmark_Status_InputModel end
struct DistributedOutputBenchmarkSceneWriterModel <:
AbstractDistributed_Output_Benchmark_Scene_WriterModel end
struct DistributedOutputBenchmarkSceneWriterModel{D} <:
AbstractDistributed_Output_Benchmark_Scene_WriterModel
declarations::D
end
DistributedOutputBenchmarkSceneWriterModel() =
DistributedOutputBenchmarkSceneWriterModel(NamedTuple())
struct DistributedOutputBenchmarkAssignmentModel{T,I,C,M} <:
AbstractDistributed_Output_Benchmark_AssignmentModel
table::T
Expand Down Expand Up @@ -49,9 +53,12 @@ PlantSimEngine.outputs_(::DistributedOutputBenchmarkStatusInputModel) = (
total=0.0,
)
PlantSimEngine.inputs_(::DistributedOutputBenchmarkSceneWriterModel) = NamedTuple()
PlantSimEngine.outputs_(::DistributedOutputBenchmarkSceneWriterModel) = NamedTuple()
PlantSimEngine.outputs_(model::DistributedOutputBenchmarkSceneWriterModel) = model.declarations
PlantSimEngine.inputs_(::DistributedOutputBenchmarkAssignmentModel) = NamedTuple()
PlantSimEngine.outputs_(::DistributedOutputBenchmarkAssignmentModel) = NamedTuple()
PlantSimEngine.outputs_(model::DistributedOutputBenchmarkAssignmentModel) =
NamedTuple{keys(model.columns)}(map(values(model.columns)) do column
Distributed(Default(zero(eltype(column))))
end)
function PlantSimEngine.run!(
::DistributedOutputBenchmarkSceneWriterModel,
status,
Expand All @@ -69,7 +76,7 @@ function PlantSimEngine.run!(
constants,
context,
)
targets = PlantSimEngine.output_targets(context, :leaves)
targets = PlantSimEngine.output_targets(context, keys(model.columns))
benchmark_assign_outputs_api!(model.mode, targets, model)
return nothing
end
Expand Down Expand Up @@ -448,9 +455,6 @@ function setup_distributed_output_public_assignment_benchmark(
data.column_values.rank :
reverse(data.column_values.rank)
table = ncolumns == 1 ? base_table : (; base_table..., rank)
output_variables = ncolumns == 1 ?
(incident_par=Default(0.0),) :
(incident_par=Default(0.0), rank=Default(0))
objects = Object[Object(:scene; scale=:Scene)]
sizehint!(objects, nobjects + 1)
for (index, object_id) in enumerate(data.object_ids)
Expand Down Expand Up @@ -481,9 +485,8 @@ function setup_distributed_output_public_assignment_benchmark(
name=:scene_assignment,
on=One(scale=:Scene),
outputs_to=(
leaves=OutputTo(
Many(scale=:Leaf, within=SceneScope());
vars=output_variables,
OutputTo(
Many(scale=:Leaf, within=SceneScope()),
),
),
),
Expand All @@ -503,9 +506,6 @@ function setup_distributed_output_wide_assignment_benchmark(
columns = NamedTuple{names}(
ntuple(index -> fill(Float64(index), nobjects), ncolumns),
)
output_variables = NamedTuple{names}(
ntuple(_ -> Default(0.0), ncolumns),
)
objects = Object[Object(:scene; scale=:Scene)]
sizehint!(objects, nobjects + 1)
for object_id in data.object_ids
Expand All @@ -532,9 +532,8 @@ function setup_distributed_output_wide_assignment_benchmark(
name=:scene_wide_assignment,
on=One(scale=:Scene),
outputs_to=(
leaves=OutputTo(
Many(scale=:Leaf, within=SceneScope());
vars=output_variables,
OutputTo(
Many(scale=:Leaf, within=SceneScope()),
),
),
),
Expand Down Expand Up @@ -625,13 +624,14 @@ function setup_distributed_output_compilation_benchmark(
end
application = if distributed
ModelSpec(
DistributedOutputBenchmarkSceneWriterModel();
DistributedOutputBenchmarkSceneWriterModel((
incident_par=Distributed(Default(0.0)),
));
name=:scene_writer,
on=One(scale=:Scene),
outputs_to=(
leaves=OutputTo(
Many(scale=:Leaf, within=SceneScope());
vars=(incident_par=Default(0.0),),
OutputTo(
Many(scale=:Leaf, within=SceneScope()),
),
),
)
Expand Down
Loading
Loading