Engee 功能单元的高级设置¶
Engee 功能块允许用户将其算法嵌入 Engee 模型,而无需在基元上重新绘制。用户可以完全控制功能块的行为定义。
下面举例说明如何行使这种控制权。
重载输出参数¶
示例转换为向量¶
您需要实现一个将输入转换为矢量的程序块。
不需要对输入进行任何操作。唯一需要做的就是将输入转换为矢量。为此,我们需要计算向量的长度并获取其元素的类型。
这项任务在 vectorize_it 模型中由 VectorizeIT 模块实现。

让我们来看看 Engee 函数的代码。我们感兴趣的是程序块的函数以及覆盖维数和输出类型的方法。
让我们从函数的定义开始:
``朱莉娅
function (c::Block)(t::Real, cache, in1)
如果 in1 是抽象矢量
cache .= in1
elseif in1 是抽象数组
cache .= vec(in1)
elseif in1 isa Real
cache[1] = in1
结束
结束
这里我们需要注意 cache 变量。如果启用`Use external cache for non-scalar output` 设置,则为 Engee 功能块声明该变量:

只有当程序块只有一个***输出时,该设置才有效。
接下来,我们来定义维度和输出类型。您可以通过选择_覆盖类型继承方法_和_覆盖尺寸继承方法_来分别定义它们。不过,您也可以在一个函数中定义它们。为此,请选择 _Use common method for types and dimensions inheritance_(类型和维度继承使用通用方法)(启用这两种方法后,该选项将变为可用):

在本例中,后一个选项是最合适的。让我们来看看该方法的代码:
``朱莉娅
function propagate_types_and_dimensions(inputs_types::Vector{DataType}, inputs_dimensions::Vector{<:Dimensions})::Tuple{Vector{DataType}, Vector{<:Dimensions}}
in1t = 输入类型[1]
in1d = 输入尺寸[1]
outd = (prod(in1d), )
返回 ([eltype(in1t)], [outd]);
结束
输入是两个向量:输入的类型及其维数。在输出端,必须形成一个由两个向量组成的元组:
需要单独指出的是,维度是作为一个元组(即函数size()
的结果)传递的,而数据类型是作为一个元素类型传递的。在我们的例子中,它不是Vector{eltype(in1t)}
,而只是eltype(in1t)
。
严格的块类型和结果缓存¶
示例计算方程组¶
Julia 语言允许动态分配内存,但这会导致代码速度变慢。因此,Engee 功能块会对仿真速度产生负面影响。本例演示了如何最大限度地提高 Engee 功能块的性能。
使用一个单独的变量来存储程序块的工作结果(缓存),可以摆脱动态内存分配,大大加快程序块的工作速度。我们来看一个简单的例子:
Without cache:
1.056730 seconds (1000.00 k allocations: 76.294 MiB, 90.59% gc time)
With cache:
0.023816 seconds
从示例中可以看出,应用缓存是一种非常可取的做法。对于 Engee Function,我们可以通过两种方式定义缓存:
1.指定 Use external cache for non-scalar output 复选框(用于单一输出); 2.
2.在程序块定义中手动创建缓存
为最大限度地提高性能,块结构中的所有字段都应具有特定类型和固定大小。
举例来说,cached_calcs 模型和ComputingTypesRuntime 块。

该程序块实现了以下方程组:
$$
\begin{cases}
y_1 = sin(u_1) + cos(u_2)\\
y_2 = u_1 + u_2
\end{cases}
$$
由于输入可能是非标量的,因此该系统的实现需要使用缓存。
为了创建缓存,我们需要定义一个类型化结构。通常的定义组件结构方法不允许我们这样做,但我们可以启用使用普通代码设置,并在 "普通 "代码中为程序块创建结构。定义程序块结构的代码如下:
``朱莉娅
struct Block{CacheType1,CacheType2} <: AbstractCausalComponent
c1::CacheType1;
c2::CacheType2;
函数 Block()
sz1 = OUTPUT_SIGNAL_ATTRIBUTES[1].dimensions;
sz2 = OUTPUT_SIGNAL_ATTRIBUTES[2].dimensions;
type1 = OUTPUT_SIGNAL_ATTRIBUTES[1].type;
type2 = OUTPUT_SIGNAL_ATTRIBUTES[2].type;
c1 = isempty(d1) ?zero(t1) :zeros(t1, d1)
c2 = isempty(d2) ?zero(t2) : zeros(t2, d2)
new{typeof(c1), typeof(c2)}(c1,c2);
结束
结束
需要注意的是,缓存类型和维度是从输出中派生出来的。而输出的属性是在_Use common method for types and dimensions inheritance_(使用类型和维度继承的常用方法)中通过方程组的单一计算得出的:
``朱利亚
function propagate_types_and_dimensions(inputs_types::Vector{DataType}、
inputs_dimensions::Vector{<:Dimensions})
::Tuple{Vector{DataType}, Vector{<:Dimensions}}
in1t = 输入类型[1]
in2t = 输入类型[2]
in1d = 输入维数[1]
in2d = 输入维度[2]
mockin1 = zeros(in1t, in1d)
mockin2 = zeros(in2t, in2d)
mockout1 = sin.(mockin1) .+ cos.(mockin2)
mockout2 = mockin1 .+ mockin2
return ([eltype(mockout1), eltype(mockout1)], [size(mockout1), size(mockout1)])
结束
这种方法只会导致一次内存分配。
直接穿通¶
举例说明:打破代数循环¶
作为一个例子,让我们考虑一下打破代数循环的实际任务。建议使用延迟块打破代数循环。但这种方法会导致结果失真。而且 IC 块并不能打破循环。
因此,让我们创建这样一个 Engee 功能块来实现下面的方程组并打破代数循环:
$$
y(t) =
\begin{cases}
IC, t = 0\\
u, t > 0
\end{cases}
$$
为了打破代数循环,我们需要删除直接穿通属性。这可以通过 Engee 功能块的设置来实现,也可以通过设置_覆盖直接穿通设置方法_选项来编程实现。
示例实现可在LoopBreaker块中的loopbreaker模型中找到。

程序块的工作原理如下:设置参数IC
,该参数将在模型初始化时和模拟时间的初始时刻输出。在接下来的仿真步骤中,程序块的输入将立即传递到输出。让我们来看看这种算法的实现:
区块的定义如下
``朱莉娅
struct Block{T} <: AbstractCausalComponent
InCn::T
函数 Block()
InCn_t = INPUT_SIGNAL_ATTRIBUTES[1].type;
new{InCn_t}(IC)
结束
结束
其函数为
```julia
函数 (c::Block)(t::Real, in1)
如果 t<=0.0
返回 c.InCn
否则
返回 in1
结束
结束
此外,还定义了_覆盖直接穿通设置方法_。这里不需要逻辑或计算,因此我们只需打开循环:
``朱莉娅
function direct_feedthroughs()::Vector{Bool} 返回
return [false] 返回 [false
结束
让我们确保模拟工作正常:
设置采样时间¶
示例"减慢 "输出¶
有些模型和算法可能需要在一定时间内刷新程序块的输出。对于 Engee 功能块,用户有两种方法来实现这种要求:
- 通过程序块参数明确指定采样周期
- 编写自己的算法来确定所需的周期
本示例采用第二种方法。
考虑 sample_time_override 模型:

在功能上,Engee 功能块 ST_Orig 和 ST_Override 与高速缓存和严格类型部分的算法相对应。不过,现在的要求是产生周期最小的结果。让我们看看内置的采样周期继承机制是如何工作的:

我们可以看到,周期 D1 已被继承,即最高周期。但我们的任务是继承 D2。为此,让我们在 Engee 函数代码中启用 "覆盖采样时间继承方法 "部分,并选择最慢的周期:
``朱莉娅
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}、
fixed_solver::Bool)::SampleTime
max_period = argmax(t -> t.period, inputs_sample_times)
返回 max_period
结束
执行此方法后,结果如下

可以看出,现在继承了所需的周期,D2
#### 重要!
第一个参数是一个 SampleTime 类型的命名元组向量,格式为
``朱莉娅
SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
此元组的模式字段取以下值::离散、:连续、:常量、:FiM。
第二个参数是一个标志,表示求解器的类别。如果标志为 true,则选择恒步求解器。
本文讨论了 Engee 功能块的高级编程方法。通过这些方法,您可以详细定义所需的程序块行为,并实现最高性能。本资料不包括编写块代码时的典型错误和编写安全代码的技巧。
{"id": "dc3fc2ec-72de-4ec4-bba0-4da76f928d2c", "data": [{"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.513888888888889, "title": {"text": ""}, "len": 0.9525371828521435, "x": 0.9934383202099737}, "name": "Step", "yaxis": "y", "legendgroup": "Step", "line": {"color": "rgba(0, 154, 250, 1.000)", "shape": "linear", "dash": "solid", "width": 1}, "y": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "type": "scatter", "x": [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.2, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.3, 1.31, 1.32, 1.33, 1.34, 1.35, 1.36, 1.37, 1.38, 1.39, 1.4, 1.41, 1.42, 1.43, 1.44, 1.45, 1.46, 1.47, 1.48, 1.49, 1.5, 1.51, 1.52, 1.53, 1.54, 1.55, 1.56, 1.57, 1.58, 1.59, 1.6, 1.61, 1.62, 1.63, 1.64, 1.65, 1.66, 1.67, 1.68, 1.69, 1.7, 1.71, 1.72, 1.73, 1.74, 1.75, 1.76, 1.77, 1.78, 1.79, 1.8, 1.81, 1.82, 1.83, 1.84, 1.85, 1.86, 1.87, 1.88, 1.89, 1.9, 1.91, 1.92, 1.93, 1.94, 1.95, 1.96, 1.97, 1.98, 1.99, 2]}, {"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.513888888888889, "title": {"text": ""}, "len": 0.9525371828521435, "x": 0.9934383202099737}, "name": "LoopBreaker", "yaxis": "y", "legendgroup": "LoopBreaker", "line": {"color": "rgba(227, 111, 71, 1.000)", "shape": "linear", "dash": "solid", "width": 1}, "y": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], "type": "scatter", "x": [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.2, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.3, 1.31, 1.32, 1.33, 1.34, 1.35, 1.36, 1.37, 1.38, 1.39, 1.4, 1.41, 1.42, 1.43, 1.44, 1.45, 1.46, 1.47, 1.48, 1.49, 1.5, 1.51, 1.52, 1.53, 1.54, 1.55, 1.56, 1.57, 1.58, 1.59, 1.6, 1.61, 1.62, 1.63, 1.64, 1.65, 1.66, 1.67, 1.68, 1.69, 1.7, 1.71, 1.72, 1.73, 1.74, 1.75, 1.76, 1.77, 1.78, 1.79, 1.8, 1.81, 1.82, 1.83, 1.84, 1.85, 1.86, 1.87, 1.88, 1.89, 1.9, 1.91, 1.92, 1.93, 1.94, 1.95, 1.96, 1.97, 1.98, 1.99, 2]}], "config": {"showlegend": true, "xaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [0, 0.5, 1, 1.5, 2], "range": [-0.06000000000000005, 2.06], "domain": [0.05100612423447069, 0.9934383202099737], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["0.0", "0.5", "1.0", "1.5", "2.0"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "y", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "paper_bgcolor": "rgba(255, 255, 255, 1.000)", "annotations": [], "height": 400, "margin": {"l": 0, "b": 20, "r": 0, "t": 20}, "plot_bgcolor": "rgba(255, 255, 255, 1.000)", "yaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [0, 25, 50, 75, 100], "range": [-3, 103], "domain": [0.03762029746281716, 0.9901574803149606], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["0", "25", "50", "75", "100"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "x", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "legend": {"yanchor": "top", "xanchor": "left", "bordercolor": "rgba(0, 0, 0, 1)", "bgcolor": "rgba(255, 255, 255, 1.000)", "borderwidth": 1, "tracegroupgap": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "title": {"font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}, "text": ""}, "traceorder": "normal", "x": 0.07}, "width": 1066.84375}}