Status
direction
Conceptual layer
③ Analytics state estimation on the Plant Box
Repo layer
L3 intelligence-core (stamped-l3-twin)
Source
architecture section 3.6.8, section 5.1 PlantState · ADR 033 · placement D3 · Index: README.

1. What the twin is#

A twin is a model of one real asset, kept in step with that asset's live data, feeding a named decision. Stamped's twins are grey-box digital shadows:

  • Physics gives structure (heat balance, the heater coil as a queue of billets, quench, ageing).
  • An estimator keeps it in step with noisy sensors and states its uncertainty (Kalman level first; unscented Kalman filter once a step test identifies the plant; MHE only if it wins the offline benchmark, D19 DECIDED).
  • Monte Carlo carries uncertainty in physical parameters (hundreds of draws per heat-treatment basket).
  • A decision layer turns state and risk into alerts, actions, signals or write requests, only under accepted procedures.
Twin (forging sector pack)Data inStateOutputs
Induction billet heater1 s exit temperature, push times, part, windowLevel ± spread (v1); temperature of each billet in the coil (v2); pyrometer offset (v3)Exit forecast for the coil under hold, step down, step up
FlowPushes, press parts, countersPlant state, billets waiting, stop clockStop and restart events, waiting count, rhythm breaks
Heat treatment60 s zones, quench tank, gasInferred baskets, metal lag, tank temperature, ageing clocksZone and ageing alerts, water forecast, per-basket risk
Per-part recordHeater and flow outputsBillet-to-part links, one loss cause per pushPart flags, loss ledger

The heater twin becomes a full twin (it writes) only at AL2Autonomy levels (direction; fast-loop stages 1–3 = AL1–AL3) or AL3. The heat-treatment twin always stays a shadow.

2. Module layout#

New package stamped_l3_core/twin/ in intelligence-core:

stamped_l3_core/twin/
  kit/          platform: kalman, ukf, gate, montecarlo, calibration (moving horizon),
                replay (leave-one-run-out), drift, modelcard and parameter-row loader
  assets/       sector pack physics: induction_heater (v1-v3), flow, ht_furnace, quench,
                qfa (quench factor), ageing (Arrhenius)
  genealogy/    billets (one cause per push), parts (billet-to-part link, flags), baskets
  procedures/   runner (accepted procedures), budget (client view), write_request
  runtime/      engine (per-second loop), plant_state, checkpoint, io, health

Not in the twin: reading the PLC (L1), storing records (L2), choosing who gets a card (L4), sending messages (L5).

3. The per-second loop#

  1. Read the latest readings from the plant broker (PLC time and edge receive time).
  2. Check freshness (under 5 s), physical bounds and frozen values; otherwise mark the affected state uncertain.
  3. Load the parameter row for the current context (06).
  4. Predict with physics; update the estimator through the gate (on each push, or each minute for heat treatment).
  5. Derive forecasts, risks, waiting counts and stop forecasts; publish PlantState direction on twin/state/{line}.
  6. Run accepted procedures; unaccepted ones run in shadow and are only logged.
  7. Checkpoint after every push; send records to L2.

Event time and wall time are separate. Physics and records use event time. A message goes out only if its event is younger than the procedure's freshness window (30 s for signals, 5 min for alerts). Backlogged data updates state but never triggers messages.

4. Plant states#

StateEntered whenAllowed
RunningPushes and parts at the part's rhythmAll accepted procedures
StoppedNo part or no push for 60 sHold and stop/start procedures
ChangeoverPart changesChangeover and first-part procedures; new parameters loaded
RestartFirst pushes after a stopFirst-part flags; no writes until the coil has been flushed
UnknownData gap, conflicting signals, startupPart-independent alerts only; no writes, no signals

5. Recovery#

FaultResponse
Crash or rebootRestore the last checkpoint. Gap under 3 min: resume with widened uncertainty. Longer: state uncertain until the coil is flushed
Data gapState uncertain; the writer loses the heartbeat and restores the operator's values
Buffer replay after an outageUpdate state; send no messages; mark the period as replayed
Frozen tagTreat as missing; data-quality alert to the Stamped admin
Sensor misreadPhysical floor plus a 4-sigma gate; after 3 rejections accept the next to avoid lockout
Clock jump over 2 sFlag the window; the writer stops (02)
Unknown partLearning mode (06)
Model driftRefit; a write-enabled tag returns to AL2

The twin runs under a supervisor with automatic restart and an internal watchdog that exits when no tick happens for 5 s. It publishes a heartbeat every second to the writer. The loop has no hidden wall-clock dependency, so every incident replays from L2 through the same code.

6. Model governance#

  • Every model has a card: asset, boundary, measured output, decision, owner, hard stops, version.
  • Parameters are versioned rows with source, date, data volume and confidence.
  • A new estimator replaces the current one only if it wins the same leave-one-run-out replay on error, alarm precision and recall, and interval coverage.
  • Uncertainty must be honest (stated 90% intervals cover about 90%) before any standing write uses it.
  • Weekly drift monitoring with a refit trigger.
  • Model sophistication follows evidence: a richer model waits for the data that identifies it.

7. Performance and language#

The heaviest per-second work (a 14-billet heat balance, an estimator update, flow events) costs microseconds to a millisecond in NumPy; Monte Carlo per basket runs once a minute in milliseconds. The delay that matters is I/O, which no language changes. The twin stays in Python on the Plant BoxPlant-side computer for the fast loop (direction; D4) (D3), reusing the analysis code and matching the rest of L3. If one routine exceeds 100 ms, only that routine moves to Numba or a Rust extension. The plant image is compiled with Nuitka.

8. Deployables and boundaries#

DeployableCommandRuns
Scheduled L3 (existing)stamped-l3-coreCloud or plant server
Twin runtime (new)stamped-l3-twinPlant Box (cloud while messages only)

Import-linter rules: the scheduler, engines and agentic code never import the twin's write path; the twin never imports the scheduler, agentic, fine-tune or challenger code. The plant image installs only the twin extra. Anything that becomes a card still goes out as EvidenceLayer contract for detector output (direction; as built: Finding finding.json 1.2.0) (direction; as built: FindingAs-built L3 detector output admitted to L4 (finding.json 1.2.0) 1.2.0) through the outbox.

9. Packs#

Lives inHolds
Platform (twin/kit/)Estimators, gate, Monte Carlo, calibration, replay, drift, loaders
Forging sector packAsset physics, default priors per alloy family, procedure templates
Site packTag bindings, model cards per asset, parameter rows, part aliases, written limits, roster roles, allow-listed tags

Plant names never appear in platform or sector pack code.

10. Reference tests#

Before rewritten modules replace analysis scripts, they reproduce the scripts' outputs on recorded data within stated tolerances: forecast error, ledger totals, link rates, flag counts, basket counts and physics self-checks. Plant data stays in the private site workspace; product repos hold expected numbers and synthetic fixtures of the same shape.

Page history: last 2 changes
  1. 2026-10-07 docs(technical): rewrite fast-loop/; all architecture diagrams in house style 7330f47
  2. 2026-10-03 docs(decisions): add ADR-033..038 (twin runtime, fast read path, plant-side writer, message classes, alerts and quality-to-lot link, part-keyed parameters), fast-loop technical set, rebuilt index with renumbering map; fix bare-number link text and ranges 22e2872

Diagram

100%

Search the architecture