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 = (equal 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 value1
. This is the simplest assignment of a variable value; -
Variable
a
is assigned the value1
, but with an explicit specification of the Float64 (floating point) data type; Now its actual value is1.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
- array data type. -
N
- dimensionality of the array. Can be specified explicitly or determined by the length and number ofdims
. If specified explicitly, it must correspond to the length and number ofdims
. -
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
Variable typing
Engee supports explicit and implicit variable typing - this means that a variable’s data type can be explicitly specified (forced assignment) or determined based on its 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:
Here, the Int64 data type, which contains only integers, was explicitly specified for the z
variable. An attempt to assign a real number 3.14
(corresponding to the Float64 data type) to the z
variable caused an error (InexactError: Int64).
Attempting to assign an inappropriate data type to a variable causes an error and the creation of the
|
Corteges
A Cortex (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 immutable 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 the ordinal indices[1]
,[2]
and[3]
respectively. With the help of the variablex
containing the values of the ordinary tuple and the index[3]
, we obtained a specific value of the variable -"Hello, World!"
, but not the whole tuple. -
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])
This example supports accessing tuple elements not only through indices (as in the usual tuple [1],…,[n]
), but also through 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 you to work 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:
-
Integer
1
. -
The real number
3.14
. -
The string
"Hello, world!"
.
Then the tuple values are assigned in parallel to three variables a
, b
and c
respectively. After the code execution, the a
variable will contain the value 1
, the b
variable - 3.14
, and the c
variable - 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, tuple is used to store information and concurrent assignment is used to unpack the tuple values into individual variables. String interpolation is then applied to output information about the person with usage of the $
literal:
-
info = ("Ilya", 25, "Engineer")
- a tupleinfo
is created with three elements: the string"Ilya"
(name), the integer25
(age) and the string"Engineer"
(occupation). -
name, age, occupation = info
- using parallel assignment, the tuple values are unpacked into three variables:name
,age
andoccupation
. Now the variable name contains the value"Ilya"
, age contains the integer25
, and occupation contains the occupation"Engineer"
. -
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.
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 may 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. a 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 the local variable x
in the local scope.
Further calling the function with the example_function()
command 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 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)
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 print two values of the variable at the same time, 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 inside a certain scope (for example, inside a function or code block) is used as in the global scope. 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). The received object is assigned to the m
variable.
Next, let’s replace the parameters of the 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 m
variable:
Next, let’s use the method run to run the simulation of the model through the variable:
engee.run("m")
After successful simulation, save the Engee model 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 only appear in the file browser when the model is saved. 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 the blocks Sine Wave and Terminator, with writing enabled for signalling between them:
Block Sine Wave is the only block in the model whose parameters can be configured. Left-click on the block and set variables for each parameters according to the figure:
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 simulation of the model by setting the model name to a separate variable (earlier in the article m
) and using the engee.run
method, or manually, via the button Start . The graph will show that the block Sine Wave has changed its parameters and the simulation ran correctly:
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:
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:
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 simulation is finished and model signal recording is enabled. Let’s consider the example with the model from the previous point, 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 unload the data from simout
into 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:
Information about the process of saving simulation results is described in the article about Software processing of simulation results in Engee.