Normally, a simulation is run for a given duration with
- If you do not specify a till or duration parameter, like ::
, the simulation will run till there are no events left, or otherwise infinitely.
If it required that the simulation does not stop when there are no more events, which can be useful for animation, issue
Finally, it is possible to return control to ‘main’ from a component with
For instance, if we want to stop a simulation after 50 ships are created
class ShipGenerator(sim.Component): def process(self): for _ in range(50): yield self.hold(iat.sample()) Ship() env.main().activate
Or, if you want to terminate a run based upon a condition
class RunChecker(sim.Component): def process(self): while True: if len(q0) + len(q1) > 10: env.main.activate() yield self.standby()
It is perfectly possible and sometimes very useful to continue a simulation after a run statement, like
env.run(100) q.reset_statistics() env.run(1000) q.print_statistics()
The salabim time (now) can be reset to 0 (or another time) with
Please note that in this case, user time values has to be corrected accordingly.
By default, salabim time does not have a specific dimension, which means that is up to the modeller what time unit is used, be it seconds, hours, days or whatever.
It can be useful to work in specific time unit, as this opens the possibility to specify times and durations in another time unit.
In order to use time unit, the environment has to be initialized with a time_unit parameter, like
env = sim.Environment(time_unit='hours')
From then on, the simulation runs in hours. Standard output is in then in hours and for instance
self.enter(q) yield self.hold(48) print(env.now() - self.queuetime())
means hold for 48 (hours) and 48 will be printed.
But, now we also specify a time in another time unit and get times in a specific time unit
self.enter(q) yield self.hold(env.days(2)) print(env.to_minutes(env.now() - self.queuetime()))
means hold for 2 days = 48 hours and 2880 (48 * 60) will be printed.
With this, it is possible to set the speed of the animation. For instance if we want one second of real time to correspond to 5 minutes
The following time units are available:
- ‘n/a’ which means nothing is assigned and conversions are not supported
For conversion from a given time unit to the simulation time unit, the following calls are available:
For conversion from the simulation time unit to a given time unit, the following calls are available:
Distributions (apart from IntUniform, Poisson and Beta) can also specify the time unit, like
env = sim.Environment(time_unit='seconds') processingtime_dis = sim.Uniform(10, 20, 'minutes') dryingtime_dis = sim.Normal(2, 0.1, 'hours')
Note that distribution keep the time unit information and therefore salabim is able to present information on the distribution in pretty format: So
Uniform distribution 0x2bfb4d14c50 lowerbound=10 minutes upperbound=20 minutes randomstream=0x2bfb2cc0c78 Normal distribution 0x2bfb42ce630 mean=2 hours standard_deviation=0.1 hours coefficient_of_variation=0.05 randomstream=0x2bfb2cc0c78
It is possible to scale a non level monitor (particularly length_of_stay of a queue) to another time unit, which is particularly useful for print_histogram. For example
will use minutes as the time unit.
This is equivalent to
And if the environment’s time unit is seconds, equivalent to
(waitingline.length_of_stay * 60).print_histogram()
Usage of the the trace facility¶
Tracing can be turned on at time of creating an environment
env = sim.Environment(trace=True)
and can be turned on during a simulation run with
env.trace(True) and likewise
turned off with
env.trace(False). The current status can be queried with
It is also possible to direct the trace output to file, by using
with open('output.txt', 'w') as out: env = sim.Environment(trace=out)
out = open('output.txt', 'w') env.trace(out) ... out.close()
Interpretation of the trace¶
A trace ouput looks like
line# time current component action information ----- ---------- -------------------- ----------------------------------- ------------------------------------------------ line numbers refers to Example - basic.py 11 default environment initialize 11 main create 11 0.000 main current 12 car.0 create 12 car.0 activate scheduled for 0.000 @ 6 process=process 13 main run scheduled for 5.000 @ 13+ 6 0.000 car.0 current 8 car.0 hold scheduled for 1.000 @ 8+ 8+ 1.000 car.0 current 8 car.0 hold scheduled for 2.000 @ 8+ 8+ 2.000 car.0 current 8 car.0 hold scheduled for 3.000 @ 8+ 8+ 3.000 car.0 current 8 car.0 hold scheduled for 4.000 @ 8+ 8+ 4.000 car.0 current 8 car.0 hold scheduled for 5.000 @ 8+ 13+ 5.000 main current
The texts are pretty self explanatory. If a mode is given, that will be shown as well.
Note that line numbers sometimes has an added +, which means that the activation is actually the statement following the given line number. When there is more than one source file involved, the line number may be preceded by a letter. In that case, the trace will contain an information line to which file that letter refers to.
The text ‘process=’ refers to the activation process, which is quite often just process.
When a time is followed by an exclamation mark (!), it means that the component is scheduled urgent, i.e. before all other events for the same moment.
Suppressing components from being shown in the trace¶
It is possible to suppress the trace when a specific component becomes or is current. This can be either indicated at creation of a component with
c = sim.Component(suppress_trace=True)
or later with
Note that this suppresses all trace output during the time a component is current.
Showing standby components in the trace¶
By default standby components are (apart from when they become non standby) suppressed from the trace. With
env.suppress_trace_standby(False) standby components are fully traced.
Changing the format of times and durations¶
It is possible to change the format of times in trace, animation, etc. with the method
For instance, if 5 decimals in the trace are to be shown instead of the default 3, issue
Make sure that the format represents 10 characters.
Adding lines to the trace output¶
A model can add additional information to the trace with the
Environment.print_trace() method. This methods accepts up to five parameters to be show on one line. When trace is False, nothing will be displayed.
env.print_trace('', '**ALERT**', 'Houston, we have a problem with', c.name())
Refer to the reference section for details.
Redirecting trace output¶
Normally, all trace output is written to stdout.
However, if the trace parameter of Environment() or Environment.trace() is a file handle (open for write), then the trace output will go to the specified file. Note that it is required to close the file handle at the end, so a context manager (‘with’) is recommended. Example
with open('output.txt', 'w') as out: env = sim.Environment(trace=out) ... env.run()
Alternatively, stdout can be set to a file, in which case ALL salabim output will be redirected. This can be done like
save_stdout = sys.stdout sys.stdout = open('output.txt', 'w')
If required, it is possible to revert to the original stdout
sys.stdout.close() sys.stdout = save_stdout
Suppressing line numbers in the trace¶
Particularly when the trace output is written to a file, it may be useful to suppress line numbers in the trace. This can result in dramatic (up to 50 times!) performance increase, because the calculation of line numbers is very demanding.
The method Environment.suppress_trace_linenumbers() can be used to disable/enable line number in the trace
env = sim.Environment(trace=True) env.suppress_trace_linenumbers(True)
By default, line numbers are not suppressed in the trace. The current status can be queried with