← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / simpy / references / real-time.md

Real-time simulation

Verified 2026-07-23 against SimPy 4.1.2.

simpy.rt.RealtimeEnvironment retains the Event/Process API but delays event processing so simulation time tracks wall-clock time.

from simpy.rt import RealtimeEnvironment

env = RealtimeEnvironment(
    initial_time=0,
    factor=0.1,
    strict=True,
)

Parameters

strict=False suppresses deadline failure. It permits drift; it does not make an overloaded simulation accurately synchronized.

Minimal bounded example

import time
from simpy.rt import RealtimeEnvironment

def ticker(env, count):
    for index in range(count):
        before = time.monotonic()
        yield env.timeout(1)
        elapsed = time.monotonic() - before
        print(index, env.now, elapsed)

env = RealtimeEnvironment(factor=0.05, strict=True)
process = env.process(ticker(env, count=3))
env.run(until=process)

Use time.monotonic() for elapsed wall time. Calendar time can jump.

Strict-mode behavior

Callbacks and generator code execute synchronously on the simulation thread. Slow computation, blocking I/O, logging, garbage collection, OS scheduling, and loaded CI hosts all consume the real-time budget.

import time
import simpy.rt

def slow(env):
    time.sleep(0.02)
    yield env.timeout(1)

env = simpy.rt.RealtimeEnvironment(factor=0.01, strict=True)
env.process(slow(env))
try:
    env.run()
except RuntimeError:
    print("simulation missed its real-time budget")

This sleep() intentionally demonstrates a missed deadline. Do not put blocking sleep in ordinary SimPy process logic to represent simulated delay; use env.timeout().

Appropriate use

Real-time execution is useful when wall-clock synchronization is intrinsic:

It is usually inappropriate for Monte Carlo replications: normal Environment runs faster, is less affected by host load, and preserves the same model-time logic.

External I/O boundary

SimPy itself is single-threaded and does not make external I/O asynchronous. Integrating devices or services requires an explicit adapter and failure model. Document:

The bundled skill CLIs intentionally provide no device, network, plugin, or external-service integration.

Drift measurement

Choose a real origin and compare expected elapsed wall time with actual monotonic elapsed time:

import time

origin_real = time.monotonic()
origin_sim = env.now

def drift_seconds(env, factor):
    expected = (env.now - origin_sim) * factor
    actual = time.monotonic() - origin_real
    return actual - expected

Define sign, sampling instant, percentile, maximum allowed lag, and platform before testing. A mean near zero can hide large deadline misses.

Testing strategy

  1. Test model logic with normal Environment.
  2. Test deterministic ordering and interrupts independently of wall time.
  3. Keep real-time tests short and separately marked.
  4. Use time.monotonic() and broad platform-aware bounds.
  5. Test both strict=True deadline detection and intentional strict=False lag.
  6. Avoid exact-duration assertions.
  7. Record Python/SimPy version, OS, architecture, host load assumptions, and factor.

Example tolerant assertion:

start = time.monotonic()
env = RealtimeEnvironment(factor=0.02, strict=False)
env.run(until=env.timeout(2))
elapsed = time.monotonic() - start
assert elapsed >= 0.02
assert elapsed < 1.0

The upper bound is deliberately generous; tune it for controlled hardware, not a busy shared runner.

Boundaries and stopping

Real-time environments inherit Environment.run() semantics:

Always bound real-time runs. A tiny factor combined with a huge event count can still consume substantial CPU, and a huge factor can make a short model wait for a long wall duration.

Limitations

Sources

See sources.md for the 4.1.2 real-time topical guide and simpy.rt API.