处理不同尺寸的信号
矢量化和广播是在*Engee*中处理模型的重要技术,可让您有效处理不同维度的信号。使用矢量化,您可以对数组整体执行计算,避免显式元素循环,从而加快计算速度并简化代码。反过来,Broadcast 还能在执行操作时自动匹配数组之间的维度,从而方便地处理不同格式的数据。
在建模中使用矢量化和广播可以高效处理和分析多维数据。矢量化通过将数组作为一个整体来处理,减少了操作次数,而广播则消除了手动匹配维度的需要。
建模中的矢量化
仿真中的矢量化可以一次性评估多个参数值的模型行为,这在敏感性分析和测试中特别有用。这意味着,您可以指定一个参数矩阵,同时获得所有参数值的结果,而无需对每个参数值分别运行。
例如,将Sine Wave 程序块的 Amplitude 参数设置为"[[1, 2];[3, 4]]"矩阵值,意味着程序块将生成矩阵中指定振幅的多个信号。下图显示了 Sine Wave 程序块如何生成具有指定振幅的多个信号,您可以立即看到 Amplitude 参数不同值的结果:
广播
广播允许您匹配数组维度,对数组执行操作。在 Engee 中,这种机制会自动扩展数组维度,以允许对看似不兼容的数组进行操作。例如,如果一个数组是 2x2
,而另一个数组是 1x2
,那么将这两个数组相加,就会使数组的尺寸相容。
请看一个例子,其中有两个 Constant 块和一个 Add 块。参数为"[3, 4; 5, 6]"的*常数*块被添加到参数为"[2, 3]"的*常数*块中。在该广播中,程序会自动将数组调整为兼容大小,并执行逐元素添加:
由于字符串已自动匹配,因此程序块输出结果将是"[5, 7; 7, 9]"。这种方法可以轻松地在不同大小的数组之间执行操作,大大简化了建模中多维信号的处理。
自定义总线类型
在学习定制轮胎类型章节之前,建议您首先阅读Bus Creator 和Bus Selector 区块。 |
在*Engee*中,"BusSignal "类型支持自定义总线数据类型。该类型允许您为处理此类数据的区块指定总线结构。例如,*Engee*中使用命名元组(NamedTuple
)作为总线对象:
bus = (s1 = 4, s2 = 5.5, s3 = [1, 2, 3])
总线信号 "类型的参数包括信号名称(Names
)、基本类型(BaseTypes
)和尺寸(Dims
)。例如,有三个信号的总线可描述如下:
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int, Float64, Vector{Int}}, ((), (), (3,))}
这里:
-
:s1
,:s2
,:s3
- 信号名称; -
Tuple{Int, Float64, Vector{Int}}
- 信号数据类型; -
()、()、(3,)`--信号的维数(例如,`s3`是长度为`3`的数组)。
在信号和总线名称中,除反向逗号( "`
)外,允许使用任何字符。
您可以通过函数创建总线对象,也可以将总线类型设置为变量,以便在块设置中使用。为简化总线类型的操作,可使用提取名称、数据类型和尺寸的函数:
get_names_types_dims(::Type{BusSignal{Names, Types, Dimensions}}) where {Names, Types, Dimensions}
get_bus_names(::Type{BusSignal{Names, Types, Dimensions}}) where {Names, Types, Dimensions}
get_bus_types(::Type{BusSignal{Names, Types, Dimensions}}) where {Names, Types, Dimensions}
get_bus_dimensions(::Type{BusSignal{Names, Types, Dimensions}}) where {Names, Types, Dimensions}
第一个函数允许您从总线类型中一次性获取所有总线参数的信息,而后三个函数则分别获取总线名称、类型和尺寸的信息。
函数示例
bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, Int8}, ((), (2,), (2, 2))}
# Экземпляром шины такого типом могла бы выглядеть, например, так: (s1 = 5, s2 = [4.3, 5.4], s3 = Int8[1 2; 3 4])
get_names_types_dims(but_type) # Результат: ((:s1, :s2, :s3), (Int64, Float64, Int8), ((), (2,), (2, 2))) - кортеж из трех кортежей, каждый из которых описывает свой параметр типа шины
get_names(but_type) # Результат: (:s1, :s2, :s3)
get_types(but_type) # Результат: Tuple{Int64, Float64, Int8}, то есть параметр пока что возвращается в исходном виде, что несколько неудобно. В одной из ближайших задач будет исправлено и результатом будет кортеж (Int64, Float64, Int8)
get_dimensions(but_type) # Результат: ((), (2,), (2, 2))
例如,"get_bus_signal_type "方法将命名元组转换为相应的总线类型:
bus = (s1 = 5, s2 = [4.3, 5.4], s3 = (a = 4, b = 5.5))
bus_type = get_bus_signal_type(bus)
# bus_type равен BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, BusSignal{(:a, :b), Tuple{Int64, Float64}, ((), ())}}, ((), (2,), ())}
也支持嵌套结构,其中一个总线的元素可以是另一个总线的元素(上例)。要在元素类型中指定嵌套总线的类型,必须指定嵌套总线的完整类型。 |
尽管 Engee 中的总线对象是以元组命名的,但我们提供了特殊的构造函数来创建它们。这些函数允许您创建总线对象,并确保其结构与指定的总线类型相匹配。以下选项可用于创建总线对象:
-
使用命名元组:
BusSignal{Names, Types, Dimensions}(x::NamedTuple{TupleNames, TupleTypes}) where {Names, Types, Dimensions, TupleNames, TupleTypes}
-
使用命名参数:
BusSignal{Names, Types, Dimensions}(kwargs...) where {Names, Types, Dimensions}
-
车队一如既往
BusSignal{Names, Types, Dimensions}(x::Tuple) where {Names, Types, Dimensions}
创建总线对象的示例
让我们来创建一个总线对象:
bus_type = BusSignal{(:s1, :s2), Tuple{Int64, Float64}, ((), ())}
-
对于已命名的元组:
bus1 = bus_type((s1 = 5, s2 = 6.4))
这里传递的是一个已命名元组,其结构与
bus_type
完全一致。 -
对于已命名参数
bus2 = bus_type(s1 = 5, s2 = 6.4)
此处,总线结构由命名参数创建。信号名称(
s1
、s2
)及其类型必须与`bus_type`的定义一致。 -
对于常规元组
bus3 = bus_type(5, 6.4)
这里的参数是按照
bus_type
(总线类型)定义的顺序传递的。参数的数量、类型和大小必须与总线参数一致。
因此,在每个示例中,都将创建一条相同 bus_type
的总线。但应注意
-
对于
bus1
和bus2
,信号的名称、类型和大小必须与总线参数一致; -
对于
bus3
,所传递参数的数量、类型和大小应符合总线类型的要求。
对于Constant 等程序块,可以通过 Output data types 参数指定总线类型值,然后在 Output bus type 中指定其结构。
例如,让总线值 bus_value = (a = 10, b = [2.5, 3.5], c = (x = 1, y = 2))
。则其类型指定为
bus_type = BusSignal{(:a, :b, :c), Tuple{Int, Vector{Float64}, NamedTuple{(:x, :y), Tuple{Int, Int>>}, ((), (2,), ((), ())}}
支持自定义总线类型操作的区块列表: |