Macros and globals allow you to define custom model code that is included in the model.
For instance, you could use macros to define a custom function that is accessible in all the equations in your model. For example, imagine you had a model where you needed to calculate the sine of the division of the sum and product of three numbers in many different places. Now you could write this equation out wherever it occurred. However, that might be tedious and prone to error. Additionally, if you later decided you had to use the cosine instead of sine, there would be many different parts of your model you would need to update.
By defining macros for models we can create a function to carry out this repetitive task. Insight Maker can define single-line functions like so:
myFn(a, b, c) <- sin((a+b+c)/(a*b*c))
# calling the function
myFn(1, 2, 3)
Multiline functions can be defined using this syntax:
Function myFn(a, b, c)
x <- (a+b+c)
y <- (a*b*c)
return sin(x/y)
End Function
# calling the function
myFn(1, 2, 3)
Either form makes the function available throughout the model. Below, a herd grows in two separate regions. The model's globals define the density-dependent growth rate once, as growthRate(population, capacity) <- 0.4 * (1 - population / capacity), and both growth flows call it. There is now only one place to edit if the growth assumption changes.
{
"engine": "SIMULATION_PACKAGE",
"name": "Using a Macro",
"simulation": {
"algorithm": "RK1",
"time_start": 0,
"time_length": 30,
"time_step": 0.25,
"time_units": "YEARS"
},
"engine_settings": {
"globals": "growthRate(population, capacity) <- 0.4 * (1 - population / capacity)"
},
"elements": [
{
"type": "STOCK",
"name": "North Herd",
"behavior": {
"initial_value": "200",
"non_negative": true
}
},
{
"type": "STOCK",
"name": "South Herd",
"behavior": {
"initial_value": "500",
"non_negative": true
}
},
{
"type": "VARIABLE",
"name": "North Capacity",
"behavior": {
"value": "1000"
},
"display": {
"interactive": true,
"interactive_min": 100,
"interactive_max": 2000
}
},
{
"type": "VARIABLE",
"name": "South Capacity",
"behavior": {
"value": "1500"
},
"display": {
"interactive": true,
"interactive_min": 100,
"interactive_max": 2000
}
},
{
"type": "FLOW",
"name": "North Growth",
"from": null,
"to": "North Herd",
"behavior": {
"value": "[North Herd] * growthRate([North Herd], [North Capacity])"
}
},
{
"type": "FLOW",
"name": "South Growth",
"from": null,
"to": "South Herd",
"behavior": {
"value": "[South Herd] * growthRate([South Herd], [South Capacity])"
}
},
{
"type": "LINK",
"from": "North Capacity",
"to": "North Growth"
},
{
"type": "LINK",
"from": "South Capacity",
"to": "South Growth"
}
],
"visualizations": [
{
"name": "North Herd",
"type": "TIME_SERIES",
"elements": [
"North Herd"
]
},
{
"name": "South Herd",
"type": "TIME_SERIES",
"elements": [
"South Herd"
]
}
]
}
Macros can also be used to define variables that will be available in the model such as a counter. Another use of macros is to set the random seed for the model, so the same set of random numbers will be generated each model run:
setRandSeed(99)
See the Advanced Equations section of this manual for more information on the features and syntax you can use in your macros.
As another example, if you wanted a matrix multiplication function (useful as the * operator does element-wise multiplication in Insight Maker), you could use something like this. Once placed in your Macros, you can call the function anywhere in your model formulas. Try editing the matrices below:
function MatMult(a, b)
# assumes a and b are matrices defined as a vector of rows
# assumes matrices are correctly sized, does not do any error checking
res <- repeat(repeat(0,a.length()), b{1}.length())
for row from 1 to a.length()
for col from 1 to b{1}.length()
total <- 0
for item from 1 to a{1}.length()
total <- total + a{row, item} * b{item, col}
end loop
res{row, col} <- total
end loop
end loop
return res
end function
# calling the function
MatMult({{1,2,3}, {4,5,6}}, {{7,8},{9,10},{11,12}})