Concepts¶
A tour of the four ideas that everything else in SimWeave is built on.
1. The SimEnvironment is the clock¶
A SimEnvironment owns the simulation clock, the priority event queue,
and a list of registered entities. You pick a fixed tick dt and an
end time, then call env.run():
env = sw.SimEnvironment(dt=0.1, end=100.0)
env.register(some_entity)
env.run() # advances until env.clock.now >= end
env.run(until=50.0) # or advance only as far as you ask
Every tick, the environment:
- fires any scheduled events whose time is ≤
now - calls
entity.tick(env)on every registered entity, in registration order - advances
nowbydt
2. Everything ticking is an Entity¶
Queues, services, arrival generators, agents, warehouses and the viz
recorders all subclass Entity. To make a new domain object, override
the lifecycle hooks you care about:
class HeartbeatLogger(sw.Entity):
def __init__(self, name="hb"):
super().__init__(name=name)
self.beats = 0
def on_register(self, env):
# Called once when the entity joins an environment.
self.beats = 0
def tick(self, env):
# Called every dt while now < end.
self.beats += 1
Registration order matters: an entity registered later sees the post-tick state of one registered earlier. This is how the viz recorders work — register them after the entity they observe so they snapshot the post-tick state, not the pre-tick state.
3. Continuous systems live alongside discrete ones¶
simweave.continuous.simulate integrates a DynamicSystem with a
fixed-step solver (RK4 by default; the [optim] extra unlocks SciPy's
adaptive solvers for stiff systems). The result is a SimulationResult
with .time, .state, .state_labels, .system_name.
You can also wrap a continuous system as a ContinuousProcess entity
and register it on a SimEnvironment, in which case the integrator
steps once per simulation tick and exposes the latest state via the
process attribute. This is how hybrid sims (continuous physics +
discrete events) are wired up.
4. Replication is a first-class citizen¶
run_monte_carlo(scenario_fn, n_runs, seed) (and the parallel
run_batched_mc) return an MCResult carrying every replicate's
output and the seeds used. Almost every plot helper accepts an
MCResult, a raw (n_runs, n_time) ndarray, or a (times, samples)
tuple — you can bring your own ensemble if you have one already.
Putting it together¶
A typical SimWeave program looks like this:
- Build domain entities (queues, services, agents, warehouses).
- Build any recorders you want for time-series observation.
- Construct a
SimEnvironment, register everything, callrun(). - Read
entity.history/recorder.timesetc. or hand them to a plot helper for visualisation. - Wrap steps 1–4 in a function taking a seed, and hand it to
run_monte_carlowhen you want an ensemble.