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 # Simple assignment
a::Float64 = 1.0 # Assignment with explicit data type indication
b = 1 + 2 # Assignment through evaluation of an expression
c = b = 5 # Multiple assignment
a,b,c = (10,20,30) # Unpacking the tuple
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 # Integer, Int64 data type v = 1.0 # Real number, Float64 data type v = true # Boolean value, data type Bool v = "a" # Character string, data type 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 of integers, data type Vector{Int64} v = [1.0, 2.0, 3.0] # Vector of real numbers, data type Vector{Float64} v = [true, false] # Vector of logical values, data type Vector{Bool} v = ["a", "b", "c"] # Vector of character strings, data type 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 of integers, data type Matrix{Int64} v = [1.0 2.0 3.0] # Matrix of real numbers, data type Matrix{Float64} v = [true false] # Matrix of logical values, data type Matrix{Bool} v = ["a" "b" "c"] # The matrix of character strings, the data type Matrix{String} v = ([1 2 3; 4 5 6; 7 8 9]) # Multiline matrix, data type 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)
# or
A = Array{Float64}(undef, 2, 3, 4) # We get a three-dimensional array with the dimension 2*3*4 , the data type is Float64, the dimension is set automatically
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 is executed, 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. 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)# you can use a global variable outside of the function.
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 # creating a global variable
function example_function()
global_var = 20 # creating a local variable (inside a function) with the same name as the global one
println(global_var) # a local variable inside the function will be used.
end
example_function() # function call
println(global_var) # The global variable has not been changed inside the function, so its value is output.
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 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 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 # The Amplitude becomes equal to 1
b_var = 2 # Bias becomes equal to 2
f_var = 3 # Frequency becomes 3
p_var = 1 # Phase becomes equal to 1
s_var = 1 # The sample time becomes 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) # The Amplitude becomes 6
b_var = (6/f_var) # Bias becomes equal to 2
f_var = 3 # Frequency is equal to 3
p_var = (1 + f_var) # Phase becomes 4
s_var = 1 # The sample time is 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 storing the simulation results will appear in the file browser. 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 # connecting the Plots module
plot(m.time, m.value) # output of time and value from the variable 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.