A Simple Discrete-Event Simulation: Part 67

Today I began implementing the capability of handling different types of entities. The two main aspects of this problem are how to assign properties to an entity and how those properties affect how an entity is processed by different parts of the system.

Addressing the first aspect, I originally defined a property of entityType that was supplied as a parameter to the entity’s constructor, and that value was set to zero for all entities and thereafter ignored. This is an example of defining a custom property for each type of characteristic. This is a good option if you are hard-coding a simulation for a specific application. Another option would be to define a base class and then attach custom properties through inheritance. Still another option, which I am implementing today, involves creating a general structure to define an arbitrary number of properties for each entity. I’m doing this with the idea of creating the most abstract and general type of base framework. In this we are trading away some speed and clarity for generalizability.

I first define a global data structure that describes what all the characteristics are and what values each characteristic can take on. Conceptually it looks like this:

This can be implemented as a two-dimensional linked list, a two dimensional array, a list of arrays, or whatever, so the depiction is rather general. In this example, and since we’re using JavaScript, we’ll use an array of arrays, where the first element of each sub-array is the name of the property (e.g., color) and the subsequent elements are the possible values of that property (e.g., “orange,” “ecru,” “chartreuse“).

This entire structure should be defined before any entity is defined. When each entity is defined, it includes a property called propertyList, which is a single-dimensioned list (array) of values for each possible characteristic. We could allow multiple values per characteristic but let’s just keep it simple, OK?

This mechanism is a bit kludgy and slow since it will often involve the use of strings, the comparison of which adds a certain amount of overhead. There are many ways to improve on the compactness and speed of the operations we’ll add (enumerated types, bitwise mapping, and so on), but this will keep things very clear and require the least amount of custom coding.

Here’s the code for the global data structure and the passive entity type. The data structure defining properties and values is defined first so that entities, when they are created, can be set up to store values for each possible property. The setProperty method is used to define an entity’s properties when it’s created and the getProperty method is used to determine the entity’s properties when it’s being processed.

Now we come to the other main aspect of this problem, which is how an entity’s characteristics affect how it is processed. In practice, in this kind of model, an entity’s type can determine its processing time (within any given component) and diversion percentage (the chance of going to each connected, downstream component when it gets forwarded from its current component). If we’re simulating a tollbooth we might choose a process time of 45 seconds for vehicles paying by cash but only 10 seconds for vehicles with automated transponders (e.g., E-ZPass). Travelers entering a country at a port of entry might be referred for secondary processing at random two percent of the time if they are a citizen of that country of ten percent of the time if they are a non-citizen. The diversion rate for citizens of watchlisted countries might be referred at an even higher rate.

Let’s tackle processing times first, since that’s just a single value for now. Processing times at different Process components may be dependent on different properties. I’ve modeled 40 or 50 land border crossings and know that primary processing time might be affected by citizenship, conveyance type (i.e., car, truck, bus, pedestrian), and membership in pre-clearance programs while the time it takes to pay tolls will depend on the method of payment. Therefore, a different method of identifying types has to be determined for each type of Process component and the value of the process time has to be specified for relevant combination of types. We’ll create dedicated functions to define synthetic type indices that will be used to determine the process time to use.

Let’s define some properties. The first lines define the properties and values that are possible while the function is called to assign values to each entity’s properties when it’s created. Notice also that the function assigns a color value to each entity based on its residency type (blue, purple, and yellow) and modifies it (light or darker) based on its processing speed. The display code has been updated so that the entities are represented as the same 5-pixel radius, red or green circle based on movement permissions we’ve had until now, but with a 3-pixel radius disk superimposed to show the type of entity. (The 3D entities haven’t yet been updated to incorporate this information.) Using lighter and darker colors is not the clearest way to differentiate easily between fast and slow entities but it demonstrates the idea. I’ve used more explicit text and graphic indicators to represent types and statuses in other simulations I’ve written, and the BorderWizard family of simulation tools I worked on used several graphical queues to represent different property values. Conveyance types (car, truck, bus, pedestrian) were represented by different shapes, residency type by different vehicle colors, toll type by hood color, and commercial vehicle type by trailer color. We’re keeping things simple for this project but the possibilities are endless.

Now we’ll create functions that generate indices based on the values of the properties. We’ll have all the entities with an E-ZPass-like credential get processed in a one time, visitors without a fast credential get processed in a different time, and citizens and legal permanent residents (LPRs) without fast credentials get still a different processing time. A similar function for diversion percentages generates indices strictly based on residency type. These can be custom-generated for each type of component on a case-by-case basis.

SO how does all this information get used? First we’ll expand the definition of process times and routing tables we pass to the components as they’re defined. Notice that we don’t assign special traverse times to the Queue component because that is about how long it takes to traverse a queue (and this value might be zero) and doesn’t necessarily have anything to do with the type of entity. For the routing table we’re saying that citizens don’t get diverted to secondary very often, LPRs get diverted slightly more often, and visitors get diverted quite often. (These percentages are only chosen to illustrate the effects.)

Then we’ll have to change how the variables are accessed within the components themselves. Here’s how process time is handled within Process components; we simply use the process time element indicated by the array index we generate based on the entity’s property values, in the second and third lines from the bottom.

Diversion percentages work the same way except they apply to all components except Arrivals, Paths, and Exits. It only matters when we’re model logic routing (option 3) and it only has an effect when an entity is available to be forwarded. Other than that we’ve simply added an extra layer of indirection. (The relevant code starts on line 37.)

So that’s all there is to it! Well, OK, it’s easy enough but spread around quite a lot. This first pass at adding this functionality works, as you can see by running the model, but we still need to streamline this process if possible and add some validation. For example, we should ensure that the size of each array and sub-array matches the number of possibilities that actually exist. We ultimately want to assign these values in the clearest way possible but there is an unavoidable level of complexity that the programmer will have to deal with. If you have any suggestions for ways to simplify any of this (before I think of some), then by all means let me know. We also need to update the reporting capability to capture statistics relevant to the new subtypes we’ve defined, and we need to find a way to add type indicators to the entities in the 3D representation.

This entry was posted in Simulation and tagged , . Bookmark the permalink.

Leave a Reply