再探冷却

改变环境条件

我们将从一个简单的例子开始介绍了时间事件。我们将重新审视在前面物理类型中介绍的热学模型。不过,这次我们将对该系统添加一个扰动。具体地说,我们将仿真开始的半秒后让环境温度的突然下降。修正后的模型可以如下:

model NewtonCoolingDynamic
  "Cooling example with fluctuating ambient conditions"
  // Types
  type Temperature=Real(unit="K", min=0);
  type ConvectionCoefficient=Real(unit="W/(m2.K)", min=0);
  type Area=Real(unit="m2", min=0);
  type Mass=Real(unit="kg", min=0);
  type SpecificHeat=Real(unit="J/(K.kg)", min=0);

  // Parameters
  parameter Temperature T0=363.15 "Initial temperature";
  parameter ConvectionCoefficient h=0.7 "Convective cooling coefficient";
  parameter Area A=1.0 "Surface area";
  parameter Mass m=0.1 "Mass of thermal capacitance";
  parameter SpecificHeat c_p=1.2 "Specific heat";

  // Variables
  Temperature T_inf "Ambient temperature";
  Temperature T "Temperature";
initial equation
  T = T0 "Specify initial value for T";
equation
  if time<=0.5 then
    T_inf = 298.15 "Constant temperature when time<=0.5";
  else
    T_inf = 298.15-20*(time-0.5) "Otherwise, increasing";
  end if;
  m*c_p*der(T) = h*A*(T_inf-T) "Newton's law of cooling";
end NewtonCoolingDynamic;

高亮行为if语句。这条特定的if语句为T_inf的计算提供两条不同的等式。

时间变量

你会注意到这个模型中没有声明变量time。之所以如此是因为time是一个内置在所有Modelica模型内的变量。

两个方程中实际使用哪个依赖于条件表达式time<=0.5。正因为这个表达式仅仅取决于time,而不是在模型中的任何其他变量,我们可以把这两个公式之间的转换称为“时间事件”。关键的一点是,对这些方程进行积分时,我们可以告诉对方程组进行积分的求解器精确地停在第0.5秒,然后继续使用不同的公式进行求解。在下一节我们介绍的经典的弹跳球例子里,我们会看到对于其他状态的事件而言,这是不可能的。

但是现在,让我们继续考虑我们的冷却例子。如果我们对这个模型进行一秒钟的仿真,我们将得到以下的温度轨迹:

../../../_images/NCD.png

正如你在这些结果中看到的,环境温度确实在半秒后开始减少。在研究的温度本身的动态响应时,我们看到两个不同的阶段。第一阶段是趋向平衡态的初始瞬态响应(从而和环境温度相匹配)。第二阶段是随在环境温度的减小的进行的追踪。

初始瞬态

值得注意的是,这是建模中一个的非常常见的问题。你常常要模拟的是系统对一些扰动的响应(如在此例中环境温度的下降)。但是,如果系统开始时不处于某种平衡态下,系统响应将包括某种形式的初始瞬态(图示)。为了清楚地区分这两种响应,我们要避免它们之间有任何重叠。要做到这一点最简单的方法就是从平衡态开始仿真(参见我们之前在稳定状态初始化的讨论)。这完全避免了初始瞬态,允许我们完全专注于我们所感兴趣的扰动。

正如我们在初始化中的讨论所了解到的一样,我们可以通过添加初始方程解决初始瞬态的问题。我们用初始方程找到这样的一个T值,使得我们的系统从平衡态开始仿真,即:

model NewtonCoolingSteadyThenDynamic
  "Dynamic cooling example with steady state conditions"
  type Temperature=Real(unit="K", min=0);
  type ConvectionCoefficient=Real(unit="W/(m2.K)", min=0);
  type Area=Real(unit="m2", min=0);
  type Mass=Real(unit="kg", min=0);
  type SpecificHeat=Real(unit="J/(K.kg)", min=0);

  parameter ConvectionCoefficient h=0.7 "Convective cooling coefficient";
  parameter Area A=1.0 "Surface area";
  parameter Mass m=0.1 "Mass of thermal capacitance";
  parameter SpecificHeat c_p=1.2 "Specific heat";

  Temperature T_inf "Ambient temperature";
  Temperature T "Temperature";
initial equation
  der(T) = 0 "Steady state initial conditions";
equation
  if time<=0.5 then
    T_inf = 298.15 "Constant temperature when time<=0.5";
  else
    T_inf = 298.15-20*(time-0.5) "Otherwise, increasing";
  end if;
  m*c_p*der(T) = h*A*(T_inf-T) "Newton's law of cooling";
end NewtonCoolingSteadyThenDynamic;

我们唯一改变了的是初始化的方程。相比从某一固定温度开始系统的仿真,我们现在让仿真开始时温度的变化为零(至少在一开始没有外加扰动影响的时候)。现在的温度响应不再包括任何初始瞬态,我们可以只专注于系统对干扰的响应。

../../../_images/NCSTD.png

简洁性

if语句的一个问题是,它们可以让相对简单的行为变化看起来相当复杂。我们可以用好几种另外的语法结构以更少的代码获得相同的行为。

第一种方法是使用一个if表达式。相对于包含方程组“分支”的if语句,if表达式只包含表达式分支。此外,语法上if表达式也更为简洁。倘若我们使用if表达式,我们的equation部分会被简化为:

equation
  T_inf = 298.15 - (if time<0.5 then 0 else 20*(time-0.5));
  m*c_p*der(T) = h*A*(T_inf-T) "Newton's law of cooling";

此外,我们可以使用许多Modelica的内置函数,如max来表示在环境温度的变化,例如:

equation
  T_inf = 298.15 - max(0, 20*(time-0.5));
  m*c_p*der(T) = h*A*(T_inf-T) "Newton's law of cooling";

事件

我们已经看到了几种方式来表达在系统行为的突然变化。但要特别指出的是,我们不仅仅描述了环境温度的变化,我们也规定了何时(when)它会发生变化。

考虑最后一个例子:我们的系统初始状态是一个平衡态。在仿真的开始,系统没有显著的动态。因为系统中并没有真正变化,积分器是不可能积累显著的积分误差。所以,为了最小化完成仿真所需的时间,可变步长的积分器会在这样的情况下提高其步长。

但这样做有其风险。风险在于,系统中突然的扰动可能会让积分器“措手不及” 。如果发生了这样的扰动,积分器对于大步长不会导致显著积分误差的假设,便不再成立了。

那么问题就变成,积分器如何知道何时它可以使用一个大的时间步长,何时不能。通常情况下,这些积分方案使用一种试错法。积分器试图使用大步长,然后估算由该步长引入的误差。如果误差小于某个阈值,则积分器接受新算出的状态(或可能尝试更大的步长)。但倘若在该步长引入了过多的错误,那么积分器便尝试较小的步长。但它们不知道为了符合误差阈值要求需要多小的步长。这意味着它们将盲目地尝试更小的步长,直到符合条件。

但Modelica不止是关于相关系统的积分。Modelica编译器研究问题的结构。在我们的所有例子中,编译器都可以看到系统有一个明显的行为变化。不仅如此,它可以看到这个行为改变是一个时间事件。亦即一个这样的一个事件,其发生时间被先验地、不需要任何关于解的轨迹的信息就被确定

因此,Modelica语言编译器会告诉底层的积分器,在第0.5秒时系统行为会突然发生变化,然后它会指导积分器一步不多地积到该时点。结果,在时间步长内从不会发生急剧的变化。相反,该积分器会简单地在该事件的另一侧重新开始积分。这完全避免为了最小化步长内的误差而去盲目搜索截止时间。相反,该积分器会自动恰好积到该点,然后在该点后重新启动。

Modelica语言有众多能优化仿真进行方式的特性。这是一个其中一个例子。读者可以在关于事件的紧接小节中找到对于事件处理的进一步讨论。在接下来的章节中,我们还可以看到另一种更复杂事件的例子。这种事件会依赖于解中的变量的值。