1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
.. _porting_from_simpy2:
=========================
Porting from SimPy 2 to 3
=========================
Porting from SimPy 2 to SimPy 3 is not overly complicated. A lot of changes
merely comprise copy/paste.
This guide describes the conceptual and API changes between both SimPy versions
and shows you how to change your code for SimPy 3.
Imports
=======
In SimPy 2, you had to decide at import-time whether you wanted to use a normal
simulation (``SimPy.Simulation``), a real-time simulation
(``SimPy.SimulationRT``) or something else. You usually had to import
``Simulation`` (or ``SimulationRT``), ``Process`` and some of the SimPy
keywords (``hold`` or ``passivate``, for example) from that package.
In SimPy 3, you usually need to import much less classes and modules (for
example, all keywords are gone). In most use cases you will now only need to
import :mod:`simpy`.
**SimPy 2**
.. code-block:: python
from Simpy.Simulation import Simulation, Process, hold
**SimPy 3**
.. code-block:: python
import simpy
The ``Simulation*`` classes
===========================
SimPy 2 encapsulated the simulation state in a ``Simulation*`` class (e.g.,
``Simulation``, ``SimulationRT`` or ``SimulationTrace``). This
class also had a ``simulate()`` method that executed a normal simulation,
a real-time simulation or something else (depending on the particular class).
There was a global ``Simulation`` instance that was automatically created when
you imported SimPy. You could also instantiate it on your own to uses SimPy's
object-orient API. This led to some confusion and problems, because you had to
pass the ``Simulation`` instance around when you were using the object-oriented
API but not if you were using the procedural API.
In SimPy 3, an :class:`~simpy.core.Environment` replaces ``Simulation`` and
:class:`~simpy.rt.RealtimeEnvironment` replaces ``SimulationRT``. You always
need to instantiate an environment. There's no more global state.
To execute a simulation, you call the environment's
:meth:`~simpy.core.Environment.run()` method.
**SimPy 2**
.. code-block:: python
# Procedural API
from SimPy.Simulation import initialize, simulate
initialize()
# Start processes
simulate(until=10)
.. code-block:: python
# Object-oriented API
from SimPy.Simulation import Simulation
sim = Simulation()
# Start processes
sim.simulate(until=10)
**SimPy3**
.. code-block:: python
import simpy
env = simpy.Environment()
# Start processes
env.run(until=10)
Defining a Process
==================
Processes had to inherit the ``Process`` base class in SimPy 2. Subclasses had
to implement at least a so called *Process Execution Method (PEM)* (which is
basically a generator function) and in most cases ``__init__()``. Each process
needed to know the ``Simulation`` instance it belonged to. This reference was
passed implicitly in the procedural API and had to be passed explicitly in the
object-oriented API. Apart from some internal problems, this made it quite
cumbersome to define a simple process.
Processes were started by passing the ``Process`` and a generator instance
created by the generator function to either the global ``activate()`` function
or the corresponding ``Simulation`` method.
A process in SimPy 3 is a Python generator (no matter if it’s defined on module
level or as an instance method) wrapped in a :class:`~simpy.events.Process`
instance. The generator usually requires a reference to a
:class:`~simpy.core.Environment` to interact with, but this is completely
optional.
Processes are can be started by creating a :class:`~simpy.events.Process`
instance and passing the generator to it. The environment provides a shortcut
for this: :meth:`~simpy.core.Environment.process()`.
**SimPy 2**
.. code-block:: python
# Procedural API
from Simpy.Simulation import Process
class MyProcess(Process):
def __init__(self, another_param):
super().__init__()
self.another_param = another_param
def generator_function(self):
"""Implement the process' behavior."""
yield something
initialize()
proc = Process('Spam')
activate(proc, proc.generator_function())
.. code-block:: python
# Object-oriented API
from SimPy.Simulation import Simulation, Process
class MyProcess(Process):
def __init__(self, sim, another_param):
super().__init__(sim=sim)
self.another_param = another_param
def generator_function(self):
"""Implement the process' behaviour."""
yield something
sim = Simulation()
proc = Process(sim, 'Spam')
sim.activate(proc, proc.generator_function())
**SimPy 3**
.. code-block:: python
import simpy
def generator_function(env, another_param):
"""Implement the process' behavior."""
yield something
env = simpy.Environment()
proc = env.process(generator_function(env, 'Spam'))
SimPy Keywords (``hold`` etc.)
==============================
In SimPy 2, processes created new events by yielding a *SimPy Keyword* and some
additional parameters (at least ``self``). These keywords had to be imported
from ``SimPy.Simulation*`` if they were used. Internally, the keywords were
mapped to a function that generated the according event.
In SimPy 3, you directly yield :mod:`~simpy.events` if you want to wait for an
event to occur. You can instantiate an event directly or use the shortcuts
provided by :class:`~simpy.core.Environment`.
Generally, whenever a process yields an event, the execution of the process is
suspended and resumed once the event has been triggered. To motivate this
understanding, some of the events were renamed. For example, the ``hold``
keyword meant to wait until some time has passed. In terms of events this means
that a timeout has happened. Therefore ``hold`` has been replaced by a
:class:`~simpy.events.Timeout` event.
.. note::
:class:`~simpy.events.Process` is also an :class:`~simpy.events.Event`. If
you want to wait for a process to finish, simply yield it.
**SimPy 2**
.. code-block:: python
yield hold, self, duration
yield passivate, self
yield request, self, resource
yield release, self, resource
yield waitevent, self, event
yield waitevent, self, [event_a, event_b, event_c]
yield queueevent, self, event_list
yield get, self, level, amount
yield put, self, level, amount
**SimPy 3**
.. code-block:: python
yield env.timeout(duration) # hold: renamed
yield env.event() # passivate: renamed
yield resource.request() # Request is now bound to class Resource
resource.release() # Release no longer needs to be yielded
yield event # waitevent: just yield the event
yield env.all_of([event_a, event_b, event_c]) # waitvent
yield env.any_of([event_a, event_b, event_c]) # queuevent
yield container.get(amount) # Level is now called Container
yield container.put(amount)
yield event_a | event_b # Wait for either a or b. This is new.
yield event_a & event_b # Wait for a and b. This is new.
yield env.process(calculation(env)) # Wait for the process calculation to
# to finish.
Partially supported features
----------------------------
The following ``waituntil`` keyword is not completely supported anymore:
.. code-block:: python
yield waituntil, self, cond_func
SimPy 2 was evaluating ``cond_func`` after *every* event, which was
computationally very expensive. One possible workaround is for example the
following process, which evaluates ``cond_func`` periodically:
.. code-block:: python
def waituntil(env, cond_func, delay=1):
while not cond_func():
yield env.timeout(delay)
# Usage:
yield waituntil(env, cond_func)
Interrupts
==========
In SimPy 2, ``interrupt()`` was a method of the interrupting process. The
victim of the interrupt had to be passed as an argument.
The victim was not directly notified of the interrupt but had to check if the
``interrupted`` flag was set. Afterwards, it had to reset the interrupt via
``interruptReset()``. You could manually set the ``interruptCause`` attribute
of the victim.
Explicitly checking for an interrupt is obviously error prone as it is too easy
to be forgotten.
In SimPy 3, you call :meth:`~simpy.events.Process.interrupt()` on the victim
process. You can optionally supply a cause. An
:exc:`~simpy.exceptions.Interrupt` is then thrown into the victim process,
which has to handle the interrupt via ``try: ... except Interrupt: ...``.
**SimPy 2**
.. code-block:: python
class Interrupter(Process):
def __init__(self, victim):
super().__init__()
self.victim = victim
def run(self):
yield hold, self, 1
self.interrupt(self.victim_proc)
self.victim_proc.interruptCause = 'Spam'
class Victim(Process):
def run(self):
yield hold, self, 10
if self.interrupted:
cause = self.interruptCause
self.interruptReset()
**SimPy 3**
.. code-block:: python
def interrupter(env, victim_proc):
yield env.timeout(1)
victim_proc.interrupt('Spam')
def victim(env):
try:
yield env.timeout(10)
except Interrupt as interrupt:
cause = interrupt.cause
Conclusion
==========
This guide is by no means complete. If you run into problems, please have
a look at the other :doc:`guides <index>`, the :doc:`examples
<../examples/index>` or the :doc:`../api_reference/index`. You are also very
welcome to submit improvements. Just create a pull request at `bitbucket
<https://bitbucket.org/simpy/simpy/>`_.
|