Package org.mitre.sim.api3

Please read this first - Foundation classes and documentation to build process oriented simulations.

See:
          Description

Interface Summary
Filter Interface representing filters used to refine Populations.
Population Interface that represents sets of entities.
SimEventListener Interface used to create SimEvent handlers.
 

Class Summary
Entity Abstract class from which all entities in a simulation are derived.
PeriodicEntity Used to generate SimEvents at a specifed simulation time interval.
SimEvent Event Class used by PeriodicEntity to signal the passage of simulation time.
Simulation Abstract class used to create a simulation.
Trigger Abstract class used to create all simulation developer defined triggers.
WaitResult Structure returned by Entity methods waitForTime, waitForAction, and waitForActionOrTrigger giving the circumstainces of the return.
 

Package org.mitre.sim.api3 Description

Please read this first - Foundation classes and documentation to build process oriented simulations.

Java Logo

Java Meets Simulation


MITRE Sponsored Research to Improve Java Simulation Performance
API Documentation Version 3.0
1 March 2004

Project Site
Developer
Contact
Sim Team

The MITRE Corporation

Contents

Building Simulations Infrastructure Documentation

Introduction

This package contains the foundation classes needed to build process-oriented and scheduled event simulations.

All simulations evolve simulated time to model some aspect of the real world. This package assumes that a simulation consists of entities and their environment that evolve time synchronously, that the entities can interact with each other, and interact with their environment. Each entity executes events or activities that occur at points in simulated time; time evolves for that entity during the gaps between the events.

Constructing a Simulation

In the terminology used in this package, an entity is a persistent simulated being. It controls its own behavior through an agenda and reacts to external events or conditions by action methods. Entities are initialized and registered with a simulation. A simulation is run to execute it and its entities. Simulations are built by defining classes of entities, one Java class for each entity type. Java class Entity is the base class for all simulation entities. An instance of a Simulation subclass represents a group of entities sharing a common definition of simulation time.

Example: Constructing a Simulation

Here we build a simple simulation. Two instances of class MyEntity are running in the same simulation; each instance has a different name but the same behavior. The class diagram for that simulation is:
First Example Class Diagram
The class MySimulation contains the main method that actually runs the simulation. It does this by creating a new instance of itself and invoking the framework method run on that instance. The framework also defines method initialize that may be overridden by classes extending Simulation. The executive executes the initialize method when simulation time is zero and before simulation time begins to advance. MySimulation creates two instances of MyEntity and affiliates them with the simulation using the register framework method. Entities not registered with a simulation are ignored by the executive. This source code is available in tar and zip archive formats.
class MyEntity extends Entity {

  public MyEntity (String name) {
    super(name);
  }
  public void agenda() {
  }
}

public class MySimulation extends Simulation {

  public void initialize() {
    register(new MyEntity("Foo"));
    register(new MyEntity("Bar"));
  }
  public static void main(String[] args) {
    new MySimulation().run();
  }
}

Advancing Time

Simulations manipulate simulation time, normally relying on an executive to orchestrate the execution of a set of entities. The executive uses simulation time to decide the sequence of entity execution. Entities assign values of simulation time to their individual activities. The executive uses this assignment to start, suspend, resume, and terminate entity execution. This manipulation of entity execution allows the executive to create causal relationships in the simulation. So, if an entity assigns event A to simulation time t and another entity assigns event B to time t+1, then event A will occur before event B.

Entities assign simulation time to their computations using inherited framework methods defined on class Entity. This section introduces the waitForTime(x) method that always returns control to the Entity after x units of time have elapsed. Other methods, described in later sections, provide more complex control of simulated time.

Example: Advancing Time

This example extends the simulation constructed in the previous section. Here, class MyEntity displays the string "Hello", "Still here", In this simulation, class MyEntity displays the string "Hello", "Still here", and "Good Bye" at three different points in the evolution of simulation time. As before, two instances of class MyEntity are running in the same simulation. Each instance has a different name but the same behavior. This example illustrates:
  • Implicit assignment of entity computations to specific values of simulation time and
  • Simulation executive action to coordinate the execution of multiple entities.
The first action in the MyEntity agenda displays the string "Hello" using convenience method msg. This action is implicitly assigned a simulation time value of zero. The next statement in the agenda is waitForTime(2.0). The waitForTime invocation tells the executive that the entity wants to place 2.0 units of simulation time between the display of string "Hello" and the display of "Still here." This implicitly assigns a simulation time value of 2.0 to the display of string "Still here." The fourth statement, waitForTime(3.0), places 3.0 units of simulation time between the display of "Still here" and the display of "Good Bye." The display of "Good Bye" is thus assigned a simulation time of 5.0.

The class MySimulation contains the main method that actually runs the simulation. It does this by creating a new instance of itself and invoking the framework method run on that instance. The framework also defines method initialize that may be overridden by classes extending Simulation. The executive executes the initialize method when simulation time is zero and before simulation time begins to advance. MySimulation creates two instances of MyEntity and affiliates them with the simulation using the register framework method. Entities not registered with a simulation are ignored by the executive. This source code is available in tar and zip archive formats.

class MyEntity extends Entity {

  public MyEntity (String name) {
    super(name);
  }
  public void agenda() {
    info("Hello");
    waitForTime(2.0);
    info("Still here");
    waitForTime(3.0);
    info("Good Bye");
  }
}

public class MySimulation extends Simulation {

  public void initialize() {
    register(new MyEntity("Foo"));
    register(new MyEntity("Bar"));
  }
  public static void main(String[] args) {
    new MySimulation().run();
  }
}
In the output of MySimulation (below), the first line contains infrastructure version information displayed by the executive. In the rest of the output, info adds the entity name and current simulation time to the supplied message string. Below is the output of MySimulation. The first line is infrastructure version information displayed by the executive. In the rest of the output, info adds the entity name and current simulation time to the supplied message string.

The output demonstrates the action of the executive to both assign simulation time to entity computations and coordinate the execution of multiple entities. The 2.0 and 3.0 unit waits between the messages caused "Hello" to receive a simulation time of 0.0, "Still here" a value of 2.0, and "Good Bye" a value of 5.0 (2.0 + 3.0). The action of the two instances of MyEntity, Foo and Bar, was interleaved by the executive to maintain proper temporal ordering. Observe that both entities displayed "Hello" before either entity displayed "Still here."

Sim API 3.2, Simple Executive 1.4, Thread Flow 1.0
0.0 Bar Hello
0.0 Foo Hello
2.0 Bar Still here
2.0 Foo Still here
5.0 Bar Good Bye
5.0 Foo Good Bye

Triggers

In the last section, entities used the framework method
waitForTime to create a relationship between their local computations and the simulation-wide advance of time. The role of the executive as coordinator of entity execution was also demonstrated. This section introduces the Trigger framework class and waitForActionOrTrigger framework method.

Users define Triggers so that entities may control the advance of their simulation time based on changes in the overall state of the simulation. Trigger objects are tailored by overriding the condition method; the method returns a boolean value. The simulation executive evaluates the method when needed and returns a true value when it is satisfied. Since the user can't determine when and how often the executive evaluates condition, implementations of the condition method should not have side effects in the simulation. Anonymous subclasses are a convenient way to use Triggers because they are syntactically concise and the code for a specific Trigger is unlikely to be eligible for general reuse.

The inherited framework method waitForActionOrTrigger(Triggers, x) defined in class Entity provides another way for Entities to advance simulation time. This method is similar to the waitForTime method used in section Advancing Time. It tells the executive that the entity wants to change the simulation time associated with the next computation. Unlike waitForTime, however, here the amount of simulation time advanced is conditional. If an entity's agenda contains the statement waitForTime(7.0), simulated time always advances 7.0 units. With waitForActionOrTrigger(Triggers, x), the time associated with the next statement depends on whether any of its Triggers was satisfied. If a Trigger was satisfied, the time of the next statement is the when the trigger was satisfied. If none of the Triggers were satisfied within x units of simulated time, simulated time advances x units.

Example: Triggers

The phenomenon used to illustrate these ideas is the relationship between the weather and a household air conditioner. It has two active entities and a passive support object. Entities model the change in air temperature and the behavior of a thermostat. A support object represents the atmosphere which is affected by the temperature model and sensed by the thermostat model. Here is the class diagram for this simulation:
Trigger Class Diagram
The TemperatureModel entity creates a very simple temperature profile by increasing the temperature from 80 to 90 degrees at a rate of one degree per unit of simulation time. Each time TemperatureModel changes the temperature, it updates the Atmosphere object using the setAirTemperature method and displays the new value using info. This source code is available in tar and zip archive formats.
public class TemperatureModel extends Entity {

  public TemperatureModel() {
    super("Temperature");
  }
  public void agenda() {
    for (double temp = 80; temp <= 90; temp++) {
      info(temp + " degrees");
      Atmosphere.setAirTemperature(temp);
      waitForTime(1.0);
    }
  }
}

public class Atmosphere {

  static double airTemperature = 0.0;

  public static void setAirTemperature(double t) {
    airTemperature = t;
  }
  public static double getAirTemperature() {
    return airTemperature;
  }
}
Class ThermostatModel contains an anonymous subclass of Trigger. The anonymous subclass supplies an implementation of method condition that tests whether the temperature of the atmosphere is greater than or equal to 85 degrees. It returns true if it is and false if it is not. A single instance of this new subclass is created and assigned to the variable tooHot.

The agenda for entity ThermostatModel is simple. It waits until the air temperature goes above 85 degrees and then displays the message "Please turn on the air conditioner." It uses the conditional wait capability of the waitForActionOrTrigger framework method. With waitForActionOrTrigger(tooHot), the time associated with the next statement will be the value of simulation time when tooHot.condition() was first true. That is, the time associated with the message "Please turn on the air conditioner." will be the time that the air temperature first reached 85 degrees.

public class ThermostatModel extends Entity {

  public ThermostatModel() {
    super("Thermostat");
  }
  Trigger tooHot = new Trigger() {
    public boolean condition() {
      return Atmosphere.getAirTemperature() >= 85;
    }
  };
  public void agenda() {
    waitForActionOrTrigger(tooHot);
    info("Please turn on the air conditioner.");
  }
}
The class WeatherSim creates the context for the simulation by registering an instance of TemperatureModel and an instance of ThermostatModel.
public class WeatherSim extends Simulation {

  public void initialize() {
    register(new TemperatureModel());
    register(new ThermostatModel());
  }
  public static void main(String[] args) {
    new WeatherSim().run();
  }
}
The simulation output below shows TemperatureModel increasing the temperature one degree for each unit of simulation time. It ranges from 80 degrees at time 0.0 to 90 degrees at time 10.0. At simulation time 5.0 the temperature reached 85 degrees. This triggered the tooHot Trigger in ThermostatModel. When that happened, the waitForActionOrTrigger(tooHot) statement returned and the message "Please turn on the air conditioner." was displayed. This message was also associated with simulation time 5.0.
Sim API 3.2, Simple Executive 1.4, Thread Flow 1.0
 0.0 Temperature 80 degrees
 1.0 Temperature 81 degrees
 2.0 Temperature 82 degrees
 3.0 Temperature 83 degrees
 4.0 Temperature 84 degrees
 5.0 Temperature 85 degrees
 5.0 Thermostat  Please turn on the air conditioner.
 6.0 Temperature 86 degrees
 7.0 Temperature 87 degrees
 8.0 Temperature 88 degrees
 9.0 Temperature 89 degrees
10.0 Temperature 90 degrees

Action Methods

The previous section showed how entities can react to changes in the environment by using Triggers. This section presents another reactive behavior mechanism that lets entities interact directly. This mechanism supports: Entities offer services to each other by creating action methods. An action method is a method added to a subclass of
Entity whose name is of the form "actionX" where X is one or more characters. Action methods are different from other methods in that their invocation can change the control flow of the entity that offers them. Specifically, receipt of an action method invocation can effect when waitForAction invocations return.

This section also introduces the framework method waitForAction. In section Triggers, a variant of waitForAction was used to wait for a Trigger to be satisfied. Here the entity waits for an certain amount of time to pass. The difference between waitForAction(t) and waitForTime(t) is how they handle action methods. When waitForTime(t) is used, the framework method always returns control after exactly t units of simulation time have passed. During those t units of simulation time, any number of action methods invocations could have been received by the entity. When waitForAction(t) is used, control returns when either t units of simulation time have passed or an action method invocation has been received.

Method waitForAction returns an instance of class WaitResult that contains the circumstances surrounding the return (waitForTime(t) and waitForActionOrTrigger also return an instance of WaitResult). Methods on WaitResult include:

Example: Action Methods

This example program has two directly interacting entities. One entity proactively employs a service provided by a second entity. The second entity is mostly reactive but imposes constraints over the timing of the service being offered. Here is the class diagram for the example program:
Action Method Class Diagram
PhilanthropistModel is a proactive entity that accepts an instance of GoodCauseModel when it is constructed. At two different points in simulation time, PhilanthropistModel interacts with the GoodCauseModel entity to make a donation using the actionDonate action method. This source code is available in tar and zip archive formats.
public class PhilanthropistModel extends Entity {

  GoodCauseModel recipient;

  public PhilanthropistModel(GoodCauseModel gcm) {
    super("Philanthropist");
    recipient = gcm;
  }
  public void agenda() {
    info("Hello");
    waitForTime(1.0);
    recipient.actionDonate("Food");
    waitForTime(1.0);
    recipient.actionDonate("Shelter");
    waitForTime(1.0);
    info("Good Bye");
  }
}
GoodCauseModel accepts donations as long as a donation is made at least every four units of simulation time. If four units of simulation time elapse without a donation, the GoodCauseModel displays the message "No donations received in last 4 hours, going home." and then stops. GoodCauseModel uses the WaitResult instance returned by waitForAction to control its main loop. If actionOccurred returns true, then a donation was received in less than four units of time and GoodCauseModel waits for more donations by remaining in the loop and making another call to waitForAction(4.0). If the return value is false, four units of time have passed without a donation and GoodCauseModel gives up by exiting its main loop, displaying a message, and ending its agenda.
public class GoodCauseModel extends Entity {

  public GoodCauseModel() {
    super("GoodCause");
  }
  public void actionDonate(String donation) {
    info(donation + " received");
  }
  public void agenda() {
    while (waitForAction(4.0).actionOccurred()) {}
    info("No donations received in last 4 hours, going home.");
  }
}
The CharitySim class creates the context for the simulation. It makes a single instance of GoodCauseModel and assigns it to the variable recipient. This was done so that the new instance can be both passed to the framework method register and provided to the PhilanthropistModel constructor.
public class CharitySim extends Simulation {

  public void initialize() {
    GoodCauseModel recipient = new GoodCauseModel();
    register(recipient);
    register(new PhilanthropistModel(recipient));
  }
  public static void main(String[] args) {
    new CharitySim().run();
  }
}
The simulation output shows the interaction between the Philanthropist and the GoodCause. Philanthropist begins with the message "Hello," waits 1.0 units of time, and then makes a food donation. The corresponding output shows the Philanthropist message "Hello" at time 0.0 and a message from GoodCause at time 1.0 acknowledging receipt of the food. The invocation of the action method actionDonate(String donation) caused the waitForAction(4.0) invocation to return with a value of true after a wait of only 1.0 units of time. Since it received a positive return value, main loop in GoodCause makes a second call to waitForAction(4.0).

Philanthropist waits another 1.0 units of time and donates shelter. GoodCause acknowledges the donation of shelter with a message at time 2.0. Finally, Philanthropist waits 1.0 unit of time, says "Good Bye," and ends. As this was the third time Philanthropist waited for 1.0 units of time, the "Good Bye" message is associated with simulation time 3.0. At simulation time 3.0, 1.0 units of time have elapsed since the last donation. GoodCause is waiting in a call to waitForAction(4.0) and has 3.0 units of time left until waitForAction returns with a value of false. A return value of false lets GoodCause exit its main loop and then display its final message. The time associated with the "No donations received in last 4 hours, going home." message is 6.0, which is 4.0 units of time after the last donation at simulation time 2.0.

Sim API 3.2, Simple Executive 1.4, Thread Flow 1.0
0.0 Philanthropist Hello
1.0 GoodCause Food received
2.0 GoodCause Shelter received
3.0 Philanthropist Good Bye
6.0 GoodCause No donations received in last 4 hours, going home.

Explicitly Scheduled Events

So far, an interacting process view of simulation has been taken in the framework description. In this section, a scheduled event view is presented. This includes: The interacting process view considers each entity instance to have its own thread of control. The thread of control is confined to the
agenda and any code called by the agenda. An entity agenda manages proactive behavior with invocation of time management services and other entity action methods while trigger and action method reception handles the reactive behavior. All entity control threads can be thought of as running in parallel with the simulation executive moderating their competing requests to advance simulation time.

The scheduled event view of simulation sees the world as a collection of events rather than entities. An events is a block of code executed at a single point in simulation time. A simulation contains a dynamic collection of scheduled events, where scheduled event is the pairing of an event (a block of code) with a value of simulation time telling when the code should be executed. The executive executes scheduled events in order of the time values associated with each event. Once executed, the scheduled event is removed from the collection.

Scheduled events are added to the collection by events themselves. Events create new scheduled events by invoking a framework service that associates the name of an event with an amount of simulation time that must pass before the event is executed. In this framework, an event is any Entity method other that the agenda method itself. Events are scheduled using the schedule(String methodName, double eventDelay) framework service. This service can be used to schedule events from any Entity method including the agenda.

A scheduled event simulation is simply the process where events execute and create other scheduled events. To start this process, a mechanism is needed to identify and execute the initial event; the agenda method serves this purpose. Because the agenda method is invoked at simulation time zero by the executive, it is the right place to schedule an initial event or, more likely, a chain of events. Once that initial event is scheduled, the agenda can complete while the chain of events goes on.

The event scheduling capability introduced in this section works in harmony with the time advancement services used by interacting process-oriented simulations. Entities can simultaneously control their agendas with time advancement services and schedule chains of events as long as they observe the following:

Methods used for scheduled events can receive arguments. Arguments to a scheduled event are stored by the framework when the event is scheduled and then passed to the event method at the future invocation time. Arguments are passed to the framework as an array of Objects using the schedule(String methodName, double eventDelay, Object[] arguments) framework service. When an event is scheduled using this service, the framework determines the class of each supplied argument. It uses the name of the method itself plus the list of argument class names to find the appropriate method for the event. If a method of the approporate signature cannot be found, the expection java.lang.IllegalArgumentException is thrown.

Example: Scheduled Events

The SchedEventSim simulation below creates a single instance of class SchedEntity. That entity instance, in turn, creates a chain of scheduled events. The scheduled events are method eventOne at time 2.0 and then method eventTwo at time 5.0. The source code for this example is available in tar and zip archive formats.
public class SchedEventSim extends Simulation {
  public void initialize() {
    register(new SchedEntity("SchedEntity"));
  }
  public static void main(String[] args) {
    new SchedEventSim().run();
  }
}

public class SchedEntity extends Entity {
    public SchedEntity(String name) {
      super(name);
    }
    public void eventOne(){
      info("Event One");
      schedule("eventTwo", 3.0);
    }
    public void eventTwo(){
      info("Event Two");
    }
    public void agenda() {
      info("Hello");
      schedule("eventOne", 2.0);
      info("Good Bye");
    }
}
The executive invokes the agenda method at simulation time zero. Because no time advancement services are invoked, the agenda displays the message "Hello", schedules an event, and displays the message "Good Bye" all at simulation time zero. The eventOne method displays the message "Event One" at time 2.0 because it was scheduled at time zero with a 2.0 time unit delay. The eventTwo method displays the message "Event Two" at time 5.0 because it was scheduled by eventOne at time 2.0 with a requested delay of 3.0. Note that events continued to execute even though the agenda finished execution at time zero.
Sim API 4.0, Reference Executive 3.0, Thread Flow 1.6
0.0 SchedEntity Hello
0.0 SchedEntity Good Bye
2.0 SchedEntity Event One
5.0 SchedEntity Event Two

Example: Mixed Process and Scheduled Events

The MixedModeSim simulation below creates a single instance of class MixedModeEntity and informs the executive that the simulation should be stopped after 9.0 units of simulation time have passed. That entity instance, in turn, creates a chain of scheduled events. Unlike the previous example, the agenda remains active and performs operations in parallel with the scheduled event execution. The source code for this example is available in tar and zip archive formats.
public class MixedModeSim extends Simulation {
  public void initialize() {
    register(new MixedModeEntity("MixedModeEntity"));
    setTimeLast(9.0);
  }
  public static void main(String[] args) {
    new MixedModeSim().run();
  }
}

public class MixedModeEntity extends Entity {
    public MixedModeEntity(String name) {
      super(name);
    }
    public void eventOne(){
      info("Event One");
      schedule("eventTwo", 3.0, new Object[] {"Fa "});
    }
    public void eventTwo(String message){
      info("Event Two " + message);
      schedule("eventTwo", 1.5, new Object[] {message + "La "});
    }
    public void agenda() {
      info("Hello");
      waitForTime(1.0);
      schedule("eventOne", 2.0);
      waitForTime(4.0);
      info("Good Bye");
    }
}
The agenda begins execution at time zero and displays the message "Hello." It waits 1.0 units of time then schedules method eventOne with a 2.0 time unit delay. This means eventOne will be executed at time 3.0. It then waits for 4.0 units of time. During this delay, time 3.0 arrives and event eventOne displays the message "Event One." Event eventOne also schedules eventTwo for execution with a 3.0 unit delay. This gives eventTwo a scheduled execution time of 6.0. Eventually 4.0 units of time pass for the agenda and it displays the message "Good Bye" at simulation time 5.0. When time 6.0 arrives, eventTwo is executed. It displays message "Event Two Fa" at time 6.0 and reschedules itself to run again in 1.5 units of time. This is an infinite loop and only ends because MixedModeSim set the last value of time to 9.0.
Sim API 4.0, Reference Executive 3.0, Thread Flow 1.6
0.0 MixedModeEntity Hello 
3.0 MixedModeEntity Event One 
5.0 MixedModeEntity Good Bye 
6.0 MixedModeEntity Event Two Fa  
7.5 MixedModeEntity Event Two Fa La  
9.0 MixedModeEntity Event Two Fa La La  

Populations and Filters

Simulation entities often respond to changes in the state of other entities. These changes in state may include the registration of new entities and the completion of others, and their consequent exit from the simulation. Often it is convenient to characterize the state of interest to one entity in terms of membership of other entities in a set. The set might be characterized, for instance, as "all the currently active entities of class WidgetModel" or "all active WidgetModel entities that have been active for at least time t." The Population and Filter interfaces in this package exist to allow entities to define interesting entity sets, examine those sets, and wait for changes in those sets.

Populations

The interface
Population represents a set of entities. The set is dynamic in the sense that its membership varies as simulation time progresses. The executive maintains the membership of each Population as the state of the overall simulation changes. An entity can examine the state of a Population object, but can not change it directly.

The simulation developer does not implement the Population interface. Entity code obtains Population objects from the executive, or by modifying existing Population objects. All Population objects are managed by the executive on the entity's behalf.

Entity code obtains an initial Population object by invoking the method createPopulation. This method takes a Java Class, which must be the Entity class or a subclass. The call returns a Population containing all the currently active instances of the specified class and any subclass. If no instances of the class (or subclass) have been registered at the time of the call, the size of Population will be zero. As instances are registered the size of the Population will increase.

Filters

By using createPopulation an entity can obtain Population objects defined by class. By applying Filter objects an entity can obtain Populations whose membership depends on entity state. Implementations of the Filter interface are created by a simulation developer. The interface defines one method, passesFilter, which represents a predicate to apply to each member of a Population. The predicate's computation can involve anything the developer likes, but typically will involve the state of Population members.

Filters are used to define Populations. The developer creates an implementation of Filter, and passes an instance in the applyFilter method on an existing Population. The result is a new Population whose members are all the members of the original Population for which passesFilter returns true. As with all Populations, the membership is dynamic. If no members pass the filter at the time the filtered Population is created, the size of the Population will be zero. The membership may grow:

The latter might occur if, for instance, the entities have a location, the filter criterion is distance of an entity from a point, and the point moves in simulation time.

Filters can be composed in the mathematical sense. A Population obtained from applyFilter could be the object of another call to applyFilter with a second Filter instance, from the same or another Filter class. Thus, given

Population myWidgets = createPopulation(WidgetModel.class);
Population blueWidgets = myWidgets.applyFilter(blueFilter);
Population hotBlueWidgets = blueWidgets.applyFilter(hotFilter);
the members of hotBlueWidgets are all active instances of WidgetModel (or a subclass) which pass both blueFilter and hotFilter.

Filters can be used to exclude entities rather than include them. The method rejectFilter returns a new Population whose members are those of the original Population for which passesFilter of the supplied filter returns false. (This defines a "set difference" operation.)

Populations can be created whose membership is the union of two Populations. If the method join is invoked on Population p1, passing Population p2 as the parameter, the resulting Population will contain all the members of p1 and p2. (This defines a "set union" operation.)

Example: Chaining Populations and Filters

The object diagram below illustrates definition of Populations in various ways. Going down the left side of the tree, the Population myWidgets is formed by invoking createPopulation(WidgetModel.class) on the Simulation. It contains all active instances of WidgetModel and its subclasses. The Population filter1pop is obtained by applying filter1 to myWidgets. It contains all members of myWidgets for which passesFilter of filter1 returns true. The Population filtered1and2Pop contains all members of myWidgets that pass both filter1 and filter2.

Going down the right side of the tree, myOthers contains all active instances of OtherModel and its subclasses. Invoking myOthers.join(myWidgets) yields combinedInstances, whose members are all those of myOthers and myWidgets. The members of combinedButNot1 are the members of combinedInstances that do not pass filter1.

Populations object diagram

Populations resemble Java Sets. They are collections of objects that contain no duplicates. There are Set-like operations that can be applied to examine their contents, specifically contains, isEmpty, iterator, size, and toArray.

Generally speaking, the membership of a Population will change only when simulation time can change. However, if an entity examines a Population, then registers Entities that could belong to the Population, and examines the Population again, it may find that the membership has changed.

An Entity should not yield to the executive (e.g., by calling waitForTime while holding a Population Iterator. The behavior of the Iterator is undefined in such a circumstance.

Triggers on Populations

An Entity uses Triggers to tie its local computations to the overall state of the simulation. As significant expressions of simulation state, Populations are a natural source of useful triggers. The framework offers a number of convenient ways to construct a Trigger from a Population. The Population methods becomesEmpty, excludes, includes, sizeBelow, and sizeExceeds return Triggers that can be used in the various forms of waitForActionOrTrigger. The intended idiom resembles this:
WaitResult wr = waitForActionOrTrigger(myPopulation.sizeExceeds(0), backstopTime);

Example: An Elk Herd

This example illustrates the use of Populations, Filters, Events, and Actions. The simulation contains models of a number of elk in a heard. Each elk is assigned a life span when it is created. Each elk lives for that amount of simulation time, unless it is first killed. The simulation also models a herd manager whose purpose is to keep the elk population within predetermined bounds. When the manager detects that the total elk population has dropped below the minimum, it will create more elk. When the manager detects that the population of adult elk has exceeded the maximum, it kills a number of adults. The herd manager also simulates the natural reproduction by periodically creating a new elk.

Each elk is represented by an instance of ElkModel. The herd manager is represented by one instance of HerdManagerModel. The overall simulation is an instance of PopulationSim. This source code is available in tar and zip archive formats.

The code for the PopulationSim appears below. The HerdManagerModel registers all the ElkModels in the simulation. A last simulation time is set of 40.0.
public class PopulationSim extends Simulation {
  public void initialize() {
    register (new HerdManagerModel());
    setTimeLast(40.0);
  }

  public static void main(String[] args) {
    new PopulationSim().run();
  }
}
The code for the ElkModel appears below. The constructor is called by the HerdManagerModel, which supplies a name with a serial number, and a lifespan. The ElkModel agenda completes when simulation time has advanced by the lifespan, unless actionKill is called first. That would cause the waitForAction invocation to return, ending the instance.
public class ElkModel extends Entity {
  private double lifespan;
  private double birthday;

  public ElkModel(String name, double lifespan) {
    super(name);
    this.lifespan = lifespan;
  }

  public void agenda() {
    info("Born with lifespan " + lifespan);
    birthday = getTimeNow();
    waitForAction(lifespan);
    info("Died at age " + getAge());
  }

  public double getAge() {
    return getTimeNow() - birthday;
  }

  public void actionKill() {
    info("Killed");
  }
}
The critical part of the code for HerdManagerModel appears below, with line numbers. Recall that the herd manager has three tasks: replenish the herd when its numbers drop too low; cull the herd when adults grow too numerous; and periodically add an elk as part of normal growth. For the first two tasks the manager uses Populations created in lines 2 and 3. Line 2 produces a Population that contains all active instances of ElkModel. Line 3 produces a Population of adults by applying a Filter. The Filter definition, implemented as the inner class AdultFilter, appears in lines 38 to 41. Note that at the time the Populations are obtained they are empty, because no ElkModel instances have been registered. They gain members when the initial population is created and registered on line 4.

The herd manager uses the Populations to detect the conditions that the total herd has become too small or that the population of adults has grown too large. The manager uses the Populations by obtaining Triggers on them in lines 5 and 6. (We will come back to line 7 shortly.) The endless loop of lines 8 to 16 awaits the trigger conditions. Both conditions are waited for in line 9. If the herdTooSmall Trigger caused the wait to return (line 10), the herd is replenished (lines 25 to 28). If the herdTooBig Trigger caused the wait to return (line 13), the herd is culled (lines 30 to 36). Note, in the cullHerd method, the use of an Iterator on adultsInHerd Population to examine the membership of adultsInHerd. Note also that invoking the action method on ElkModels causes their agenda to terminate when they are activated, but the membership of adultsInHerd does not change until after the HerdModelManager yields to the executive.

The normal function of the herd manager consists of creating and registering an ElkModel periodically. HerdManagerModel accomplishes this using scheduled events. The event to be scheduled is the method eventNormalBirth in lines 19 to 22. The initial event is scheduled on line 7. Each time the event method executes it schedules its own next event on line 22.

 1  public void agenda() {
 2    elkHerd = createPopulation(ElkModel.class);
 3    adultsInHerd = elkHerd.applyFilter(new AdultFilter());
 4    for (int s = 0; s < initialPopulationSize; s++) register(createElk());
 5    Trigger herdTooSmall = elkHerd.sizeBelow(minimumPopulation);
 6    Trigger herdTooBig = adultsInHerd.sizeExceeds(maximumPopulation);
 7    schedule("eventNormalBirth", birthInterval);
 8    while(true) {
 9      WaitResult wr = waitForActionOrTrigger(herdTooSmall, herdTooBig);
10      if (wr.triggerOccurred() && wr.trigger() == herdTooSmall) {
11        replenishHerd(minimumPopulation);
12      }
13      else if (wr.triggerOccurred() && wr.trigger() == herdTooBig) {
14        cullHerd(maximumPopulation / 2);
15      }
16    }
17  }
18
19  public void eventNormalBirth() {
20    info("Normal birth");
21    register(createElk());
22    schedule("eventNormalBirth", birthInterval);
23  }
24
25  private void replenishHerd(int number) {
26    info("Replenishing herd by " + number);
27    for (int s = 0; s < number; s++)  register(createElk());
28  }
29
30  private void cullHerd(int number) {
31    info("Culling herd by " + number);
32    Iterator iter = adultsInHerd.iterator();
33    for (int i = 0; i < number && iter.hasNext(); i++) {
34      ((ElkModel)iter.next()).actionKill();
35    }
36  }
37
38  public class AdultFilter implements Filter {
39    public boolean passesFilter(Entity e) {
40      return ((ElkModel)e).getAge() >= adultAge;
41    }
42  }

Simulation Control

There are two domains of simulation control: internal and external. These domains are seen from the perspective of the simulation executive thread. Internal controls are available to the executive thread and the simulation entities. External controls are available to threads outside the simulation.

Threads

It is important to understand where the simulation executive get its thread because the thread source can limit accessibility to external controls. A simulation executive gets its thread when an instance of
Simulation is created. This thread can be a new one or the thread that created the Simulation instance. A new thread is created for the executive by invoking the start method on the Simulation instance. The thread that created the Simulation instance is used by the executive when the run method is invoked. The start method is inherited by Simulation from class java.lang.Thread. Method run is specified in interface java.lang.Runnable. Reuse of the creating thread is illustrated in the code fragment below:
1  x = 13;
2  new MySimulation().run();
3  x = 27;
In line one above, x is set to a value of 13. In line two, a new instance of MySimulation is created and the run method invoked. The thread of control goes into the run invocation and does not return until the simulation is finished. That is, the run invocation blocks. All aspects of the simulation see that value of x as 13. In line three, the simulation is complete and the value of x is set to 27. No part of the simulation ever saw x equal 27. In this situation it is not possible to use external controls.

If external controls are needed, the simulation executive must run in an separate thread. This is illustrated in the code segment below:

1  x = 13;
2  new MySimulation().start();
3  x = 27;
In line one above, x is set to a value of 13. In line two, a new instance of MySimulation is created and the start method invoked. This creates a new thread for the simulation executive and allows it to proceed in parallel. Line three sets the value of x to 27. This may happen before the simulation is finished. Entities in the simulation may see x have a value of 13 and then 27 depending how the new thread of the simulation and the thread that created it are interleaved. In this situation it is possible to use external controls.

Ending a Simulation

The simulation developer decides when a simulation starts, the maximum pace at which it will run, and the conditions by which it will stop. A simulation will come to an end for one of the following reasons: A simulation can be stopped by any entity using the framework method
stopSimulation. This method informs the executive that the simulation should end as soon as is practical. When a simulation is stopped in this way, the notification services described in section Completion Notification are invoked.

The executive contains a value of simulation time known as the "last value of time." By default, this value is set to Double.POSITIVE_INFINITY. Framework method setTimeLast is available on class Simulation to adjust the last value of simulation time. When the executive detects that an entity is attempting to assign a value of simulation time greater than the last value of time, it will stop the simulation as soon as is practical. When a simulation is stopped because it reached the last value of simulation time, the notification services described in section Completion Notification are invoked.

Completion Notification

As described in section Simulation Control above, an entity or simulation can end in several ways Given this uncertainty, framework methods are available for developers to override with code that is executed when either a simulation or entity ends. These methods are:

Example: Completion notification

This simple example demonstrates notification of both Entity and Simulation completion handling. Class InterruptedEntity overrides framework method entityComplete with a single line method that displays the message "Entity complete." The agenda attempts to display message "Hello" at time 0.0, "Still here" at time 2.0, and "Good Bye" at time 5.0. This source code is available in tar and zip archive formats.
public class InterruptedEntity extends Entity {

  public InterruptedEntity() {
    super("InterruptedEntity");
  }
  public void entityComplete() {
    info("Entity complete");
  }
  public void agenda() {
    info("Hello");
    waitForTime(2.0);
    info("Still here");
    waitForTime(3.0);
    info("Good Bye");
  }
}
Class FinalSimulation overrides the framework method simulationComplete with a single line method that displays the message "Simulation complete." It also uses framework method setTimeLast to set the last value of simulation time to 1.0.
public class FinalSimulation extends Simulation {

  public void initialize() {
    register(new InterruptedEntity());
    setTimeLast(1.0);
  }

  public void simulationComplete() {
    info("Simulation complete");
  }

  public static void main(String[] args) {
    new FinalSimulation().run();
  }

}
The output of the simulation entity InterruptedEntity contains only two of the three messages in its agenda. The simulation executive stopped the simulation at time 2.0 and invoked the completion method for the entity, displaying "Entity complete" and then the completion method for the simulation, displaying "Simulation complete." The last value of simulation time was 2.0 and not the value 1.0 as requested by the framework invocation setTimeLast(1.0). The simulation executive is only required to stop the simulation as soon as is practical after the requested value of 1.0.
Sim API 3.2, Simple Executive 1.4, Thread Flow 1.0
0.0 InterruptedEntity Hello
2.0 InterruptedEntity Still here
2.0 InterruptedEntity Entity complete
2.0 FinalSimulation Simulation complete

Pacing a Simulation

Simulations can run as fast as the supporting platform will permit or can be paced by the progress of real time. By default, the simulation executive will execute a simulation as fast as possible. That is, the simulation will complete in the minimum amount of real time. The
setPace method on class Simulation gives the developer control over the maximum rate at which the simulation will evolve. This is done by specifying the minimum number of real milliseconds that must pass each time the simulation evolves one unit of simulation time.

It may take more than the specified number of milliseconds to compute a unit of simulation time. That is, the supporting platform may not have sufficient power to achieve the desired rate. In this case, the use of setPace has no effect. The simulation can be reset to run as fast as possible by invoking the method with a zero argument (setPace(0))

Example: Setting the Simulation Pace

This example program demonstrates the use of framework method setPace to control the maximum rate at which the simulation evolves. Class RocketModel simulates the countdown of a rocket as it prepares for launch. It does this by displaying a message heard many time at Cape Canaveral: "T minus i seconds" where i is the number of seconds until launch. The count goes down from 10 seconds to one second. After that the "Blast off!" message is displayed. The waitForTime framework method is used to place a delay of one unit of simulation time between counts. This source code is available in tar and zip archive formats.
public class RocketModel extends Entity {

  public RocketModel() {
    super("Rocket");
  }

  public void agenda() {
    for (int count = 10; count > 0; count--) {
      info("T minus " + count + " seconds");
      waitForTime(1.0);
    }
    info("Blast off!");
  }

}
The LaunchSim class creates one instance of entity RocketModel and registers it with the simulation executive. Using setPace it then informs the infrastructure that a minimum of 1000 milliseconds (one second) of real time must pass for each unit of simulation time.
public class LaunchSim extends Simulation {

  public void initialize() {
    register(new RocketModel());
    setPace(1000);
  }

  public static void main(String[] args) {
    new LaunchSim().run();
  }

}
The output of the Launch Simulation shows how the infrastructure creates the desired pace of simulation. The output does not appear immediately. There is a one second delay between each displayed line of the count down. That is, it will take about 10 seconds, as measured by your wrist watch, for all the output below to appear.
Sim API 3.5, Reference Executive 2.2, Thread Flow 1.3
 0.0 Rocket T minus 10 seconds
 1.0 Rocket T minus  9 seconds
 2.0 Rocket T minus  8 seconds
 3.0 Rocket T minus  7 seconds
 4.0 Rocket T minus  6 seconds
 5.0 Rocket T minus  5 seconds
 6.0 Rocket T minus  4 seconds
 7.0 Rocket T minus  3 seconds
 8.0 Rocket T minus  2 seconds
 9.0 Rocket T minus  1 seconds
10.0 Rocket Blast off!

Pause and Resume

External controls are actions taken by a thread outside the
Simulation instance. All external controls are methods on instances of Simulation. They are:

Example: Simulation Pause and Resume

As in LaunchSim above, DelayedLaunchSim creates and registers a single instance of RocketModel. As before, it also uses setPace to create a rate where one unit of simulation time corresponds to one second of real time. The method delay(long) is a convenience to hide the clutter of an exception handler.

The main method creates a instance of DelayedLaunchSim and gives it a new thread of control by invoking method start. After line 16 is executed there are two threads running in parallel, one in the main method and one in the simulation executive. Line 17 puts the main thread to sleep for 5 seconds. While the main thread is sleeping, the simulation continues to run. The main thread wakes back up in line 18 and uses the external control method pauseSimulation to stop the advance of the simulation. When line 19 is executed, the main thread will be put back to sleep for 5 more seconds. The simulation executive thread continues in the paused state initiated in line 18. Finally, after sleeping for 5 seconds, the main thread wakes up and resumes the executive thread using framework method resumeSimulation. This source code is available in tar and zip archive formats.

 1  public class DelayedLaunchSim extends Simulation {
 2
 3    public void initialize() {
 4      register(new RocketModel());
 5      setPace(1000);
 6    }
 7
 8    public void delay(long delay) {
 9      try {
10        Thread.sleep(delay);
11      } catch (Exception e) {}
12    }
13
14    public static void main(String[] args) {
15      DelayedLaunchSim dls = new DelayedLaunchSim();
16      dls.start();
17      dls.delay(5000);
18      dls.pauseSimulation();
19      dls.delay(5000);
20      dls.resumeSimulation();
21    }
22
23  }
The output of the Delayed Launch Simulation shows how the infrastructure creates the desired pace of simulation and the effect of external controls. While the main thread slept in line 17, the simulation conducted the countdown from 10 down to 5. Each count taking one second of real time. After the countdown reached 5, the main thread woke up and paused the simulation. This results in a 5 second delay after which the countdown resumed at a normal pace. It takes about 15 seconds, as measured by your wrist watch, for all the output below to appear.
Sim API 3.5, Reference Executive 2.2, Thread Flow 1.3
 0.0 Rocket T minus 10 seconds
 1.0 Rocket T minus  9 seconds
 2.0 Rocket T minus  8 seconds
 3.0 Rocket T minus  7 seconds
 4.0 Rocket T minus  6 seconds
 5.0 Rocket T minus  5 seconds
Five second delay
 6.0 Rocket T minus  4 seconds
 7.0 Rocket T minus  3 seconds
 8.0 Rocket T minus  2 seconds
 9.0 Rocket T minus  1 seconds
10.0 Rocket Blast off!

Event Listeners

The framework employs the event-listener pattern to support communications between entities and external processes such as graphical user interfaces. Developers can create listeners by implementing interface
SimEventListener. These listeners wait for the arrival of SimEvent events. Currently, PeriodicEntity is the only source of SimEvents in the framework.

PeriodicEntity is a framework-provided subclass of Entity. Instances of PeriodicEntity produce a stream of SimEvents for their listeners at regular intervals of simulation time. The amount of simulation time between events is specified when the instance of PeriodicEntity is created. Listeners are affiliated with an instance of PeriodicEntity by the addSimEventListener method and removed with the removeSimEventListener method.

Example: Event Listeners

Class ListenerSim uses PeriodicEntity to create periodic events for a listener. The Listener class extends SimEventListener to become a listener for SimEvent events. The listener simply displays the simulation time of the SimEvent events as they are received.

During initialization, a single instance of PeriodicEntity is created and registered with the executive. The value 1.5 supplied to the PeriodicEntity constructor means that this instance will create events every 1.5 units of simulation time for all its registered listeners. Next, an instance of Listener is created and added to the list of listeners waiting for SimEvents to arrive. In the last step of initialization, framework method setTimeLast is used to set the last value of simulation time to 6.0. Instances of PeriodicEntity do not stop on their own so if this step were omitted, the simulation would not terminate. This source code is available in tar and zip archive formats.

public class ListenerSim extends Simulation {

  public class Listener implements SimEventListener {
    public void simEventOccurred(SimEvent se) {
      info("Event received at " + se.getTime());
    }
  }

  public void initialize() {
    PeriodicEntity generator = new PeriodicEntity(1.5);
    generator.addSimEventListener(new Listener());
    register(generator);
    setTimeLast(6.0);
  }

  public static void main(String[] args) {
    new ListenerSim().run();
  }

}
The output of the Listener Simulation shows that events were produced every 1.5 unit of simulation time. The event stream concluded when the last value of simulation time, 6.0, was reached.
Sim API 3.5, Reference Executive 2.2, Thread Flow 1.3
1.5 ListenerSim Event received at 1.5
3.0 ListenerSim Event received at 3.0
4.5 ListenerSim Event received at 4.5
6.0 ListenerSim Event received at 6.0

Graphical User Interface

A graphical user interface is provided that offers access to the external simulation control services. It is a new and evolving capability that will be better documented in future releases of the framework. The GUI display is turned on and off with a new
Simulation method setVisible(boolean). The GUI is turn off by default. A handy way to use the GUI is to turn it on when a Simulation instance is created. This is illustrated below:
public class MySimulation extends Simulation {

  public void initialize() {
    ...
  }

  public static void main(String[] args) {
    new MySimulation().setVisible(true);
  }
}

Handling Exceptions

It is possible that the
agenda or entityComplete methods will throw an exception. The simulation executive will catch any exception thrown by the these method and supply that exception to the exceptionHandler method below:
public void exceptionHandler(Exception e) {
  e.printStackTrace();
}
If this happens, no further attempt will be made to run agenda or entityComplete. If an exception handler other than the one shown above is needed, it can be overridden. The executive will catch and discard any exceptions thrown by exceptionHandler itself.

Compiling and Executing Simulations

This framework supplements the Java platform to make simulation development simpler and more efficient. It places no fundamental restrictions on how the Java language is used or what packages may be employed in a simulation. The framework itself, however, does make use of an extension to the Java language called AspectJ. A knowledge of AspectJ is not required to use the simulation framework and no framework class contains AspectJ code. The only place AspectJ appears is during compilation. Simulations must be compiled with the AspectJ compiler, ajc, rather than javac. The AspectJ compiler, ajc, is a super set of javac and should present do differences to a Java developer. It produces the same byte code as javac and is thus completely compatible with the overall Java platform. To begin using the simulation framework, perform the following tasks:

Installing AsceptJ

Download the AspectJ installation file, aspectj-1.1.1.jar, from the developer
home page. Execute this file from any directory using the following command:
java -jar aspectj-1.1.1.jar
The installer will ask for the location of your existing Java Home directory and will then ask you where to place the aspectj1.1 directory. The path you choose will be referenced in this document as the ASPECTJ_HOME. To gain access to the ajc compiler, add the following directory to your command path:
ASPECTJ_HOME/bin

Compiling Simulations

Download the simulation framework jar file, sim.jar, from the developer home page. Place the sim.jar file in a convenient directory. In this documentation, that directory will be known as SIM_HOME.

Make the following invocation to compile program MySim.java using the simulation framework:

ajc -aspectpath SIM_HOME/sim.jar MySim.java

Executing Simulations

Programs compiled with ajc are executed by the same Java virtual machine as programs compiled with javac. To execute an instance of class Simulation, both sim.jar and aspectjrt.jar must be in the class path. The following command will execute MySim.class if executed from the directory that contains it.
java -cp .:SIM_HOME/sim.jar:ASPECTJ_HOME/lib/aspectjrt.jar MySim
If the sim.jar, aspectjrt.jar, and the local directory are included in the definition of the CLASSPATH environment variable, the invocation can be shortened to:
java MySim

Compiling with AspectJ in Ant

The following ant target is provided as an example of how ajc can be used in an ant script rather than javac.
  <target name="build">
    <taskdef name="ajc"
             classname="org.aspectj.tools.ant.taskdefs.AjcTask">
      <classpath>
        <fileset dir="lib">
          <include name="aspectjtools.jar"/>
	</fileset>
      </classpath>
    </taskdef>
    <ajc srcdir="src"
         destdir="classes"
         aspectpath="sim.jar">
       <classpath>
         <fileset dir="lib">
           <include name="yourJarFile.jar"/>
         </fileset>
      </classpath>
    </ajc>
  </target>

Using AspectJ in JBuilder

There is a plug-in for JBuilder that incorporates AspectJ. It is available from
aspectj4jbuildr.sourceforge.net and the project download page. The current plug-in is for AspectJ 1.1.1 and JBuilder 9. Installation steps are:

Property File Parameters

The simulation infrastructure obtains runtime parameters from a Java properties file. The name of the properties file is sim.properties and is located in CVS at CVSRoot/sim/resources/sim.properties. To be accessible at runtime, a resources directory containing sim.properties must exist at the root of the class file structure. This occurs two ways: The Ant script CVSRoot/sim/build.xml moves the resources directory to these two places. Ant target build copies the resources directory to the CVSRoot/sim/classes directory. Ant target jar copies the resources directory into the sim.jar archive.

The property file contents are described below:

Property Name Description Default Value
org.mitre.sim.Flow.class Process lifecycle services class name. org.mitre.sim.api3.flow.threads.ThreadFlow
org.mitre.sim.Executive.class Simulation executive class name. org.mitre.sim.api3.exec.reference.ReferenceExecutive
org.mitre.sim.GUI.components The components to show on the Simulation's GUI. control
org.mitre.sim.GUI.component.<component name>.class The java class to load for a particular graphical component [control] = org.mitre.sim.api3.gui.SimControl
org.mitre.sim.GUI.component.<component name>.* Properties specific to a particular GUI component N/A
org.mitre.sim.GUI.component.control.timeFormatter.class The time format class to use when displaying the simulation time java.text.DecimalFormat
org.mitre.sim.GUI.component.control.timeFormatter.format Format used to convert the simulation time into a displayable string #########0.0######
org.mitre.sim.Simulation.level The level that the Simulation logger should log messages at INFO
org.mitre.sim.Executive.level The level that the Executive logger should log messages at OFF
org.mitre.sim.Flow.level The level that the Flow logger should log messages at OFF
org.mitre.sim.LogicalProcess.level The level that the Logical Process [entities] logger should log messages at INFO
org.mitre.sim.<Logger Name>.handlers The default set of handlers to use for a given logger [Simulation, Executive, Flow, LogicalProcess] = console
org.mitre.sim.<Logger Name>.handler.<handler name>.class The java class to load for a given handler console = java.util.logging.ConsoleHandler
org.mitre.sim.<Logger Name>.handler.<handler name>.level The level that a given handler should log at java.util.logging.Level.ALL [logs all messages]
org.mitre.sim.<Logger Name>.handler.<handler name>.formatter.class The formatter for a given handler [console] = org.mitre.sim.api3.util.BasicFormatter
org.mitre.sim.<Logger Name>.handler.<handler name>.formatter.format The format of a log record. This is only useful for the org.mitre.sim.api3.util.BasicFormatter. {0,date,MMM dd, yyyy} {0,time,HH:mm:ss} {1} {2}'\n'{3}: {4} {5,choice,0#'\n'|1#'\n' {6}}


Entity Finite State Machine

Each entity in a simulation is characterized by a finite state machine. The states of the machine are enumerated by class
State and described in the state transition diagram below:
Entity Life Cycle

Entity States

Entity State Transitions

  1. Instance of an Entity subclass is instantiated.
  2. Simulation executive detected newly registered entity instance.
  3. The simulation executive selected the entity for execution.
  4. The entity agenda invoked the waitForTime method.
  5. The entity agenda invoked the waitForAction or waitForActionOrTrigger methods.
  6. The waiting entity became ready for activation because either
  7. The active entity either
  8. While awaiting activation, the entity was forced to completion by
  9. While waiting for simulation time to pass or some change in the simulation environment, the entity was forced to completion by
  10. Method run on the Simulation subclass ended.

System Organization

Overall System Class Diagram

Time Method Summary

Method Invocation at time ti Return Description Action Method Invoked at time ta Trigger Condition Becomes True at time tc Time Elapses x Units
waitForTime (x) Return control to the Entity after x units of time have elapsed. Nothing Happens Nothing Happens Returns:
WaitResult (false, false, null, ti+x)
waitForAction (x) Return control to this Entity when any action method has been invoked or x units of time have elapsed. Returns:
WaitResult (true, false, null, ta)
Nothing Happens Returns:
WaitResult (false, false, null, ti+x)
waitForActionOrTrigger (triggers, x) Return control to this Entity when any of a set of triggers has been satisfied or x units of time have elapsed. Returns:
WaitResult (true, false, null, ta)
Returns:
WaitResult (false, true, trigger, tc)
Returns:
WaitResult (false, false, null, ti+x)


Copyright © 2003-2004 The MITRE Corporation