Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6f40854
WIP - preliminary work on updating set_param! functionality
rjplevin Dec 19, 2019
4bbda5e
Merge branch 'master' into set-param-update
rjplevin Dec 19, 2019
5beebb4
WIP, debuggin param import in composites
rjplevin Jan 9, 2020
bf979c1
Merge branch 'master' into set-param-update
rjplevin Jan 9, 2020
3cdbed7
WIP - changes to @defcomposite
rjplevin Jan 21, 2020
6d751af
WIP - Rewrite test_defcomposite.jl for new syntax.
rjplevin Jan 22, 2020
2c5fa12
WIP -- revising composite imports and par/var access
rjplevin Feb 3, 2020
21234be
Merge branch 'master' into set-param-update
rjplevin Feb 3, 2020
3d21807
WIP -- handling of imports and revised @defcomposite syntax
rjplevin Feb 6, 2020
3212d03
WIP - test_composite_simple2.jl now runs.
rjplevin Feb 15, 2020
9f00c20
Merge commit '197dc69624dd065ff1256efdb459375556be85c9' into set-para…
rjplevin Feb 15, 2020
f086e40
WIP debugging
rjplevin Feb 17, 2020
55768be
Debugging
rjplevin Feb 17, 2020
7d8a9de
WIP - working through tests
rjplevin Feb 20, 2020
22db122
WIP - Almost all tests are working. A few in test_parametertypes.jl a…
rjplevin Feb 21, 2020
d341fc4
Removed stale comment
rjplevin Feb 21, 2020
c7bdfb4
Wrap long lines
rjplevin Feb 22, 2020
518a1af
Added implementation suggestions to types/defs.jl
rjplevin Feb 25, 2020
ae0edd8
Fix bug in testing for colon support
lrennels Feb 25, 2020
fb8dee4
Merge branch 'set-param-update' into colon-bug-v2
lrennels Feb 26, 2020
7389420
Revised internals/composite_components.md
rjplevin Feb 26, 2020
9d7bb85
Updated internals doc
rjplevin Feb 28, 2020
d07b2c5
Merge branch 'master' into set-param-update
rjplevin Feb 28, 2020
19e21c6
Merge branch 'set-param-update' into colon-bug-v2
lrennels Feb 28, 2020
75aa1a3
Update Project.toml
lrennels Feb 28, 2020
8a2fa96
hacky fix for setting multiple parameters
corakingdon Mar 3, 2020
d0594fb
Update FUND and DICE2010 to mimi-next
lrennels Mar 3, 2020
d19a5ea
Merge branch 'set-param-update' of https://github.com/mimiframework/M…
lrennels Mar 3, 2020
e1c534a
Test MimiRICE2010 on v2.0.3
lrennels Mar 3, 2020
45f98da
Update RICE dependency path to mimi-next fork
lrennels Mar 3, 2020
b5d4426
Merge master
lrennels Mar 3, 2020
61f2d9f
Fix test in timesteparrays
lrennels Mar 4, 2020
3fcee37
Lower compat version for VegaLite
lrennels Mar 4, 2020
948b4a2
Merge branch 'set-param-update' into colon-bug-v2
lrennels Mar 4, 2020
e89e030
Merge pull request #660 from mimiframework/colon-bug-v2
lrennels Mar 4, 2020
de35c51
merge master
lrennels Mar 5, 2020
fb72803
Update test_all_models.jl script
davidanthoff Mar 12, 2020
c6a097e
Merge branch 'set-param-update' of https://github.com/mimiframework/M…
lrennels Mar 12, 2020
323fe94
Add DICE2013 mimi-next branch
lrennels Mar 12, 2020
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ProgressMeter = "1.2"
StatsBase = "0.32"
StringBuilders = "0.2"
TableTraits = "0.4.1, 1"
VegaLite = "1, 2.0"
VegaLite = "1"
julia = "1.2"

[extras]
Expand Down
22 changes: 11 additions & 11 deletions contrib/test_all_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@


packages_to_test = [
"MimiDICE2010",
"MimiDICE2013",
"MimiRICE2010",
"MimiFUND",
"MimiPAGE2009",
"MimiDICE2016" => ("https://github.com/AlexandrePavlov/MimiDICE2016.jl", "master"),
"MimiSNEASY" => ("https://github.com/anthofflab/MimiSNEASY.jl", "master"),
"MimiFAIR" => ("https://github.com/anthofflab/MimiFAIR.jl", "master"),
"MimiMAGICC" => ("https://github.com/anthofflab/MimiMAGICC.jl", "master"),
"MimiHECTOR" => ("https://github.com/anthofflab/MimiHECTOR.jl", "master")
"MimiDICE2010" => ("https://github.com/anthofflab/MimiDICE2010.jl", "mimi-next"),
"MimiDICE2013" => ("https://github.com/lrennels/MimiDICE2013.jl.git", "mimi-next"),
"MimiRICE2010" => ("https://github.com/lrennels/mimi-rice-2010.jl", "mimi-next"),
"MimiFUND" => ("https://github.com/fund-model/MimiFUND.jl", "mimi-next"),
"MimiPAGE2009" => ("https://github.com/fund-model/MimiFUND.jl", "mimi-next"),
# "MimiDICE2016" => ("https://github.com/AlexandrePavlov/MimiDICE2016.jl", "master"),
# "MimiSNEASY" => ("https://github.com/anthofflab/MimiSNEASY.jl", "master"),
# "MimiFAIR" => ("https://github.com/anthofflab/MimiFAIR.jl", "master"),
# "MimiMAGICC" => ("https://github.com/anthofflab/MimiMAGICC.jl", "master"),
# "MimiHECTOR" => ("https://github.com/anthofflab/MimiHECTOR.jl", "master")
]

using Pkg
Expand All @@ -30,7 +30,7 @@ mktempdir() do folder_name
pkg_that_errored = []
Pkg.activate(folder_name)

Pkg.develop(PackageSpec(path=joinpath(homedir(), ".julia", "dev", "Mimi")))
Pkg.develop(PackageSpec(path=joinpath(@__DIR__, "..")))

Pkg.add([i isa Pair ? PackageSpec(url=i[2][1], rev=i[2][2]) : PackageSpec(i) for i in packages_to_test])

Expand Down
189 changes: 145 additions & 44 deletions docs/src/internals/composite_components.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,183 @@
# Composite Components

## Goals
## Overview

In Mimi v0.4, we have two levels of model elements, (i) `Model` and (ii) `Component`. (For now, we ignore the distinction between model / component "definition" and "instance" types.) The primary goal of the `MetaComponent` construct is to extend this structure recursively to _N_ levels, by allowing a `MetaComponent` to contain other `MetaComponent`s as well as `LeafComponent`s, which cannot contain other components.
This document describes the core data structures used to implement in Mimi 1.0.

## Major elements
This suggests three types of elements:
Prior versions of Mimi supported only "flat" models, i.e., with one level of components. The current version supports mulitple layers of components, with some components being "final" or leaf components, and others being "composite" components which themselves contain other leaf or composite components. This approach allows for a cleaner organization of complex models, and allows the construction of building blocks that can be re-used in multiple models.

1. `LeafComponent(Def|Instance)` -- equivalent to the Mimi v0.4 `Component(Def|Instance)` concept.
To the degree possible, composite components are designed to operate the same as leaf components, though there are necessarily differences:

1. `MetaComponent(Def|Instance)` -- presents the same API as a `LeafComponent(Def|Instance)`, but the variables and parameters it exposes are the aggregated sets of variables and parameters exposed by its components, each of which can be a `MetaComponent` or `LeafComponent`. A `MetaComponentInstance` creates no new storage for variables and parameters; it references the storage in its internal components. By default, the `run_timestep` method of a `MetaComponentInstance` simply calls the `run_timestep` method of each of its internal components in dependency order.
1. Leaf components are defined using the macro `@defcomp`, while composites are defined using `@defcomposite`. Each macro supports syntax and semantics specific to the type of component. See below for more details on these macros.

1. `Model` -- Contains a top-level `MetaComponentInstance` that holds all the actual user-defined components, which are instances of `MetaComponentInstance` or `LeafComponentInstance`. The API for `Model` delegates some calls to its top-level `MetaComponentInstance` while providing additional functionality including running a Monte Carlo simulation.
1. Leaf composites support user-defined `run_timestep()` functions, whereas composites have a built-in `run_timestep()` function that iterates over its subcomponents and calls their `run_timestep()` function. The `init()` function is handled analogously.

## Implementation Notes
### Classes.jl

### Model
Most of the core data structures are defined using the `Classes.jl` package, which was developed for Mimi, but separated out as a generally useful julia package. The main features of `Classes` are:

* A `Model` will be defined using the `@defmodel` macro.
1. Classes can subclass other classes, thereby inheriting the same list of fields as a starting point, which can then be extended with further fields.

* As with the currently defined (but not exported) `@defmodel`, component ordering will be determined automatically based on defined connections, with loops avoided by referencing timestep `[t-1]`. This simplifies the API for `addcomponent!`.
1. A type hierarchy is defined automatically that allows classes and subclasses to be referenced with a single type. In short, if you define a class `Foo`, an abstract type called `AbstractFoo` is defined, along with the concrete class `Foo`. If you subclass `Foo` (say with the class `Bar`), then `AbstractBar` will be a subtype of `AbstractFoo`, allowing methods to be defined that operate on both the superclass and subclass. See the Classes.jl documentation for further details.

* We will add support for two optional functions defined inside `@defmodel`:
* `before(m::Model)`, called before the model runs its first timestep
* `after(m:Model)`, called after the model runs its final timestep.
For example, in Mimi, `ModelDef` is a subclass of `CompositeComponentDef`, which in turn is a subclass of `ComponentDef`. Thus, methods can be written with arguments typed `x::ComponentDef` to operate on leaf components only, or `x::AbstractCompositeComponentDef` to operate on composites and `ModelDef`, or as `x::AbstractComponentDef` to operate on all three concrete types.

* A `Model` will be implemented as a wrapper around a single top-level `MetaComponent` that handles the ordering and iteration over sub-components. (In an OOP language, `Model` would subclass `MetaComponent`, but in Julia, we use composition.)
## Core types

![MetaComponent Schematic](../figs/Mimi-model-schematic-v3.png)
These are defined in `types/core.jl`.

1. `MimiStruct` and `MimiClass`

### MetaComponent
All structs and classes in Mimi are derived from these abstract types, which allows us to identify Mimi-defined items when writing `show()` methods.

* Defined using `@defcomp` as with `LeafComponent`. It's "meta" nature is defined by including a new term:

`subcomps = [sc1, sc2, sc3, ...]`, where the referenced sub-components (`sc1`, etc.) refer to previously defined `ComponentId`s.
1. `ComponentId`

* A `MetaComponent`'s `run_timestep` function is optional. The default function simply calls `run_timestep(subcomps::Vector)` to iterate over sub-components and calls `run_timestep` on each. If a `MetaComponent` defines its own `run_timestep` function, it should either call `run_timestep` on the vector of sub-components or perform a variant of this function itself.
To identify components, `@defcomp` creates a variable with the name of
the component whose value is an instance of this type. The definition is:

* The `@defcomp` macro allows definition of an optional `init` method. To this, we will add support for an `after` method as in `@defmodel`. We will allow `before` as an alias for `init` (perhaps with a deprecation) for consistency with `@defmodel`.
```julia
struct ComponentId <: MimiStruct
module_obj::Union{Nothing, Module}
comp_name::Symbol
end
```

## Other Notes
1. `ComponentPath`

* Currently, `run()` calls `_run_components(mi, clock, firsts, lasts, comp_clocks)` with simple vectors of firsts, lasts, and comp_clocks. To handle this with the recursive component structure:
A `ComponentPath` identifies the path from one or more composites to any component, using an `NTuple` of symbols. Since component names are unique at the composite level, the sequence of names through a component hierarchy uniquely identifies a component in that hierarchy.

* Aggregate from the bottom up building `_firsts` and `_lasts` in each `MetaComponentInstance` holding the values for its sub-components.
```julia
struct ComponentPath <: MimiStruct
names::NTuple{N, Symbol} where N
end
```

* Also store the `MetaComponentInstance`'s own summary `first` and `last` which are just `min(firsts)` and `max(lasts)`, respectively.
## Model Definition

* Currently, the `run()` function creates a vector of `Clock` instances, corresponding to each model component. I see two options here:
Models are composed of two separate structures, which we refer to as the "definition" side and the "instance" or "instantiated" side. The definition side is operated on by the user via the `@defcomp` and `@defcomposite` macros, and the public API.

1. Extend the current approach to have each `MetaComponentInstance` hold a vector of `Clock` instances for its sub-components.
The instantiated model can be thought of as a "compiled" version of the model definition, with its data structures oriented toward run-time efficiency. It is constructed by Mimi in the `build()` function, which is called by the `run()` function.

2. Store a `Clock` instance with each `MetaComponentInstance` or `LeafComponentInstance` and provide a recursive method to reset all clocks.
The public API sets a flag whenever the user modifies the model definition, and the instance is rebuilt before it is run if the model definition has changed. Otherwise, the model instance is re-run.

The model definition is constructed from the following elements.

### Other stuff
### Leaf components

* This might be is a good time to reconsider the implementation of external parameters. The main question is about naming these and whether they need to be globally unique or merely unique within a (meta) component.
1. `DatumDef`

* Unit conversion components should be simple "multiplier" components that are bound with specific conversion factors, conceptually like a "closure" on a component.
This is a superclass holding elements common to `VariableDef` and `ParameterDef`, including the `ComponentPath` to the component in which the datum is defined, the data type, and dimension definitions. `DatumDef` subclasses are stored only in leaf components.

* An "identity" component takes an input (external input, bound constant) and allows multiple components to access it. One issue is how to handle the type of argument. Could function wrappers be useful here?
* Identity is simply a unit conversion of 1.
1. `VariableDef <: DatumDef`

* If > 1 component exports parameters of the same name, it's an error. At least one comp must rename.
This class adds no new fields. It exists to differentiate variables from parameters.

* David suggested making composite comps immutable, generating a new one each time a change is made. (Why not just have the CompositeComponentInstance be immutable?)
1. `ParameterDef <: DatumDef`

## Notes from 9/21/18 Meeting
This class adds only a "default value" field to the `DatumDef`. Note that functions defined to operate on the `AbstractDatumDef` type work for both variable and parameter definitions.

```
@defcomp foo begin
Component(bar; export=[var_1, var_2, param_1])
Component(Mimi.adder, comp_1; # rename locally as :comp_1
bind=[par_3 => 5, # set a parameter to a fixed value
par_4 => bar.var_1]) # connect a parameter to a variable
1. `ComponentDef`

Instances of `ComponentDef` are defined using `@defcomp`. Their internal `namespace`
dictionary can hold both `VariableDef` and `ParameterDef` instances.

### Composite components

Composite components provide a single component-like interface to an arbitrarily complex set
of components (both leaf and composite components).

1. `DatumReference`

This abstract class serves as a superclass for `ParameterDefReference`, and
`VariableDefReference`.

1. `ParameterDefReference`, and `VariableDefReference`

These are used in composite components to store references to `ParameterDef` and `VariableDef` instances defined in leaf components. (They are conceptually like symbolic links in a
file system.) Whereas a `VariableDef` or `ParameterDef` can appear in a leaf
component, references to these may appear in any number of composite components.

"Importing" a parameter or variable from a sub-component defines a reference to that
datum in a leaf component. Note that if a composite imports a datum from another
composite, a reference to the leaf datum is stored in each case. That is, we don't
store references to references.

1. `CompositeComponentDef <: ComponentDef`

Instances of `CompositeComponentDef` are defined using `@defcomposite`. Their internal `namespace` dictionary can hold instances of `ComponentDef`, `CompositeComponentDef`, `VariableDefReference` and `ParameterDefReference`.
Composite components also record internal parameter connections.

1. `ModelDef <: CompositeComponentDef`

A `ModelDef` is a top-level composite that also stores external parameters and a list
of external parameter connections.

### Parameter Connections

Parameters hold values defined exogneously to the model ("external" parameters) or to the
component ("internal" parameters).

1. `InternalParameterConnection`

Internal parameters are defined by connecting a parameter in one component to a variable
in another component. This struct holds the names and `ComponentPath`s of the parameter
and variable, and other information such as the "backup" data source. At build time,
internal parameter connections result in direct references from the parameter to the
storage allocated for the variable.

1. `ExternalParameterConnection`

Values that are exogenous to the model are defined in external parameters whose values are
assigned using the public API function `set_param!()`, or by setting default values in
`@defcomp` or `@defcomposite`, in which case, the default values are assigned via an
internal call to `set_param!()`.

External connections are stored in the `ModelDef`, along with the actual `ModelParameter`s,
which may be scalar values or arrays, as described below.

1. `ModelParameter`

This is an abstract type that is the supertype of both `ScalarModelParameter{T}` and
`ArrayModelParameter{T}`. These two parameterized types are used to store values set
for external model parameters.

## Instantiated Models



1. `ComponentInstanceData`

This is the supertype for variables and parameters in component instances.

```julia
@class ComponentInstanceData{NT <: NamedTuple} <: MimiClass begin
nt::NT
comp_paths::Vector{ComponentPath} # records the origin of each datum
end
```
```

1. `ComponentInstanceParameters`

1. `ComponentInstanceVariables`

1. `ComponentInstance`

1. `LeafComponentInstance <: ComponentInstance`

1. `CompositeComponentInstance <: ComponentInstance`

The `run_timestep()` method of a `ComponentInstance` simply calls the `run_timestep()`
method of each of its sub-components in dependency order.

1. `ModelInstance <: CompositeComponentInstance`


## User-facing Classes

1. `Model`

The `Model` class contains the `ModelDef`, and after the `build()` function is called, a `ModelInstance` that can be run. The API for `Model` delegates many calls to either its top-level `ModeDef` or `ModelInstance`, while providing additional functionality including running a Monte Carlo simulation.

1. `ComponentReference`

1. `VariableReference`
9 changes: 2 additions & 7 deletions examples/01-onecomponent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@ using Mimi

end


# Create a model uses the component
# =================================

@defmodel begin

m = Model()

component(component1)
end
m = Model()
add_comp!(m, component1)

# Run model
# =========
Expand Down
42 changes: 0 additions & 42 deletions src/core/_preliminary.jl

This file was deleted.

Loading