Bolte, J.P. J.A. Fisher, and D.H. Ernst. 1993. An object-oriented, message-based environment for integrating continuous, event-driven and knowledge-based simulation. Proceedings: Application of Advanced Information Technologies: Effective Management of Natural Resources. ASAE. June 18-19, Spokane, WA.
KEYWORDS: Computer Simulation, Artificial Intelligence, Object Oriented Programming
*J. P. Bolte, Assistant Professor, Bioresource Engineering Dept.,
Oregon State University; J.A. Fisher, Research Associate, Institute
for Simulation and Training, University of Central Florida; D.H.
Ernst, Research Assistant, Bioresource Engineering Dept., Oregon
State University.
A high-level, object-oriented simulation environment has been
developed. The environment provides a number of different types
of simulation objects classes, including continuous simulators,
discrete events, and knowledge-based agents, each of which can
be subclassed and instantiated using full inheritance into more
specific types of simulation objects. The simulation environment
provides a simulation clock controlling and coordinating time-based
operations, maintains an event list of "interesting"
events scheduled to occur at some point in future time, and provides
a number of different notification and message-passing mechanisms
allowing communication and interaction between objects at several
different levels via messages directed to specific objects, general
notification messages, and a blackboard. Because all objects
in the system subclass from a single high-level simulation object,
integration of conventional continuous simulators running at variable
timesteps, discrete events and expert system-based simulation
agents is straightforward. The environment consists of a number
of reusable object classes and is written in the C++ programming
language
The computer has been a tool for scientists and engineers for the last 40 years. Numerical methods were born with the FORTRAN programming language in the late 1950's. At this time, scientists and engineers had a new powerful tool that allowed them to model complex mathematical relationships. Both hardware and software tools continued to improve and evolve. One very important development in computer language representation has been the emergence of the object-oriented programming (OOP) paradigm. The first object-oriented simulation language (OOSL), Simula, was introduced in 1967. Since that time several new OOSL's have been created. Unfortunately, these have lacked several capabilities required for representing the complexities of biological systems, and in particular have lacked consistent, flexible interfaces allowing integration of continuous, discrete and knowledge-based simulation objects and events. To provide these capabilities and take advantage of the power of object-oriented representations, a simulation framework was developed and implemented with the following goals: 1) to develop a simulation environment taking advantage of the powerful capabilities of modern object-oriented representations to provide improved tools and better modularity of simulation constructs and to provide a consistent, unified approach towards integrating continuous, event driven, and knowledge-based simulations, 2) to create mechanisms for inter-object communication for simulation objects without introducing object dependencies, 3) to produce simulation class constructs that would contribute to the creation of a robust object oriented simulation language, and 4) to provide mechanisms for seamlessly integrating knowledge-based "agents" with more traditional numerical representations. This research establishes a procedure for including intelligent simulation objects into a simulation model.
OOP languages are lacking some features which are necessary for robust simulations. Four important features that are not standard in OOP languages are object time-flow synchronization, event-handling, object-independent interobject communication, and support for intelligent agent-based simulation objects.
Time synchronizati on is necessary to provide high-level control over various parts of the simulation system, managing the updating of objects with differing timesteps and controlling the overall simulation flow. Event-handling is required for discrete simulation objects and provides a basis for posting or broadcasting the occurrence of interesting system and simulation object events. Interobject communication without object dependence is highly desirable for increasing program modularity and flexibility, and is not a built-in
feature of any language. Programmers and simulationist's have long enjoyed AI techniques but incorporating them into a simulation model has never been simple. Agent technology, integrated into the simulation environment, provides a mechanism for adding intelligent monitoring and control to a simulation. The simulation framework described here provides support for these areas.
Simulation Objects
In an object-oriented simulation language, simulation objects
representing real-world entities are simulated through time.
These objects represent the fundamental simulation unit. Simulation
objects may come in different shapes and sizes but they all conform
to minimum simulation environment interface requirements and have
a common base level of functionality by virtue of the fact that
they all derive from a single, generic high-level simulation object
(SimObj) class which provides for and dictates certain
behaviors. These simulation objects typically contain one or more
state variables which start with some initial value at the start
of the simulation, and are then updated in response to an Update()
message through simulated time as the simulation progresses.
The virtual simulation object class SimObj provide an abstract
description and response to maintaining data, generating plots,
and tracing simulation results for all derived child simulation
objects. SimObj provides data and behavior that is common
to all simulation objects and provides most of the required functionality
for interacting with the simulation environment.
User-defined simulation object classes derive from the abstract
class SimObj. A simulation object has data and methods
just like any object. Its data and methods are specifically designed
to aid in the simulation of the object itself. These simulation
objects provide the data and behavior specific to their uniqueness
while the parent class SimObj provides data and functionality
that is common to all simulation objects. Simulation objects
are designed to package data and behavior that is representative
of the object being simulated.
The class SimObj provides certain data and behavior to
a derived simulation object. Every simulation object has a name.
This name is a character string and it's purpose is to identify
the object with a meaningful name during reporting, debugging,
and tracing. An object's timestep is the time interval between
object updates, and can be set on an object-by-object basis. Deterministic
simulation objects are typically updated at a constant interval
while stochastic simulation objects are updated at intervals
established by their random number distributions. The priority
of a simulation object establishes it's ordering sequence in the
simulation event list when two objects are being updated at the
same time.
It is sometimes useful to have simulation objects store data.
This storage of information can be accomplished by giving a SimObj
a reference to a DataObject. The DataObject is
a repository for data. DataObjects have pre-defined behaviors
which allow for automatic creation of multiple dynamic plots from
the data and representation in a tabular (spreadsheet) form. The
ability to debug a simulation object is another necessary requirement
of simulation languages. Tracing lets a user view the state of
simulation objects through time. The TraceArray is an array
of structures that store information about the current state of
an object. The simulation objects trace can be directed to an
output device for debugging purposes. SimObj::StartTrace(),
SimObj::StopTrace(), and SimObj::InspectProperty()
are methods for tracing the state of a simulation object.
One thing that is common in many simulation languages is the ability
to transfer tokens or objects from one object to another. In a
queuing simulation, customers in a bank can be considered a Widget
or Token that is passed from one object in the system to
the next object in the system. Also, manufacturing lines that
produce some product will move a component from one workstation
to the next. Thus, object transfer capabilities are considered
necessary in a simulation language. To provide such object transfer
capabilities, the SimObj class provides two mechanisms
to transfer objects between objects. The first method is a Transfer()
function which requires a reference to the receiving object. The
second method utilizes the simulation framework's interobject
message-passing mechanisms through Broadcast-Notify and
Broadcast-Notify-Transfer methodologies discussed below.
These message and object passing schemes have proven very useful
because the sender doesn't need to know who the receiver is, reducing
object dependencies and increased modularity.
In every simulation language, the state variables of a system
can change value only during an event. An event is defined
as a change of state. Changes in state can occur continuously
as time evolves or at discrete moments in time. If a change of
state is described by continuous differential equations over time,
then numerical integration procedures must be utilized to change
the state variables. Discrete changes in state variables do not
require integration procedures and are therefore much simpler
since no ordinary differential equations are required. SimObj::State()
is virtual method which packages the differential equations necessary
to update the state of the simulation object. SimObj::Update()
is a virtual method of a simulation object which defines the actions
to be taken to update the simulation object. The SimObj::Update()
method is called by the simulation environment whenever the simulation
object needs to be updated. SimObj::Update() and SimObj::State()
define how an object changes state. SimObj::Update() is
utilized in both discrete and continuous simulation objects, while
SimObj::State() is only utilized by continuous simulation
objects. Continuous simulation objects that have differential
equations use SimObj::Update() in concert with the numerical
integrator and the SimObj::State() method to integrate
and update the simulation object over the object's timestep. The
SimObj::Update() method for a derived simulation object
is called automatically by the simulation environment when an
event is removed from the top of the simulation event list.
Time-flow Synchronization
There are several classes which when working together, make up
a simulation framework useful for simulation modeling and analysis.
The simulation environment class SimEnv handles the clock,
event list, communication, replications, and reports. The Rand
class provides random number generators from eighteen different
distributions. Individual objects participating in the simulation
subclass from the SimObj class. This class defines the
interface between the simulation environment and individual simulation
objects representing various systems components.
The SimEnv simulation environment class. The SimEnv
class provides many of the necessary components for simulation
modeling. SimEnv provides a simulation clock, event list,
simulation object list, and standard reporting capabilities.
SimEnv provides a time flow mechanism or simulation clock
and basic simulation features common to simulation engines for
computer simulation. Time flow mechanisms are critical for both
continuous and event driven simulations. Because different objects
in the system may widely varying time constants, each continuous
object may define its own unique time step. Functionally, the
simulation clock is initialized to the starting time for the simulation
and registered events are placed on the event list. The event
list is a list of future events which is sorted from top to bottom
by ascending time and descending priority. When the simulation
begins, the clock removes the first event on the event list, updates
the current time to the event time, and then updates the event.
When the system is updated in the event routine, new events can
be scheduled and added to the event list. The process repeats
by removing the next object at the top of the event list, resetting
the clock, and then updating the object. The procedure of advancing
the simulation clock continues until no more items are on the
event list, or a simulation stopping condition ends the simulation.
Variable time increment simulations therefore jump through time
from one event to the next event, and the state of the system
is updated during that event.
In summary, a simulation begins at some starting time, usually 0, by initializing the clock, initializing statistics, and initializing the event list. The clock removes the first item on the event list, adjusts the clock time, updates the event, and then repeats. During the updating of the system, new events may be registered. The simulation will continue indefinitely until the event list becomes empty or some pre specified condition stops the simulation. Figure 1 presents a flow chart of the simulation process.
Figure 1. Flow Diagram of the Simulation Process
The SimEnv::RegisterSimObj() method allows the simulator
to add new simulation objects into the simulation environment.
It is necessary to register simulation objects with a SimEnv
if events from a simulation object are to be placed on the event
list. SimEnv::RegisterEvent() allows one to manually schedule
events with the simulation environment. Since events are what
drive the simulation, a simulation will not take place unless
events are registered. Continuous simulation objects can automatically
register an Update() event at each timestep in which it
needs to run. An event on the event list is a structure which
contains; a SimObj, the time when this event shall occur,
a priority with which the event can be scheduled with respect
to other events scheduled at the same point in time, an event
type identifier, and a slot for passing any event-associated data.
Registering events can take place at anytime during a simulation.
Simulation analysts often find the development of simulation logic
complex and confusing. Two features of the SimEnv class
assist the user in the understanding of simulation flow and object
state. SimEnv::GetEventListPtr() provides external access
to the simulation clock's event list. This method is useful during
debugging when a user wants to see if all the proper events are
showing up on the event list. Another feature (tracing) outputs
the state of a simulation object through time. If a simulation
object has tracing enabled, then the SimEnv class will
output the state of this object as it is simulated through time.
SimEnv::ViewEventList() allows the user to view the entire
eventList for this environment. This can be a necessary requirement
during debugging a simulation and it also gives the user a better
understanding of the flow of events through time.
Simulation Object List. The simulation object list (simObjList)
is a data member of the simulation environment. The object itself
is implemented as container that holds simulation objects and
any associated notification filters (described below). The simulation
environment requires that simulation objects be registered with
the simulation environment and that a reference to the simulation
object is placed on the simObjList. The simObjList
is necessary so that the simulation environment will always have
access to the simulation objects that will participate in the
simulation.
Simulation Clock and Event List. The simulation clock
and event list are two components of the simulation environment
that virtually run the simulation. Since events are place on the
event list in increasing time, the simulation clock can update
its current time each time it removes an event from the event
list.
Before events can be registered with the simulation environment,
the simulation objects associated with the event must be registered
with the simulation environment. This is accomplished using the
SimEnv::RegisterSimObj() method. RegisterSimObj()
specifies the simulation object to register and whether an
event will be immediately registered. When continuous simulation
objects are registered simulation environment, corresponding update
events are automatically placed on the event list. Simulation
objects that do not begin simulating at the start of a simulation,
but will have events registered in the future, will be registered
with no automatic event registration. Both stochastic and deterministic
simulation objects are handled consistently.
Communication among objects is a required component of any simulation.
In an object-oriented view, individual simulation objects send
messages to other objects to implement some action or get some
information. In a robust simulation environment, message passing
should be implemented at several different levels to provide either
direct or indirect communication capabilities. In Fig. 2, object
1 sends a message to object 2. Object 2 takes some action in response
to the message being sent, and replies to the sender of the message.
This is a form of object-dependent communication because
object 1 must have some reference to (direct knowledge of) object
2 to send it a message.
Figure 2. Dependent object communication.
In the object-oriented paradigm, programs are created by building
objects and sending messages. A problem with this type of communication
is that it introduces object dependencies. In Fig. 2, Object
1 must have a reference to object 2 to send it a message. Object
dependence can become a problem in simulation modeling. There
are situations where the object sender may not know who the object
receivers are. In Fig. 3, the 'Ice-cream man' simulation object
Broadcast()'s to anyone within hearing range that it has
ice-cream to sell, with no direct knowledge of who might be listening
or how they may respond. Anyone interested will receive the message
and respond accordingly. What is needed in this situation is an
object-independent communication mechanism.
Figure 3. Ice-cream man Broadcast's a message.
The simulation environment provides an AddNotifyFilter()
method as a mechanism for an object to indicate that it is interested
in being notified when a particular broadcast occurs. The number
of simulation objects in the notify filter can be none, one, or
as many as every simulation object that is registered with the
simulation environment. AddNotifyFilter() works with Broadcast()
to provide an additional form of communication between objects.
For example, an object broadcasts that it wants to transfer some
data. Any object that has registered interest in this type of
notification from the broadcasting simulation object may have
an opportunity to receive this data. The order of opportunity
in receiving such a message is linked to the ordering of simulation
objects in the SimEnv::simObjList.
The second step in object independent communication is for individual simulation objects to Broadcast() their messages at appropriate times. When an object Broadcast()'s a message to the system, the system takes the message and routes it to objects that have previously expressed interest in this message from this object. Figure 4 shows the interobject communication process.
Figure 4. Broadcast/Notify Interobject Communication
One form of the notification message allows the transfer of particular
type of object. Objects can broadcast a 'NT_TRANSFER' message
and include in the argument list the object it wants to transfer.
This can be described as the PUSH scenario because we push an
object to another object by using the broadcast message. Objects
that have registered interest in the broadcasting object will
get the opportunity to receive this Broadcast. If the receiver
of the broadcast message wants to accept the transfer of the object,
it replies TRUE to the broadcasting object and takes the sent
object. If the receiver of the message is not interested in the
message, it replies FALSE. When all message receivers respond
FALSE to a message senders request to transfer an object, then
it is the responsibility of the message sender to handle the disposal,
balking, or alternative transfer of this object.
Another way to transfer objects could be called the PULL scenario.
Objects can broadcast a NT_INIT_TRANSFER message when they want
to receive a certain type of object. Objects that have expressed
interest in receiving messages from this object have the opportunity
to respond with a TRUE or FALSE. If an object responds TRUE to
this message, it should place a reference to the object to be
transferred in the extra data field of the notification filter.
This process is very similar to the PUSH scenario except that
objects are pulled from the object that receives the message instead
of being pushed to the object that receives the message. If the
response is TRUE, the sender object knows that the extra data
field of the broadcast message will contain the object which was
pulled from the object that received the message.
The Blackboard. The blackboard is an system object that
can be used by any simulation object as a semi-permanent repository
of messages. Usually, simulation objects will post messages to
the blackboard using the SimEnv::Post() method, and other
simulation objects will read these messages. The process of passing
and reading messages is an additional form of inter-object communication
provided by the simulation environment. The difference between
blackboard communication and the Broadcast() message passing
scheme is in the speed of delivery. Broadcasted messages are sent
"now" (in simulated time) and are then discarded from
the system, whereas blackboard messages remain on the blackboard
indefinitely until explicitly cleared. The amount of time between
a message being posted to the blackboard and being read is unspecified.
Simulation objects communicate with other simulation objects via
the Broadcast-Notify, Broadcast-Notify-Transfer, and blackboard
methodologies. These types of communication do not require that
the sender of a message know the receiver. Broadcasting and posting
to the blackboard are two powerful message passing schemes which
allow for global or object specific communication without introducing
object dependencies.
Simulationists have long wanted to add AI capabilities into simulation
languages. Unfortunately, many of the languages that were useful
for implementing AI techniques were not appropriate for developing
simulation languages. OOP languages provide sufficient abstractive
power to provide consistent representations for both AI and simulation.
Adding intelligence to a simulation model is just a matter of
developing intelligent simulation objects that inherit AI capabilities
(Fig. 5). These intelligent simulation objects (Agents)
typically know how to answer questions and solve problems for
other simulation objects. They may assist other simulation objects
during the running of a simulation model. Simulation objects
can interact with agents in four possible ways. The first three
possibilities, Broadcasting messages, direct invocation of an
object's methods, and posting to a blackboard, were discussed
previously. The fourth approach is when an agent observes the
state of simulation objects from an external viewpoint. These
different approaches to getting agents involved in simulations
allow for a high degree of flexibility in bring AI capabilities
into the simulation environment. If a real-time agent that responds
immediately to problems is necessary, then the Broadcast or direct
method invocation schemes should be used. If it is sufficient
that agent interacts with the simulation at the agents own leisure,
the blackboard or observing agent is a better solution. Figure
5 presents the different ways that agents can participate in a
simulation.
Figure 5. Four different ways that Agents can get
involved in simulations.
Since agents are just another simulation object, albeit with somewhat
different capabilities, they are added to the simulation model
just like any other simulation object. Because agents subclass
from SimObj, they are simply registered with the simulation
environment like any other simulation object. How agents participate
in a simulation is dependent on how quickly the agent is required
to solve some problem. For example, consider the case of a simulation
of a salmon hatchery facility. A salmon hatchery simulation
is running and one of the simulation objects (e.g. fish lot) has
reached a minimum oxygen threshold value for sustaining growth.
We would like to have an agent interact with the simulation
to monitor such problems and take corrective action. Several
approaches are possible, depending on the requirements of the
simulation. If the agents should immediately respond to any problems
(rarely the case in the real world), the fish lot would either
directly talk to the agent about the problem ( object-dependent
communication) or broadcast a message that the agent had previously
be asked to listen for (object-independent communication). Alternatively,
the intent of the simulation may be to explore the effects of
scheduling and time management on the agents availability for
fixing problems. In this case, the agent would be assigned a
time step proportional to its availability for action. At each
of the agent's timesteps, it would receive an Update()
message from the simulation environment. In response to this
message, it would either directly observe each object it was monitoring
(object-dependent communication) or look for problems previously
posted on the blackboard which have not been resolved (object-independent
communication). In other case, it would invoke its knowledgebase
for the particular problem, and take any required steps to correct
the problem.
The power of modern OOP languages, coupled with the capabilities
for extendibility through the development of abstract data types,
make them excellent platforms for implementing simulations. We
have described a system which builds on the power of OOP by providing a consistent semantics and protocol for developing simulation representations, providing for both continuous and discrete representation, supporting various levels in object-dependent and object-independent interobject communication, and integrating tradition simulation representation with AI approaches. The system has been used successfully to build several complex simulations of biological phenomena, and has proven to be an excellent platform for handling to complexity of large-scale model development.
Copyright © 1996 John Bolte