PAYATHON 2026

Modelica 的 when/elsewhen 含 reinit() 时如何改写并处理 Dymola 报错?

数据小许

结论

按照 Modelica 3.6 规范,reinit() 只能出现在 when-equation 中,不能直接移到 algorithm 里。x := y 也不能用来重新初始化连续状态。可以先尝试在 equation 中去掉当前触发报错的 elsewhen 结构。如果确实需要算法式计算,就将普通离散计算与状态重置分开,仍在方程区使用 reinit()。规范:reinit

Modelica 规范允许在方程区使用 elsewhen。仅凭简化片段和报错,无法确定 Dymola 不支持的具体组合,也无法判断是否与所用版本有关。

为什么不能只把等号改成赋值符号?

equation 中的等式共同约束当前值,求值顺序不由书写顺序决定。例如,在同一个已触发的 when-equation 中:

a = b + 1;
b = 2;

结果应满足 b = 2、a = 3。如果直接改成 algorithm 中的语句:

a := b + 1;
b := 2;

第一行读取执行到该处时的 b,第二行才更新 b。对于在该算法中赋值的离散变量,算法执行前会在概念上用 pre(b) 初始化;如果此前没有更新 b,第一行使用的就是这个值。因此,结果可能与原方程不同。规范:Algorithm Sections

这就是报错中“右侧依赖左侧变量”的含义。转换时要逐项确认:表达式需要当前事件中算出的值,还是需要 pre(...) 所表示的事件迭代前值。变量相互依赖时,单靠调整语句顺序也未必能保留原方程的语义。

可以怎样保留 equation 并改写 elsewhen?

对于两个标量 Boolean 条件,可以尝试合并触发入口,再根据条件的上升沿选择分支。下面是结构示例,实际模型中还需补齐变量声明、初始化和连续状态方程:

equation
  when {c1, c2} then
    v = if edge(c1) then
          (if keep1 then pre(v) else value1)
        else
          (if keep2 then pre(v) else value2);

    if edge(c1) then
      if reset1 then
        reinit(x, target1);
      end if;
    elseif edge(c2) then
      if reset2 then
        reinit(x, target2);
      end if;
    end if;
  end when;

这里假设原逻辑是 when c1 ... elsewhen c2 ...,v 是事件更新的离散变量,x 是连续状态,keep1/keep2 和 reset1/reset2 分别代表各分支的保持条件与重置条件。

edge(c1) 等价于 c1 and not pre(c1)。用它选择分支,可以区分条件是一直为真,还是本次刚变为真。如果两个条件同时产生上升沿,前面的分支优先。规范:事件相关运算符

对于向量触发条件,需要逐元素处理上升沿。不要直接把 when {c1, c2} 改成 when c1 or c2:当 c1 已经为真时,c2 随后变为真可能不会让整个 or 表达式再次产生上升沿。

这是一种消除 elsewhen 的改写思路,尚未在题目对应的 Dymola 环境中验证。如果触发条件依赖正在更新的变量,还需要检查事件迭代能否收敛。

确实需要 algorithm 时怎么办?

可以让 algorithm 负责更新离散变量和计算重置目标值,再由独立的 when-equation 执行 reinit()。拆分时要特别注意保持原来的触发时机和分支优先级。

如果使用重置请求变量连接两个区域,要确认每次请求都能形成新的事件。例如,一个持续为 true 的 Boolean 请求不会在下一次需要重置时自动再次触发 when request。连续多次请求必须有可区分的变化,不能只反复赋值为 true。

算法中的条件分支可以省略某些离散变量的赋值,使其保留 pre(...) 值;但如果前面的语句已经更新了该变量,省略后续赋值会保留刚更新的值。迁移时应检查整个算法的执行顺序。

“什么也不做”需要区分变量类型

对于由 when-equation 更新的离散变量:

v = pre(v);

表示在该次更新中保持原值。普通变量的定义还需要满足 when-equation 的分支一致性要求。

对于连续状态 x,不需要重置时应让 reinit() 不执行,x 继续遵循原有连续方程。不要为了补齐分支而添加 x = pre(x)。reinit() 有专门的结构规则,不能直接套用普通赋值方程的补齐方式;同一个状态的重置也应集中在同一个 when-equation 中。规范:When-Equations 与 reinit

排查时,建议提供 Dymola 版本,以及包含变量类型、初始化、连续状态方程和两个相关分支的最小模型。题目中的省略号、变量名混用和示意条件不足以确定具体编译原因,也不能据此保证某种改写一定通过检查。

备注:内容仅供参考。