Note
Read the accompanying Medium essay for a general audience: Modeling the Factory: Why Processes Trump Assets.
This PoC models beer production with a heterogeneous experiment on two axes and tests a single architectural hypothesis: when there is a process run (ProcessRun) independent of the analytical unit equipment, does anomaly detection remain consistent even if the equipment changes?
Two axes of heterogeneity:
- Same equipment type, different topology. Fermentation runs across three sites with different equipment counts, units, and scales:
- Topology A: 1 x 200 HL fermenter, analog valve, °C.
- Topology B: parallel 4 x 100 HL fermenters, on/off solenoid per tank, °F.
- Topology C: 2 x 150 HL fermenters + shared glycol utility measured only in total, analog valve, °C.
- Different equipment types, single run. The run does not end in the fermenter; the product matures in a different equipment type (conditioning / Brite tank). The measurement schema of this tank is disjoint from the fermenter's (temperature + tank pressure + diacetyl/VDK), and two faults occur only in this equipment. The conditioning phase also runs with different tank counts (1/2/1), units (°C/°F), and pressure units (bar/psi).
The second axis is decisive: in a single equipment type, the objection "aggregation is also done on the asset side" is at its strongest. When different equipment types come into play, the process contract is the only analytical unit spanning the run from start to finish — because asset rows per equipment type have disjoint feature spaces and cannot be stacked into a single model.
The data is synthetic. The results are not a claim of site efficiency, but a controlled proof of the architectural hypothesis with a fixed SEED=42: while the algorithm, hyperparameters, and seed are constant, the only variable is the data layer (raw asset vs. canonical process).
| Experiment Arm | Data Layer | Evaluation | Macro-F1 | Healthy FPR |
|---|---|---|---|---|
| Raw asset model (trained on A) | Raw °C | A → A (home site) | 85.0% | 2.4% |
| Raw asset model (trained on A) | Raw °F | A → B (site transfer) | 35.9% | 100.0% |
| Canonical process model (A only) | Canonical | A → B | 74.0% | 35.9% |
| Canonical global process (A+B) | Canonical | A+B → B | 96.6% | 2.5% |
| Same global model, unseen topology | Canonical | A+B → C | 93.4% | 11.8% |
| Asset-local model (control) | Canonical | Each topology its own model | 85–96% | 3–10% |
The only variable is the data layer: same OneClassSVM, same hyperparameters, same seed. The raw asset model is strong in A's raw °C schema (85.0%), but collapses with a 100% FPR when applied to B's raw °F/solenoid schema via naive column alignment. When the same model is trained on the canonical process contract, it rises to 96.6% on A+B → B; it runs on the unseen C but requires local calibration with an 11.8% FPR.
| Proof | Result |
|---|---|
| Conditioning ProcessRun model (single global A+B) | macro-F1 88.9% / 89.2% / 89.0% (A / B / C) |
DIACETYL_STALL recall (carrying signal: VDK) |
100% |
PRESSURE_LOSS recall (carrying signal: pressure) |
99% |
| Can the fermenter raw schema see these faults? | No — schemas are disjoint |
| Definition redundancy (asset vs. process, multi-type) | 6 adapters → 1 aggregator |
The same single global model, evaluated with a holdout discipline, works across all three conditioning topologies. Because the two conditioning-specific faults occur only on signals (VDK, pressure) that are completely absent from the fermenter tags, an asset model tied to the fermenter has no feature to respond with — structural blindness.
Axis 1 isolates the core of the hypothesis (the difference comes from the data layer, not the algorithm) and tests it on the opponent's strongest ground: under a single equipment type, asset-side aggregation seems most plausible. Axis 2 shifts the argument from a difference of degree to a difference of kind:
- With a single equipment type, per-asset modeling creates a stacking issue ("3-vessel run = 3 rows"); the rows are in the same feature space, the only challenge is consolidation.
- With different equipment types, the fermenter row (
temp/valve/gravity) and the conditioning row (cond_temp/pressure/vdk) are incommensurably different: they share no common measurement columns (manifest:cross_equipment.raw_schemas_disjoint = true). These two row types cannot be stacked into a single model. The only unit spanning the run end-to-end isProcessRun.
Thus, the objection to "just aggregate on the asset side" cannot hold with the second equipment type: the signal needed to catch the conditioning fault is absent from the fermenter asset space.
The most common and seemingly justified criticism is:
"In asset-centric modeling, we can also perform unit conversion, aggregation, or direct mapping. Isn't process-centric modeling redundant then?"
This criticism confuses transformation with a contract. The PoC deliberately separates the two.
1. Transformation can also be done on the asset side — the PoC proves this, it does not claim otherwise. °F → °C, volume-weighted average, solenoid 0/1 → cooling demand, and multi-vessel aggregation are not unique to process modeling. The PoC shows this as a fair control: three separately written asset adapters for each topology produce canonical outputs that are numerically identical to the general process aggregator at the np.allclose level on 3/3 topologies (manifest.canonical_outputs_numerically_equal). Transformation capability is not the point of distinction.
2. A contract is not the result of a transformation, but the decision governing it. The contract represents three key decisions:
- Analytical Unit (Grain). Saying "I can aggregate" does not specify what the analysis row is. Empirical proof: even though the canonical feature names are identical, the model trained on A-only yields 74.0% macro-F1 and 35.9% FPR on B; the value lies in the common unit that places B's normal behavior into the same analysis space, enabling multi-topology training (A+B → B = 96.6%). Axis 2 takes this argument to its limit: with different equipment types, the grain is the only possible unit.
- Single Point of Ownership — Against N×M Explosion. In asset-side mapping, the definition of "process temperature" is copied to every adapter. The difference is the replication count: 3 topologies × 2 equipment types → 6 asset adapters vs. 1 general role-based aggregator (
ownership_cost.multi_type_reuse_ratio = 6). - Provenance and Quality Flags. The contract also carries how reliable a value is:
measurement_coverage_pct,utility_scope,allocation_to_equipment_available. Topology C's shared glycol utility cannot be allocated to individual vessels; the contract flags this and does not fabricate granularity that is not present in the source.
3. Conclusion: asset-side aggregation is not a refutation, but an implementation. If a disciplined asset-side harmonization aims for a common canonical unit + constant feature semantics + role-based schema + single definition reuse, that person has already built the process model under a different name. The real distinction is not "asset vs. process tool" but local/asset-specific semantics versus shared/process semantics.
4. Boundaries where the distinction narrows (honestly).
- Small, static fleet: if one or two assets never change, the reuse argument weakens.
- Risk of over-aggregation: volume-weighted average can mask a single hot vessel; therefore, the PoC intentionally keeps the
spreadfeatures. The same principle applies to conditioning —DIACETYL_STALLis caught in parallel topology only viadiacetyl_spread_ppb. - Contract does not learn physics: a common unit does not eliminate the need to learn local normal behavior — unseen C still requires calibration (11.8% FPR).
What the asset hierarchy records and what it does not. An industrial model is typically a rigid ISA-95 physical tree (Site → Area → ProcessCell → WorkUnit → Unit); its weight lies in the bottommost measurement/valve nodes. This tree answers "which equipment is where?" but does not contain a Batch/Recipe/SKU/ProcessSegment type entity. It cannot structurally answer: "which tank family did lot X run in?", "when did this run start, what product was it?", "how do I compare the same product across different equipment compositions?". The run/product grain and lineage cannot be derived from the asset model; it must be added as a separate axis — this axis is the process contract.
Furthermore, vessel nodes typically carry only identity/location; engineering parameters like thermal mass, cooling capacity, volume, and unit do not exist in the asset model and must be defined by curation (see asset_registry_raw.csv vs. curated_engineering_attributes.csv). The value-adding engineering is precisely in the contract layer.
Multi-type and stage-dependent reality. The same process step can be mapped to radically different equipment (open fermentation ↔ uni-tank that does fermentation+maturation in a single body). Identity transforms between stages: the base product in fermentation differentiates into the final product via blending/dosing; the SKU in packaging is not the same as the fermentation run. A maturation tank can hold multiple fermentation batches together (many-to-one). Conclusion: the model key cannot be "SKU"; the key is a process-run ID that carries the link between stages; recipe/SKU/material are its mutable attributes. This PoC models the fermentation + conditioning stages and blend lineage, raising this reality to the level of proof rather than claim.
Each batch is simulated as 1008 observations at 10-minute intervals over seven days. There are 10 batches for each (product, class) (first 7 train, 3 test); each vessel reports multi-point temperatures (median representative + intra-vessel spread). Product is not a key, but an attribute carried by the contract (target: Pilsner 12 °C, Ale 20 °C).
Fermentation faults (injected only into the first fermenter; aggregation must preserve the spread):
| Scenario | Carrying Signal |
|---|---|
HEALTHY |
— |
OVERHEATING / COLD_CRASH |
Temperature (setpoint error) |
STUCK_FERMENTATION |
Gravity (only gravity features detect this) |
SENSOR_DRIFT |
Intra-vessel spread (median remains unchanged) |
Conditioning faults (second equipment type; signals are absent in the fermenter schema):
| Scenario | Carrying Signal |
|---|---|
HEALTHY |
— |
DIACETYL_STALL |
Diacetyl (VDK remains stuck high) |
PRESSURE_LOSS |
Tank pressure (carbonation cannot reach setpoint) |
| Dimension | Topology A | Topology B | Topology C |
|---|---|---|---|
| Fermenter Composition | 1 x 200 HL | 4 x 100 HL | 2 x 150 HL |
| Total Active Volume | 200 HL | 400 HL | 300 HL |
| Control / Unit | Analog · °C | Solenoid · °F | Analog · °C |
| Utility Visibility | Total aggregate | Total aggregate | Shared utility measurement |
| Raw Feature Count | 5 | 17 | 7 |
| Vessel Utility Allocation | Available | Available | Not available |
| Dimension | Topology A | Topology B | Topology C |
|---|---|---|---|
| Brite Tank Composition | 1 x 200 HL | 2 x 200 HL | 1 x 300 HL |
| Temp / Pressure Unit | °C / bar | °F / psi | °C / bar |
| Measured Signals | cond_temp, pressure, VDK | cond_temp, pressure, VDK | cond_temp, pressure, VDK |
The analysis unit is not a tank, but ProcessRun. The same volume-weighted, role-based aggregation applies to both equipment types:
- Fermenter role:
process_temp_mean_c,process_temp_spread_c,intra_vessel_spread_c,gravity_weighted_mean,gravity_spread,cooling_intensity_c_per_min, plusmeasurement_coverage_pct,utility_scope,allocation_to_equipment_availableflags. - Conditioning role:
cond_temp_mean_c,cond_temp_spread_c,intra_vessel_spread_c,pressure_mean_bar,pressure_spread_bar,diacetyl_ppb_mean,diacetyl_spread_ppb.
The model remains independent of equipment composition using product-relative features (temp_setpoint_error_c, gravity_attenuation, cond_temp_setpoint_error_c, pressure_setpoint_error_bar).
The experiment does not compare the process-centric approach with a weak asset arm:
- Raw asset transfer control (core evidence): same
OneClassSVM, trained on A's raw native schema (°C, valve %, absolute cooling) and applied directly to B's raw schema (°F, solenoid, absolute cooling scaling with volume). A → A 85.0%; A → B 35.9% / 100% FPR. - Asset-specific adapter control: custom wide-schema adapter for each topology produces canonical outputs numerically identical to the general aggregator on 3/3 topologies. The difference is not transformation capability, but the ownership boundary of 6 adapters → 1 aggregator.
- Model controls: asset-local (separate model for each topology), A-only canonical, global canonical A+B, and leave-one-topology-out on unseen C.
All arms use the same algorithm family, nu=0.01, gamma="scale", same feature roles, and same batch holdout (healthy batches 0–6 train, 7–9 test; anomalous batches only for evaluation).
Branches added with the same discipline turn a single result into an evidence matrix:
- Product × topology. The global model is high on both products in B (Pilsner 97%, Ale 96%); product is a contract attribute, not the model key.
- Harmonization ablation (raw A → B, temperature signal).
native ≈ 32%→+unit_convert ≈ 83%→+product_setpoint ≈ 85%. Unit conversion is the step that unlocks transfer; subsequent steps refine it. - Multi-seed × multi-algorithm. 6 seeds × {OneClassSVM, IsolationForest}. The raw arm collapses on every algorithm; the process arm is superior on every algorithm (
data_layer_delta_macro_f1 > 0: OCSVM +0.61, IForest +0.22). Absolute level is algorithm-dependent; the data-layer effect is algorithm-independent. - Few-shot calibration (unseen C). As k ∈ {0,1,2,3} healthy C batches are added, C's FPR drops (12% → 5%).
- Fault taxonomy. The global fermentation model captures temperature, gravity, and intra-vessel spread faults with distinct signals.
- Blend genealogy (many-to-one). Several fermentation runs merge into a single blend lot; detection is evaluated at the blend-lot level (macro-F1 100%, 26 lots). A relationship that the asset hierarchy cannot structurally express is resolved at the run level.
- Multi-equipment type (cross-equipment). The conditioning ProcessRun model is ~89% on all three conditioning topologies;
DIACETYL_STALLandPRESSURE_LOSSoccur on signals not present in the fermenter schema and are caught with 99–100% recall. The fermenter asset model is structurally blind.
flowchart LR
S[Physical simulator] --> FA[Fermenter A: 1 x 200 HL, C]
S --> FB[Fermenter B: 4 x 100 HL, F]
S --> FC[Fermenter C: 2 x 150 HL + shared utility]
S --> CT[Conditioning tanks: A/B/C, bar/psi, VDK]
FA --> RAW[Raw native schema] --> RM[Raw asset A-model]
FB -. collapses on raw B .-> RM
FA --> EQ[Long-form equipment observations]
FB --> EQ
FC --> EQ
CT --> EQ
EQ --> AG[Generic role-based aggregator]
AG --> PR[ProcessRun contract: Fermenter + ConditioningTank]
PR --> GM[One A+B global model per role]
GM -. unseen topology C .-> FC
GM -. conditioning-only faults .-> CT
RM --> E[Evidence, dashboard, figures]
GM --> E
| File | Responsibility |
|---|---|
config.py |
Physics, seed, three fermentation topologies, second equipment type (conditioning), physical parameters, 2 products, fault classes, model registry, MC seeds |
simulator.py |
Fermentation kinetics + conditioning kinetics (temperature, pressure, VDK), multi-point measurement |
topology.py |
Multi-equipment records, general role-based aggregator, three fermentation adapters, conditioning frame/aggregator/raw layer, blend genealogy, cross-equipment manifest findings |
schemas.py |
Entry point for contract generation |
analyzer.py |
Raw transfer, canonical models, product matrix, ablation, calibration, genealogy, fault taxonomy, robustness, cross-equipment experiment |
dashboard.html |
Local, interactive topology, batch, and evidence dashboard (+ multi-equipment type section) |
reporter.py |
Static figures (Figure 1–3) |
test_poc.py |
Regressions for physics, contract, core claims, enrichments, and multi-equipment type |
data/topology_contract_manifest.json |
Schema, adapter, volume, granularity, ownership, curator, and cross_equipment evidence |
data/conditioning_process_contract.csv |
Canonical run-level contract for the second equipment type |
data/conditioning_asset_raw_features.csv |
Conditioning honest raw asset layer (disjoint schema from fermenter) |
data/results.json / results.js |
Machine-readable metrics and evidence |
Python 3.10+ is recommended.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python simulator.py
python schemas.py
python analyzer.py
python reporter.py
python -m unittest -v
open dashboard.htmlThe dashboard reads data/results.js directly over file://; no server is required.
Each batch has a deterministic RNG stream. The test suite verifies:
- Each fermentation topology has 200/400/300 HL and each run contains 1008 points.
- Raw schemas are different (5/17/7 features) and asset/process canonical outputs are numerically equal.
- Utility allocation for C remains unavailable and C is excluded from A+B training.
- A+B training improves B transfer compared to the A-only model.
- Core claim: raw asset model collapses, process contract transfers.
- Multi-equipment type: ProcessRun covers two equipment types, raw schemas are disjoint, conditioning-specific faults are caught by the process run, fermenter model is structurally blind, and multi-type ownership reuse ratio (6×) is verified.
- The data is physically guided but synthetic; it does not prove real plant distribution.
- There are two equipment types (fermenter, conditioning), a limited fault set, and a single model family; real OEM/historian behavior is broader.
- Conditioning detection is strong on conditioning-specific faults but healthy alarm burden is higher than the fermentation arm (~17–20% FPR); this indicates a need for threshold calibration before production.
- In parallel runs, faults are only injected into the first vessel; other combinations have not been tested.
- Missing sensors, time alignment, mapping errors, and drift are not included in the stress test.
- Artifact counts are measured; engineering time, maintenance cost, and TCO are not.
The value of a process-centric contract is not that it is the only way to enable asset transformation. Its value is that it unifies the same work process carried out by a different number, arrangement, and type of equipment under a stable analysis unit and clear granularity semantics. For a single equipment type, this enables multi-topology training and fewer contract artifacts; when different equipment types come into play, the process run becomes the only possible analytical unit capable of spanning disjoint asset schemas. None of this eliminates the need for calibration on unseen topologies.