NETWORKSTRESS TEST
PRODUCTION ยท WAREHOUSE ยท CONSUMER โ€” AIR / SEA / ROAD ยท Estay Dynamics
About โ† Projects

Click a node or lane to select it ยท scroll to zoom ยท drag to pan ยท Map = geography, Diagram = topology by type

Nodes

Edges

Phase 2 โ€” Stress Testing

Monte Carlo Stress Test

Every edge fails at random with its own baseFailureRate; every node fails with a configurable rate (nodes don't carry a per-node rate in the base schema). Runs many independent trials and shows the distribution of outcomes, not just one scenario.

Time-to-Survive vs. Time-to-Recover

Checks the currently applied scenario (set it via the Scenario panel, a table's disable toggle, or a Contingency/Adversarial result) against consumer inventory buffers. A static single comparison, not a day-by-day dynamic simulation โ€” see "How this works" below.

Adversarial Worst-Case Search

Greedily finds the worst combination of up to N simultaneous failures (network-interdiction framing) by repeatedly re-running the N-1 scan against the already-degraded network. A tractable heuristic, not a guaranteed global worst case.

How the solver works

Each product is solved as its own min-cost flow problem (successive shortest augmenting paths with node potentials), production and warehouse nodes are node-capacitated via the standard split-node technique, and consumer demand is the sink. Products are solved sequentially in ascending priority order, each consuming from whatever edge capacity earlier products left behind. This is a deliberate, documented approximation of the much harder general multi-commodity flow problem โ€” a higher-priority product claims a shared lane first; a lower-priority one gets only the leftover. It is not a global joint optimum across products.

A scenario disables or derates specific nodes/lanes to model a disruption before re-solving โ€” compare Key Results before and after. The N-1 scan automates this: it tries disabling every currently-active node and lane, one at a time, and ranks them by how much service level each single loss costs โ€” the power-grid "N-1 contingency" idea applied to a supply network.

About the generated starting network

"Generate" produces a random network clustered around real-world trade regions so the map looks plausible, with mode-characteristic lane defaults and a coverage pass that guarantees every node has at least one upstream path for each product it handles โ€” but it is not calibrated to a pristine 100% baseline service level. A freshly generated network typically clears only 55-70% of demand out of the box, the same way a real, un-optimized network would: some lanes are genuinely capacity-constrained, some nodes are one hop further from supply than others. That's an intentional, honest starting point for a stress-testing tool, not a bug to chase to zero โ€” edit capacities, add lanes, or regenerate with a different seed/fanout to explore how those choices change resilience.

Phase 2 โ€” Monte Carlo, Time-to-Survive/Recover, and adversarial search

Monte Carlo samples many independent random scenarios (each edge fails with its own lane baseFailureRate; each node fails at a configurable rate) and reports the distribution of outcomes โ€” mean, percentiles, worst trial observed โ€” rather than one hand-picked "what if". It assumes independent failures: a real regional event that knocks out several correlated lanes at once isn't modeled here.

Time-to-Survive vs. Time-to-Recover compares, for the currently applied scenario, how long a consumer's inventory buffer covers its shortfall (buffer days ร— pre-disruption receipt รท shortfall rate) against how long the disrupted element takes to come back (meanDisruptionDays, per element, defaulting to 14 days for a node and reusing lane defaults for an edge). If time-to-survive is shorter, that consumer stocks out before recovery โ€” the gap is genuine downtime. This is a static, single comparison built on the same solver as everything else here, not a day-by-day dynamic simulation with depleting/replenishing inventories and cascading recovery โ€” an intentional, documented simplification of the full MIT CTL / Sheffi resilience framework this app is built around, not a claim of full dynamic realism.

Adversarial search greedily re-runs the N-1 contingency scan against the network as it's already been degraded by prior steps, each time removing whatever does the most additional damage, up to a budget of simultaneous failures. Like the N-1 scan, this is a tractable heuristic (network-interdiction framing), not a guaranteed globally-worst combination โ€” exhaustively checking every possible combination of even a handful of failures out of hundreds of elements is computationally infeasible.

Swapping in a different solver

The flow engine (web/src/flowSolver.js) is a plain min-cost flow implementation โ€” successive shortest augmenting paths with node potentials โ€” behind a narrow, exact interface: a class you construct with a node count, feed edges via addEdge(from, to, cap, cost), then call run(source, sink, maxFlow) on. network.js is the only file that talks to it, so any replacement that keeps that same shape โ€” and the same "flow is optimal for cost" guarantee โ€” drops in without touching the rest of the app.

Going further than a drop-in swap โ€” genuine joint optimization across all products at once, instead of this app's documented sequential-by-priority approximation โ€” is a bigger change: it needs network.js's orchestration rewritten too, and realistically a real LP/MILP engine, since hand-written JavaScript doesn't have a competitive general-purpose one.

Read the full solver guide โ†’ โ€” the exact interface contract, a complete worked example of a second, independently-implemented solver, how to wire it in, and how to verify it before trusting it.