Models, Trials, and env

A sim.Model describes the shape of one simulated world. It names the parameters supplied by an experiment, the outputs collected after each trial, the mutable state inside a trial, and the passive simulation entities that processes use to interact.

import cimba.sim as sim

import cimba.random as random

class Clinic(sim.Model):
    arrival_rate: sim.Param
    mean_service: sim.Param
    completed: sim.Output
    avg_waiting: sim.Output
    waiting_room: sim.Queue
    doctor: sim.Resource
    served: sim.State

model = Clinic("clinic")

The class is not one patient or one run. It is the declaration for every trial that the experiment will create. Each trial receives its own arrival_rate, mean_service, outputs, queue, resource, and state counter.

The trial record

Process functions receive the current trial as env:

@model.process
def arrivals(env: Clinic):
    while True:
        sim.hold(random.exponential(1.0 / env.arrival_rate))
        env.waiting_room.put(1)

The env object is trial-local. Reading env.arrival_rate reads the value for this trial, and env.waiting_room is the queue handle created for this trial. Another replication or parameter combination gets a different record and different native entities.

That separation is the reason Cimba Python can run experiments in parallel. One trial does not share simulation state with another trial.

Field roles

sim.Param fields are inputs. They are set by model.experiment(...) and may be scalars or swept arrays.

sim.Output fields are results. They start as missing values and are usually written by a collector after the trial finishes.

sim.State and sim.FloatState fields are mutable trial-local variables. Use them for counters, flags, and numeric state that should reset for every trial.

Entity fields such as sim.Queue, sim.Resource, sim.Pool, sim.Store, sim.Condition, and sim.Dataset are handles to native simulation objects. Declare them on the model so Cimba Python can create, record, and destroy the right objects for every trial.

A useful rule of thumb

Put model state on env when a process needs it during the simulated run. Keep ordinary Python analysis code outside the process body and use it before or after exp.run().

This keeps the compiled simulation path focused on simulation behavior, while the Python side remains free for preparing inputs, summarizing outputs, and plotting results.