"""Narrative constants surfaced in ``summary.json`` (and the README).
These three lists are the qualitative section of the deliverables:
``key_assumptions``, ``model_limitations``, and
``additional_scenarios_proposed``. They live in their own module so
:mod:`mine_sim.io_writers` stays focused on serialisation and well under the
file-size budget. Keep these in lockstep with ``conceptual_model.md`` and
``README.md``.
"""
from __future__ import annotations
#: Benchmark identifier surfaced at the top of ``summary.json``.
DEFAULT_BENCHMARK_ID: str = "001_synthetic_mine_throughput"
DEFAULT_KEY_ASSUMPTIONS: tuple[str, ...] = (
"Time horizon: a hard cut at t=480 min via env.run(until=480). Only "
"end_dump events that close strictly before 480 credit tonnes; loads or "
"dumps still in progress at the cut are discarded (operator-facing "
"'tonnes closed at shift end').",
"Routing: static shortest-time per scenario via Dijkstra on free-flow "
"edge times (distance_m / (max_speed_kph*1000/60)). Recomputed only when "
"a scenario closes or upgrades edges; a truck commits to its path at "
"dispatch and does not re-plan mid-leg.",
"Capacity-1 directed edges are modelled as independent SimPy Resources, "
"mirroring edges.csv literally: the 8 single-lane segments are E03_UP/"
"DOWN (ramp), E05_TO/FROM_CRUSH (crusher approach), E07_TO/FROM_LOAD_N "
"and E09_TO/FROM_LOAD_S (pit-face roads).",
"Each physical direction is a separate resource: E03_UP and E03_DOWN are "
"two capacity-1 lanes, so opposing trucks do not contend for one shared "
"lane (no head-on blocking is modelled).",
"Travel-time noise: an independent per-edge-traversal lognormal "
"multiplier with mean 1 and cv=0.10 (sigma^2 = ln(1+cv^2), mu = "
"-sigma^2/2). Empty trucks use empty_speed_factor=1.00, loaded trucks "
"loaded_speed_factor=0.85.",
"Loading and dumping times are drawn from normal_truncated(mean, sd) "
"floored at max(0.1, sample) so durations are strictly positive without "
"biasing the mean. Parameters: L_N 6.5/1.2, L_S 4.5/1.0, D_CRUSH 3.5/0.8 "
"min (crusher_slowdown raises D_CRUSH to 7.0/1.5).",
"Dispatch: each empty truck is sent to argmin(travel_to_loader + "
"queue_len * mean_load_time + own_mean_load), where queue_len counts "
"trucks in service plus waiting. Ties break by lower loader_id. The "
"route is static; the loader choice is dynamic.",
"Initial conditions: all trucks released simultaneously at t=0 from PARK; "
"no warmup, staged ramp-up, or shift-handover modelling.",
"Throughput accounting: tonnes are credited at end_dump (not arrive or "
"start_dump). Every completed dump credits exactly the truck payload "
"(homogeneous 100 t fleet).",
"Scope: only ore haulage to the primary crusher is simulated "
"(LOAD_N/LOAD_S -> CRUSH). WASTE and MAINT remain in the graph for "
"completeness but carry no truck traffic and are excluded from routing "
"and dispatch.",
"Reproducibility: per-replication seed = base_random_seed (12345) + "
"replication_index, with independent numpy SeedSequence streams per "
"stochastic source, so each replication is independently reproducible and "
"the full run is deterministic.",
"Uncertainty: 95% confidence intervals are Student-t with n-1 = 29 "
"degrees of freedom over 30 replications per scenario.",
)
DEFAULT_MODEL_LIMITATIONS: tuple[str, ...] = (
"No truck breakdowns, refuelling, or maintenance windows: truck "
"availability is held at 1.00 for the whole 480-min shift, so the model "
"estimates an upper-bound, fully-available throughput.",
"Separate ramp directions: E03_UP and E03_DOWN are two independent "
"single-lane resources. If the real ramp is one shared physical lane, "
"true congestion (head-on / passing constraints) would be worse than "
"modelled.",
"Static routing: trucks commit to their shortest-time path at dispatch "
"and never divert, even if a capacity-1 edge develops a queue. A real "
"dispatcher could re-route through the bypass, so modelled queueing on "
"single-lane edges is an upper bound.",
"Boundary under-count: productive time and tonnes accrue only on "
"completed phases. A load/dump/leg straddling t=480 contributes nothing, "
"slightly under-counting both utilisation and the final cycle.",
"Crusher never blocks downstream: D_CRUSH has no stockpile back-pressure, "
"full-bin signal, or scheduled maintenance. Real surge-pile limits are "
"not represented.",
"Single homogeneous payload: every dump is exactly 100 t. Heterogeneous "
"trucks, ore blending, and grade-dependent processing are not modelled.",
"Free-flow (capacity 999) edges have effectively unlimited capacity: "
"trucks do not interact on multi-lane haul roads, so headway and "
"following-distance effects are ignored.",
"No warmup trimming: the shift starts empty (all trucks at PARK, all "
"queues empty). The empty-system bias is small because the fleet reaches "
"steady state within a few cycles, but it is not zero.",
"Deterministic speeds aside from edge noise: max-speeds and per-truck "
"speed factors are fixed. Only the per-edge lognormal multiplier "
"(cv=0.10) introduces travel variability — weather, dust, and visibility "
"are not modelled.",
"Node coordinates from nodes.csv are used for visualisation only; "
"haul-road bends and grade are not propagated into travel time beyond the "
"given distance_m and max_speed_kph.",
)
DEFAULT_ADDITIONAL_SCENARIOS_PROPOSED: tuple[str, ...] = (
"trucks_12_ramp_upgrade (included here as the 7th scenario): combines a "
"12-truck fleet with the ramp upgrade to test whether the two "
"investments are complementary, substitutive, or independent. Because "
"the loaded legs to the crusher never use the ramp, the upgrade adds "
"little on top of the fleet increase — the pair is close to "
"non-interacting.",
"Crusher reliability: inject random short outages on D_CRUSH (e.g. 5 min "
"every 60 min) to size the surge-pile buffer and quantify the cost of "
"unplanned crusher downtime, since the crusher is the binding constraint.",
"Dynamic re-routing: re-plan when a queue at a capacity-1 edge exceeds a "
"threshold (e.g. 2 waiting trucks). Quantifies the upside available from "
"a smarter dispatcher over the static-routing baseline.",
"Crusher service-time upgrade: cut mean dump time from 3.5 to 2.5 min and "
"re-run trucks_12 to confirm the crusher is the true ceiling and value a "
"faster tip.",
"Heterogeneous fleet: replace some 100 t trucks with 150 t trucks to "
"trade cycle count for per-cycle payload and find the mix that maximises "
"tonnes given the crusher constraint.",
"Mid-shift loader outage: take L_N or L_S offline for 30 min mid-shift to "
"size the single-loader fall-back tonnes and inform redundancy planning.",
)
__all__ = [
"DEFAULT_ADDITIONAL_SCENARIOS_PROPOSED",
"DEFAULT_BENCHMARK_ID",
"DEFAULT_KEY_ASSUMPTIONS",
"DEFAULT_MODEL_LIMITATIONS",
]
src/mine_sim/narrative.py
← Back to submission · View raw on GitHub