# How-to Guide 4: Work with Timesteps, Parameters, and Variables

## Timesteps and available functions

An `AbstractTimestep`

i.e. a `FixedTimestep`

or a `VariableTimestep`

is a type defined within Mimi in "src/time.jl". It is used to represent and keep track of time indices when running a model.

In the `run_timestep`

functions which the user defines, it may be useful to use any of the following functions, where `t`

is an `AbstractTimestep`

object:

```
is_first(t) # returns true or false, true if t is the first timestep to be run for the respective component
is_last(t) # returns true or false, true if t is the last timestep to be run for the respective component
gettime(t) # returns the year represented by timestep t
```

There are also two helper types `TimestepValue`

and `TimestepIndex`

that can be used with comparison operators (`==`

, `<`

, and `>`

) to check whether an `AbstractTimestep`

`t`

during the `run_timestep`

function corresponds with a certain year or index number. For example:

```
if t > TimestepValue(2020)
# run this code only for timesteps after the year 2020
end
if t == TimestepIndex(3)
# run this code only during the third timestep
end
```

See below for further discussion of the `TimestepValue`

and `TimestepIndex`

objects and how they should be used.

The API details for AbstractTimestep object `t`

are as follows:

- you may index into a variable or parameter with
`[t]`

or`[t +/- x]`

as usual - to access the time value of
`t`

(currently a year) as a`Number`

, use`gettime(t)`

- useful functions for commonly used conditionals are
`is_first(t)`

and`is_last(t)`

- to access the index value of
`t`

as a`Number`

representing the position in the time array, use`t.t`

. Users are encouraged to avoid this access, and instead use comparisons with`TimestepIndex`

objects to check if an AbstractTimestep`t`

corresponds with a specific index number, as described above.

Indexing into a variable or parameter's `time`

dimension with an `Integer`

is deprecated and will soon error. Instead, users should take advantage of the `TimestepIndex`

and `TimestepValue`

types. For examples we will refer back to our component definition above, and repeated below.

```
@defcomp MyComponentName begin
regions = Index()
A = Variable(index = [time])
B = Variable(index = [time, regions])
c = Parameter()
d = Parameter(index = [time])
e = Parameter(index = [time, regions])
f = Parameter(index = [regions])
function run_timestep(p, v, d, t)
v.A[t] = p.c + p.d[t]
for r in d.regions
v.B[t, r] = p.f[r] * p.e[t, r]
end
end
end
```

`TimestepIndex`

has one field, `index`

, which refers to the absolute index in the parameter or variable array's `time`

dimension. Thus, constructing a `TimestepIndex`

is done by simply writing `TimestepIndex(index::Int)`

. Looking back at our original component example, one could modify the first line of `run_timestep`

to always refer to the first timestep of `p.d`

with the following. One may index into the `time`

dimension with a single `TimestepIndex`

, or an `Array`

of them.

`v.A[t] = p.c + p.d[TimestepIndex(1)]`

`TimestepValue`

has two fields, `value`

and `offset`

, referring to the value within the `time`

dimension and an optional `offset`

from that `value`

. Thus, constructing a `TimestepValue`

is done either by writing `TimestepValue(value)`

, with an implied offset of 0, or `TimestepValue(value, offset = i::Int)`

, with an explicit offset of i. One may index into the `time`

dimension with a single `TimestepValue`

, or an `Array`

of them. For example, you can use a `TimestepValue`

to keep track of a baseline year.

`v.A[t] = p.c + p.d[TimestepValue(2000)]`

You may also use shorthand to create arrays of `TimestepIndex`

using Colon syntax.

```
TimestepIndex(1):TimestepIndex(10) # implicit step size of 1
TimestepIndex(1):2:TimestepIndex(10) # explicit step of type Int
```

Both `TimestepIndex`

and `TimestepArray`

have methods to support addition and subtraction of integers. Note that the addition or subtraction is relative to the definition of the `time`

dimension, so while `TimestepIndex(1) + 1 == TimestepIndex(2)`

, `TimestepValue(2000) + 1`

could be equivalent to `TimestepValue(2001)`

**if** 2001 is the next year in the time dimension, or `TimestepValue(2005)`

if the array has a step size of 5. Hence adding or subtracting is relative to the definition of the `time`

dimension.

## DataType specification of Parameters and Variables

By default, the Parameters and Variables defined by a user will be allocated storage arrays of type `Float64`

when a model is constructed. This default "number_type" can be overriden when a model is created, with the following syntax:

`m = Model(Int64) # creates a model with default number type Int64`

But you can also specify individual Parameters or Variables to have different data types with the following syntax in a `@defcomp`

macro:

```
@defcomp example begin
p1 = Parameter{Bool}() # ScalarModelParameter that is a Bool
p2 = Parameter{Bool}(index = [regions]) # ArrayModelParameter with one dimension whose eltype is Bool
p3 = Parameter{Matrix{Int64}}() # ScalarModelParameter that is a Matrix of Integers
p4 = Parameter{Int64}(index = [time, regions]) # ArrayModelParameter with two dimensions whose eltype is Int64
end
```

If there are "index"s listed in the Parameter definition, then it will be an `ArrayModelParameter`

whose `eltype`

is the type specified in the curly brackets. If there are no "index"s listed, then the type specified in the curly brackets is the actual type of the parameter value, and it will be represent by Mimi as a `ScalarModelParameter`

.

## More on parameter indices

As mentioned above, a parameter can have no index (a scalar), or one or multiple of the model's indexes. A parameter can also have an index specified in the following ways:

```
@defcomp MyComponent begin
p1 = Parameter(index=[4]) # an array of length 4
p2 = Parameter{Array{Float64, 2}}() # a two dimensional array of unspecified length
end
```

In both of these cases, the parameter's values are stored of as an array (p1 is one dimensional, and p2 is two dimensional). But with respect to the model, they are considered "scalar" parameters, simply because they do not use any of the model's indices (namely 'time', or 'regions').

## Updating an external parameter

When `set_param!`

is called, it creates an external parameter by the name provided, and stores the provided scalar or array value. It is possible to later change the value associated with that parameter name using the functions described below.

`update_param!(m, :ParameterName, newvalues)`

Note here that `newvalues`

must be the same type (or be able to convert to the type) of the old values stored in that parameter, and the same size as the model dimensions indicate. Also note that it if you have updated the time dimension of the model with `set_dimension!(m, :time, values)`

you will need to update all parameters with a `:time`

dimension, **even if the values have not changed**, so that the model can update the underlying time labels attached to the parameters.

#### Setting parameters with a dictionary

In larger models it can be beneficial to set some of the external parameters using a dictionary of values. To do this, use the following function:

`set_leftover_params!(m, parameters)`

Where `parameters`

is a dictionary of type `Dict{String, Any}`

where the keys are strings that match the names of the unset parameters in the model, and the values are the values to use for those parameters.

## Using NamedArrays for setting parameters

When a user sets a parameter, Mimi checks that the size and dimensions match what it expects for that component. If the user provides a NamedArray for the values, Mimi will further check that the names of the dimensions match the expected dimensions for that parameter, and that the labels match the model's index values for those dimensions. Examples of this can be found in "test/test*parameter*labels.jl".