3 Create a Model

Tutorial 3: Create a Model

This tutorial walks through the steps to create a new model, first a one-region model and then a more complex two-region model.

While we will walk through the code step by step below, the full code for implementation is also available in the examples/tutorial folder in the Mimi github repository.

Working through the following tutorial will require:

If you have not yet prepared these, go back to the main tutorial page and follow the instructions for their download.

Constructing A One-Region Model

In this example, we construct a stylized model of the global economy and its changing greenhouse gas emission levels through time. The overall strategy involves creating components for the economy and emissions separately, and then defining a model where the two components are coupled together.

There are two main steps to creating a component, both within the @defcomp macro which defines a component:

Starting with the economy component, each variable and parameter is listed. If either variables or parameters have a time-dimension, that must be set with (index=[time]).

using Mimi

@defcomp grosseconomy begin
	YGROSS	= Variable(index=[time])	# Gross output
	K	= Variable(index=[time])	# Capital
	l	= Parameter(index=[time])	# Labor
	tfp	= Parameter(index=[time])	# Total factor productivity
	s	= Parameter(index=[time])	# Savings rate
	depk	= Parameter()			# Depreciation rate on capital - Note that it has no time index
	k0	= Parameter()			# Initial level of capital
	share	= Parameter()			# Capital share

Next, the run_timestep function must be defined along with the various equations of the grosseconomy component. In this step, the variables and parameters are linked to this component and must be identified as either a variable or a parameter in each equation. For this example, v will refer to variables while p refers to parameters.

It is important to note that t below is an AbstractTimestep, and the specific API for using this argument are described in detail in the userguide in Advanced Topics: Timesteps and available functions.

	function run_timestep(p, v, d, t)
		# Define an equation for K
		if is_first(t)
			# Note the use of v. and p. to distinguish between variables and parameters
			v.K[t] 	= p.k0	
		else
			v.K[t] 	= (1 - p.depk)^5 * v.K[t-1] + v.YGROSS[t-1] * p.s[t-1] * 5
		end

		# Define an equation for YGROSS
		v.YGROSS[t] = p.tfp[t] * v.K[t]^p.share * p.l[t]^(1-p.share)
	end
end

Next, the component for greenhouse gas emissions must be created. Although the steps are the same as for the grosseconomy component, there is one minor difference. While YGROSS was a variable in the grosseconomy component, it now enters the emissions component as a parameter. This will be true for any variable that becomes a parameter for another component in the model.

@defcomp emissions begin
	E 	= Variable(index=[time])	# Total greenhouse gas emissions
	sigma	= Parameter(index=[time])	# Emissions output ratio
	YGROSS	= Parameter(index=[time])	# Gross output - Note that YGROSS is now a parameter

	function run_timestep(p, v, d, t)

		# Define an equation for E
		v.E[t] = p.YGROSS[t] * p.sigma[t]	# Note the p. in front of YGROSS
	end
end

We can now use Mimi to construct a model that binds the grosseconomy and emissions components together in order to solve for the emissions level of the global economy over time. In this example, we will run the model for twenty periods with a timestep of five years between each period.


using Mimi

function construct_model()
	m = Model()

	set_dimension!(m, :time, collect(2015:5:2110))

	# Order matters here. If the emissions component were defined first, the model would not run.
	add_comp!(m, grosseconomy)  
	add_comp!(m, emissions)

	# Set parameters for the grosseconomy component
	set_param!(m, :grosseconomy, :l, [(1. + 0.015)^t *6404 for t in 1:20])
	set_param!(m, :grosseconomy, :tfp, [(1 + 0.065)^t * 3.57 for t in 1:20])
	set_param!(m, :grosseconomy, :s, ones(20).* 0.22)
	set_param!(m, :grosseconomy, :depk, 0.1)
	set_param!(m, :grosseconomy, :k0, 130.)
	set_param!(m, :grosseconomy, :share, 0.3)

	# Set parameters for the emissions component
	set_param!(m, :emissions, :sigma, [(1. - 0.05)^t *0.58 for t in 1:20])
	connect_param!(m, :emissions, :YGROSS, :grosseconomy, :YGROSS)  
	# Note that connect_param! was used here.

	return m

end #end function

Note that as an alternative to using many of the set_param! calls above, one may use the default keyword argument in @defcomp when first defining a Variable or Parameter, as shown in examples/tutorial/01-one-region-model/one-region-model-defaults.jl.

Now we can run the model and examine the results:

# Run model
m = construct_model()
run(m)

# Check model results
m[:emissions, :E]

# Plot model results
explore(m, :emissions, :E)

# Observe all model result graphs in UI
explore(m)

Constructing A Multi-Region Model

We can now modify our two-component model of the globe to include multiple regional economies. Global greenhouse gas emissions will now be the sum of regional emissions. The modeling approach is the same, with a few minor adjustments:

To create a three-regional model, we will again start by constructing the grosseconomy and emissions components, making adjustments for the regional index as needed. Each component should be saved as a separate file.

As this model is also more complex and spread across several files, we will also take this as a chance to introduce the custom of using Modules to package Mimi models, as shown below.

using Mimi

@defcomp grosseconomy begin
	regions = Index()	#Note that a regional index is defined here

	YGROSS	= Variable(index=[time, regions])	# Gross output
	K 	= Variable(index=[time, regions])	# Capital
	l 	= Parameter(index=[time, regions])	# Labor
	tfp	= Parameter(index=[time, regions])	# Total factor productivity
	s 	= Parameter(index=[time, regions])	# Savings rate
	depk	= Parameter(index=[regions])	# Depreciation rate on capital - Note that it only has a region index
	k0	= Parameter(index=[regions])	# Initial level of capital
	share	= Parameter()	# Capital share

	function run_timestep(p, v, d, t)
		
		# Note that the regional dimension is used below and parameters and 
		variables are indexed by 'r'

		# Define an equation for K
		for r in d.regions
			if is_first(t)
				v.K[t,r] = p.k0[r]
			else
				v.K[t,r] = (1 - p.depk[r])^5 * v.K[t-1,r] + v.YGROSS[t-1,r] * p.s[t-1,r] * 5
			end
		end

		# Define an equation for YGROSS
		for r in d.regions
			v.YGROSS[t,r] = p.tfp[t,r] * v.K[t,r]^p.share * p.l[t,r]^(1-p.share)
		end
	end
end

Save this component as gross_economy.jl

using Mimi	#Make sure to call Mimi again

@defcomp emissions begin
	regions	=	Index()	# The regions index must be specified for each component

	E		= Variable(index=[time, regions])	# Total greenhouse gas emissions
	E_Global		= Variable(index=[time])		# Global emissions (sum of regional emissions)
	sigma		= Parameter(index=[time, regions])	# Emissions output ratio
	YGROSS		= Parameter(index=[time, regions])	# Gross output - Note that YGROSS is now a parameter

	function run_timestep(p, v, d, t)

		# Define an eqation for E
		for r in d.regions
			v.E[t,r] = p.YGROSS[t,r] * p.sigma[t,r]
		end

		# Define an equation for E_Global
		for r in d.regions
			v.E_Global[t] = sum(v.E[t,:])
		end
	end
end

Save this component as emissions.jl

Let's create a file with all of our parameters that we can call into our model. This will help keep things organized as the number of components and regions increases. Each column refers to parameter values for a region, reflecting differences in initial parameter values and growth rates between the three regions.

l = Array(Float64,20,3)
for t in 1:20
	l[t,1] = (1. + 0.015)^t *2000
	l[t,2] = (1. + 0.02)^t * 1250
	l[t,3] = (1. + 0.03)^t * 1700
end

tfp = Array(Float64,20,3)
for t in 1:20
	tfp[t,1] = (1 + 0.06)^t * 3.2
	tfp[t,2] = (1 + 0.03)^t * 1.8
	tfp[t,3] = (1 + 0.05)^t * 2.5
end

s = Array(Float64,20,3)
for t in 1:20
	s[t,1] = 0.21
	s[t,2] = 0.15
	s[t,3] = 0.28
end

depk = [0.11, 0.135 ,0.15]
k0 	 = [50.5, 22., 33.5]

sigma = Array(Float64,20,3)
for t in 1:20
	sigma[t,1] = (1. - 0.05)^t * 0.58
	sigma[t,2] = (1. - 0.04)^t * 0.5
	sigma[t,3] = (1. - 0.045)^t * 0.6
end

Save this file as region_parameters.jl

The final step is to create a module.

module MyModel

using Mimi

include("region_parameters.jl")
include("gross_economy.jl")
include("emissions.jl")

export construct_MyModel

function construct_MyModel()

	m = Model()

	set_dimension!(m, :time, collect(2015:5:2110))
	set_dimension!(m, :regions, [:Region1, :Region2, :Region3])	 # Note that the regions of your model must be specified here

	add_comp!(m, grosseconomy)
	add_comp!(m, emissions)

	set_param!(m, :grosseconomy, :l, l)
	set_param!(m, :grosseconomy, :tfp, tfp)
	set_param!(m, :grosseconomy, :s, s)
	set_param!(m, :grosseconomy, :depk,depk)
	set_param!(m, :grosseconomy, :k0, k0)
	set_param!(m, :grosseconomy, :share, 0.3)

	# set parameters for emissions component
	set_param!(m, :emissions, :sigma, sigma)
	connect_param!(m, :emissions, :YGROSS, :grosseconomy, :YGROSS)

	return m

end #function

end #end module

Save this file as MyModel.jl

We can now run the model and evaluate the results.

using Mimi

include("MyModel.jl")
using .MyModel
m = construct_MyModel()
run(m)

# Check results
m[:emissions, :E_Global]

# Observe model result graphs
explore(m)