Variable

In Cropbox, a variable is defined as a unit element of modeling that denotes a value determined by a specific operation relying on other variables. Each variable represents a field within the system struct defined by the @system macro.

Variable Declaration

Variables are declared when a system is declared with the @system macro. @system macro accepts lines of variable declaration specified by its own syntax. They are loosely based on Julia syntax sharing common expressions and operators, but have distinct semantics as explained below.

name[(args..; kwargs..)][: alias] [=> body] ~ [state][::type][(tags..)]

  • name: variable name (usually short abbreviation)
  • args: automatically bound depending variables
  • kwargs: custom bound depending variables (only for call now)
  • alias: alternative name (long description)
  • body: code snippet (state/type specific, begin .. end block for multiple lines)
  • state: verb indicating kind of state (empty if not State-based)
  • type: internal type (i.e. Float64 by default for most State variable)
  • tags: variable specific options (i.e. unit, min/max, etc.)

Example

Here is an example of a system declaration where all three variables are valid declarations:

@system S begin
    a: variable_a ~ advance
    b(a) => a^2 ~ track
    c => true ~ ::Bool
end
Main.S

Variable States

Within Cropbox, a variable inside a system can be one of many different abstract types based on the variable's purpose. Depending on its type, each variable has its own behavior when a system is instantiated. In Cropbox, we refer to these as the state of the variables, originating from the term state variables often used in mathematical modeling.

Note

Specifying a state is not mandatory when declaring a variable. Cropbox also allows plain variables, which are commonly used for creating variable references to other systems.

Currently, there are 19 different variable states implemented in Cropbox.

Instant derivation

  • preserve: keeps an initially assigned value with no further updates; constants, parameters
  • track : evaluates expression and assigns a new value for each time step
  • flag : checks a conditional logic; similar to track with boolean type, but composition is allowed
  • remember : keeps tracking the variable until a certain condition is met; like track switching to preserve

Cumulative update

  • accumulate: emulates integration of a rate variable over time; essentially Euler method
  • capture: calculates the difference between time steps
  • integrate: calculates an integral over a non-time variable using Gaussian method
  • advance: updates an internal time-keeping variable

Data source

  • provide: provides a table-like multi-column time-series data; i.e. weather data
  • drive: fetches the current value from a time-series; often used with provide; i.e. air temperature
  • tabulate: makes a two dimensional table with named keys; i.e. partitioning table
  • interpolate: makes a curve function interpolated with discrete values; i.e. soil characteristic curve

Equation solving

  • solve: solves a polynomial equation symbolically; i.e. quadratic equation for coupling photosynthesis
  • bisect: solves a nonlinear equation using bisection method; i.e. energy balance equation

Dynamic structure

  • produce: attaches a new instance of dynamically generated system; i.e. root structure growth

Language extension

  • hold: marks a placeholder for the variable shared between mixins
  • wrap: allows passing a reference to the state variable object, not a dereferenced value
  • call: defines a partial function accepting user-defined arguments, while bound to other variables
  • bring: duplicates variables declaration from another system into the current system

Instant derivation

preserve

preserve variables are fixed values with no further modification after instantiation of a system. As a result, they are often used as the state for parameter variables, which allow any initial value to be set via a configuration object supplied at the start of a simulation. Non-parameter constants are also used for fixed variables that do not need to be computed at each time step.

Supported tags: unit, optional, parameter, override, extern, ref, min, max, round

Example

@system S(Controller) begin
    a => 1 ~ preserve
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr1.0
21.0 hr1.0
32.0 hr1.0


track

track variables are evaluated and assigned a new value at every time step. In a conventional model, these are the variables that would be computed in every update loop. At every time step, the formula in the variable code is evaluated and saved for use. This assignment of value occurs only once per time step, as intended by the Cropbox framework. No manual assignment of computation at an arbitrary time is allowed. This is to ensure that that there are no logical errors resulting from premature or incorrectly ordered variable assignments. For example, a cyclical reference between two track variables is caught by Cropbox as an error.

Supported tags: unit, override, extern, ref, skip, init, min, max, round, when

Example

@system S(Controller) begin
    a ~ advance
    b(a) => 2*a ~ track
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Float64
10.0 hr0.00.0
21.0 hr1.02.0
32.0 hr2.04.0


flag

flag variables are expressed in a conditional statement or logical operator for which a boolean value is evaluated at every time step. They function like a track variable but with a boolean value.

Supported tags: parameter, override, extern, once, when

Example

@system S(Controller) begin
    a ~ advance
    b => 1 ~ preserve

    f(a, b) => (a > b) ~ flag
end

simulate(S; stop=2)
3×4 DataFrame
Rowtimeabf
Quantity…Float64Float64Bool
10.0 hr0.01.0false
21.0 hr1.01.0false
32.0 hr2.01.0true


remember

remember variables keep track of a variable until a specified condition is met. When the condition is met, it is saved as either its latest update or a specified value. They are like track variables that turn into preserve variables. The when tag is required to specify condition. Unless specified, the initial value for remember defaults to 0.

Supported tags: unit, init, when

Example

@system S(Controller) begin
    t(context.clock.tick) ~ track
    f(t) => t > 1 ~ flag
    r1(t) ~ remember(when=f)
    r2(t) => t^2 ~ remember(when=f)
end

simulate(S; stop=2)
3×5 DataFrame
Rowtimetfr1r2
Quantity…Float64BoolFloat64Float64
10.0 hr0.0false0.00.0
21.0 hr1.0false0.00.0
32.0 hr2.0true2.04.0


Cumulative update

accumulate

accumulate variables emulate the integration of a rate variable over time. It uses the Euler's method of integration. By default, an accumulate variable accumulates every hour, unless a unit of time is specified.

Supported tags: unit, init, time, timeunit, reset, min, max, when

Example

@system S(Controller) begin
    a => 1 ~ accumulate
    b => 1 ~ accumulate(u"d")
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Quantity…
10.0 hr0.00.0 d
21.0 hr1.00.0416667 d
32.0 hr2.00.0833333 d


capture

capture variables calculate the difference of a variable between time steps. The time tag allows evaluations for varying rates of time.

Supported tags: unit, time, timeunit, when

Example

@system S(Controller) begin
    a => 1 ~ track
    b(a) => a + 1 ~ capture
    c(a) => a + 1 ~ accumulate
end

simulate(S; stop=2)
3×4 DataFrame
Rowtimeabc
Quantity…Float64Float64Float64
10.0 hr1.00.00.0
21.0 hr1.02.02.0
32.0 hr1.02.04.0


integrate

integrate variables calculate an integral over a non-time variable using the Gaussian method.

Supported tags: unit, from, to

Example

@system S(Controller) begin
    w => 1 ~ preserve(parameter)
    a => 0 ~ preserve(parameter)
    b => π ~ preserve(parameter)
    f(w; x) => w*sin(x) ~ integrate(from=a, to=b)
end

instance(S)
S
context=<Context>
config=<Config>
w=1.0
a=0.0
b=3.14159
f=2.0


advance

advance variables update an internal time-keeping variable. By default, it starts at 0 and increases by 1 every time step. Note that the unit does not have to be time-related.

Supported tags: init, step, unit

Example

@system S(Controller) begin
    a ~ advance(init=1)
    b ~ advance(step=2)
    c ~ advance(u"m")
end

simulate(S; stop=2)
3×4 DataFrame
Rowtimeabc
Quantity…Float64Float64Quantity…
10.0 hr1.00.00.0 m
21.0 hr2.02.01.0 m
32.0 hr3.04.02.0 m


Data source

provide

provide variables provide a DataFrame with a given index (index) starting from an initial value (init). By default, autounit is true, meaning that provide variables will attempt to get units from column names.

Supported tags: index, init, step, autounit, parameter

Example

@system S(Controller) begin
    a => DataFrame("index (hr)" => 0:2, "value (m)" => 0:10:20) ~ provide
end

instance(S).a
3×2 DataFrame
 Row  index      value      Quantity…  Quantity… 
─────┼──────────────────────
   1 │      0 hr        0 m
   2 │      1 hr       10 m
   3 │      2 hr       20 m


drive

drive variables fetch the current value from a time-series. It is often used in conjunction with provide.

Supported tags: tick, unit, from, by, parameter, override

Example

@system S(Controller) begin
    a => [2, 4, 6] ~ drive
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr2.0
21.0 hr4.0
32.0 hr6.0


tabulate

tabulate variables make a two dimensional table with named keys. The rows tag must be assigned.

Supported tags: unit, rows, columns, parameter

Example

@system S(Controller) begin
    T => [
      # a b
        0 4 ; # A
        1 5 ; # B
        2 6 ; # C
        3 7 ; # D
    ] ~ tabulate(rows=(:A, :B, :C, :D), columns=(:a, :b))
end

instance(S)
S
context=<Context>
config=<Config>
T=[0.0 4.0; 1.0 5.0; 2.0 6.0; 3.0 7.0]


interpolate

interpolate variables make a curve function for a provided set of discrete values.

Supported tags: unit, knotunit, reverse, parameter

Example

@system S(Controller) begin
    m => [1 => 10, 2 => 20, 3 => 30] ~ interpolate
    a(m) => m(2.5) ~ track
end

instance(S)
S
context=<Context>
config=<Config>
m=3-element extrapolate(interpolate((::Vector{Int64},), ::Vector{Int64}, Gridded(Linear())), Throw()) with element type Float64:…
a=25.0

A matrix can also be used instead of a vector of pairs.

@system S(Controller) begin
    m => [1 10; 2 20; 3 30] ~ interpolate
    a(m) => m(2.5) ~ track
end

instance(S)
S
context=<Context>
config=<Config>
m=3-element extrapolate(interpolate((::Vector{Int64},), ::Vector{Int64}, Gridded(Linear())), Throw()) with element type Float64:…
a=25.0


Equation solving

solve

solve variables solve a polynomial equation symbolically. By default, it will return the highest solution. Therefore, when using the lower tag, it is recommended to pair it with another tag.

Supported tags: unit, lower, upper, pick

Example

The solution is x = 1, 2, 3

@system S(Controller) begin
    a => 1 ~ preserve(parameter)
    b => -6 ~ preserve(parameter)
    c => 11 ~ preserve(parameter)
    d => -6 ~ preserve(parameter)
    x(a, b, c, d) => begin
        a*x^3 + b*x^2 + c*x + d
    end ~ solve
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=-6.0
c=11.0
d=-6.0
x=3.0


bisect

bisect variables solve a nonlinear equation using the bisection method. The tags lower and upper must be provided.

Supported tags: unit, evalunit, lower, upper, maxiter, tol, min, max

Example

The solution is x = 1, 2, 3

@system S(Controller) begin
    x(x) => x^3 - 6x^2 + 11x - 6 ~ bisect(lower=0, upper=3)
end

instance(S)
S
context=<Context>
config=<Config>
x=3.0


Dynamic structure

produce

produce variables attach a new instance of a dynamically generated system.

Supported tags: single, when

Example

@system S begin
    a => produce(S) ~ produce
end

@system SController(Controller) begin
    s(context) ~ ::S
end

instance(SController)
SController
context=<Context>
config=<Config>
s=<S>


Language extension

hold

hold variables are placeholders for variables that are supplied by another system as a mixin.

Supported tags: None

Example

@system S1 begin
    a ~ advance
end

@system S2(S1, Controller) begin
    a ~ hold
    b(a) => 2*a ~ track
end

simulate(S2; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Float64
10.0 hr0.00.0
21.0 hr1.02.0
32.0 hr2.04.0


wrap

wrap allows passing a reference to the state variable object, not a dereferenced value

Supported tags: None

Example

@system S(Controller) begin
    a          => 1       ~ preserve
    b(a)       => a == a' ~ flag
    c(wrap(a)) => a == a' ~ flag
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=true
c=false


call

call defines a partial function accepting user-defined arguments

Supported tags: unit

Example

@system S(Controller) begin
    a => 1 ~ preserve
    f(a; x) => a + x ~ call
    b(f) => f(1) ~ track
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
f=<call>
b=2.0


bring

bring duplicates variable declaration from another system into the current system

Supported tags: parameters, override

Example

@system S1 begin
    a => 1 ~ preserve
    b(a) => 2a ~ track
end

@system S2(Controller) begin
    c(context) ~ bring::S1
end

instance(S2)
S2
context=<Context>
config=<Config>
a=1.0
b=2.0
c=<S1>


Variable Tags

Most variable states have tags in the form of (tag) for tag-specific behaviors. Available tags vary between variable states. Some tags are shared by multiple variable states while some tags are exclusive to certain variable states.

autounit

Allows provide variables to automatically assign units to variables depending on column headers of the DataFrame. By default, autounit is true and only needs to be specified when false.

Used by: provide

Example

@system S(Controller) begin
    a => DataFrame("index" => (0:2)u"hr", "value (m)" => 0:10:20) ~ provide
    b => DataFrame("index" => (0:2)u"hr", "value (m)" => 0:10:20) ~ provide(autounit=false)
end
Main.S
instance(S).a
3×2 DataFrame
 Row  index      value      Quantity…  Quantity… 
─────┼──────────────────────
   1 │      0 hr        0 m
   2 │      1 hr       10 m
   3 │      2 hr       20 m
instance(S).b
3×2 DataFrame
 Row  index      value (m)  Quantity…  Int64     
─────┼──────────────────────
   1 │      0 hr          0
   2 │      1 hr         10
   3 │      2 hr         20


by

Specifies the column and series from which the drive variable receives data. Can be omitted if the variable name is identical to column name.

Used by: drive

Example

@system S(Controller) begin
    p => DataFrame(index=(0:2)u"hr", a=[2,4,6], x=1:3) ~ provide
    a ~ drive(from=p)
    b ~ drive(from=p, by=:x)
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Float64
10.0 hr2.01.0
21.0 hr4.02.0
32.0 hr6.03.0


columns

Specifies the names of columns from the table created by the tabulate variable.

Used by: tabulate

Example

@system S(Controller) begin
    T => [
      # a b
        0 4 ; # A
        1 5 ; # B
        2 6 ; # C
        3 7 ; # D
    ] ~ tabulate(rows=(:A, :B, :C, :D), columns=(:a, :b))
end

instance(S).T
    a    b   
───┼──────────
 A │ 0.0  4.0
 B │ 1.0  5.0
 C │ 2.0  6.0
 D │ 3.0  7.0


evalunit

Specifies the evaluation unit of bisect, as opposed to the unit of solution.

Used by: bisect

Example

@system S(Controller) begin
    f(x) => (x/1u"s" - 1u"m/s") ~ track(u"m/s")
    x(f) ~ bisect(lower=0, upper=2, u"m", evalunit=u"m/s")
end

instance(S)
S
context=<Context>
config=<Config>
f=0.0 m s^-1
x=1.0 m


extern

Used by: preserve, track, flag

from

drive: Specifies the DataFrame that the drive variable will receive data from. If the variable name of the drive variable differs from column name, from must be accompanied with by.

integrate: Specifies lower bound of integration.

Used by: drive, integrate

Example: drive

@system S(Controller) begin
    p => DataFrame(index=(0:2)u"hr", a=[2,4,6], x=1:3) ~ provide
    a ~ drive(from=p)
    b ~ drive(from=p, by=:x)
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Float64
10.0 hr2.01.0
21.0 hr4.02.0
32.0 hr6.03.0


Example: integrate

@system S(Controller) begin
    w => 1 ~ preserve(parameter)
    a => 0 ~ preserve(parameter)
    b => π ~ preserve(parameter)
    f(w; x) => w*sin(x) ~ integrate(from=a, to=b)
end

instance(S)
S
context=<Context>
config=<Config>
w=1.0
a=0.0
b=3.14159
f=2.0


index

Used by provide variables to specify the index column from provided DataFrame. Can be omitted if DataFrame contains column "index".

Used by: provide

Example

@system S(Controller) begin
    a => DataFrame(i=(0:3)u"hr", value=0:10:30) ~ provide(index=:i)
end

instance(S)
S
context=<Context>
config=<Config>
a=4×2 DataFrame…


init

Assigns the first value of the variable at system instantiation.

Used by: track, remember, accumulate, advance, provide

Example

@system S(Controller) begin
    a => 1 ~ accumulate(init=100)
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr100.0
21.0 hr101.0
32.0 hr102.0


knotunit

Specifies the unit of discrete x-values of interpolate.

Used by: interpolate

Example

@system S(Controller) begin
    m => ([1 => 10, 2 => 20, 3 => 30]) ~ interpolate(u"s", knotunit=u"m")
    n(m) ~ interpolate(u"m", reverse)
    a(m) => m(2.5u"m") ~ track(u"s")
    b(n) => n(25u"s") ~ track(u"m")
end

instance(S)
S
context=<Context>
config=<Config>
m=3-element extrapolate(interpolate((::Vector{Quantity{Int64, 𝐋, FreeUnits{(m,), 𝐋, nothing}}},), ::Vector{Quantity{Int64, 𝐓, FreeUnits{(s,), 𝐓, nothing}}}, Gridded(Linear())), Throw()) with element type Quantity{Float64, 𝐓, FreeUnits{(s,), 𝐓, nothing}}:…
n=3-element extrapolate(interpolate((::Vector{Quantity{Int64, 𝐓, FreeUnits{(s,), 𝐓, nothing}}},), ::Vector{Quantity{Int64, 𝐋, FreeUnits{(m,), 𝐋, nothing}}}, Gridded(Linear())), Throw()) with element type Quantity{Float64, 𝐋, FreeUnits{(m,), 𝐋, nothing}}:…
a=25.0 s
b=2.5 m


lower

Specifies the lower bound of the solution for solve and bisect variables.

Used by: solve, bisect

Example

The solution is x = 1, 2, 3

@system S(Controller) begin
    a => 1 ~ preserve(parameter)
    b => -6 ~ preserve(parameter)
    c => 11 ~ preserve(parameter)
    d => -6 ~ preserve(parameter)
    x(a, b, c, d) => begin
        a*x^3 + b*x^2 + c*x + d
    end ~ solve(lower=1.1, upper=2.9)
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=-6.0
c=11.0
d=-6.0
x=2.0


max

Defines the maximum value of the variable.

Used by: preserve, track, accumulate, bisect

Example

@system S(Controller) begin
    a => 1 ~ accumulate(max=1)
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr0.0
21.0 hr1.0
32.0 hr1.0


maxiter

Defines the maximum number of iterations for the bisect.

Used by: bisect

Example

@system S(Controller) begin
    x(x) => x - 0.25 ~ bisect(lower=0, upper=1, maxiter=4)
end

instance(S)
S
context=<Context>
config=<Config>
x=0.25


min

Defines the minimum value of the variable.

Used by: preserve, track, accumulate, bisect

Example

@system S(Controller) begin
    a => -1 ~ accumulate(min=-1)
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr0.0
21.0 hr-1.0
32.0 hr-1.0


once

Makes a flag variable unable to go from true to false.

Used by: flag

Example

@system S(Controller)begin
    a ~ advance(init=1)
    f(a) => (a % 2 == 0) ~ flag(once)
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeaf
Quantity…Float64Bool
10.0 hr1.0false
21.0 hr2.0true
32.0 hr3.0true


optional

Makes a preserve variable optional, allowing a system to be instantiated without variable assignment.

Used by: preserve

Example

@system S(Controller) begin
    a ~ preserve(optional, parameter)
    b => 1 ~ preserve
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimeb
Quantity…Float64
10.0 hr1.0
21.0 hr1.0
32.0 hr1.0


override

Used by: preserve, track, flag, drive, bring

Example

@system S1 begin
    a ~ track(override)
end

@system S2(Controller) begin
    c(context, a) ~ ::S1
    a => 1 ~ track
end

instance(S2)
S2
context=<Context>
config=<Config>
c=<S1>
a=1.0


parameter

Allows the variable to be altered through a configuration at system instantiation.

Used by: preserve, flag, provide, drive, tabulate, interpolate

Example

@system S(Controller) begin
    a ~ preserve(parameter)
end

instance(S; config = :S => :a => 1)
S
context=<Context>
config=<Config>
a=1.0


parameters

Use by bring variables to duplicate only variables that can have the parameter tag (if they did not have the parameter tag originally, they become parameters regardless). The duplicated variables must have their values reassigned through a configuration.

Used by: bring

Example

@system S1 begin
    a => 1 ~ preserve
    b(a) => 2a ~ track
    c => true ~ flag

    d(a) ~ accumulate
end

@system S2(Controller) begin
    p(context) ~ bring::S1(parameters)
end

instance(S2; config = :S2 => (:a => 2, :b => 3, :c => false))
S2
context=<Context>
config=<Config>
a=2.0
b=3.0
c=0.0
p=<S1>


pick

Picks which solution to return based on tag argument.

Used by: solve

Example

The solution is x = 1, 2, 3

@system S(Controller) begin
    a => 1 ~ preserve(parameter)
    b => -3 ~ preserve(parameter)
    c => 2 ~ preserve(parameter)
    x(a, b, c) => begin
        a*x^2 + b*x + c
    end ~ solve(pick=:minimum)
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=-3.0
c=2.0
x=1.0


ref

Used by: preserve, track

reset

Resets the sum to 0 at every time step.

Used by: accumulate

Example

@system S(Controller) begin
    a => 1 ~ accumulate(reset)
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr0.0
21.0 hr1.0
32.0 hr1.0


reverse

Returns the inverse function of an existing interpolate variable.

Used by: interpolate

Example

@system S(Controller) begin
    m => ([1 => 10, 2 => 20, 3 => 30]) ~ interpolate
    n(m) ~ interpolate(reverse)
    a(m) => m(2.5) ~ preserve
    b(n) => n(25) ~ preserve
end

instance(S)
S
context=<Context>
config=<Config>
m=3-element extrapolate(interpolate((::Vector{Int64},), ::Vector{Int64}, Gridded(Linear())), Throw()) with element type Float64:…
n=3-element extrapolate(interpolate((::Vector{Int64},), ::Vector{Int64}, Gridded(Linear())), Throw()) with element type Float64:…
a=25.0
b=2.5


round

Rounds to the nearest integer or to a floor or ceiling based on tag argument.

Used by: preserve, track

Example

@system S(Controller) begin
    a => 1.4 ~ preserve(round)
    b => 1.4 ~ preserve(round=:round)
    c => 1.4 ~ preserve(round=:ceil)
    d => 1.6 ~ preserve(round=:floor)
    e => 1.6 ~ preserve(round=:trunc)
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=1.0
c=2.0
d=1.0
e=1.0


rows

Specifies the names of rows from the table created by the tabulate variable. Required tag for tabulate.

Used by: tabulate

Example

@system S(Controller) begin
    T => [
      # a b
        0 4 ; # A
        1 5 ; # B
        2 6 ; # C
        3 7 ; # D
    ] ~ tabulate(rows=(:A, :B, :C, :D), columns=(:a, :b))
end
Main.S


single

Used by: produce

skip

Used by: track

Example

@system S(Controller) begin
    a ~ advance
    b(a) => 2*a ~ track(skip=true)
end

simulate(S; stop=2)
3×3 DataFrame
Rowtimeab
Quantity…Float64Float64
10.0 hr0.00.0
21.0 hr1.00.0
32.0 hr2.00.0


step

advance: Specifies the increments of the advance variable.

provide: Specifies the intervals of the index column.

Used by: advance, provide

Example: advance

@system S(Controller) begin
    a ~ advance
    b ~ advance(step=2)
    c ~ advance(step=-1)
end

simulate(S; stop=2)
3×4 DataFrame
Rowtimeabc
Quantity…Float64Float64Float64
10.0 hr0.00.00.0
21.0 hr1.02.0-1.0
32.0 hr2.04.0-2.0


Example: integrate

@system S(Controller) begin
    a => DataFrame("index (hr)" => 0:4, "value (m)" => 0:10:40) ~ provide(step=2u"hr")
end

instance(S).a
3×2 DataFrame
 Row  index      value      Quantity…  Quantity… 
─────┼──────────────────────
   1 │      0 hr        0 m
   2 │      2 hr       20 m
   3 │      4 hr       40 m


tick

Used by: drive

time

Accumulates variable at a specified rate of time.

Used by: accumulate, capture

Example

@system S(Controller) begin
    t(x=context.clock.time) => 0.5x ~ track(u"hr")
    a => 1 ~ accumulate
    b => 1 ~ accumulate(time=t)
end

simulate(S; stop=5)
6×4 DataFrame
Rowtimetab
Quantity…Quantity…Float64Float64
10.0 hr0.0 hr0.00.0
21.0 hr0.5 hr1.00.5
32.0 hr1.0 hr2.01.0
43.0 hr1.5 hr3.01.5
54.0 hr2.0 hr4.02.0
65.0 hr2.5 hr5.02.5


timeunit

Specifies the time unit of the variable.

Used by: accumulate, capture

Example

@system S(Controller) begin
    a => 1 ~ accumulate(timeunit=u"d")
end

simulate(S; stop=2)
3×2 DataFrame
Rowtimea
Quantity…Float64
10.0 hr0.0
21.0 hr0.0416667
32.0 hr0.0833333


to

Specifies upper bound of integration.

Used by: integrate

Example

@system S(Controller) begin
    w => 1 ~ preserve(parameter)
    a => 0 ~ preserve(parameter)
    b => π ~ preserve(parameter)
    f(w; x) => w*sin(x) ~ integrate(from=a, to=b)
end

instance(S)
S
context=<Context>
config=<Config>
w=1.0
a=0.0
b=3.14159
f=2.0


tol

Defines the tolerance for the bisection method used in bisect.

Used by: bisect

Example

@system S(Controller) begin
    x(x) => x - 2.7 ~ bisect(lower=1, upper=3)
    y(y) => y - 2.7 ~ bisect(lower=1, upper=3, tol=0.05)
end

instance(S)
S
context=<Context>
config=<Config>
x=2.7
y=2.71875


unit

Specifies the unit of the variable. The tag unit can be omitted.

Used by: preserve, track, remember, accumulate, capture, integrate, advance, drive, tabulate, interpolate, solve, bisect, call

@system S(Controller) begin
    a => 1 ~ preserve(unit=u"hr")
    b => 1 ~ preserve(u"hr")
end

instance(S)


upper

Specifies the upper bound of solution.

Used by: solve, bisect

Example

The solution is x = 1, 2, 3

@system S(Controller) begin
    a => 1 ~ preserve(parameter)
    b => -6 ~ preserve(parameter)
    c => 11 ~ preserve(parameter)
    d => -6 ~ preserve(parameter)
    x(a, b, c, d) => begin
        a*x^3 + b*x^2 + c*x + d
    end ~ solve(upper=2.9)
end

instance(S)
S
context=<Context>
config=<Config>
a=1.0
b=-6.0
c=11.0
d=-6.0
x=2.0


when

Specifies when a variable should be evaluated. It is supplied with a flag variable, and the specified variable is evaluated when the flag variable is true.

Used by: track, flag, remember, accumulate, capture, produce

Example

@system S(Controller) begin
    a ~ advance
    flag(a) => (a >= 2) ~ flag
    b(a) => a ~ track(when=flag)
end

simulate(S; stop=3u"hr")
4×4 DataFrame
Rowtimeaflagb
Quantity…Float64BoolFloat64
10.0 hr0.0false0.0
21.0 hr1.0false0.0
32.0 hr2.0true2.0
43.0 hr3.0true3.0