The calculation algorithms, with worked examples. Identical to the white paper bundled with the application.

1. Scope of this paper

This document explains what Stack by F³ Lab computes, in the order the application computes it, with a worked number at every step. It is written for a design or quality engineer who knows GD&T and needs to defend a result to a customer — not for a statistician, and not as a substitute for the user guide, which explains how to drive the application.

Three method families run side by side on the same chain, always all three:

MethodThe question it answersWhat it costs you
Worst caseCan any legal combination of parts fail?Assumes extremes coincide — usually a much wider band than reality.
RSSHow wide is the band if the contributors vary independently?Only legitimate when they really are independent.
Monte CarloWhat fraction of assemblies is inside the limits?Only as good as the per-contributor distributions you assume.

Every number printed in this paper is produced by the application itself and is re-checked by the automated test suite on every build (TSA.Tests/WhitePaperTests.cs). Where the paper states a physical truth the application does not currently model, it says so, in section 10, in full.

Reading convention. Numbers in this paper use a decimal point. Lengths are millimetres unless stated otherwise. All results quoted are for the T ROOM slot of the bundled tutorial project (examples/tutorial.stack), Monte Carlo seed 12345, 1 000 000 samples.

2. The chain

A stack-up is a closed loop of vectors. You start at one surface, travel through the assembly along a chosen axis — one dimension per hop — and come back to where you started. The dimension you care about is the vector that closes the loop.

Each hop is a contributor: one row of the stack-up grid, carrying one drawing callout. A contributor gives the chain two things and nothing else:

  • a signed length — where the next hop starts;
  • a half-width — how far that length may wander.

The vector you draw on the canvas is the row. Its direction sets the sign; its assigned feature supplies the numbers.

The sign convention

The convention is absolute, not relative: a vector pointing right (or up, on a vertical stack) counts as +; a vector pointing left (or down) counts as . It does not matter which way the requirement was drawn, and it does not matter in which order the rows sit.

The requirement has no sign. The requirement vector is the result, not a term. It is drawn with an arrowhead at each end precisely because it has no direction to contribute — it is a span, and it never enters any sum. The application enforces this: a requirement row cannot even be given a feature.
face A face B face C REQUIREMENT — a span, no direction #1 +60.000 (points right → +) #2 −24.000 (points left → −) #3 +18.000 Σ = +60 − 24 + 18 = +54.000 — the nominal result, and the span the requirement measures
The chain as vectors. Direction alone decides the sign — right is plus, left is minus. The requirement (top) carries arrowheads at both ends and contributes nothing to the sum; it is what the sum is compared against.

Closing the loop

The nominal result is simply the signed sum of the contributions:

nominal = Σ contributioni

A chain that does not close geometrically still computes — the application adds up whatever rows are there — but the number then answers a question you did not ask. The canvas shows closure directly, and the grid's Σ row is always the same number the results strip prints.

Rows with no length. A geometric tolerance (a position zone, a flatness band) is a tolerance without a dimension. Such a row contributes zero to the nominal and only its half-width to the spread. That is why a chain can contain more rows than it has hops.

3. One contributor's numbers

Before any method runs, every row is converted to one canonical form: an equal-bilateral pair value ± half. Everything downstream — worst case, RSS, Monte Carlo, every temperature slot — reads only that pair.

Mean-centring, and the chain's two centres

What it means physically: a dimension written 10 +0.2/−0 does not vary about 10. It varies about 10.1, because 10.1 is the middle of the band the part is allowed to occupy. Adding the drawing's nominal would put the centre of the chain somewhere no part can be.

mean = nominal + (tol⁺ + tol⁻) / 2 (tol⁻ is stored negative)

So 10 +0.2/−0 enters the chain as 10.100, not 10.000, and that is what the grid's CONTRIB. column shows.

A chain therefore has TWO centres, and they answer different questions.
  • NOMINAL — what the drawing states. For the row above, 10.000000.
  • MEAN — the middle of the population: the nominal plus every row's mean shift. Here 10.100000.

Worst case is stated about the NOMINAL, because that is how a drawing reads its own limits, so this row prints 0.200000 above and 0.000000 below 10 — the drawing's own deviations, not 10.100 ±0.100. RSS, Monte Carlo and the closed-form σ are stated about the MEAN, because that is what the parts are centred on and what the sampler draws around. The limits are the same either way: 10.00010.200.

On a chain of symmetric rows — most chains — the two are the same number and the application shows one merged cell. Where they differ, the results strip splits that cell along the method rows, the grid's Σ cell names the mean in its tooltip, and the INFO column spells out the conversion on every affected row. The difference is never silent, and neither centre is ever hidden behind the other.

The half-width and ÷2

What it means physically: however lopsided the drawing's deviations, the width of the band does not change when you re-centre it. So the half width is half the total band:

half = |tol⁺ − tol⁻| / 2

A minus-sense row subtracts its mean but still adds its half-width — flipping a symmetric band leaves the band just as wide.

The ÷2 convention. A drawing states a geometric zone whole: a position callout of ⌀0.3 means the axis may lie anywhere inside a cylinder 0.3 across. A stack-up hop, however, runs from the axis to one side. So a geometric row contributes ±zone/2, and the application switches the /2 divisor on by itself when a row becomes geometric.

Asymmetric size → mean-centred 10.000 10.200 drawing nominal mean 10.100 half ±0.100 10 +0.2 / −0 → 10.100 ± 0.100 Geometric zone → ÷2 ⌀0.300 zone ±0.150 the chain runs axis → edge, so the row contributes half the zone
Left: the band is re-centred, never re-sized. Right: a whole zone becomes a ± half-width. Taking the whole zone instead is a deliberate opt-out — correct only when the chain crosses the entire zone rather than running from the axis to one side; the INFO column flags it when you do.

4. Worst case

What it means physically: every part in the assembly is simultaneously at its most unhelpful legal extreme. Not likely — but legal, and therefore something a customer can hand you.

result = Σ contributioni ± Σ halfi

The half-widths are added linearly. Signs never enter the tolerance sum: a minus-sense row can push the result either way just as a plus-sense row can.

One exception, and it is physical: the material bonus

That ± hides an assumption — that every row can reach both ends of the band. A Ⓜ/Ⓛ bonus earned from a feature that is itself in the chain cannot. The bonus is the departure of that feature from its material condition, so at the material condition it is exactly zero. Adding the whole bonus to both ends says the hole is at maximum material and away from it in the same breath. No such part exists.

The application therefore computes the two ends separately and asks every bonus how much of it exists at that extremity — none at its own material end, all of it at the far end:

MAX = Σ contributioni + Σ halfi · a⁺i MIN = Σ contributioni − Σ halfi · a⁻i (a ≡ 1 for every ordinary row)

Which end a bonus falls on is derived, never declared. Maximum material is the largest size for an external feature and the smallest for a hole, so the modifier and the hole/shaft answer decide it — and a minus-sense row enters the chain negated, which turns its material end round with it. Two bonuses in one chain need not land on the same end.

Worked example — one hole pass

A ⌀10.5 ±0.2 hole located by position ⌀0.3 Ⓜ, against a ⌀10 bolt, stacked the classic way with ÷2 throughout: the size contributes 5.25 ±0.100, the zone ±0.150, and the bonus — the whole size range 0.4, halved — ±0.200.

plain ± : 5.25 ± 0.450 → 4.800 … 5.700 honest : 5.25 −0.250 / +0.450 → 5.000 … 5.700

The MAX end is unchanged and always was right: at the largest hole the bonus really is fully earned, and the chain reaches 5.700. The MIN end moves up by exactly the bonus, 4.8005.000, because reaching it needs the hole at maximum material — and there the bonus does not exist. The band is asymmetric because physically it never was symmetric.

A bonus the sheet cannot explain is left alone. If the feature of size is not a row of this chain, nothing in the sheet says where that variation comes from — so the bonus keeps its full, symmetric zone, exactly as before. The correction applies only where the dependency is actually written down.

Worked example — tutorial sheet R-003

The chain is a floating-fastener assembly: two bolts of ⌀10 pass through two ⌀10.5 ±0.2 holes located by position ⌀0.3 Ⓜ, with a 60 basic hole spacing on each plate. The chain crosses four hole passes. Each pass contributes the hole radius (÷2 on the ±0.2 band → ±0.100), the position zone (÷2 on ⌀0.3 → ±0.150) and the Ⓜ bonus that hole earns as it departs from maximum material (the whole 0.4 size range, ÷2 → ±0.200).

TermCounthalf eachΣ half
hole size, ±0.2 halved40.1000.400
position zone ⌀0.3 halved40.1500.600
Ⓜ bonus — the hole's 0.4 size range, halved40.2000.800
bolts, basic spacing (no tolerance)40.0000.000
reach → MIN — every hole at maximum material, so no bonus1.000
reach → MAX — every hole at least material, so the whole bonus1.800
half the span1.400

Nominal 1.000, so the worst-case band is 0.0002.800asymmetric, because the bonus exists only at the end where the holes are away from maximum material. The requirement is "clearance ≥ 0", and worst case just reaches it.

What worst case costs. It answers a question about legality, not about production. On a chain of n similar contributors the worst-case band grows like n, while the band a real population occupies grows like √n. Designing every chain to pass worst case is, past a handful of contributors, a way of buying tolerance you will never need.

5. RSS

What it means physically: the contributors are separate features made by separate operations, so they do not conspire. When one is at its high limit the next is somewhere in the middle. Variances — not widths — add.

halfRSS = √( Σ halfi² )

In this application RSS is defined on the stated half-widths, exactly as textbooks define it. It does not read the per-row distribution setting; that is Monte Carlo's job.

Worked example — the same R-003 chain

Σ half = 4×0.100 + 4×0.150 + 4×0.200 = 1.800000 √Σ half² = √(4×0.100² + 4×0.150² + 4×0.200²) = √0.290000 = 0.538516

Same inputs, same chain: half the worst-case span is 1.400 and the RSS half is 0.538516 — a factor of 2.6.

Worth a glance forward: three times the simulated σ of this very chain (§10) is 0.542627, so on this drawing RSS lands within a percent of the honest 99.73 % band. That is a coincidence of these particular tolerances, not a law — RSS is defined on the DECLARED half-widths and knows nothing about material bonuses — but it is the reason RSS is left exactly as textbooks define it: it stays comparable with what another tool would print for the same rows.

Worst case — lengths laid end to end Σ half = 1.000000 0.100 0.150 RSS — the same lengths added as squares √Σ half² = 0.360555 the same eight tolerances — 2.8× shorter 0.100 0.150 0.180 why: independent → at right angles worst case would say 0.250
Identical inputs, two ways of adding them. Worst case lays the half-widths end to end. RSS treats them as perpendicular legs, so the result is the diagonal — always shorter, and shorter by more the more contributors there are.
When RSS is legitimate. Only when the contributors are genuinely independent. Two dimensions machined in one setup on one machine, two features of the same casting, or — see §8.2 — a size and the material bonus it earns, are not independent, and RSS will read narrower than the truth. RSS also says nothing about yield: it is a band, not a population.

6. Monte Carlo

What it means physically: build a million virtual assemblies. For each one, draw a realised value for every contributor from an assumed distribution, add the chain up, and record the result. Then count.

Distributions and the ±3σ convention

Each row carries its own distribution:

SettingSampled asVariance from half-width h
Normal (default)Box–Muller, σ = h/3, untruncatedh²/9
Uniformflat on [−h, +h]h²/3
Triangularsum of two uniforms, symmetric on [−h, +h]h²/6
±tolerance = ±3σ is a convention, not a law. Nothing in metrology says a tolerance band is three standard deviations wide. It is the industry's usual working assumption — a process centred and capable enough that the tolerance limits sit at 3σ — and this application adopts it explicitly. If your process is wider or narrower than that, the Monte Carlo numbers move accordingly, and the honest fix is to state the real σ by widening or narrowing the row's half-width, or to choose Uniform where the band is a hard limit rather than a process.

Because the Normal draw is untruncated, about 0.27 % of samples fall outside the stated band of any given row. That is intentional: a real process does produce the occasional out-of-tolerance part, and truncating would quietly promise an inspection gate the drawing does not require. It does mean the simulated minimum and maximum can sit outside the worst-case band, which is not a contradiction — they are answers to different questions.

Yield, Cpk and the acceptance rule

Yield is counted, not modelled: the fraction of simulated assemblies that landed inside the stated limits. A limit that is not stated does not constrain anything, so a one-sided requirement ("clearance ≥ 0") is judged on its one side only.

Cpk asks how many 3σ steps fit between the mean and the nearest stated limit:

Cpk = min( (mean − Min) / 3σ , (Max − mean) / 3σ ) — over the limits that exist

The acceptance rule — one rule, evaluated once:

PASS requires BOTH. The achieved yield must reach the project's yield target and, when a Cpk target is set, the achieved Cpk must reach it too. Yield alone is not enough: a distribution can sit almost entirely inside the limits while hugging one of them, which is exactly the situation Cpk exists to catch.

This matters beyond pedantry. The Monte Carlo panel badge, the requirement's GO/NO GO, the Results tab and the printed report all call the same function on the same simulated run. Earlier versions let each surface ask its own question — the strip asked whether a ±Z·σ band fitted, the panel asked yield and Cpk, the report asked whether literally zero samples fell outside — and on one real field project those three read PASS, FAIL and FAIL. They cannot disagree now, by construction.

0.00.20.4 0.60.81.0 1.21.41.6 clearance, mm μ = 0.999959 ±3σ = ±0.360873 Min = 0 (no Max stated) 3 × target Cpk 1.67 × σ = 0.602 the target only reaches 0.397 — the real limit is further away still R-003, T ROOM — achieved Cpk 2.771 against a target of 1.67
Cpk read geometrically. "Cpk ≥ 1.67" means the nearest stated limit must lie at least 3 × 1.67 × σ from the mean. Here it lies further, so the sheet passes on capability as well as on yield. The application draws exactly these thresholds on the Monte Carlo panel, computed once so the chart and the verdict cannot disagree.

7. Temperature

What it means physically: a part at 250 °C is not a part at 20 °C with a correction bolted on. It is a different part — longer, and its tolerance band is longer too, because the band is also a length of the same material.

Every temperature slot is a complete, separate calculation. T MIN, T ROOM and T MAX each run the whole chain — nominals, tolerances, worst case, RSS and a full Monte Carlo simulation. A room-temperature result is never scaled afterwards.

At a slot, every contributor is multiplied by its own factor, taken from the material of the part that owns the feature:

factor = 1 + strain(room → T) contribution(T) = contribution(room) × factor half(T) = half(room) × factor

The strain is the integral of α(T)·dT over the range, so a material defined by a nonlinear α(T) curve is integrated properly rather than evaluated at a single α. Steel and aluminium in the same chain each move by their own amount; that is the whole point.

Worked example — R-003 across the sweep

The project is set to −50 °C / 20 °C / 250 °C. The plates are aluminium, the bolts steel, both with α(T) curves.

Slotnominalworst-case minworst-case maxhalf
T MIN (−50 °C) 0.983768 -0.014683 2.780979 1.397831
T ROOM (20 °C) 1.000000 0.000000 2.800000 1.400000
T MAX (250 °C) 1.059791 0.054140 2.869963 1.407911

Read the last column: the tolerance band itself is 0.15 % narrower cold and 0.57 % wider hot. That is small here, and it is not small on a long chain in a plastic or an alloy with a large α — which is exactly why the application refuses to scale a finished result.

Note also the verdict: the requirement is clearance ≥ 0, and the cold slot's worst case is −0.014683. The sheet passes at room and fails cold. A single-temperature analysis would have missed it.

0.00.6 1.01.4 1.82.2 Min = 0 T MIN −0.015 … 1.982 T ROOM 0.000 … 2.000 T MAX 0.054 … 2.065 the cold band crosses the limit — FAIL at T MIN R-003 worst case at each slot — three separate calculations
Three complete bands, not one band shifted. The centre marks are the slot nominals; the band widths differ too, because tolerances expand with the material that carries them.

8. GD&T in the chain

Geometric callouts enter a stack-up as three different kinds of thing, and it is worth keeping them apart.

CalloutWhat it gives the chainContributes to the nominal?
Position, profile, flatness, … (the zone)a half-width, ±zone/2 by defaultno
Datum reference (RMB)where the chain starts — nothing numericno
Ⓜ / Ⓛ on the tolerance (MMC/LMC)a bonus half-widthno
Ⓜ / Ⓛ on a datum (MMB/LMB)a datum shift half-widthno
Ⓤ unequally disposed zonethe zone as a centred band, plus an explicit shift vectorthe shift vector does
Two notation-only modifiers. Ⓟ (projected zone) and the Ⓤ symbol on a frame are carried by the drawing but enter no arithmetic. A Ⓤ zone is modelled as a centred zone plus a separate basic vector for the midpoint shift, because the direction of that shift depends on how the surface sits in the stack and cannot be read off the callout.

What a bonus IS

Physically: a Ⓜ modifier says the stated zone applies when the feature is at maximum material. Make the hole bigger than that, and the extra material you removed is room the axis can use — the zone grows by exactly the amount the feature departed from the material condition.

bonus = |realised size − material-condition size| (0 at the material condition) max bonus = the whole size tolerance range

Which end of the size band is the "material condition" depends on whether the feature is internal or external:

Ⓜ MMC is at…Ⓛ LMC is at…
External (a shaft)the largest sizethe smallest size
Internal (a hole)the smallest sizethe largest size
A ⌀10.5 ±0.2 hole located by position ⌀0.3 Ⓜ — seen down the axis at MMC — hole ⌀10.3, the smallest zone ⌀0.3 bonus = 0 at LMC — hole ⌀10.7, the largest zone ⌀0.7 — grown by the whole bonus bonus = 0.4 (the whole size range) inner envelope ⌀10.0 the same circle in both — the VIRTUAL CONDITION hole grows 0.4 zone grows 0.4 Dashed grey: the same hole pushed to the very edge of its zone — in both cases its near side lands exactly on the green circle. Radial distances from the ⌀10.0 envelope are drawn 120× — at true scale the two holes would look identical.
The bonus is not extra freedom given away — it is freedom the part paid for. Every millimetre the hole departs from maximum material is spent on displacement, so the green inner envelope (the virtual condition) is the same circle in both pictures. That single fact is the whole of the boundary method in section 9 — and the reason section 10 exists.

A bonus is derived, not drawn

Here is the trap, and how the application avoids it.

A bonus is a function of a number the simulation is already drawing. If the feature of size is a member of the same chain, then sampling the bonus as if it were an independent random variable counts the same physical variation twice: once as the size row, once as the bonus row. The band comes out too wide and the yield too pessimistic.

The application stores the dependency as the bonus's zone formula=abs(U@ID−L@ID) over the feature of size — and the simulator reads it. Per sample it computes an availability a ∈ [0, 1]: the fraction of the maximum bonus that this virtual part actually earned.

a = ( h − end·δ ) / 2h (a size term: 0 at the material condition, 1 at the far end) a = 1 − |δ| / h (a datum's own location zone: all of it at true position, none at the edge) sampled bonus = draw × a

Worked example — one shaft, three rows

A ⌀20 ±0.05 shaft, a ⌀0.1 position zone, and the MMC bonus the shaft earns (±0.05 after the ÷2). Normal distributions throughout.

Quantityvalue
Σ of the declared halves0.150000
worst case, reach → MAX (the shaft's Ⓜ bonus is spent there)0.100000
worst case, reach → MIN (the bonus is whole there)0.150000
RSS half (√Σ h²)0.086603
σ if all three rows were independent0.028868
surviving variance fraction of the bonus, E[a²]0.277778
σ with the bonus still drawn separately, deflated by E[a²]0.025154
σ with the bonus widening the zone it belongs to0.030174

Read the last two rows together — they are the whole story. Deflating the bonus removes the double count, and that part was never real spread. But a bonus does not sit beside the position zone as a second error: it makes the zone the same error lives in wider. Drawing them as two rows and adding the variances leaves out the cross term 2·hzone·hbonus·E[a]·E[u²], and putting it back raises σ by 20.0 % — to 0.030174, which is what the application simulates and what its closed-form σ reports. A bonus whose source is not in the chain is fully earned in every sample, the same read worst case takes.

Worst case follows the same physics. Since 1.2.0 the two ends of the worst-case band are computed separately (§4), so the shaft's Ⓜ bonus counts at the MIN end — where the shaft is small and has really earned it — and not at the MAX end, where the shaft is at maximum material. On the chain above that is +0.100 / −0.150, not the ±0.150 the sum of the declared halves gives. The MIN end is unchanged; it always was right.
RSS deliberately keeps its band semantics. RSS is defined on the stated tolerances, and that is what it stays. Honouring the correlation would narrow it by the same factor as σ — the bonus row's variance deflates by E[a²] = 0.277778, so the ±3σ band would go 0.086603 → 0.075462. Read Monte Carlo (or the ±Z·σ band) when you want the corrected statistical answer.

9. The boundary method

Everything above stacks a located feature as several contributors: its size, the zone that locates it, and any bonuses. Many engineers never do it that way. They collapse the lot into the two envelopes the feature can never cross and stack a single number.

Both schools are supported. They are alternatives, never layers.

The derivation

The contract is one line:

nominal = (inner + outer) / 2 tolerance = ± (outer − inner) / 2

Let the feature of size have mid-of-band mid and half-range h (so minSize = mid − h, maxSize = mid + h, and the full range is 2h), let t be the geometric tolerance zone and d the datum-shift allowance.

Without a material modifier (RFS) there is no bonus. The zone applies in full at every realised size, so both envelopes sit one zone outside the size limits and the nominal does not move:

[ minSize − t − d , maxSize + t + d ] → nominal = mid, half = h + t + d

With Ⓜ or Ⓛ the modifier pins the envelope generated on its own material side — that is ASME Y14.5's virtual condition, and the picture in §8.1 is exactly why: every unit of bonus earned by departing from the material condition is spent on displacement, so that envelope never moves. The other side opens by the whole size range 2h — the resultant condition:

outer pinned (external Ⓜ, internal Ⓛ): [ minSize − t − d − 2h , maxSize + t + d ] → nominal = mid − h inner pinned (external Ⓛ, internal Ⓜ): [ minSize − t − d , maxSize + t + d + 2h ] → nominal = mid + h either way: half = 2h + t + d

The full table — ⌀10 ±0.1 located by position ⌀0.2

FeatureModifierinnerouternominalhalf
shaft (external)Ⓜ MMC 9.510.3 9.90.4
shaft (external)RFS 9.710.3 10.00.3
shaft (external)Ⓛ LMC 9.710.5 10.10.4
hole (internal)Ⓜ MMC 9.710.5 10.10.4
hole (internal)RFS 9.710.3 10.00.3
hole (internal)Ⓛ LMC 9.510.3 9.90.4
Hole ⌀10.5 ±0.2, position ⌀0.3 Ⓜ — the tutorial's boundary row 10.010.410.8 11.211.6 size band 10.3 … 10.7 boundary envelope 10.0 … 11.4 → 10.7 ± 0.7 VIRTUAL CONDITION 10.3 − 0.3 = 10.0 pinned — never moves RESULTANT CONDITION 10.7 + 0.3 + 0.4 = 11.4 opens by the whole size range midpoint 10.7
The tutorial's own boundary feature. Note where the midpoint lands: 10.7 is the largest size the hole may be. It is the arithmetic centre of an asymmetric pair, not any typical part. Hold that thought — section 10 is about what happens when a statistical method is handed that midpoint.

The modifier rules

What goes into a boundary is decided by the modifiers on the drawing, and they form a sequence, not four free inputs:

  1. Is there a position/location tolerance at all? Without one there is no zone t and no modifier to ask about.
  2. Does that tolerance carry Ⓜ/Ⓛ? If so the bonus is taken automatically from the feature of size — it is the size range. It is never a separate pick and never a typed number, because a bonus that does not equal the size range is not a bonus.
  3. Does the datum reference carry Ⓜ/Ⓛ (MMB/LMB)? Only then is there a datum shift, and only then does the datum's own feature of size — and its own tolerance to higher-order datums — enter the arithmetic. At RMB, d ≡ 0.
A convention we chose, stated plainly. The datum shift d relaxes both envelopes and never moves the nominal. Strictly, a datum shift leaves the virtual condition untouched relative to the datum reference frame. But a stack-up measures from the actual datum feature, which may sit anywhere inside that allowance in either direction — so widening both sides is the honest and conservative read for this purpose. Ⓜ and Ⓛ on a datum relax the envelope identically, so the two are not distinguished.
A defect we fixed. Up to and including version 1.1.0 the derivation applied the Ⓜ answer unconditionally: it widened by the whole size range and moved the nominal to the least-material size even when the tolerance was stated RFS. A tolerance stated RFS earns no bonus, so the shipped result was both too wide (±0.4 instead of ±0.3) and in the wrong place (9.9 instead of 10.0). The modifier is now an input, not an assumption. Existing files re-open on the Ⓜ branch — the only shape earlier versions could write — and reproduce their stored numbers exactly.
The two schools must not be layered. A boundary row already contains the size, the geometric tolerance and every bonus. Stacking any of those ingredients again — as its own row, or as a bonus earned from one of them — double counts it. The application detects this and reports it as a warning on the analysis and in the alert strip. It warns and never corrects: the chain is your drawing of your own intent, and only you can say which school a given link belongs to.

10. The honest section — what the statistical path samples

The boundary method is correct as an envelope, and it is the right input for worst case. Handed to a statistical method as if it were a distribution, it misleads — so the application no longer does that. This section says exactly what was wrong, exactly how much, what the engine does instead, and where it still cannot know.

None of what follows is a rumour. It was measured against the application's own engine and cross-checked against a direct integration of the physics.

The one-sentence version. Worst case stacks the ROWS of a chain, because a row states limits and limits are what worst case adds up. The statistical methods stack the PHYSICS those rows describe: a Ⓜ/Ⓛ bonus widens the zone it belongs to instead of being drawn as a second error, and a composed boundary is taken back apart into the size, the zone and the bonus it collapsed. The same drawing therefore gives the same band and the same distribution whichever school you write it in — which is the only way either of them can be checked.

One boundary row, examined

Take the tutorial's own case: a bolt of ⌀10 passing through a hole ⌀10.5 ±0.2 located by position ⌀0.3 Ⓜ. One pass through that hole contributes to the chain the hole's radius plus wherever the axis actually landed. Written as a physical model:

D ~ Normal(10.5, σ = 0.2/3) the realised hole diameter a = clip( (D − 10.3) / 0.4 , 0, 1 ) how much bonus this hole earned: 0 at MMC, 1 at LMC r = 0.15 + 0.2·a the radial position zone AT THAT SIZE u ~ Normal(0, σ = r/3) where the axis actually landed c = D/2 + u this pass's contribution

Two facts follow, and they pull in opposite directions.

Fact one — the envelope is right

Work out the extremes of c for any in-tolerance part and you get exactly 5.005.70 — which is the derived boundary 10.0 / 11.4, halved. The boundary method computes the right envelope. Worst case reads that envelope and is right to.

Fact two — the distribution inside it is not right

meanσshape
The truth (the model above) 5.2500 0.09044 right-skewed, skew +0.249
The envelope read as a distribution (10.7 ± 0.7, halved, Normal) 5.3500 0.11667 symmetric

The variance of the truth splits cleanly:

Var(c) = Var(D/2) + E[r²]/9 = 0.001111 + 0.007068 = 0.008179 → σ = 0.09044

So reading the envelope as a distribution makes σ 1.29× too wide and the mean 0.10 too high. Two independent errors produce that, and it helps to name them separately:

  1. The midpoint of an asymmetric envelope pair is not the centre of mass. 5.35 is half of 10.7 — the least-material size. Putting the distribution's centre there asserts that the typical hole is the largest hole the drawing allows. It is not; the typical hole is 10.5.
  2. The half-width 0.35 is a linear worst-case sum. It is (0.4 size range + 0.3 zone) / 2 — the two added end to end, exactly as worst case adds them. Feeding a linear worst-case sum into a Gaussian as ±3σ is precisely the double count that RSS exists to avoid.
What the engine does about it. For Monte Carlo, the closed-form σ and everything derived from them, a boundary you composed from a feature of size and a tolerance zone is taken back apart: the size, the zone and the material bonus are sampled separately, exactly as the classic school would. Worst case keeps reading the envelope — that is what an envelope is for, and §4 shows it is exact there. Two things follow. The row's DISTRIBUTION setting now applies to the size and the zone inside the boundary rather than to the envelope, so the old advice to switch a boundary row to Uniform no longer applies. And a boundary whose two envelopes were typed in by hand, or one carrying a datum-shift (MMB/LMB) allowance, cannot be taken apart — there is nothing to read, and inventing a distribution for a datum shift would be the same mistake one level down. Such a row is still stacked as the envelope, the table above is exactly what it does, and the analysis says so in its warnings.

The joint space — why the corner is nearly empty

This is the picture that explains everything else. Two axes: how far the hole departed from its nominal size, and where its axis actually landed. Every real part is one dot in this field.

The permitted region is not a rectangle. The position zone grows with the hole, so the region is a wedge: narrow at maximum material, wide at least material.

Where parts actually land — and what the two boundaries mean here position deviation u −0.20 +0.20 MMC ⌀10.3 LMC ⌀10.7 c = 5.00 — the entire lower edge reachable at every hole size c = 5.70 ONE corner: hole at LMC AND axis at the zone edge horizontal axis: hole size deviation · vertical axis: position deviation u the zone widens with the hole, so the permitted region is a wedge, not a rectangle each dot = one virtual part (559 shown) what the boundary row assumes centre 5.35 5.00 5.70 a symmetric bell spread across the whole envelope, centred on the corner's side 1.6 % of its mass sits beyond 5.60 the truth puts 0.04 % there
The key picture. Left: the real joint space. The line c = 5.00 is the whole lower edge of the wedge — every hole size can reach the virtual condition, which is why worst case at the MIN end is exactly right. The line c = 5.70 touches a single corner: it needs the hole at least material and the axis at the very edge of the widened zone, both at once. That is not the tail of one variable; it is a corner of a two-dimensional space, and the dot cloud shows it is nearly empty. Right: the boundary row asks Monte Carlo to spread a symmetric bell over the whole envelope, which puts probability mass exactly where physics puts almost none.

Numbers for the same statement, computed exactly rather than sampled — the probability that one pass exceeds a given value:

c exceedstruththe modeloverstated by
5.500.586 %9.93 %17×
5.600.037 %1.606 %43×
5.700.0015 %0.135 %91×
A footnote on the last row. The truth column is not identically zero above 5.70 only because the application's Normal draw is untruncated by convention (see §6.1) — those samples are parts outside their own size or position tolerance. For parts that are in tolerance the probability of exceeding 5.70 is exactly zero.

Why worst case does not notice

This is the crux, and it is short. Worst case only ever uses the midpoint as a term in a sum and a difference:

5.35 − 0.35 = 5.00 5.35 + 0.35 = 5.70

The artificial midpoint cancels exactly. It never has to mean anything. Monte Carlo, by contrast, uses that midpoint as the centre of a distribution — and there, nothing cancels.

Which is why the midpoint is no longer shown anywhere. 5.35 is neither of the chain's two centres (§3.1): the drawing's nominal is 5.25 and so is the population's mean, both recovered from the feature of size the boundary collapsed. The envelope survives only inside the worst-case sum and difference above, where it cancels — so the ends are the same two doubles they always were, while the number printed between them is one a reader can act on. A boundary the application cannot take apart is the exception: there nothing is knowable, both centres fall back to the midpoint, and the analysis says so rather than inventing a nominal.

Two distributions, one axis

One pass through the hole — density of c 5.005.205.40 5.605.80 contribution of one pass, mm 5.00 5.70 ← the derived envelope, correct → truth — mean 5.2500, σ 0.09044, right-skewed the boundary row — mean 5.3500, σ 0.11667 5.2500 5.3500 0.10 too high Both curves are drawn from the arithmetic — the green one by numerical integration of the model above, the orange one from N(5.35, 0.35/3).
Same axis, same envelope. The truth (green) is narrower, sits lower and leans right — its long tail towards 5.70 is the corner of the joint space, and it is thin. The model (orange) is symmetric, wider, and centred where the truth has already started to fall away. Note that the model is not simply "conservative": it is shifted, and a shifted mean moves Cpk in whichever direction the shift happens to point.

R-003 against R-004 — the same drawing, twice

The bundled tutorial contains the same floating-fastener assembly built both ways: R-003 the classic school (size + zone as separate rows) and R-004 the boundary school. Same drawing, same topology, seed 12345, 1 000 000 samples, T ROOM. The third column is a direct integration of the physical model above — what the assembly actually does.

R-003 — classicR-004 — boundaryphysical reference
mean 0.999966 0.999955 1.000000
σ 0.181059 0.181085 0.180876
Cpk 1.841 1.841 1.843
worst case, MIN end 0.000 0.000 0.000
worst case, MAX end 2.800 2.800

The two schools now agree, and both agree with the physics. Worst case matches at both ends — the MAX ends differ by 0.000 mm, and in fact by 4 × 10⁻¹⁵ at every temperature slot, which is floating-point noise and nothing else. The simulated means differ by 1 × 10⁻⁵ and the simulated σ by 1.4 × 10⁻⁴; both sit 0.10 % above the integrated reference, which is inside the sampling error of a million draws (σ/√2n ≈ 0.07 %). The application's closed-form σ is the same double on both sheets — 0.18087578440110785 — because both sheets now describe the same physical model.

Worst case agreeing at the MIN end was never in doubt: at maximum material the bonus is zero, and maximum material is the extremity that sets the minimum clearance. Both schools describe the same virtual condition — the whole lower edge of the wedge in the picture above. Agreeing at the MAX end is the result of §4: up to 1.1.0 the classic sheet read −0.800 … 2.800, because the four bonuses were subtracted at the very extremity that earns none of them.

What each school had been getting wrong

Both errors are exact, and neither is a rounding artefact. Per hole pass, with the halves the chain actually carries (size ±0.10, zone ±0.15, Ⓜ bonus ±0.20):

Modelvariance / passσ of the chain
classic rows drawn independently, bonus deflated by E[a²] = 0.2777780.0048460.139222
+ the missing cross term 2·0.15·0.20·E[a]·E[u²] = 0.0033330.0081790.180876
the boundary envelope read as ±3σ, (0.35/3)²0.0136110.233333

The classic side was missing the cross term — 40.8 % of the honest variance of every located hole. A Ⓜ bonus is not a second position error beside the zone; it widens the zone the same error lives in, so the two share one draw and their widths add before the squaring, not after.

The boundary side was reading a linear worst-case sum as a standard deviation. Worst case adds 0.20 + 0.15 = 0.35 end to end; a simulator handed that number as ±3σ produces a distribution 1.290× too wide, and centred 0.10 per pass too high because the envelope's midpoint is the least-material size. Taking the row apart removes both at once — which is why R-004's simulated mean moved from 1.400 to 1.000 while its worst-case band did not move at all.

And the number printed between the two ends followed. R-004's sheet went on reading 1.400 beside a simulated mean of 1.000 for as long as the envelope midpoint was treated as a nominal. It is neither of the chain's centres (§3.1): the same decomposition that gives the statistics their mean gives the sheet its nominal, so R-003 and R-004 now state 1.000000 at every temperature slot — with 0.0000002.800000 unchanged to the last bit.

A side effect worth naming: the phantom thermal mass is gone. A boundary row's stated value is an envelope midpoint (10.7), not a length (10.5), and thermal growth is proportional to length — so R-004 used to grow 3.8 % more than R-003 between 20 °C and 250 °C, on a drawing where nothing is 3.8 % longer. Now that both paths stack the lengths, the two agree at every slot: 0.059791 mm each, simulated and stated alike. The envelope is still exactly what the worst-case ENDS are built from — that is what an envelope is for — it simply no longer masquerades as a centre.

What this means for you today

The rule of thumb.
  • Stack a located feature in whichever school you think in. Both give the same worst-case band and the same distribution now, so the choice is about how you read the drawing, not about which answer you get.
  • Compose a boundary, do not type it in, if you want statistics from it. A composed boundary knows what it is made of and the simulation takes it apart; two numbers typed by hand carry no such information, and the application says so in the analysis warnings instead of pretending.
  • Leave a boundary row's DISTRIBUTION on Normal. It now describes the size and the zone inside the boundary. The older advice in this paper — set a boundary row to Uniform — applied to sampling the envelope itself, and that is no longer what happens, except on a hand-typed boundary where it still holds.
  • A repeated feature is still a repeated INSTANCE. A feature is a callout on the drawing and each chain row is one instance of it — the four passes in R-003 are four different holes in two plates, not one hole counted four times. Drawing them independently is correct; the application also binds each Ⓜ bonus to the hole it stands next to, rather than to the first hole on the sheet.
  • Two things the model still does not claim. The realised position is drawn as a symmetric deviation inside the zone, not as a radial two-dimensional offset; and the ±tol = ±3σ convention is untruncated, so a few draws per thousand land outside their own tolerance. Both are stated in §6.1 and neither is hidden behind a boundary row any more.

Status. Resolved. The statistical path samples the physical model, the two schools agree with each other and with an independent integration of that model, and the envelope arithmetic — which was never in question — is unchanged. The remaining honest gap is the hand-typed boundary and the datum-shift allowance, both of which the application now reports rather than guessing at.

11. Validation & evidence

Nothing in this paper is a description of intent. Each statement is pinned by an automated test that runs on every build, and the build is red if any of them disagrees with the code.

EvidenceWhat it pins
docs/validation-report.pdf Six published worked examples from independent sources (a bearing clearance, a blocks-in-housing gap, a linear stack, an ISO 286 H8/h7 fit, a radial ⌀/2 case and a distribution check) recomputed by this engine and printed with their sources. The PDF is regenerated by the test suite and the test fails if any row prints a FAIL.
ValidationBenchmarkTests, PublishedExampleTests The engine against published numbers, to the published digits; two of them load the bundled example projects end to end.
BoundaryMethodTests Every branch of the boundary derivation, including the round-trip of files written by earlier versions.
BonusCorrelationTests The per-sample bonus identity, the sign of the correlation, the σ reduction with real numbers, and that an unlinked bonus behaves exactly as it always did.
PerSlotToleranceTests, ResultBandRowSetTests That Σ CONTRIB ± Σ TOLERANCE for a slot equals the MIN/MAX shown for that slot — the grid adds up to the results strip, digit for digit.
WorstCaseBonusTests That a Ⓜ/Ⓛ bonus is absent at maximum material in worst case too, at every temperature slot — and that the classic and the boundary school then produce the SAME band, asserted against each other rather than against literals.
StatisticalVerdictTests One acceptance rule across the panel, the strip, the Results tab and the reports.
WhitePaperTests This document. It re-reads both language versions, recomputes every marked number from the engine, and fails if the paper and the code have drifted apart — or if the two languages state different numbers.

The full test methodology is described in docs/TESTING.md; the algorithms above are specified for developers in docs/dev/domain-and-analysis.md.

Stack by F³ Lab — white paper on the calculation algorithms. Every marked number in this document is regenerated and re-checked by the test suite.