Chapters 9 and 10 needed approximations for one reason: the upper level’s backorders make each lower location’s resupply time random, and the waits of different requests move together, and no formula carries that coupling whole.
A simulation carries it because it has no choice. A storeroom’s request waits at the depot exactly as long as the depot’s queue makes it wait, and the storeroom’s stock reflects that wait. A two-level model is the single-location model twice, with one location as the other’s supplier.
The validation follows the chapter’s pattern:
Four storerooms, each seeing 45 failures a year, rebuild three in ten in their own shop in 30 days and send the rest to a depot: 10 days’ transit, 60 days’ repair. \(S_0 = 16\) at the depot, \(S_j = 4\) at each storeroom. 1,000-year replications, \(n = 30\).
| Measure | METRIC | VARI-METRIC | Status | Simulated |
|---|---|---|---|---|
| \(\bar{B}_0\), depot | 5.0201 | 5.0201 | exact | 5.0098 \(\pm\) 0.0216 |
| \(\bar{W}\), days | 14.54 | 14.54 | exact | 14.52 \(\pm\) 0.05 |
| \(\mu_j\), storeroom pipeline | 3.2276 | 3.2276 | exact | 3.2234 to 3.2282, each \(\pm\) 0.008 |
| \(\bar{I}_j - \bar{B}_j\) | 0.7724 | 0.7724 | exact | 0.7719 to 0.7767 |
| \(\bar{B}_j\), per storeroom | 0.4055 | 0.4815 | in dispute | 0.4813 to 0.4825, each \(\pm\) 0.004 |
| Backorders, four storerooms | 1.6218 | 1.9259 | in dispute | 1.9269 |
The check: the exact rows are covered, so the disputed rows can be read. VARI-METRIC’s 0.4815 lies inside every storeroom’s interval; METRIC’s 0.4055 lies far below. METRIC understates by about 16%.
Four storerooms, one cutout a week each, base stock \(S_j = 3\), a one-week transit from a hub running \((r_0, Q_0) = (9, 8)\) against a three-week supplier. Chapter 10 computed every measure exactly. Let \(W\) represent the time a storeroom’s request waits at the hub.
| Measure | Exact | Simulated |
|---|---|---|
| \(\bar{B}_0\), hub | 1.0273 | 1.0173 \(\pm\) 0.0087 |
| \(\overline{\mathit{RR}}_0\) | 0.6030 | 0.6045 \(\pm\) 0.0021 |
| \(\bar{W}\), weeks | 0.2568 | 0.2547 \(\pm\) 0.0018 |
| \(P(W = 0)\) | 0.6030 | 0.6051 \(\pm\) 0.0020 |
| \(P(W \le 1)\) | 0.9117 | 0.9129 \(\pm\) 0.0009 |
| \(\bar{B}_j\), per storeroom | 0.0726 | 0.0707 to 0.0724, each \(\pm\) 0.0016 |
The check: \(P(W = 0)\) is the hub’s ready rate, 0.6051 against 0.6045; and Little’s law gives \(\bar{W} = \bar{B}_0/4 = 1.0173/4 = 0.2543\) against 0.2547.
Read this run slowly: four intervals miss, the hub’s backorders, its mean wait, \(P(W = 0)\) and \(P(W \le 1)\), with one storeroom’s backorders. They miss together and in one direction: the hub had slightly more stock than its long-run average across these thirty replications, and every measure depending on it moved the same way.
That is what correlated intervals from one set of runs do. Checked as a family at the Bonferroni level, \(1 - 0.05/k\) for \(k\) measures, this run passes. Two further runs of 300 replications on other streams put the hub’s backorders at 1.0283 \(\pm\) 0.0027 and 1.0263 \(\pm\) 0.0027, both covering 1.0273. The model is right; the first thirty replications were a little lucky.
The storerooms’ backorders average 0.0717, beside chapter 10’s routes: exact 0.0726, conditioned on the wait 0.0726, two moments 0.0712, mean wait only 0.0505. The simulation sits with the first three: the mean wait alone understates a storeroom’s backorders by 30%, seen from outside the mathematics.
Chapter 10 set both policies by iteration and said nothing exact covers the case. Lots of 1, 2 or 4 at 150 a year at the storeroom; the hub serves 250 lots a year of its own; two-month supplier, one-month transfer; \(k = \$82.50\), \(h = \$28.75\), storeroom \(b = \$287.50\), hub \(b_0 = \$115\). Policies \((25, 49)\) at the storeroom and \((120, 92)\) at the hub.
| Storeroom, predicted | simulated | Hub, predicted | simulated | |
|---|---|---|---|---|
| Total, a year | $1,371.26 | $1,313.68 \(\pm\) 2.46 | $2,279.69 | $2,200.02 \(\pm\) 3.69 |
| Orders a year | 6.12 | 6.1153 \(\pm\) 0.0114 | 8.70 | 8.6936 \(\pm\) 0.0106 |
| Ready rate | 0.9227 \(\pm\) 0.0011 | 0.8083 \(\pm\) 0.0017 | ||
| Lot fill rate | 0.9108 \(\pm\) 0.0011 | 0.8001 \(\pm\) 0.0017 |
The check: order rate is units over batch: \(300/49 = 6.12\) at the storeroom and \((500 + 300)/92 = 8.70\) at the hub, which sees the storeroom’s 300 units a year in orders of 49.
The iteration’s policies cost less than it said: $57.58 less at the storeroom and $79.67 at the hub, about 4% at each. The moment route is conservative here, the safe direction.
The chapter’s own classes show the mechanism. A network of realistic size needs more: several items per location, transport between locations, costs by location and cost line, and a report a manager can read. ksl.modeling.supplychain supplies those.
A network is a tree of inventory holding points, each holding one or more items, each item running its own policy, rooted at an external supplier, with customer demand at the leaves and a transport time on each edge.
| This book | The package |
|---|---|
| \((r, Q)\); \((s, S)\); \((R, s, S)\) | sQ(s, Q); sS(s, S); sSPeriodic(s, S, reviewInterval) |
| Base stock at level \(S\) | sQ(s = S - 1, Q = 1) |
| \(\bar{I}\), \(\overline{\mathit{IO}}\), \(\bar{B}\) | On Hand, On Order, Amt BackLogged |
| \(1 - \overline{\mathit{RR}}\) | Fraction of Time w/o Stock |
| Lot fill rate; unit fill rate | First Fill Rate; Unit Fill Rate |
The package is released as experimental, and its interface may change between KSL versions. Everything here was written against KSL R1.7.1; check the release notes before relying on a name or default with a later version.
supplyChain("CutoutNetwork") {
transportStrategy = perIHPTimeBased
val cutout = item("Cutout", leadTime = constant(3.0), unitCost = 115.0)
holdingPoint("Hub") {
attachedToExternalSupplier()
inventory(cutout) { sQ(s = 9, Q = 8, initialOnHand = 17) }
tier(count = 4, namePrefix = "Store", transportTime = constant(1.0)) {
inventory(cutout) { sQ(s = 2, Q = 1, initialOnHand = 3) }
demand(cutout, exponential(mean = 1.0, stream = autoStream()))
}
}
}| Measure | Exact | The chapter’s classes | The package |
|---|---|---|---|
| Hub on hand | 2.5273 | 2.5334 \(\pm\) 0.0124 | 2.5334 \(\pm\) 0.0124 |
| Hub backorders | 1.0273 | 1.0173 \(\pm\) 0.0087 | 1.0173 \(\pm\) 0.0087 |
| Storeroom 1 backorders | 0.0726 | 0.0721 \(\pm\) 0.0016 | 0.0721 \(\pm\) 0.0016 |
The package and the chapter’s classes agree exactly, to every digit: they draw the demand streams from the same stream numbers and implement the same assumptions, so they follow the same sample path event for event. That is the strongest agreement two independent programs can show.
Lost sales are not available through a specification. A lost-sales system can be built object by object, but its fill rates then count only demands that were not lost, so they overstate service. The lost-sales example used the chapter’s own classes for that reason.
A node has one supplier. The network is a tree, so a storeroom sending some requests to its own shop and the rest to a depot is outside what a specification describes.
A periodic review interval is a constant, which covers every periodic example in the book.
Random number streams are the modeler’s responsibility. Identical streams with an identical structure give an identical sample path. That makes common random numbers possible, and it is also why runs meant to be independent must be given different streams.
The chapter’s package inventory.simulation has three nouns that become classes: the stock point, the policy it runs, and the supplier that fills its orders.
The event is not a class. A demand and a receipt are methods of the stock point, scheduled on the KSL’s calendar: two event methods and one decision method, where the event table puts them.
Lost sales is not a subclass. Whether an unmet demand waits is a property of the demand, so it is a flag; every policy runs under either setting without being written twice.
Periodic review is a policy, not a stock point. It is the one policy that owns an event, the review; under it the stock point’s decision method does nothing on a demand.
A two-level system is not a class. A stock point obtains stock by asking an InventoryFillerIfc to fill an order. A supplier with a lead time is one kind of filler, and so is another stock point behind a TransitLink. The coupling chapters 9 and 10 worked so hard to approximate is, here, simply the order of the calls.
val hub = RQSimInventory(m, 120, 92,
filler = LeadTimeSupplier(m, ConstantRV(2.0 / 12.0), name = "Supplier"), name = "Hub")
val store = RQSimInventory(m, 25, 49,
filler = TransitLink(m, hub, ConstantRV(1.0 / 12.0), name = "Transfer"), name = "Storeroom")
CustomerDemand(m, hub, ExponentialRV(1.0 / 250.0, streamNum = 1), lots(2), name = "HubCrews")
CustomerDemand(m, store, ExponentialRV(1.0 / 150.0, streamNum = 3), lots(4), name = "StoreCrews")### the cutout in job lots: storeroom (25, 49), hub (120, 92), in years
Storeroom:
ordering 504.5150 +/- 0.9402
holding 686.1641 +/- 1.6630
backorder 123.0035 +/- 2.9464
total 1313.6826 +/- 2.4588
orders a year 6.1153 +/- 0.0114
The storeroom’s filler is a TransitLink to the hub: each storeroom order becomes a demand at the hub, and what the hub ships arrives a month later. Nothing else distinguishes a hub from a storeroom.
There is one test for every example with an exact column, and each asserts the agreement the example states: the ledger test asserts equality row by row, since nothing in it is random, and every other test asserts that each interval covers the exact value.
A test that checks eleven measures from the same runs, each at 95%, will find a miss among them more often than not, and the measures of one run tend to miss together. Each test builds its intervals at the Bonferroni level, \(1 - 0.05/k\) for \(k\) measures, so a correct model passes the whole family at 95%.
The tests run at a fixed seed, so a test that passes once passes every time. They do not pin the printed simulated digits, which a later KSL version may move within a half-width. What they guarantee is what matters: every interval still covers the exact value it was printed beside.
A two-level simulation carries the coupling whole, as one stock point supplying another.
VARI-METRIC is right to the third decimal on the utility’s plan; METRIC understates storeroom backorders by about 16%.
Four correlated misses are one event, passed as a family at the Bonferroni level and covered by longer runs; the mean-wait route understates storeroom backorders by 30%.
Chapter 10’s job-lot policies cost about 4% less than predicted: the moment route is conservative.
The KSL package reproduces the network to every digit, with stated limits.
No class is a two-level system; the coupling is the order of the calls, and every exact example is a test.
This was the last session of the course material.
Today’s material was Multi-Echelon Systems, The KSL Supply Chain Package and Designing the Software.
For review:
The chapter’s summary collects the rules of this unit in one place, and is worth reading as a review of the whole course.