3. Modelica语法与方程建模基础
Modelica 的建模过程,本质上是用一种声明式语言把物理系统的守恒关系、本构关系和连接关系写成方程。与命令式语言不同,Modelica 模型里没有“先算谁、后算谁”的固定顺序,求解器会根据方程之间的依赖关系自动决定计算顺序。因此,理解变量与参数声明、方程与算法段、初始方程、when 与 if 语句、内置函数与运算符,以及注释与文档规范,是写出可编译、可求解、可维护模型的前提。
变量与参数声明:区分“随时间变化”与“建模前确定”
在 Modelica 中,变量声明决定了一个量的因果角色和求解属性。最常用的前缀有 parameter、constant、input、output,以及描述状态的 discrete。参数在仿真开始前必须被赋值,仿真过程中保持不变;变量则随时间变化,由方程求解得到。
model RC_Circuit
parameter Real R(unit="Ohm") = 1000 "电阻";
parameter Real C(unit="F") = 1e-6 "电容";
Real u_C(unit="V", start=0) "电容电压";
Real i(unit="A") "回路电流";
input Real u_in(unit="V") "输入电压";
output Real y(unit="V") "输出电压";
equation
u_in = i*R + u_C;
i = C*der(u_C);
y = u_C;
end RC_Circuit;
这里 start=0 是变量的初始猜测值,不是初始方程;它只帮助求解器在迭代时找到合适的初值。真正决定初始状态的是 initial equation 段。单位 unit 是可选属性,但强烈建议填写,因为单位一致性检查能在编译阶段发现量纲错误。
方程段与算法段:声明式与命令式的边界
equation 段是 Modelica 的核心。它要求方程数量与未知变量数量匹配,且方程之间不能互相矛盾。方程可以是等式,也可以是 connect() 这样的连接方程。连接方程会自动生成势变量相等、流变量之和为零的守恒关系,这是多领域建模的关键机制。
algorithm 段则允许写赋值语句、if 分支和 for 循环,适合表达查表、分段函数或需要显式控制流的计算。但算法段中的赋值是命令式的,求解器无法像方程那样自由重排,因此应尽量把物理关系放在 equation 段,把纯计算逻辑放在 algorithm 段。
初始方程:让仿真从正确的状态出发
动态模型需要初始条件。Modelica 提供 initial equation 段,用于指定仿真开始时刻的约束。例如电容电压初始为零、电感电流初始为零,或者系统处于稳态时 der(x)=0。如果初始方程数量不足,求解器会使用变量的 start 值作为补充,但这可能导致不一致的初始状态。
initial equation
u_C = 0; // 电容初始电压为零
der(u_C) = 0; // 初始时刻处于稳态
注意:initial equation 中的方程只在 t=0 时刻生效,不会影响后续仿真。对于高阶系统,初始方程的数量必须与状态变量数量一致,否则会出现“初始条件不足”或“初始条件过约束”的报错。
when 与 if:离散事件与条件表达
if 表达式用于连续或分段连续的条件赋值,例如 y = if x > 0 then 1 else -1;。它不会产生事件,只是根据条件选择表达式。when 语句则用于离散事件,只在条件从 false 变为 true 的瞬间执行一次,常用于采样、开关动作和状态切换。
when u_C >= 0.5 then
open = true;
end when;
when 语句中的赋值是离散的,变量应声明为 discrete 或使用 pre() 运算符读取事件前的值。例如 pre(open) 表示事件发生前的 open 值。如果误用连续变量在 when 中赋值,会导致求解器无法正确处理事件,甚至仿真失败。
常用内置函数与运算符
Modelica 内置了大量数学函数和运算符,常用的包括:der() 求导、pre() 读取事件前值、abs() 绝对值、sqrt() 平方根、sin()/cos() 三角函数、min()/max() 极值、mod() 取模、floor()/ceil() 取整。运算符方面,+、-、*、/、^ 分别对应加减乘除和幂运算,. 用于成员访问,: 用于数组切片。
需要特别注意的是,der() 只能作用于连续变量,不能作用于参数或离散变量;pre() 只能用于 when 语句内部或离散变量的读取。这些限制是 Modelica 保证方程系统可解性的重要规则。
模型注释与文档规范
Modelica 支持两种注释:// 单行注释和 /* ... */ 多行注释。此外,字符串字面量可以直接作为声明或方程的注释,例如 Real x "位移";。这些字符串会被工具提取到文档中,因此建议用简洁的中文或英文描述物理含义和单位。
对于模型文档,推荐使用 annotation(Documentation(info="...")) 提供更完整的说明,包括模型用途、参数含义、使用示例和参考文献。良好的注释规范不仅方便他人复用,也能在团队协作中减少沟通成本。
上图把模型结构分成声明区、方程段、初始方程、事件与算法、内置函数五个部分,并标出了它们之间的依赖关系。声明区提供参数和变量,方程段建立物理约束,初始方程确定起点,事件与算法处理离散逻辑,内置函数则贯穿所有计算。理解这张结构图,就能在写模型时快速判断一段代码应该放在哪个段里。
when 而不是 if,以避免求解器在事件检测上出现数值抖动。