# 模型编译过程中的参数估值


介绍模型编译过程中的参数估值现象。

# 引发参量估值的情形

有的时候在模型代码中明明声明的是独立参量,但在仿真实例变量树中该参量值不可更改。出现这种情况的原因是在模型翻译过程中参量被估值了。引发参量估值通常有如下情形。

(1)有参量存在Evaluate=true的注解,例如:Modelica.Mechanics.MultiBody.Parts.FixedTranslation,其中参量r被声明为独立参量。而另一个参量lengthDirection的赋值方程中引用了r,并且该参量存在Evaluate=true的注解,如下所示。

parameter SI.Position r[3](start = {0, 0, 0})
    "Vector from frame_a to frame_b resolved in frame_a";
parameter Types.Axis lengthDirection = to_unit1(r - r_shape)
    "Vector in length direction of shape, resolved in frame_a" annotation (
      Evaluate = true, Dialog(
        tab = "Animation", 
        group = "if animation = true", 
        enable = animation));

注解Evaluate=true表示希望该参量在编译过程中被估值,而估值该参量进而需要估值参量r,因此r在仿真实例的变量树中变为不可更改了。该例子是某单位反馈过来的一个问题,其希望模型导出为FMU之后参量r是可以更改的。对于此需求,可以不使用标准库的FixedTranslation,而是从其复制出一个新组件模型,将其中的注解Evaluate=true予以删除。

(2)参量影响模型变量数或方程数,主要情形包括:出现在条件表达式、出现在数组的声明下标中。

model Case2_1
    parameter Real p1 = 2.4;
    parameter Real p2 = 1.5;
    parameter Real p3 = 3.6;
    parameter Integer n = 5;
    Real x1 = if p1 > 2 then x2+sin(time) else y3[5];
    Real x2;
    Real y1 if p3 > 0;
    Real y2;
    Real y3[n];
equation 
    if p2 > 1 then 
        x2 = 2*x1+1;
    else
        x2 = 5-x1;
    end if;
    if p3 > 0 then 
        y1 = x1-2;
        y2 = y1+sin(time);
    else
        y2 = 2*x1;
    end if;
    y3 = fill(time, n);
end Case2_1;

对于示例Case2_1,在模型翻译过程中p2,p3n被编译器估值了。对p3n的估值是容易理解的,这是因为n决定了数组y3的元素个数,p3决定了y1及其方程是否存在。p2之所以被估值是因为按照Modelica规范,出现方程区的if语句的每个分支中应具有相同数目的方程,但以参数表达式为条件的if语句除外。换而言之,对于以参数表达式为条件的if语句,其每个分支中可以具有不同数目的方程,例如Case2_1中的第二个if语句。然后,根据Modelica规范的单赋值规则,在模型仿真过程中方程数目必须保持不变。因此,为了避免仿真过程中if语句生效分支的切换可能导致方程数不一样,基于简化处理原则,SysplorerD软件均在模型编译时对if语句的参数表达式形式的条件(不管if语句各分支中方程数目是否一致)进行估值。若实际应用过程中需要让p2在求解器生成之后仍然可调,则可以将if语句改写为等价的if表达式形式,如示例Case2_2所示。

model Case2_2
    parameter Real p1 = 2.4;
    parameter Real p2 = 1.5;
    parameter Real p3 = 3.6;
    parameter Integer n = 5;
    Real x1 = if p1 > 2 then x2+sin(time) else y3[5];
    Real x2;
    Real y1 if p3 > 0;
    Real y2;
    Real y3[n];
equation 
    x2 = if p2 > 1 then 2*x1+1  else 5-x1;
    if p3 > 0 then 
        y1 = x1-2;
        y2 = y1+sin(time);
    else
        y2 = 2*x1;
    end if;
    y3 = fill(time, n);
end Case2_2;

此外,算法中参数表达式形式的if语句条件是不会被估值的,故将if语句置于算法中亦可以让p2在求解器生成之后仍然可调,如Case2_3所示。

model Case2_3
    parameter Real p1 = 2.4;
    parameter Real p2 = 1.5;
    parameter Real p3 = 3.6;
    parameter Integer n = 5;
    Real x1 = if p1 > 2 then x2+sin(time) else y3[5];
    Real x2;
    Real y1 if p3 > 0;
    Real y2;
    Real y3[n];
algorithm 
    if p2 > 1 then 
        x2 := 2*x1+1;
    else
        x2 := 5-x1;
    end if;
equation
    if p3 > 0 then 
        y1 = x1-2;
        y2 = y1+sin(time);
    else
        y2 = 2*x1;
    end if;
    y3 = fill(time, n);
end Case2_3;

示例Case2_3尽管可以达到让p2在求解器生成之后仍然可调的目的,但一般不推荐这么做。相对于Case2_2Case2_3将原本的线性问题人为变更为了一个非线性问题。这也是能用方程表达就不推荐使用算法表达的其中一个原因。

(3)在Sysplorer的仿真设置-模型翻译中,“参数估值以便优化模型”选项被勾选。

在此特别指出,合理地利用参量的估值注解,有助于提升模型的计算效率。参量值的代入,不仅可以消除一些局部的重复计算,而且可能缩小代数环的规模。特别是 0 与 1 这类特殊值的代入,有的情况下可以显著简化模型的计算。因此,建议为不需要在求解器生成之后仍需修改的参量添加Evalute=true的注解。

勾选“参数估值以便优化模型” 选项后,模型中的参量在能够估值情况下均会被估值。

(4)参数被final操作符修饰,或者参数为非公有组件。

model Case2_4
    final parameter Real p1 = 1.5;
    parameter Real p3 = 3.6;
    Real x;
protected
    parameter Real p2 = 2.4;
equation 
    x = p1 * sin(p2 * time) + p3;
end Case2_4;

按照Modelica语义,由final操作符修饰的组件,其值表达式不可以被修改。同样按照Modelica语义,非公有组件在模型外部不可访问。这意味模型翻译之后此两类参数不能够被修改,故在模型编译过程中会对其进行估值。在Case2_4中,参数p1final操作符修饰,参数p2为非公有组件,二者在模型翻译过程中均被估值,在求解器生成之后不可更改。

(5)参数值在仿真开始之后不可变更。

这种情况主要包括:

(a)出现在外部对象构造函数的实参表达式中;

(b)出现在delay表达式的非首个实参表达式中。按照Modelica语义,外部对象在整个仿真期间只能构造一次,这意味着其构造函数的实参不能为时变表达式或可调参数表达式,故模型编译过程中会对外部对象构造函数的实参表达式中参数进行估值。对于两参数的delay表达式,其第二个参数决定了数据缓存的大小,而对于三参数的delay表达式,其第三个参数决定了数据缓存的大小。在求解过程中数据缓存一旦创建,其大小则不可变更,故模型编译过程中会对delay表达式中与影响数据缓存大小的参数进行估值。

需要指出的是,以上两种情况中的参数值在仿真之前其实是可以更改的,只要确保在仿真开始之后不可变更。但目前Sysplorer内核尚未精细区分仿真前可调与仿真过程中可调的参数,而是将可调参数笼统地等同为仿真过程中可调的参数,故而将仅只能在仿真前可调的参数给估值了。在后续的某个版本中将对此进行精细化区分。