# 常量、参量与变量


介绍模型中常量、参量与变量的特性和使用方法。

# 常参量的时不变特性

按照Modelica​规范,常量与参量(也称参数)均是不随时间变化的,其值在仿真过程中保持不变。常量值与参量值在结果曲线图上呈现为一条水平直线。常量与参量的区别是,参量值在求解器生成之后在每次仿真之前是可以修改的,而常量值只能在建模环境中进行编辑,一旦生成求解器,则不可再修改。

model Case1_1
    constant Real c1 = 1.6;
    parameter Real p1 = 2.4;
    parameter Real p2 = 2*p1+c1;
    parameter Real p3 = c1+2;
    Real x1 = 2*c1+3;
    Real x2 = 3*x1-2;
    Real y1 = p2+p3-x1;
    Real y2 = y1+x2;
end Case1_1;

常量与参量的不随时间变化的特性是可以通过(由方程或算法表达的)约束关系传播的。在示例模型 Case1_1 中,由于常量 c1 不随时间变化,故参量p3 、变量x1x2 也不随时间变化,因此参量p3 、变量x1x2 实质上均是常量。同样地,由于参量 p1 不随时间变化,故变量 y1y2 亦不随时间变化,因此变量 y1y2实质上亦均是参量。

由于在求解器生成之后,常量值即保持不变,故而在模型翻译过程中,会进行常量值代入操作,并对常量表达式进行求值,同时对参量与变量执行可变性推导。因此,经过模型翻译之后 Case1_1 实质变换成了如下 Case1_2 所示形式。

model Case1_2
    constant Real c1 = 1.6;
    parameter Real p1 = 2.4;
    parameter Real p2 = 2*p1+1.6;
    constant Real p3 = 3.6;
    constant Real x1 = 6.2;
    constant Real x2 = 16.6;
    parameter Real y1 = p2-2.6;
    parameter Real y2 = y1+16.6;
end Case1_2;

如上所述,经过模型翻译之后,尽管 p1 , p2 , y1 , y2 最终实质上均为参量,但在仿真实例的变量树中,只有 p1 可以更改。由此就需要引入一个新的概念——独立参量(也称自由参量)。独立参量是指可以独立地变更的参量,表现为其赋值方程等号右边为常数(字面常量或文字常量)。在以上示例中, p2 尽管也被声明为参量,但其依赖于 p1 ,故而会随 p1 的变更而变更,此类参量称为非独立参量,其值不可以独立地更改,只能随独立参量而被动变更。同样地,y1y2 亦只能随 p1 的变更而变更。

# 仿真过程中可调的参量

前已述及,按照Modelica​规范,参量在仿真过程中是保持不变的。若为独立参量,则在仿真之前可以更改。然而某些特定应用中,希望参量在仿真过程中是可以调整的。例如故障仿真,希望在仿真过程中通过改变参量值,实现某些种故障注入。另外,有些应用场景下,希望通过在仿真过程中在线调整参数的方式获得效果更优的参量值。基于此类需求,Sysplorer内核对参量的可调性进行了拓展,在交互仿真模式下(即与外部存在数据交互的情况下),允许在仿真过程中对独立参量进行更改。而在独立仿真模式下(即与外部不存在数据交互的情况下),在仿真过程中则不允许对独立参量进行更改。相对而言,独立仿真模式下因为不需要处理外部输入变更,因而其仿真效率更高些。

基于以上阐述,可调参量必定为模型中声明的独立参量,且在模型编译过程中未被估值(将专题予以介绍)。在示例 Case1_2 中,p1为可调参量。需要指出的是,此处的可调参量等同于FMI规范中可变性为tunable、因果性为parameter的量。示例 Case1_2 中的 p2y1y2 按照FMI规范其可变性均为tunable,因果性为calculatedParameter。我们称此类参量为依赖参量,即依赖于可调参量的参量。

# fixed 属性为 false 的参量

根据Modelica规范,参量的内置属性fixed默认为true,但可以根据实际需要将参量的内置属性fixed设置为false。例如下面的平面单摆模型。

model PlanarPendulum
  Real x(start = 0.8, fixed = true);
  Real y(start = 0.6, fixed = true);
  Real u(start = 0);
  Real v;
  Real F;
  parameter Real L(fixed = false);
  parameter Real m = 1;
  constant Real g = 9.81;
  Real w = abs(x);
equation 
  u = der(x);
  v = der(y);
  m * der(u) = -x * F / L;
  m * der(v) = -y * F / L - m * g;
  x ^ 2 + y ^ 2 = L ^ 2;
end PlanarPendulum;

在上面模型中,参量 L 的内置属性被设置false,此时需要根据 xy的初始值计算出参量L ,故模型中将xy的内置属性fixed均设置为true。在模型初始化过程中,根据xy的初始值,由方程x ^ 2 + y ^ 2 = L ^ 2计算参量L。可见,从计算关系上L等同于一个时不变(不随时间变化)的变量。但在物理上杆长L通常认为是一个参数,只是其值是通过xy的初始值计算出来的。

内置属性fixedfalse的参量不常见,在参量值需要通过变量初始值计算出来的情况下需要如此处理。在Modelica​标准库中也有用到内置属性fixedfalse的参量,例如Modelica.Mechanics.MultiBody.Joints.Assemblies.JointRRP中的参量 e_im,其值通过一个初始方程由变量初始值计算得到。因此,内置属性fixedfalse的参量一般仅用在参量值需要变量初始值计算而来的场景中。笔者曾经调试过一个包含内置属性fixedfalse的参量的模型,其出发点是参数q依赖于参量p,但参数q无法通过一个关于参量p的简单关系计算得到,而是建模者在初始算法区通过一段算法代码由参数p计算出参量q,大致类似于如下情形:

model Case1_3
    parameter Real p = 2.4;
    parameter Real q(fixed=false);
initial algorithm
    q=ceil(p);
    if q>3 then
        q=q+1;
    else
        q=3-q;
    end if;
end CaseCase1_3;

以上模型尽管通过引入内置属性fixedfalse的参量解决了实际问题,但其实有更为简单的表达方式,只需按如下Case1_4所示增加一个函数定义即可q表示为p的一个简单计算关系式。

model Case1_4
    parameter Real p = 2.4;
    parameter Real q = fn(p);
    
    function fn
        input Real u;
        output Real y;
    algorithm
        y=ceil(u);
        if y>3 then
            y=y+1;
        else
            y=3-y;
        end if;
    end fn;
end Case1_4;

由此可见,只有需要根据变量初始值计算参量值时,才有引入内置属性fixedfalse的参量的必要。

# 常参量的求解优化

基于常参量的不随时间变化的特性,求解器在仿真过程中会优化常参量的计算方式。在独立仿真模式下,不管仿真多长时间,不管仿真多少步,常参量均只会计算一次,其在仿真结果文件中也只保存一个值,如此可以提升仿真效率。对于这一点,工程实施团队曾经几次遇到过疑惑或误解。最近一次发生在某个分布式联合仿真项目中,问题表现为某个模型单独仿真比较快,将其与其他模型联合到一起进行分布式联合仿真时,即使扣除数据通讯的时间开销,该模型的仿真速度仍明显慢于单独仿真。实施团队根据此现象错误地推断为求解器存在问题。后来经过分析发现造成此现象的原因是,单独仿真时将该模型的输入全部设置为常数,由输入变量的常量特性传播导致模型中的许多变量也被推导为不随时间变化,故而在求解过程中均只会计算一次。而进行联合仿真时,该模型的输入来自于其他模型,是随时间变化的,在仿真过程中每一步均需要计算一次,因此导致了仿真速度上的明显差异。

在此需要特别提醒的是,对于具有外部输入的模型,在单独进行仿真测试或评估时,需要考虑输入变量的一般性或广泛代表性,不能基于一种特殊的情况而推导出一般性的结论。

# 连续变量与离散变量

最后,讲述一下变量的情况。顾名思义,变量是随时间变化的,可以认为其是关于时间变量的一个函数。变量可分为连续时间变量与离散时间变量,二者分别简称为连续变量与离散变量。在Modelica​中,非实型变量默认为离散时间变量。实型变量中,在when语句中计算的变量也属于离散时间变量。

此处需要注意的是,离散时间变量并不只是因为其值是离散的,而是指其值的变化时间是离散的,其值不是任意时间均会发生变化,而只是在事件时间发生改变。也就是说,离散时间变量是指其值的改变仅发生在事件时间的变量。从这个角度而言,在非事件时间可以不计算离散变量。关于事件与when语句,以及Modelica将非实型变量默认为离散时间变量带来的不便,将在后续专题中进一步进行讲述。

受到一些其他工具或过程式程序思维的影响,建模时常见的一个问题是取变量的上一步值。在Modelica中,对于连续时间变量,这一说法是不成立的。因为对于陈述式模型而言,模型的描述与模型的求解是分离的,这要求模型描述中只能用到模型中声明或定义的概念。这可以类比下程序语言,程序中使用到的自定义标识符均需要在程序中定义。不关联到求解的情况下,模型中没有步长或上一步之类的概念,Modelica规范不提供此类语义。关于这一点,此处不展开讲述,在后续有关陈述式的专题再适当提及。对于离散时间变量,“取变量的上一步值”可以姑且认为是“取变量在上一个事件时间的值”。若如此,可以使用pre操作符实现。在同步时钟语义中,可以使用previous操作符获取时钟化变量在上一个时钟计时时刻的值。

# 小结

合理地定义常量、参量与变量可提升模型代码的可读性,便于参数化建模与参数化仿真。正确理解并有效利用常参量的时不变特性及其传播性,可提升模型的仿真计算效率。