2026-04-29__001_synthetic_mine_throughput__claude-code__claude-opus-4-7__agent-teams-nelson-max-thinking

Date: 2026-04-29 · Benchmark: 001_synthetic_mine_throughput · Harness: claude-code · Model: claude-opus-4-7 (agent-teams-nelson-max-thinking) · ? Unrecorded

Scores

Category Points Max
Conceptual modelling 19 20
Data and topology 15 15
Simulation correctness 19 20
Experimental design 14 15
Results & interpretation 14 15
Code quality 9 10
Traceability 5 5
Total 95 100

Run metrics

Evaluation report

Scenario Mean throughput
baseline 12,960
trucks_4 7,813.333
trucks_12 12,996.667
ramp_upgrade 13,033.333
crusher_slowdown 6,560
ramp_closed 12,883.333

Source files

Downloads

Conceptual model

Conceptual Model: Synthetic Mine Throughput Simulation

This document describes the conceptual model underlying the discrete-event simulation (DES) of a synthetic open-pit mine haulage system. The simulation is built with SimPy and is designed to answer six operational decision questions about mine throughput under varying fleet sizes, road configurations, and crusher conditions.


System Boundary

Included in the model:

Excluded from the model:


Entities

Trucks are the only active entities. Each truck is characterised by:

Ore payloads are not modelled as separate entities; a loaded truck implicitly carries 100 tonnes and delivers them when it completes a dump cycle.


Resources

Resources limit simultaneous access and cause queuing when saturated.

ResourceNode / EdgeCapacityNotes
Loader NorthLOAD_N1Mean load time 6.5 min, SD 1.2 min
Loader SouthLOAD_S1Mean load time 4.5 min, SD 1.0 min
CrusherCRUSH1Mean dump time 3.5 min, SD 0.8 min
Ramp outboundE03_UP (J2→J3)1Narrow uphill ramp; primary bottleneck
Ramp inboundE03_DOWN (J3→J2)1Same physical constraint, separate edge
Crusher approach outboundE05_TO_CRUSH (J4→CRUSH)1Single-lane dump approach
Crusher approach inboundE05_FROM_CRUSH (CRUSH→J4)1Single-lane return
North pit face access outboundE07_TO_LOAD_N (J5→LOAD_N)1Single-lane face road
North pit face access inboundE07_FROM_LOAD_N (LOAD_N→J5)1Single-lane face road
South pit face access outboundE09_TO_LOAD_S (J6→LOAD_S)1Single-lane face road
South pit face access inboundE09_FROM_LOAD_S (LOAD_S→J6)1Single-lane face road

All edges with capacity < 999 are wrapped as SimPy Resource objects with that capacity. Edges with capacity = 999 are treated as unconstrained (trucks travel freely after a travel-time delay).


Events

Each truck cycles through the following events repeatedly until the shift ends:

  1. Truck dispatched — Dispatcher assigns a truck (at PARK or returning from crusher) to an available loader or the loader with the shortest expected wait.
  2. Truck departs toward loader — Truck acquires capacity on each road segment in sequence along the shortest-time path.
  3. Truck arrives at loader queue — Truck requests the loader resource.
  4. Loading starts — Loader resource granted; loading time sampled from truncated normal.
  5. Loading ends — Truck now carries 100 t; loader resource released.
  6. Truck departs toward crusher — Truck travels loaded at 85 % of road speed, acquiring segment resources along the route.
  7. Truck arrives at crusher queue — Truck requests the crusher resource.
  8. Dumping starts — Crusher resource granted; dump time sampled from truncated normal.
  9. Dumping ends — Truck delivers 100 t to the crusher; crusher resource released; tonnes_delivered counter incremented.
  10. Truck returns empty — Truck travels empty back to PARK (or is immediately re-dispatched if a loader is waiting).
  11. Shift end — At shift_length_hours * 3600 simulation seconds, in-flight cycles are counted if the truck has already completed loading (ore is already in transit); cycles not yet loaded are abandoned.

State Variables

VariableDescription
truck.locationCurrent node or edge of each truck
truck.loadedBoolean — whether the truck is carrying ore
truck.assigned_loaderLoader currently assigned to this truck (None when idle)
queue_length[resource]Number of trucks waiting for each loader/crusher/road segment
resource_busy_time[resource]Cumulative seconds each resource has been in use
tonnes_deliveredRunning total of ore (tonnes) dumped at the crusher per replication
cycle_timesList of full cycle durations (dispatch → dump end) per truck per replication
`truck_wait_time[loadercrusher]`

Assumptions

Derived from Data

Introduced by the Model

Limitations


Performance Measures

MeasureDefinitionHow Computed
Tonnes per hour (t/h)Total ore delivered to crusher divided by shift durationtonnes_delivered / shift_length_hours per replication; mean and 95 % CI across replications
Total tonnes deliveredCumulative ore dumped at CRUSH per shiftSum of 100 t increments at each dump event
Truck cycle time (min)Time from truck dispatch to end of dumpRecorded for each completed cycle; mean and SD reported
Loader utilisationFraction of shift time a loader is actively loadingresource_busy_time[loader] / shift_length_seconds
Crusher utilisationFraction of shift time the crusher is actively dumpingresource_busy_time[crusher] / shift_length_seconds
Queue wait time (min)Mean time trucks wait for each resourcetotal_wait_time[resource] / number_of_service_events
Top bottlenecksResources with highest utilisation or wait timeRanked by utilisation across all resources
95 % confidence intervalUncertainty estimate on mean t/hmean ± 1.96 × (SD / sqrt(replications))

Results are aggregated across 30 replications per scenario and written to results.csv (one row per replication) and summary.json (scenario-level statistics).

README

Synthetic Mine Throughput — SimPy Discrete-Event Simulation

A discrete-event simulation of an open-pit mine haulage system built with SimPy. Six operational scenarios are modelled over an 8-hour shift with 30 replications each.


Install

Python 3.11+ is recommended.

pip install -r requirements.txt

Or with uv:

uv sync

How to Run

Run all six required scenarios:

python run.py

Run a single scenario:

python run.py --scenario baseline

Run with a custom replication count:

python run.py --scenario baseline --replications 30

Available scenario IDs: baseline, trucks_4, trucks_12, ramp_upgrade, crusher_slowdown, ramp_closed.

Run with a warmup period (excludes the first N minutes from queue / utilisation statistics; throughput denominator becomes shift - warmup):

python run.py --scenario baseline --warmup-minutes 30

Shipped scenarios use warmup_minutes: 0; the CLI flag overrides that for ad hoc steady-state analysis.


How to Reproduce Results

Seeds are controlled per replication: seed = base_random_seed + replication_index. The baseline scenario uses base_random_seed = 12345, giving seeds 12345–12374 across 30 replications. All other scenarios inherit this setting unless overridden in their YAML. Running python run.py with no arguments reproduces the published results.csv, summary.json, and event_log.csv exactly.


Conceptual Model

See conceptual_model.md for the full model description.

Summary: Trucks cycle from a central parking area to one of two ore loaders (North Pit or South Pit), then travel loaded to the primary crusher, dump 100 t, and return empty. Resources that can form queues — loaders, crusher, and single-lane road segments — are modelled as SimPy Resource objects. The dispatcher assigns each idle truck to the nearest available loader, breaking ties by shortest expected cycle time. Routing uses shortest-time Dijkstra over open edges, so the bypass route (J2→J7→J8→J4) is used automatically when it is faster than the main ramp.


Main Assumptions

For the full assumptions list see conceptual_model.md.


Routing and Dispatching Logic

Routing: Shortest-time Dijkstra over open edges. Edge traversal time is computed as distance / (max_speed_kph × speed_factor), where speed_factor = 0.85 when loaded and 1.00 when empty. Closed edges are excluded from the graph. If no path exists the simulation raises an error rather than producing silent wrong results.

Dispatching: nearest_available_loader — the idle truck is assigned to the loader with the shortest expected travel time from the truck’s current position. When two loaders have equal travel time the tie is broken by shortest_expected_cycle_time, which accounts for estimated queue wait at each loader.

Capacity-constrained segments: Edges with capacity = 1 in edges.csv are wrapped as SimPy Resource(env, capacity=1). A truck must acquire the resource before traversing the edge and releases it on arrival. Separate resources are used for each direction. Affected edges in the baseline topology:

EdgeRouteDirection
E03_UPJ2 → J3 (main ramp)Outbound
E03_DOWNJ3 → J2 (main ramp)Inbound
E05_TO_CRUSHJ4 → CRUSHOutbound
E05_FROM_CRUSHCRUSH → J4Inbound
E07_TO_LOAD_NJ5 → LOAD_NOutbound
E07_FROM_LOAD_NLOAD_N → J5Inbound
E09_TO_LOAD_SJ6 → LOAD_SOutbound
E09_FROM_LOAD_SLOAD_S → J6Inbound

The bypass route (E15/E16/E17) has capacity 999 and is treated as unconstrained.


Key Results

All figures are from summary.json, 30 replications × 8-hour shift. 95 % CI computed using Student’s t-distribution with df = n − 1 (scipy.stats.t.interval(0.95, df=29)).

ScenarioTruckst/h (mean)95 % CICrusher utilAvg cycle (min)
baseline81620[1611, 1629]0.9528.9
trucks_44977[972, 981]0.5724.1
trucks_12121625[1612, 1637]0.9542.7
ramp_upgrade81629[1620, 1639]0.9528.8
crusher_slowdown8820[812, 828]0.9655.9
ramp_closed81610[1599, 1621]0.9529.1

Answers to the 6 Operational Decision Questions

Q1: What is the baseline throughput?

1620 t/h [95 % CI: 1611–1629], equivalent to 12,960 t per 8-hour shift. Mean truck cycle time is 28.9 minutes. The crusher runs at 95 % utilisation, indicating it is near saturation under the baseline 8-truck fleet.

Q2: What are the likely bottlenecks?

The top_bottlenecks ranking in summary.json (sorted by utilisation, then queue time) lists D_CRUSH (crusher) first in every scenario:

ScenarioTop bottleneckUtilisationMean queue (min)
baselineD_CRUSH0.954.6
trucks_4D_CRUSH0.570.8
trucks_12D_CRUSH0.9617.2
ramp_upgradeD_CRUSH0.954.6
crusher_slowdownD_CRUSH0.9627.9
ramp_closedD_CRUSH0.954.7

The crusher is the binding resource in every configuration except trucks_4, where it runs at 0.57 utilisation and the fleet is the binding constraint instead. The South loader (L_S) is consistently second-highest by utilisation (0.91 baseline, 0.92 trucks_12) because the dispatcher preferentially sends trucks to the faster loader when it is idle.

Note on the narrow ramp (E03_UP). A naive ranking by mean queue time would surface E03_UP at the top of the baseline list (6.0 min mean queue), but its utilisation is only 3.3 % — inconsistent with a true bottleneck. This queue is a startup-stampede artifact: at t = 0 all 8 trucks dispatch simultaneously, the nearest-loader policy sends them all toward L_S via E03_UP, and they queue once. After the first cycle, the fleet has spread across both loaders and E03_UP is essentially unused — routing for L_N already bypasses it via J2→J7→J5. Sorting top_bottlenecks by utilisation (with queue time as tiebreaker) removes this misleading artifact while leaving the underlying data visible in results.csv.

Q3: How sensitive is throughput to fleet size?

Fleett/hChange vs. baseline
4 trucks977−40 %
8 trucks (baseline)1620
12 trucks1625+0.3 %

The system is strongly fleet-limited below 8 trucks and crusher-saturated above 8. Adding trucks beyond the baseline provides almost no gain (1625 vs. 1620 t/h, within the confidence intervals). The crusher service rate (~3.5 min per dump, capacity 1) sets a theoretical ceiling of approximately 1629 t/h under the baseline payload and shift length. Any further throughput gain requires either a faster crusher or a second crusher rather than additional trucks.

Q4: What is the impact of upgrading the main ramp?

Marginal: 1629 t/h vs. 1620 t/h baseline — a 0.6 % improvement, within noise.

The ramp upgrade (E03_UP/DOWN capacity raised to 999, speed raised from 18/22 to 28 km/h) removes the capacity constraint on the main ramp. However, the baseline routing already directs L_N-bound trucks via the bypass (J2→J7→J5), which is faster than the narrow ramp. Only L_S-bound trucks use E03, and these are spread out enough in steady state that the ramp is not a binding constraint. The ramp is correctly absent from the top_bottlenecks list in both baseline and ramp_upgrade once results are ranked by utilisation, confirming the ramp was not limiting throughput.

Recommendation: Do not invest in a ramp upgrade to increase throughput. The crusher is the binding resource.

Q5: How sensitive is throughput to crusher service time?

Highly sensitive: a doubling of mean dump time (3.5 → 7.0 min) drops throughput by 49 % (1620 → 820 t/h).

Under crusher_slowdown, the crusher remains at 0.96 utilisation but now processes trucks at half the rate. Mean crusher queue time rises from 4.6 to 27.9 minutes, and average truck cycle time extends from 28.9 to 55.9 minutes. Loader utilisations drop sharply (L_S: 0.91 → 0.42; L_N: 0.50 → 0.37) as trucks spend most of their cycle waiting at the crusher. The system is highly sensitive to crusher throughput because the crusher is the single-server bottleneck for the entire fleet.

Recommendation: Crusher reliability and service rate are the most critical operational parameters. Even moderate crusher slowdowns (e.g. blocked chutes, liner wear) will have a disproportionate effect on shift tonnage.

Q6: What happens if the main ramp is closed?

Small impact: 1610 t/h vs. 1620 t/h baseline — a 0.6 % reduction. Rerouting via the bypass is fully viable.

When E03_UP and E03_DOWN are closed, the router automatically finds paths through the western bypass (J2→J7→J8→J4 for L_S-bound trucks; J2→J7→J5 for L_N-bound trucks). The bypass adds some travel distance but the route times are comparable. Crusher utilisation remains at 0.95 and truck utilisation is essentially unchanged (0.783 baseline vs. 0.782 ramp_closed). The confidence intervals overlap substantially ([1611–1629] baseline vs. [1599–1621] ramp_closed), so the difference is not statistically significant at the 95 % level.

Note: In results.csv, the edge_E03_UP_queue_time and edge_E03_DOWN_queue_time columns are 0.0 for ramp_closed and ramp_upgrade scenarios — this is correct because those edges do not exist as resources in those scenarios (closed or unconstrained respectively), not a data error.

Recommendation: The bypass provides adequate rerouting capacity. A ramp closure need not halt production, though travel times are slightly longer for L_S-bound trucks.


Likely Bottlenecks

Based on utilisation and queue-time analysis across all scenarios:

  1. Crusher (D_CRUSH) — the primary steady-state bottleneck in all scenarios except trucks_4. Utilisation 0.95–0.96; mean queue wait 4.6–27.9 min depending on service rate. Any reduction in crusher throughput has an immediate and disproportionate effect on overall t/h.

  2. Loader South (L_S) — consistently second-highest utilisation (0.91 baseline, 0.92 trucks_12). The South loader is faster (4.5 min mean) but heavily loaded because the dispatcher preferentially assigns trucks there when it is idle. Queue time 1.6 min baseline, rising to 2.2 min with 12 trucks.

  3. Crusher approach road (E05_TO_CRUSH) — single-lane access to the crusher, utilisation ~0.43–0.45. Not a bottleneck at current fleet sizes but could become one if throughput increases.

  4. South pit return road (E09_FROM_LOAD_S) — single-lane pit access, utilisation ~0.48 baseline. Co-occupies the South pit cycle alongside L_S; not currently binding but the highest-utilisation road segment.

A startup-transient artifact appears on E03_UP (high queue time, ~3 % utilisation) for the first ~20 minutes of each replication while the fleet spreads from PARK. This is correctly excluded from the bottleneck ranking by sorting on utilisation; see Q2.


Limitations


Suggested Improvements and Further Scenarios

  1. Warmup period in shipped scenarioswarmup_minutes support is implemented in the runner but the shipped YAMLs use 0. Bump baseline to 30–60 min in the YAML to make the bottleneck ranking and queue-time statistics reflect steady-state operation only. The CLI override (--warmup-minutes 30) provides the same effect ad hoc.

  2. Stochastic breakdowns — model loader and crusher failures using exponential time-to-failure and lognormal repair times to assess availability risk.

  3. Second crusher scenario — add a second crusher unit to test whether a parallel dump point breaks the current throughput ceiling.

  4. Shift-change scenario — introduce a 15-minute production pause at hour 4 to quantify the tonnage cost of crew changeover.

  5. Dynamic dispatch with real-time queue feedback — upgrade the dispatcher to use live queue lengths rather than estimated wait times for assignment decisions.

  6. Bypass capacity constraint scenario — set E15/E16/E17 capacity to 1 or 2 to test whether the bypass becomes a bottleneck if the ramp is closed long-term.

  7. Sensitivity analysis on CV — vary travel time noise (CV = 0.05, 0.10, 0.20) to quantify how road condition variability affects throughput confidence intervals.


Output Files

FileDescription
results.csvOne row per replication per scenario (180 rows); scenario-level and replication-level metrics
summary.jsonScenario-level statistics: mean t/h, 95 % CI, utilisations, queue times, bottleneck ranking
event_log.csvFull event trace (~92,000 events); columns: time, truck_id, event_type, node, tonnes
topology.pngVisualisation of the road network graph with node types and edge capacities
conceptual_model.mdFormal conceptual model document

← Back to leaderboard