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 bycallandintegrate.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;timeandtickfromClock.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; essentiallytrackturning 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
SCropbox.@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 = 2julia> @config :S => :a => 1, :S => :a => 2
Config for 1 system:
S
a = 2julia> @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>