Engee documentation

Working with variables in Engee

To create a new variable, enter the name (variable name) in command line/script editor and use the assignment operator = (equals sign) to assign the desired value.

Variables in Engee can be set in different ways. Let’s consider the main assignment methods based on the following code and three variables - a, b, c:

a = 1 # Простое присваивание
a::Float64 = 1.0 # Присваивание с явным указанием типа данных
b = 1 + 2 # Присваивание через вычисление выражения
c = b = 5 # Множественное присваивание
a,b,c = (10,20,30) # Распаковка кортежа

As a result of executing the code, we can get the following:

  • The variable a is assigned the value 1. This is the simplest assignment of a variable value;

  • Variable a is assigned the value 1, but with an explicit specification of the Float64 (floating point) data type; Now its actual value is 1.0;

  • The variable b is assigned the result of the expression 1 + 2, i.e. 3;

  • The value 5 is assigned to variables b and c;

  • The values from the tuple (10, 20, 30) are unpacked and assigned to variables a, b and c respectively;

Note that variables in Engee can be scalar, vector and matrix variables:

  • Scalar - has a single data value. It is specified using variables in the form of integers, real numbers, logical values and character strings. For example:

    v = 1 # Целое число, тип данных Int64
    v = 1.0 # Вещественное число, тип данных Float64
    v = true # Логическое значение, тип данных Bool
    v = "a" # Символьная строка, тип данных String
  • Vector is an ordered collection of elements that can be represented as a one-dimensional array. It is specified by usage of variables that contain integers or real numbers, logical values or character strings. For example:

    v = [1, 2, 3] # Вектор целых чисел, тип данных Vector{Int64}
    v = [1.0, 2.0, 3.0] # Вектор вещественных чисел, тип данных Vector{Float64}
    v = [true, false] # Вектор логических значений, тип данных Vector{Bool}
    v = ["a", "b", "с"] # Вектор символьных строк, тип данных Vector{String}
  • Matrix is a two-dimensional array of data consisting of rows and columns. A matrix can be used to represent many variables simultaneously. Each element of a matrix can be a scalar value such as an integer, a real number, a logical value, or a character string. For example:

    v = [1 2 3] # Матрица целых чисел, тип данных Matrix{Int64}
    v = [1.0 2.0 3.0] # Матрица вещественных чисел, тип данных Matrix{Float64}
    v = [true false] # Матрица логических значений, тип данных Matrix{Bool}
    v = ["a" "b" "с"] # Матрица символьных строк, тип данных Matrix{String}
    v = ([1 2 3; 4 5 6; 7 8 9]) # Многострочная матрица, тип данных Matrix{Int64}

To create arrays with a dimension greater than three, you can use the undef value, which indicates that the array is not initialised. The elements of such an array can contain arbitrary data. Let’s choose one of the two structures to create an array:

Array{T}(undef, dims)
Array{T,N}(undef, dims)

Where:

  • T is the data type of the array.

  • N - dimensionality of the array. Can be specified explicitly or determined by the length and number of dims. If specified explicitly, it must correspond to the length and number of dims.

  • dims - tuple or a number of integer arguments corresponding to the length in each dimension.

For example:

A = Array{Float64, 3}(undef, 2, 3, 4)

# или

A = Array{Float64}(undef, 2, 3, 4) # Получим трехмерный массив с размерностью 2*3*4, тип данных Float64, размерность задается автоматически
Output of the result of array creation
2×3×4 Array{Float64, 3}:
[:, :, 1] =
6.94241e-310  6.94267e-310  6.94267e-310
6.94272e-310  6.94267e-310  6.94267e-310

[:, :, 2] =
6.94267e-310  6.94267e-310  6.94267e-310
6.94267e-310  6.94267e-310  6.94267e-310

[:, :, 3] =
6.94267e-310  6.94267e-310  6.94267e-310
6.94267e-310  6.94267e-310  6.94267e-310

[:, :, 4] =
6.94267e-310  6.94267e-310  6.94267e-310
6.94267e-310  6.94267e-310  6.94267e-310

Read more about creating arrays at Arrays.

Read more about data types in Types.

Variable typing

Engee supports explicit and implicit variable typing - meaning that a variable’s data type can be explicitly specified (forced assignment) or determined based on the contents at runtime. The main data types include:

  • Integer - represents integers, e.g. 1, 100, -1.

  • Float - represents real numbers (floating point), e.g. 1.0, -3.14.

  • String - represents text enclosed in inverted commas, e.g. "Hello, world!".

  • Array - represents an ordered set of elements, e.g. [1, 2, 3].

Let’s consider the explicit specification of the variable data type on the following example:

variables article example 1

Here, the z variable was explicitly assigned the Int64 data type, which contains only integers. An attempt to assign a real number 3.14 (corresponding to the Float64 data type) to the z variable caused an error (InexactError: Int64).

An attempt to assign an inappropriate data type to the variable causes an error and creates the err variable in the variables window. The err variable stores information about the last error in the value column in the variables window. If necessary, hide the err variable by analogy with other variables or manually using the nothing object:

err = nothing

Consider implicit typing, in which a program determines the data type of a variable based on its contents:

variables article example 2

In implicit typing, assigning a different data type to a variable does not cause an error. Assignment of a data type is based on the content (value) of the variable.

You can learn more about typing in the article Types in Engee.

Tuples

A tuple in *Engee is an ordered and immutable data structure. Tuples are created using parentheses ( ) and commas.

Tuples are described using the parameterised data type MyType{ParamType1, ParamType2, ParamType3, …​} and represent the data type of elements in a direct sequence. The elements of a tuple can have different data types. For example, a tuple can contain an integer, a real number and quoted text at the same time. Let’s consider such a tuple:

tuple = (1, 3.14, "Hello, world!")

In this case, the tuple has data type Tuple(Int64, Float64, String).

Tuples can be ordinary and named:

  • A regular tuple is an ordered and unchangeable data structure. Each element of an ordinary tuple can be of any data type. Its elements are arranged in a specific order. The elements are accessed through their indices. Let’s consider such a tuple by example:

    x = (1, 3.14, "Hello, world!")
    x[3]

    In the example, the variable x contains three values - 1, 3.14 and "Hello, world!". These values correspond to ordinal indices [1], [2] and [3] respectively. With the help of the variable x containing the values of the ordinary tuple and index [3], we obtained a specific value of the variable - "Hello, World!", but not the whole tuple.

    special variables article 2 3

  • A named tuple is a special type of tuple, similar to a regular tuple, but in which each element has its own name. Instead of referring to elements by index as in normal tuples, named tuples can also refer to elements by their names. An example of a named tuple:

    x = (a=1, b=3.14, c="Hello, world!")
    
    println(x.a)
    println(x.b)
    println(x.c)
    
    println(x[1])
    println(x[2])
    println(x[3])

    special variables article 2 4

This example supports accessing tuple elements not only via indices (as in a normal tuple [1],…​,[n]), but also via variable names - x.a, x.b, x.c, etc.

Since a tuple is an immutable structure - the order of its elements matters and is preserved. The immutability of a tuple allows working with multiple assignments.

Multiple assignment is a feature that allows you to assign values to multiple variables simultaneously using a tuple. This facility avoids temporary variables and improves code readability. For example:

tuple = (1, 3.14, "Hello, world!")
a, b, c = tuple

A tuple tuple with three elements is created here:

  1. Integer 1.

  2. The real number 3.14.

  3. The string `Hello, world!

Next, the tuple values are assigned in parallel to three variables a, b and c respectively. After the code execution, variable a will contain the value 1, variable b 3.14, and variable c will contain the string "Hello, world!". In this way, you can simultaneously assign several variables without explicitly specifying tuple values using string interpolation:

info = ("Ilya", 25, "Engineer")
name, age, occupation = info
println("Name: $name, Age: $age, Occupation: $occupation")

In this code, the tuple is used to store information and the concurrent assignment is used to unpack the tuple values into individual variables. String interpolation is then applied to output information about the person by usage of the $ literal:

  1. info = ("Ilya", 25, "Engineer") - a tuple info is created with three elements: the string "Ilya" (name), the integer 25 (age) and the string "Engineer" (occupation).

  2. name, age, occupation = info - using parallel assignment, the tuple values are unpacked into three variables: name, age and occupation. Now the variable name contains the value "Ilya", age contains the integer 25, and occupation contains the occupation "Engineer".

  3. println("Name: $name, Age: $age, Occupation: $occupation") - outputs a string usage of string interpolation (using $name, $age, and $occupation to insert variable values into the string). Executing this line of code will output:

Name: Ilya, Age: 25, Occupation: Engineer

Thus, the code creates a tuple with information about the person, and then unpacks this tuple into separate variables for easier access to the data and outputs the information to the script editor or command line.

special variables article 1 1

For more information about working with tuples in Engee, see Functions in Julia. For more information about interpolation in Julia, see Strings in Julia.

Scope

By a code block we mean a code fragment enclosed in the syntactic construct function (function) or let …​ end. Inside such code blocks you can declare local variables that will be visible only inside this block and will not conflict with variables in other parts of the code (for example, with global variables).

Visibility defines where in the code you can use a variable. Thus, variables are local and global:

  • Local (internal) variable - a variable is considered local if it is declared and defined inside its parent element, e.g. function, object, code block (in which it is defined), etc. Local variables are visible only inside the scope in which they are defined and are inaccessible outside of it. Let’s consider a local variable by example:

    function example_function()
        x = 10
        println(x)
    end

Here function example_function() defines a function named example_function containing a local variable x in the local scope.

Further calling the function with example_function() will output the value of the x variable equal to 10:

example_function()

Executing the code also created an example-function variable with the Function data type in the Engee variable window.

After the function is executed, the local variable x goes out of scope and becomes inaccessible outside of the function. This means that the variable exists only within the block in which it was defined.

Let’s try to call the variable outside the local scope using the following code:

println(x)

println error 1

As a result of code execution, we got an error that the x variable is not defined. We conclude that a local variable is limited to the scope inside the code block (in which it is defined) and is not defined from outside.

The external code has no access to the local variable and generates an error that the local variable is not defined.

  • Global (external) variable - a variable is considered global if it is defined outside functions, loops and code blocks with local scope. A global variable is accessible from any part of the code and is located in the global scope, which by default is the Main module. Within the Main module, you can define submodules that will have their own global variables. Let’s consider a global variable by example:

    global_var = 20
    function example_function()
        println(global_var)
    end

This code sets the global variable global_var and defines the example_function() function. example_function() outputs the value of the global variable. To output the result of the function, enter the following code:

example_function()
println(global_var)# можно использовать глобальную переменную за пределами функции

After calling example_function(), the value of the global variable global_var equal to 20 is printed. The println function will simultaneously print two values of the variable, once from the function and once outside it.

Usage of such a variable may be difficult due to the shadowing problem when there is a variable with the same name in the local scope.

Variable shadowing is a situation when a variable with the same name as in the global scope is used inside a certain scope (for example, inside a function or code block). As a result, a variable inside the scope shadows (overwrites) a variable with the same name from the external scope.

Be careful when setting variables with the same names - because of shading, a variable inside a restricted scope may have a different value or type than a variable with the same name in other parts of the programme.

For example, let’s consider the code with creating two variables with the same names and values, and without shading:

global_var = 10  # создание глобальной переменной

function example_function()
    global_var = 20  # создание локальной переменной (внутри функции) с тем же именем, что и глобальная
    println(global_var)  # будет использована локальная переменная внутри функции
end

example_function()  # вызов функции
println(global_var)  # глобальная переменная не была изменена внутри функции, поэтому выводится ее значение

A local variable with the same name global_var as the global variable is created inside the function. This does not change the global variable, but creates a new local variable visible only inside the function. This outputs the values of both variables as 20 (global) and 10 (local) respectively. Thus, creating variables in different scopes helps to avoid problems with overwriting variables with the same names.

Variables in the Modelling Environment

Variables as parameters of models

In Engee, variables can be set as parameters of a model. Consider the following example:

engee.create("model")

Use the create method to create a model in Engee named model. Next, let’s assign a variable:

m = engee.gcm()

Using the method gcm we access the created model and return an object representing this model (in our case GCM - Get Current Model or Get Current Model). The obtained object is assigned to the m variable.

Next, let’s replace the parameters of modelling with set_param!:

engee.set_param!(m, "name" => "Tsit5", "type" => "variable-step")

The code changes the parameters of the solver and its step from constant to variable.

See if the parameters of the model have changed using the method gcm:

engee.get_param("model")

The parameters of the model have changed to new parameters due to the variable m:

variables article 2 10

Next, let’s run the simulation of the model through the variable using the method run:

engee.run("m")

After successful simulation, save the model Engee with object properties (variable m) to the given path in the file browser. In our case:

engee.save(m,"/user/start/examples/base_simulation/command_control/model.engee"; force=true)

If a file with this name already exists (in our case model.engee) - the force=true option will overwrite the model with data from the m variable without asking for confirmation. This is useful if you need to overwrite existing files when saving the model.

The Engee model will appear in the file browser only after saving it. Saving the model will allow you to work with it in the future and use the full functionality of file browser.

More possibilities for working with variables in the modelling environment are presented in the article Software control of modelling.

Variables as parameters of blocks

You can control block parameters without programmatic methods by simply setting individual parameter values as variables. Let’s consider an example with the model presented in the article Software processing of simulation results in Engee. The model consists of blocks Sine Wave Function and Terminator, with writing enabled to signal between them:

simout 1

The Sine Wave Function block is the only block in the model whose parameters can be configured. Left-click on the block and set the variables for each parameters according to the figure:

comparison sinewave variables

After setting the variable names for the block values, assign values for each of these variables:

a_var = 1 # Amplitude становится равным 1
b_var = 2 # Bias становится равным 2
f_var = 3 # Frequency становится равным 3
p_var = 1 # Phase становится равным 1
s_var = 1 # Sample time становится равным 1

Let’s run the model simulation by giving the individual variable a model name (earlier in the article m) and using the engee.run method, or manually, via the button Start start simulation button. The graph will show that the block Sine Wave Function has changed its parameters and the simulation has run correctly:

sine wave result modeling

Setting variables as parameters of blocks is useful if the block has dependencies that affect one or another parameter:

a_var = (2*f_var) # Amplitude становится равным 6
b_var = (6/f_var) # Bias становится равным 2
f_var = 3 # Frequency равен 3
p_var = (1 + f_var) # Phase становится равным 4
s_var = 1 # Sample time равен 1

Algebraic expressions, including those with variables, can be used as block parameters:

one var choose variables

Output

The variable a_var is 6, then the amplitude after multiplication by 3 will be 18, we will verify this by plotting the simulation results:

one var choose variables graph

Usage of different sets of variable values (saved in different scripts) allows modelling objects with the same structure but different parameters.

Saving simulation results

It is possible to save the results of a model simulation to Engee RAM using the simout variable. The variable is created automatically when the model simulation is finished and model signal recording is enabled. Let’s consider the example with the model from the previous paragraph, but using the previously created variable m.

After the model simulation is completed, the simout variable will appear in the file browser, storing the simulation results. Let’s upload the data from simout to the m variable:

m = collect(simout["newmodel_1/Sine Wave.1"])

Visualise the results of the simulation using the Plots library and our variable:

using Plots # подключаем модуль Plots
plot(m.time, m.value) # вывод time (время) и value (значение) из переменной m

After the code is finished, we will see the following graph:

simout graph result

Information on the process of saving simulation results is described in the article on Software processing of simulation results in Engee.