from .energy_carbon_emissions_data import CONVERSION_FACTORS from .energy_carbon_emissions_types import CarbonEmissions from .math_round_div import round_div # quantity (thousandths of a unit) x factor (hundred-thousandths of a kg) # / 100000 is grams. DIVISOR = 100_000 MAX_SAFE = 9007199254740991 def carbon_emissions(fuel: str, quantity: int, year: int) -> CarbonEmissions: """kgCO2e, as whole grams, for an amount of a fuel, from the UK government conversion factor set for ``year``. """ if not isinstance(quantity, int) or isinstance(quantity, bool) or quantity < 0: raise ValueError( "quantity must be a whole number of thousandths of a unit, 0 or more, received %s" % (quantity,) ) if not any(f.fuel == fuel for f in CONVERSION_FACTORS): raise ValueError('unknown fuel "%s"' % (fuel,)) row = next((f for f in CONVERSION_FACTORS if f.fuel == fuel and f.year == year), None) if row is None: raise ValueError("no conversion factor for %s in %s" % (fuel, year)) whole, rest = divmod(quantity, DIVISOR) grams = whole * row.factor + round_div(rest * row.factor, DIVISOR, "half-up") if grams > MAX_SAFE: raise ValueError("emissions too large to calculate exactly") return CarbonEmissions(fuel=fuel, unit=row.unit, year=year, scope=row.scope, factor=row.factor, grams=grams)