# 连接器与连接
本章内容涵盖了连接器(connectors)、连接方程(connect-equations)和连接(connections)。
连接器和连接方程的设计使得不同的组件可以用定义良好的语义以图形方式连接起来。然而,图形部分是可选的,该内容可以在注释中找到。
# 连接方程和连接器
对象之间的连接(connection)是通过类的方程节中的连接方程引入的。连接方程的语法如下:
connect "(" component_reference "," component_reference ")" ";"
连接方程由对两个连接器的引用来构造,每个引用有下列形式之一:
, 为连接器, 为 的连接器元素,n>=1, 。 - m.c,m 为类中的非连接器元素,c 为 m 的连接器元素。
任何组件都可选择使用数组下标,数组下标必须是参数表达式。如果连接语句引用了连接器数组,数组的维数必须匹配,两个数组一一对应的元素对作为一对标量连接器进行连接。
数组用法的示例:
connector InPort = input Real;
connector OutPort = output Real;
block MatrixGain
input InPort u[size(A, 2)];
output OutPort y[size(A, 1)];
parameter Real A[:, :] = [1];
equation
y = A * u;
end MatrixGain;
Modelica.Blocks.Sources.Sine sinSource[5];
MatrixGain gain (A = 5 * identity(5));
MatrixGain gain2(A = ones(2, 5));
OutPort x[2];
equation
connect(sinSource.y, gain.u); // Legal
connect(gain.y, gain2.u); // Legal
connect(gain2.y, x); // Legal
三个主要的任务为:
- 详悉阐述可扩展连接器。
- 从连接方程构建连接集。
- 从整个模型生成方程。
# 连接集
连接集是通过连接方程连接起来的变量的集合。一个连接集要么仅仅包含流(flow)变量,要么仅仅包含非流(non-flow)变量。
# 内部和外部连接器
在一个元素实例 M 中,M 的每个连接器元素称为 M 的外部连接器。所有其他的连接器元素,层次性地包含在 M 中但不在 M 的任何一个外部连接器中,称为 M 的内部连接器。内外部连接器的确定是在将 outer 元素解析为相应的 inner 元素之前。
示例:
该图显示了下面的语句中到模型 mi 中的连接器 c 的连接。考虑下面在组件 m0 的模型中的连接语句:
connect (m1.c, m3.c); //m1.c and m3.c are inside connectors
connect (m2.c, m3.c); //m2.c and m3.c are inside connectors
在组件 m3 的模型中(c.x 是 c 内部的子连接器):
connect(c, m4.c); // c is an outside connector, m4.c is an inside connector
connect(c.x, m5.c); // c.x is an outside connector, m5.c is an inside connector
connect(c, d); // c is an outside connector, d is an outside connector
在组件 m6 的模型中:
connect(d, m7.c); // d is an outside connector, m7.c is an inside connector
# 可扩展连接器
如果在连接器定义中出现 expandable 前缀,该连接器的所有实例都被作为可扩展连接器而引用。没有这个前缀的连接器的实例将被作为非扩展连接器引用。
在生成连接方程之前,在可扩展连接器中声明的非参数(non-parameter)的标量变量或者非参数的数组元素,都被标记为潜在地出现。非参数的数组维数可以用冒号“:”表示,说明其维数未知(注意,这种维度的大小不能用 size 来确定,参见数组维和维长度的操作函数)。这同时适于简单类型和结构类型的变量。
然后,对包含了可扩展的连接器的连接进行细化:
- 如果一个连接方程引用了一个可能存在的组件作为参数的一部分,它将被标记为存在,这将允许连接到其中未声明的连接器。该规则不适用于完整的参数。
- 之后,connect 方程中至少有一个连接器必须引用已声明的组件。
- 如果另一个连接器是未声明的,它必须在已声明的组件中,并按如下方式处理:
- 可扩展连接器实例被自动扩展为具有使用名称和相应类型的新组件。
- 如果未声明的组件是下标的,则创建一个数组变量,并执行到特定数组元素的连接。在数组中引入元素会给出一个至少包含指定元素的数组,其他元素要么不创建,要么有默认值(即,好像它们只是潜在地存在,关于 size 的使用的相同注意事项也适用于此)。
- 如果连接方程另一端的变量是 input 或 output,则组件将是 input 或 output,以满足连接和连接器的约束中对不可扩展连接器的限制。
(一般规则确保内部和外部连接器的一致性,并处理到新组件的多个连接。在没有其他连接涉及这些变量的简单情况下,现有方引用内部连接器(即组件的连接器),新变量将复制其因果关系,因为可扩展连接器必须是外部连接器。)
另外:
- 当两个可扩展连接器连接时,每个连接器都增加了仅在另一个可扩展连接器中声明的变量(新变量既不是 input 也不是 output)。这样重复,直到所有连接的可扩展连接器实例都有匹配的变量。
(即,每个连接器实例都被扩展为所有连接器变量的并集。) - 在阐述中引入的变量遵循生成连接集的附加规则。
- 如果一个变量作为输入出现在一个可扩展连接器中,它应该在同一扩展集中的至少一个其他可扩展连接器实例中作为非输入出现。扩展集定义为通过细化将具有匹配变量的连接的可扩展连接器实例的集合。这个小例子展示了可扩展连接器的正常使用方式:
- 有许多总线实例连接在一起。通常它们有相同的名称,但这不是必需的。
- 信号有一个来源:sensor.sensor.speed。
- 该信号有零个或多个用途:actuator.actuator.speed。
示例:
expandable connector EngineBus
end EngineBus;
partial block Sensor
RealOutput speed; // Output, i.e., non−input
end Sensor;
partial block Actuator
RealInput speed; // Input
end Actuator;
model SensorWBus
EngineBus bus;
end SensorWBus;
replaceable Sensor sensor;
equation
connect(bus.speed, sensor.speed);
// Provides'speed'
end SensorWBus;
model ActuatorWBus
EngineBus bus;
replaceable Actuator actuator;
equation
connect(bus.speed, actuator.speed);
// Uses'speed'
end ActuatorWBus;
model Engine
ActuatorWBus actuator;
SensorWBus sensor;
EngineBus bus;
equation
connect(bus, actuator.bus);
connect(bus, sensor.bus);
end Engine;
- 可扩展连接器中的所有组件均被看作连接器实例,即使没有这样声明。
(即,可能连接到比如实型变量这样的非连接器上。)
示例:
expandable connector EngineBus // has predefined signals
import Modelica.Units.SI;
SI.AngularVelocity speed;
SI.Temperature T;
end EngineBus;
partial block Sensor
RealOutput speed;
end Sensor;
model Engine
EngineBus bus;
replaceable Sensor sensor;
equation
connect(bus.speed, sensor.speed);
// connection to non−connector speed is possible
// in expandable connectors
end Engine;
- 可扩展连接器中不能包含用前缀 "flow" 声明的组件,但可以包含具有 "flow" 组件的不可扩展连接器组件。
示例:
import Interfaces=Modelica.Electrical.Analog.Interfaces;
expandable connector ElectricalBus
Interfaces.PositivePin p12, n12; // OK
flow Modelica.Units.SI.Current i; // Error
end ElectricalBus;
model Battery
Interfaces.PositivePin p42, n42;
ElectricalBus bus;
equation
connect(p42, bus.p42); // Adds new electrical pin
connect(n42, bus.n42); // Adds another pin
end Battery;
- 可扩展连接器只能与其他可扩展连接器相连。
如果连接方程引用了一个可能存在的变量或可变元素,在可扩展连接器中,该变量或可变元素被标记为存在,并且由于上述段落,可以推断出该总线变量在连接方程中是应被视为输入,还是应被视为输出。如果声明中没有 input / output 前缀,则添加该 input 或 output 前缀。
示例:
expandable connector EmptyBus
end EmptyBus;
model Controller
EmptyBus bus1;
EmptyBus bus2;
RealInput speed;
equation
connect(speed, bus1.speed); // OK; only one undeclared and not subscribed.
connect(bus1.pressure, bus2.pressure); // Error; both undeclared.
connect(speed, bus2.speed[2]); // Introduces speed array (with element [2]).
end Controller;
size 未定义的可扩展连接器数组组件(见数组维和维长度的操作函数)称为不定长数组组件。这样的组件不能在没有下标的情况下使用,并且下标必须对数组进行切片,以便去除未定义的维度。
示例: 不定长数组组件的有效和无效使用:
expandable connector EngineBus
end EngineBus;
partial block Sensor
RealOutput speed;
end Sensor;
model Engine
parameter Integer n = 1;
EngineBus bus;
replaceable Sensor sensor;
RealOutput sensorSpeeds[:];
equation
/* Comments below refer to the use of sizeless array bus.speed. */
connect(bus.speed[n], sensor.speed ); // OK; subscribe to scalar component.
connect(bus.speed, sensorSpeeds); // Error; missing subscripts.
public
Real a[:] = bus.speed; // Error; missing subscripts.
Real b[2] = bus.speed[{1, 3}]; // OK; subscript selects fixed size sub−array.
end Engine;
经过如此细化之后,可扩展连接器当作普通连接器看待,连接也作为普通连接,所有潜在存在但实际不存在的变量或(数组)元素是没有定义的。
(工具可以将它们去掉或给它们赋一个缺省值,如,对 Real 类型的变量可以赋零。)
给潜在存在的变量或未声明的数组元素施加修改算子是错误的。如果有表达式引用了潜在存在但实际不存在的变量或(数组)元素,这也是错误的(表达式只能从连接器中实际声明的总线上“读”变量的值)。这种细化暗示了,即使可扩展连接器中没有相应的组件,也能与它连接。
注意
上面所描述的变量引入只是概念上的,并不必以任何方式影响实例层次。
另外,很重要的一点是必须考虑下面的细化规则:
- 可扩展连接器分层次嵌套。其意思是在细化过程中,任一层级都必须包含外部和内部连接器。
- 当处理一个有 inner 前缀的可扩展连接器时,在细化时必须考虑所有外层(outer)实例。
示例:引擎系统,有传感器、控制器、作动器和通过总线交换信息(即通过可扩展连接器)的装置:
import Modelica.Units.SI;
import Modelica.Blocks.Interfaces.RealInput;
// Plant Side
model SparkPlug
RealInput spark_advance;
...
end SparkPlug;
expandable connector EngineBus
// No minimal set
end EngineBus;
expandable connector CylinderBus
Real spark_advance;
end CylinderBus;
model Cylinder
CylinderBus cylinder_bus;
SparkPlug spark_plug;
...
equation
connect(spark_plug.spark_advance,
cylinder_bus.spark_advance);
end Cylinder;
model I4
EngineBus engine_bus;
Modelica.Mechanics.Rotational.Sensors.SpeedSensor speed_sensor;
Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temp_sensor;
parameter Integer nCylinder = 4 "Number of cylinders";
Cylinder cylinder[nCylinder];
equation
// adds engine_speed (as output)
connect(speed_sensor.w, engine_bus.engine_speed);
// adds engine_temp (as output)
end I4
connect(temp_sensor.T, engine_bus.engine_temp);
// adds cylinder_bus1 (a nested bus)
for i in 1:nCylinder loop
connect(cylinder[i].cylinder_bus,
engine_bus.cylinder_bus[i]);
end for;
end I4;
鉴于上述关联性,从概念上引入了一个由所有连接器并集构成的连接器。
引擎总线包含以下变量声明:
RealOutput engine_speed;
RealOutput engine_temp;
CylinderBus cylinder_bus[1];
CylinderBus cylinder_bus[2];
CylinderBus cylinder_bus[3];
CylinderBus cylinder_bus[4];
# 连接方程的生成
在生成连接方程之前,outer 元素被解析为实例层次中对应的 inner 元素(参见动态名字查找 inner 声明的实例层次名字查找)。每个连接语句的参数被解析为两个连接器元素。
对于连接语句:
connect(a, b);
的每一次使用,组件 a 和 b 中的简单的组件(变量)都形成一个连接集,并指示它们是来自内部连接器还是来自外部连接器。
定义9.1 基本元素。 基本元素具有简单类型或类型定义为 operator record (即,operator record 的一个组件不能被继续拆分为子组件)。
集合中的元素是一个简单变量二元组,其中包含内部 / 外部指示信息。如果一个二元组同时属于两个连接集,则将这两个连接集合并,直到一个二元组只出现在一个集合中。复合连接器类型被分解为基本组件,outer 组件映射到相应的 inner 组件—并不影响 outer 连接器是内部的这种属性;outer 连接器映射到相应的 inner 连接器—它们仍被当作外部连接器。
(原因:inner / outer 作为连接集的一部分,保证来自不同层级的连接能被分别处理。连接集是由简单元素组成的,而不是连接器。这样,可把连接作为层次化的连接器的一部分进行处理,还使得可以直接从连接集产生方程。连接集中的所有变量,由于连接语句的要求,要么是 flow 变量,要么是 non-flow 变量。从 outer 到 inner 元素的映射必须在连接集合并之前,这是为了得到一个零和方程,并且保证 outer 元素定义了连接器“一端”的方程,inner 元素定义了连接器另一端的方程。)
以下仅包含单个成员的连接集也同样存在(并被合并):
- 每个简单流变量是内部连接器。
- 在可扩展连接器的扩展过程中加入的每个流变量,既是内部又是外部(连接器)。
注意
流量变量不是直接在可拓展连接器中,而是在可拓展连接器内部的一个连接器中。
(理由:若这些变量未被连接,它们将生成仅包含该元素的集合, 因此这些变量会被隐式设为零值(见下文)。若已连接,该集合将被合并,且在起始处添加此项不会产生影响。)
每个连接集用于产生势变量和流变量(零和)的方程,形式如下:
; // non-flow variables ;// flow-variables
对于操作符记录类型,这使用操作符‘0’必须在操作符记录中定义并且所有用于 operator record 的流变量必须是相同的操作符记录类型。这意味着,为了拥有 operator record 类型的流变量,operator record 必须定义加法、负数和“0”;而这些操作应该定义一个加性群。
产生流变量的方程时,上式中连接器变量 zi 前的符号,对于内部连接器为 +1,对于外部连接器为 -1。
示例:
model Circuit
Ground ground;
Load load;
Resistor resistor;
equation
connect(load.p, ground.p);
connect(resistor.p, ground.p);
end Circuit;
model Load
extends TwoPin;
Resistor resistor;
equation
connect(p, resistor.p);
connect(resistor.n, n);
end Load;
合并之前的连接集:
{<load.p.i, inside>}
{<load.n.i, inside>}
{<ground.p.i, inside>}
{<load.resistor.p.i, inside>}
{<load.resistor.n.i, inside>}
{<resistor.p.i, inside>}
{<resistor.n.i, inside>}
{<resistor.p.i, inside>, <ground.p.i, inside>}
{<resistor.p.v, inside>, <ground.p.v, inside>}
{<load.p.i, inside>, <ground.p.i, inside>}
{<load.p.v, inside>, <ground.p.v, inside>}
{<load.p.i, outside>, <load.resistor.p.i, inside>}
{<load.p.v, outside>, <load.resistor.p.v, inside>}
{<load.n.i, outside>, <load.resistor.n.i, inside>}
{<load.n.v, outside>, <load.resistor.n.v, inside>}
合并之后有:
{<load.p.i, outside>, <load.resistor.p.i, inside>}
{<load.p.v, outside>, <load.resistor.p.v, inside>}
{<load.n.i, outside>, <load.resistor.n.i, inside>}
{<load.n.v, outside>, <load.resistor.n.v, inside>}
{<load.p.i, inside>, <ground.p.i, inside>, <resistor.p.i, inside> }
{<load.p.v, inside>, <ground.p.v, inside>, <resistor.p.v, inside>}
{<load.n.i, inside>}
{<resistor.n.i, inside>}
于是,方程为:
load.p.v = load.resistor.p.v;
load.n.v = load.resistor.n.v;
load.p.v = ground.p.v;
load.p.v = resistor.p.v;
0 = (-load.p.i) + load.resistor.p.i;
0 = (-load.n.i) + load.resistor.n.i;
0 = load.p.i + ground.p.i + resistor.p.i;
0 = load.n.i;
0 = resistor.n.i;
示例:
model Circuit
Ground ground;
Load load;
inner Resistor resistor;
equation
connect(load.p, ground.p);
end Circuit;
model Load
extends TwoPin;
outer Resistor resistor;
equation
connect(p, resistor.p);
connect(resistor.n, n);
end Load;
合并之前的连接集:
{<load.p.i, inside>}
{<load.n.i, inside>}
{<ground.p.i, inside>}
{<resistor.p.i, inside>}
{<resistor.n.i, inside>}
{<load.p.i, inside>, <ground.p.i, inside>}
{<load.p.v, inside>, <ground.p.v, inside>}
{<load.p.i, outside>, < resistor.p.i, inside>}
{<load.p.v, outside>, <resistor.p.v, inside>}
{<load.n.i, outside>, <resistor.n.i, inside>}
{<load.n.v, outside>, <resistor.n.v, inside>}
合并之后有:
{<load.p.i, outside>, <resistor.p.i, inside>}
{<load.p.v, outside>, <resistor.p.v, inside>}
{<load.n.i, outside>, <resistor.n.i, inside>}
{<load.n.v, outside>, <resistor.n.v, inside>}
{<load.p.i, inside>, <ground.p.i, inside>}
{<load.p.v, inside>, <ground.p.v, inside>}
{<load.n.i, inside>}
于是,方程为:
load.p.v = resistor.p.v;
load.n.v = resistor.n.v;
load.p.v = ground.p.v;
0 = (-load.p.i) + resistor.p.i;
0 = (-load.n.i) + resistor.n.i;
0 = load.p.i + ground.p.i;
0 = load.n.i;
这对应于直接连接电阻。
# 连接和连接器的约束
- 连接方程(以及超定连接的特殊函数)只能在方程中使用,不能在条件不是参数表达式的 if 程中使用,也不能在 when 方程中使用。
(for-equation 总是有数组表达式的参数表达式。) - 连接器组件不能用前缀 parameter 或 constant 来声明。在连接方程中,基本组件只能连接参数变量和参数变量、常量变量和常量变量。
- 连接方程构造只接受连接方程和连接器中指定的连接器引用形式。
- 在连接方程中,两个连接器必须具有具有相同尺寸的相同命名组件元素;递归到基本组件。具有相同名称的基本组件被匹配,并且属于相同的连接集。
- 两个连接器匹配的基本组件必须具有相同的基本类型,流变量只能连接到其他流变量,流变量只能连接到其他流变量,因果变量(输入 / 输出)只能连接到因果变量(输入 / 输出)。
- 因果变量(输入 / 输出)的连接集最多只能包含来自一个内部输出连接器(对于按合并到输出的连接规定扩展的状态机)或一个公共外部输入连接器的变量。
(也就是说,一个连接集最多只能包含一个信号源。) - 对于包含为 non-partial 模型或 block 生成的因果变量的连接集,必须至少满足以下条件之一:
- 该连接集包括来自外部公共可扩展连接器的变量;
- 该集合包含来自受保护的外部连接器的变量,
- 它包含来自一个内部输出连接器的变量,或,
- 来自一个公共外部输入连接器,或,
- 该集合仅由来自一个内部输入连接器的一个变量组成,该连接器不属于可扩展连接器的一部分。 (即,连接集必须 - 除非模型或块是部分的 - 包含一个信号源(第 5 项涵盖了组件的连接器未连接且文本给出源的情况)。)
- 来自受保护的外部连接器的变量必须是包含至少一个内部连接器或一个声明的公共外部连接器的连接集的一部分(即,它不得是可扩展连接器的隐式定义部分)。
(否则将无法推断出可扩展连接器元素的因果关系。) - 在连接集中,所有具有非空数量属性的变量必须具有相同的数量属性。
- 连接语句不能(直接或者间接地)连接 outer 元素的两个连接器。(间接连接就好比它们在同一个连接集中-然而,与 outer 元素的连接在形成连接集之前被“提升”。否则连接集可能包含“多余的”信息,使方程个数不满足模型和 blocks 本地平衡的要求)。
- 引用连接器时使用的下标必须是参数表达式或者是特殊操作符“:”。
- 如果(连接中的)数组大小不匹配,则在由连接集生成方程之前,使用长度为 1 的维数从左开始填充原始变量直到维数大小匹配。
- 如果在连接的组件中有常量或参数,就会适当产生 assert 语句,且不生成连接。
- 关于条件连接器,请参见条件组件声明。
# 连接器的尺寸约束
对于每一个 non-partial 连接器类,流变量的数量应该等于既不是参数、常量、输入、输出、stream 变量,也不是流变量的变量数目。“变量的数目”是将连接器类中的所有记录、数组类型变展开成简单标量类型之后,所有的变量数目。超定类型或者记录类(见连接图及其运算符)的变量数目是相应 equalityConstraint() 函数的输出参数的大小。
(可扩展连接器类不在此范围内,因为它们的组件声明只是约束的一种形式。)
示例:
connector Pin // A physical connector of Modelica.Electrical.Analog
Real v;
flow Real i;
end Pin;
connector Plug // A hierarchical connector of Modelica.Electrical.MultiPhase
parameter Integer m = 3;
Pin p[m];
end Plug;
connector InputReal = input Real; // A causal input connector
connector OutputReal = output Real; // A causal output connector
connector Frame_Illegal
Modelica.Units.SI.Position r0[3] "Position vector of frame origin";
Real S[3, 3] "Rotation matrix of frame";
Modelica.Units.SI.Velocity v[3] "Abs. velocity of frame origin";
Modelica.Units.SI.AngularVelocity w[3] "Abs. angular velocity of frame";
Modelica.Units.SI.Acceleration a[3] "Abs. acc. of frame origin";
Modelica.Units.SI.AngularAcceleration z[3] "Abs. angular acc. of frame";
flow Modelica.Units.SI.Force f[3] "Cut force";
flow Modelica.Units.SI.Torque t[3] "Cut torque";
end Frame_Illegal;
连接器 Frame_Illegal(想要用在不含有超定(over-determined)连接器的简单 MultiBody-package 中)是不合规定的,因为流变量和 non-flow 变量的数目不相配。解决方法是创建两个连接器类,其中两个 3 元素向量(例如,a 和 z)是无因果关系的 Real 类型变量,两个类中的其他变量是输入与输出成对匹配的。这保证了模型只能连接成树形结构,或者在每个连接成的机械环路中需要一个“解环”接头:
connector Frame_a "correct connector"
input Modelica.Units.SI.Position r0[3];
input Real S[3, 3];
input Modelica.Units.SI.Velocity v[3];
input Modelica.Units.SI.AngularVelocity w[3];
Modelica.Units.SI.Acceleration a[3];
Modelica.Units.SI.AngularAcceleration z[3];
flow Modelica.Units.SI.Force f[3];
flow Modelica.Units.SI.Torque t[3];
end Frame_a;
connector Frame_b "correct connector"
output Modelica.Units.SI.Position r0[3];
output Real S[3, 3];
output Modelica.Units.SI.Velocity v[3];
output Modelica.Units.SI.AngularVelocity w[3];
Modelica.Units.SI.Acceleration a[3];
Modelica.Units.SI.AngularAcceleration z[3];
flow Modelica.Units.SI.Force f[3];
flow Modelica.Units.SI.Torque t[3];
end Frame_b;
下面的连接器 Plug_Expanded 和 PlugExpanded2 是 正确的,但是 Plug_Expanded_Illegal 就不正确,因为如果“n”和“m”不相等,non-flow 变量和流变量的数目就不相等。不知道工具用什么通用的方法可以发现类似 Plug_Expanded_Illegal 的连接器是不合规定的。但是,当仿真模型中的参数和常量的值给定之后,总可以发现这个缺陷。
connector Plug_Expanded "correct connector"
parameter Integer m=3;
Real v[m];
flow Real i[m];
end Plug_Expanded;
connector Plug_Expanded2 "correct connector"
parameter Integer m=3;
final parameter Integer n=m;
Real v[m];
flow Real i[n];
end Plug_Expanded2;
connector Plug_Expanded_Illegal "connector is illegal"
parameter Integer m=3;
parameter Integer n=m;
Real v[m];
flow Real i[n];
end Plug_Expanded_Illegal;
# 过度约束的连接
在连接图中,当环路结构导致方程组出现特殊问题时,这些连接器包含相互依赖的非流动(即势能)变量。当此类图中出现环路结构时,生成的方程组将处于超约束状态——即方程数量超过变量数量。这是因为除了环路周围的连接方程外,连接器中的某些非流动变量之间还存在隐式约束。目前的技术水平下,若没有模型设计者的额外信息,无法自动消除方程组中多余的方程。
本节描述了用于此类超约束的基于连接的方程系统的方程运算符集,使得模型设计者能够在模型中指定足够的信息,以允许 Modelica 环境自动删除多余的方程。
连接器可能含有冗余变量。例如,3 维空间中两个坐标系统的相对方位可以用三个独立变量描述。然而,每个在变量定义区域用 3 个变量对方位的描述必须至少有一个奇异点(singularity)。因此在一个连接器中不能只声明 3 个变量,而必须是 n(n>3)个变量。这 n 个变量不再相互独立,必须满足 n-3 个方程的约束。用有冗余的变量集合加上约束方程来描述,就不会再有奇异点。由组件和含冗余变量的连接器组成的模型,如果它的连接结构中有环路,就可能会产生方程数多于未知变量数的微分代数方程系统。通常这些多余的方程与其它的方程是相容的,即存在唯一的数学解。这样的模型不能使用当前所知的符号转换方法进行处理。为了克服这种情况,定义了操作符以使 Modelica 翻译器能删去多余的方程。在某些特定情况下,这是通过将连接集中 non-flow 变量的等式方程,替换为一定(推导出的)数目的方程来完成的。
本节讨论一类因含冗余变量的连接器而导致产生的超定系统的处理方法。超定系统的产生还有其他缘由,例如,流变量的显式零和(zero-sum)方程,不能用下面所述的方法处理。
# 连接图及其运算符
在一个 type 或 record 的声明中可以定义函数“equalityConstraint(…)”,其原型如下:
type Type // overdetermined type
extends 〈base type〉;
function equalityConstraint // non−redundant equality
input Type T1;
input Type T2;
output Real residue[n];
algorithm
residue := ...;
end equalityConstraint;
end Type;
record Record
〈declaration of record fields〉;
function equalityConstraint // non−redundant equality
input Record R1;
input Record R2;
output Real residue[n];
algorithm
residue := ...;
end equalityConstraint;
end Record;
残差的数组维数 n 应为可在翻译时求值的常量 Integer 表达式,满足 n≥0。 equalityConstraint() 函数的输出“residue”,大小应该已知,比如说为常量 n。该函数应用 n>=0 个无冗余的方程,表示两个 type 实例 T1 与 T2 或 record 实例 R1 与 R2 之间的等式关系。这些方程的 residues(the residues of these equations )在大小为 n 的向量 “residue” 中返回。用下面的 n 个无冗余方程表达关系式 R1=R2(0 表示相应大小的零向量):
Record R1, R2;
equation
0 = Record.equalityConstraint(R1, R2);
(如果记录 Record 中的元素不相互独立,则方程 “R1=R2” 含有冗余方程。)
声明了函数 equalityConstraint 的 type 类称为超定 type(overdetermined type)。声明了函数 equalityConstraint 的 record 类称为超定 record(overdetermined record)。含有超定类型和 / 或超定记录类实例的连接器称为超定连接器(overdetermined connector)。超定类型或记录,既不能具有流组件,本身也不能用作流组件类型。如果将数组用作任何 Connections 的参数。*函数将其视为一个单元——与 connect 不同,对这种情况没有特殊处理,比较连接方程和连接器。
超定连接器中的超定 type / record 的每个实例,是虚拟连接图(virtual connection graph)中的一个节点(node),用于为 connect(…) 语句生成方程时,决定何时使用标准方程“R1=R2”或方程“0=equalityConstraint(R1,R2)”。虚拟连接图的分支(branches)通过“connect(…)”隐式定义或通过“Connections.branch(…)”语句显式定义,见下表。“onnections”是一个全局作用域上的内置包,它定义了内置操作符。此外,虚拟连接图的相关结点必须分别用函数 “Connections.root(…)”和“Connections.potentialRoot(…)”定义为根(roots)或潜根(potential roots)。在下表中, 连接器实例 A 和 B 可能具有层次结构,例如, A 可能为“EnginePort.Frame”的缩写。
| Expression | Description | Details |
|---|---|---|
| connect(A, B) | Optional spanning-tree edge | Operator 9.1 |
| Connections.branch(A.R, B.R) | Required spanning-tree edge | Operator 9.2 |
| Connections.root(A.R) | Definite root node | Operator 9.3 |
| Connections.potentialRoot(A.R, ...) | Potential root node | Operator 9.4 |
| Connections.isRoot(A.R) | Predicate for being selected as root | Operator 9.5 |
| Connections.rooted(A.R) | Predicate for being closer to root | Operator 9.6 |
Operator 9.1 connect
connect(A, B)
为虚拟连接图定义可选的生成树边,从连接器实例 A 中的超定类型或记录实例到连接器实例 B 中相应的超定类型或记录实例。对应的超定类型或记录实例的类型必须相同。
Operator 9.2 Connections.branch
Connections. branch ( A.R, B. R)
为虚拟连接图定义从连接器实例 a 中的过确定类型或记录实例 R 到连接器实例 B 中相应的过确定类型或记录实例 R 所需的生成树边。这个函数可以在所有允许使用连接方程的地方使用。
Operator 9.3 Connections.root
Connections. root( A. R)
连接器实例 A 中的超定类型或记录实例 R 是虚拟连接图中的(确定的)根节点。
Operator 9.4 Connections.potentialRoot
Connections. potentialRoot ( A. R) Connections. potentialRoot ( A.R, priority =p)
连接器实例 A 中的超定类型或记录实例 R 是优先级为 p (p≥0) 的虚拟连接图中的潜在根节点,如果没有提供第二个参数, 则优先级为零。p 应为 Integer 类型的形参表达式。在没有定义 Connections.root 的虚拟连接子图中,选择优先级号最低的一个潜在根作为根。
Operator 9.5 Connections.isRoot
Connections. isRoot( A. R)
如果连接器实例 A 中的超定类型或记录实例 R 被选为虚拟连接图中的根,则返回 true。
Operator 9.6 Connections.rooted
Connections. rooted ( A. R) rooted ( A. R) // d e p r e c a t e d !
如果使用操作符 connections .root (A.R),或者使用等价但已弃用的操作符 root(A.R),则必须只有一个 Connections.branch(A.R,B.R) 涉及 A.R(Connections.rooted 的参数必须是 Connections.branch 的第一个参数)。在这种情况下,如果 A.R 比 B.R 更接近生成树的根,则 connections .root (A.R) 返回 true;否则返回 false。
注意
Connections.branch, Connections.root, Connections.potentialRoot 都不生成方程。它们只在虚拟连接图中生成结点和分支,用于分析目的。
# 将连接图转换为树形图并生成方程(
在为 connect(…) 生成方程之前,通过从图中删除可打断分支,将虚拟连接图转换成生成树的集合,方法如下:
- 用“Connections.root(…)”语句定义的每个根结点是一个生成树的确定的根。
- 虚拟连接图可以由一些未连接在一起的子图的集合组成。集合中的每个子图应该至少有一个根结点或一个 potential root 结点。如果集合中某子图不包含任何根结点,那么选择该子图中优先级最小的一个 potential root 节点作该子图的根。可以在类中用上表中的函数 Connections.isRoot(…) 对选择进行查询。
- 如果在子图中有 n 个选定的根,那么必须删去可打断分支,结果是有 n 个生成树,它们以选定的根结点作为根。
这样处理之后,再用下面的方法生成连接方程:
- 对于一个生成树中的每个可打断分支(即 connect(A, B) 语句,) ,按照连接方程的生成所述生成连接方程。
- 对于不在任何生成树中的每个可打断分支,除了超定 type / record 实例 R 之外,按照连接方程的生成所述生成连接方程。这时,(对 R)用生成方程“0 = R.equalityConstraint(A.R, B.R)”以代替“A.R = B.R”。
# 示例
虚拟连接图示例:
# 电力系统的超定连接器
基于 Park 转换理论的电力系统中的超定连接器定义为:
type AC_Angle "Angle of source, e.g., rotor of generator"
extends Modelica.Units.SI.Angle; // AC_Angle is a Real number
// with unit = "rad"
function equalityConstraint
input AC_Angle theta1;
input AC_Angle theta2;
output Real residue[0] "No constraints";
algorithm
/* make sure that theta1 and theta2 from joining edges are identical */
assert(abs(theta1 - theta2) < 1.e-10, "Consistent angles");
end equalityConstraint;
end AC_Angle;
connector AC_Plug "3-phase alternating current connector"
import Modelica.Units.SI;
AC_Angle theta;
SI.Voltage v[3] "Voltages resolved in AC_Angle frame";
flow SI.Current i[3] "Currents resolved in AC_Angle frame";
end AC_Plug;
连接器中的电流和电压的定义是相对于 harmonic,功率源(power source)的调和高频信号定义,该信号用发电机转子的转角 theta 来描述。由于电源的基本高频信号不是微分方程的一部分,仿真会快得多。例如,当电源和 the rest of line 在固定频率下操作(=nominal case)时,AC_Plug.v 和 AC_Plug.i 为常量。这种情况下,变步长积分器可以选择更大的步长。元素,如 3 相电感,可以实现为:
model AC_Inductor
parameter Real X[3,3], Y[3,3]; // component constants
AC_Plug p;
AC_Plug n;
Real omega;
equation
Connections.branch(p.theta,n.theta); //edge in virtual graph
// since n.theta = p.theta
n.theta = p.theta; // pass angle theta between plugs
omega = der(p.theta); // frequency of source
zeros(3) = p.i + n.i;
X*der(p.i) + omega*Y*p.i = p.v - n.v;
end AC_Inductor;
在定义电源频率 theta 这个重要变量的位置,必须使用 Connections.root(…):
AC_Plug p;
equation
Connections.root(p.theta);
der(p.theta) = 2*Modelica.Constants.pi*50 // 50 Hz;
对虚拟连接图进行分析时,标识出不必在组件之间传递 AC_Angle 的连接器,以免出现冗余方程。
# 三维机械系统中的超定连接器
三维机械系统的超定连接器可以定义为:
type TransformationMatrix = Real[3,3];
type Orientation "Orientation from frame 1 to frame 2"
extends TransformationMatrix;
function equalityConstraint
input Orientation R1 "Rotation from inertial frame to frame 1";
input Orientation R2 "Rotation from inertial frame to frame 2";
output Real residue[3];
protected
Orientation R_rel "Relative Rotation from frame 1 to frame 2";
algorithm
R_rel := R2*transpose(R1);
/*
If frame_1 and frame_2 are identical, R_rel must be
the unit matrix. If they are close together, R_rel can be
linearized yielding:
R_rel = [ 1, phi3, -phi2;
-phi3, 1, phi1;
phi2, -phi1, 1 ];
where phi1, phi2, phi3 are the small rotation angles around
axis x, y, z of frame 1 to rotate frame 1 into frame 2.
The atan2 is used to handle large rotation angles, but does not
modify the result for small angles.
*/
residue := { Modelica.Math.atan2(R_rel[2, 3], R_rel[1, 1]),
Modelica.Math.atan2(R_rel[3, 1], R_rel[2, 2]),
Modelica.Math.atan2(R_rel[1, 2], R_rel[3, 3])};
end equalityConstraint;
end Orientation;
connector Frame "3-dimensional mechanical connector"
import Modelica.Units.SI;
SI.Position r[3] "Vector from inertial frame to Frame";
Orientation R "Orientation from inertial frame to Frame";
flow SI.Force f[3] "Cut-force resolved in Frame";
flow SI.Torque t[3] "Cut-torque resolved in Frame";
end Frame;
从坐标系 A 到坐标系 B 的固定平移可定义为:
model FixedTranslation
parameter Modelica.Units.SI.Position r[3];
Frame frame_a, frame_b;
equation
Connections.branch(frame_a.R, frame_b.R);
frame_b.r = frame_a.r + transpose(frame_a.R)*r;
frame_b.R = frame_a.R;
zeros(3) = frame_a.f + frame_b.f;
zeros(3) = frame_a.t + frame_b.t + cross(r, frame_b.f);
end FixedTranslation;
由于坐标系 frame_a.R 与坐标系 frame_b.R 存在代数耦合关系,必须在虚拟连接图中定义一条边。在惯性系统中,方向参数始终被统一初始化,因此必须将惯性系统连接器中的方向定义为根节点。
model InertialSystem
Frame frame_b;
equation
Connections.root(frame_b.R);
frame_b.r = zeros(3);
frame_b.R = identity(3);
end InertialSystem;