Concepts
Deep Cost Modeling
Bottom-up cost decomposition, lot-size economics, learning curves, and breakpoints.
Every ARCNM quote is a curve over batch size, not a single number. Setup amortises over more units, fixtures amortise once, tooling has a finite life, learning compounds. Deep Cost Modeling decomposes all four — bottom-up, per process, per coefficient — so every euro of the unit price traces back to a named source.
The cost equation
unit_cost = material_cost
+ (setup_time / n) × labour_rate ← amortised setup
+ cycle_time × labour_rate × attendance ← run labour
+ cycle_time × machine_rate ← machine burden
+ tooling_amortisation
+ overhead_variable + overhead_fixed
+ calibration_adjustment ← see Calibration
attendance is the share of an operator the machine consumes while it
runs — one operator tending two machines is 0.5. Setup is always fully
attended.
Does your machine rate include the operator?
Both conventions are in everyday use: a machine-only rate with labour
billed beside it, and an all-in workstation rate with the operator
inside. The engine prices in the first, so a machine carries
rate_operator_eur_per_h — the operator wage its rate already contains.
Leave it at 0 (the default) if your hourly rate covers the machine
only. Set it if your rate is all-in: that wage is then lifted off the
machine line and billed once, on the labour line, instead of twice.
A rate produced by the VDI 3258 calculator is all-in, and the calculator fills this in for you.
Each coefficient (labour rate, machine rate, feed, overhead) resolves through the environment cascade. Material mass uses a discipline-aware blank:
- Milling: rectangular blank (raw bar minus envelope + chip-overrun)
- Turning: cylindrical bar
- Sheet metal: sheet blank
- Casting: poured-mass-plus-runner
Setup time is amortised into labour (operator-present during setup — HGB §255 / IAS 2 convention), not split into a separate line item.
The lot-size curve
POST /calculations always returns the full curve. Default
grid:
n = 1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500
Each grid point carries:
{
"quantity": 50,
"unit_cost_eur": 12.84,
"direct_unit_cost": 10.62,
"overhead_var": 1.45,
"overhead_fixed": 0.77,
"machine_id_at_n": "machine_haas_vf2"
}
cost_at(n) linearly interpolates between grid points. For curves
where you need a non-default grid (e.g. n = 7, 350), pass lot_grid
on the request and the exact points are computed.
Why machine_id_at_n?
Optimal machine choice changes with batch size:
- n = 1: a 3-axis vertical mill with manual setup.
- n = 50: 5-axis mill with a vise tombstone.
- n = 1000: a dedicated horizontal machining center with a pallet changer.
machine_id_at_n records which machine was selected at each point so
the audit row tells you why the price stepped.
Breakpoints — the economically meaningful kinks
The curve isn't smooth; it has kinks. We surface them as
breakpoint records so a UI can render "the price drops at n=50
because setup amortises here":
{
"breakpoints": [
{
"kind": "setup_run_crossover",
"quantity": 25,
"unit_cost_eur": 18.42,
"rationale": "Per-unit amortised setup falls below cycle-time labour"
},
{
"kind": "machine_class_crossover",
"quantity": 250,
"unit_cost_eur": 11.20,
"rationale": "5-axis machining-center cheaper than 3-axis at this volume"
}
]
}
All breakpoint kinds — every one computed from that calculation's own figures, never from generic assumptions:
kind |
What it means |
|---|---|
setup_run_crossover |
Amortised setup falls below 50 % of the unit cost |
fixture_amortisation |
The fixture hardware (tiered by the part's own setup count × envelope) amortises below €1/part |
machine_class_crossover |
A different machine class becomes cheaper at this volume |
subcontract_lot_minimum |
An external lot minimum (heat treat, coating) amortises below €1/part |
Walk breakpoints directly to drive a tooltip UI.
The lot-size optimum — production plus inventory
The cheapest €/piece on the curve is always the biggest lot — that is
amortisation, not an optimum. The recommendation ARCNM makes instead
minimises the total annual cost, decomposed the way real
manufacturing economics work: lot-fixed costs (setup, per-lot
programming, transfer, order minimums — the per-lot cost K that
degrades with lot size) plus lot-variable cost (cycle time,
material — proportional to quantity, taken at cumulative-annual
learning so a big lot is never credited with learning the shop reaches
anyway once it produces the year's demand in any lot split) plus the
cost of holding the resulting inventory (capital tied up in stock
at an environment-tunable rate, plus physical storage per kg on the
finished part's mass), capped at one year of demand.
Two refinements make the recommendation realistic rather than mathematically cute:
- Flat-optimum band. The total-cost curve is famously flat around
its minimum (running
x·q*costs½(x + 1/x)of the optimum's fixed/holding trade — +37 % quantity is within 5 %). The raw argmin would ride a sub-percent slope to the grid maximum; ARCNM instead recommends the smallest lot within 5 % of the minimum — near- identical cost, least capital tied up, and headroom for MOQs, pack sizes, storage limits and shelf-life (the literature's own non-cost order-quantity gates).band_min_quantity/band_max_quantitybound the near-optimal range; an ordered lot already inside the band is reported as optimal instead of churned. - Stock reach. Every lot-size recommendation carries its Reichweite (months of stock) — the standard over-stocking Kennzahl — so "lot 2 500 at 100/yr" reads as 25 years of stock, not as a price win.
The classic Andler / EOQ closed form (q* = √(2·D·K/h), with the true
un-amortised per-lot cost as K and h evaluated on the ordered lot's
unit value) is computed as a cross-check and its quantity gets an exact
curve point; the authoritative optimum is the banded search over the
real curve — the standard quantity-discount-EOQ procedure generalised
from price brackets to a continuous per-part curve.
K is derived from the curve itself — K = n·(c(n) − c_variable(n)),
where c_variable re-prices the same lot with every per-lot charge
zeroed — rather than restated as a separate formula. That guarantees K
carries exactly the uplifts the curve carries (burden rate, per-machine
setup/programming rate overrides, FAI, variable overhead, calibration).
Re-deriving it by hand had silently dropped all of those, which
reclassified real fixed cost as variable and biased the recommendation
toward smaller lots.
One basis applies on both sides of one year of demand. Wright learning
is a function of cumulative units built, which the shop reaches within the
year under any lot split, so an over-coverage lot earns no extra learning
credit — it is priced on the same annual basis and penalised honestly by
the growing holding term. (An earlier revision switched lots above D
back to the raw quote price, which made the series discontinuous at D
and let over-coverage lots win admissions they lose like-for-like.)
analytics.lot_size_curve.optimum:
{
"quantity": 250,
"unit_cost_eur": 11.20,
"effective_unit_cost_eur": 11.64,
"current_quantity": 50,
"current_unit_cost_eur": 12.84,
"current_effective_unit_cost_eur": 12.95,
"net_annual_saving_eur": 1310.0,
"annual_volume": 1000,
"andler_quantity": 231,
"band_min_quantity": 250,
"band_max_quantity": 500
}
effective_unit_cost_eur is the per-piece cost on that annual-total
basis (each curve point also carries its own value, so the UI draws
exactly the curve the recommendation came from);
net_annual_saving_eur is what moving to the recommended lot is worth
per year, inventory included.
Perspectives. The holding rate is environment-scoped for a reason:
a manufacturer values stock at manufacturing cost, while a buyer's
capital is tied up at the purchase price (cost plus supplier margin) —
scale holding_cost_rate_per_year accordingly (e.g. 8 % × price/cost
ratio), and add obsolescence/insurance components for parts that carry
that risk; German practice totals 15–30 %/yr where the platform default
(8 % + storage) is deliberately conservative. Time-phased demand
methods (Wagner-Whitin, Silver-Meal) and the finite-production-rate
EPQ correction are documented research directions — they need order
schedules the platform does not ingest today, and the EPQ correction is
a near-no-op for job-shop parts.
Next to it, analytics.lot_size_curve.recommendations carries at
most two machine-readable optimization directions — a deliberate
choice: two solid, defensible recommendations beat ten fragile ones.
kind |
Fires when |
|---|---|
adjust_lot_size |
The recommended lot differs from the ordered one and the net annual saving is material |
reduce_setup_time |
Setup still dominates (≥ 30 %) the lot cost at the recommended lot — the SMED direction, with the annual saving of halving setup quantified |
The same directions are available through the
arcnm_optimize_lot_size MCP tool and, per part, on the portfolio
economics read.
Optimization directions — the full findings surface
Lot sizing is one lever of several. analytics.optimization.findings
carries every research-grounded direction derived from the
calculation's own results, and
GET /calculations/{id}/optimization merges them with live
supplier-quote findings — one endpoint that the
arcnm_optimization_directions MCP tool and the in-app AI agents
wrap identically.
Each finding is tagged three ways:
audience—humanfindings are few, materiality-gated headline directions (rendered as sentences in the app);agentfindings are denser signals for multi-criteria optimization by an AI agent (agents read both).confidence—quantifiedfigures are exact engine math;directionalfindings state a lever whose € outcome needs a requote, RFQ, or product decision.method— the industry method implemented (e.g.total_cost_optimum_andler,smed,chessboard_D6_process_benchmarking,near_net_shape,sampling_skip_lot).
The v1 catalog:
kind |
audience | fires when |
|---|---|---|
adjust_lot_size |
human | recommended lot ≠ ordered and the net annual saving (incl. inventory holding) is material |
reduce_setup_time |
human | setup ≥ 30 % of the lot cost at the recommended lot (SMED direction, halving quantified) |
review_tolerance_cost |
human | tolerance callouts drive ≥ 10 % of the unit cost (exact closed-form uplift; the relaxed-class € needs a re-price) |
improve_material_utilization |
human/agent | buy-to-fly ≥ 3:1 with a material share ≥ 15 % (human at volume, agent below the near-net crossover) |
benchmark_secondary_processes |
human | external processing (heat treat / coating / weld) ≥ 25 % of the unit cost |
supplier_price_gap |
human | latest same-currency quote ≥ 10 % above should-cost — renegotiate or run an RFQ |
reduce_inspection_cost |
agent | inspection ≥ 8 % of the unit cost (sampling / skip-lot direction) |
reduce_programming_cost |
agent | programming ≥ 15 % of the unit cost (CAM reuse / family parts) |
volume_bundling_leverage |
agent | the part's own curve prices ≥ 8 % reduction for doubling the lot |
review_dfm_issues |
agent | medium+ severity DFM issues exist (full instances listed) |
calibrate_should_cost |
agent | latest quote ≥ 15 % below should-cost — the model, not the supplier, is the outlier |
Findings arrive sorted by annual saving. Nothing in the list is a generic tip: every figure derives from that calculation's decomposition, curve, geometry, or quotes, and absent basis means an absent finding.
Learning curve
Cumulative-average learning is applied by default — as cumulative volume grows, the average time per unit comes down. The learning rate is environment-tunable per discipline and per machine class through calibration. If your shop learns faster on aluminium 5-axis than on titanium turning, calibration picks that up from your observations.
VDI 3258 (overhead-rate framework)
For German manufacturing customers, ARCNM emits a VDI 3258-conformant overhead breakdown — variable vs fixed, machine hour vs labour hour, allocated vs absorbed:
{
"vdi_3258": {
"machine_hour_rate_eur": 78.40,
"labour_hour_rate_eur": 42.10,
"overhead_variable_pct": 18.5,
"overhead_fixed_pct": 22.0,
"absorbed_at_n": 50
}
}
Auditors recognise the structure; finance teams reconcile it directly against ERP.
Cost decomposition by line item
A cost breakdown like this is available within the calculation's
analytics blob (the top-level response carries unit_cost,
total_cost, setup_cost, and currency — see the
Calculations reference):
{
"material": 3.21,
"setup": 1.05,
"cycle_labour": 4.12,
"machine": 3.66,
"tooling": 0.18,
"overhead_variable": 0.62,
"overhead_fixed": 0.85
}
The line items sum to the unit cost.
Cost drivers by feature (value engineering)
The line-item breakdown answers what kind of cost you're paying (material vs machine vs labour). The cost-driver breakdown answers where on the part that cost lives — which hole, pocket, face, or turned diameter takes the most time and money. It's the first thing a value-engineering review needs: "if I want this part cheaper, what do I change first?"
ARCNM re-derives this by attributing the time-driven cost lines back onto the feature each machining operation was planned for, then ranking features by attributed cost. It's a pure reporting view over the same numbers as the line-item breakdown — it never re-prices the part.
It's available in the calculation's analytics.cost_drivers blob (REST
and MCP alike):
The blob is self-contained — a consumer can rank, sum, and reconcile the drivers without reaching back into the rest of the payload:
{
"cost_drivers": {
"currency": "EUR",
"unit_cost": 12.84,
"unit_cost_pre_fx": 12.84,
"attributed_cost_eur": 7.84,
"attributed_cost_share": 0.61,
"shared_cost_eur": 5.00,
"shared_cost_share": 0.39,
"total_machining_time_s": 206.2,
"drivers": [
{
"rank": 1,
"feature_id": "f3a1…",
"face_uuid": null,
"feature_kind": "pocket",
"label": "4000 mm² pocket, 20.0 mm deep",
"op_codes": ["pocket_mill"],
"time_s": 184.2,
"time_share": 0.893,
"cost_eur": 5.91,
"cost_share": 0.46,
"cost_components": {
"machine_eur": 3.55,
"labour_eur": 2.07,
"finishing_eur": 0.29
},
"reasons": ["deep pocket — multiple Z passes"]
},
{
"rank": 2,
"feature_id": "9c02…",
"face_uuid": null,
"feature_kind": "hole",
"label": "Ø8.0×24 mm through hole",
"op_codes": ["drill", "ream"],
"time_s": 22.0,
"time_share": 0.107,
"cost_eur": 1.93,
"cost_share": 0.15,
"reasons": []
}
]
}
}
Field notes:
| Field | Meaning |
|---|---|
rank |
1 = the single biggest cost driver (drivers are pre-sorted by cost_eur descending). |
feature_id / face_uuid |
Stable ids — face_uuid is the durable cross-extraction face identity (present for face drivers). |
feature_kind |
hole, pocket, face, turning_face, bend, … (or operation when an op has no resolvable source feature). |
label |
Human-readable feature description an estimator would recognise. |
op_codes |
The operations applied to that feature (e.g. a hole that is drilled then reamed). |
time_s / time_share |
Per-part machining time attributed to the feature, and its fraction of total machining time. |
cost_eur |
Attributed cost in pre-FX EUR (the line-item basis). |
cost_share |
Fraction of the unit cost. Multiply by the top-level unit_cost to recover the figure in the display currency. |
cost_components |
Run-cost split of cost_eur — machine_eur + labour_eur + finishing_eur sum to it exactly, so a consumer can trace each driver's figure back to the machine, labour, and finishing lines. Absent on calculations persisted before the field existed — gate on the key. |
reasons |
Short physics notes explaining why the operation took as long as it did. |
attributed_cost_share (the sum of all drivers) plus
shared_cost_share (amortised setup, programming, inspection,
material, tooling, overhead — everything not attributable to a single
feature) reconcile to 1.0, and attributed_cost_eur + shared_cost_eur == unit_cost_pre_fx, so the breakdown always sums to the headline
price. Every value is a plain JSON scalar/array — no custom decoding.
Per-operation phase times are also surfaced under
analytics.time_breakdown.operations (one record per operation, each
tagged with its feature_id) if you want to roll the numbers up a
different way.
cost_drivers is null for parts routed to catalogue-buy or
bar-stock-saw, which have no machining operations to attribute.
Where calibration enters
Geometry, features, machine selection, and process planning are derived from industry-standard manufacturing theory and don't change as you calibrate — calibration applies a conservative, fully-logged adjustment to the final price. This separation is what makes the audit trail defensible: you can always tell what's "the engine's view of reality" and what's "your environment's correction".
A conservative safe band caps the adjustment per quote; values outside that range are logged + suppressed, never silently applied.
Currencies
All cost fields are EUR by default. Pass currency on the request to
get the response converted at the daily ECB middle rate:
{ "currency": "USD" }
Supported currencies: EUR, USD, GBP, CHF, JPY, CAD. Conversion is at quote time, not at retrieval time — re-quote to pick up a fresher FX rate.
See also
- Concepts → Extraction — feeds the cost model.
- Concepts → Calibration & environments — how rates resolve.
- Concepts → Audit & provenance — every term traces back.
- API → Calculations — endpoint reference.