Skip to content

SimPy Compatibility

Every modelling feature of SimPy 4.1.2, 98 in all, with its status in YAML, the builder and the low-level API: native and checked, or a difference kept by design. A native feature is checked by conformance tests that run a plain SimPy 4.1.2 model and the same model in all three APIs, and compare event times, outcomes and balances. The page is generated from tests/conformance/register.yaml, the list the tests read, so a feature is native here only with its checks.

96 features are native and checked in all three APIs, and 2 are differences kept by design. The last section lists those and one more, strict pacing at factor 0, which is not a SimPy feature of its own.

The low-level API is SimPy with a few classes added, so most SimPy features work in it; SimPy and desflow explains how the two fit together. Where desflow differs from SimPy on purpose, the difference is listed under the table.

The sections are numbered by kind. C1 to C7 are SimPy's core operations, such as events, conditions, interrupts, resources and stores. S1 to S3 are support features desflow builds from those operations: breakdowns, schedules and replay. SimPy has no single call for them, so their conformance tests compare with the same behaviour written in plain SimPy.

Section Operations native in all three APIs
C1: Events and Processes 18
C2: Wait for One or All 6
C3: Interrupts and Preemption 4
C4: Resources 12
C5: Stores and Containers 20
C6: Run Control 21
C7: Patience 2
S1: Breakdowns 1
S2: Schedules 1
S3: Replay 1
Differences Kept by Design 3 differences

Under each feature, API details opens to show its SimPy construct, its form in YAML, the builder and the low-level API, and the conformance tests that check it.

C1: Events and Processes

Waiting a fixed time, events a task can fire or fail, including an event that triggers once, and processes: starting one, with or without a delay and without waiting for it, waiting for it, reading its return value or its failure, being told when it ends, running actions when an event happens, and inspecting an event, a process or the running process while it runs.

Timed wait: A task waits a set or sampled time, such as the time packing takes, before it goes on.

API details
  • SimPy: simpy.events.Timeout
  • YAML: a task with service and no resource
  • Builder: @app.task(service_id=...) with no resource
  • Low-level: yield env.timeout(delay)
  • Checks: test_c1_timed_wait
  • Taught on: Timed wait

Signal event success and value: One task fires a named signal with a value, and every task waiting for it wakes up and reads that value.

API details
  • SimPy: simpy.events.Event.succeed(value)
  • YAML: signals: [restocked], signal: restocked on the firing task and wait: {signal: restocked}
  • Builder: add_signal, @app.task(signal=...), wait={"signal": ...}, or context.signal(name).fire(value)
  • Low-level: Signal(env, name): yield signal.wait() and signal.fire(value)
  • Checks: test_c1_signal_event_success_and_value
  • Taught on: Signal event success and value

Signal event failure: A signal fires as a failure, and every task waiting for it fails with the same reason.

API details
  • SimPy: simpy.events.Event.fail(exception)
  • YAML: a failing task with signal; its waiters fail with its reason
  • Builder: the same, or context.signal(name).fail(reason)
  • Low-level: signal.fail(reason); waiters receive SignalFailed
  • Checks: test_c1_signal_event_failure
  • Taught on: Signal event failure

Start and wait for a process: A task starts a child flow, such as an inspection, and waits until it ends.

API details

Process return value: The task that waited for a child flow reads what the child produced.

API details
  • SimPy: a process's return value
  • YAML: wait.inspect.value.<field>, the last payload of the child flow
  • Builder: the same, or the value of context.call(...)
  • Low-level: value = yield env.process(child())
  • Checks: test_c1_process_return_value
  • Taught on: Process return value

Process failure: A failure inside a child flow reaches the task waiting for it, which can handle it or fail in turn.

API details
  • SimPy: an exception raised in a process reaches the process that waits for it
  • YAML: fail, fail_reason and on_fail on a task; a failure no on_fail handles fails the waiting task
  • Builder: the same, and a function that raises fails its task
  • Low-level: try: yield env.process(child()) and except
  • Checks: test_c1_process_failure, test_c1_process_failure_handled_by_on_fail
  • Difference: By default (on_unhandled_failure is continue) a failure no task waits for ends the entity's flow in YAML and the builder, publishes failed and is counted, and the run goes on; under on_unhandled_failure of stop it stops the run, matching SimPy and the low-level API, where an exception nobody handles stops the run.
  • Taught on: Process failure

Event inspection: A condition reads whether a signal has fired, whether it succeeded, and its value.

API details
  • SimPy: Event.triggered, .processed, .ok, .value
  • YAML: signal.<name>.count, .ok and .value in a condition
  • Builder: context.signal(name).wait() and its SimPy attributes, or the signal's count, ok and value
  • Low-level: the same attributes of SimPy events and Signal
  • Checks: test_c1_event_inspection
  • Taught on: Event inspection

Process inspection: After a wait, a task reads whether a child flow has finished, whether it succeeded, and its value.

API details
  • SimPy: Process.is_alive, .triggered, .ok, .value
  • YAML: wait.<branch>.done, .ok and .value after the wait
  • Builder: the process context.spawn and context.call return
  • Low-level: the process env.process returns
  • Checks: test_c1_process_inspection
  • Taught on: Process inspection

Wait for a shared condition: A task waits until a condition on the model's state holds, such as stock reaching 5.

API details

The running process: process.name, process.instance and process.target read the running task's name, instance id and what it waits on now, as SimPy's active process, its name and its target.

API details

Start a flow without waiting for it: A task spawns another task as a new entity, keyed <task>-n, with the spawning entity's key in attr.parent, and goes on without waiting.

API details

One-shot event: A one-shot signal fires once, as SimPy's Event does: it does not re-arm, a wait after the firing resumes at once, and a second firing is refused.

API details

Actions when an event happens: An on: list runs actions when a signal fires or a task ends: fire a signal (copying another outcome), spawn a flow, interrupt a task, or set a value, as SimPy's callbacks, trigger and defused failures.

API details

Failure nobody handles: Under on_unhandled_failure: stop a task, child flow or one-shot signal failure nobody handles stops the run, as SimPy; under continue, the default, it is defused and the run goes on.

API details

Timeout value: A wait timeout carries a value the waiter receives, as SimPy's env.timeout(delay, value).

API details
  • SimPy: events.Timeout(delay, value)
  • YAML: wait: {timeout: {after: 40 s, value: ...}}
  • Builder: the same mapping in wait=
  • Low-level: env.timeout(delay, value)
  • Checks: test_c1_timeout_value
  • Taught on: Timeout value

Wait for a process already running: A running wait holds until the running instances of a task on the waiter's entity, and the entities it spawned, all end, as SimPy's yield process.

API details

Start a process after a delay: A spawn with after starts the new entity that long after the task finishes, as SimPy's start_delayed.

API details

Be told when an event happens: A subscribe interrupts the task when an event happens, carrying the event and its value, as SimPy's subscribe_at.

API details

C2: Wait for One or All

Waiting for the first of several events, or for all of them, as SimPy's AnyOf and AllOf conditions do, with conditions inside conditions, a rule of your own such as at least some of them (SimPy's condition with a count rule), a condition over no events, and a request, put or get as one of the events.

Wait for one event: A task waits for whichever of several events happens first, such as a delivery or a time limit.

API details

Wait for all events: A task waits until every one of several events has happened.

API details

Nested and mixed conditions: A wait nests any, all and at_least to any depth, as SimPy's conditions combine with & and |.

API details

A condition with a custom rule: at_least: n of several branches is done when n of them are, as SimPy's Condition with a count rule.

API details

A condition over no events: An empty any or all is accepted and succeeds at once with an empty value, as SimPy does.

API details

Request, put or get as a wait branch: A request, get or put can be a wait branch; a won request holds its unit through the service, and a losing branch is cancelled.

API details

C3: Interrupts and Preemption

Interrupting a task while it waits or while it is in service, and a higher-priority request taking a resource unit from a lower-priority user, with the cause that tells the user who took it and since when it held it.

Interrupt while waiting: Another task or a scenario step interrupts a task while it is still in a queue.

API details
  • SimPy: simpy.Process.interrupt while the process waits for a request
  • YAML: interrupt: {task: waiter} on a scenario step or a task; on_interrupt on the task
  • Builder: context.interrupt("waiter"); @app.task(on_interrupt=...)
  • Low-level: process.interrupt(cause), caught as simpy.Interrupt
  • Checks: test_c3_interrupt_while_waiting
  • Taught on: Interrupt while waiting

Interrupt during service: A task is interrupted while it holds a resource, and resumes, restarts or abandons its work.

API details

Priority preemption: A more urgent request takes a resource unit away from a less urgent user, who is interrupted.

API details

Preemption cause: A task that loses its unit to a more urgent request learns who took it and since when it had held the unit.

API details

C4: Resources

Requesting and releasing resource units, in order of arrival or of priority, with a default capacity of one unit or no limit, reading the count, users and queue, limiting the waiting line, cancelling a request before or after its grant, holding several resources at once, and resources of your own classes.

Ordinary request and release: A task takes one unit of a resource, such as a packer, waits in line if none is free, and gives it back when done.

API details

Priority request: Waiting requests are served by priority, the lowest number first, instead of in order of arrival.

API details
  • SimPy: simpy.resources.resource.PriorityResource
  • YAML: queue: priority on the resource and priority on a task
  • Builder: add_resource(..., queue="priority") and @app.task(priority=...)
  • Low-level: DynamicResource(..., queue="priority") and request(priority=...)
  • Checks: test_c4_priority_resource_request, test_c4_priority_request_with_mixed_preempt_flags
  • Difference: Equal priorities are served first in, first out, as in SimPy; desflow also offers fifo and lifo queues, under which a request's priority is ignored.
  • Taught on: Priority request

Cancel before a grant: A task leaves the queue before it is served, so it never takes a unit.

API details

Cancel after a grant: A task that already holds a unit stops early and gives the unit back.

API details
  • SimPy: Request.cancel() after the grant releases nothing; Resource.release(request) does
  • YAML: an interrupt with on_interrupt: abandon gives the unit back
  • Builder: the same
  • Low-level: request.cancel() releases nothing; resource.release(request)
  • Checks: test_c4_cancel_after_a_grant
  • Taught on: Cancel after a grant

Hold several resources: A task holds more than one resource at once, taken in a fixed order, and can hand them to the next task.

API details
  • SimPy: nested requests, each held while the next is awaited
  • YAML: resource: [picker, bench], acquired in that order; keep hands units to the next task
  • Builder: @app.task(resource_id=["picker", "bench"], keep=[...])
  • Low-level: nested with blocks
  • Checks: test_c4_hold_several_resources, test_c4_keep_hands_a_unit_to_the_next_task
  • Difference: A model whose tasks acquire two resources in opposite orders is refused when it is built, because those tasks could each wait for ever; SimPy runs it. Two requests under one all wait are made together, so another task that acquires the two one after the other, in either order, is refused too.
  • Taught on: Hold several resources

Resource capacity defaults to 1: A resource declared with no capacity has one unit, and a capacity of infinity makes a resource that never queues.

API details

Resource count, queue and users: A condition or a script reads how many units are held, which requests hold them and which wait.

API details

Release event and its request: Giving a unit back returns an event that holds the request it released and is triggered at once.

API details
  • SimPy: resources.resource.Release.request
  • YAML: implicit: a task releases its units when it ends
  • Builder: get_resource(name).release(request)
  • Low-level: resource.release(request) returns a DynamicRelease whose request is the request
  • Checks: test_c4_release_event
  • Taught on: Release event and its request

Usage since: A granted request records the time its unit was granted, and a preemption report carries it.

API details
  • SimPy: resources.resource.Request.usage_since
  • YAML: usage_since in the preempted record
  • Builder: get_resource(name).users[i].usage_since, and the preempted record
  • Low-level: request.usage_since
  • Checks: test_c4_usage_since
  • Taught on: Usage since

Default request priority 0: A request that names no priority has priority 0, so it ties with priority 0 and goes ahead of priority 1.

API details
  • SimPy: resources.resource.PriorityRequest(priority=0)
  • YAML: a task with no priority on a queue: priority resource
  • Builder: @app.task(resource_id=...) with no priority
  • Low-level: request() with no priority
  • Checks: test_c4_default_priority
  • Taught on: Default request priority 0

A waiting line with a limit: A resource refuses a request when a set number of requests wait already, and a task that is refused balks.

API details

Subclass a resource to change its rules: A resource of your own class changes when a request is granted, how the waiting line is kept and what a request or release does.

API details

C5: Stores and Containers

Putting and getting items in a store and amounts in a container, with capacity limits or none, priority items of any orderable type, filtered gets, cancelled puts and gets, the order of puts and gets at the same time, reading what a store holds and its put and get queues, a put or get that the task does not wait for, and stores and containers of your own classes.

Bounded store put: A task adds an item to a store, and waits when the store is full.

API details
  • SimPy: simpy.resources.store.Store.put
  • YAML: put: {store: parcels} on a task
  • Builder: @app.task(put={"store": "parcels"})
  • Low-level: yield parcels.put(item)
  • Checks: test_c5_bounded_store_put
  • Taught on: Bounded store put

Bounded store get: A task takes an item from a store, and waits when the store is empty.

API details
  • SimPy: simpy.resources.store.Store.get
  • YAML: get: {store: parcels} on a task
  • Builder: @app.task(get={"store": "parcels"})
  • Low-level: item = yield parcels.get()
  • Checks: test_c5_bounded_store_get
  • Taught on: Bounded store get

Equal-time puts and gets: When items are added and taken at the same moment, waiting tasks are served in the same order as in SimPy.

API details
  • SimPy: simpy.resources.store.Store, one waiting get granted per trigger
  • YAML: get: {store: parcels} and put: {store: parcels} on tasks
  • Builder: @app.task(get={"store": "parcels"})
  • Low-level: yield parcels.put(item), item = yield parcels.get()
  • Checks: test_c5_equal_time_puts_and_gets_follow_simpy_store, test_c5_equal_time_puts_and_gets_as_tasks
  • Difference: While a get with a filter waits, the store grants as SimPy's FilterStore does, looking at every waiting get each time. Once no filtered get waits, it grants one get per trigger, as Store does, where FilterStore goes on to the next. With filtered and unfiltered gets in one queue, gets at the same instant can therefore resume in a different order than in FilterStore; each get receives the same item.
  • Taught on: Equal-time puts and gets

Bounded quantity put: A task adds an amount to a container, such as boxes to stock, and waits when there is no room.

API details
  • SimPy: simpy.resources.container.Container.put
  • YAML: put: {container: stock, amount: 2} on a task
  • Builder: @app.task(put={"container": "stock", "amount": 2})
  • Low-level: yield stock.put(2)
  • Checks: test_c5_bounded_quantity_put
  • Taught on: Bounded quantity put

Bounded quantity get: A task takes an amount from a container, and waits until there is enough.

API details
  • SimPy: simpy.resources.container.Container.get
  • YAML: get: {container: stock, amount: 1} on a task
  • Builder: @app.task(get={"container": "stock", "amount": 1})
  • Low-level: yield stock.get(1)
  • Checks: test_c5_bounded_quantity_get
  • Taught on: Bounded quantity get

Priority item storage: Items in a store come out by priority, the lowest number first, instead of in the order they went in.

API details
  • SimPy: simpy.resources.store.PriorityStore
  • YAML: stores.<name>.priority: true and a put with priority
  • Builder: add_store(..., priority=True) and put={"store": ..., "priority": ...}
  • Low-level: DynamicStore(..., priority=True) with simpy.PriorityItem
  • Checks: test_c5_priority_item_storage
  • Difference: Equal priorities come out in put order in the builder and YAML, which add a rising put order to the priority; a plain SimPy PriorityStore gives no order among equal priorities.
  • Taught on: Priority item storage

Filtered retrieval: A task takes only an item that matches a condition, such as a parcel for its own area.

API details
  • SimPy: simpy.resources.store.FilterStore.get(filter)
  • YAML: get: {store: parcels, filter: item.area == attr.area}
  • Builder: get={"store": "parcels", "filter": ...}, a condition or a function of the item
  • Low-level: parcels.get(filter=lambda item: ...)
  • Checks: test_c5_filtered_retrieval
  • Difference: A priority store takes no filter, because SimPy has no store that both ranks and filters.
  • Taught on: Filtered retrieval

Cancel store get: A task stops waiting for an item, for example when its patience runs out.

API details
  • SimPy: simpy.resources.base.Get.cancel()
  • YAML: patience on a task with a store get
  • Builder: @app.task(get={"store": ...}, patience=...)
  • Low-level: get.cancel()
  • Checks: test_c5_cancel_store_get
  • Taught on: Cancel store get

Cancel quantity get: A task stops waiting for an amount from a container.

API details
  • SimPy: simpy.resources.base.Get.cancel()
  • YAML: patience on a task with a container get
  • Builder: @app.task(get={"container": ...}, patience=...)
  • Low-level: get.cancel()
  • Checks: test_c5_cancel_quantity_get
  • Taught on: Cancel quantity get

Cancel store put: A task stops waiting for room in a full store.

API details
  • SimPy: simpy.resources.base.Put.cancel()
  • YAML: an interrupt with on_interrupt: abandon while the put waits
  • Builder: the same
  • Low-level: put.cancel()
  • Checks: test_c5_cancel_store_put
  • Taught on: Cancel store put

Cancel quantity put: A task stops waiting for room in a full container.

API details

Put and get queues of a store or container: A condition reads how many puts or gets wait at a store or container, and is checked again when a waiter joins or leaves.

API details

Custom store and container types: A store or container of your own class, which changes how an item or amount is taken in or out.

API details

Custom put and get queue types: A store or container whose waiting puts and gets are kept in a queue class of your own.

API details

Custom put and get event classes: A store or container that creates its own put and get events, such as a get that carries extra data.

API details
  • SimPy: resources.base.BaseResource.put, .get
  • YAML: stores.<name>.type: !python pkg.module.MyStore, with put and get set on the class
  • Builder: add_store(..., type=MyStore)
  • Low-level: class MyStore(StoreBase): put = BoundClass(MyPut)
  • Checks: test_c5_custom_event_classes
  • Taught on: Custom put and get event classes

A put or get the process does not wait for: A task makes a put or get and carries on at once, without waiting for it to be granted.

API details

Container with no capacity limit: A container that has no upper level, so a put never waits.

API details

Read a store's items: A condition counts the items in a store or asks whether an item is in it.

API details

Store with no capacity limit: A store that has no upper size, so a put never waits.

API details

Priorities of any orderable type: A priority store ranks items by a number, a string, a tuple or any value that can be ordered.

API details

C6: Run Control

Starting a run from a given time and state, reading the current time, ending it at a time, at an event or on a condition and reading the value that ended it, draining it, running it in segments, stepping through it one model event at a time, scheduling an event with a priority, an environment class of your own, and the pace of the run: its factor, strict pacing and syncing with the wall clock.

Initial time: The simulation clock starts at a given time instead of 0.

API details
  • SimPy: simpy.core.Environment(initial_time=...)
  • YAML: simulation.initial_time
  • Builder: SimulationContext(..., initial_time=100)
  • Low-level: DynamicRealtimeEnvironment(initial_time=100)
  • Checks: test_c6_initial_time
  • Taught on: Initial time

Initial resources: Each resource starts with a given number of units.

API details
  • SimPy: simpy.resources.resource.Resource(capacity=...)
  • YAML: resources.<name>.current_cap
  • Builder: add_resource(name, current_cap=2, ...)
  • Low-level: CapacityConfig(current_cap=2, ...)
  • Checks: test_c6_initial_resources
  • Taught on: Initial resources

Initial inventory: Containers start with a given level, and stores with given items.

API details
  • SimPy: Container(init=...); items in a Store
  • YAML: containers.<name>.level and stores.<name>.items
  • Builder: add_container(..., level=6) and add_store(..., items=[...])
  • Low-level: ContainerConfig(level=6) and DynamicStore(..., items=[...])
  • Checks: test_c6_initial_inventory
  • Taught on: Initial inventory

Initial entity state: Each arriving entity, such as an order, starts with given attributes.

API details

Stop at a time: The run ends at a given simulation time.

API details

Stop at an event: The run ends when a given event happens, such as a closing signal, even before its end time.

API details
  • SimPy: simpy.core.Environment.run(until=event)
  • YAML: run.stop: {signal: closing} beside run.until
  • Builder: app.run(until=..., stop={"signal": "closing"}), a signal's name, or a function returning an event
  • Low-level: env.run(until=event)
  • Checks: test_c6_stop_at_an_event, test_c6_stop_at_an_event_at_the_same_instant_as_until
  • Difference: When the stop and until fall at the same time, until ends the run first, as SimPy's own until would.
  • Taught on: Stop at an event

Explicit stop condition: The run ends as soon as a condition on the model's state holds.

API details
  • SimPy: env.run(until=event) with a process that succeeds the event when the condition holds
  • YAML: run.stop: {condition: level.stock >= 5}
  • Builder: app.run(until=..., stop={"condition": "..."})
  • Low-level: env.watch_changes(check) and env.run(until=event)
  • Checks: test_c6_explicit_stop_condition
  • Taught on: Explicit stop condition

Stop arrivals and drain: New arrivals stop, and the run goes on until everything already in the model has finished.

API details

Step one event: The run advances one event at a time, so a script can watch each step.

API details
  • SimPy: simpy.core.Environment.step()
  • YAML: SimulationContext.from_yaml(path).start(), then .step()
  • Builder: app.start(), app.step(), app.close()
  • Low-level: env.step()
  • Checks: test_c6_step_one_event
  • Difference: A blueprint has no stepping key; its context, from from_yaml, steps as a builder context does.
  • Taught on: Step one event

Inspect next event time: A script reads when the next event will happen without running it.

API details

A step processes only the model's events: step() and peek() count the events of the model, not the statistics, flush, lag, ingress and watcher processes that desflow adds, so a script steps a model as it would step it in SimPy. Inside run() the step processes every event, desflow's own included, because SimPy's run() calls step() for each event in its one queue.

API details

Value of a run stopped at an event: run() returns the value of the event that stopped it, and raises the exception of a stop that failed. The value is a signal's payload for a stop on a signal, True for a condition, and the event's own value for a function. It is None when until or a drain ends the run, or when until and the stop are due together. A stop on a signal that fails raises SignalFailed, the exception the failed event carries, which is what SimPy raises. The run closes before it returns, so the sinks have their records.

API details
  • SimPy: simpy.core.Environment.run(until=event)
  • YAML: SimulationContext.from_yaml(path).run() returns the value; desflow run exits with an error when the stop failed
  • Builder: app.run(until=..., stop=...) and app.finish() return the value
  • Low-level: env.run(until=event)
  • Checks: test_c6_run_value
  • Taught on: Value of a run stopped at an event

Current time: t is the simulation clock, read in a condition or an expression, so a route or a balk can depend on the current time.

API details
  • SimPy: core.Environment.now
  • YAML: t in a condition, such as t < 110
  • Builder: context.env.now
  • Low-level: env.now
  • Checks: test_c6_current_time
  • Taught on: Current time

Schedule an event with a priority: env.schedule(event, priority, delay) puts an event on the queue, and a fire action with after fires a one-shot signal that many seconds from now, as SimPy's schedule does.

API details

Urgent and normal event priorities: An urgent event is processed before the normal events at its time, as SimPy's URGENT and NORMAL order them; urgent: true on a fire action resumes the waiters first.

API details

Run in segments: advance(until) runs a started run to a time or an event and returns with the run open, and finish() runs on to the end; the segments give the records of one run. resume_pacing() paces again from now after a pause.

API details

Subclass the environment: A subclass of DynamicRealtimeEnvironment runs the model, so an override of step or schedule sees its events, as a subclass of SimPy's Environment does.

API details

Real-time factor: factor sets how many real seconds one simulation second takes, so a run paces to the wall clock, as SimPy's RealtimeEnvironment does.

API details
  • SimPy: rt.RealtimeEnvironment(factor)
  • YAML: simulation.factor
  • Builder: SimulationContext(factor=)
  • Low-level: DynamicRealtimeEnvironment(factor=)
  • Checks: test_c6_real_time_factor
  • Taught on: Real-time factor

Strict pacing: With strict on, a paced step more than factor seconds behind the wall clock raises RuntimeError, as SimPy's RealtimeEnvironment(strict=True) does.

API details

Strict by default: The low-level environment is strict unless strict=False is passed, as SimPy's is; the builder and YAML default to off.

API details
  • SimPy: rt.RealtimeEnvironment(strict=True)
  • YAML: simulation.strict, default false
  • Builder: SimulationContext(strict=False), the default
  • Low-level: DynamicRealtimeEnvironment(strict=True), the default
  • Checks: test_c6_strict_default
  • Taught on: Strict by default

Sync with the wall clock: sync() resets only the wall-clock anchor (real_start), as SimPy's does, so strict stops counting the time before it. Going on after a pause with the pace kept needs resume_pacing(), which also moves the simulation anchor.

API details
  • SimPy: rt.RealtimeEnvironment.sync
  • YAML: not in YAML: app.start(), then app.env.sync()
  • Builder: app.env.sync() after start(); the builder syncs once when it builds a strict run
  • Low-level: env.sync()
  • Checks: test_c6_sync_catches_up
  • Taught on: Sync with the wall clock

C7: Patience

Customers who give up after waiting too long (reneging) and customers who do not join a queue that is too long (balking).

Reneging: A customer leaves the queue after waiting longer than their patience.

API details

Balking: A customer does not join the queue at all when a condition holds, such as the queue being too long.

API details

S1: Breakdowns

A resource that fails and is repaired, with the work it interrupts.

Breakdown and repair: A resource fails from time to time and is repaired, interrupting the work it was doing.

API details

S2: Schedules

A schedule of times at which an arrival rate or a capacity changes during a run.

Finite schedules: Arrival rates or capacities change at set times during the run.

API details
  • SimPy: a process that changes values at set times, started before the others
  • YAML: scenario steps with at, path and value
  • Builder: add_scenario(steps) or add_step(at, path=..., value=...)
  • Low-level: run_scenario(env, steps, until=...)
  • Checks: test_s2_step_at_an_arrival_time
  • Difference: A step applies before every other event at its time, so the arrival gap drawn then uses the new value.
  • Taught on: Finite schedules

S3: Replay

Arrivals read from a local CSV or Parquet file instead of drawn at random.

Local replay: Arrivals come from rows in a local CSV or Parquet file instead of a random distribution.

API details

Differences Kept by Design

  • A run with no until (SimPy: core.Environment.run() returns when no event is left): A run with a sink or an ingress connector and no until runs for ever, because a live run has no end: the connectors keep events coming. Give it an until, with a drain when it should finish its work, or a stop that is sure to happen, or stop it with Ctrl+C.
  • Strict pacing at factor 0 (SimPy: rt.RealtimeEnvironment(factor=0, strict=True) raises once the wall clock has moved): An unpaced run has no wall clock to fall behind, so SimPy's check at factor 0 would raise on the first step of every unpaced script. strict is never checked at factor 0, also before go_live_at. Taught on: Strict pacing at factor 0
  • Capacity 0 (SimPy: resources.resource.Resource(capacity=0) raises ValueError, and so do Store and Container): A resource, store or container may have a capacity of 0, at the start or later, because its capacity changes during a run: a shift with no packers, a shelf that is closed. At capacity 0 a resource grants no request, and a store or container accepts no put, until the capacity rises; a get still takes what a store or container holds.