Engee Function
在模型中使用Julia代码。
类型: EngeeFunction
图书馆中的路径:
|
资料描述
座 Engee Function 允许您在*Engee*模型中使用Julia代码。
在页面上阅读更多关于Julia编程语言的信息 编程. |
在街区里 Engee Function 使用*Julia*语言的大部分功能是可以接受的。 但是,不提供在块中使用*Pkg*包管理器。 |
使用
要将Julia代码集成到*Engee*模型中,您必须:
-
向模型添加块 Engee Function 从*基本部分/自定义函数*块库
;
-
在 设置窗口
在块的*主*选项卡上 Engee Function 点击*编辑源代码*按钮打开源代码编辑器(EngeeFunctionCode):
源代码单元格
EngeeFunctionCode源代码编辑器由带有Julia代码的功能单元组成。 默认情况下,有三个单元格可用:辅助(不可编辑), Component struct code 和 Step method code (单元格可以隐藏):
要连接其他源代码文件,可以使用单元格中的’include()`函数 Common code (有关单元格的描述,请参阅 下文):
|
整个块代码 Engee Function 你可以在单元格中写 Common code 通过获得对组件结构、签名和函数数量的完全控制。 |
要添加/删除其他功能单元,请单击"管理方法"按钮 并选中/取消选中相应的单元格。:
→
每个单元负责块的独特功能 Engee Function . 让我们更详细地看看它们:
-
信息单元格(不可编辑)-自动显示块变量 Engee Function 、输入和输出信号的属性(维度、类型、离散度)和其他用户定义的参数。 其内容根据块设置动态更新。 单元格始终处于活动状态,但未在"方法管理"菜单中选择该单元格。
. 它有一个半透明的文本,显示提示。
更改块参数不仅会影响信息单元格的内容,还会影响其他单元格中的提示,这些提示也会以半透明文本显示。 -
定义组件结构-添加单元格 Component struct code ,其中定义了块组件的结构 Engee Function (继承自’AbstractCausalComponent’类型)。 结构字段在不可编辑字符串`struct Block<:AbstractCausalComponent`和`end’之间定义。 默认情况下,`g’参数被创建,由`gain`块的值初始化,并且`Block()'构造函数不接受参数。
-
使用通用代码—-添加单元格 *Common code ,其中代码以自由形式编写。 默认情况下,单元格为空。 例如,如果标准结构在 Component struct code 如果它不适合(由于’struct Block<:AbstractCausalComponent’的不可编辑性`,那么您可以禁用*Define component struct*以删除单元格并手动在 Common code . 这同样适用于方法管理菜单中的任何功能。
-而不是标准的单元格,你可以编写自己的代码 Common code .
声明一个组件和一个函子是需要工作的。 Engee Function . 如果*Define component structure*和*Define step method*被禁用,那么它们的代码必须设置在 Common code 否则,该块将不起作用。 要重新定义继承函数(复盖,见下文),您首先需要包含相应的单元格,擦除其内容,然后在 Common code . -
定义步骤方法-添加单元格 Step method code ,其定义了计算块的输出信号的步进方法 Engee Function . 方法签名是根据端口选项卡中相应端口标签的值自动生成的。 该方法表示为函子(有关详细信息,请参阅 这里)并且在模拟的每个步骤中被调用。 字段在`function(c::Block)(t::Real,in1)`和`end’的不可编辑行之间定义。 第一个参数是`t::Real’是仿真时间,然后传输输入信号变量。 输出的计算值使用关键字`return’返回。
-
定义更新方法-添加一个单元格*更新方法代码*其中方法`更新!'更新块的内部状态 Engee Function 在模拟的每一步。 第一个参数`c::Block`是块结构,第二个`t::Real`是仿真时间,然后传输输入信号。 如果块没有内部状态,则该方法可以保持为空并简单地返回’c'。
如果您需要定义多个’更新!'方法`或设置具有不同签名的方法,然后您可以禁用*更新方法代码*单元格并在单元格中编写代码 Common code . 编译器会自动检测’更新的存在!'方法。'并将其用于模拟。 -
定义终止方法代码-添加*终止方法代码*单元格,该单元格在块模拟完成时执行 Engee Function (使用’终止!'的方法)。 `C::Block’的第一个参数是块结构。 默认情况下,该方法不执行任何其他操作,只返回’c'。
-
复盖类型继承方法-添加*Types继承方法*单元格,该单元格复盖类型继承方法。
详细了解类型继承方法
设置类型继承方法:
-
如果未选中该复选框(默认情况下),则根据输入/输出端口描述中指定的规则继承输入/输出端口类型。
-
如果选中该框,则输入/输出端口类型将根据源代码中的"propagate_types"函数*Types继承方法*单元格中指定的规则继承。
-
Propagate_types函数接受一个参数,一个类型向量,每个输入信号都有一个类型,并返回一个输出类型向量。
-
默认单元格代码:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} # 返回输出信号类型数组的函数。 # 如果使用默认算法,它将被忽略。 # 在这种情况下,输入信号的类型和 # '增益'块的参数。 input_type = first(inputs_types) # promote_type返回给出参数类型的类型。 # 在与这些类型的对象进行算术运算期间。 output_type = promote_type(input_type, eltype(gain)) return [output_type] end
这里,输入信号的公共元素和在设置参数部分的块设置中指定的参数的元素被取为继承类型。
-
-
Override dimensions继承方法-添加一个cell*Dimensions继承方法*,它重新定义了维度的继承方法。
详细了解维度继承的方法
设置维度的继承方法:
-
如果未选中该复选框(默认情况下),则根据输入/输出端口描述中指定的规则继承输入/输出端口的尺寸。
-
如果选中该框,则输入/输出端口的维度将根据源代码中*维度继承方法*单元格中的函数"propagate_dimensions"中指定的规则继承。
-
'Propagate_dimensions’函数为每个输入信号获取元组(维度)数组,并为输出返回维度数组。
-
默认单元格代码:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} # 返回输出信号维度数组的函数。 # 如果使用默认算法,它将被忽略。 # 在这种情况下,输入信号的尺寸被考虑在内和 # '增益'块的参数。 input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* gain return [size(mock_output)] end
在这里,要继承维度,需要一个具有必要维度(mock_input)的数组,它由零组成,并乘以设置参数部分的块设置中指定的参数元素,然后取其维度。
-
-
使用通用方法进行类型和维度继承-添加*通用类型和维度继承方法*单元格,该单元格使用通用方法同时重新定义类型和维度的继承。
详细了解类型和维度的继承方法
与类型(类型继承方法)或维度(维度继承方法)的特定方法不同,一般方法会同时包括类型和维度:
-
如果未选中(默认情况下),则忽略常规方法,输入/输出端口的维度和类型将根据输入/输出端口描述或继承方法*复盖类型继承方法*(如果启用)和*复盖维度继承
-
如果选中该框,则输入/输出端口的维度和类型将根据源代码中函数`propagate_types_and_dimensions`单元格*通用类型和维度继承方法*中指定的规则继承。
默认单元格代码:
function propagate_types_and_dimensions(inputs_types::Vector{DataType}, inputs_dimensions::Vector{<:Dimensions})::Tuple{Vector{DataType}, Vector{<:Dimensions}} # 返回信号类型数组和数组的函数 # 输出信号的尺寸。 这个功能可以使用 # 如有必要,在同一时间重新定义继承算法。 # 类型的信号,以及用于维度的继承的算法。 outputs_types = propagate_types(inputs_types) outputs_dimensions = propagate_dimensions(inputs_dimensions) return outputs_types, outputs_dimensions end
依赖关系
要使用此单元格,请选中*Override type inheritance method*和*Override dimensions inheritance method*框。
-
-
Override sample time inheritance method-添加单元格*Sample times inheritance method*,该单元格重新定义计算步骤的继承方法。
详细了解计算步骤的继承方法
计算步骤’SampleTime`和’propagate_sample_times’函数的结构代码不会自动添加到旧*Engee*模型的EngeeFunctionCode源代码中。 要细化旧模型,请自行添加计算步骤的结构和函数。 设置计算步骤的继承方法:
-
如果未选中该复选框(默认情况下),则使用高级选项卡的*采样时间继承方法*参数中的计算步骤的预设继承方法(默认情况下为`Default`)。 阅读有关预安装方法的更多信息。 下面。
-
如果选中该复选框,则忽略高级选项卡的预设方法(该参数不可用),则使用来自EngeeFunctionCode源代码中的*Sample times inheritance method*单元格的独立方法。
为了使独立方法工作,您需要找到`propagate_sample_times`函数的字符串,并手动设置所需的计算步骤。
默认单元格代码:
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime # 返回块的采样时间的函数。 # 它仅在"自定义"继承模式下使用。 # Fixed_solver参数指示是否正在使用求解器。 # 具有恒定步长(true)或具有可变步长(false)。 # 使用时间继承的更复杂示例 # 可以在文档中查看块采样。 return first(inputs_sample_times) end
其中计算步骤具有结构:
const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
-
-
复盖直接馈送设置方法-添加一个单元*直接馈送设置方法*定义直接端到端连接。
详细了解直接端到端连接的定义
定义直接的端到端连接:
-
如果未选中该复选框(默认情况下),则直接端到端连接不可用。 这意味着输出信号将不受输入端口的值控制,并允许单元开环。
-
如果选择此选项,则直接端到端连接可用。 这意味着输出信号直接由输入端口的值控制。
默认单元格代码:
function direct_feedthroughs()::Vector{Bool} # 返回布尔值数组的函数。, # 端到端连接。 如果数组的第i个元素为true, # 第i端口具有端到端连接。 # 如果使用默认算法,它将被忽略。 return [true] end
例如_:
function direct_feedthroughs()::Vector{Bool} if gain == 2 return [false] else return [true] end end
-
获取属性的常量和函数
为了找出块可执行代码中的类型,大小和其他辅助信息,请在代码中使用以下常量 Engee Function :
-
'BLOCK_NAME’是块的名称。 添加到Engee画布的每个块都有一个可以通过此常量访问的名称。 例如,您可以在错误初始化期间引用BLOCK_NAME来输出其中的块名称。
-
'START_TIME—-从模型设置开始模拟。
-
'STOP_TIME—-从模型设置中结束模拟。
-
'INPUT_SIGNAL_ATTRIBUTES'—每个输入端口的属性列表。 例如,要找出第一个输入信号的属性,请使用’INPUT_SIGNAL_ATTRIBUTES[1]`,其中`1`是块的第一个输入端口。 Engee Function .
-
'OUTPUT_SIGNAL_ATTRIBUTES'—每个输出端口的属性列表。 例如,要找出第一个输出信号的属性,请使用’OUTPUT_SIGNAL_ATTRIBUTES[1]`,其中`1`是块的第一个输出端口。 Engee Function .
要了解有关特定块端口的更多信息,您可以通过添加点来参考其信号的属性 . 常量`INPUT_SIGNAL_ATTRIBUTES[i]之后,其中
[i]是输入端口号,`OUTPUT_SIGNAL_ATTRIBUTES[i]
,其中`[i]`是输出端口号,分别。 您可以通过以下联系功能了解更多信息:
-
'dimensions'-信号的维度。 它可以缩短为’dims'。
-
"类型—-信号的类型。 它可以缩短为’tp'。
-
'sample_time’是计算步骤。 它是一种类似于信号属性的结构,可以通过点访问。 .. 提供两种转换功能:
-
'周期—-计算步骤的周期。 完整的转换函数是’sample_time。期'。 它可以缩短为’St.p'。
-
'offset'-计算步骤的偏移。 完整的转换函数是’sample_time。偏移'。 它可以缩短为’St.o'。
-
-
'direct_feedthrough—-指示循环端口是否打开。 它仅用于输入端口(仅检查输入端口的属性)。 它可以缩短为’df'。
模型示例 Engee Function 具有所有常量和转换函数:
struct Block <: AbstractCausalComponent end
function (c::Block)(t::Real, x1, x2)
y1 = [START_TIME, STOP_TIME]
y2 = collect(INPUT_SIGNAL_ATTRIBUTES[1].dimensions)
y3 = OUTPUT_SIGNAL_ATTRIBUTES[1].dims[1]
y4 = (INPUT_SIGNAL_ATTRIBUTES[2].type == Int64)
y5 = (OUTPUT_SIGNAL_ATTRIBUTES[4].tp == Bool)
y6 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.period
y7 = OUTPUT_SIGNAL_ATTRIBUTES[1].st.p
y8 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.offset
y9 = OUTPUT_SIGNAL_ATTRIBUTES[2].st.o
y10 = INPUT_SIGNAL_ATTRIBUTES[1].direct_feedthrough
y11 = INPUT_SIGNAL_ATTRIBUTES[2].df
return (y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11)
end
使用固定点和自定义总线
座 Engee Function ,并且还 命令提示符 Engee,支持定点(
Fixed
)和自定义总线类型(`BusSignal')。 这些构造有助于控制处理整数、实数、固定和复杂数据类型时的操作行为。
固定点(`Fixed-Point')的类型和功能
在文章中 Engee 中的定点算术(固定点 它描述了如何在*Engee*中使用固定点。 文章中给出的构造对块也有效 Engee Function ,这是块支持的方式:
-
'FixedPoint’是所有固定数字的抽象类型`;
-
'Fixed’是一种特定类型的固定数字,通过指示位表示手动创建。;
-
'菲(。..)`-使用数值和表示参数创建`Fixed’类型的值;
-
'fixdt(。..)'-创建一个固定类型,指示符号、宽度和小数位数。
例如:
a = fi(5, 1, 16, 4) # 有符号数,16位,4分数
b = fi(10, 0, 8, 0) # 8位的无符号整数
T = fixdt(1, 24, 8) # 24位的固定类型,其中8位是小数
c = Fixed(0x01ff, T) # 直接从位表示创建固定数字
固定号码 Engee Function 五月:
-
作为参数发送;
-
在组件结构中使用(cell*Component struct code*);
-
使用*Step方法代码*单元格中的’emul','esum','ediv’与它们一起执行算术(有关更多信息,请参阅转换函数和类型化算术运算’econvert','esum','emul`,'ediv`);
-
应用于`propagate_types(…)','propagate_dimensions(…)`.
使用自定义总线类型("BusSignal")
自定义总线类型允许您以结构化元组(NamedTuple
)的形式指定输入和输出信号的类型,其中包含名称,类型和尺寸的描述 Engee Function . 可用功能:
-
'BusSignal{...}'是具有名称、类型和尺寸的总线类型。;
-
'get_bus_names(type`—-获取信号名称列表;
-
'get_bus_types(type)—-获取信号的类型;
-
'get_bus_dimensions(type)—-获取信号的尺寸;
-
'get_names_types_dims(type)—-一次获取所有内容;
-
'get_bus_signal_type(::NamedTuple)`—通过值确定`BusSignal`的类型。
一个确定和分析轮胎类型的例子:
bus = (a = 1, b = [2.0, 3.0], c = (x = 3, y = 4))
bus_type = get_bus_signal_type(bus)
get_bus_names(bus_type) # => (:a, :b, :c)
get_bus_types(bus_type) # => (Int64, Vector{Float64}, NamedTuple{...})
get_bus_dimensions(bus_type) # => ((), (2,), ((), ()))
你可以明确地描述轮胎:
MyBus = BusSignal{(:s1, :s2), Tuple{Int64, Float64}, ((), ())}
signal = MyBus((s1 = 5, s2 = 6.4))
还支持嵌套总线。:
Inner = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ())}
Outer = BusSignal{(:a, :b), Tuple{Float64, Inner}, ((), ())}
块中的用法 Engee Function
'Fixed`和’BusSignal’类型可以用于块的不同部分。 Engee Function :
-
在块参数中,您可以通过`fi(设置固定点值。..)',并且还将总线结构传递为’NamedTuple'。 总线类型可以通过’get_bus_signal_type(…)'.
-
在*Common code*单元格中,您可以定义数据类型('Fixed`’BusSignal{…}'),组件结构’结构块'),创建辅助函数或初始化值。 如果禁用了*步骤方法代码*或*组件结构代码*,您也可以在此处移动主逻辑。
-
在*Component structure code*单元格中,当描述结构的字段时,如果字段是复合信号,则可以使用`Fixed`类型的值以及`BusSignal`类型。
-
*步骤方法代码*单元实现块的主要逻辑。 在这里,固定数字和总线信号可以参与计算、比较和结构处理。
-
在*Types继承方法*单元格中,您可以使用’Fixed`和’BusSignal’类型来分析输入并设置组件的输出类型。
-
在*维度继承方法*单元中,您可以使用总线中的"固定"数据和值来确定输出信号的维度。
-
在*更新方法代码*单元格中,如果块具有内部状态,则可以使用"Fixed"或"BusSignal"等字段在模拟的每个步骤中存储或更改此状态。
-
在*Terminate方法代码*单元格中,如果结构包含适当的字段,则这些类型可用于记录最终状态。
-
在*Common types and dimensions inheritance method*cell中,如果两种类型和维度都需要同时处理,`Fixed`和`BusSignal`也可以参与计算逻辑。
因此,'固定`,'fi(。..)`,'fixdt(。..)','BusSignal’和’get_bus’功能适用于块的配置和操作的所有方面 Engee Function -无论是在执行阶段还是在信号的类型和维度的生成阶段。 它们可以在所有单元格的参数和源代码中自由使用(如果它们正常工作)。
转换函数和类型化算术运算’econvert','esum','emul`,'ediv`
在街区里 Engee Function ,以及在 命令行 Engee*功能可用,允许您执行指定输出类型的算术运算,使用舍入和溢出控制在类型之间转换值,并使用专用规则(`_promote_type')预先确定结果类型。 这些函数用于逻辑 *Engee Function ,写在源代码单元格*步骤方法代码*,*通用代码*和其他。
可用功能:
-
"econvert"-类型之间的值转换;
-
'esum`,'esub`,'emul','ediv—-设置输出类型的算术运算;
-
"阿穆尔!'-带有结果缓存的矩阵乘法;
-
`*_promote_type—-基于输入类型的输出函数。
这些函数可以应用于标量和数组。 结果总是以用户显式指定的类型返回(或自动输出),这使得块行为稳定和预期。 |
支持的类型
此集合中的所有函数都适用于以下类型:
-
实数’Float16`,'Float32`,'Float64`
-
整数’Int8','Int16','Int32','Int64','Int128','UInt8','UInt16','UInt32','UInt64','UInt128`
-
逻辑:'Bool`
-
Fixed:'Fixed`(通过’fi(创建。..)
或’fixdt(。..)
) -
复杂’复杂{T}`,其中’T’是上述任何类型
函数也可以使用点语法()与数组和矢量化表达式一起使用。
). 接下来,让我们更详细地看一下这些功能。
使用"econvert"转换数字
"Econvert"函数允许您通过指定舍入方法和溢出处理方法将值转换为指定类型。:
econvert(type::Type{T1}, var::T2; rounding_mode::RoundingMode=RoundDown, overflow::Bool=true)
这里:
-
'type'-将值转换为的类型。 如果’var’是复数,则指定基实数类型。 例如,'econvert(Float32,Complex{Int16}(1+2im))
返回’复杂{Float32}
; -
'var—-转换的值;
-
'rounding_mode—-舍入方法('RoundDown`’RoundUp`,'RoundNearest`,'RoundNearestTiesAway','RoundToZero`);
-
'overflow—-如果’true`,则使用溢出行为(wrap);如果`false',则启用饱和—值限制在类型的范围内。
"Econvert"函数可用于比较前的类型转换,在算术表达式内部,在条件和其他情况下的信号处理过程中。
使用`esum`,esub
,emul
,ediv
,emul的类型控制算术!
类型化算术运算的函数’esum`,esub
,emul
,`ediv','emul!'具有相同的接口:
esum(x1, x2, out_type, overflow=true, rm=RoundDown)
esub(x1, x2, out_type, overflow=true, rm=RoundDown)
emul(x1, x2, out_type, overflow=true, rm=RoundDown)
ediv(x1, x2, out_type, overflow=true, rm=RoundDown)
emul!(cache, x1, x2, out_type, overflow=true, rm=RoundDown)
争论:
-
'x1','x2—-操作的参数(允许标量或数组);
-
'out_type’是生成和返回结果的类型。;
-
'overflow'-启用溢出('true`)或饱和('false`);
-
'rm’是舍入方法(见上面的列表)。
函数’esum'、'esub`、`emul`和`ediv`返回与指定的’out_type’对应的值。
埃米尔!'function’用于在处理矩阵时加快速度:它不会创建新数组,而是将结果写入已经分配的`缓存'。 `Eltype(cache)`匹配`out_type’是很重要的。
结果取决于`out_type’的类型:它不仅影响最终结果,还影响中间计算的行为,这对于`Fixed`和`Complex’尤其重要。
使用`*_promote_type`推断结果类型
如果块参数(例如,SumType`或`multitype')设置为字符串`"Inherit"
,则使用函数`*_promote_type’根据输入数据类型自动确定结果类型。
如果您需要计算操作的结果类型,请使用列出的函数:
-
定义添加相同类型`T`的’n’值时的输出类型:
sum_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
-
定义通过’n’值添加两个不同类型`T1`和`T2’时的输出类型:
sum_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
-
定义在添加任意数量的具有不同类型的参数时的输出类型。:
sum_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
-
定义在添加相同类型`T`的`n`值时累加器的类型:
sum_accumulator_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
-
通过`n`值添加两个不同类型的`T1`和`T2`时确定累加器的类型。:
sum_accumulator_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
-
定义在添加任意数量的具有不同类型的参数时累加器的类型。:
sum_accumulator_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
-
定义将相同类型的值乘以"T"时的输出类型:
mul_promote_type(::Type{T}, hdlsetup::Bool=false)
-
定义两个不同类型`T1`和`T2`相乘时的输出类型:
mul_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)
-
定义在划分类型为"T"的单个值时的输出类型:
div_promote_type(::Type{T}, hdlsetup::Bool=false)
-
定义将类型为"T1"的值除以类型为"T2"的值时的输出类型:
div_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)
这里:
-
'hdlsetup`是一个逻辑参数,用于确定是否考虑到硬件平台('TargetHardware')的具体情况。 默认情况下:
hdlsetup = TargetHardware == "C" ? false : true
-
这些函数方便地用于*类型继承方法*单元格中,以根据输入和参数准确确定输出块类型。
使用示例
下面是实现表达式`a*x+b’的组件的示例。 中间乘法和最终加法的类型使用’MulType`和’SumType’参数设置。 如果指定了"继承"`则自动输出该类型。
使用a*x+b的例子_
参数 Engee Function :
a = fi(5, 1, 16, 4)
b = fi(11, 1, 24, 7)
MulType = "Inherit"
SumType = "Inherit"
块代码(在单元格中*公共代码*,维度继承方法*和*类型继承方法):
-
单元格*通用代码*:
struct Block{SumType, MulType, aType, bType} <: AbstractCausalComponent a::aType b::bType function Block() sum_type = OUTPUT_SIGNAL_ATTRIBUTES[1].type mul_type = if MulType == "Inherit" mul_promote_type(INPUT_SIGNAL_ATTRIBUTES[1].type, eltype(a)) else MulType end new{sum_type, mul_type, typeof(a), typeof(b)}(a, b) end end
-
Cell*Dimensions继承方法*:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* a .* b return [size(mock_output)] end
-
Cell*Types继承方法*:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} mul_type = if MulType == "Inherit" mul_promote_type(inputs_types[1], eltype(a)) else MulType end sum_type = if SumType == "Inherit" sum_promote_type(mul_type, eltype(b)) else SumType end return [sum_type] end
因此,函数"econvert","esum","emul","ediv"和规则"promote_type"允许您控制块内计算的类型和行为。 Engee Function . 它们支持所有主要的数值类型,包括固定点和复杂值,并可用于计算、比较、继承逻辑和使用块的组件行为调整。 Engee Function .
诊断功能’warning','stop_simulation','pause_simulation','info`
块的源代码中 Engee Function 还提供"warning"、"stop_simulation"、"pause_simulation"和"info"功能,使您可以在仿真阶段与模型诊断系统进行交互,从而在 诊断窗口 . 这些函数可以在以下源代码单元中使用:
-
组件结构代码
-
步骤方法代码
-
更新方法代码
-
终止方法代码
-
通用代码
函数’warning','stop_simulation', 因此,尽管这些函数在*Component struct code*单元格中被正式允许,但实际上它们在那里的放置没有意义:此单元格仅用于描述块结构,而不是用于可执行逻辑。 为了使诊断函数正常工作,必须将它们放在支持的仿真方法中:步骤方法、更新方法、*终止方法*或*公共代码*中,如果在那里重新定义了相应的方法。 正确放置的例子:
|
通过类比,您可以复盖在模拟期间调用的任何受支持的方法(例如,update!
,'终止`,'步骤'),在*通用代码*单元格中手动-如果禁用相应的标准单元格(例如,定义更新方法,*定义步骤方法*等。).
要在*Common code*单元格中正常工作,可以使用函数`warning`,stop_simulation
,pause_simulation`和`info
:
-
内部函数重新定义负责执行模型的方法的行为,例如`step`,
update!','终止!
. 例如,如果在*步骤方法代码*单元格中正常指定的函子是在*公共代码*中定义的,那么它内部的诊断功能将正常工作。 -
从负责执行模型的方法调用的辅助函数内部。 也就是说,如果在*公共代码*中定义了辅助函数,并且例如在`step`中使用,那么对其内部诊断函数的调用也将正确执行。
重新定义’更新! 或其他方法不取代`step
(function(c::Block)(t,in的强制实现。..)
). *Engee*需要此功能作为模拟的入口点。
一个正确重新定义’更新!'in*通用代码*:
# 具有组件结构代码的结构(如果禁用,则必须以通用代码呈现)
struct Block <: AbstractCausalComponent
g::Real
function Block()
new(gain)
end
end
# 步骤方法(在模拟的每个步骤调用的强制方法)
function (c::Block)(t::Real, in1)
c = update!(c, t, in1)
return c.g .* in1
end
# 重新定义的更新方法!
function update!(c::Block, t::Real, in1)
if t == 5.0
info("update triggered at t = $t") # 诊断信息
end
return c
end
因此,将在模型的诊断窗口中收到以下消息:
接下来,让我们仔细看看诊断功能本身。:
-
'warning'-`warning(msg::String)'函数输出警告消息。 模拟仍在继续。 这对于指出需要注意但不会停止执行的非关键问题或条件非常有用。 例子::
if t == 5.0 || t == 7.5 warning("time == $t") end
-
'stop_simulation—-'stop_simulation(msg::String)'函数立即终止模拟并输出完成消息。 它用于指示一个临界条件,在该条件下继续建模是不可能的或不希望的。 例子::
if t == 5.0 stop_simulation("time == $t") end
-
'pause_simulation—-`pause_simulation(msg::String)'函数暂停模拟并显示指定的暂停消息。 可以使用按钮手动恢复模拟 待续 :
此函数对于分析给定时间的模型状态非常有用。 例子::
if t == 5.0 pause_simulation("time == $t") end
-
'info—-`info(msg::String)'函数输出信息消息。 它用于在不影响仿真执行的情况下显示中间值。 例子::
if t == 5.0 || t == 7.5 info("time == $t") end
港口
入口
输入端口 — 输入端口
+ '标量|/'向量|/`矩阵'
Details
指定为标量、矢量或矩阵的输入端口。
使用以下选项在块的*Ports*选项卡中配置输入端口:
-
标签-指定输入端口的名称。 默认情况下,不填写*Label*单元格(未指定名称)。
-
类型-输入信号数据类型。 选择其中一个选项:
-
某种类型(除`继承’之外的所有)-检查某种类型的信号被发送到输入端口。 为输入端口选择特定的数据类型。 支持的信号’Float16`,
Float32
,Float64
,ComplexF32
,ComplexF64
,Bool
,Int8
,Int16
,Int32
,Int64
,Int128
,UInt8
,UInt16
,UInt32
,UInt64
,`UInt128'。 -
'Inherit'(默认)-从关联块继承数据类型。 它可以是任何类型的数据。
-
-
尺寸-输入信号的尺寸:
-
所有维度的继承('-1’默认情况下)—继承发送到输入端口的信号的维度(信号可以有任何维度)。
-
定义的尺寸-输入信号必须具有指定数量的元素。 维度用Julia表示法(作为元组)指定,例如,一维二维信号的`(2,)
或多维信号的
(2,3,4)`。 如果指定了'-1',则继承维度。 -
继承其中一个维度-继承发送到输入端口的信号的维度与数据结构的显式指示。 例如,
(-1,2)
—预计第一个维度是继承的,第二个维度是显式设置的。
-
-
输出总线类型-输入总线类型,如果输入信号的数据类型*类型*具有类型`BusSignal'(总线),则替换输入信号的尺寸*大小*。 默认值为’BusSignal{(),Tuple{},()}'。 阻止 Engee Function 我弄清楚哪个总线来到输入,将类型设置为值’Inherit`就足够了。 显式类型指示仅对输出信号是必要的。
座 Engee Function 它不继承总线到输出端口,虽然它可以接收它们到输入。 要继承输出端口上的总线(用于传输到其他块),必须在*Output bus type*参数中显式指定总线类型。 -
直接馈通-定义直接端到端连接:
-
如果选中该复选框(默认情况下),则直接端到端连接可用。 这意味着输出信号直接由输入端口的值控制。
-
如果未选中该复选框,则直接端到端连接不可用。 这意味着输出信号将不受输入端口的值控制,并允许单元开环。
-
出口;出口
输出端口 — 输出端口
+ '标量|/'向量|/'矩阵`
Details
指定为标量、矢量或矩阵的输出端口。
使用以下选项在块的*Ports*选项卡中配置输出端口:
-
标签-指定输出端口的名称。 默认情况下,*Label*单元格为空(未指定名称)。
-
类型-输出信号的数据类型。 选择其中一个选项:
-
Specific type(all except`Inherit')-我们定义输出信号的数据类型。 为输出信号选择特定的数据类型。 支持的信号’Float16`,
Float32
,Float64
,ComplexF32
,ComplexF64
,Bool
,Int8
,Int16
,Int32
,Int64
,Int128
,UInt8
,UInt16
,UInt32
,UInt64
,'UInt128'。 -
'Inherit'(默认)-继承输出信号的数据类型。 当有多个不同类型的输入信号时,计算最小的公共类型。 它可以是任何类型的数据。
-
-
尺寸-输出信号的尺寸:
-
所有维度的继承(默认情况下`-1`)-继承应用于输出端口的信号的维度。 输出信号将具有作为广播机制的结果而获得的维度-Julia将自动将较小数据数组的维度扩展到较大的维度,以便正确继承维度。
-
定义的尺寸-输出信号必须具有指定数量的元素。 维度用Julia表示法(作为元组)指定,例如,一维二维信号的`(2,)
或多维信号的
(2,3,4)`。 如果指定了'-1',则继承维度。 -
继承其中一个维度-继承发送到输出端口的信号的维度,并带有数据结构的显式指示。 例如,
(-1,2)
—预计第一个维度是继承的,第二个维度是显式设置的。
-
-
输出总线类型-输出总线类型,如果输出信号的数据类型*类型*具有类型`BusSignal'(总线),则替换输入信号的尺寸*大小*。 默认值为’BusSignal{(),Tuple{},()}'。 阻止 Engee Function 如果您已向出口发出轮胎,则必须明确指定其类型。
参数
主要
输入端口数 — 定义输入端口的数量
+
1(默认)
Details
定义块的输入端口数。 *输入端口数*参数的值将对应于输入端口数。
输出端口数 — 指定输出端口数
+
1(默认)
Details
定义块的输出端口数。 *输出端口数*参数的值将对应于输出端口数。
采样时间 —
计算步骤之间的间隔
'-1(默认)`
Details
将计算步骤之间的间隔指定为非负数。 要继承计算步骤,请将此参数设置为-1。
高级
使用外部缓存进行非标量输出 — 对非标量输出使用外部缓存
+
禁用(默认情况下)|/'启用
Details
指定对非标量(多维)输出信号使用外部高速缓存来保存*Engee*RAM。 使用,如果块有 Engee Function 只有一个输出端口:
-
如果未选中该复选框(默认情况下),则不使用外部缓存。
-
如果选中该框,则输出信号可以接受额外的"缓存"参数,这必须在EngeeFunctionCode的源代码中考虑到。 如果单元具有一个输出端口,则在单元 Step method code 'cache’参数会自动添加到函数参数列表中,除非端口维度显式设置为'()'(标量)。 在源代码中,要求根据输出信号的维度编写函子。 函子可以在单元格中定义 Common code :
-
如果输出信号是标量:
function (c::Block)(t::Real, x) return c.g * x end
-
如果输出信号是非标量的:
function (c::Block)(t::Real, cache, x) cache .= c.g .* x nothing end
其中’t’是时间,'x’是参数(来自输入端口的信息)。 必须指定time参数,即使它不包含在块参数中。
-
样本时间继承方法 —
计算步骤的继承方法的定义
'默认(默认)|/'离散|/'连续`
Details
如果在EngeeFunctionCode源代码中选中了*Override sample time inheritance method*复选框,则*Sample time inheritance method*设置从*Advanced*选项卡中消失。 在这种情况下,计算步骤的继承方法由函数单元格的代码*样本时间继承方法*确定。 |
根据所选值定义计算步骤的继承方法:
如果在*采样时间*字段中指定了值`-1`,则根据*采样时间继承方法*字段中指定的方法继承计算步骤,具体取决于所选值(默认 ,离散 ,连续')。 在所有其他情况下(采样时间不等于'-1’和采样时间大于或等于'0 )-块 Engee Function 它使用*采样时间*字段的指定值,忽略*采样时间继承方法*。
|
-
'Default’是继承默认计算步骤的方法。 块时始终使用’Default’继承方法 Engee Function 它不是离散的或连续的。 法得到任何一种计算步骤。 选择此方法时,块 Engee Function 它将根据以下原则继承计算步骤:
-
如果块没有输入端口,则在输出处有一个连续的计算步骤。
-
如果所有计算步骤在输入处相同,则输出处的计算步骤与输入相同。
-
如果在输入步骤中存在连续的计算步骤,则在输出处也存在连续的计算步骤。
-
如果在输入计算步骤中存在固定次要(FiM,Fixed-In-Minor),则不存在连续计算步骤和 求解器与可变间距-输出是一个固定的小一个。
-
如果在输入处没有连续且固定数量的计算步骤并且并非所有计算步骤都相等,则仅考虑输入处的离散计算步骤,对于其中一个选项是有效的。:
-
如果离散计算步骤的最大公约数与输入计算步骤之一重合或使用恒定步长求解器,则输出是具有最大公约数的步长的离散计算步骤。
-
如果变步求解器和输入离散计算步骤的最大公约数与任何输入计算步骤不匹配,则输出为固定小的。
-
-
-
'离散`是用于获得离散计算步骤的继承方法。 选择此方法时,块 Engee Function 它将根据以下原则继承计算步骤:
-
如果输入端有连续或固定的小计算步长,则输出为带有求解器步长的离散计算步长(即使求解器带有可变步长)。
-
如果在输入处存在离散计算步骤,则输出是与输入离散计算步骤具有最大公约数的离散计算步骤。
-
-
连续是一种继承方法,用于获得与输入计算步骤无关的连续计算步骤。
使用类似于`Default`继承方法的操作重新定义`propagate_sample_times`函数的示例:
+
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
nonnegative_sample_times = filter(
st -> st.period >= 0,
collect(values(inputs_sample_times)),
)
finite_periods = filter(
st -> !isinf(st.period),
nonnegative_sample_times,
) .|> (st -> st.period)
output_sample_time = if !isempty(nonnegative_sample_times)
if allequal(nonnegative_sample_times)
first(nonnegative_sample_times)
elseif any(st -> st.period == 0 // 1 && st.mode == :Continuous, nonnegative_sample_times)
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
elseif any(st -> st.mode == :FiM, nonnegative_sample_times) && !fixed_solver
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
elseif (
all(x -> x.period > 0 // 1, nonnegative_sample_times) &&
(fixed_solver || gcd(finite_periods) in finite_periods)
)
(period = gcd(finite_periods), offset = 0 // 1, mode = :Discrete)
else
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
end
else
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
end
return output_sample_time
end
如果函数’propagate_sample_times’返回`(period=0//1,offset=0//1,mode=:Discrete)',则此计算步骤将被视为具有求解器步骤的离散步骤。 |
设置参数
参数数量 — 指定参数的数量
+
1(默认)
Details
块中使用的参数的数量。
参数 — 将参数定义为变量
+ '标量|/'矢量`|`数组'
Details
定义要在块源代码中使用的参数 Engee Function . 在参数中,您可以设置:
-
'Name’是参数的名称。;
-
'Value’是参数的值。 任何Julia表达式都可以设置为值。
参数的值和名称可以在参数选项卡中更改。 第一个参数的名称(默认情况下存在)是’gain`,其值为'2'。 新参数称为’parameter2',然后按升序排列。 新参数的默认值为'0'。
Engee变量窗口中可用的全局变量 您可以将参数选项卡设置为插入块源代码的变量。 Engee Function . 如果参数的名称和全局变量匹配,参数值将自动从全局变量中替换。
要设置总线参数,请将`Value`参数的总线值设置为命名元组,例如`(s1=5,s2=4)`。
区分在"参数"选项卡中设置的块参数和源代码中的全局变量非常重要。 Engee Function ,来自变量窗口中的全局变量*Engee* ![]() |
让我们考虑全局变量完全对应于源代码中的变量的情况。 例如,设置了三个全局变量 , , 与价值观`1`, 2
, 3
相应地。 所有三个全局变量都用作块参数。 Engee Function :
然后添加参数的源代码将如下所示:
struct Block <: AbstractCausalComponent
a::Real
b::Real
c::Real
function Block()
new(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
return (c.a .* x .+ c.b) ./ c.c
end
此源代码定义了"块"结构,它允许您自定义组件的行为。 该示例使用块参数名称’a_param`、'b_param’和’c_param’来设置结构参数。 , ,而 相应地。 代码还定义了’function(c’方法。::Block)(t::Real,x::Vector{<:Real})`,它按块参数缩放向量`x’的每个元素 ,补充 并将结果除以 . 这允许您根据块参数的值灵活地更改和归一化向量`x'。
让我们考虑一个仅使用参数选项卡的参数的情况。:
struct Block <: AbstractCausalComponent end
function(c::Block)(t::Real,x::Vector{<:Real})
返回(a_param.*x.+b_param)。/c_param
结束
这些参数将是块代码中的全局变量。 这意味着它们将始终在块代码的任何部分中可用,而不必在每个函数或代码块中重复定义它们。 这大大简化了代码,并且可以轻松更改参数选项卡中的参数,而不会影响源代码。
让我们考虑参数与源代码不完全匹配的情况。 例如,有一个参数`a_param’等于'100'。 源代码中有一个结构字段 :
struct Block <: AbstractCausalComponent
a::Real
function Block()
new(a_param/10)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
c.a
end
在此代码中,`a_param’参数用于初始化字段。 `struct Block`通过其构造函数的结构,该构造函数将参数值除以'10'。 在这种情况下,该字段在块的函子中返回 .
变量可以在源代码中全局化。:
a = 1;
b = 2;
c=3;
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a .* x .+ b) ./ c
end
在代码中创建了一个块结构,并定义了一个用全局变量执行数学运算的函数。 , 和 通过将它们应用于变量’x'。
如果您*不想*使用参数选项卡中的参数,则可以直接在源代码中初始化变量。:
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x)
a = 1;
b = 2;
c = 3;
return (a .* x .+ b) ./ c
end
代码中创建了一个块结构,并定义了一个用局部变量执行数学运算的函数。 , 和 通过将它们应用于变量’x'。
让我们考虑最有效和正确的方法来做到这一点。 阻止 Engee Function 它工作正常,选中选项框*使用外部缓存进行非标量输出*在块的主选项卡上 Engee Function . 源代码将如下所示:
struct Block{Ta, Tb, Tc} <: AbstractCausalComponent
a::Ta
b::Tb
c::Tc
function Block()
Ta = typeof(a_param); Tb = typeof(b_param); Tc = typeof(c_param)
all(isreal, (a_param, b_param, c_param)) ||
error("块参数必须是真实的")
all(x->isempty(size(x)), (a_param, b_param, c_param)) ||
error("块参数必须是")
new{Ta, Tb, Tc}(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, cache::Vector{<:Real}, x::Vector{<:Real})
cache .= (c.a .* x .+ c.b) ./ c.c
nothing
end
此代码只能在单元格中编写。 Common code ,由于标准单元格不允许编辑组件结构的定义和函子的签名。 |
字段 , 和 结构是在构造函数中严格检查类型的块参数。 这些参数中的每一个都必须是一个真正的标量('Real`),这确保了运行时计算的准确性。
'Block()构造函数检查传递参数的类型 , 和 . 如果其中至少有一个不是真正的标量或具有不适当的维度(它们必须是标量),则构造函数会生成带有相应消息的错误。 验证后,构造函数使用值初始化结构的字段 , 和 指定的类型是’Ta
,'Tb’和’Tc'。
为块实例定义的计算函数需要时间’t`,外部缓存和实数的向量`x'。 字段用于此函数 , 和 用于计算值的`块’结构,然后将其写入缓存。 这通过重用提供的缓存来避免不必要的内存分配。
因此,"块"结构通过使用外部缓存来存储计算结果,确保严格的数据类型管理和资源的有效利用。
如果你想改变块参数,那么你需要使用’可变’结构。 考虑一个例子:
mutable struct Counter{T} <: AbstractCausalComponent
limit::T
iter::T
功能计数器()
isempty(size(limit))//error("$BLOCK_NAME的块限制必须是标量")
isreal(limit)||error("block BLOCK_NAME的块限制必须是实数")
T=typeof(限制)
iter=零(T)
新的{T}(限制,iter)
结束
结束
函数(C::计数器)(t::真实)
返回c.iter
结束
功能更新!(C::Counter,t::Real)
c.iter+=1
如果c.iter>c.limit
c.iter=零(c.iter)
结束
c
结束
"计数器"结构是一种"可变"数据类型,用于计算具有指定限制的迭代,并且是强类型的。 结构的`limit`和`iter’字段表示块参数。:
-
'limit’是计数器的极限值。;
-
`iter’是计数器的当前值。
结构构造函数检查(验证)`limit`参数以查看参数是否是标量和真实数据类型。 之后,`iter`字段用相应类型’T’的空值初始化。 '更新!'功能’更新计数器的状态 ,在每次调用时将`iter`的值增加一个。 如果`iter’的当前值超过`极限',它被重置为零,并允许计数器循环回到其初始状态。
块的源代码中 Engee Function 您可以使用’include`,指的是外部代码。 这允许您从源代码中的外部代码(如果有的话)初始化变量。 |
实际的数据类型以及对可能的数据类型的支持取决于块内的用户代码。 |
示例代码
该示例示出了块的简化实现。 Discrete-Time Integrator,基于Julia代码集成到*Engee*模型中。 选择直接欧拉法作为积分法。 在块的*高级*选项卡上 Engee Function 为*采样时间继承方法*参数设置值`Discrete'。 接下来,填写源代码单元格,如下所示:
-
在牢房里 Common code :
mutable struct Block{T} <: AbstractCausalComponent const dt::Float64 state::T gain::Float64 function Block() dt = OUTPUT_SIGNAL_ATTRIBUTES[1].sample_time.period state = initial_condition gain = k new{typeof(state)}(dt, state, gain) end end
-
在牢房里 Step method code :
return c.state
-
在*更新方法代码单元格中*:
c.state += in1 * c.dt * c.gain return c
因此,将获得以下源代码:
'Initial_condition`和’k’参数在块设置的*Parameters*选项卡中初始化 Engee Function :
在模型模拟的第一步,`c.state`块的内部状态使用`initial_condition`参数的值初始化。
之后,在每个计算步骤中,块返回内部状态’c.state’作为输出信号,并在`update!中重新计算其值!'方法。
组件结构在单元格中重新定义 Common code ,而不是在 Component struct code ,因为需要更灵活的定义:结构必须是可变的,并且由与状态类型对应的类型*T*参数化。 标准定义在 Component struct code 它仅适用于不可变和非参数化结构。 |
说明
注释在 Engee Function 它们允许您在模型中直接在其名称下显示块参数。 要添加它们,请打开 设置窗口 街区 Engee Function 然后转到*Annotation*选项卡。 选择所需的块属性标记并将其添加到文本编辑器中。
在这个标签上:
-
左侧显示可用选项列表(隐藏选项除外)。
-
右侧有一个文本编辑器,您可以在其中设置带有%<参数名称>格式标记的注释。
-
参数可以手动传输,通过自动完成,或使用按钮
.
退出编辑器后(例如,在其外部单击时),将应用注释:标记会自动替换为实际参数值,最终文本显示在块名称下(或在其上方,如果名称放在顶部)。
要删除批注,必须在编辑器中删除相应的标记。
可用标记
属性标记将自动替换为当前参数值。 以下标记可用:
-
Ports:'%<Inputs>','%<Outputs>—-输入和输出端口数’%<InputPort1Type>','%<OutputPort1Type>','%<InputPort1Size>','%<OutputPort1Size>—-数据类型和信号尺寸。
-
时间特性:'%<Sampleetime>—-离散化;'%<Sampleetimeinheritancemethod>'—离散化的继承方法。
-
代码块:'%<ComponentStructCode>','%<StepMethodCode>—-步骤的结构和方法的代码。
-
Parameters:'%<Parameters>`,`%<Parameter1Name>','%<Parameter1Value>'—参数的名称和值。
-
Enable flags:'%<DefineComponentStruct>`,
%<UseCommonCode>
,%<DefineStepMethod>
,%<definupdatemethod>
,%<DefineTerminateMethod>
—启用相应的代码段。 -
重新定义方法:'%<OverrideTypesInhMethod>`,
%<OverrideDimsInhMethod>
,`%<Overridesampleetimeinhmethod>'—类型继承设置。 -
Other:'%<UseExternalCache>—-外部缓存使用情况。