# 模型开发


组件模型是物理系统基本要素。每个组件可以具有参数、变量、行为和端口。参数描述固定特性,变量说明可变物理属性,行为阐释物理本构,端口用于外部连接。组件变量与端口变量之间存在约束方程。
每一个组件的生成都是通过 Modelica 代码实现的,因此基于 Modelica 进行相关组件开发及系统仿真,最为关键的步骤即为组件代码的编写,模型开发步骤如图所示:

# 理论分析

每个组件具有参数、变量、行为和接口。组件参数表示物理元件相对固定的特性,如管道部件的管径和管长、机械部件的质量和惯性等。组件变量用于描述元件的物理属性,如管道部件的压力流量、机械部件的位置与受力等。组件行为是指元件的物理本构或约束关系,如质量守恒定律、机械牛顿定律等。组件行为采用方程描述,具体的方程包括代数方程、常微分方程或偏微分方程。代数方程一般表示代数约束;常微分方程一般表示时间相关的动态过程;偏微分方程一般表示与时间和空间相关或以场形式存在的动态过程。
每一个组件建模所需要的参数,变量及方程基本上都来源于相关组件的数学原理。可以根据需求查找相关资料,理解其工作原理,学会用数学方程(可以是已有的经过验证的数学原理,或者也可以是经过自己推倒并简化的数学方程)来描述物理元件的行为。

# 原型开发

# 模型创建

MWORKS 支持层次化模型库开发,一般可以通过右击所需建立下层模型的 package,“编辑→新建嵌套类”来创建所需模型,同时加上相应的类注释。 建立的所有类别(package,model,function)都需要相应的注释,一般类注释普遍采用下面的形式,一般用“""”进行注释,如:

package Hydraulics "液压组件模型库"
model OrificeM "主管节流孔模型"
……
end OrificeM;
function FlowArea "通流面积函数"
……
end FlowArea;
end LiquidRocketEngine;

# 主代码编写

  1. 量纲定义

    Modelica 标准库中(Modelica.SIunits)比较全面地定义了国际单位制(SI),一般不需要再在自建模型库中定义新的单位制,但是往往有些工程上常常使用或者符合中国人习惯的单位使用没有被预定义,那么就需要进行单位的显示设置。

    单位的显示一般分为两个方面,一是参数框上的单位显示,二是后处理界面的曲线数据单位的显示。MWORKS 提供对显示单位(displayUnit)的支持,模型库创建者完全可以按照需要建立不同显示单位的量纲,但必须符合国际单位制。

    所添加量纲的定义一般存放在指定的模型子库中,一般名为 Types 或者 SI,尽量与标准库的 SIunits 区分开。

    type AbsolutePressure = Pressure (min = 0, displayUnit = "MPa");
    
  2. 参变量定义

    在查找完整的建模理论之后,在所创建的模型类中进行参变量的定义(其中包括工质的相关定义,一般建议将工质的定义与普通参变量定义相隔开),并且定义的每个参量,变量都要注释其含义,以增强代码的可读性,注释语尽量简明扼要,一般用""进行注释,如:

    //参数
    parameter Modelica.SIunits.Diameter dm(displayUnit = "mm") = 0.01 "主管道直径";
    parameter Real km = 0.1 "主管道摩擦系数";
    //变量
    Modelica.SIunits.Pressure dp(displayUnit = "bar") "压差";
    Modelica.SIunits.VolumeFlowRate q(displayUnit = "l/min") "体积流量";
    Modelica.SIunits.Density rho = 850 "密度";
    Modelica.SIunits.KinematicViscosity nu = 6e-5 "粘度";
    Modelica.SIunits.Area Am "主管道流通面积";
    Real Cq "流量系数";
    
  3. 编写方程和算法

    定义完整的参变量之后,就可以在模型类创建方程区域进行方程和算法的编写,一般通过理论查找的方程都可以通过定义的参变量在方程区域直接进行编写,但要注意方程导数的转换等特殊情况的处理(即 ),同时尽可能简化方程,以降低求解难度。算法一般在函数中使用较多,其编写与方程类似,但注意需要在算法区域编写。

    在模型代码的方程区域,某一个方程的含义或某一部分方程的含义都要注释清楚,一般用“//”进行注释,如:

    equation
    //压差方程
    dp = port_A.p - port_B.p;
    //管道流通面积
    Am = FlowArea(dm);
    //管道流量计算
    Cq = 1 / (sqrt(km / 2));
    q = Cq * Am * sqrt(2 * abs(dp) / rho) * sign(dp);
    //接口方程
    q = port_A.q;
    port_A.q + port_B.q = 0;
    

    同理,在模型代码的算法区域,某一个算法的含义或某一部分算法的含义都要注释清楚,一般用“//”进行注释,如:

    function FlowArea "流通面积"
    input Real x;
    output Real y;
    algorithm
    y=(1 / 4) *Modelica.Constants.pi*x^2;
    end FlowArea;
    

# 图标设计

图标设计对于模型库而言,是必不可少的一部分,关系到整个模型库的风格和外观,图标的设计工具有很多,如 Microsoft Office Visio 绘图或者 Adobe PhotoShop 等,但一般通过其他软件制作的图标或者网上直接下载的图片,加载到 MWORKS 中其清晰度会降低,因此如果不追求图标的华丽,还是建议利用 MWORKS 自带的画图工具建立图标。 多数情况下,组件图标的默认显示大小固定(如图),一般都以 100*100 大小处理,并且保持“组件缩放系数不变”,以防止在拖动过程中出现组件图标的破损。

# 参数面板设计

参数定义完成后,需要进行相应的参数框显示设置,不同类别的参数一般都分 Tab 或分 Group 进行显示,都是通过 annotation 的注解语句实现的,具体代码格式如下:

parameter Modelica.SIunits.Diameter dm(displayUnit = "mm") = 0.01 "主管道直径"
annotation (Dialog(tab = "常规", group = "结构参数"));
parameter Real km = 0.1 "主管道摩擦系数"
annotation (Dialog(tab = "常规", group = "特性参数"));

# 说明添加

建立一个模型库,除了需要在模型库中添加相应的 UsersGuide 说明库及组件说明视图外,还需要撰写模型库相关的用户文档,主要文档包括:

  1. 组件开发说明

    较为详细地讲述模型组件的信息,一般在模型组件开发前创建,并在组件开发过程中不断修改和完善,格式不唯一,但主要内容要包括:

    (1) 功能描述(简介,原理图,实际功能)

    主管道节流孔是用以模拟流体在三通模型中通过主路的压损特性,可以实现主管阻压降功能,假设摩擦系数在流体通过时保持恒定,可以通过参数面板设置主管道的直径和摩擦系数,用于计算流通面积和流量系数,进而进行体积流量计算。

    (2) 基本假设

    • 不考虑油液温度变化,油液温度保持恒定;
    • 油液假设不可压缩,体积不会随着压力而变化。

    (3) 模型原理

    通过主管道节流孔的体积流量计算如下:

    主管道流通面积:

    流量系数:

    体积流量:

    ρ:油液密度

    (4) 参考文献

    [1] I.E, ldelchik, Handbook of Hydraulic Resistance, 3rd edition, Begell House.

    (5) 主要参变量

    • 参数

      tab 参数 group 参数 参数名称 默认值 单位 参数描述
      常规 结构参数 dm 10 mm 主管道直径
      特性参数 km 0.1 / 主管道摩擦系数

    • 变量

      变量类型 变量名称 单位 类型 描述
      结果变量 q l/min Real 体积流量
      Am m2 Real 主管道流通面积
      Cq / Real 流量系数
      中间变量(隐藏处理) dp bar Real 压差

    (6) 接口信息

    接口 变量 范围/单位 数据维度 数据类型 描述
    port_A p bar [1] Real 接口压力
    q L/min [1] Real 接口流量
    port_B p bar [1] Real 接口压力
    q L/min [1] Real 接口流量

    (7) 其他说明

    体积流量需要转换为 0 压下体积流量,相应的容性件会把接收到的 0 压下体积流量重新转换为容性件当前压力下的体积流量。

  2. 模型库测试报告

    模型测试过程中创建并完成,主要描述组件的测试内容(组件功能),测试方式(单元测试、子系统测试、系统测试),测试结果及结果分析等,保证模型的正确性。

# 优化处理

# 组件重用性

“重用”是面向对象语言的最大特点之一。Modelica 作为面向对象建模语言,提供继承(Extends)、变型(Modification)和重声明(Redeclaration)等机制,能够方便地支持模型重用。继承是对已有类型的重用,结合变型与重声明,实现对基类的定制与扩展。本节介绍如何利用这些机制来建立可重用的模型。

  • 抽象与继承

    定义抽象类型并继承使用,是实现模型重用的一种重要手段。例如许多电子元件都具有一个共性,即都具有两个端口。根据这一共性,我们可以定义一个抽象的元器件类型 OnePort,具有两个端口 p 与 n,还具有一个物理量 v 用于表示这个组件两端的电势差。

    采用继承机制建立的模型更加简洁。通过对基类数据及算法的重用,避免了不必要的代码重复。如果要修改共有的特性,只需修改基类 OnePort 即可。Modelica 标准库中就大量使用了继承机制。如果需要对一系列物理组件进行建模,而这些组件之间又具备诸多共同特性时,就可以应用抽象与继承建立可重用的模型。

  • 重声明

    除了继承与变型之外,Modelica 还提供了另外一种重用机制——重声明。相比继承机制的代码重用,变型机制的参数化功能,重声明机制能够有效地支持衍生设计。

    重声明语句以“redeclare”前缀予以标识。变型中的 redeclare 结构使用另一个声明替换变型元素中局部类或组件的声明。重声明既可以针对组件,也可以针对类型。无论哪种方式,都使得类型作为模型的参数,从而让抽象模型更具柔性。

    model HeatExchanger
    replaceable parameter GeometryRecord geometry;
    replaceable input Real u[2];
    end HeatExchanger;
    
    HeatExchanger heatExchanger(
    redeclare parameter GeoHorizontal geometry;
    redeclare input Modelica.SIunits.Angle u[2]);
    
  • inner/outer

    inner/outer 作为 Modelica 语言的高级特征之一,提供了一种外层变量或外层类型的引用机制。在元素前面使用“inner”前缀修饰,定义了一个被引用的外层元素;在元素前面使用“outer”前缀修饰,该元素引用相匹配的外层 inner 元素;对于一个 outer 元素,至少应存在一个相应的 inner 元素声明。inner/outer 相当于定义了一个全局接口或变量,可以在嵌套的所有实例层次中被访问。

# 组件初始化

Modelica 描述动态模型时,本质上就是描述模型状态是如何随时间变化的。当启动一次仿真时,状态需要被初始化。从数学角度而言,对于常微分方程和微分代数方程需要设定初始值,即初始条件。

  • 设置初始条件

    1. 在定义变量时直接为其设定初始值。

      model Sample1
      parameter Real x0=1.2;
      Real x(start=x0,fixed=true);
      equation
      der(x)=2*x-1;
      end Sample1;
      

      其中,属性 start 用于为变量设定初始值,属性 fixed 用于设定初始值的性质。当属性 fixed 置为 true 时,表示该初始值是既定初始值,必须得到满足,即变量的初始值必须等于由 start 指定的值。当属性 fixed 置为 false 或者缺省时,表示该初始值是备选初始值,可以不满足。备选初始值有两个方面的作用:其一,在求解初值时,若初值系统缺少约束条件,取备选初始值进行补充;其二,在求解非连续系统时,将该值当作变量的迭代起始值。

    2. 定义初始方程或初始算法。

      model Sample2
      Real x;
      initial equation
      der(x)=0;
      equation
      der(x)=2*x-1;
      end Sample2;
      

      在模型的 initial equation 部分定义的方程属于初始方程。初始方程是一种初始约束条件,表达初始时刻变量和变量导数之间的数值约束关系,常用于为变量导数设定初始值。此外,还可以通过定义初始算法(initial algorithm)来给定初始约束条件。

  • 确定初始条件个数

    对于以状态空间形式表示的常微分方程系统(ODE),,其初值系统有 个未知量 ,但模型方程只有 个,因此还需要 个初始条件。

    对于微分代数方程 DAE,初始条件个数的确定要比 ODE 复杂。

    例如,对于方程 ,其中 是状态变量, 是代数变量。方程共有 个原始方程。其初值系统有 个变量,因此也必须有 个方程。这意味着用户可以指定 个初始条件,但由于 DAE 系统可能是高指标的,其中可能包含隐含初始条件,因此用户给定的初始条件通常必须少于 个。

    如果一个模型规模较大,而且是高指标的,那么让用户确定需要给定多少个初始条件是件很困难的事情。为此,如果用户指定的初始条件太多,MWORKS 将输出错误信息,根据提示信息,用户可以移除某些初始条件。

    避免初始条件过多的一个有效方法是,将具有 start 属性的变量的 fixed 属性设置为 false,这时 MWORKS 根据需要自动选择备选初始条件并实现相容初始值求解;如果缺少初始条件,MWORKS 自动选择状态变量的 start 值补充初始条件。

# 组件健壮性

建立模型时需要注意模型的健壮性,需要考虑到模型在使用过程中会遇到极端问题和误用问题,一般提高模型健壮性的方法有:

  • 限位(保证某些参变量在设置和求解过程中的正确性,如气液容积的非负性,绝对压力的非负性,管道长度的非负性等等),可使用 Min/Max、assert 等关键字或者其他限位方式(限位函数,限位组件,If 限位语句)对模型进行限位;
  • 使用 final 关键字等限制模型参数被修改或变型。

# 组件仿真效率

  • 尽量使用 equation

    如果没有特殊的目的,请尽量使用 equation。原因是在 algorithm 块中,变量可能被赋值多次,使得工具无法对其进行符号分析,提高仿真效率。例如,无法得到求解雅可比矩阵的函数。

  • 避免不必要的事件

    事件会中断积分算法,从而影响仿真速度,因此避免不必要的事件可以较大地提供仿真速度。如:

    der(x)= noEvent(if y<0 then 0 else y^2);
    
  • 合适的积分算法

    积分算法有自己的适用范围,因此需要模型的特点,采用合适的算法,例如模型是刚性的,则需要采用求解刚性问题的积分算法。

  • 合适的误差

    误差越小仿真时间越长,结果越可靠,而误差越大仿真速度越快,但结果越不可靠。可以使用不同的误差仿真,比较它们的结果,而确定合适的误差。

  • 为函数提供雅可比函数

    对于隐式方程组,分析器使用符号推导,得到求解雅可比矩阵的函数,避免数据求解。由于分析器不能推导用户自定义函数,因此如果用户为自定义函数提供导数函数,可以提高仿真效率。如:

    model JacobianExample
    Real x,y;
    function f
    input Real x;
    output Real y;
    annotation (derivative=f_Jac);
    algorithm
    y := sin(x)+cos(x)+x;
    end f;
    function f_Jac
    input Real x;
    input Real dx;
    output Real dy;
    algorithm
    dy := dx * (cos(x)-sin(x)+1);
    end f_Jac;
    equation
    der(y) = 2.0;
    y = f(x);
    end JacobianExample;
    
  • 变量消除

    分析器会进行符号处理,消除别名变量。因此用户手动消除别名变量对仿真速度没有影响,而且容易使代码变得不直观。除非有证据表明消除变量对仿真速度有影响,否则不建议手动消除别名变量,如:

# 模型封装

模型封装在一定程度上可以说是模型分解的逆过程,一般层次化模型中的底层模型包含了模型的活动和功能的所有细节,较高层次的模型隐藏了相关模型的活动和细节,依赖于底层模型实现。一般模型的封装主要有两种形式:连接封装和重用封装。

# 连接封装

如下图,三通模型一般由主管节流孔、分支管道节流孔和节点容腔组成,将这些现有的底层模型相互连接,就可以封装形成三通模型,进行整体使用。

# 重用封装

基于功能的组件分解所形成的功能仿真模型的层次结构中,模型组合是通过上层模型对下层模型的模型调用和变型实现的,如下图所示,动态管道中一般包含传热模块,流动模块,结构模块等,将这些模块合理的组合封装起来就形成较为全面的动态管道模型,可以作为整体供系统使用。