How to Score Cube Utilization Per Location
You have a location master with rated cube and a live on-hand feed with product dimensions, and you need one honest number per slot: what fraction of this location’s usable volume is actually working? The naive answer — occupied cube over rated cube — is wrong twice over. Rated cube includes beam and flue clearance the slot can never fill, and even a perfectly managed slot cannot tessellate real cartons to 100%, so dividing by the raw volume guarantees every location scores low and looks like it needs consolidating. This page builds the single-location scorer that gets it right, discounting both clearance and a packing-efficiency factor so that a well-packed slot reads near 1.0. It is the focused how-to behind the Space & Cube Utilization Scoring cluster.
Prerequisites
Confirm each of these before wiring the scorer into a reporting job:
- Python 3.10+ — the implementation uses
X | Noneunions,list[...]generics, and adataclassfor the result. - A location master — rated
gross_cube_ft3and a measured or defaultedclearance_factorper rack type, exported from the WMS. - A live on-hand feed —
occupied_cube_ft3per location, derived from unit dimensions times on-hand quantity, measured to the same envelope as gross cube. - A packing-efficiency estimate — the best realistic fill fraction for the storage medium, from a stacking study or a conservative
0.85default for mixed cartons. - Consistent units — gross and occupied cube in the same unit (cubic feet here); a unit mismatch is the fastest way to a nonsense score.
Configuration Block
Every tunable lives in one externalized profile keyed by rack type, so a pallet lane and a shelf bin can carry different clearance and packing assumptions without touching code. The two levers that decide the score are clearance_factor (volume the slot structurally cannot use) and packing_efficiency (volume real cartons cannot tessellate into).
# cube_score.yaml — one profile per rack/storage type
selective_pallet:
clearance_factor: 0.12 # beam + flue + handling clearance as a fraction of gross
packing_efficiency: 0.85 # best realistic fill; score normalizes so this reads ~1.0
over_cube_tolerance: 0.02 # occupied may exceed effective by this before it is flagged
shelf_bin:
clearance_factor: 0.06
packing_efficiency: 0.78 # loose eaches tessellate worse than palletized cases
over_cube_tolerance: 0.05
# Equivalent Python config dict consumed by the scorer
CUBE_SCORE = {
"selective_pallet": {
"clearance_factor": 0.12,
"packing_efficiency": 0.85,
"over_cube_tolerance": 0.02,
},
"shelf_bin": {
"clearance_factor": 0.06,
"packing_efficiency": 0.78,
"over_cube_tolerance": 0.05,
},
}
Implementation
The scorer is one focused function. It computes usable cube from gross net of clearance, discounts that to effective capacity with the packing-efficiency factor, then divides occupied cube by effective capacity to produce a score where 1.0 means “as full as this medium realistically packs.” It returns a typed result carrying the intermediate volumes so the caller can trend reclaimable cube, not just the ratio, and it flags the two anomalies that signal bad input: an over-cubed slot and a non-positive capacity.
from __future__ import annotations
import logging
from dataclasses import dataclass
logger = logging.getLogger("space.cube_score")
@dataclass(frozen=True)
class CubeScore:
location_id: str
utilization: float # occupied / effective capacity, clamped to [0, 1]
effective_cube_ft3: float # usable cube after the packing-efficiency discount
reclaimable_cube_ft3: float # effective capacity currently empty
flag: str # ok | over_cube | no_capacity
def score_location_cube(
location_id: str,
gross_cube_ft3: float,
occupied_cube_ft3: float,
clearance_factor: float,
packing_efficiency: float,
over_cube_tolerance: float = 0.02,
) -> CubeScore:
"""Score one location's cube utilization as occupied / (usable × packing efficiency)."""
usable = gross_cube_ft3 * (1.0 - clearance_factor)
effective = usable * packing_efficiency
if effective <= 0.0:
logger.warning("Non-positive effective cube at %s (gross=%.1f, clr=%.2f, pack=%.2f)",
location_id, gross_cube_ft3, clearance_factor, packing_efficiency)
return CubeScore(location_id, 0.0, 0.0, 0.0, "no_capacity")
ratio = occupied_cube_ft3 / effective
flag = "ok"
if ratio > 1.0 + over_cube_tolerance:
logger.warning("Over-cube at %s: occupied %.1f > effective %.1f (ratio %.2f)",
location_id, occupied_cube_ft3, effective, ratio)
flag = "over_cube"
utilization = min(max(ratio, 0.0), 1.0)
reclaimable = max(effective - occupied_cube_ft3, 0.0)
logger.info("Scored %s: U=%.3f, reclaimable=%.1f ft3 [%s]",
location_id, utilization, reclaimable, flag)
return CubeScore(location_id, utilization, effective, reclaimable, flag)
Step-by-Step Walkthrough
- Net out structural clearance.
usable = gross_cube_ft3 * (1 - clearance_factor)removes the beam, flue, and handling clearance the slot can never carry inventory in. Readingclearance_factorfrom the rack-type profile keeps a shelf bin (little clearance, ~0.06) and a pallet lane (more, ~0.12) honest against the same code. - Discount to effective capacity.
effective = usable * packing_efficiencyaccounts for the geometry real cartons cannot tessellate into. This is the step that makes the score reachable: because the denominator is what the medium realistically packs, a well-run slot lands near1.0instead of being permanently capped in the 0.8s. - Guard the denominator. A
gross_cube_ft3of zero, aclearance_factorof1.0, or apacking_efficiencyof0.0all drive effective capacity to zero. The function returns ano_capacityflag rather than dividing by zero, so one malformed master row never aborts a facility-wide scoring pass. - Divide and detect over-cube.
occupied / effectiveis the raw ratio. A value above1 + over_cube_tolerancemeans occupied cube exceeds what the slot can hold — almost always a dimension or unit error upstream — so it is flaggedover_cubebefore the score is clamped to[0, 1]. - Report reclaimable cube.
reclaimable_cube_ft3is the empty portion of effective capacity. It is the number that actually drives right-sizing: a0.40utilization on a 120-cubic-foot lane exposes ~72 reclaimable cubic feet, which is what the re-slotting economics weigh against the move cost.
Verification
Assert the scorer’s invariants directly — a full slot reads near 1.0, an over-cubed slot is flagged not crashed, and a degenerate master row returns no_capacity. These checks run without any live feed.
import logging
logging.basicConfig(level=logging.INFO)
# A pallet lane packed to its realistic maximum should score ~1.0, not ~0.75.
full = score_location_cube("A-01", gross_cube_ft3=100.0, occupied_cube_ft3=75.0,
clearance_factor=0.12, packing_efficiency=0.85)
assert 0.99 <= full.utilization <= 1.0, full.utilization
# A half-empty lane exposes reclaimable cube for the re-slotting break-even.
half = score_location_cube("A-02", gross_cube_ft3=100.0, occupied_cube_ft3=33.0,
clearance_factor=0.12, packing_efficiency=0.85)
assert round(half.utilization, 2) == 0.44, half.utilization
assert half.reclaimable_cube_ft3 > 40.0
# A dimension error must flag, not crash.
bad = score_location_cube("A-03", gross_cube_ft3=100.0, occupied_cube_ft3=200.0,
clearance_factor=0.12, packing_efficiency=0.85)
assert bad.flag == "over_cube" and bad.utilization == 1.0
# A degenerate master row must degrade gracefully.
degen = score_location_cube("A-04", gross_cube_ft3=0.0, occupied_cube_ft3=0.0,
clearance_factor=0.12, packing_efficiency=0.85)
assert degen.flag == "no_capacity"
print(f"OK — full={full.utilization:.2f}, half={half.utilization:.2f}, "
f"reclaimable={half.reclaimable_cube_ft3:.0f} ft3")
Sample expected output:
INFO:space.cube_score:Scored A-01: U=1.000, reclaimable=0.0 ft3 [ok]
INFO:space.cube_score:Scored A-02: U=0.444, reclaimable=42.6 ft3 [ok]
WARNING:space.cube_score:Over-cube at A-03: occupied 200.0 > effective 74.8 (ratio 2.67)
OK — full=1.00, half=0.44, reclaimable=43 ft3
Common Pitfalls
- Dividing by rated cube instead of effective capacity. Skip the packing-efficiency discount and every location caps out in the low 0.8s, so real over-packing hides and healthy slots read as under-filled. Always normalize against effective capacity so
1.0is genuinely reachable. - A packing efficiency copied across media. Palletized cases tessellate far better than loose eaches; using
0.85for a shelf bin that really packs at0.78overstates its capacity and understates its utilization. Keep onepacking_efficiencyper rack type in the config, not a single global constant. - Occupied and gross measured to different envelopes. If occupied cube comes from tight case dimensions but gross is measured to the rack rails, the ratio is biased low everywhere and every slot looks like a consolidation candidate. Measure both to the same usable envelope and reconcile a sample by hand.
- Ignoring the over-cube flag. An
over_cuberesult is not a full slot — it is a data defect (wrong unit, doubled quantity, stale dimension). Route flagged locations to a data-quality queue rather than clamping and forgetting; a run of them usually points at one bad SKU dimension record.
Related
- Space & Cube Utilization Scoring — the parent guide covering honeycombing detection, the facility roll-up, and the velocity interaction.
- Threshold Optimization for Re-slotting — where the reclaimable-cube figure meets the move-cost break-even.
- Travel-Distance & Pick-Path Cost Modeling — the travel model a right-sizing pass reshapes once cube is reclaimed.