概述
正如我们在之前关于稳态初始化的讨论中已经提到的那样,行为由模型中包含的方程以及赋予模型中状态变量的初始条件共同表示。在 Modelica 中,初始条件是通过将普通方程(存在于方程部分)与任何初始方程(存在于初始方程部分)相结合来计算得出的。
对于新用户来说,最先感到困惑的问题之一就是理解需要多少个初始条件。这个问题的答案很简单。为了使初始问题表述得当(即初始方程既不过多也不过少),我们需要在初始方程部分的方程数量与系统中的状态数量相同。需要注意的是,初始方程过少时,工具可以补充额外的方程以使问题表述得当,但初始方程过多时,我们可能无法解决问题(因为这取决于工具识别和消除冗余方程的能力,而不同的工具在这方面提供的支持程度也不同)。
当然,说初始方程的数量必须与状态的数量相等回答了一个问题,但很快又产生了另一个问题,即我们如何确定有多少个状态?对于我们在本章中看到的模型,答案相当简单。到目前为止,我们每个示例中的状态都是出现在 der(...) 操作符内部的变量。换句话说,在那些示例中,我们对每个求导的变量都是一个状态。
常微分方程
需要指出的是,并非我们所求解的每一个变量都必定是状态变量。在本章中,我们所见到的所有模型都是常微分方程(ODE)。在处理常微分方程时,每一个求导的变量都是一个状态变量,这也就意味着你需要为每个这样的求导变量设定一个初始方程。但在后续的章节中,我们最终会遇到所谓的微分代数方程(DAEs)的例子。在这些情况下,只有部分求导的变量可以被视为状态变量。
事实证明,理解初始化过程其实并不需要我们深入探讨动态偏微分方程(DAEs)。实际上,所有的 Modelica 工具都会执行一种叫做“索引简化”的操作。虽然索引简化算法本身相当复杂(所以我们现在就不深入探讨了),但其效果却相当简单。索引简化会将 DAE 方程转换为常微分方程(ODE)。换句话说,Modelica 编译器会将我们 Modelica 代码中所包含的任何 DAE 问题转换为这种相对容易解释的 ODE 形式。
那么,我们暂且不讨论 DAE 和索引简化的问题,直接从初始化的讨论开始吧,假设我们的问题已经简化为一个常微分方程(ODE)。在这种情况下,我们真正需要理解的只是模型中的所有状态都需要进行初始化,而且我们的模型将具有以下一般形式的常微分方程:
其中 表示当前的模拟时间,是在时间 时我们系统中各状态的值, 是在时间 时我们系统所接受的任何外部输入的值。
请注意,变量上方的箭头仅仅表示该变量为向量,而非标量。另外请注意,在这个问题中唯一被标出为有导数的变量是 。这就是我们确定 表示系统状态的方式。关于这个系统,还有一个需要注意的事项是,函数 和都不依赖于 。
如果您仔细思考一下就会发现, 和 都是处于我们系统之外的元素。我们既不对其进行计算,也不对其进行控制。之所以我们将 称为我们系统的状态,是因为它是(我们系统内部所能提供的)唯一需要的信息,而 和 则是(我们为了得出解决方案而需要计算的)唯一需要计算的内容。
回到初始化这个话题上来,在正常的时间步中,我们将通过积分来求解 以计算出 。换句话说:
只要存在前一个时间步长,这一切就都能正常运行。如果没有前一个时间步长,那么我们代入到方程中的 的值就必须是我们在模拟中 的初始值。换句话说,就是我们的初始条件。
人们可能会认为,我们应当通过添加这样一个方程来明确初始条件:
其中 是我们模拟的开始时间, 则是初始值的明确说明。在指定初始条件时,为状态提供明确的值是常见的情况。因此,我们肯定需要能够处理这种情况。但这种方法对于我们在稳态初始化中所展示的情况并不适用。在那里,我们没有为状态提供明确的初始值。相反,我们为 提供了初始值。那么,我们如何同时处理这两种情况呢?
初始方程
答案是:在我们模拟开始时,我们需要解决这样一个问题:
请注意新增的一项功能——。此新功能代表了我们在初始方程部分所设定的任何方程。由于 会同时接受 和 作为参数,这使得我们能够表达出广泛的初始条件。为了为状态设定明确的初始值,我们可以将 定义为:
但我们也可以通过定义来表达我们希望从稳态解入手的愿望,其定义如下:
当然,我们还可以将这些不同的形式进行混合使用,或者根据不同的状态使用各种各样的其他形式来描述我们的初始条件。所以在编写初始方程时,您只需要记住它们需要符合上述的通用形式,并且不能比系统中的状态数量还要多。
结论
正如我们在本章中所展示的那样,Modelica 中的初始方程结构使我们能够以多种方式来初始化我们的系统。最终,所有这些方法都会为我们的系统中的状态计算出初始值。但我们在描述这些值的具体计算方式时拥有极大的自由度。
这是 Modelica 的一大优势所在。在 Modelica 中,初始化处理被置于首要位置,这种灵活性在众多实际应用中都发挥了重要作用。