06 - The Carbon Engine
Status. Draft v0.1 · First draft: 17-03-2026 · Pre-discussion. This doc is a plain-language explainer of the canonical engine specification at Docs/05-carbon-engine.md; that source is authoritative for any precise behaviour.
Why this matters. The Carbon Engine is where the platform’s claim “we produce trustworthy carbon numbers” stops being marketing and becomes engineering. It is the deterministic calculation subsystem at the centre of SIDK. It is the thing a verifier audits when they audit a claim. It is the reason the same Tenant’s data can produce a CBAM declaration, a CSRD-compatible PCF, an EN 15804 EPD, and an Indian CCTS compliance number from the same underlying inputs without recomputation. If you understand the engine’s six load-bearing ideas - four-method dispatch with audited fallback, boundary as a first-class input, mass-balance for integrated routes, propagated uncertainty with parallel validation, evidence-tier provenance, deterministic versioning - you understand the engineering ground the rest of the platform stands on.
1. What it is, in one paragraph
The Carbon Engine takes a subject (a Lot, a ProcessUnit, a Site, a Period - the thing you want a carbon number for) and a boundary (the question you are asking: gate-to-gate, cradle-to-gate location-based, CBAM production process, EPD A1-A3), and produces a CalculationResult - a structured record carrying the headline value, every component that contributed to it, the method used for each component, the factor versions consulted, the propagated uncertainty, the parallel-method validation result, and the evidence breakdown. The result is deterministic - the same inputs and versions produce the same number every time - and replayable - a verifier can independently re-run the calculation and reach the same value. The engine is vertical-agnostic: the same engine serves steel, cement, aluminium, pharma, and any future carbon-focused product, with vertical-specific content (which method is preferred for which source, which factors apply, which boundaries are allowed) carried in pack content, not in the engine code.
2. The four methods
Carbon emissions can be measured or computed several ways, with different accuracy and applicability trade-offs. The engine recognises four. From most direct to most defaulted:
Method 1 - Direct measurement (CEMS). The chimney has a Continuous Emissions Monitoring System: a stack analyser sampling CO₂ concentration and a flow meter measuring volume. The engine integrates concentration × flow over time. E = Σₜ (Q_t × C_t × Δt). Used for major regulated stacks where the cost of the instrumentation is justified by the regulatory scrutiny.
Method 2 - Fuel-combustion calculation. Meter the fuel going in, multiply by its calorific value, then by its emission factor, then by an oxidation factor. E = AD × NCV × EF × OF. Used for burners, boilers, reheaters - the thousands of small combustion sources where CEMS would be overkill but the fuel meter is reliable.
Method 3 - Mass balance. Account for carbon entering the system in multiple streams and leaving in multiple streams; what isn’t accounted for as a non-emission output was emitted. E = [Σ(m_in × C_in) − Σ(m_out × C_out) − ΔC_stock − C_captured] × 44/12. The only correct method for integrated metallurgical sites where carbon enters as coke, coal, and limestone, and leaves in steel, slag, off-gas exports, and dust. This is the headline method for BF-BOF steel - Section 4 below digs in.
Method 4 - Activity-data with default factor. Multiply activity data by a published default emission factor. E = AD × default_EF. The floor - used where a higher-tier method is infeasible, for minor sources or supply-chain inputs without primary data.
The methods are not four answers to the same question. They are four tiers of preference the engine selects between, per emission source, based on what data is available.
3. Fallback with audit, not silent substitution
For each emission source identified in the boundary scope, the engine reads the source’s preferredMethod from the topology, checks whether the method’s prerequisites are met for the period (telemetry coverage above threshold, lab results available, factor coverage), and either uses it or falls back to the next-tier method.
Two things make this different from a spreadsheet:
Every fallback is recorded. The CalculationResult.components[] array carries both method (the method actually used) and methodFallbackReason (why, if it wasn’t the preferred). A verifier reviewing the result can see “we wanted CEMS for this stack but the analyser was down 14% of the period, so we fell back to fuel-combustion.” No silent substitution.
Mass balance has no fallback. Method 3 is intentionally a no-fallback method. If a route-level mass balance fails (carbon ledger does not close), the engine does not substitute per-source Method 2 calculations, because per-source calculations would miss the off-gas exports and double-count the recycled by-product gases. They would produce a number that is systematically wrong, not just less accurate. The engine fails loudly: the period fails Readiness Check and the operations team has to close the ledger before the period can be locked. This is one of those design choices that looks pedantic on a slide and is load-bearing in real plant operations.
4. Mass balance - why BF-BOF is special
The integrated blast-furnace + basic-oxygen-furnace route is where steel’s carbon problem stops being a fuel-combustion problem and becomes an accounting problem.
Coal enters the system as coke (in the blast furnace) and pulverised coal injection (PCI, also in the BF). Some of its carbon ends up as CO₂ in the off-gas. Some leaves in molten iron and ultimately in steel (small but non-zero). Some leaves in slag. Some leaves in dust. Some leaves in by-product gas sold to an external power plant. Some is recycled internally to fire the on-site stoves and reheaters. Some accumulates or depletes in inventory at the period boundary.
A fuel-combustion calculation that just looks at coke + PCI tonnages and multiplies by a coal emission factor over-counts - because it ignores that 8 kg/t-slab leaves in steel, 18 kg/t in by-products, 12 kg/t in off-gas exports, and 5 kg/t in stock. The worked example in the engine spec lands on 547 kg C net emitted per tonne of slab, from 590 kg C input, yielding 2,005 kg CO₂/t slab via the 44/12 stoichiometric conversion. The engine produces this as a single Method-3 component with every input and output Stream identified and traceable.
Two sub-rules worth carrying:
By-product gas accounting. When blast-furnace gas, coke-oven gas, or BOF gas leaves the calculation boundary (e.g., sold to an external power plant), the carbon moves out of the balance and is recorded as a Scope 1 emission with a note for downstream reconciliation. When the gas stays inside (used in on-site reheaters), it stays in the balance. The same carbon is never on both sides; the engine validates this at topology-write time and at calculation time.
Carbon capture is conditional on permanence. The mass balance includes a C_captured term, but only if the destination is permanent (geologic sequestration, mineralisation in cement/concrete). Capture into short-lived products (beverages, dry ice, urea - which release CO₂ over months) does not deduct from emissions in v1; it is recorded on the result as a non-deducted line item, visible to verifiers but not subtracted from the total. This is the kind of asymmetry that gets argued about in policy circles; the engine’s defensible default is conservatism.
5. Boundary as a first-class input
The same Lot has different carbon numbers under different boundaries. A coil’s gate-to-gate emissions (just the rolling step) are small. Its cradle-to-gate location-based emissions, including the BF-BOF that produced the slab, the iron ore mining, the coke oven, the limestone calcination, and the location-based grid factor for purchased electricity, are large. Its CBAM production process emissions are different again, because CBAM’s boundary definition has its own rules for what precursors and indirect emissions count. None of these are “the” answer; they are answers to different questions. For the full boundary vocabulary, see 02a - Boundaries.
The engine treats boundary as a first-class input. Every CalculationResult carries its full boundary descriptor, including:
kind- closed enum: SITE_OPERATIONAL, SITE_FULL_GHG, GATE_TO_GATE, CRADLE_TO_GATE_LOCATION, CRADLE_TO_GATE_MARKET, CBAM_PRODUCTION_PROCESS, EPD_A1_A3, and a few more.scope2Method- LOCATION_BASED or MARKET_BASED, where Scope 2 is in scope.scope3Inclusion- which Scope 3 categories are in.geography- which regional grid factor applies; which transport factors.reportingDate- which factor versions are valid (factors have effective-from/to windows).
Two calculations of the same subject with different boundaries are two distinct CalculationResults. Both are retained. Both are queryable. The engine never overwrites one with the other.
Dual Scope 2 is computed always. For any boundary that includes Scope 2, the engine computes both location-based and market-based values, regardless of which one the caller asked for. The caller’s scope2Method selection determines which value populates the headline value field; the alternative is stored under components[].scope2Alternative. This way a future view (a CBAM definitive period that requires location-based; an EPD that allows market-based with attestation) does not require recomputation. Small cost now, big saving later.
6. Allocation - how a process’s emissions become a product’s emissions
The four methods give you the total emissions of a process over a period. To get a product-level number - this coil’s PCF, this billet’s PCF - you have to allocate that total across the products and by-products the process produced.
The engine implements the GHG Protocol Product Standard hierarchy 1:
- Physical causality first. Mass-based allocation is the dominant default for steel transformations - a heat producing multiple billets, a coil cut into shorter coils, a continuous caster producing slabs. Mass is the carbon-bearing thing being transformed; allocating by mass honours physical causality.
- Energy-based for utility-shared processes (a boiler producing both steam and exported electricity, where mass is irrelevant).
- Economic as the loophole - only with a dual-actor-approved Submission carrying a free-text
allocationJustification. The engine refuses to economically-allocate silently; the operator has to sign for it. - System expansion / substitution for specific named cases - by-product gas exports (credit by avoided emissions of the displaced fuel), slag-as-cement-clinker-substitute (credit by avoided clinker emissions). System-expansion credits are always recorded as separate negative components, never folded into gross emissions. Gross and net are both visible.
Regime-specific defaults apply: GHG Product Standard defaults to physical-first; CBAM has its own specific by-product treatments; EN 15804 has specific recycling-credit rules (Module D, separately reported). The engine respects the boundary’s allocation default unless overridden with a justified Submission.
7. The lot-lineage walk - how product PCFs work
For a product-level PCF - say, the cradle-to-gate carbon of a specific HRC coil - the engine walks the lot lineage DAG backward from the coil to its ancestors: the slab that became this coil, the heat that cast this slab, the iron and scrap that fed this heat, all the way back to ore receipts and scrap purchases. Each ancestor contributes its allocated emissions to the descendant’s PCF.
Two things are worth understanding here:
The lineage is event-based. A Lot is PRODUCED_BY one (and only one) TransformationEvent. That event has inputs (Lots consumed) and outputs (Lots produced). To walk backward from a Lot, follow its PRODUCED_BY edge to the event, then iterate the event’s input Lots. The graph is a DAG, not a tree (a single TransformationEvent typically produces multiple output Lots from multiple input Lots), and the walk is recursive.
Historical emissions arrive via the most recent CalculationResult on the ancestor, not via a Lot-level field. Iron ore received last year was already given a carbon number in some prior period’s CalculationResult. When this year’s coil walks the lineage back to that ore, it pulls the most recent locked result for that ore Lot. This is what makes monthly closes self-contained - once a period is locked, its CalculationResults stand as the persisted answer for the Lots produced in it.
There is a max walk depth (default 50, configurable); for most steel routes a coil’s lineage is well under 10 steps deep, so truncation is rare. When it does happen, it is reported.
8. Uncertainty - both analytical and Monte Carlo
A carbon number without an uncertainty band is a marketing number, not a scientific one. The engine propagates uncertainty through every component, then aggregates to the result level, and stores the propagation method explicitly so a verifier can second-guess the choice.
Per-method default propagation:
- Methods 1 and 2 (multiplicative chains like
E = AD × NCV × EF × OF) default to GUM first-order - the standard sensitivity-coefficient formulau_rel(E) = √(Σ uᵢ²)from the Guide to the Expression of Uncertainty in Measurement 2. Mathematically exact for multiplicative log-normal chains. - Method 3 (mass balance -
E = Σ(m·C_in) − Σ(m·C_out) − ΔC_stock − C_captured) defaults to Monte Carlo with Latin Hypercube Sampling. The mass-balance formula is a subtraction of close magnitudes - 547 kg out of 590 kg total carbon input. GUM first-order on this would inflate the uncertainty pessimistically; LHS samples per-iteration with input correlations respected, and gets a much tighter, more honest band. - Method 4 defaults to DECLARED (the factor’s own declared uncertainty band) or IPCC_TIER_1 (the default factor’s IPCC-supplied uncertainty), with no further propagation - the chain has only one element.
The MC-as-check protocol. Even when the primary method is analytical (Methods 1 or 2), the engine also runs Monte Carlo LHS as a parallel validation on every locked-period calculation. It computes the delta between the primary value and the MC median, and emits a finding if the delta exceeds threshold (10% → OK_WITHIN_BOUND, 10–25% → WARNING, >25% → CRITICAL). The canonical value is always the primary method’s; the MC result lives in uncertaintyValidation on the same CalculationResult. This is one of the engine’s stronger trust properties - every locked claim has been cross-checked by an independent uncertainty method.
Coverage factor is explicit. Lab certs and verifier statements typically report uncertainty at k=2 (~95% coverage); GUM internal convention is k=1 (~68%). The engine stores coverageFactor explicitly on every uncertainty record, converts internally to k=1 for propagation, and converts at output. No ambiguity in the band labels users see.
9. Cross-method shadow calculation
Beyond MC-as-check on uncertainty, the engine runs a stronger validation: two independent methods on the same source, in parallel.
For a blast-furnace top-gas stack with both CEMS (Method 1) and mass-balance (Method 3) prerequisites met, the engine runs both during a period lock. The CEMS-derived number is the authoritative one; the mass-balance number is the shadow. The engine computes the delta, compares it to the combined uncertainty, and emits a ReconciliationFinding with status OK_WITHIN_BOUND, WARNING_BEYOND_BOUND, or - if WARNING persists for three consecutive periods on the same source - DIVERGENCE_PERSISTENT.
Persistent divergence triggers a CRITICAL Readiness Check that blocks the period lock without an explicit override. The engine does not auto-switch to the shadow method - silent downgrade is more dangerous than a blocked lock. The operations team must investigate (CEMS calibration drift? stale coal-CV lab data? an unaccounted gas stream?) and resolve, or override with a dual-actor-approved Submission that gets recorded on the LockedSnapshot.
This is the engine’s strongest external-trust feature. Verifiers under ISO 14064-3 expect independent-method validation 3; CBAM’s Annex IV expects documented QC mechanisms; EU ETS MRR requires it at higher tiers. The engine’s shadow-calc orchestration produces this evidence automatically rather than as a verifier-specific ask.
10. Evidence breakdown - the five-tier model
Every component on a CalculationResult carries an evidence tier describing how directly the underlying data was measured. Aligned to MRV (Measurement, Reporting, Verification) practice:
| Tier | Description | Typical source |
|---|---|---|
| Tier 4 - continuous | Continuous direct measurement | CEMS |
| Tier 3 - periodic | Periodic measurement | scheduled stack tests, lab assays per batch |
| Tier 2 - declared | Supplier-declared with documentation | supplier PCF with verification, supplier-declared NCV |
| Tier 1 - default | Industry / IPCC default factor | IPCC default natural gas EF |
| Substituted | Value substituted for missing data | EPA Part 75-style substitution during a CEMS gap |
The result’s evidenceBreakdown aggregates these to the boundary level - “30% Tier 4, 32% Tier 3, 20% Tier 2, 8% Tier 1, 10% substituted” - and also reports the source axis (sensor / lab / ERP / supplier / calculated) separately. A sophisticated buyer or regulator can distinguish a well-measured claim from a heavily-defaulted one. Without this distinction, every claim looks the same.
11. Versioning, lock, and restatement
The engine never silently rewrites history.
Two version concepts. Every CalculationResult carries a calculationVersionId (which engine binary ran it) and methodVersionIds[] (the rule version of each method actually used). Both are needed to replay a historical calculation exactly. Newer engine code must respect older method specs to reproduce historical results.
Lock = a snapshot, not a state. When a ReportingPeriod is LOCKED, the engine writes a LockedSnapshot containing all CalculationResults for the period, all EvidencePackages, the pinned factor versions, and a contentHash over the canonical JSON. The snapshot is immutable. Subsequent reads of that period’s data serve from the snapshot - late-arriving data is not silently incorporated.
Restatement is auditable, not destructive. If a locked period needs to be revised - material late data, a corrected lab result, a methodology update - the operations team files a PERIOD_RESTATEMENT Submission. Upon dual-actor approval, the engine recomputes and writes a new LockedSnapshot with parentSnapshotId pointing to the original. Both snapshots remain queryable forever; the diff endpoint shows exactly what changed. For CBAM and similar regimes, restatements above a materiality threshold trigger declaration amendments.
This is the audit discipline that makes the engine’s output evidence rather than assertion. A verifier auditing a Tenant’s 2026 CBAM declaration in 2028 can read the original LockedSnapshot, see any restatements that happened after, and walk the diff. Nothing is hidden.
12. Calculation Readiness Check - pre-flight before lock
A monthly lock pulls in millions of telemetry points, hundreds of lab results, dozens of Submissions, and a complex topology. Locking a period that has problems - overdue calibrations, missing lab results, unreconciled mass balances, persistent shadow-calc divergence - and finding out after the snapshot has been signed and submitted to a regime is bad. The Readiness Check runs before the lock, surfaces problems, and gates the lock until they are resolved or explicitly overridden.
Checks have three severity levels:
- INFO - informational; lock proceeds.
- WARNING - lock proceeds; warning recorded on the snapshot.
- CRITICAL - lock blocked. Must be resolved (data fixed) or explicitly overridden via a dual-actor-approved
READINESS_OVERRIDESubmission with a free-text justification. Overrides are visible to verifiers.
The full check catalogue lives in the kernel’s edge-and-integration document. Examples that the engine produces directly: MASS_BALANCE_RECONCILIATION_FAILED, MEASUREMENT_BASIS_MISMATCH, CALIBRATION_OVERDUE, MC_CONVERGENCE_NOT_REACHED, RECONCILIATION_DIVERGENCE_PERSISTENT, UNCERTAINTY_VALIDATION_DIVERGENCE_CRITICAL. Each names exactly what failed and which entities are affected.
13. Why this all matters for the platform claim
Three load-bearing properties fall out of this design:
Deterministic, replayable, multi-version. A verifier can pull a LockedSnapshot from two years ago, run the original calculation under the original engine and method versions, and reproduce the same number byte-for-byte. This is the engineering ground for the platform’s claim that the data is evidence, not assertion.
One canonical inventory, many regime projections. The same underlying CalculationResults serve CBAM (installation-period SEE under CBAM_PRODUCTION_PROCESS boundary), CSRD (Scope 1+2+3 under SITE_FULL_GHG), EN 15804 EPDs (cradle-to-gate under EPD_A1_A3), and Indian CCTS (intensity per unit product). One engine, several boundaries, no recomputation. This is the technical answer to the Tenant’s question “do I have to run separate carbon-accounting systems for each regime?”
Shadow-calc, evidence tiering, and Submission-gated overrides are the engine’s contribution to the trust chain. The Tenant signs the claim; the Verifier signs the attestation; the platform signs the delivery - but everything below the Tenant’s signature is the engine’s discipline. A Tenant signing a poorly-grounded number is signing a poorly-grounded number; the engine makes it hard to do so accidentally, and makes it visible when it is done deliberately.
The next doc, 07 - Inputs and sensors, is about how the data the engine consumes actually gets in.
References & further reading
Source spec (canonical)
Docs/05-carbon-engine.md- Carbon Engine Spec v1.3. The full, normative spec covering all four methods, boundaries, allocation, lot-lineage, uncertainty propagation (GUM and MC variants), shadow calculation, period lock and restatement, Readiness Check, and the method registry. Authoritative for any precise behavioural question. This explainer is a paraphrase; where they disagree, the spec is right.Docs/SIDK Handoff Docs/- kernel-side documentation. Seearchitecture.mdfor how the engine sits inside SIDK’s seven primitives, anddata-patterns.mdfor how cache invalidation and replay work.
External authoritative sources (cited inline)
Further reading
- JCGM 101:2008 - Supplement 1 to GUM: Propagation of distributions using a Monte Carlo method. The GUM-family standard authorising Monte Carlo as a propagation method, including conditions under which MC should be preferred over first-order analytical (subtraction of close magnitudes is one of the named cases). https://www.bipm.org/en/committees/jc/jcgm/publications
- EU ETS Monitoring and Reporting Regulation (Commission Implementing Regulation (EU) 2018/2066). The regulatory regime that defines tiered monitoring (Tier 1–Tier 4) for EU ETS installations; the upstream of much of the engine’s evidence-tier model. https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32018R2066
- US EPA - 40 CFR Part 75 (Continuous Emission Monitoring). The US regulation for CEMS used by acid-rain and cross-state air-pollution sources; the source of the “EPA Part 75 missing-data substitution” pattern the engine’s substitution hierarchy is loosely modelled on. https://www.ecfr.gov/current/title-40/chapter-I/subchapter-C/part-75
- EN 14181 / EN 15259. European standards for stationary-source emissions measurement (calibration and quality assurance of automated measuring systems, and sampling-section requirements). The reason the engine treats
MeasurementDevice.calibrationDueAt < periodEndas a CRITICAL Readiness Check by default. https://www.en-standard.eu/bs-en-14181-2014/ · https://www.en-standard.eu/bs-en-15259-2007/ - IPCC 2006 Guidelines for National Greenhouse Gas Inventories, Volumes 1 and 2. The default emission factors and methodology defaults the engine reaches for in Method 4. https://www.ipcc-nggip.iges.or.jp/public/2006gl/
- GHG Protocol - Calculation tools and guidance. Sector-specific calculation guidance (iron and steel; aluminium; cement; chemicals) showing worked examples. https://ghgprotocol.org/calculation-tools
- World Steel Association - CO₂ data collection methodology (annual). The industry-collected methodology behind the World Steel Sustainability Indicators report; useful for benchmarking the engine’s per-route output against industry averages. https://worldsteel.org/wider-sustainability/sustainability-indicators/
Footnotes
-
GHG Protocol - Product Life Cycle Accounting and Reporting Standard (2011). The basis for the allocation hierarchy (physical first, economic only with justification, system expansion for specific cases). https://ghgprotocol.org/product-standard ↩
-
JCGM 100:2008 - Evaluation of measurement data - Guide to the expression of uncertainty in measurement (GUM). The international standard for analytical uncertainty propagation; the source of the first-order sensitivity-coefficient formula the engine uses for Methods 1 and 2. https://www.bipm.org/en/committees/jc/jcgm/publications ↩
-
ISO 14064-3:2019 - Specification with guidance for the verification and validation of greenhouse gas statements. The procedural standard verifiers operate under; specifies expectations for independent-method validation, sampling, and evidence review. https://www.iso.org/standard/66455.html ↩