Advanced How-to Guide: Build and Init Functions

In some cases, it may be useful for a user to better understand the inner-workings of the internal build function, as well as the ModelInstance and ModelDef types. In addition, components with one-time computations irrespective of timesteps may lend themselves to the use of the optional init function, as described below.

The Internal 'build' Function and Model Instances

The structural definition of a Model is held in the mutable ModelDef, and then when you call the run function on your model, first the internal build function is called, which produces a ModelInstance, and then the ModelInstance is run. A model instance is an instantiated version of the model you have designed where all of the component constructors have been called and all of the data arrays have been allocated. If you wish to create and run multiple versions of your model, you can use the intermediate build function and store the separate ModelInstances. This may be useful if you want to change some parameter values, while keeping the model's structure mostly the same. For example:

instance1 = Mimi.build(m)
run(instance1)

update_param!(m, ParameterName, newvalue)
instance2 = Mimi.build(m)
run(instance2)

result1 = instance1[:Comp, :Var]
result2 = instance2[:Comp, :Var]

Note that you can retrieve values from a ModelInstance in the same way you index into a model.

The init function

The init function can optionally be called within @defcomp and before run_timestep. Similarly to run_timestep, this function is called with parameters init(p, v, d), where the component state (defined by the first three arguments) has fields for the Parameters, Variables, and Dimensions of the component you defined.

If defined for a specific component, this function will run before the timestep loop, and should only be used for parameters or variables without a time index e.g. to compute the values of scalar variables that only depend on scalar parameters. Note that when using init, it may be necessary to add special handling in the run_timestep function for the first timestep, in particular for difference equations. A skeleton @defcomp script using both run_timestep and init would appear as follows:

@defcomp component1 begin

    # First define the state this component will hold
    savingsrate = Parameter()

    # Second, define the (optional) init function for the component
    function init(p, v, d)
    end

    # Third, define the run_timestep function for the component
    function run_timestep(p, v, d, t)
    end

end