impl/typescript.ts
1,461 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { CONVERSION_FACTORS } from "./energy_carbon_emissions_data.ts"; ← this capability’s own data, compiled from data/conversion-factors.json into the same file by fune build
import { type CarbonEmissions } from "./energy_carbon_emissions_types.ts";
// quantity (thousandths of a unit) x factor (hundred-thousandths of a kg)
// / 100000 is grams.
const DIVISOR = 100_000;
/**
* kgCO2e, as whole grams, for an amount of a fuel, from the UK government
* conversion factor set for `year`.
*/
export function carbonEmissions(fuel: string, quantity: number, year: number): CarbonEmissions {
if (!Number.isInteger(quantity) || quantity < 0) {
throw new RangeError(`quantity must be a whole number of thousandths of a unit, 0 or more, received ${quantity}`);
}
if (!CONVERSION_FACTORS.some((f) => f.fuel === fuel)) {
throw new RangeError(`unknown fuel "${fuel}"`);
}
const row = CONVERSION_FACTORS.find((f) => f.fuel === fuel && f.year === year);
if (row === undefined) {
throw new RangeError(`no conversion factor for ${fuel} in ${year}`);
}
// Split the quantity so the product never leaves the exact range.
const whole = Math.floor(quantity / DIVISOR);
const rest = quantity - whole * DIVISOR;
const grams = whole * row.factor + roundDiv(rest * row.factor, DIVISOR, "half-up");
if (grams > Number.MAX_SAFE_INTEGER) {
throw new RangeError("emissions too large to calculate exactly");
}
return { fuel, unit: row.unit, year, scope: row.scope, factor: row.factor, grams };
}