子系统接口

本章的重点是组件模型可以如何被组织成可重用的子系统。正如我们在本章中许多例子里看到的,这例子总会出现一个常见的模式。为了进一步了解这种模式,让我们回顾子系统模型的各个方面。

参数

一般而言,子系统的参数应为public。通常情况下,除非modelblock内定义的声明前带有protected关键字,否则所有声明都是公有的。在这种情况下,如果想通过在声明前添加public关键字去表明其为公共的话,也是可以的。换句话说:

model PublicAndProtected
  Real x; // This is public (because that is the default)
protected
  Real y1; // This is protected
  Real y2; // This is *also* protected
public
  Real z1; // This is public
  Real z2; // This is *also* public
equation
  // ...
end PublicAndProtected;

在本章中的例子中,经常可以见到参数定义要不是在定义的开始(这里参数默认为public)就是在一系列明确标记\ public的声明里。

很显然,参数定义为公有,让子系统的用户可以进行访问。我们很快就会在对修改语句以及传值的讨论里看到,应如何将参数传值给较低级别的组件。但就目前而言,最主要的一点是要认识到参数声明是子系统设计模式的一部分。

连接器

从某种意义上说,子系统模型和系统模型的区别就在于连接器。系统模型完整且可以立刻进行模拟。正因为如此,系统模型不希望外界任何元素影响,亦即不会有连接器。子系统可以封装组件的复杂层级结构(最小的单元为公式)。但是,由于子系统模型包括连接器,其实模型是作为较大系统的一部分来使用的。此外,正如我们在本章所看到的例子一样,由于连接器需要在连接到,他们应该是public

分层连接

由于子系统通常只会由组件或其他子系统组成,该子系统与外界的物理相互作用通常会重定向到内部的组件或子系统内。子系统边界的连接器实际上充当的是内部连接器的“代理”。我们在这一章已经多次看到这种模式。在GearWithBacklash模型里可以看到一个简单的例子:

  connect(flange_a, inertia_a.flange_a)

请注意,connect语句将子系统连接器实例flange_a与子部件inertia_a的连接器实例inertia_a.flange_a。这是子系统模型的通用设计模式。而这个模式很容易识别。因为connect语句提到的连接器中的一个会包含“.”,而另一个则没有“.”。对于所有连接两个内部部件的“内部”连接,其connect语句提到的两个连接器里均会带有“.”。例如:

  connect(idealGear.flange_b, inertia_b.flange_a)

分层连接公式生成

本书已多次提到flow变量的正负号规则(例如在我们对非因果连接的讨论里)。flow变量为正值表示该流在进入组件或子系统。同时,我们也指出,连接生成方程式,使得连接对应的所有flow变量的和为零。

但是,处理分层子系统定义时,此规则有一个点不同。对于子系统, flow正负号约定保持不变。所以,在连接器的flow变量正值还是代表流守恒量到该组件。连接集内所有flow变量会产生一个守恒方程这点也仍然成立。然而,在守恒方程中,内部组件连接器的flow变量将会与子系统连接器的flow变量具有相反的符号。

要理解这意味着什么,让我们考虑以下GearWithBacklash模型的两个connect语句:

  connect(flange_a, inertia_a.flange_a)
    annotation (Line(points={{-80,0},{-100,0}},
      color={0,0,0}, smooth=Smooth.None));
  connect(idealGear.flange_b, inertia_b.flange_a)
    annotation (Line(points={{10,0},{40,0}},
      color={0,0,0}, smooth=Smooth.None));

从这些方程里,我们得到以下两个连接集:

  • 连接集#1flange_ainertia_a.flange_a

  • 连接集#2idealGear.flange_binertia_b.flange_a

在每个连接集内都有一个flow变量tau。使用前述的连接规则,我们可能会预期flow变量将产生以下两个等式:

flange_a.tau + inertia_a.flange_a.tau = 0;
idealGear.flange_b.tau + inertia_b.flange_a = 0;

然而,在我们以往对连接的讨论里,所有组件都是在同一层次的(例如idealGear.flange_binertia_b.flange_a这样的)。但并不是所有子系统模型都是如此。而且,如上所述,若连接穿过不同层级,我们需要对不同层级内的连接器引入不同的符号。考虑到这一点,实际将产生的公式会是:

-flange_a.tau + inertia_a.flange_a.tau = 0;
idealGear.flange_b.tau + inertia_b.flange_a = 0;

请注意flange_a.tau前面的减号。记住flange_a充当inertia_a.flange_a的代理。以此为前提,则通过改变flange_a.tau的符号,上述的第一个方程可以转化为:

inertia_a.flange_a.tau = flange_a.tau;

换句话说,通过改变flange_a.tau符号,任何流过flange_a守恒量会流过inertia_a.flange_a。而这正是我们在这种“代理”关系时所期望的行为。

实现

子系统模型通常只封装了某个组件集合。正如我们在本节已经讨论过的,子系统暴露的参数和连接器均为public。子系统的用户将只能与这些公有部件进行交互。

子系统的实际内部细节为子系统的实现。对于子系统,实现通常由组件和其他子系统的集合组成。子系统实现里各个组件和子系统相互连接,且它们至少有一个连接器与子系统连接器相连。

正常情况下,最好不要让子系统的最终用户看到这些实现细节。要做到这一点,子系统中的所有非parameter声明通常会标记为protected。这样做主要有两个原因。首先,这对子系统模型的用户隐藏了实现细节。作用是将接口简化为只有参数和连接器,避免了将用户真正需知道的内容与其不需要知道(甚至不应知道)的内容混在一起。将实现细节设定为protected另一个原因在于,这让开发者在之后能够灵活地提高或重构实现细节。若允许用户引用实现细节里的部件,这意味着用户就会(可能甚至是无意地)依赖于实现细节。其结果是,如果实现细节在以后发生改变,最终用户的模型就无法工作了。