Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import re
from typing import Optional
from .fleet_emissions_data import GHG_FACTORS ← this capability’s own data, compiled from data/ghg-factors.json into the same file by fune build
from .fleet_emissions_types import EmissionsUnit, FleetEmissions
DECIMAL = re.compile(r"([0-9]+)(?:\.([0-9]+))?")
MAX_SAFE = 9007199254740991
def _format_factor(factor: int) -> str:
return "%d.%05d" % (factor // 100000, factor % 100000)
def fleet_emissions(activity: str, quantity: str, unit: EmissionsUnit, factor_year: int) -> FleetEmissions:
"""Scope 1 emissions for fuel burned or distance driven, from the UK
government's conversion factors for company reporting.
The quantity is decimal text and the factor an integer in 1e-5 kg, so the
product is exact and rounded once, to the gram. Multiplying floats instead
drifts in the last place and can round a half-gram the wrong way.
"""
if unit not in ("litres", "km", "miles"):
raise ValueError('unknown unit "%s": use litres, km or miles' % (unit,))
if isinstance(factor_year, bool) or not isinstance(factor_year, int):
raise ValueError("factorYear must be a whole year, received %s" % (factor_year,))
match = DECIMAL.fullmatch(quantity) if isinstance(quantity, str) else None
if match is None:
raise ValueError('quantity must be plain decimal text, not negative, received "%s"' % (quantity,))
fraction = match.group(2) or ""
digits = (match.group(1) + fraction).lstrip("0")
if len(digits) > 15 or len(fraction) > 9:
raise ValueError(
'quantity must have at most 15 significant digits and 9 decimal places, received "%s"' % (quantity,)
)
found: Optional[int] = None
year_known = False
activity_known = False
for row in GHG_FACTORS:
if row.factor_year != factor_year:
continue
year_known = True
if row.activity != activity:
continue
activity_known = True
if row.unit == unit:
found = row.factor
if not year_known:
raise ValueError("no conversion factors for %d: this package carries 2023 to 2026" % (factor_year,))
if not activity_known:
raise ValueError('unknown activity "%s" in the %d factors' % (activity, factor_year))
if found is None:
raise ValueError("no conversion factor for %s per %s in %d" % (activity, unit, factor_year))
# grams = quantity x factor x 1e-5 kg x 1000 g/kg = quantity x factor / 100
numerator = int(match.group(1) + fraction) * found
denominator = 10 ** len(fraction) * 100
grams = (2 * numerator + denominator) // (2 * denominator)
if grams > MAX_SAFE:
raise ValueError("the result exceeds 2^53 - 1 grams")
return FleetEmissions(
co2e_grams=grams, factor=_format_factor(found), factor_year=factor_year, activity=activity, unit=unit
)