Values and Units

Most Cropbox models need only @system, @config, simulate, and visualize. The functions on this page become useful when Julia code crosses the boundary between a Cropbox state and an ordinary value, when data enters or leaves a unit-aware model.

Read current values with value

A field of a system is usually a Cropbox state object. Read its stored value with either postfix ' or value:

s.mass'
value(s.mass)

value(x) has these public conveniences:

  • a Cropbox state returns its current stored value;
  • a vector of states returns a vector of their values;
  • an ordinary value is returned unchanged;
  • a Cropbox Plot wrapper returns its backend object;
  • a Gather returns its store.

Postfix ' is concise in the REPL. value is easier to read in functions, callbacks, and teaching material. Reading a value does not update the model.

There is also an inspection-oriented call form:

value(instance, :variable, explicit_arguments...; dependency_overrides...)
value(SystemType, :variable, explicit_arguments...; dependency_values...)

It evaluates the declaration body without storing the result. This is intended for checking preserve, track, and call equations. It is not a general replacement for update!: cumulative, event, and solver behaviors have lifecycle rules that a body-only evaluation cannot reproduce.

Remove a dependency's unit with nounit

Inside a declaration, Cropbox normally unwraps a dependency to its unit-aware value. nounit(state) instead passes its plain numerical magnitude:

@system UnitBoundary(Controller) begin
    length => 1                      ~ preserve(u"m")
    length_number(nounit(length))    ~ track
    length_cm(nounit(length, u"cm")) ~ track
end

The first numerical result is 1; the second is 100. Supplying a unit first converts to that unit and then removes it.

Use nounit at a deliberate boundary to code that cannot accept Unitful quantities. Keep units through the rest of the model so dimensional errors are still detected. The output option simulate(...; nounit=true) is different: it strips units only after the simulation has finished.

Convert values with unitfy and deunitfy

unitfy(value, unit)
deunitfy(value)
deunitfy(value, unit)

unitfy attaches a unit to a plain value or converts an existing compatible quantity. deunitfy(value) removes units without changing the displayed magnitude. deunitfy(value, unit) converts first and then removes the unit.

using Cropbox

(
    attached = unitfy(2, u"m"),
    converted = unitfy(2u"m", u"cm"),
    stripped = deunitfy(2u"m"),
    stripped_cm = deunitfy(2u"m", u"cm"),
)
(attached = 2 m, converted = 200 cm, stripped = 2, stripped_cm = 200)

Scalars, arrays, tuples, and ranges are supported. nothing and missing are preserved. Incompatible dimensions raise a Unitful error rather than being silently reinterpreted. Temperature ranges are handled as affine units, so use unitfy instead of multiplying a numeric range by an offset unit.

DataFrame round trips

deunitfy(df) strips each unit-aware column and records its unit in the column name. unitfy(df) reads those suffixes back:

using DataFrames

df = DataFrame(time = (0:2)u"d", mass = [0, 2, 5]u"g")
plain = deunitfy(df)
restored = unitfy(plain)

(plain = plain, restored = restored)
(plain = 3×2 DataFrame
 Row  time (d)  mass (g) 
      Int64     Int64    
─────┼────────────────────
   1 │        0         0
   2 │        1         2
   3 │        2         5, restored = 3×2 DataFrame
 Row  time       mass      
      Quantity…  Quantity… 
─────┼──────────────────────
   1 │       0 d        0 g
   2 │       1 d        2 g
   3 │       2 d        5 g)

A suffix such as "mass (g)" is read as a unit. A suffix beginning with a colon, such as "date (:Date)", is read as a type constructor instead. Date has a built-in mapping; other constructors must be available in the evaluation scope or supplied as keywords to unitfy.

This convention is used by the built-in CSV stores. Treat column names as a file-format contract: changing or removing the final parenthesized suffix changes how the data is parsed.

Where these APIs appear

NeedAPIWorked example
read a model fieldvalue or 'Quick Start and inspection workflow
exchange CSV data with unitsunitfy, deunitfyconfiguration workflow and built-in stores
inspect a state-dependent formulavalue(system, :name, ...)advanced debugging only

Manual updates are covered by Simulation. Stochastic parameter values are covered by Configure Models and Scenarios. For and wrap, see Behaviors and Tags. Dynamic child systems and traversal are documented in Dynamic Hierarchies.