Документация Engee

Переменные

Окно переменных variables article 2 1 — это инструмент Engee, предназначенный для управления переменными. В нем содержится информация о переменных, созданных в процессе работы.

Переменная — это имя, ассоциированное (связанное) с некоторым значением. Значение — это число, строка, матрица или другой тип данных. Переменные в Engee доступны в командной строке, редакторе скриптов и окне Переменные. Переменные также могут быть переданы как параметры блоков среды моделирования.

Для открытия окна переменных выберете значок variables article 2 1 в левом верхнем меню рабочей области Engee. Переменные можно создать:

  • Через командную строку:

    variables article 1

  • Через редактор скриптов. Для этого создайте новый скрипт (в примере назван new), добавьте область ввода кода и создайте переменные, нажав на зеленую кнопку Выполнить variables article 2 1 1.

    variables article 2 1 1

В окне переменных представлены следующие столбцы:

  • имя переменной;

  • значение переменной;

  • тип данных переменной;

  • размер переменной (в килобайтах).

Можно переместить столбцы окна переменных, перетаскивая заголовок мышью. Также можно менять ширину этих столбцов.

variables article 2 2

Можно редактировать значение переменной, нажав по имени двойным кликом или вызвав контекстное меню правой кнопкой мыши для выбора опции Редактировать значение.

variables article 2 4

Для редактирования имени переменной выберите соответствующую опцию из контекстного меню, вызванного правым кликом по имени переменной, либо дважды нажав по ее имени:

variables delete 1 1

Подробнее о разрешенных именах переменных можно ознакомиться в статье.

Можно скопировать имя или значение переменной, нажав по имени и выбрав функцию Копировать в контекстном меню.

variables article 2 3

Можно очистить ненужные переменные, нажав правой кнопкой мыши и выбрав Очистить (также используется клавиша delete). Для очистки всех переменных выберите иконку метлы в правом верхнем углу.

variables delete 1

В языке программирования Julia можно использовать не только латинские символы, но и символы из Unicode, например, \delta. При вводе символа \delta в командной строке, нажатии клавиши Tab можно получить соответствующий символ, который можно использовать в качестве имени переменной. Полученный символ можно скопировать из командной строки и вставить в редактор скриптов для использования.

Содержимое переменных

Значение переменной с типом данных Vector или Matrix может быть изменено в редакторе векторов и матриц в окне переменных.

Для примера поставьте любое число в любую свободную ячейку матрицы — матрица будет автоматически расширена (заполнена нулями) до этой ячейки. Ввод переменной в командной строке или редакторе скрипта покажет измененную матрицу:

variables article 2 6

Добавление строки с текстом в ячейку матрицы изменит ее тип данных на Any, что позволит переменной содержать значения любого типа. Вы можете изменять матрицу в любое время в редакторе векторов и матриц.

Импорт и экспорт

Engee поддерживает запись и выгрузку переменных в форматах .mat и .jld2:

  • .mat — формат, который используется в программном обеспечении MATLAB.

  • .jld2 — формат, который используется в языке программирования Julia.

Чтобы записать переменные в .mat или .jld формат, выберите их в окне переменных variables article 2 1 и правой кнопкой мыши вызовите контекстное меню. В меню нажмите Сохранить как…​:

variables mat jld exp

Это откроет окно экспорта переменных:

variables mat export 2

  1. Задайте имя файла, который будет содержать переменные.

  2. Выберите формат экспортируемого файла — MAT или JLD2.

  3. Отследите директорию, в которую будет сохранен файл.

  4. Задайте путь, по которому будет сохранен файл. В этой области будут отображены все папки файлового браузера Engee.

  5. Возвращайтесь из текущей директории на уровни выше, вплоть до /user (начала пути файлового браузера Engee).

  6. Создайте новую папку в директории пункта 3.

Сохраненный файл в формате MAT или JLD2 отобразится в окне файлов img4. Двойной клик левой кнопкой мыши по сохраненному файлу импортирует переменные в рабочую область Engee. Импортированные переменные автоматически добавятся в окно переменных variables article 2 1.

MAT

Используйте пакет MAT.jl для работы с данными в формате .mat в Julia (подробнее здесь).

Вы можете работать с форматом MAT программно, через редактор скриптов или командную строку. Например, рассмотрим следующий код чтения одной переменной из MAT:

using MAT # подключаем библиотеку MAT для работы с файлами .mat
a = 1 # задаем переменную а со значением 1
file = matopen("/user/mat_file.mat") # открываем файл mat_file.mat для чтения с помощью функции matopen и создаем объект file
variable_mat = read(file, "a") # считывает переменную "a" из файла типа MAT и присваивает ее значение переменной variable_mat
close(file) # закрывает файл

Для записи переменной в MAT-файл используем код:

variable_1 = 1 # зададим переменную variable_1 и присвоим ей значение 1
file = matopen("/user/new_mat_file.mat", "w") # открывает файл new_mat_file.mat в режиме записи ("w")
write(file, "variable_mat", variable_1) # записывает значение переменной variable_1 в переменную variable_mat в файл new_mat_file.mat
close(file) # закрывает файл

С помощью кода мы записали переменную в файл формата MAT. Схожим образом, через функцию matwrite, можем записать Dict (словарь) в формат MAT, используя его ключи в качестве имен переменных:

mat_Dict = Dict(
"variable_1" => 1,
"variable_2" => -2,
"variable_3" => "Hello, world")
matwrite("/user/Dict.mat", mat_Dict)

Код создаст файл Dict.mat, содержащий словарь с именами переменных.

Вы можете использовать аргумент сжатия данных compress в значении true (по умолчанию отключен, false) для записи больших данных в формате MAT:

mat_Dict = Dict(
"variable_1" => 1,
"variable_2" => -2,
"variable_3" => "Hello, world")
matwrite("/user/Dict.mat", mat_Dict; compress = true)

JLD2

Используйте пакет JLD2.jl для работы с данными в формате .jld2 в Julia (подробнее здесь).

Загрузим модуль JLD2:

import Pkg; #импорт менеджера пакетов
Pkg.add("JLD2") #добавления пакета JLD2

Пакет JLD2.jl поддерживает интерфейсы функции на подобии jldsave. Например:

using JLD2 #загрузка модуля

x = 1
y = 2
z = 42

# Простейший случай:
jldsave("example.jld2"; x, y, z)
# это равносильно
jldsave("example.jld2"; x=x, y=y, z=z)

# Новые имена можно присваивать лишь части аргументов
jldsave("example.jld2"; x, a=y, z)

# а чтобы внести полную путаницу, можно сделать так
jldsave("example.jld2"; z=x, x=y, y=z)

Если в файле требуется сохранить только один объект для последующей загрузки, можно воспользоваться функциями save_object и load_object. Например:

save_object(filename, x) #сохраняет объект x в новом JLD2-файле в filename, если файл существует по этому пути, он будет перезаписан.

load_object(filename, x) #загружает объект x

Переменные в Среде вычислений

Чтобы создать новую переменную, введите имя (название переменной) в командную строку/редактор скриптов и оператором присваивания = (знак равно) присвойте нужное значение.

Переменные в Engee могут быть заданы разными способами. Рассмотрим основные способы присваивания на основе следующего кода и трех переменных — a, b, c:

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

В результате выполнения кода можно получим следующее:

  • Переменной a присваивается значение 1. Это самое простое присваивание значения переменной.

  • Переменной a присваивается значение 1, но с явным указанием типа данных Float64 (с плавающей запятой). Теперь ее действительное значение — 1.0.

  • Переменной b присваивается результат выражения 1 + 2, то есть 3.

  • Значение 5 присваивается переменным b и c.

  • Значения из кортежа (10, 20, 30) распаковываются и присваиваются переменным a, b и c соответственно.

Отметим, что переменные в Engee могут быть скалярными, векторными и матричными:

  • Скаляр — имеет единичное значение данных. Задается с помощью переменных в виде целых и вещественных чисел, логических значений и символьных строк. Например:

    v = 1 # Целое число, тип данных Int64
    v = 1.0 # Вещественное число, тип данных Float64
    v = true # Логическое значение, тип данных Bool
    v = "a" # Символьная строка, тип данных String
  • Вектор — это упорядоченная коллекция элементов, которая может быть представлена в виде одномерного массива. Задается с использованием переменных, которые содержат целые или вещественные числа, логические значения или символьные строки. Например:

    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}
  • Матрица — двумерный массив данных, состоящий из строк и столбцов. Матрица может быть использована для представления множества переменных одновременно. Каждый элемент матрицы может быть скалярным значением, таким как целое число, вещественное число, логическое значение или символьная строка. Например:

    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}

Для создания массивов с размерностью больше трех можно использовать значение undef, которое указывает, что массив не инициализирован. Элементы такого массива могут содержать произвольные данные. Выберем одну из двух структур для создания массива:

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

где:

  • T — тип данных массива.

  • N — размерность массива. Может быть задана явно или определяться длиной и количеством dims. Если указана явно, то должна соответствовать длине и количеству dims.

  • dims — кортеж или ряд целочисленных аргументов, соответствующих длине в каждом измерении.

Например:

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

# или

A = Array{Float64}(undef, 2, 3, 4) # Получим трехмерный массив с размерностью 2*3*4, тип данных Float64, размерность задается автоматически
Вывод результата создания массива
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

Подробнее о создании массивов читайте здесь.

Подробнее о типах данных читайте в статье.

Типизация переменных

Engee поддерживает явную и неявную типизации переменных — это значит, что тип данных переменной может быть явно указан (принудительно присвоен) или определен на основе содержимого в процессе выполнения программы. Среди основных типов данных можно выделить:

  • Integer — представляет целые числа, например 1, 100, −1.

  • Float — представляет вещественные числа (с плавающей запятой), например 1.0, −3.14.

  • String — представляет текст, заключенный в кавычки, например "Hello, world!".

  • Array — представляет упорядоченный набор элементов, например, [1, 2, 3].

Рассмотрим явное указание типа данных переменных на следующем примере:

variables article example 1

Здесь для переменной z был явно задан тип данных Int64, который содержит только целые числа. Попытка присвоить переменной z вещественное число 3.14 (соответствующее типу данных Float64) вызвало ошибку (InexactError: Int64).

Попытка присвоить переменной несоответствующий тип данных приводит к ошибке и созданию переменной err в окне переменных. Переменная err хранит информацию о последней ошибке в столбце значений в окне переменных. При необходимости скройте переменную err по аналогии с другим переменными или вручную с помощью объекта nothing:

err = nothing

Рассмотрим неявную типизацию, в которой программа определяет тип данных переменной на основе содержимого:

variables article example 2

При неявной типизации присвоение другого типа данных переменной не вызывает ошибку. Присвоение типа данных происходит на основе содержимого (значения) переменной.

Подробнее ознакомиться с типизацией можно в статье Типы в Engee.

Кортежи

Кортежи (Tuple) в Engee — это упорядоченная и неизменяемая структура данных. Кортежи создаются с помощью круглых скобок ( ) и запятых.

Кортежи описываются с помощью параметризованного типа данных MyType{ParamType1, ParamType2, ParamType3, …​} и представляют собой тип данных элементов в прямой последовательности. При этом элементы кортежа могут иметь разные типы данных. Например, кортеж может содержать целое число, вещественное число и текст в кавычках одновременно. Рассмотрим такой кортеж:

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

В данном случае кортеж имеет тип данных Tuple(Int64, Float64, String).

Кортежи бывают обычными и именованными:

  • Обычный кортеж — это упорядоченная и неизменяемая структура данных. Каждый элемент обычного кортежа может быть любого типа данных. Его элементы располагаются в определенном порядке. К элементам обращаются через их индексы. Рассмотрим такой кортеж на примере:

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

    В примере переменная x содержит три значения — 1, 3.14 и "Hello, world!". Этим значениям соответствуют порядковые индексы [1], [2] и [3] соответственно. С помощью переменной x, содержащей значения обычного кортежа и индекса [3], получили конкретное значение переменной — "Hello, World!", а не весь кортеж целиком.

    special variables article 2 3

  • Именованный кортеж — это особый тип кортежа, похожий на обычный кортеж, но в котором каждый элемент имеет свое имя. Вместо обращения к элементам по индексу как в обычных кортежах, в именованных кортежах также можно обращаться к элементам по их именам. Пример именованного кортежа:

    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

В этом примере поддерживается обращение к элементам кортежа не только через индексы (как в обычном кортеже [1],…​,[n]), но и через имена переменных — x.a, x.b, x.c и т.д.

Поскольку кортеж является неизменяемой структурой — порядок его элементов имеет значение и сохраняется. Неизменность кортежа позволяет работать с множественным присваиванием.

Множественное присваивание — это особенность, позволяющая присваивать значения нескольким переменным одновременно с помощью кортежа. Это средство позволяет избежать временных переменных и улучшить читаемость кода. Например:

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

Здесь создается кортеж tuple с тремя элементами:

  1. Целое число 1.

  2. Вещественное число 3.14.

  3. Строка "Hello, world!".

Далее значения кортежа параллельно присваиваются трем переменным a, b и c соответственно. После выполнения кода переменная a будет содержать значение 1, переменная b 3.14, а переменная c — строку "Hello, world!". Так можно одновременно присвоить несколько переменных без явного указания значений кортежа с помощью интерполяции строк:

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

В этом коде кортеж используется для хранения информации, а параллельное присваивание – для распаковки значений кортежа в отдельные переменные. Затем для вывода информации о человеке применяется интерполяция строк с использованием литерала $:

  1. info = ("Ilya", 25, "Engineer") — создается кортеж info с тремя элементами: строкой "Ilya" (имя), целым числом 25 (возраст) и строкой "Engineer" (род занятий).

  2. name, age, occupation = info — с помощью параллельного присваивания значения кортежа распаковываются в три переменные: name, age и occupation. Теперь переменная name содержит значение "Ilya", age содержит целое число 25, а occupation содержит род занятий "Engineer".

  3. println("Name: $name, Age: $age, Occupation: $occupation") — выводится строка с использованием интерполяции строк (используются $name, $age, и $occupation для вставки значений переменных в строку). В результате выполнения этой строки кода будет выведено:

Name: Ilya, Age: 25, Occupation: Engineer

Таким образом, код создает кортеж с информацией о человеке, а затем распаковывает этот кортеж в отдельные переменные для более удобного доступа к данным и выводит информацию в редактор скриптов или командную строку.

special variables article 1 1

Подробнее о работе с кортежами в Engee описано в статье Функции в Julia. Про интерполяцию в Julia можно узнать из статьи Строки в Julia.

Область видимости

Под блоком кода будем понимать фрагмент кода, заключенный в синтаксическую конструкцию function (функция) или let …​ end. Внутри таких блоков кода можно объявлять локальные переменные, которые будут видны только внутри этого блока и не будут конфликтовать с переменными в других частях кода (например с глобальными переменными).

Область видимости определяет, где в коде вы можете использовать переменную. Так, переменные бывают локальные и глобальные:

  • Локальная (внутренняя) переменная — переменная считается локальной, если она объявлена и определена внутри своего родительского элемента, например функции, объекта, блока кода (в котором определена) и т.д. Локальные переменные видны только внутри той области видимости, в которой определены и недоступны за ее пределами. Рассмотрим локальную переменную на примере:

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

Здесь function example_function() определяет функцию с именем example_function, содержащую локальную переменную x в локальной области видимости.

Дальнейший вызов функции командой example_function() выведет значение переменной x, равному 10:

example_function()

Выполнение кода также создало переменную example-function с типом данных Function в окне переменных Engee.

После выполнения функции, локальная переменная x выходит из области видимости, и становится недоступной за пределами функции. Это означает, что переменная существует только в пределах блока, в котором была определена.

Попробуем вызвать переменную вне локальной области с помощью следующего кода:

println(x)

println error 1

В результате выполнения кода мы получили ошибку, что переменная x не определена. Делаем вывод, что локальная переменная ограничивается областью видимости внутри блока кода (в котором определена) и не определяется извне.

Внешний код не имеет доступа к локальной переменной и выдает ошибку, что локальная переменная не определена.

  • Глобальная (внешняя) переменная — переменная считается глобальной, если определена вне функций, циклов и блоков кода с локальной областью видимости. Глобальная переменная доступна из любой части кода и находится в глобальной области видимости, которая по умолчанию является модулем Main. Внутри модуля Main можно определить подмодули, для которых будут свои глобальные переменные. Рассмотрим глобальную переменную на примере:

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

Этот код устанавливает глобальную переменную global_var и определяет функцию example_function(). example_function() выводит значение глобальной переменной. Для вывода результата функции введите следующий код:

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

После вызова example_function() выводится значение глобальной переменной global_var равное 20. Функция println одновременно выведет два значения переменной, один раз — из функции, другой — за ее пределами.

Использование такой переменной может быть затруднено из-за проблемы затенения, когда имеется переменная с тем же именем в локальной области видимости.

Затенение переменных — это ситуация, когда внутри определенной области видимости (например, внутри функции или блока кода) используется переменная с тем же именем, что и в глобальной области. В результате переменная внутри области видимости затеняет (перезаписывает) переменную с тем же именем из внешней области.

Будьте внимательны при задании переменных с одинаковыми именами — из-за затенения переменная внутри ограниченной области может иметь иное значение или тип, чем переменная с тем же именем в других частях программы.

Для примера рассмотрим код с созданием двух переменных с одинаковыми именами и значениями, и без затенения:

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

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

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

Внутри функции создается локальная переменная с тем же именем global_var, что и глобальная. Это не изменяет глобальную переменную, а создает новую локальную, видимую только внутри функции. В результате выводятся значения обеих переменных, равные 20 (глобальная) и 10 (локальная) соответственно. Таким образом, создание переменных в разных областях видимости помогает избежать проблем с перезаписью переменных с одинаковыми именами.

Переменные в Среде моделирования

Переменные как параметры моделей

В Engee переменные могут быть заданы как параметры модели. Рассмотрим следующий пример:

engee.create("model")

С помощью метода create создана модель в Engee с именем model. Далее присвоим переменную:

m = engee.gcm()

Методом gcm обращаемся к созданной модели и возвращаем объект, представляющий эту модель (в нашем случае GCM — Get Current Model или получение текущей модели). Полученный объект присваивается переменной m.

Далее заменим параметры моделирования с помощью set_param!:

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

В коде изменяются параметры решателя и его шаг с постоянного на переменный.

Посмотрите, изменились ли параметры модели с помощью метода gcm:

engee.get_param("model")

Параметры модели поменялись на новые благодаря переменной m:

variables article 2 10

Далее с помощью метода run запустим симуляцию модели через переменную:

engee.run("m")

После успешной симуляции сохраните модель Engee со свойствами объекта (переменная m) по заданному пути в файловый браузер. В нашем случае:

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

Если файл с таким именем уже существует (в нашем случае model.engee) — опция force=true перезапишет модель данными из переменной m без запроса подтверждения. Это полезно, если нужно перезаписать существующие файлы при сохранении модели.

Модель Engee появится в файловом браузере только после ее сохранения. Сохранение модели позволит работать с ней в будущем и применять весь функционал файлового браузера.

Еще больше возможностей для работы с переменными в среде моделирования представлено в статье Программное управление моделированием.

Переменные как параметры блоков

Вы можете управлять параметрами блоков без программных методов, просто устанавливая отдельные значения параметров как переменные. Рассмотрим пример с моделью, представленной в статье. Модель состоит из блоков Sine Wave и Terminator, для сигнала между ними включена запись:

simout 1

Блок Sine Wave – единственный блок в модели, чьи параметры можно настроить. Нажмите левой кнопкой мыши по блоку и задайте переменные для каждого параметра в соответствии с рисунком:

comparison sinewave 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

Запустим симуляцию модели, задав отдельной переменной имя модели (ранее в статье m) и используя метод engee.run, или вручную, через кнопку Запустить модель start simulation button. На графике будет видно, что блок Sine Wave изменил свои параметры и симуляция прошла корректно:

sine wave result modeling

Устанавливать переменные в качестве параметров блоков полезно в случае, если блок имеет зависимости, влияющие на тот или иной параметр:

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

В качестве параметров блока могут использоваться алгебраические выражения, в том числе с переменными:

one var choose variables

Вывод

Переменная a_var равна 6, тогда амплитуда после умножения на 3 будет равна 18, убедимся в этом на графике результатов симуляции:

one var choose variables graph

Использование разных наборов значений переменных (сохраненных в разных скриптах) позволяет моделировать объекты с одинаковой структурой, но разными параметрами.

Сохранение результатов симуляции

Можно сохранить результаты симуляции модели в оперативную память Engee с помощью переменной simout. Переменная создается автоматически после завершения симуляции модели при включенной записи сигналов модели. Рассмотрим пример с моделью из предыдущего пункта, но используем созданную ранее переменную m.

После завершения симуляции модели в файловом браузере появится переменная simout, хранящая результаты симуляции. Выгрузим данные из simout в переменную m:

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

Визуализируем результаты симуляции с помощью библиотеки Plots и нашей переменной:

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

После завершения работы кода мы увидим следующий график:

simout graph result

Информация о процессе сохранения результатов симуляции описана в статье о Программная обработка результатов симуляции в Engee.