Cooling Revisited

Changing Ambient Conditions

We will start with a simple example that demonstrates time events. We will revisit the thermal model introduced previously in the section on Physical Types. However, this time we will introduce a disturbance to that system. Specifically, we will trigger an abrupt decrease in the ambient temperature after half a second of simulation. This revised model is written as follows:

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;

The highlight lines show an if statement. This particular if statement provides two different equations for computing T_inf.

Time

You will note in this model the variable time is not declared within our model. This is because time is a built-in variable in all Modelica models.

The decision about which of the two equations will actually be used depends on the conditional expression time<=0.5. It is because this expression only depends on time and not any other variables in our model that we can characterize the transition between these two equations as a “time event”. The key point is that when integrating these equations, we can tell the solver that integrates our system of equations to stop precisely at 0.5 seconds and then resume again using a different equation. We’ll see examples of other state events where this would not be possible, in the next section when we present the classic Bouncing Ball example.

But for now, let us continue with our cooling example. If we simulate this model for one second, we get the following temperature trajectory:

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

As you can see in these results, the ambient temperature does indeed start to decrease after half a second. In studying the dynamic response of the temperature itself, we see two distinct phases. The first phase is the initial transient response toward equilibrium (to match the ambient temperature). The second phase is the tracking of the ambient temperature as it decreases.

Initial Transients

It is worth noting that this is a very common issue in modeling. Frequently, you wish to model the systems response to some disturbance (like the ambient temperature decrease in this case). However, if you don’t start your system in some kind of equilibrium state, the system response will also include some kind of initial transient (like the one shown here). In order to distinguish these two responses clearly, we want to avoid any overlap between them. The simplest way to do that is to start the simulation in an equilibrium condition (as discussed previous in our discussion of Steady State Initialization). This avoids the initial transient altogether and allows us to focus entirely on the disturbance that we are interested in.

As we learned during our discussion of Initialization, we can solve this problem of initial transients by simply including an initial equation that will determine a value for T such that our system starts in an equilibrium state, i.e.,

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;

The only thing we’ve changed is the initial equation. Instead of starting our system at some fixed temperature, we start it at a temperature such that the change in temperature (at least initially, prior to our disturbance) is zero. Now the temperature response no longer includes any initial transient and we can focus only on the response to the disturbance:

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

Compactness

One issue with if statements is that they can make relatively simple changes in behavior appear quite complicated. There are a couple of alternative constructs we can use to get the same behavior with fewer lines of code.

The first approach is to use an if expression. Whereas an if statement includes “branches” containing equations, an if expression has branches that contain only expressions. Furthermore, the syntax for an if expression is also less verbose. If we had chosen to use an if expression our equation could have been simplified to:

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";

Alternatively, we could use one of the many built-in Modelica functions, like max, to represent the change in the ambient temperature, e.g.,

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";

Events

We’ve seen several ways to express the fact that there is an abrupt change in the behavior of our system. But it’s important to point out that we are not just describing a change in the ambient temperature, we are also specifying when it changes. This a subtle, but very important, point.

Consider the last example, where our system began in an equilibrium state. At the start of the simulation, there are no significant dynamics. Since nothing is really changing in the system, the integrator is unlikely to accumulate significant integration error. So, in order to minimize the amount of time required to complete the simulation, variable time step integrators will, in such circumstances, increase their step size.

There is, however, a risk in doing this. The risk is that the integrator may get “blind-sided” by a sudden disturbance in the system. If such a disturbance were to occur, the integrator’s assumptions that a large step will not lead to significant integration error would not be true.

The question then becomes, how can the integrator know when it can take a large time step and when it cannot. Typically, these integration schemes use a kind of trial and error approach. They try to take large step and then estimate the amount of error introduced by that step. If it is less than some threshold, then they accept the state (or perhaps try a larger step). If, on the other hand, the step introduces too much error, then they try a smaller step. But they cannot know how small a step will be required to get under the error threshold, which means they will continue to blindly try smaller and smaller steps.

But Modelica is about much more than integrating the underlying system. Modelica compilers study the structure of the problem. In all of our examples, the compiler can see that there is a distinct change in behavior. Not only that, it can see that this change in behavior is a time event, i.e., an event whose time is known a priori without any knowledge of the solution trajectory.

So, what a Modelica compiler will do is inform the underlying integrator that there will be an abrupt change in behavior at 0.5 seconds and it will instruct the integrator to simply integrate exactly up to that point and no further. As a result, the abrupt change never occurs within a time step. Instead, the integrator will simply restart on the other side of the event. This completely avoids the blind searching for the cutoff time that minimizes the error in the step. Instead, the integrator will integrate right up to that point automatically and then restart after that point.

This is one of many examples of features in Modelica that optimize the way a simulation is carried out. A more detailed discussion of this kind of handling can be found in the upcoming section on Events. In the coming sections, we’ll also see more complex examples of events that depend on the solution variables.