Declaration
Cropbox.@system
— Macro@system name[{patches..}][(mixins..)] [<: type] [decl] -> Type{<:System}
Declare a new system called name
with new variables declared in decl
block using a custom syntax. The resultant system is subtype of System
or a custom type
. mixins
allows reusing specification of existing systems to be pasted into the declaration of new system. patches
may provide type substitution and/or constant definition needed for advanced use.
Variable
name[(args..; kwargs..)][: alias] [=> expr] [~ [state][::type|<:type][(tags..)]]
name
: variable name; usually short abbreviation.args
: automatically bound depending variableskwargs
: custom bound depending variables; used bycall
andintegrate
.alias
: alternative name; long description.expr
: state-specific code snippet; use begin-end block for multiple statements.type
: internal data type; default is Float64 for many, but not all, variables.tags
: state-specific options;unit
,min
/max
, etc.
States
hold
: marks a placeholder for variable shared between mixins.wrap
: passes a state variable to other fucnction as is with no unwrapping its value.advance
: manages a time-keeping variable;time
andtick
fromClock
.preserve
: keeps initially assigned value with no further updates; constants, parameters.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.track
: evaluates variable expression as is for each update.remember
: keeps tracking variable until a certain condition is met; essentiallytrack
turning intopreserve
.provide
: manages a table of time-series in DataFrame.drive
: fetches the current value from a time-series; maybe supplied byprovide
.call
: defines a partial function bound with some variables.integrate
: calculates integral using Gaussian method; not for time domain.accumulate
: emulates integration of rate variable over time; essentially Euler method.capture
: calculates difference between integration for each time step.flag
: sets a boolean flag; essentiallytrack::Bool
.produce
: attaches a new instance of system dynamically constructed; i.e. root structure growth.bisect
: solves nonlinear equation using bisection method; i.e. gas-exchange model coupling.solve
: solves polynomial equation symbolically; i.e. quadratic equations in photosynthesis model.
Examples
julia> @system S(Controller) begin
a => 1 ~ preserve(parameter)
b(a) ~ accumulate
end
S
Cropbox.@config
— Macro@config c.. -> Config | Vector{Config}
Construct a set or multiple sets of configuration.
A basic unit of configuration for a system S
is represented by a pair in the form of S => pv
. System name S
is expressed in a symbol. If actual type of system is used, its name will be automatically converted to a symbol.
A parameter name and corresponding value is then represented by another pair in the form of p => v
. When specifiying multiple parameters, a tuple of pairs like (p1 => v1, p2 => v2)
or a named tuple like (p1 = v1, p2 = v2)
can be used. Parameter name must be a symbol and should indicate a variable declared with parameter
tag as often used by preserve
state variable. For example, :S => (:a => 1, :b => 2)
has the same meaning as S => (a = 1, b = 2)
in the same scope.
Configurations for multiple systems can be concatenated by a tuple. Multiple elements in c
separated by commas implicitly forms a tuple. For example, :S => (:a => 1, :b => 2), :T => :x => 1
represents a set of configuration for two systems S
and T
with some parameters. When the same names of system or variable appears again during concatenation, it will be overriden by later ones in an order appeared in a tuple. For example, :S => :a => 1, :S => :a => 2
results into :S => :a => 2
. Instead of commas, +
operator can be used in a similar way as (:S => :a => 1) + (:S => :a => 2)
. Note parentheses placed due to operator precedence.
When multiple sets of configurations are needed, as in configs
for simulate
, a vector of Config
is used. This macro supports some convenient ways to construct a vector by composing simpler configurations. Prefix operator !
allows expansion of any iterable placed in the configuration value. Infix operator *
allows multiplication of a vector of configurations with another vector or a single configuration to construct multiple sets of configurations. For example, !(:S => :a => 1:2)
is expanded into two sets of separate configurations [:S => :a => 1, :S => :a => 2]
. (:S => :a => 1:2) * (:S => :b => 0)
is multiplied into [:S => (a = 1, b = 0), :S => (a = 2, b = 0)]
.
Examples
julia> @config :S => (:a => 1, :b => 2)
Config for 1 system:
S
a = 1
b = 2
julia> @config :S => :a => 1, :S => :a => 2
Config for 1 system:
S
a = 2
julia> @config !(:S => :a => 1:2)
2-element Vector{Config}:
<Config>
<Config>
julia> @config (:S => :a => 1:2) * (:S => :b => 0)
2-element Vector{Config}:
<Config>
<Config>