Datasets, Summaries and Reporting
sim.Dataset is a trial-local list of untimed numeric observations. Add one
observation with env.waits.add(value) each time the measured event happens.
At collection time, call methods such as env.waits.mean() or
env.waits.quantile(0.95) to turn that list into the metric you want to
report for the current replication. Time-weighted histories are attached to
simulation entities automatically and read back with env.<entity>.history(),
which returns a time-series handle for the same method-style summary calls.
Datasets
Declare datasets as model or component fields:
class Clinic(sim.Model):
waits: sim.Dataset
avg_wait: sim.Output
p95_wait: sim.Output
Inside process code, each add() appends one sample to the current trial’s
dataset:
env.waits.add(wait_time)
At collection time, summarize that trial-local list into the outputs you care about:
@model.collect
def collect_stats(env: Clinic):
env.avg_wait = env.waits.mean()
env.p95_wait = env.waits.quantile(0.95)
A dataset field is created separately for every trial row. If an experiment
uses replications=50, then env.waits is 50 independent datasets, not
one dataset shared by all replications. Each replication records its own
samples. The model-level @model.collect callback, or a component-level
@sim.collect callback, writes one output value per replication for each
metric you choose. For example, exp["avg_wait"] contains 50
per-replication averages, while exp["p95_wait"] contains 50
per-replication percentile estimates. Use exp.summary() or ordinary Python
after exp.run() to summarize those output values across replications.
Datasets are reset when the measurement window opens after warmup. Values added before warmup are discarded, so collectors see the samples for the measured part of that trial.
Datasets support method-style compiled calls: add(), count(),
mean(), min(), max(), std(), median(), quantile(),
print(), print_file(), fivenum(), fivenum_file(),
histogram(), histogram_file(), correlogram(),
correlogram_file(), pacf_correlogram(), and
pacf_correlogram_file().
Time series
Unlike datasets, a time series is not something you populate yourself. Every
stateful native entity – sim.Queue, sim.Resource, sim.Pool,
sim.Store, and each priority queue in sim.PQueues – has its value
(queue length, resource holders, pool holders, store length) recorded by the
engine automatically for as long as the trial runs. Calling .history() on the entity itself hands back that recording
as a time-series handle:
class Clinic(sim.Model):
waiting_room: sim.Queue
nurse: sim.Resource
mean_queue_len: sim.Output
@model.collect
def collect_stats(env: Clinic):
env.mean_queue_len = env.waiting_room.history().mean()
.history() is available on every field kind that records one:
sim.Queue, sim.Resource, sim.Pool, sim.Store, and indexed
elements of sim.PQueues (env.pqs[i].history()). It works the same way
inside component methods (self.waiting_room.history().mean()).
The handle returned by .history() summarizes and reports
time-weighted statistics: a value the queue held for a long stretch
counts more than one held only briefly, which is why these are separate
.history()... methods rather than reusing the dataset methods above
(whose statistics are unweighted, one sample = one vote). The supported
chained methods are:
count(), min(), max(), mean(), std(), median(),
print(), print_file(), fivenum(), fivenum_file(),
histogram(), histogram_file(), correlogram(),
correlogram_file(), pacf_correlogram(), pacf_correlogram_file().
There is no free-function form: .history() is the only way to reach an
entity’s recorded time series. Report several statistics off the same entity
by chaining .history() again for each one (env.q.history().mean(),
env.q.history().std(), …) – each call is a cheap native lookup, not a
new recording.
For text reports and text-mode plots, the no-suffix methods above (both
dataset and .history() methods) print to stdout for console and notebook
use; the *_file() variants write to a path handle created with
sim.log_text() instead. A dataset report is produced from the current
trial’s dataset. In multi-replication experiments, writing every replication
to the same path can interleave or overwrite raw samples depending on the
append flag and parallel execution. These methods are most useful in
single-trial debugging; scalar sim.Output fields are usually better for
large parallel experiments.
Model.experiment(..., warmup=..., duration=...) controls the measurement
window: warmup lets the model reach a representative state before summaries are
collected.