1============ 2Environments 3============ 4 5.. currentmodule:: simpy.core 6 7A simulation environment manages the simulation time as well as the scheduling 8and processing of events. It also provides means to step through or execute the 9simulation. 10 11The base class for all environments is :class:`~simpy.core.BaseEnvironment`. 12"Normal" simulations usually use its subclass 13:class:`~simpy.core.Environment`. For real-time simulations, SimPy provides a 14:class:`~simpy.rt.RealtimeEnvironment` (more on that in 15:doc:`real-time-simulations`). 16 17 18.. _simulation-control: 19 20Simulation control 21================== 22 23SimPy is very flexible in terms of simulation execution. You can run your 24simulation until there are no more events, until a certain simulation time is 25reached, or until a certain event is triggered. You can also step through the 26simulation event by event. Furthermore, you can mix these things as you like. 27 28For example, you could run your simulation until an interesting event occurs. 29You could then step through the simulation event by event for a while; and 30finally run the simulation until there are no more events left and your processes 31have all terminated. 32 33The most important method here is :meth:`Environment.run()`: 34 35- If you call it without any argument (``env.run()``), it steps through the 36 simulation until there are no more events left. 37 38 .. warning:: 39 40 If your processes run forever (``while True: yield env.timeout(1)``), this 41 method will never terminate (unless you kill your script by e.g., pressing 42 :kbd:`Ctrl-C`). 43 44- In most cases it is advisable to stop your simulation when it reaches 45 a certain simulation time. Therefore, you can pass the desired time via the 46 *until* parameter, e.g.: ``env.run(until=10)``. 47 48 The simulation will then stop when the internal clock reaches 10 but will not 49 process any events scheduled for time 10. This is similar to a new 50 environment where the clock is 0 but (obviously) no events have yet been 51 processed. 52 53 If you want to integrate your simulation in a GUI and want to draw a 54 process bar, you can repeatedly call this function with increasing *until* 55 values and update your progress bar after each call: 56 57 .. code-block:: python 58 59 for i in range(100): 60 env.run(until=i) 61 progressbar.update(i) 62 63- Instead of passing a number to ``run()``, you can also pass any event to it. 64 ``run()`` will then return when the event has been processed. 65 66 Assuming that the current time is 0, ``env.run(until=env.timeout(5))`` is 67 equivalent to ``env.run(until=5)``. 68 69 You can also pass other types of events (remember, that 70 a :class:`~simpy.events.Process` is an event, too):: 71 72 >>> import simpy 73 >>> 74 >>> def my_proc(env): 75 ... yield env.timeout(1) 76 ... return 'Monty Python’s Flying Circus' 77 >>> 78 >>> env = simpy.Environment() 79 >>> proc = env.process(my_proc(env)) 80 >>> env.run(until=proc) 81 'Monty Python’s Flying Circus' 82 83.. _simulation-step: 84 85To step through the simulation event by event, the environment offers 86:meth:`~Environment.peek()` and :meth:`~Environment.step()`. 87 88``peek()`` returns the time of the next scheduled event or *infinity* 89(``float('inf')``) if no future events are scheduled. 90 91``step()`` processes the next scheduled event. It raises an 92:class:`EmptySchedule` exception if no event is available. 93 94In a typical use case, you use these methods in a loop like: 95 96.. code-block:: python 97 98 until = 10 99 while env.peek() < until: 100 env.step() 101 102 103State access 104============ 105 106The environment allows you to get the current simulation time via the 107:attr:`Environment.now` property. The simulation time is a number without unit 108and is increased via :class:`~simpy.events.Timeout` events. 109 110By default, ``now`` starts at 0, but you can pass an ``initial_time`` to the 111:class:`Environment` to use something else. 112 113.. note:: 114 115 Although the simulation time is technically unitless, you can pretend that 116 it is, for example, in seconds and use it like a timestamp returned by 117 :func:`time.time()` to calculate a date or the day of the week. 118 119The property :attr:`Environment.active_process` is comparable to 120:func:`os.getpid()` and is either ``None`` or pointing at the currently active 121:class:`~simpy.events.Process`. A process is *active* when its process function 122is being executed. It becomes *inactive* (or suspended) when it yields an 123event. 124 125Thus, it only makes sense to access this property from within a process 126function or a function that is called by your process function:: 127 128 >>> def subfunc(env): 129 ... print(env.active_process) # will print "p1" 130 >>> 131 >>> def my_proc(env): 132 ... while True: 133 ... print(env.active_process) # will print "p1" 134 ... subfunc(env) 135 ... yield env.timeout(1) 136 >>> 137 >>> env = simpy.Environment() 138 >>> p1 = env.process(my_proc(env)) 139 >>> env.active_process # None 140 >>> env.step() 141 <Process(my_proc) object at 0x...> 142 <Process(my_proc) object at 0x...> 143 >>> env.active_process # None 144 145An exemplary use case for this is the resource system: If a process function 146calls :meth:`~simpy.resources.resource.Resource.request()` to request 147a resource, the resource determines the requesting process via 148``env.active_process``. Take a `look at the code`__ to see how we do this :-). 149 150__ https://gitlab.com/team-simpy/simpy/-/blob/master/src/simpy/resources/base.py 151 152 153Event creation 154============== 155 156To create events, you normally have to import :mod:`simpy.events`, instantiate 157the event class and pass a reference to the environment to it. To reduce the 158amount of typing, the :class:`Environment` provides some shortcuts for event 159creation. For example, :meth:`Environment.event()` is equivalent to 160``simpy.events.Event(env)``. 161 162Other shortcuts are: 163 164- :meth:`Environment.process()` 165- :meth:`Environment.timeout()` 166- :meth:`Environment.all_of()` 167- :meth:`Environment.any_of()` 168 169More details on what the events do can be found in the :doc:`guide to events 170<events>`. 171 172 173Miscellaneous 174============= 175 176Since Python 3.3, a generator function can have a return value: 177 178.. code-block:: python 179 180 def my_proc(env): 181 yield env.timeout(1) 182 return 42 183 184In SimPy, this can be used to provide return values for processes that can be 185used by other processes: 186 187.. code-block:: python 188 189 def other_proc(env): 190 ret_val = yield env.process(my_proc(env)) 191 assert ret_val == 42 192 193Internally, Python passes the return value as parameter to the 194:exc:`StopIteration` exception that it raises when a generator is exhausted. So 195in Python 2.7 and 3.2 you could replace the ``return 42`` with a ``raise 196StopIteration(42)`` to achieve the same result. 197 198To keep your code more readable, the environment provides the method 199:meth:`~Environment.exit()` to do exactly this: 200 201.. code-block:: python 202 203 def my_proc(env): 204 yield env.timeout(1) 205 env.exit(42) # Py2 equivalent to "return 42" 206