GHG-ALIGN
GHG Scenario Alignment
1. Identity
Depends on module:
2. Executive Overview
2.1. Purpose of GHG Scenario Alignment
Purpose
Evaluates how closely an entity’s emissions trajectory aligns with a reference climate scenario (IPCC, IEA, SBTi, NGFS) and goal (e.g. 1.5°C, 2.0°C).
This engine represents the top of the GHG computation pyramid.
Typical usage
- Alignment with IPCC / IEA pathways
- Temperature score derivation
- Inputs to transition-risk and strategy dashboards
Produces:
- Alignment score
- Distance/ratio/area metrics
- Temperature score (optional)
- Full governance metadata
Supports:
- CSRD
- ESRS E1
- SBTi reporting
- Climate transition risk modeling
- Portfolio alignment
2. Purpose
Evaluates how closely an entity’s emissions trajectory aligns with a reference climate scenario (IPCC, IEA, SBTi, NGFS).
Produces:
- Alignment score
- Distance/ratio/area metrics
- Temperature score (optional)
- Full governance metadata
Supports:
- CSRD
- ESRS E1
- SBTi reporting
- Climate transition risk modeling
- Portfolio alignment
3. Contract Schemas (ZAR)
All inputs and outputs MUST conform to ZAR-registered schemas.
3.1. Input Schema
ZAR Address:
schema.compute.ghg.scenario.inputs.v1_0_0
Required:
emissions(time series)scenario_curve_refOR resolution inputsalignment_metric(DISTANCE|RATIO|AREA)alignment(BY_YEAR)
Optional:
scopestart_yearend_yearsector_keyregiontempTarget
3.2. Options Schema
ZAR Address:
schema.compute.ghg.scenario.options.v1_0_0
Options:
normalization(NONE|BASE_YEAR)temperature_mapping_refexceedance_policy(ALLOW|CLIP)roundingensemble_policy(MEDIAN|P33|P67)
3.3. Output Schema
ZAR Address:
schema.compute.ghg.scenario.output.v1_0_0
Output MUST include:
alignment_score
distance_series
temperature_score (optional)
metadata
4. Architectural Principles (Normative)
4.1. Separation of Concerns (MANDATORY)
The engine MUST strictly separate:
- Scenario Resolution
- Provider Selection
- License Validation
- Region Fallback
- Variable Harmonization
- Alignment Mathematics
Alignment mathematics MUST NOT perform provider selection logic.
Violation of this rule invalidates audit guarantees.
5. Scenario Resolution Architecture
Scenario resolution occurs before alignment.
Inputs:
- sector_key
- region
- temperature target
- canonical_variable_key
Outputs:
- scenario artifact (fully resolved)
6. Sector-Priority Policy (Normative)
6.1. Policy Artifact
Scenario resolution MUST be governed by:
scenario_align.policy.vX.json
Artifact MUST be:
- Versioned
- Hashable
- Immutable once published
- Audit-referenceable
Defines:
- provider precedence
- region ladder
- sector defaults
- metric defaults
- quality ranking
6.2. Provider Order
Example:
"preferred_order": ["IEA","SBTi","NGFS","IPCC"]
Rules:
- Providers MUST be attempted in declared order.
- Dynamic reordering is forbidden.
- Policy version MUST be recorded in metadata.
7. License-Gated Provider Resolution
The engine MUST call:
LicenseService.check(clientId, provider, purpose="analytics")
The engine MUST NOT inspect license flags directly.
Resolution algorithm:
- Load provider order from policy.
- For each provider:
- Check license
- If granted → attempt resolution
- If denied → skip
- If none available:
- Fallback to IPCC baseline
- If still unresolved:
- Raise SCENARIO_PROVIDER_UNAVAILABLE
7.1. Mandatory Metadata
metadata.selection = {
policy_version,
providers_tried,
provider_selected,
license_status,
region_used,
metric_used
}
Required for:
- CSRD defensibility
- External assurance
- Federation reproducibility
8. Region Ladder Resolution
Default ladder:
COUNTRY → EU27 → OECD → GLOBAL
Rules:
- Attempt country.
- If unavailable, escalate.
- First valid match MUST be selected.
- Ladder steps MUST be recorded.
If adjustment factors exist:
- Apply AFTER region fallback.
- Record adjustment in metadata.
9. Canonical Variable Resolution
All scenario artifacts MUST expose:
canonical_variable_key
Examples:
gco2_per_kwhtco2_per_t_steelco2_total_gt_per_year
Provider adapters MUST map external variable names to canonical keys.
Mappings MUST be versioned in EngineAliasMap.
10. Metric Harmonization
If company metric ≠ scenario metric:
Conversion MUST occur before alignment.
Rules:
- Absolute ↔ Intensity conversions MUST be deterministic.
- Derivations MUST be logged.
- All transformations MUST be reproducible.
11. Scope Handling
Supported:
TOTALSCOPE1+2SCOPE1+2+3
If scope not provided:
- Engine MUST default to sector-defined scope from NACE metadata.
Scope used MUST be recorded.
Absolutely — here’s a production-ready rewrite of Section 12 that’s tighter, less repetitive, and gives engineers exactly what to implement. It also cleanly replaces the old 4.5.1 without duplicating it.
12. IPCC Ensemble Construction (Normative)
When the selected provider is IPCC (AR6 IAM pathways), the engine MUST resolve scenario curves by constructing an ensemble time-series from vetted IAM rows.
12.0. Inputs and Outputs
Inputs (minimum):
scenario_class(derived from tempTarget, e.g. C1 for 1p5C, C3 for 2C)canonical_variable_keyregion_resolved(after region ladder)years(horizon)ensemble_policy(MEDIAN default; optional P33, P67)
Output (resolved scenario artifact MUST include):
series:[[year, value], ...]for ensemble statistic(s)ensemble_meta: provenance + traceability block (see 12.3)
12.1. Scenario Class Routing (Normative)
IPCC alignment MUST use AR6 category codes (C1–C7) to route temperature intent:
- 1p5C → C1 (no/limited overshoot)
- 2C → C3 (well-below 2°C)
The class routing MUST be deterministic and versioned (policy or engine mapping).
12.2. Row Selection Rules (Normative)
The engine MUST select candidate rows from the IPCC scenario store using the following filters:
1. Class filter
Select only rows where category_code == scenario_class.
2. Vetting filter (default = strict)
Rows MUST be vetted (e.g., category_vetting_historical == true or equivalent vetted flag).
- Non-vetted rows MUST be excluded unless
options.allow_non_vetted = true. 3. Region filter Select rows matching the resolved region. - If the IPCC dataset is global-only for the requested variable, the engine MUST treat region as GLOBAL and record this in metadata. 4. Variable filter Only include rows that map to the requested canonical_variable_key.
- Variable mapping MUST be resolved via EngineAliasMap (versioned). 5. Year coverage requirement Candidate rows MUST cover the requested horizon.
- If partial coverage exists, the engine MUST either:
- (a) truncate to overlapping years, or
- (b) error with
SCENARIO_NO_OVERLAP, depending on engine optionoptions.year_policy. 6. Minimum ensemble size If fewer thanmin_models_requiredrows remain (default: 3), the engine MUST returnSCENARIO_PROVIDER_UNAVAILABLE(or a more specificIPCC_ENSEMBLE_TOO_SMALL) and include counts in the error payload.
12.3. Ensemble Construction Rules (Normative)
For each (year, canonical_variable_key, region_resolved):
- Collect values across all selected IAM rows.
- Compute MEDIAN (mandatory).
- If requested and available, compute P33 and/or P67.
- Construct the ensemble series as:
{
"ensemble": "median",
"canonical_variable_key": "gco2_per_kwh",
"unit": "gCO2/kWh",
"series": [[2025, 320.0], [2030, 180.0], ...]
}
Determinism requirements:
- Sorting of models MUST be stable (e.g., lexical by
model, thenscenario) before any aggregation. - The ensemble MUST be reproducible given identical dataset version + filters.
12.4. Output Traceability Requirements (Normative)
The resolved IPCC scenario artifact MUST include an audit block sufficient to reproduce the ensemble externally:
"ensemble_meta": {
"scenario_class": "C1",
"ensemble_policy": ["MEDIAN","P33","P67"],
"models_used": ["REMIND-MAgPIE","GCAM","WITCH-GLOBIOM"],
"model_count": 3,
"source_rows_ref": ["<row_id_1>","<row_id_2>","<row_id_3>"],
"dataset_ref": "ipcc_ar6_vetted_withclimate_short",
"dataset_version": "2024.0",
"dataset_checksum": "sha256:...",
"filters_applied": {
"vetted_only": true,
"region": "EU27",
"canonical_variable_key": "gco2_per_kwh",
"years": [2025,2050]
}
}
Additionally:
- If available in the selected rows, the engine MUST propagate exceedance probabilities into metadata:
p_exceed_1p5c_*, p_exceed_2c_*(and emulator cross-checks if present)
- Row-level lineage MUST be reconstructible (via
source_rows_refor equivalent).
12.5. Error Conditions (Normative)
The engine MUST raise structured errors with context:
IPCC_VARIABLE_RESOLUTION_FAILEDIPCC_NO_ROWS_MATCHIPCC_ENSEMBLE_TOO_SMALLSCENARIO_NO_OVERLAP
Errors MUST include:
scenario_classcanonical_variable_keyregion_resolvedvetted_onlycandidate_row_countselected_row_countdataset_version/checksum
13. Alignment Mathematics (Normative)
Let:
- = company emissions at year
- = scenario emissions at year
- Horizon defined as
- = number of years in the overlapping horizon
All alignment metrics MUST be computed only on the overlapping year domain after scenario resolution and normalization.
13.1. Distance Metric (Default)
Per-year distance:
Aggregate alignment score:
Properties:
- Positive indicates average exceedance.
- Negative indicates average underperformance (below pathway).
- Units preserved from input metric.
13.2. Ratio Metric
Per-year ratio:
Where:
- is a small positive constant to prevent division by zero.
Aggregate ratio score:
Properties:
- → perfect alignment.
- → exceedance.
- → outperforming scenario.
13.3. Area Metric (Cumulative Overshoot)
Cumulative exceedance over horizon:
Properties:
- Only positive exceedance is accumulated.
- Units match emissions metric.
- Represents total overshoot across horizon.
- Sensitive to horizon length (non-normalized).
13.4. Horizon Determination Rules (Normative)
%reduction_required_2030 = 1 − (pathway_2030 / entity_2024)
Reduction Logic:
Absolute reduction:
13.5. Cumulative Budget Overshoot
Σ emissions 2025–2050 vs integrated pathway
13.6. Temperature Score Mapping
- Derive implied annual reduction rate:
- Map to temperature via provider-specific function:
And require:
- mapping functions versioned
- deterministic
- provider-dependent
13.7. Horizon Determination Rules (Normative)
The effective horizon MUST be:
If intersection is empty:
→ Raise SCENARIO_NO_OVERLAP
If partial overlap:
- Either truncate (default)
- Or error if
options.year_policy = STRICT
Horizon MUST be recorded in metadata:
"alignment_horizon": {
"start": 2025,
"end": 2050,
"n_years": 26
}
14. Normalization
If BASE_YEAR normalization:
- Normalize and to
1.0at base year. - Compute alignment on normalized curves.
Metadata MUST record:
- base year
- normalization applied
15. Temperature Mapping
If temperature_mapping_ref provided:
- Map alignment score to implied temperature
- Mapping MUST be deterministic
- Mapping MUST be versioned
16. Error Model (Production Registry)
Engine MUST use structured error codes:
SCENARIO_PROVIDER_UNAVAILABLELICENSE_DENIEDREGION_FALLBACK_FAILEDVARIABLE_RESOLUTION_FAILEDMETRIC_TYPE_MISMATCHSCENARIO_NO_OVERLAPSCENARIO_NON_FINITE_VALUE
Errors MUST include:
provider attemptedpolicy_versionregion ladder statecanonical_variable_keyexecution_ref
17. Federation & Audit Requirements
To reproduce externally, export MUST include:
MEID- Build hash
- Execution reference
- Input schema version
- Options schema version
- Scenario artifact
- Policy artifact hash
- Alignment metric
- Normalization settings
- Emissions trajectory
Provenance chain MUST show:
MEID_CALC_GHG_AGGR
→ MEID_CALC_CARBON_BUDGET
→ MEID_SCEN_GHG_ALIGN
Rebuild MUST produce identical output.
18. Performance Characteristics
- Time Complexity: O(n)
- Memory: O(n)
- Suitable for long-horizon analysis (to 2100)
- Designed for portfolio-scale batch execution
19. Compliance & Governance Alignment
Engine supports:
- CSRD Article 19a
- ESRS E1
- SBTi temperature scoring
- NGFS portfolio alignment
- Financial climate risk disclosures
20. Production Guarantees
This engine guarantees:
- Deterministic output
- Provider auditability
- License compliance
- Region fallback transparency
- Canonical variable integrity
- Federation reproducibility
- Strict architectural separation
Demo
This micro-engine demonstrates:
- SSOT policy artifact (provider fallback order + sector dictionary + defaults)
- Strong typing (
sectorKeyunion generated from policy JSON) - Query normalization (defaults injected + required-field validation)
- Provider selection (policy chain + license gating)
- Series output with audit metadata suitable for governance and reporting
Demo 1: Power generation (EU27), 1.5°C, 2025–2050
{
"providerPolicy": [
"IEA",
"SBTi",
"NGFS",
"IPCC"
],
"licenseFlags": {
"iea": false
},
"preferredStat": "median",
"includeUncertainty": "core",
"interpolation": "linear",
"sectorKey": "power_generation",
"region": "EU27",
"variable": "gco2_per_kwh",
"tempTarget": "1p5C",
"horizon": {
"start": 2025,
"end": 2050
},
"title": "Power generation decarbonization trajectory"
}Loading...
Demo 2: Steel (GLOBAL), 2°C, 2025–2050
{
"providerPolicy": [
"IEA",
"SBTi",
"NGFS",
"IPCC"
],
"licenseFlags": {
"iea": false
},
"preferredStat": "median",
"includeUncertainty": "core",
"interpolation": "linear",
"sectorKey": "steel",
"region": "GLOBAL",
"variable": "tco2_per_t_steel",
"tempTarget": "2C",
"horizon": {
"start": 2025,
"end": 2050
},
"title": "Steel intensity trajectory"
}Loading...
Governance notes
- The provider order is defined in the policy artifact (
scenario_align.policy.v1.json) - The selected provider is recorded in
metadata.selection.policyApplied - The policy artifact can be hashed and referenced for auditability in future reporting pipelines
Accompanying files: /workspaces/zayaz-docs/docusaurus/src/engines/ghg/scenario_align
APPENDIXES
Appendix A — Reference Tables and Seeds (Production)
A.1. seed_provider_policy.csv (Sector-Priority Policy)
sector_key,preferred_order,region_ladder,temp_targets,metric_default,policy_profile,quality_rank,notes
autos.c29_1,"[IEA,SBTi,NGFS,IPCC]","[COUNTRY,EU27,EEA,GLOBAL]","[1p5C,2C]",intensity,default,4,"Use-phase intensity benchmark with fallbacks."
steel.c24_1,"[IEA,SBTi,NGFS,IPCC]","[COUNTRY,EU27,OECD,GLOBAL]","[1p5C,2C]",intensity,default,5,"Primary vs EAF handled by subsector/variant."
power_generation.d35,"[IEA,NGFS,IPCC]","[COUNTRY,EU27,GLOBAL]","[1p5C,2C]",intensity,default,5,"Grid intensity is canonical variable."
A.2. seed_region_ladder.csv
sector_key,region_from,region_to,order
autos.c29_1,ISO2,EU27,1
autos.c29_1,ISO2,EEA,2
autos.c29_1,ISO2,GLOBAL,3
autos.c29_1,EU27,GLOBAL,4
autos.c29_1,EEA,GLOBAL,5
A.3. seed_variable_mapping_autos.csv (minimal end-to-end)
sector_key,variable_key,description,units,aggregation,scope,notes
autos.c29_1,gco2_per_km_passenger,"WTW passenger vehicle intensity (fleet avg)","gCO2/km",as_is,S3,"Primary alignment variable."
autos.c29_1,grid_intensity_gco2_per_kwh,"Grid intensity for BEV/PHEV conversion","gCO2/kWh",as_is,S2,"Used when deriving BEV use-phase."
autos.c29_1,pkm_total,"Passenger-km activity (road)","billion pkm/yr",median,Total,"Used if intensity must be derived from totals."
autos.c29_1,co2_transport_total,"Total transport CO2","MtCO2/yr",median,Total,"Backstop / reconstruction."
Appendix B — dbt Models / Views (Contracts)
B.1. Effective policy view
models/scenario_policy/provider_policy_effective.sql
select
sector_key,
preferred_order,
region_ladder,
temp_targets,
metric_default,
policy_profile,
quality_rank
from {{ ref('seed_provider_policy') }};
B.2. NACE alignment defaults view (exact JSON for adapter)
models/nace/vw_nace_alignment_defaults.sql
select
n.sector_key,
n.nace_code,
jsonb_build_object(
'sectorKey', n.sector_key,
'naceCode', n.nace_code,
'policyProfile', coalesce(n.policy_profile, p.policy_profile),
'qualityRank', coalesce(n.quality_rank, p.quality_rank),
'providerOrder', p.preferred_order,
'regionLadder', coalesce(n.region_ladder_override, p.region_ladder),
'tempTargets', coalesce(n.default_temp_targets, p.temp_targets),
'metricDefault', coalesce(n.default_metric, p.metric_default),
'variables', coalesce(n.variable_keys, '[]'::jsonb),
'intensity', jsonb_build_object(
'denominatorKey', n.intensity_denominator_key,
'units', n.intensity_units,
'activityUnits', n.activity_units
)
) as adapter_payload
from {{ ref('dim_nace_codes') }} n
join {{ ref('provider_policy_effective') }} p
on p.sector_key = n.sector_key;
B.3. Materialized version + indexes
models/nace/mv_nace_alignment_defaults.sql
{{ config(materialized='materialized', indexes=[
{'columns': ['sector_key']},
{'columns': ['nace_code']},
{'columns': ['adapter_payload'], 'type': 'gin'}
]) }}
select * from {{ ref('vw_nace_alignment_defaults') }};
Result: adapter can query one row per NACE and get the JSON blob it needs.
Appendix C — License Module Integration (Production Contract)
C.1. License check response shape (minimum)
type LicenseDecision = {
provider: "IEA"|"SBTi"|"NGFS"|"IPCC";
result: "granted"|"denied";
entitlementRef?: string; // artifact id / subscription id
checksum?: string; // immutability proof
checkedAt: string; // ISO timestamp
reason?: string; // human-readable
}
C.2. Provider selection MUST log tried providers
type ProviderSelectionLog = {
policyVersion: string;
policyProfile: string;
providersTried: Array<{provider: string; decision: "granted"|"denied"}>;
providerSelected: string;
regionUsed: string;
regionPathTried: string[];
canonicalVariableKey: string;
}
Appendix D — Provider Selection Flow (TS Skeleton)
import { LicenseService } from "@zayaz/licensing";
type Provider = "IEA" | "SBTi" | "NGFS" | "IPCC";
export async function pickProvider(ctx: {
clientId: string;
sectorKey: string;
region: string;
tempTarget?: "1p5C"|"2C";
metric?: "absolute"|"intensity";
canonicalVariableKey: string;
policy: {
preferred_order: Provider[];
region_ladder: string[];
temp_targets: ("1p5C"|"2C")[];
metric_default: "absolute"|"intensity";
policy_version: string;
policy_profile: string;
};
}) {
const tried: Array<{provider: Provider; decision: "granted"|"denied"}> = [];
for (const provider of ctx.policy.preferred_order) {
const decision = await LicenseService.check({
clientId: ctx.clientId,
provider,
purpose: "analytics",
sectorKey: ctx.sectorKey
});
tried.push({ provider, decision: decision.result });
if (decision.result === "granted") {
return { provider, license: decision, tried };
}
}
// if policy didn't include IPCC, allow public baseline
if (!ctx.policy.preferred_order.includes("IPCC")) {
return {
provider: "IPCC" as const,
license: { provider: "IPCC", result: "granted", entitlementRef: "public", checkedAt: new Date().toISOString() },
tried
};
}
throw new Error(`SCENARIO_PROVIDER_UNAVAILABLE: tried=${JSON.stringify(tried)}`);
}
Appendix E — Region Ladder Flow (Pseudo + Expected behavior)
Algorithm
- Take requested region (ISO2/ISO3 if country).
- Walk ladder until a provider returns a series for (scenario, variable, region).
- Stop at first match (deterministic).
Example tried regions (autos)
NO → EU27 → EEA → GLOBAL
Metadata MUST record
- regionUsed
- regionsTried (in order)
Appendix F — Scenario Artifact Contract (Stored/Resolved)
Minimum schema stored in ZAR registry:
{
"provider": "NGFS",
"scenarioId": "NetZero2050",
"temperatureIntent": "1p5C",
"region": "EU27",
"canonicalVariableKey": "gco2_per_kwh",
"metricType": "intensity",
"unit": "gCO2/kWh",
"series": [[2025, 320.0], [2030, 180.0], [2040, 70.0], [2050, 10.0]],
"version": "2025.1",
"provenanceHash": "sha256:..."
}
Appendix G — End-to-End Example Request/Response
G.1. Request (adapter-resolved)**
{
"clientId": "ECO-196-123-456-789",
"sectorKey": "autos.c29_1",
"region": "NO",
"tempTarget": "1p5C",
"canonicalVariableKey": "gco2_per_km_passenger",
"horizon": { "start": 2025, "end": 2050 },
"mode": "scoring",
"alignmentMetric": "DISTANCE",
"normalization": "BASE_YEAR"
}
G.2. Response (includes audit block)**
{
"alignmentScore": 0.82,
"distanceSeries": [[2025,0.0],[2030,12.4],[2040,8.1],[2050,1.2]],
"metadata": {
"selection": {
"policyVersion": "1.0.0",
"policyProfile": "default",
"providersTried": [{"provider":"IEA","decision":"denied"},{"provider":"SBTi","decision":"granted"}],
"providerSelected": "SBTi",
"regionsTried": ["NO","EU27","GLOBAL"],
"regionUsed": "EU27",
"canonicalVariableKey": "gco2_per_km_passenger",
"metricUsed": "intensity"
},
"scenarioArtifactRef": "zar:scenario/SBTi/Transport_SDA_1p5/v2024.0",
"normalization": { "type": "BASE_YEAR", "baseYear": 2025 }
}
}
Appendix H — “What programmers implement first” checklist
Phase 0 (Dev E2E)
- Seed tables: provider policy, region ladder, autos variable mapping
vw_nace_alignment_defaults(or materialized mv_*)- pickProvider() wired to License Module
- Provider adapters stubbed: return mock series per provider
- Scenario artifact resolution stub (by ref or query)
- Output metadata block exactly as spec
Phase 1 (Real data)
- IEA adapter reads licensed dataset
- NGFS adapter reads NGFS pathways store
- SBTi adapter reads SDA curves store
- IPCC adapter builds ensembles from IAM rows
- Variable canonicalization + unit normalization