Quick Start

This tutorial builds a complete model, changes its parameters, runs it, and selects useful output. It assumes basic Julia syntax but no previous Cropbox experience.

Load Cropbox

using Cropbox

Cropbox re-exports the u"..." unit string used throughout this manual. Attaching units to model variables catches incompatible calculations early and makes simulation output self-describing.

Declare a model

The following system describes biomass increasing at a constant rate until a target mass is reached.

@system Growth(Controller) begin
    rate: growth_rate    => 1.5            ~ preserve(parameter, u"g/hr")
    target: target_mass  => 5              ~ preserve(parameter, u"g")

    mass(rate)                             ~ accumulate(u"g")
    mature(mass, target) => mass >= target ~ flag
end
Main.Growth

Read each declaration from left to right:

  • rate and target are fixed values. The parameter tag makes them configurable.
  • mass depends on rate and accumulates it using the simulation time step.
  • mature is recalculated as a Boolean condition. Because mass only increases in this model, it remains true after the threshold is reached.
  • Controller supplies configuration and a simulation clock to the top-level system.

Long names after : are aliases. They improve inspection output without making equations verbose.

Inspect defaults

Use parameters before running an unfamiliar model.

parameters(Growth)

Config for 1 system:

Growth
rate=1.5 g hr^-1
target=5 g

look shows declarations and documentation. It accepts either a system type or an instance.

look(Growth, :mass)
[doc]

[code]
  mass(rate) ~ accumulate(u"g")

Configure a scenario

Configuration keys form a system-variable-value path. A named tuple is a convenient way to set several variables in one system.

config = @config (
    Growth => (rate = 2.0, target = 7.0),
    Clock => :step => 30u"minute",
)

Config for 2 systems:

Growth
rate=2.0 g hr^-1
target=7.0 g
Clock
step=1//2 hr

Numbers supplied for unitful parameters are interpreted in the parameter's declared unit. Explicit quantities are also accepted.

Create an instance

s = instance(Growth; config)
Growth
context=<Context>
config=<Config>
rate(growth_rate)=2.0 g hr^-1
target(target_mass)=7.0 g
mass=0.0 g
mature=false

instance initializes the full system and performs its initial update. Access a variable with property syntax; postfix ' retrieves the current value stored in a Cropbox state.

s.mass', s.mature'
(0.0 g, false)

Calling update!(s) advances this same instance. Most analyses should use simulate, which also collects output.

Run the model

Stop on the model condition and request only the columns needed for the result.

result = simulate(Growth;
    config,
    stop = :mature,
    target = [:mass, :mature],
)
8×3 DataFrame
Rowtimemassmature
Quantity…Quantity…Bool
10//1 hr0.0 gfalse
21//2 hr1.0 gfalse
31//1 hr2.0 gfalse
43//2 hr3.0 gfalse
52//1 hr4.0 gfalse
65//2 hr5.0 gfalse
73//1 hr6.0 gfalse
87//2 hr7.0 gtrue

The initial state is included when the default snapshot rule is used. The default index is context.clock.time, displayed as time.

To stop after a duration instead, pass a number or quantity.

simulate(Growth; config, stop = 2u"hr", target = :mass)
5×2 DataFrame
Rowtimemass
Quantity…Quantity…
10//1 hr0.0 g
21//2 hr1.0 g
31//1 hr2.0 g
43//2 hr3.0 g
52//1 hr4.0 g

Compare scenarios

! expands an iterable value into a vector of configurations.

configs = @config config + !(Growth => :rate => [1.0, 2.0, 3.0])

comparison = simulate(Growth;
    configs,
    stop = 4u"hr",
    target = :mass,
    meta = :Growth,
)
27×4 DataFrame
Rowtimemassratetarget
Quantity…Quantity…Quantity…Quantity…
10//1 hr0.0 g1.0 g hr^-17.0 g
21//2 hr0.5 g1.0 g hr^-17.0 g
31//1 hr1.0 g1.0 g hr^-17.0 g
43//2 hr1.5 g1.0 g hr^-17.0 g
52//1 hr2.0 g1.0 g hr^-17.0 g
65//2 hr2.5 g1.0 g hr^-17.0 g
73//1 hr3.0 g1.0 g hr^-17.0 g
87//2 hr3.5 g1.0 g hr^-17.0 g
94//1 hr4.0 g1.0 g hr^-17.0 g
100//1 hr0.0 g2.0 g hr^-17.0 g
111//2 hr1.0 g2.0 g hr^-17.0 g
121//1 hr2.0 g2.0 g hr^-17.0 g
133//2 hr3.0 g2.0 g hr^-17.0 g
142//1 hr4.0 g2.0 g hr^-17.0 g
155//2 hr5.0 g2.0 g hr^-17.0 g
163//1 hr6.0 g2.0 g hr^-17.0 g
177//2 hr7.0 g2.0 g hr^-17.0 g
184//1 hr8.0 g2.0 g hr^-17.0 g
190//1 hr0.0 g3.0 g hr^-17.0 g
201//2 hr1.5 g3.0 g hr^-17.0 g
211//1 hr3.0 g3.0 g hr^-17.0 g
223//2 hr4.5 g3.0 g hr^-17.0 g
232//1 hr6.0 g3.0 g hr^-17.0 g
245//2 hr7.5 g3.0 g hr^-17.0 g
253//1 hr9.0 g3.0 g hr^-17.0 g
267//2 hr10.5 g3.0 g hr^-17.0 g
274//1 hr12.0 g3.0 g hr^-17.0 g

Metadata columns identify the configuration used for each run. For larger experiments, request individual metadata pairs rather than every parameter in a system.

Plot output

visualize accepts the data frame returned by simulate.

visualize(result, :time, :mass; kind = :line)
Example block output

It can also simulate a system and plot the result in one call:

visualize(Growth, :time, :mass;
    config,
    stop = 4u"hr",
    kind = :line,
)
time (hr) 0 1 2 3 4 0 2 4 6 8 mass (g)

Next steps