Miscellaneous

Run control

Normally, a simulation is run for a given duration with

env.run(duration=100)
If you do not specify a till or duration parameter, like ::
env.run()

, 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

env.run(till=sim.inf)

Finally, it is possible to return control to ‘main’ from a component with

env.main().activate()

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

env.reset_now()

Please note that in this case, user time values has to be corrected accordingly.

Time units

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 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 is possible to set the speed of the animation. For instance if we want one second of real time to correspond to 5 minutes

env.speed(sim.minutes(5))

The following time units are available:

  • ‘years’
  • ‘weeks’
  • ‘days’
  • ‘hours’
  • ‘minutes’
  • ‘seconds’
  • ‘milliseconds’
  • ‘microseconds’
  • ‘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:

  • years()
  • weeks()
  • days()
  • hours()
  • minutes()
  • seconds()
  • milliseconds()
  • microseconds()

For conversion from the simulation time unit to a given time unit, the following calls are available:

  • to_years()
  • to_weeks()
  • to_days()
  • to_hours()
  • to_minutes()
  • to_seconds()
  • to_milliseconds()
  • to_microseconds()

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 the conversion to the current time unit is made immediately and that all related output is therefore in the current simulation time unit, so

processingtime_dis.print_info()
dryingtime_dis.print_info()

will print

Uniform distribution 0x25783c11358
  lowerbound=600.0
  upperbound=1200.0
  randomstream=0x25783b89818
Normal distribution 0x25783bff8d0
  mean=7200.0
  standard_deviation=360.0
  coefficient_of_variation=0.05
  randomstream=0x25783b89818

Usage of the the trace facility

Control

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 env.trace(False).

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

c.suppress_trace(True)

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 Environment.time_to_str_format()

For instance, if 5 decimals in the trace are to be shown instead of the default 3, issue

env.time_to_str_format('{:10.5f}')

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.

Example

env.print_trace('', '**ALERT**', 'Houston, we have a problem with', c.name())

Refer to the reference section for details.

Redirecting trace output

Trace output (as all other salabim output) will be written to stdout. It is possible to use standard Python functionality to send all output to another file with

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

All trace output is also written to logging.