forio Toggle navigation

SimLang: Language Overview

A model written in SimLang is an equations file (.eqn). The equations describe external factors and conditions affecting the simulation, as well as the connections between user decisions and simulation results. Together, these equations define the cause-and-effect model that underlies your project.

The model file (equations file) can contain, in any order: decisions, variables, and properties.

Each decision or variable definition can include functions that are built in to the language.

Like other system dynamics -inspired languages (such as Vensim), SimLang is designed to support time-based models. Therefore, each project with a SimLang model automatically has access to several operations that advance the model through time (e.g. step). When you build your User Interface, you set up some elements to call these operations. (However, you do not define your own operations within a SimLang model: the only functions in an equations file are those used as part of the definition of a decision or variable.)

In this section, read more about:

In addition to this Language Overview, you can also jump to the Function Reference for details on all of the built-in functions that are part of SimLang.

SimLang was originally developed for use with the previous version of the Forio platform, Simulate. If you already have a model in SimLang because of your work with Simulate, see Migrating your model from Simulate to Epicenter.

Decisions & Variables

Decisions

A decision is a model element subject to end user control. It contains an equation that sets the default value, which is calculated at the start of the simulation. Otherwise, it takes its value from the end user's input. Decisions can also be recalculated, which resets their value to the equation.

When you write your model (.eqn) file, decisions are prefixed with D. For example:

D Initial Savings = 5000
D Interest Rate = 10%
D Yearly Deposits = 1000

For example, when you create the user interface for your project, you might use a decision input, slider, or radio group to capture an end user's input and store it in a model decision.

Variables

A variable is a model element with an equation that is automatically recalculated every time step. It can also take its value from the end user's input. However, once it is set by the end user, its value is fixed: it will not be automatically recalculated with each time step until a recalculate operation is explicitly called.

When you write your model (.eqn) file, variables are prefixed with V. For example:

V Interest = Previous Year Savings * Interest Rate
V Total Savings = Previous Year Savings + Interest + Yearly Deposits

For example, when you create the user interface for your project, you might use a variable, results table, line chart, or column chart to display model variables.

Naming Conventions

The names of both decisions and variables must start with a letter. They can contain uppercase letters, lowercase letters, spaces, and numbers.

Often models written in SimLang follow the traditional system dynamics convention of using multi-word variables names, including spaces, but this is only a convention.

Working with Scalars

Many decisions and variables are scalars, that is, singular values. Scalar decisions and variables can be strings, boolean values, any kind of number (integers, floating points), or a mathematical formula or equation.

See more on referencing variables from the user interface of your model.

Working with Arrays

Many decisions and variables are arrays, that is, sets of values that use the same equations. For example, a sales model might have the variables Sales and Revenue, and the decision Price, each of which is split among Books, CDs, and Games.

See more on referencing variables from the user interface of your model.

To define an array:

To define an arrayed variable or decision, you must specify both an equation and a range.

  • The equation can be numeric values, mathematical formulas, strings, etc., just as with scalar variables and decisions. To specify the equation for the initial values of an array, the values are enclosed in curly braces ({ }) and separated by commas (,).

  • The range indicates how the variable or decision is segmented. There are two ways to specify a range:

    • For enumerated ranges, use the prefix R and the name of the range, then list out the values in the range.

        R Products = Books, CDs, Games
        D Price[Products] = {100, 150, 175}
        V Sales[Products] = {100, 200, 150}
        V Revenue[Products] = Sales * Price
    • For numeric ranges, specify the variable or decision and append square brackets. Inside the brackets, list either the total number of items, or a starting index and an ending index with two periods in between. Numeric ranges are 1-based.

        D Price[3] = {100, 150, 175}
        V Sales[1..3] = {100, 200, 150}
        V Other Sales[11..12] = {1100, 1200}

To define a multidimensional array:

Arrays can also be multidimensional. Use a comma (,) in the range and enclose each dimension in curly braces ({ }).

V MySales[5, 2] = {{100, 200}, {100, 200}, {100, 200}, {100, 200}, {100, 200}}

To define arrays using default values:

To simplify the process of defining arrays, you can define particular elements by using the array index and a colon (:). Additionally, you can specify a default value using the Default keyword.

# Define an array with 1st element 100, 2nd element 200,
# and all other elements 1000
V MySales[10] = {100, 200, Default: 1000}

# Define an array with 5th element 100, 2nd element 200,
# and all other elements 1000
V MySales[10] = {5: 100, 2: 200, Default: 1000}

# Define an array and set Pencils to 100, 
# all other elements to 200
R Products = Pencils, Markers, Crayons 
V Sales[Products] = {Pencils: 100, Default: 200}

# Define an array and set the first five elements to 1000,
# and all other elements to 6000
V MySales[10] = {1..5: 1000, Default: 6000}

To define array values using expressions:

Any array definition can include an expression:

V Other Sales[1..2] = {New Sales, New Sales + Repeat Sales}

You can also use a loop to apply an expression to each array element. The syntax is:

FOREACH(<item>, <range>, <expression>)

For example:

V NewSales[10] = FOREACH(item, 10, item * 10)

is equivalent to:

V NewSales[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}

See more about loops under Control Flow.

To reference arrays in equations:

You can define an array using another array of the same dimensions in an equation. For example:

R Products = Books, CDs, Games
D Price[Products] = {100, 150, 175}
V Sales[Products] = {100, 200, 150}
V Revenue[Products] = Sales * Price

Equations can also refer to individual elements of an array, either by numeric index or by enumerated index:

V MyBookProfit = Revenue[Books] - Cost[Books]
V Demand = NewSales[1]

You can also reference sub-dimensions:

V X[5, 2] = {{100, 200}, {100, 200}, {100, 200}, {100, 200}, {100, 200}}

# Y is a one-dimensional array of two elements
V Y[2] = X[1, *]    

# Z is a two-dimensional array, but with three rows instead of five
V Z[3, 2] = X[1..3, *]

Array definitions can refer to themselves in equations, as long as they do not cause a circular reference error:

V Fib[1..5] = {1, 1, Fib[1] + Fib[2], Fib[2] + Fib[3], Fib[3] + Fib[4]}

To combine arrays with different dimensions:

Often, you may want to combine arrays of different ranges. The way to do this is with the FOREACH statement.

R Products = Books, CDs, Games
R Market Segments = 1..2

V Market Size[Market Segments] = {1000, 2000}
V Market Share[Products, Market Segments] = 
        {{20%, 30%}, {22%, 33%}, {44%, 46%}}

V Our Market[Products, Market Segments] = 
    FOREACH(p, Products,
        FOREACH(m, Market Segments,
            Market Share[p, m] * Market Size[m]
        )
    )

The equation for Our Market is arrayed over Products and Market Segments. The two FOREACH statements construct that array, applying the given expression to each new array item. This can be thought of in English as "For each product p and each market segment m in the result, the equation is Market Share[p, m] multiplied by Market Size[m]."

See more about loops under Control Flow.

Control Flow

Models written in SimLang don't have control flow in the same sense as, say, a Julia or Python program does. Rather than defining your own methods and control flow within a SimLang model, each project automatically has access to several operations that advance the model through time. At each time step, the decisions and variables are re-evaluated.

However, within the equation that defines each decision or variable, you do have a chance to use both built-in functions, as well as control flow in the form of conditionals and loops.

To learn more about built-in functions, see the Function Reference.

Loops: FOREACH

You can use FOREACH in SimLang to loop through values. This is almost always used in the definition of arrays, to apply an expression to each array element. The syntax is:

FOREACH(<item>, <range>, <expression>)

For example:

V NewSales[10] = FOREACH(item, 10, item * 10)

is a more compact way of writing:

V NewSales[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}

Loops are nestable, which is useful for multidimensional arrays. See additional examples in the Working with Arrays section, above.

Conditionals: IF

You can use IF in SimLang to choose values conditionally. The syntax is:

IF(<expression>, <equation if true>, <equation if false>)

For example:

V Profitable = IF(Profit > 0, true, false)

Note that each equation can itself include not only built-in functions but also loops or other conditionals.

Operations

Like other system dynamics -inspired languages, SimLang is designed to support time-based models. Therefore, although you do not define your own methods within a SimLang model, each project with a SimLang model automatically has access to several operations that advance the model through time.

The Interface Builder and Epicenter APIs both allow you to manipulate a run using operations exposed by the model. For SimLang models, there are several available operations: step, stepTo, reset, recalculate, goBack, and clear.

If you are using the Interface Builder to create your project's interface, you can select step under Add Operations for the Properties of a button. Optionally, you can select from the Parameters drop-down to choose how far to step (1 step, 10 steps, or to the end of the model). There is not an explicit stepTo available from the Interface Builder.

If you are creating the user interface by hand, you might have a button click call an operation. If you are using Flow.js, this looks like:

<button data-f-on-click="step(1)">Advance One Step</button>

Index of SimLang Operations Available from the UI


Operation: clear

Arguments: None

Result: Sets the current run back to its initial step (e.g. StartTime of 0). Note that you are still working with the same run. For example, run metadata is unchanged. If you would like a new run, use reset instead.


Operation: goBack

Arguments: Optional, number of time units to move backwards in the model.

Result:

  • If no argument is specified, the run goes back by 1 time unit.
  • If an argument is specified, the run goes back by the specified number of time units.
  • After the goBack() operation is complete, the sim is at the end of the previous step, with the decisions for that (previous) step still present.
  • If you try to go back before the start of the sim, the goBack() call acts just like the reset() call (see below): The sim will not go back before the simulation start time, but all the decisions will be cleared.

    For example, consider the following sequence of calls:

      step()   // advance from step 1 to step 2
      step()   // advance from step 2 to step 3
      step(2)  // advance from step 3 to step 5
      goBack() // go back from step 5 to step 4, with decisions made in step 4

Example: goBack(), goBack(2)

Notes: Note that TimeStep is a model property. If undefined, it defaults to 1, meaning time steps and time units are synonymous. However, if you set the TimeStep property, then goBack() and goBack(n) have different behaviors. For example, if you set M TimeStep = 0.5, then goBack() goes backwards by one time unit (two time steps), and goBack(2) also goes backwards by two time steps (one time unit).


Operation: step

Arguments: Optional, floating point number of time steps to advance the model.

Result:

  • If no argument is specified, the run advances by 1 time unit.
  • If an argument is specified, the run advances by the specified number of time steps.

Example: step(), step(5)

Notes: Note that TimeStep is a model property. If undefined, it defaults to 1, meaning time steps and time units are synonymous. However, if you set the TimeStep property, then step() and step(n) have different behaviors. For example, if you set M TimeStep = 0.5, then step() advances by one time unit (two time steps), and step(2) also advances by two time steps (one time unit).


Operation: stepTo

Arguments:

  • The time unit to step to (but not past).
  • Or, the string "end".

Result:

  • The run advances to the specified time unit.
  • If the argument is "end", the run advances to the end (the Final Time).

Example: stepTo(2018), stepTo(10), stepTo("end")


Operation: recalculate

Arguments: Array of strings. Each string must be a variable or decision in the model.

Result: This operation causes both variables and decisions to recalculate based on their equations in the model.

Example: recalculate(["var1", "var2", "dec1"])

Example Response:

{
  "var1": "10",
  "var2": "20",
  "dec1": "30",
}

Notes:

  • Typically decisions take their value from the end user's input; an equation defines their initial value. Calling recalculate updates the decision based on its equation.
  • Typically variables are automatically recalculated every time step based on their equation (definition) in the model. A variable can also take its value from the end user's input. However, once it is set by the end user, its value is fixed: it will not be automatically recalculated with each time step. Calling recalculate updates the variable based on its equation, and additionally "unfreezes" the variable: after calling this operation, the variable is again automatically recalculated every time step.

Operation: reset

Arguments: None

Result: This operation returns a new run, that is, a new instantiation of your model for an end user to play with. (Remember that a run is a collection of interactions with a model.) For example, this is useful if your end user needs to "start over" to create a new scenario within your project.

Note that after this call, you are working with a new run. If you would like to work with the same run but start over from the beginning of the model, use clear instead.

Example: reset()

Notes: This operation can ONLY be called from your project's user interface if you are using Flow.js.

(Under the hood, this is not an operation as defined for the RESTful Run API. If you are building your project's user interface with the Epicenter RESTful APIs, you should create a new run instead of calling reset().)


Epicenter Variables

In addition to any variables and decisions in your SimLang model, the following variables are available for reference when you are running a SimLang model on Epicenter.

  • Final Time: The end of your simulation; M EndTime.
  • Time: An array of the simulation times that you have stepped through so far.
  • Time Step: The value of the SimLang TimeStep property, M TimeStep.
  • Start Time: The beginning of your simulation; M StartTime.
  • Step: The current step. Steps are 0-based.

For example, in a model in which we have stepped twice and then asked for all of these variables, using the Run API (RESTful):

curl -G \
    'https://api.forio.com/v2/run/acme-simulations/sample-simlang-model/0000015b8d8ee74f391285f851ac6a2d899a/variables/?include=Time,Start Time,Final Time,Time Step' \
    --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9' \

which returns:

{
  "Time Step": 1,
  "Time": [
    0,
    1,
    2,
    3
  ],
  "Step": 3,
  "Start Time": 0,
  "Final Time": 10
}

Properties

Properties are characteristics of your model, declared in the model, that help set the context for your simulation and govern its overall behavior.

Most properties apply to the entire model:

  • These model properties can be declared anywhere in your model (.eqn) file.
  • They are prefixed with an M. For example:

      M StartTime = 2014

A few properties apply only to specific variables or decisions:

  • These variable properties must be declared after the variable is defined.
  • They are prefixed with a P and then added as a suffix to the name of a variable. For example:

      D Price = 100
      P Price.ExecuteDecisionImmediately = false

Only the properties listed below are supported in SimLang on Epicenter. If you have used SimLang in Simulate, you may have used other properties specific to Simulate. These are not supported in Epicenter. However, they will not cause an error if present in your model file; they are simply ignored. See more on migrating from Simulate.

The Epicenter-specific model context file also has some model properties. Properties in the context file describe characteristics such as how often decisions and variables are saved to the Epicenter backend database.

Index of SimLang Model Properties


Property: EndTime

Definition: The final time of the simulation. If less than StartTime, the simulation will not advance.

Scope: Model

Default Value: If undefined, defaults to 10.

Example:

  • M EndTime = 2020

Property: ExecuteDecisionImmediately

Definition: If true, a newly entered decision immediately impacts other model variables. If false, a newly entered decision is not reflected in the model until the next step.

Scope: Model or Decision

Default Value: If undefined, defaults to true.

Example:

  • M ExecuteDecisionImmediately = false
  • P Price.ExecuteDecisionImmediately = false

Property: InitialSteps

Definition: The number of steps to automatically advance the model when it initializes.

Scope: Model

Default Value: If undefined, defaults to 0.

Example:

  • M InitialSteps = 2

Property: ResetDecision

Definition: If true, decisions are reset to the calculated value after each step. If false, decisions hold the user-entered value.

Scope: Model or Decision

Default Value: If undefined, defaults to false.

Example:

  • M ResetDecision = true
  • P New Hires.ResetDecision = true

Property: StartTime

Definition: The starting time of the simulation.

Scope: Model

Default Value: If undefined, defaults to 0.

Example:

  • M StartTime = 2010

Property: TimeStep

Definition: The number of time units to advance in the model with each call to the operation step().

Scope: Model

Default Value: If undefined, defaults to 1.

Example:

  • M TimeStep = 0.5

Typically this is used in models that by nature are very granular, in order to properly model the underlying equations.

For instance: A call to step(1) always advances the model by one time unit, e.g. from 2010 to 2011, no matter what the value of the TimeStep property.

If you have M TimeStep = 0.5, a call to step() advances the model by half a time unit, e.g. from 2010 to 2010.5. A call to step(2) advances the model by two time units, or one time step, from 2010 to 2011. See additional examples under Operations, above.