Expand description
ODEM-rs is an Object-based Discrete-Event Modeling library for Rust,
enabling Monte Carlo–style simulation models via async
/await
. It
provides a flexible, extensible framework where concurrent agents
represent simulation actors.
§Overview
- Process-Based: Models simulation entities as asynchronous agents with isolated state and behavior, while allowing internal concurrency via jobs.
- Discrete-Event: Schedules and executes events at distinct points in model time, skipping from event to event rather than at a fixed rate.
- Deterministic: Results of simulation runs are fully portable due to
the use of deterministic pseudo-random-number generators (PRNG) in
combination with cooperative concurrency. This also enables the use of
!Send
closures while still allowing independent simulation runs to be parallelized safely. - Adaptable: Exposes library traits to tailor simulators and data structures to specific simulation models.
- Sound: Validated with Miri to detect undefined behavior and tests compliance with stacked borrow rules.
- Batteries Included: Provides essential components for simulation:
- Executor and event calendar for scheduling
- Concurrency primitives for internal synchronization
- Synchronization structures: channels and control variables
- PRNG generator streams and random variables
- Automatically garbage-collected object pools
- Ecosystem: Integrates seamlessly with
tracing
,uom
, andrand
for logging, unit-of-measure modeling, and random number generation. no_std
/no_alloc
: Supports execution in bare-metal environments even without a memory allocator, albeit with fewer dynamic-collection-based features.
§Execution Principle
The core of ODEM-rs is its event-driven execution model, which ensures that simulation models evolve incrementally in a structured manner. At any given model time, all continuations (representing agents and jobs) that are scheduled for execution are processed instantaneously in terms of model time before the clock is advanced. Thus, the passing of model time is explicit, allowing for deterministic execution. The execution order is determined by:
- Model time – Monotonically increasing time preserves cause-and-effect relationships between processes.
- Rank (between agents) – Determines execution priority among agents.
- Precedence (between jobs) – Ensures the correct sequencing of dependent jobs within one agent.
- Insertion Order – Resolves ties between jobs with equal precedence by executing them in the order they were scheduled.
Once all active continuations have been executed at a given model time, the simulation time advances to the next scheduled event, skipping idle periods where no actions occur.
This execution model is managed by an event calendar, which is a part of
the async
/await
runtime executor. The executor schedules and processes
continuations in deterministic order as described above. This allows ODEM-rs
to seamlessly integrate with Rust’s async runtime model, leveraging native
concurrency while maintaining strict event sequencing.
§Introductory Example
§Barbershop Simulation
To get you started quickly, here is an example scenario featuring a simple barbershop model. Customers arrive at random intervals and request service from a single barber. Once a customer arrives, they occupy the barber for a random duration simulating the haircut, then release the barber for the next customer. The simulation will run for 12 hours.
use odem_rs::{prelude::*, sync::facility::Facility};
use core::pin::pin;
#[derive(Config, Default)]
struct Barbershop {
barber: Facility,
rng_stream: RngStream,
}
struct Customer(f64);
impl Behavior<Barbershop> for Customer {
type Output = ();
async fn actions(&self, sim: &Sim<Barbershop>) {
// Acquire the barber
let chair = sim.global().barber.seize().await;
// Simulate the haircut duration
sim.advance(self.0).await;
// Release the barber for the next customer
chair.release();
}
}
#[odem_rs::main]
async fn main(sim: &Sim<Barbershop>) {
// Process responsible for generating customers
sim.fork(async {
let mut rng_a = sim.global().rng_stream.rng();
let mut rng_s = sim.global().rng_stream.rng();
let pool = pin!(Pool::dynamic());
loop {
// Wait a random time before creating the next customer
let arrival = rng_a.random_range(12.0..24.0);
let service = rng_s.random_range(12.0..18.0);
sim.advance(arrival).await;
sim.activate(pool.alloc(Agent::new(Customer(service))));
}
})
// Run the barbershop for 12 hours
.or(sim.advance(12.0 * 60.0))
.await;
}
More examples can be found in the examples
directory.
§Crate Organization
ODEM-rs is structured into multiple crates for modularity, which can be accessed from the root crate as follows:
Crate Name | Description |
---|---|
odem_rs | Entry crate providing access to the core simulation framework. |
odem_rs::core | Defines the foundational structures and traits for event-driven modeling. |
odem_rs::sync | Provides synchronization and communication primitives for agent-job interaction. |
odem_rs::util | Utility crate with pools, PRNG streams, statistics, and physical quantities. |
§Contributors & Acknowledgments
Inspired by the Scandinavian approach to discrete-event simulation as exemplified - among others - through Simula, GPSS, SLX, ODEMx, ODEM-rs refines these concepts in a Rust-centric framework.
ODEM-rs is part of ongoing doctoral research and has benefited from contributions by:
- Lukas Markeffsky, with whom I enjoyed in-depth discussions on Rust’s type system and whose incredible insight and sharp mind helped a lot to find and resolve soundness issues in addition to improving the ergonomics of the library.
- Paula Wiesner, with whom I performed early prototyping of agent-based approaches and who is still interested in the progress of this project, despite having successfully moved past academia. I appreciate the enthusiasm!
Explore the modules and traits to uncover the library’s full potential.
Re-exports§
pub extern crate uom;
pub use deps::odem_rs_core as core;
pub use deps::odem_rs_sync as sync;
pub use deps::odem_rs_util as util;
Modules§
- error
- This module contains the definition of the combined error-type of the simulation library.
- prelude
- A module for re-exports of simulation-library concepts that are essential for most simulation models.
Attribute Macros§
- main
- Generates a synchronous main function that sets up a simulator, runs the provided asynchronous simulation logic, and handles the results.