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.

AN OBJECT-ORIENTED, MESSAGE-BASED ENVIRONMENT FOR INTEGRATING CONTINUOUS, EVENT-DRIVEN AND KNOWLEDGE-BASED SIMULATION

J.P. Bolte, J.A. Fisher, D.H. Ernst*

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.


ABSTRACT

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

INTRODUCTION

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 FRAMEWORK

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.

Flow Diagram Graphic

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.

INTEROBJECT COMMUNICATION

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.

Dependent Object Communication Graphic

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.

Broadcast graphic

Figure 3. Ice-cream man Broadcast's a message.

During the development of the simulation environment, it was found that some form of object-independent communication (i.e. a protocol available so that a simulation object could send messages to other objects in the system without requiring a direct reference to them) was needed. The implementation of such a scheme is relatively straightforward. First, individual objects must tell the simulation environment what messages it is interested in being notified of. An object accomplishes this by registering a notification filter with the simulation environment, indicating that it wants to be notified whenever a particular message is broadcast from one or more simulation objects. Second, objects must broadcast messages to the simulation environment when it wants other objects in the system to receive those messages. To transmit a message, an object posts a Broadcast() message to the simulation environment, which will then send Notify() messages to all objects that have previously registered interest in that particular 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.

Notify graphic

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.

INTELLIGENT SIMULATION CAPABILITIES

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.

Agent graphic

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.


CONCLUSION

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.


[Biosystems Analysis Group]
John Bolte
boltej@ccmail.orst.edu

Copyright © 1996 John Bolte