Utilities API
The builder functions are convenience wrappers that wire together an Environment, a
ShopFloor, a Router, and a release policy into a ready-to-run system, so you can spin up
a complete simulation in a single call. The distribution helpers sample processing times, with
RunningStats providing an online (Welford) accumulator for mean and variance. The log sinks
write the environment's log events (and, at DEBUG, its domain events) as plain text, JSON lines,
SQLite rows or an in-memory history.
Builders
build_immediate_release_system
build_immediate_release_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
priority_policies: Callable[..., float] | None = None,
collect_workload: bool = False,
collect_time_series: bool = False,
retain_job_history: bool = False,
) -> BuiltSystem[None]
Build an immediate release (push) system with no workload control.
Creates a simple push system where jobs enter the shopfloor immediately upon arrival without any release control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
priority_policies
|
Callable[..., float] | None
|
Optional callable used to assign job priorities at servers. |
None
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
collect_time_series
|
bool
|
If True, attach a |
False
|
retain_job_history
|
bool
|
If True, servers retain completed job references. |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[None]
|
|
BuiltSystem[None]
|
and |
BuiltSystem[None]
|
|
Example
env = Environment() _, servers, shop_floor, router, _ = build_immediate_release_system(env=env) env.run(until=1000) print(f"Jobs completed: {len(shop_floor.jobs_done)}")
build_focus_system
build_focus_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
focus_weights: tuple[
float, float, float, float, float
] = (0.25, 0.25, 0.25, 0.25, 0.0),
collect_workload: bool = False,
) -> BuiltSystem[None]
Build an immediate-release (push) system that dispatches with FOCUS.
Jobs enter the shopfloor on arrival (no release control); queue ordering
at every server uses the FOCUS self-establishing rule (Kasper, Land,
Teunter 2023, Omega 114, 102726) via FocusPriorityRule. Use this to
study FOCUS as a standalone dispatching rule, independent of DRACO.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
focus_weights
|
tuple[float, float, float, float, float]
|
FOCUS mechanism weights |
(0.25, 0.25, 0.25, 0.25, 0.0)
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[None]
|
|
BuiltSystem[None]
|
|
BuiltSystem[None]
|
|
BuiltSystem[None]
|
|
Example
env = Environment() _, servers, shop_floor, router, _ = build_focus_system(env=env) env.run(until=1000)
References
Kasper, A., Land, M., Teunter, R. (2023). Towards system state dispatching in high-variety manufacturing. Omega, 114, 102726. https://doi.org/10.1016/j.omega.2022.102726
build_lumscor_system
build_lumscor_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
check_timeout: float,
wl_norm_level: float,
allowance_factor: int,
collect_workload: bool = False,
) -> BuiltSystem[LumsCor]
Build a LumsCor (load-based) pull system with workload control.
Creates a pull system using LUMS-COR (Land's Upper limit for Make-Span with CORrected workload) release policy. Jobs are held in a Pre-Shop Pool and released only when server workloads stay below configured norms.
Uses CorrectedWIPStrategy which discounts downstream workload by position, and includes starvation avoidance triggers for idle servers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
check_timeout
|
float
|
Time between pool release checks. |
required |
wl_norm_level
|
float
|
Workload norm threshold for each server. Jobs are released only if adding them keeps corrected WIP at or below this level. |
required |
allowance_factor
|
int
|
Buffer time per server for due date calculation. Higher values result in earlier (more conservative) releases. |
required |
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[LumsCor]
|
|
BuiltSystem[LumsCor]
|
instance (inspect |
BuiltSystem[LumsCor]
|
|
Example
env = Environment() psp, servers, shop_floor, router, policy = build_lumscor_system( ... env=env, check_timeout=10.0, wl_norm_level=5.0, allowance_factor=2 ... ) env.run(until=1000)
References
Land, M.J. (2006). Parameters and sensitivity in workload control. International Journal of Production Economics, 104(2), 625-638. https://doi.org/10.1016/j.ijpe.2005.03.001
build_slar_system
build_slar_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
allowance_factor: float,
collect_workload: bool = False,
) -> BuiltSystem[Slar]
Build a SLAR (Superfluous Load Avoidance Release) pull system.
Creates a pull system using SLAR release policy based on planned slack times (PST). Jobs are released from the Pre-Shop Pool when servers risk starvation or when urgent jobs need insertion.
Release triggers
- Starvation avoidance: When queue is empty or has one job, release the job with earliest planned start time.
- Urgent job insertion: When all queued jobs are non-urgent, insert the most urgent job with shortest processing time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
allowance_factor
|
float
|
Slack allowance per operation (parameter 'k' in paper). Higher values provide more buffer time per server. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[Slar]
|
|
BuiltSystem[Slar]
|
Unpacks as |
Example
env = Environment() psp, servers, shop_floor, router, policy = build_slar_system( ... env=env, allowance_factor=3.0 ... ) env.run(until=1000)
References
Land, M.J. & Gaalman, G.J.C. (1998). The performance of workload control concepts in job shops: Improving the release method. International Journal of Production Economics, 56-57, 347-364. https://doi.org/10.1016/S0925-5273(98)00052-8
build_slar_limit_system
build_slar_limit_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
allowance_factor: float,
wl_norm_level: float,
collect_workload: bool = False,
) -> BuiltSystem[SlarLimit]
Build a SLAR-Limit (load-bounded SLAR) pull system.
Creates a pull system using SLAR-Limit release policy. SLAR-Limit extends classic SLAR by adding a workload-norm limit to the urgent insertion branch: urgent PSP candidates are iterated in ascending SPT order and the first whose corrected workload contribution PT/(i+1) fits all server norms is released. If none fits, the policy falls back to the standard SLAR postponed-starvation branch.
Uses CorrectedWIPStrategy on the shopfloor (required by the norm check).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
allowance_factor
|
float
|
Slack allowance per operation (parameter 'k' in the SLAR paper). |
required |
wl_norm_level
|
float
|
Workload norm threshold applied uniformly to every server. An urgent PSP candidate is released only if adding its corrected contribution keeps every server in its routing at or below this level. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[SlarLimit]
|
|
BuiltSystem[SlarLimit]
|
instance (inspect |
BuiltSystem[SlarLimit]
|
|
Example
env = Environment() psp, servers, shop_floor, router, policy = build_slar_limit_system( ... env=env, allowance_factor=3.0, wl_norm_level=5.0 ... ) env.run(until=1000)
References
Thürer, M. & Stevenson, M. (2021). Improving superfluous load avoidance release (SLAR): A new load-based SLAR mechanism. International Journal of Production Economics, 231, 107881. https://doi.org/10.1016/j.ijpe.2020.107881
build_draco_system
build_draco_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
wip_target: int,
loop_target: int,
focus_weights: tuple[
float, float, float, float, float
] = (0.25, 0.25, 0.25, 0.25, 0.0),
total_impact_weights: tuple[float, float, float] = (
0.25,
0.25,
0.5,
),
collect_workload: bool = False,
) -> BuiltSystem[Draco]
Build a DRACO (non-hierarchical WIP control) pull system.
Creates a pull system using the DRACO policy (Kasper, Land, Teunter
2023, IJPE 257, 108768) that merges release, authorization, and
dispatching into a single per-server decision. On every job
completion at any server k, DRACO scores every candidate in
Q_k ∪ P_k by a weighted total impact w^R·R + w^A·A + w^D·D
and selects the maximum. The winner is dispatched to k next.
Wiring (performed by Draco.__init__):
- priority_policy: Draco.priority_policy (queue-side
DRACO score; returns -inf one-shot for forced PSP winners
to preserve strict paper semantics).
- shop_floor.on_processing_end: Draco.decide_next_job.
- psp.on_arrival(starvation_avoidance): prevents idle-server
starvation when a new arrival's first server is idle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
wip_target
|
int
|
Target shop WIP |
required |
loop_target
|
int
|
Target overlapping loop |
required |
focus_weights
|
tuple[float, float, float, float, float]
|
FOCUS mechanism weights |
(0.25, 0.25, 0.25, 0.25, 0.0)
|
total_impact_weights
|
tuple[float, float, float]
|
|
(0.25, 0.25, 0.5)
|
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[Draco]
|
|
BuiltSystem[Draco]
|
Unpacks as |
Example
env = Environment() psp, servers, shop_floor, router, policy = build_draco_system( ... env=env, wip_target=8, loop_target=4 ... ) env.run(until=1000)
References
Kasper, A., Land, M., Teunter, R. (2023). Non-hierarchical work-in-progress control in manufacturing. International Journal of Production Economics, 257, 108768. https://doi.org/10.1016/j.ijpe.2022.108768
build_conwip_system
build_conwip_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
wip_cap: int,
collect_workload: bool = False,
) -> BuiltSystem[ConWIP]
Build a ConWIP (Constant Work-In-Process) pull system.
Jobs wait in the Pre-Shop Pool and are released — earliest due date
first — only while the shop holds fewer than wip_cap jobs. Release
is re-checked on every job completion and on every PSP arrival.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
wip_cap
|
int
|
Maximum number of jobs allowed on the shop floor at once. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[ConWIP]
|
|
BuiltSystem[ConWIP]
|
(retune |
BuiltSystem[ConWIP]
|
|
Example
env = Environment() psp, servers, shop_floor, router, policy = build_conwip_system(env=env, wip_cap=8) env.run(until=1000)
References
Spearman, M. L., Woodruff, D. L. & Hopp, W. J. (1990). CONWIP: a pull alternative to kanban. International Journal of Production Research, 28(5), 879-894. https://doi.org/10.1080/00207549008942761
build_continuous_release_system
build_continuous_release_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
wl_norm_level: float,
allowance_factor: int = 2,
collect_workload: bool = False,
) -> BuiltSystem[ContinuousRelease]
Build a Continuous Release (workload-controlled) pull system.
Jobs are held in the Pre-Shop Pool and released continuously — on each
job completion and on PSP arrival — when their corrected workload
contribution keeps every server in their routing at or below
wl_norm_level. Requires CorrectedWIPStrategy on the shopfloor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
wl_norm_level
|
float
|
Corrected workload norm applied uniformly to every server. |
required |
allowance_factor
|
int
|
Buffer time per server for due-date planning. |
2
|
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[ContinuousRelease]
|
|
BuiltSystem[ContinuousRelease]
|
|
BuiltSystem[ContinuousRelease]
|
|
Example
env = Environment() psp, servers, shop_floor, router, policy = build_continuous_release_system( ... env=env, wl_norm_level=6.0 ... ) env.run(until=1000)
References
Fernandes, N. O. & Carmo-Silva, S. (2011). Workload control under continuous order release. International Journal of Production Economics, 131(1), 257-262. https://doi.org/10.1016/j.ijpe.2010.09.026
build_starvation_avoidance_system
build_starvation_avoidance_system(
*,
env: Environment,
scenario: Scenario | None = None,
prefix: str = "",
default_metrics: bool = True,
collect_workload: bool = False,
) -> BuiltSystem[None]
Build a starvation-avoidance-only pull system.
The simplest pull policy: a job is released from the Pre-Shop Pool only when its first routing server is idle — checked on PSP arrival and again on every job completion. There is no workload norm or WIP cap; release is driven purely by first-server starvation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
scenario
|
Scenario | None
|
Environment description (shop type, machine count, arrival
process, service-time, due-date rule). Defaults to a fresh
|
None
|
prefix
|
str
|
Prefix of every entity id the builder creates: servers
|
''
|
default_metrics
|
bool
|
Attach the shop floor's default EMA collector (True by default). |
True
|
collect_workload
|
bool
|
If True, attach a |
False
|
Returns:
| Type | Description |
|---|---|
BuiltSystem[None]
|
|
BuiltSystem[None]
|
starvation-avoidance callbacks, so there is no policy object. Unpacks as |
BuiltSystem[None]
|
|
Example
env = Environment() psp, servers, shop_floor, router, _ = build_starvation_avoidance_system(env=env) env.run(until=1000)
Scenario
Scenario
dataclass
Immutable description of a shop environment and its order stream.
Attributes:
| Name | Type | Description |
|---|---|---|
arrival_process |
Callable[[float], ScalarSource]
|
Factory producing the inter-arrival distribution. It is
called with the resolved arrival rate (orders per time unit, from
|
arrival_rate |
float | None
|
Explicit arrival rate (orders per time unit) that overrides
the mix-weighted derivation when not |
target_utilization |
float
|
Target steady-state shop utilization ρ, a fraction in
|
families |
tuple[SkuFamily, ...]
|
The product mix (one or more |
due_date_offset |
Distribution
|
Default due-date offset distribution applied to families
that do not set their own |
build_floor
build_floor(
env: Environment,
*,
prefix: str = "",
default_metrics: bool = True,
collect_workload: bool = False,
collect_time_series: bool = False,
retain_job_history: bool = False,
) -> tuple[ShopFloor, tuple[Server, ...]]
Create the ShopFloor and n_servers single-capacity servers.
The entities are named f"{prefix}shopfloor" and f"{prefix}wc-{i}" (i from 0), so systems built
with different prefixes can share an environment. collect_workload attaches a
:class:~simulatte.collectors.CurrentWorkloadCollector to the shop floor and collect_time_series a
:class:~simulatte.collectors.ServerTimeSeries to each server; env.collectors lists them.
build_router
build_router(
env: Environment,
shop_floor: ShopFloor,
servers: Sequence[Server],
*,
psp: PreShopPool | None,
priority_policies: Callable[..., float] | None = None,
prefix: str = "",
) -> Router
Assemble the Router from the family mix: arrival process, per-family routing, service-time distributions, and due-date offsets/rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Environment
|
The simulation environment. |
required |
shop_floor
|
ShopFloor
|
The shop floor the router feeds jobs into. |
required |
servers
|
Sequence[Server]
|
The server pool the routings and service-time maps are built
over. Its length must equal |
required |
psp
|
PreShopPool | None
|
The pre-shop pool, or |
required |
priority_policies
|
Callable[..., float] | None
|
Optional priority policy callable for the router. |
None
|
prefix
|
str
|
Prefix of the router's id, |
''
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
general_flow_shop
classmethod
general_flow_shop(
**overrides: Unpack[_PresetKwargs],
) -> Scenario
General Flow Shop preset (directed/sorted routing).
pure_flow_shop
classmethod
pure_flow_shop(
**overrides: Unpack[_PresetKwargs],
) -> Scenario
Pure Flow Shop preset (all machines, fixed order).
pure_job_shop
classmethod
pure_job_shop(
**overrides: Unpack[_PresetKwargs],
) -> Scenario
Pure Job Shop preset (undirected routing).
resolved_arrival_rate
resolved_arrival_rate() -> float
The exponential arrival rate (explicit override, else mix-weighted derivation).
The returned rate (orders per time unit) is fed to arrival_process in
build_router, which expects a sampler of inter-arrival times with mean
1 / rate (the default Exponential satisfies this).
single
classmethod
single(
*,
service_time: Distribution | None = None,
due_date_offset: Distribution | None = None,
twk_allowance_factor: float | None = None,
name: str = "F1",
**shop: Unpack[_SingleShopKwargs],
) -> Scenario
Convenience for the common one-product case: build a single-family Scenario.
name is always forwarded (it has a default), while service_time,
due_date_offset, and twk_allowance_factor are forwarded only when
non-None, so SkuFamily's own defaults fill the rest. **shop
forwards shop-level kwargs (shop_type, n_servers,
target_utilization, arrival_rate, ...).
ShopType
Bases: Enum
Standard workload-control benchmark shop types (by routing directedness).
Distributions
Distributions and routings are descriptions: sampler(rng) builds a sampler that draws from an
environment stream, and components bind them with env.bind (see simulatte.rng).
PureJobShopRouting
dataclass
Bases: _Routing
Pure Job Shop (PJS) routing description.
The Pure Job Shop — also called a randomly routed job shop (Conway et al.,
1967) — is the least directed of the standard workload-control benchmark
shops: each order has a random routing length and a random routing
direction (Oosterman, Land & Gaalman, 2000; Kasper, Land & Teunter, 2023).
A sampler draws a routing length k uniformly from [1, len(servers)]
and returns k distinct servers in random order (sampling without
replacement, so re-entrant flow is prohibited).
See GeneralFlowShopRouting (same length rule, but the subset is sorted
into a directed flow) and FlowShopRouting (fixed length, fully
directed) for the directed counterparts on the directedness spectrum.
Attributes:
| Name | Type | Description |
|---|---|---|
servers |
Sequence[Server]
|
The pool of servers to sample from. Its order defines the canonical machine index used by the directed descriptions. |
Example
routing = env.bind(PureJobShopRouting(servers), kind="routing", stream="r/routing/A", owner="r") routing() # Returns e.g., [server_2, server_5, server_1]
References
Oosterman, B., Land, M., & Gaalman, G. (2000). The influence of shop characteristics on workload control. International Journal of Production Economics, 68(1), 107-119. https://doi.org/10.1016/S0925-5273(99)00141-3
GeneralFlowShopRouting
dataclass
Bases: _Routing
General Flow Shop (GFS) routing description.
The General Flow Shop is the directed counterpart of the Pure Job Shop:
each order has the same random routing length k ~ U[1, len(servers)] and
the same equal per-machine inclusion probability, but the selected machines
are sorted into ascending index order so that orders flow in a single
direction with typical upstream and downstream stations (Oosterman, Land &
Gaalman, 2000; Kasper, Land & Teunter, 2023). Machines are drawn without
replacement, so re-entrant flow is prohibited.
Attributes:
| Name | Type | Description |
|---|---|---|
servers |
Sequence[Server]
|
The pool of servers to sample from. Their order defines the canonical machine index along which routings are directed. |
Example
routing = env.bind(GeneralFlowShopRouting(servers), kind="routing", stream="r/routing/A", owner="r") routing() # Returns e.g., [server_1, server_4, server_5]
References
Oosterman, B., Land, M., & Gaalman, G. (2000). The influence of shop characteristics on workload control. International Journal of Production Economics, 68(1), 107-119. https://doi.org/10.1016/S0925-5273(99)00141-3
FlowShopRouting
dataclass
Bases: _Routing
Pure Flow Shop (PFS) routing description.
The Pure Flow Shop is the most directed benchmark shop: every order has a fixed routing length equal to the number of machines and visits all servers in the same fixed (directed) sequence (Oosterman, Land & Gaalman, 2000; Kasper, Land & Teunter, 2023). The routing is deterministic — every job shares the identical routing — so the sampler ignores the RNG.
Note
Because every order visits every machine, the mean routing length is
len(servers) rather than (len(servers) + 1) / 2 as for the pure
job shop / general flow shop. Calibrate the arrival rate accordingly to
hit a target utilization (see arrival_rate_for_utilization).
Attributes:
| Name | Type | Description |
|---|---|---|
servers |
Sequence[Server]
|
The servers visited, in the fixed order they are visited. |
References
Oosterman, B., Land, M., & Gaalman, G. (2000). The influence of shop characteristics on workload control. International Journal of Production Economics, 68(1), 107-119. https://doi.org/10.1016/S0925-5273(99)00141-3
pure_job_shop_routing
pure_job_shop_routing(
servers: Sequence[Server],
) -> PureJobShopRouting
Pure Job Shop routing description over servers (see PureJobShopRouting).
general_flow_shop_routing
general_flow_shop_routing(
servers: Sequence[Server],
) -> GeneralFlowShopRouting
General Flow Shop routing description over servers (see GeneralFlowShopRouting).
pure_flow_shop_routing
pure_flow_shop_routing(
servers: Sequence[Server],
) -> FlowShopRouting
Pure Flow Shop routing description over servers (see FlowShopRouting).
Distribution
Bases: Protocol
A description of a random variate that also reports its analytical mean.
Any object with a sampler(rng) method returning a zero-argument
float sampler that draws from rng, and a mean property,
satisfies this protocol. The Router binds descriptions to its RNG
streams (env.bind); Scenario reads .mean to derive the arrival
rate for a target utilization.
Exponential
dataclass
Exponential distribution with rate rate (mean 1/rate).
Erlang
dataclass
Erlang (Gamma with integer shape) distribution, mean shape/rate.
TruncatedErlang
dataclass
Erlang truncated to [0, max_value] by rejection resampling.
The default shape=2 reproduces the classic truncated 2-Erlang service
process. mean is the TRUE conditional mean E[X | X <= max_value],
strictly below the nominal shape/rate whenever max_value is finite.
LogNormal
dataclass
Lognormal distribution with underlying-normal params mu/sigma.
Uniform
dataclass
Continuous uniform distribution on [low, high], mean (low+high)/2.
Deterministic
dataclass
Degenerate distribution that always returns value.
arrival_rate_for_utilization
arrival_rate_for_utilization(
target_utilization: float,
*,
n_servers: int,
mean_routing_length: float,
mean_processing_time: float = 1.0,
) -> float
Derive the Poisson arrival rate that yields a target shop utilization.
Shop utilization couples to the mean routing length through
rho = lambda * E[L] * E[p] / M, where lambda is the order arrival
rate, E[L] the mean routing length, E[p] the mean operation
processing time, and M the number of machines. Inverting gives::
lambda = rho * M / (E[L] * E[p])
Because E[L] differs by shop type — M for a pure flow shop versus
(M + 1) / 2 for the pure job shop / general flow shop — the arrival rate
is not portable across shop types: a pure flow shop reusing a job-shop
arrival rate is driven unstable (rho > 1). Use this helper to recompute
the rate whenever the shop type, machine count, or processing-time mean
changes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_utilization
|
float
|
Desired steady-state utilization |
required |
n_servers
|
int
|
Number of machines |
required |
mean_routing_length
|
float
|
Mean number of operations per order |
required |
mean_processing_time
|
float
|
Mean operation processing time |
1.0
|
Returns:
| Type | Description |
|---|---|
float
|
The arrival rate |
float
|
the mean inter-arrival time for an exponential arrival process. |
Example
Pure flow shop, M = 6, rho = 0.90, E[p] = 1.0 -> mean IAT 1.111
round(1 / arrival_rate_for_utilization(0.9, n_servers=6, mean_routing_length=6), 3) 1.111
General flow shop / pure job shop, E[L] = 3.5 -> mean IAT 0.648
round(1 / arrival_rate_for_utilization(0.9, n_servers=6, mean_routing_length=3.5), 3) 0.648
twk_due_date
twk_due_date(
allowance_factor: float,
) -> Callable[[Sequence[float]], float]
Create a Total Work Content (TWK) due-date offset rule.
The TWK procedure sets an order's due date proportional to its total work
content: due_date = arrival_time + K * sum(p_ij), where K is the
allowance factor and sum(p_ij) is the order's total processing time
(Kasper, Land & Teunter, 2023). Larger, more work-intensive orders are
granted proportionally more lead time than the flat-allowance rule.
The returned callable matches the Router due_date_rule contract: it
receives the job's sampled operation processing times and returns the
due-date offset (the allowance added to the arrival time).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allowance_factor
|
float
|
The constant |
required |
Returns:
| Type | Description |
|---|---|
Callable[[Sequence[float]], float]
|
A callable |
Example
rule = twk_due_date(8.74) # FOCUS pure-job-shop K (6 work centres) rule([1.0, 2.0]) # offset for a 3.0 work-content order 26.22
References
Kasper, A., Land, M., & Teunter, R. (2023). Towards system state dispatching in high-variety manufacturing. Omega, 114, 102726. https://doi.org/10.1016/j.omega.2022.102726
RunningStats
Compute running mean, variance, and standard deviation using Welford's algorithm.
Welford's algorithm maintains numerical stability by avoiding catastrophic cancellation that occurs when computing variance via the naive formula (sum of squares minus squared sum). This is especially important for simulations with many observations or values of similar magnitude.
Attributes:
| Name | Type | Description |
|---|---|---|
n |
Number of observations added. |
|
mean |
Current running mean of all observations. |
|
M2 |
Sum of squared differences from the mean (internal state). |
Example
stats = RunningStats() for value in [2.0, 4.0, 6.0]: ... stats.update(value) stats.mean 4.0 stats.std 2.0
std
property
std: float
Sample standard deviation (square root of variance).
variance
property
variance: float
Sample variance using Bessel's correction (n-1 denominator).
update
update(x: float) -> None
Add a new observation and update running statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float
|
The new value to incorporate into the statistics. |
required |
z_norm
z_norm(x: float) -> float
Compute the z-score (standard score) for a given value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float
|
The value to normalize. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The z-score (x - mean) / std, or 0.0 if insufficient data |
float
|
or zero standard deviation. |
Logging
TextSink
Bases: _StreamSink
Write each record as time | LEVEL | component | message.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str | Path | TextIO | None
|
File path (opened once in append mode, closed by :meth: |
required |
level
|
str
|
Lowest level kept. |
'INFO'
|
components
|
Iterable[str] | None
|
When given, only records of these components are kept. |
None
|
exclude
|
Iterable[str]
|
Records of these components are dropped. |
()
|
render_domain
|
bool
|
At |
True
|
JsonSink
Bases: _StreamSink
Write each record as one JSON object per line.
A log record has sim_time, sim_time_formatted, wall_time, seq, kind ("log"),
type ("log"), level, message, component and extra. A domain event (DEBUG with
render_domain) has kind "domain", its event type, level DEBUG, the namespace of its type as
component, its one-line rendering as message and its payload as data. Arguments as for
:class:TextSink.
SQLiteSink
Bases: LogSink
Insert each record as a row of the events table of an SQLite database.
The table has the columns env_id, seq, t, kind, type, level, component, message, data_json: kind is
"log" for a log event (data_json holds its extra) or "domain" for a domain event written at
DEBUG with render_domain (data_json holds its payload, component the namespace of its type).
Several environments, each with its own sink, can share one database file; :attr:env_id tells their rows
apart. Every row is committed when inserted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Database file, created if missing. |
required |
level
|
str
|
Lowest level kept. |
'INFO'
|
components
|
Iterable[str] | None
|
When given, only records of these components are kept. |
None
|
exclude
|
Iterable[str]
|
Records of these components are dropped. |
()
|
render_domain
|
bool
|
At |
True
|
path
property
path: Path
The database file.
execute_sql
execute_sql(
sql: str, params: tuple[Any, ...] = ()
) -> list[sqlite3.Row]
Execute sql with params on the database and return the rows (sqlite3.Row, indexable by name).
Raises:
| Type | Description |
|---|---|
RuntimeError
|
The sink is not attached or already closed. |
query
query(
*,
level: str | None = None,
component: str | None = None,
since: float | None = None,
until: float | None = None,
limit: int | None = None,
offset: int = 0,
) -> list[LogEvent]
The log records of the attached environment, ordered by time and emission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
str | None
|
Keep this level only (case-insensitive). |
None
|
component
|
str | None
|
Keep this component only. |
None
|
since
|
float | None
|
Keep records with |
None
|
until
|
float | None
|
Keep records with |
None
|
limit
|
int | None
|
Maximum number of records returned. |
None
|
offset
|
int
|
Number of records skipped. |
0
|
Returns:
| Type | Description |
|---|---|
list[LogEvent]
|
class: |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
The sink is not attached or already closed. |
HistorySink
Bases: LogSink
Keep the latest maxlen log records in memory, oldest first (env.log_history).
Iterating yields :class:~simulatte.events.LogEvent instances. After :meth:close the records stay
available, and no new record is kept.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
maxlen
|
int
|
Number of records kept; older ones are dropped. |
1000
|
level
|
str
|
Lowest level kept. |
'INFO'
|
components
|
Iterable[str] | None
|
When given, only records of these components are kept. |
None
|
exclude
|
Iterable[str]
|
Records of these components are dropped. |
()
|
maxlen
property
maxlen: int | None
Number of records kept.
clear
clear() -> None
Drop every record.
query
query(
*,
level: str | None = None,
component: str | None = None,
since: float | None = None,
until: float | None = None,
) -> list[LogEvent]
The kept records matching every given filter, oldest first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
str | None
|
Keep this level only (case-insensitive), e.g. |
None
|
component
|
str | None
|
Keep this component only, e.g. |
None
|
since
|
float | None
|
Keep records with |
None
|
until
|
float | None
|
Keep records with |
None
|
LogSink
Base of the log sinks: level and component filters, attachment and closing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
str
|
Lowest level kept ( |
'INFO'
|
components
|
Iterable[str] | None
|
When given, only records of these components are kept. |
None
|
exclude
|
Iterable[str]
|
Records of these components are dropped. |
()
|
Records without a component always pass the component filters.
closed
property
closed: bool
Whether :meth:close was called.
env_id
property
env_id: str | None
Identifier of the attached environment (a fresh UUID hex string per attachment), None before.
level
property
level: str
Lowest level kept, upper-case.
attach
attach(env: Environment) -> Self
Subscribe to the log events of env (and, see the module docstring, its domain events); return self.
The sink is added to env.sinks and closed by env.close(). Raises RuntimeError if the sink is
already attached or closed.
close
close() -> None
Stop receiving events and release the sink's resources. Idempotent.
diagnostic
diagnostic(t: float, message: str, **extra: Any) -> None
Write a recorder diagnostic directly, without emitting a bus event.
Diagnostics have sequence -1: they are not simulation or observer events. Closed sinks ignore them. Component filters and the warning threshold still apply.
disable_component
disable_component(component: str) -> None
Drop the records of component.
enable_component
enable_component(component: str) -> None
Keep the records of component again (and add it to the components allow-list, if one was given).