如何为Modelica多解方程中的变量设定求解域?
Modelica的核心优势之一是支持非因果建模:比如描述初始高度10米的下落物体高度与时间的关系时,你可以直接写 h=10 - g/2 * t^2,无需手动将方程改写为平方根形式,Modelica就能直接求解高度关于时间的函数——但这种方式仅在系统存在唯一解时稳定。
不过这类方程的逆函数往往存在多解:比如上述例子中,-t和+t对应的高度完全相同。如果是物体被向上抛出并在t=0时到达10米最高点的场景,负解对应的就是物体被抛出的时刻。
这类多解问题在非线性方程中很常见,比如求解 y=sin(alpha) 中的alpha时,可能的解分布在 -pi/2<alpha<pi/2 或 pi/2<alpha<3pi/2 两个区间,而这两个区间内sin函数的斜率符号完全相反,明确求解域就变得至关重要。
下面是一个展示该问题的测试模型:
model test_range Real x,x_rev(start=2),y; equation der(x)=1000; when x>=3*Modelica.Constants.pi/2 then reinit(x,Modelica.Constants.pi/2); end when; y=sin(x); y=sin(x_rev); annotation (experiment( __Dymola_NumberOfIntervals=4000, Tolerance=1e-06, __Dymola_Algorithm="Dassl")); end test_range;
在Dymola中仿真时,会出现求解区间跳变:初始解处于pi附近区间,随后跳至2pi附近区间;在OpenModelica中,x_rev的解会在 -pi/2~pi/2 和 pi/2~3pi/2 区间之间来回跳变(解的波动情况取决于仿真的区间数量)。
如何为多解变量设定求解域?
答案是可以,主要有三种常用方法:
添加硬约束方程
直接为变量添加区间限制的方程,强制求解器在指定范围内找解。比如要限定alpha在-pi/2<alpha<pi/2,可以在方程段补充:alpha >= -Modelica.Constants.pi/2; alpha <= Modelica.Constants.pi/2;注意:这类约束需要确保与整个方程系统在仿真全程兼容,否则会导致仿真终止。
使用带分支选择的逆函数
对于三角函数这类常见多解函数,Modelica标准库提供了带区间参数的逆函数。比如Modelica.Math.asin(y, interval="principal")会返回主值区间(-pi/2到pi/2)的解,interval="alternative"则返回pi/2到3pi/2区间的解。
针对测试模型,修改x_rev的方程为:x_rev = Modelica.Math.asin(y, interval="principal");就能强制x_rev始终在主值区间求解,避免跳变。
初始化与reinit配合引导
合理设置变量的start初始值,或在when语句中用reinit强制变量回到目标区间,也能引导求解器保持在指定解分支。比如在测试模型中添加:when x_rev > Modelica.Constants.pi/2 then reinit(x_rev, x_rev - Modelica.Constants.pi); end when;当x_rev偏离目标区间时,将其拉回合理范围。
内容的提问来源于stack exchange,提问作者paul

