Yesterday I gave an overview of how time is (typically) handled in continuous and discrete-event simulations. Today I want to discuss an even bigger difference between the two.
Discrete-event simulation is probably well-named because it describes what it does, which is model events individually exactly when they happen. Almost any calculations may be performed as each event is processed in its time, but there is no requirement that any calculus be used. It might be, but you can write some pretty hairy simulations that don’t involve the merest whiff of it. For example, I worked on a team that simulated the flight, maintenance, and logistics activities of groups of aircraft over time. The basis of our work was a model that have evolved over the course of several decades until it was considered the most complex ever created in its particular implementation language.
Continuous simulation, by contrast, is about the continual reevaluation of systems described by differential equations. While theoretically it might be possible to differentiate over any quantity (e.g., location, temperature, density), in practice the main quantity over which systems are differentiated is time. Running the reevaluations during each increment of time serves as a form of ongoing integration.
If we consider a simple system that models movement along a single axis we might having something like:
dx / dt = v
x is a measure of distance along an x-axis
t is time
v is velocity
Integrating this over a specified period of time we can rewrite this as:
(x – x0) / (t – t0) = v
x0 is the initial location
t0 is the initial time
Rearranging yields something we can actually use:
x = x0 + v * Δt
That is, the new x location is equal to the original x location plus the (average) velocity over the interval being considered, times that interval of time. This is probably the simplest possible differential equation I could write. I think every beginning physics and math student starts off with this or something very much like it. In (let’s say C) code you would write something like:
x = x + (v * timestep);
Things can get very tricky from there very quickly. In many cases you might be dealing with nonlinear equations. At other times you might need to solve a large number of differential equations simultaneously. You might want to calculate new values using coefficients for properties that are themselves a function of the new value you’re trying to calculate. I’ll talk about a few of these over the next couple of days but this is the main point I wanted to get across.
In my personal experience I’ve found that continuous simulations can be harder to model in terms of how the activities are represented. This is not because “calculus!” but because getting all of the effects right, describing them in terms of the correct governing equations, and working out the solutions in running code can be complicated. That said, the basic control framework of a continuous simulation is almost mindless: Do some stuff. Advance the clock by a fixed amount. Repeat. I’ve found it much easier to write statements that describe the events that are modeled in discrete-event simulations, but the implementation of the discrete-event mechanism in code can be tricky. That’s why such simulations are often written in special languages devoted to this particular practice. They can be written in a more general language, but the practitioner has to know a lot more to make it work.
It is also possible, of course, to create hybrid simulations that combine both techniques, or to write each type of model using the other’s underlying timing mechanism. One might have to be of questionable sanity or sobriety to do the latter, but it could be done.
I’ve written continuous simulations that used time steps of anywhere from 1/4 of a second to 50 seconds, but as I mentioned yesterday the range of times can be much, much larger. I’ve also worked on systems where different processes used different time steps. I’ve written and worked on discrete-event simulations where some activities were scheduled to occur at regular intervals, but those never involved integrating differential equations over time.
What these two methods have in common is that they can both be complex. Whether describing the system is conceptually easy or hard and whether the underlying implementation is easy or hard it is still incumbent upon the modeler to identify and correctly characterize the components of the system being modeled. You can encounter plenty of difficulty understanding a system and its inputs, outputs, behaviors, and interactions. Just because you know how the pieces work doesn’t guarantee that you’re going to come up with the right answer. Teams of very smart people can work on these things for years on end and still find things that are wrong, missing, improperly defined, have incorrect data, and so on.