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
!Sendclosures 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, andrandfor 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;
use rand::RngExt;
#[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().with(Agent::new));
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(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. |
§Feature Flags
Odem-rs uses Cargo feature flags to control optional functionality. All
flags listed as “default” are enabled when you add odem-rs as a
dependency without further configuration. To start from a minimal base,
set default-features = false and enable only what you need.
| Feature | Default | Description |
|---|---|---|
std | yes | Standard library support (implies alloc). |
alloc | yes | Heap allocation for dynamic data structures. |
tracing | yes | Structured instrumentation via the tracing crate. |
debug-tracing | no | Debug-level trace output for internal state machines. |
uom | yes | Type-safe SI quantities for model time. |
rand_pcg | yes | PCG-family pseudo-random number generators. |
rand_xoshiro | no | XoShiRo-family pseudo-random number generators. |
rayon | yes | Parallel iteration for independent simulation runs. |
libm | no | Software math functions for no_std environments. |
§Environment
std: Enables standard library support and impliesalloc. Disable this for bare-metal or WebAssembly targets that do not provide a standard library.alloc: Enables heap-allocated data structures such as dynamic channels, dynamic object pools, and per-type agent identifiers. Automatically activated bystd, but can be enabled independently on targets that provide an allocator without the full standard library.libm: Provides software implementations of math functions (e.g.sqrt) via thelibmcrate. Only needed inno_stdenvironments where hardware floating-point or standard-library math is unavailable. Whenstdis active, math functions from the standard library are used instead.
§Integrations
tracing: Integrates with thetracingecosystem to provide structured, span-based instrumentation of agents and continuations. Useful for debugging simulation models and understanding execution flow.debug-tracing: Emits additional debug-level trace events for finite-state machine transitions and channel operations. Atracingsubscriber must be configured for these events to be visible.uom: Enables theuomcrate for modeling time with type-safe SI quantities. With this feature, model time can be expressed using units likehour::new(12.0)orsecond::new(0.5)rather than raw numeric values.
§Random Number Generators
The rand crate is always enabled. The following features control which
PRNG backends are available:
rand_pcg: Provides PCG-family generators (Pcg32,Pcg64Dxsm, etc.) and sets theDefaultRngtype alias to a PCG variant matching the target’s pointer width.rand_xoshiro: Provides XoShiRo-family generators (Xoshiro256PlusPlus, etc.) as an alternative backend.
§Parallelism
rayon: Enables parallel iteration support via therayoncrate, allowing independent simulation replications to run across multiple threads.
§Re-exported Crates
Odem-rs re-exports its ecosystem dependencies so that simulation models can
use version-compatible crates without adding them as separate dependencies.
Each re-export is available as a module under odem_rs::, e.g.
odem_rs::rand or odem_rs::uom.
| Crate | Version | Feature |
|---|---|---|
rand | 0.10 | always |
intrusive_collections | 0.10 | always |
uom | 0.38 | uom |
tracing | 0.1 | tracing |
rayon | 1 | rayon |
typed_arena | 2 | alloc |
rand_pcg | 0.10 | rand_pcg |
rand_xoshiro | 0.8 | rand_xoshiro |
This means you can depend on odem-rs alone and use its re-exports:
use odem_rs::rand; // rand 0.10
use odem_rs::uom; // uom 0.38 (requires feature "uom")
use odem_rs::rayon; // rayon 1 (requires feature "rayon")If you need additional items from these crates (e.g. rand_distr), add
them to your Cargo.toml with a compatible version to avoid duplication:
[dependencies]
odem-rs = "0.3"
rand_distr = "0.6" # compatible with rand 0.10§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 use odem_rs_core as core;pub use odem_rs_sync as sync;pub use odem_rs_util as util;pub use uom;uompub use tracing;tracingpub use rayon;rayonpub use typed_arena;allocpub use rand_pcg;rand_pcgpub use rand_xoshiro;rand_xoshiropub use intrusive_collections;pub use rand;
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.