Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ makedocs(
"2 Explore Results" => "howto/howto_2.md",
"3 Monte Carlo + SA" => "howto/howto_3.md",
"4 Timesteps, Params, and Vars" => "howto/howto_4.md",
"5 Port to v0.5.0" => "howto/howto_5.md",
"6 Port to v1.0.0" => "howto/howto_6.md"
"5 Dimensions" => "howto/howto_5.md,",
"6 Port to v0.5.0" => "howto/howto_6.md",
"7 Port to v1.0.0" => "howto/howto_7.md"
],
"Advanced How-to Guides" => Any[
"Advanced How-to Guides Intro" => "howto_advanced/howto_adv_main.md",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/howto/howto_4.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ When `set_param!` is called, it creates an external parameter by the name provid
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.
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.

#### Setting parameters with a dictionary

Expand Down
158 changes: 60 additions & 98 deletions docs/src/howto/howto_5.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,69 @@
# How-to Guide 5: Port to Mimi v0.5.0
# How-to Guide 5: Update the Time Dimension

The release of Mimi v0.5.0 is a breaking release, necessitating the adaptation of existing models' syntax and structure in order for those models to run on this new version. This guide provides an overview of the steps required to get most models using the v0.4.0 API working with v0.5.0. It is **not** a comprehensive review of all changes and new functionalities, but a guide to the minimum steps required to port old models between versions. For complete information on the new version and its functionalities, see the full documentation.

This guide is organized into six main sections, each descripting an independent set of changes that can be undertaken in any order desired.

1) Defining components
2) Constructing a model
3) Running the model
4) Accessing results
5) Plotting
6) Advanced topics

**A Note on Function Naming**: There has been a general overhaul on function names, especially those in the explicity user-facing API, to be consistent with Julia conventions and the conventions of this Package. These can be briefly summarized as follows:

- use `_` for readability
- append all functions with side-effects, i.e., non-pure functions that return a value but leave all else unchanged with a `!`
- the commonly used terms `component`, `variable`, and `parameter` are shortened to `comp`, `var`, and `param`
- functions that act upon a `component`, `variable`, or `parameter` are often written in the form `[action]_[comp/var/param]`

## Defining Components

The `run_timestep` function is now contained by the `@defcomp` macro, and takes the parameters `p, v, d, t`, referring to Parameters, Variables, and Dimensions of the component you defined. The fourth argument is an `AbstractTimestep`, i.e., either a `FixedTimestep` or a `VariableTimestep`. Similarly, the optional `init` function is also contained by `@defcomp`, and takes the parameters `p, v, d`. Thus, as described in the user guide, defining a single component is now done as follows:

In this version, the fourth argument (`t` below) can no longer always be used simply as an `Int`. Indexing with `t` is still permitted, but special care must be taken when comparing `t` with conditionals or using it in arithmatic expressions. The full API as described later in this document in **Advanced Topics: Timesteps and available functions**. Since differential equations are commonly used as the basis for these models' equations, the most commonly needed change will be changing `if t == 1` to `if is_first(t)`

```julia
@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
A runnable model necessarily has a `time` dimension, originally set with the following call, but in some cases it may be desireable to alter this dimension by calling the following on a model which already has a time dimension set.
```
set_dimension!(m, :time, time_keys)
```

## Constructing a Model

In an effort to standardize the function naming protocol within Mimi, and to streamline it with the Julia convention, several function names have been changed. The table below lists a **subset** of these changes, focused on the exported API functions most commonly used in model construction.

| Old Syntax | New Syntax |
| ------------------------ |:-------------------------:|
|`addcomponent!` |`add_comp!` |
|`connectparameter` |`connect_param!` |
|`setleftoverparameters` |`set_leftover_params!` |
|`setparameter` |`set_param!` |
|`adddimension` |`add_dimension!` |
|`setindex` |`set_dimension!` |

Changes to various optional keyword arguments:

- `add_comp!`: Through Mimi v0.9.4, the optional keyword arguments `first` and `last` could be used to specify times for components that do not run for the full length of the model, like this: `add_comp!(mymodel, ComponentC; first=2010, last=2100)`. This functionality is currently disabled, and all components must run for the full length of the model's time dimension. This functionality may be re-implemented in a later version of Mimi.

## Running a Model

## Accessing Results

## Plotting and the Explorer UI

This release of Mimi does not include the plotting functionality previously offered by Mimi. While the previous files are still included, the functions are not exported as efforts are made to simplify and improve the plotting associated with Mimi.

The new version does, however, include a new UI tool that can be used to visualize model results. This `explore` function is described in the User Guide under **Advanced Topics**.

## Advanced Topics

#### Timesteps and available functions

As previously mentioned, some relevant function names have changed. These changes were made to eliminate ambiguity. For example, the new naming clarifies that `is_last` returns whether the timestep is on the last valid period to be run, not whether it has run through that period already. This check can still be achieved with `is_finished`, which retains its name and function. Below is a subset of such changes related to timesteps and available functions.

| Old Syntax | New Syntax |
| ------------------------ |:-------------------------:|
|`isstart` |`is_first` |
|`isstop` |`is_last` |

As mentioned in earlier in this document, the fourth argument in `run_timestep` is an `AbstractTimestep` i.e. a `FixedTimestep` or a `VariableTimestep` and is a type defined within Mimi in "src/time.jl". In this version, the fourth argument (`t` below) can no longer always be used simply as an `Int`. Defining the `AbstractTimestep` object as `t`, indexing with `t` is still permitted, but special care must be taken when comparing `t` with conditionals or using it in arithmatic expressions. Since differential equations are commonly used as the basis for these models' equations, the most commonly needed change will be changing `if t == 1` to `if is_first(t)`.

The full API:

- 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 the options listed above or a separate counter variable. each time the function gets called.

#### Parameter connections between different length components

#### More on parameter indices

#### Updating an external parameter
----
#### For example, one may wish to replace the FUND model's climate module with a different one, such as FAIR:

To update an external parameter, use the functions `update_param!` and `udpate_params!` (previously known as `update_external_parameter` and `update_external_parameters`, respectively.) Their calling signatures are:
For the purposes of this guide we focus on the first step of such modification. Since FUND runs yearly from 1950 to 3000 and FAIR yearly from 1765 to 2500, our modified model will need to run yearly from 1765 to 1950.

* `update_params!(md::ModelDef, parameters::Dict; update_timesteps = false)`
We start with FUND
```
using Mimi
using MimiFUND
m = MimiFUND.get_model()
```
where `MimiFUND.get_model` includes the call `set_dimension!(m, time, 1950:3000)`.

* `update_param!(md::ModelDef, name::Symbol, value; update_timesteps = false)`
----
#### Now we need to change the `time` dimension to be 1765 to 2500:

For external parameters with a `:time` dimension, passing `update_timesteps=true` indicates that the time _keys_ (i.e., year labels) should also be updated in addition to updating the parameter values.
Before we do so, note some important rules and precautions. These are in place to avoid unexpected behavior, complications, or incorrect results caused by our under-the-hood assumptions, but if a use case arises where these are prohibitive please get in touch on the [forum](https://forum.mimiframework.org) and we can help you out.

#### Setting parameters with a dictionary
- The new time dimension cannot start later than the original time dimension.
- The new time dimension cannot end before the start of the original time dimension ie. it cannot completely exclude all times in the original time dimension.
- The new time dimension must use the same timestep lengths as the original dimension.

The function `set_leftover_params!` replaces the function `setleftoverparameters`.
----
#### We now go ahead and change the `time` dimension to be 1765 to 2500:
```
set_dimension!(m, :time, 1765:2500)
```
At this point the model `m` can be run, and will run from 1765 to 2500 (Try running it and looking at `explore(m)` for parameters and variables with a `time` dimension!). In fact, we could start adding FAIR components to the model, which would automatically take on the entire model time dimension, ie.
```
add_comp!(m, FAIR_component) # will run from 1765 to 1950
```
**However**, the FUND components will only run in the subset of years 1950 to 2500, using the same parameter values each year was previously associated with, and containing placeholder `missing` values in the parameter value spots from 1765 to 1949. More specifically:

- The model's `time` dimension values are updated, and it will run for each year in the new 1765:1950 dimension.
```
julia> Mimi.time_labels(m)
736-element Vector{Int64}: [1765, 1766, 1767, … 2498, 2499, 2500]
```
- The components `time` dimension values are updated, but (1) the components maintain the `first` year as set implicitly by the original `time` dimension (1950) so the run period start year does not change and (2) they maintain their `last` year as set implicitly by the original `time` dimension, unless that year is now later than the model's last year, in which case it is trimmed back to the `time` dimensions last year (2500). Thus, the components will run for the same run period, or a shorter one if the new time dimension ends before the component used to (in this case 1950:2500).
```
julia> component = m.md.namespace[:emissions] # get component def(ignore messy internals syntax)
julia> component.dim_dict[:time]
[1765, 1766, 1767, … 2498, 2499, 2500]
julia> component.first
1950
julia> component.last
2500
```
- All external parameters are trimmed and padded as needed so the model can still run, **and the values are still linked to their original years**. More specifically, if the new time dimension ends earlier than the original one than the parameter value vector/matrix is trimmed at the end. If the new time dimension starts earlier than the original, or ends later, the parameter values are padded with `missing`s at the front and/or back respectively.
```
julia> parameter_values = Mimi.external_params(m)[:currtaxn2o].values.data # get param values for use in next run (ignore messy internals syntax)
julia> size(parameter_values)
(736, 16)
julia> parameter_values[1:(1950-1765),:] # all missing
julia> parameter_values[(1950-1764),:] # hold set values
```

----
#### The following options are now available for further modifcations if this end state is not desireable:

- If you want to update a component's run period, you may use the function `Mimi.set_first_last!(m, :ComponentName, first = new_first, last = new_last)` to specify when you want the component to run.
- You can update external parameters to have values in place of the assumed `missing`s using the `update_param!(m, :ParameterName, values)` function
Loading