Работа с сигналами разных размерностей
Векторизация и broadcast являются важными приемами при работе с моделями в Engee, которые позволяют эффективно обрабатывать сигналы разных размерностей. Используя векторизацию, можно выполнять вычисления над массивами целиком, избегая явных циклов по элементам, что ускоряет вычисления и упрощает код. Broadcast, в свою очередь, обеспечивает автоматическое согласование размерностей между массивами при выполнении операций, позволяя удобно работать с данными разных форматов.
Использование векторизации и broadcast в моделировании позволяет эффективно обрабатывать и анализировать многомерные данные. Векторизация сокращает количество операций за счет работы с массивами целиком, а broadcast устраняет необходимость ручного согласования размерностей.
Векторизация в моделировании
Векторизация в моделировании позволяет сразу оценивать поведение модели для множества значений параметров, что особенно полезно при анализе чувствительности и тестировании. Это значит, что вместо проведения отдельных запусков для каждого значения можно задать матрицу параметров и получить результаты для всех значений одновременно.
Например, если задать параметру Амплитуда блока Генератор синусоиды матрицу значений [[1, 2]; [3, 4]], то это будет означать, что блок генерирует несколько сигналов с амплитудами, указанными в матрице. На изображении ниже видно, как блок Генератор синусоиды с заданной амплитудой формирует несколько сигналов, что позволяет сразу увидеть результат для разных значений параметра Амплитуда:

Broadcast
Broadcast позволяет согласовывать размерности массивов для выполнения операций над ними. В Engee этот механизм автоматически расширяет размерности массивов, позволяя выполнять операции с несовместимыми на первый взгляд массивами. Например, если один массив имеет размер 2×2, а другой — 1×2, то при сложении массивы будут приведены к совместимым размерностям.
Рассмотрим пример с двумя блоками Константа и одним блоком Сложение. Блок Константа с параметром [3, 4; 5, 6] добавляется к блоку Константа с параметром [2, 3]. При этом broadcast автоматически приводит массивы к совместимым размерам и выполняет поэлементное сложение:
|
|
Результат на выходе блока будет равен [5, 7; 7, 9], так как строки были автоматически согласованы. Этот подход позволяет легко выполнять операции между массивами разных размеров и значительно упрощает работу с многомерными сигналами в моделировании.
Пользовательские типы шин
| Перед изучением раздела о пользовательских типах шин рекомендуется сначала ознакомиться с блоками Создание шины и Выбор из шины. |
В Engee реализована поддержка пользовательских типов данных для шин, представленных типом BusSignal. Этот тип позволяет задавать структуру шин для блоков, которые работают с подобными данными.
Тип BusSignal записывается следующим образом:
BusSignal{Names, Types, Dimensions, :BusName}
где
-
Names— кортеж имен сигналов (например(:a, :b, :c)); -
Types— соответствующие типы (напримерTuple{Int, Float64, Vector{Float64}}); -
Dimensions— размерности каждого сигнала (например((), (), (3,))для скаляров и вектора длины3); -
BusName— имя шины (например:MyBus).
В Types указываются только базовые типы (например, Int, Float64 и т.д.), а не контейнеры (например, Vector{Int}).
Размерности сигналов задаются отдельно в Dimensions.
|
Например, шину с тремя сигналами можно описать так:
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int, Float64, Float64}, ((), (), (3,)), :MyBus}
Здесь:
-
:s1,:s2,:s3— имена сигналов; -
Tuple{Int, Float64, Float64}— типы данных сигналов; -
(), (), (3,)— размерности сигналов (например,s3— вектор длины3). -
MyBus— имя шины.В имени сигнала и шины допустимы любые символы, кроме символа двойной кавычки ( ").
Работать с шинами можно двумя способами: создавать объекты шины (с конкретными данными) или задавать тип шины (только структуру, без данных), чтобы затем использовать его в настройках блоков. Эти два подхода отличаются синтаксисом: используются фигурные {} и круглые () скобки.
Когда использовать фигурные и круглые скобки
| Синтаксис | Когда использовать | Пример |
|---|---|---|
|
Для описания параметрического типа шины или создания объекта шины с данными. |
|
|
Для создания значения типа шины (структуры без данных) — чтобы использовать его в настройках блоков. |
|
|
В В |
Ниже приведены сигнатуры и примеры вызова этих конструкторов.
Создание объекта шины
Для создания объекта шины используются следующие конструкторы структуры BusSignal:
BusSignal{Names, Types, Dimensions, BusName}(x::NamedTuple{TupleNames, TupleTypes})
BusSignal{Names, Types, Dimensions, BusName}(; kwargs...)
BusSignal{Names, Types, Dimensions, BusName}(x::Tuple)
BusSignal{Names, Types, Dimensions, BusName}(varargs...)
Примеры:
# через именованный кортеж
bus_obj = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), (3,)), :MyBus}((a=1, b=[1.0, 2.0, 3.0]))
# через именованные аргументы
bus_obj = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ()), :Coords}(x=10, y=20)
# через кортеж
bus_obj = BusSignal{(:p, :q), Tuple{Float64, Float64}, ((), ()), :Pair}((1.5, 2.5))
# через varargs
bus_obj = BusSignal{(:m, :n), Tuple{Int, Int}, ((), ()), :Nums}(5, 6)
Создание типа шины
Для создания типа шины (структуры без данных) используются следующие методы:
BusSignal(names::Vector{Symbol}, types::Vector{DataType}, dims::Vector{<:Dims}, bus_name::Symbol)
BusSignal(names::NTuple{N, Symbol}, types::NTuple{N, DataType}, dims::NTuple{N, Dims}, bus_name::Symbol)
Примеры:
# через векторы
bus_type = BusSignal([:a, :b], [Int, Float64], [(), (3,)], :MyBus)
# через кортежи
bus_type = BusSignal((:x, :y), (Int, Int), ((), ()), :Coords)
# вложенная шина
bus_type = BusSignal((:a, :b, :c), (Int, Float64, NamedTuple{(:x, :y), Tuple{Int, Int}}), ((), (2,), ()), :MyBus)
|
Также поддерживаются вложенные структуры, где элементом одной шины может быть другая шина. Для указания типа вложенной шины среди типов ее элементов нужно указывать полный тип вложенной шины. Пример вложенной шины:
|
Несмотря на то, что объекты шин в Engee представляют собой именованные кортежи, для их создания предусмотрены специальные функции-конструкторы. Эти функции гарантируют соответствие создаваемых объектов объявленному типу шины.
Получение информации о шине
Для извлечения сведений о параметрах шины можно использовать следующие функции:
get_names_types_dims(::Type{BusSignal{Names, Types, Dimensions, BusName}}) # все параметры сразу
get_bus_names(::Type{BusSignal{Names, Types, Dimensions, BusName}}) # кортеж имен сигналов
get_bus_types(::Type{BusSignal{Names, Types, Dimensions, BusName}}) # кортеж базовых типов
get_bus_dimensions(::Type{BusSignal{Names, Types, Dimensions, BusName}}) # кортеж размерностей
get_bus_name(::Type{BusSignal{Names, Types, Dimensions, BusName}}) # имя шины
Первая функция (get_names_types_dims) возвращает все параметры шины сразу, а остальные позволяют получить имена, типы, размерности и имя шины по отдельности.
Примеры использования функций
# Задаем тип шины
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, Int8}, ((), (2,), (2, 2)), :MyBus}
# Пример шины такого типа: (s1 = 5, s2 = [4.3, 5.4], s3 = Int8[1 2; 3 4])
# Все параметры шины сразу
get_names_types_dims(bus_type)
((:s1, :s2, :s3), (Int64, Float64, Int8), ((), (2,), (2, 2)))
В результате получаем кортеж из трех кортежей, каждый из которых описывает соответствующий параметр типа шины.
# Кортеж имен сигналов
get_bus_names(bus_type)
(:s1, :s2, :s3)
# Кортеж базовых типов
get_bus_types(bus_type)
(Int64, Float64, Int8)
# Кортеж размерностей
get_bus_dimensions(bus_type)
((), (2,), (2, 2))
# Имя шины
get_bus_name(bus_type)
:MyBus
Работа с шинами в блоках базовой библиотеки
Для блоков, таких как Константа, можно указать значение типа шины через параметр Тип данных выхода, после чего задать ее структуру в параметре Тип выходной шины.

В моделировании часто используют пустую шину и заполняют ее поля по мере прохождения сигнала по модели. Для этого, когда тип шины уже задан (например bus_type), создайте пустое значение так:
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int, Float64, Float64}, ((), (), (3,)), :MyBus}
empty_bus = convert(bus_type, 0)
(s1 = 0, s2 = 0.0, s3 = [0.0, 0.0, 0.0])
Такое значение удобно подавать из блока Константа и далее заполнять через Управление шиной.
Рассмотрим пример шины: пусть она содержит поля a, b и c, где c — вложенная шина. Тогда тип шины (Тип выходной шины) можно задать так:
BusSignal((:a,:b,:c),(Int,Float64,BusSignal((:x,:y),(Int,Int),((),()),:InnerBus)),((),(2,),()),:MyBus)
Здесь:
-
Первый аргумент
(:a,:b,:c)— перечисление имен (Names) сигналов шины:a,bиc. -
Второй аргумент
(Int, Float64, BusSignal((:x,:y),(Int,Int),((),()),:InnerBus))— перечисление типов данных (Types) сигналов:-
Int— целое число, тип данных сигналаa; -
Float64— число с плавающей точкой, тип данных сигналаb; -
BusSignal:x,:y),(Int,Int),((),(,:InnerBus)— тип вложенной шины с именами сигналов(:x,:y), типами(Int,Int), размерностями((),())и именем шины:InnerBus; тип данных сигналаc.
-
-
Третий аргумент
((), (2,), ())— размерности (Dimensions) сигналов:-
()— скаляр, размерность сигналаa; -
(2,)— вектор длины2, размерность сигналаb, его значение должно быть вида[2.5, 3.5]; -
()— размерность вложенной шиныс. Ее внутренние размерности заданы внутри описания:InnerBus.
-
-
Четвертый аргумент
:MyBus— имя шины.
|
Список блоков, поддерживающих работу с пользовательскими типами шин: |

