Crate odem_rs

Source
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:
  • Ecosystem: Integrates seamlessly with tracing, uom, and rand 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 NameDescription
odem_rsEntry crate providing access to the core simulation framework.
odem_rs::coreDefines the foundational structures and traits for event-driven modeling.
odem_rs::syncProvides synchronization and communication primitives for agent-job interaction.
odem_rs::utilUtility 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.