Building Simulations in the SimulationPkg.st

  1. Create a subclass of Simulation to schedule arrivals and set up resources.
  2. Create subclasses of SimulationObjects to actually DO things in the simulation.
  3. Tell the Simulation instance to proceed for awhile.
  4. Gather the statistics you need, perhaps using some of the statistics gathering routines to send things out to disk.
Simulation instances get sent these messages:

Message:                                              What you should do in that method:                         
initialize                                            * Initialize receiver's instance variables (don't forget   
                                                      to do a super initializefirst!)                            
defineArrivalSchedule                                 * Schedule simulation objects to enter the simulation at   
                                                      various times.                                             
defineResources                                       * Specify the resources that are initially in the          
                                                      simulation                                                 
enter: anObject                                       * If you want to record any information about object       
                                                      arrivals, do it here.                                      
exit: anObject                                        * If you want to record any information about object       
                                                      departures, do it here.                                    
proceed                                               (Let it go - makes the simulation take a step.)            
finishUp                                              (Let it go - ends the simulation.)                         

Simulation instances can create their models using these messages (sent to themselves):

Message:                                                 What it does:                                            
produce: amount of: resourceName                         * Creates the amount of (String) resourceName            
coordinate: resourceName                                 * Sets up resource as being coordinated by the           
                                                         simulation                                               
schedule: actionBlock after: timeDelayInteger            * Schedules the block to occur after a delay             
schedule: actionBlock at: timeInteger                    * Schedules the block to occur at a specific time        
scheduleArrivalOf: aSimulationObject at: timeInteger     * Schedules the object instance to arrive at a time.     
scheduleArrivalOf: aSimulationObjectClass accordingTo:   * Schedules the class (subclass of SimulationObject)     
aProbabilityDistribution                                 to create new instances according to a distribution.     

SimulationObjects are sent these messages:

Message:                                                 What you should do in that method:                       
initialize                                               * If you have any instance variables, initialize them    
                                                         here.                                                    
tasks                                                    * DO WHATEVER THE OBJECT SHOULD DO!                      

SimulationObjects can also send messages to their Simulation like this:

(Simulation active) scheduleArrivalOf: self at:

(Simulation active) time + 90

SimulationObjects use these messages (sent to themselves) to describe their tasks:

Messages:                                                What it does:                                            
holdFor: aTimeDelay                                      * Make the object wait (presumably doing something) in   
                                                         Simulation time.                                         
acquire: amount ofResource: resourceName                 * Ask for amountof resource (a string), and wait in a    
                                                         queue if it's not available.                             
produce: amount ofResource: resourceName                 * Produce amount of resource, releasing waiting people   
                                                         in queue.                                                
release: aStaticResource                                 * If you held something that other people want, let it   
                                                         go.                                                      
inquireFor: amount ofResource: resourceName              * Returns true if that amount of resource is available.  
acquireResource: resourceName                            * Ask for a static resource                              
produceResource: resourceName                            * Create the staticResource                              

A Sample Simulation:

EventMonitor subclass: #DoNothing

instanceVariableNames: ''

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

Simulation subclass: #NothingAtAll

instanceVariableNames: ''

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

NothingAtAll methodsFor: 'initialization'

defineArrivalSchedule

self scheduleArrivalOf: DoNothing

accordingTo: (Uniform from:1 to:5).

NothingAtAll class methodsFor: 'demos'

aDoNothingDemo

| aSimulation aFile |

aFile := (Filename named: 'demo.events')

writeStream.

DoNothing file: aFile.

aSimulation := self new startUp.

[aSimulation time < 25]

whileTrue: [aSimulation proceed].

aFile close

What monitor file looks like:

26.7684 #DoNothing 8 enters

26.7684 #DoNothing 8 exits #DoNothing 2 enters

4.8476 #DoNothing 2 exits

6.4891 #DoNothing 3 enters

6.4891 #DoNothing 3 exits

10.8394 #DoNothing 4 enters

10.8394 #DoNothing 4 exits

15.1602 #DoNothing 5 enters

15.1602 #DoNothing 5 exits

19.489 #DoNothing 6 enters

19.489 #DoNothing 6 exits

22.067 #DoNothing 7 enters

22.067 #DoNothing 7 exits

Ways of Gathering Statistics on Simulations:

Museum Simulation (Example of using a Histogram)

Simulation subclass: #Museum

instanceVariableNames: 'statistics '

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

initialize

super initialize.

statistics := Histogram from: 5 to: 45 by: 5.

defineArrivalSchedule

self scheduleArrivalOf: Visitor accordingTo: (Uniform from: 5 to: 10).

exit: aSimulationObject

super exit: aSimulationObject.

statistics store: currentTime - aSimulationObject entryTime

printStatisticsOn: aStream

statistics printStatisticsOn: aStream

SimulationObject subclass: #Visitor

instanceVariableNames: 'entryTime '

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

initialize

super initialize.

entryTime := Simulation active time.

entryTime

^entryTime

tasks

self holdFor:

(Normal mean: 20 deviation: 5) next.

Running the Museum Simulation

| aSimulation aStream|

aSimulation := Museum new startUp.

[aSimulation time < 50] whileTrue: [aSimulation proceed].

aStream := ((Filename named: 'museum3.events') writeStream).

aSimulation printStatisticsOn: aStream.

aStream close.

(Ignore this Disk file: stuff)

Number of Minimum Maximum Average

Objects Value Value Value

4 13.6403 19.5696 16.3922

Number of

Entry Objects Frequency

5-10 0 0.0 |

10-15 1 0.25 |X

15-20 3 0.75 |XXX

20-25 0 0.0 |

25-30 0 0.0 |

30-35 0 0.0 |

35-40 0 0.0 |

40-45 0 0.0 |

Traffic (Example of Tallying Events)

Simulation subclass: #Traffic

instanceVariableNames: 'statistics '

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

defineArrivalSchedule

self scheduleArrivalOf: Car accordingTo:

(Uniform from: 0.5 to: 2).

self schedule: [self finishUp] at: 100.

"MJG Note: DOESN'T WORK!"

initialize

super initialize.

statistics := Dictionary new.

statistics at: #straight put: 0.

statistics at: #right put: 0.

statistics at: #left put: 0.

printStatisticsOn: aStream

aStream cr.

aStream nextPutAll: 'Car Direction Tally'.

statistics associationsDo:

[:assoc |

aStream cr.

assoc key printOn: aStream.

aStream tab.

assoc value printOn: aStream.]

update: key

statistics at: key

put: (statistics at: key) + 1.

SimulationObject subclass: #Car

instanceVariableNames: ''

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

tasks

"Sample, without replacement, the direction through the intersection that the car will travel"

| sample |

sample := SampleSpace data: #(left left right

straight straight straight straight straight).

(Simulation active) update: sample next.

(Ignore this ActiveSimulation stuff.)

Running the Traffic Simulation

| aSimulation aStream|

aSimulation := Traffic new startUp.

[aSimulation time < 100]

whileTrue: [aSimulation proceed].

aStream :=

((Filename named: 'traffic4.events')

writeStream).

aSimulation printStatisticsOn: aStream.

aStream close.

Car Direction Tally

#straight 48

#left 20

#right 13

Car Dealer Simulation (More complex histogram plus tallying events)

Simulation subclass: #CarDealer

instanceVariableNames: 'statistics '

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

initialize

super initialize.

statistics := Histogram from: 1 to: 365 by: 7.

defineArrivalSchedule

self scheduleArrivalOf: CarBuyer

accordingTo: (Uniform from: 2 to: 6)

startingAt: 1.0.

self scheduleArrivalOf:

(CarDelivery new) at: 90.0.

"Only one delivery is scheduled; the instance of CarDelivery will reschedule itself."

defineResources

self produce: 12 of: 'Car'.

exit: aSimulationObject

super exit: aSimulationObject.

"A CarDelivery could be exiting -- ignore it."

(aSimulationObject isKindOf: CarBuyer)

ifTrue: [statistics store:

currentTime - aSimulationObject entryTime]

printStatisticsOn: aStream

statistics printStatisticsOn: aStream.

SimulationObject subclass: #CarBuyer

instanceVariableNames: 'entryTime '

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'

initialize

super initialize.

entryTime := (Simulation active) time.

entryTime

^entryTime

tasks

self acquire: 1 ofResource: 'Car'.

SimulationObject subclass: #CarDelivery

instanceVariableNames: ''

classVariableNames: ''

poolDictionaries: ''

category: 'Simulation-Demos'!

tasks

"Get access to the Car resource and

produce 10, 11, or 12 cars."

self produce: ((SampleSpace

data: #(10 11 12)) next)

ofResource: 'Car'.

"Schedule a new delivery in 90 days"

(Simulation active) scheduleArrivalOf: self

at: (Simulation active) time + 90.

Results from the Car Dealer Simulation

Number of Minimum Maximum Average

Objects Value Value Value

24 0.0 45.6025 12.3136

Number of

Entry Objects Frequency

1-8 1 0.0416667 |X

8-15 1 0.0416667 |X

15-22 2 0.0833333 |XX

22-29 2 0.0833333 |XX

29-36 1 0.0416667 |X

36-43 3 0.125 |XXX

43-50 1 0.0416667 |X

50-57 0 0.0 |

57-64 0 0.0 |

64-71 0 0.0 |

71-78 0 0.0 |

78-85 0 0.0 |

85-92 0 0.0 |

92-99 0 0.0 |

99-106 0 0.0 |