Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from .energy_gas_kwh_types import GasEnergy, GasVolumeUnit
from .math_round_div import round_div ← from math.round-div ^1.0.0 · built alongside by fune
# 1.02264 / 3.6 in lowest terms, with the scales of the arguments folded in:
# watt-hours = hundredths of m3 x 4261 x CV (thousandths of MJ/m3) / 1,500,000.
FACTOR = 4261
DIVISOR = 1_500_000
MAX_VOLUME = 1_000_000_000_000
MAX_SAFE = 9007199254740991
def _is_int(value: object) -> bool:
return isinstance(value, int) and not isinstance(value, bool)
def gas_kwh(volume: int, volume_unit: GasVolumeUnit, calorific_value: int) -> GasEnergy:
"""Gas meter units to kWh, the way a GB bill does it: volume (x 2.83 for an
imperial meter) x 1.02264 x calorific value / 3.6, rounded once, half-up,
to the watt-hour.
"""
if not _is_int(volume) or volume < 0 or volume > MAX_VOLUME:
raise ValueError("volume must be a whole number of units from 0 to %d, received %s" % (MAX_VOLUME, volume))
if not _is_int(calorific_value) or calorific_value < 1 or calorific_value > 100000:
raise ValueError(
"calorific value must be a whole number of thousandths of a MJ/m³ from 1 to 100000, received %s"
% (calorific_value,)
)
if volume_unit == "cubic-metres":
hundredths = volume * 100
elif volume_unit == "hundreds-cubic-feet":
hundredths = volume * 283
else:
raise ValueError('unknown volume unit "%s": expected cubic-metres or hundreds-cubic-feet' % (volume_unit,))
scaled = FACTOR * calorific_value
whole, rest = divmod(hundredths, DIVISOR)
watt_hours = whole * scaled + round_div(rest * scaled, DIVISOR, "half-up")
if watt_hours > MAX_SAFE:
raise ValueError("gas energy too large to calculate exactly")
return GasEnergy(cubic_metres_hundredths=hundredths, watt_hours=watt_hours)