Skip to content

Commit

Permalink
replacing unit by in_kwh parameter for heating - fixes #223
Browse files Browse the repository at this point in the history
  • Loading branch information
han16nah committed Feb 16, 2025
1 parent 6beb42c commit f3d8913
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 57 deletions.
19 changes: 8 additions & 11 deletions co2calculator/api/energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,16 @@ def from_electricity(self, country_code: str):
country_code=country_code,
)

def from_heating(self, unit: str):
def from_heating(self, in_kwh: bool = False):
"""Calculate emissions from heating consumption
:param unit: unit of measurement for heating consumption
:type unit: str
:param in_kwh: if True, consumption is in kWh
"""
if unit is not None and not isinstance(unit, str):
raise ValueError("unit must be a string")
return _EnergyFromHeating(
consumption=self.consumption,
fuel_type=self.fuel_type,
own_share=self.own_share,
unit=unit,
in_kwh=in_kwh,
)


Expand Down Expand Up @@ -133,25 +130,25 @@ class _EnergyFromHeating(Energy):
:param consumption: energy consumption
:param fuel_type: energy mix used for heating (see HeatingFuel in constants.py)
:param own_share: the research group's approximate share of the total heating energy consumption. Value range 0 to 1.
:param unit: Unit of heating energy consumption (see Unit in constants.py)
:param in_kwh: if True, consumption is in kWh
:type consumption: float
:type fuel_type: str
:type own_share: float
:type unit: str
:type in_kwh: bool
"""

def __init__(
self,
consumption: float,
fuel_type: Optional[str] = None,
own_share: float = None,
unit: str = None,
in_kwh: bool = False,
):
# initialize
super(_EnergyFromHeating, self).__init__(
consumption=consumption, fuel_type=fuel_type, own_share=own_share
)
self.unit = unit
self.in_kwh = in_kwh

def calculate_co2e(self):
"""Calculate the CO2e emissions from heating.
Expand All @@ -163,7 +160,7 @@ def calculate_co2e(self):
options = {
"fuel_type": self.fuel_type,
"own_share": self.own_share,
"unit": self.unit,
"in_kwh": self.in_kwh,
}

# Filter out items where value is None
Expand Down
5 changes: 2 additions & 3 deletions co2calculator/data_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,15 @@ def __init__(self, data_dir=script_path):
f"{data_dir}/data/conversion_factors_heating.csv"
)

def get(self, fuel_type, unit):
def get(self, fuel_type):
"""Returns conversion factors from the database
:param fuel_type: Fuel type to be converted
:param unit: Unit of fuel consumption to be converted
:return: Conversion factor from unit to kwh
:rtype: float
"""
selected_factors = self.conversion_factors.query(
f'fuel_type == "{fuel_type.value}" & unit == "{unit.value}"'
f'fuel_type == "{fuel_type.value}"'
)
if selected_factors.empty:
raise ConversionFactorNotFound(
Expand Down
7 changes: 2 additions & 5 deletions co2calculator/energy/calculate_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,11 @@ def calc_co2_heating(

params = HeatingEmissionParameters.parse_obj(options)

if params.unit is not Unit.KWH:
if params.in_kwh is not True:
# Get the conversion factor
conversion_factor = conversion_factors.get(
fuel_type=params.fuel_type, unit=params.unit
)
conversion_factor = conversion_factors.get(fuel_type=params.fuel_type)

consumption_kwh = consumption * conversion_factor
params.unit = Unit.KWH
else:
consumption_kwh = consumption

Expand Down
12 changes: 5 additions & 7 deletions co2calculator/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class HeatingEmissionParameters(BaseModel):
fuel_type: Union[HeatingFuel, str] = HeatingFuel.GAS
country_code: Union[CountryCode2, str] = "global"
own_share: float = 1.0
unit: Union[Unit, str] = Unit.M3
in_kwh: bool = False

@validator("fuel_type", allow_reuse=True)
def check_fueltype(cls, v):
Expand All @@ -381,12 +381,10 @@ def check_fueltype(cls, v):
v = v.lower()
return HeatingFuel(v)

@validator("unit", allow_reuse=True)
def check_unit(cls, v):
if isinstance(v, str):
assert v.lower() in (item.value for item in Unit)
v = v.lower()
return Unit(v)
@validator("in_kwh", allow_reuse=True)
def check_in_kwh(cls, v):
assert isinstance(v, bool)
return v

@validator("own_share", allow_reuse=True)
def check_own_share(cls, v):
Expand Down
6 changes: 4 additions & 2 deletions docs/calculate/energy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ For the emission factors, see :doc:`Emission factors <emission_factors>`
Heating
--------

Per default, the expected unit is kWh. For some fuel types, the consumption may also be specified using different units, e.g., litres of oil or kg of wood chips.
In these cases, it is possible to specify the `unit`. The consumption will then be converted from the specified unit to kWh, based on common conversion factors:
Heating consumption is expected in different units, depending on the fuel type, e.g., litres of oil or kg of wood chips.
The consumption will first be converted to kWh, based on common conversion factors, and then be multiplied by the emission factor.
See the table below for the expected units per fuel type.
For all fuel types, the consumption can also be specified in kWh. In this case, the `in_kwh` flag must be set to `True`.

.. csv-table:: Conversion factors heating
:file: ../../co2calculator/data/conversion_factors_heating.csv
Expand Down
22 changes: 1 addition & 21 deletions tests/functional/test_data_code_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
FerryClass,
FlightRange,
BusTrainRange,
Unit,
)
from co2calculator.parameters import (
CarEmissionParameters,
Expand Down Expand Up @@ -101,6 +100,7 @@ def test_compare_enums_with_data(column_name, enum, emission_category, subcatego
pytest.param(TrainEmissionParameters, id="train"),
pytest.param(FerryEmissionParameters, id="ferry"),
pytest.param(MotorbikeEmissionParameters, id="motorbike"),
pytest.param(HeatingEmissionParameters, id="plane"),
],
)
def test_defaults(default_parameters):
Expand All @@ -113,26 +113,6 @@ def test_defaults(default_parameters):
), f"No emission factor found for default parameters of {default_parameters.__name__}"


def test_defaults_heating():
"""Test if default parameters are available in the csv files"""

# Get the emission factor for the default parameter combination
default_parameters = HeatingEmissionParameters()
if default_parameters.unit is not Unit.KWH:
# Get the conversion factor
conversion_factor = conversion_factors.get(
fuel_type=default_parameters.fuel_type, unit=default_parameters.unit
)
assert isinstance(
conversion_factor, float
), f"No conversion factor found for {default_parameters.fuel_type} and {default_parameters.unit}"
default_parameters.unit = Unit.KWH
co2e = emission_factors.get(default_parameters.dict())
assert isinstance(
co2e, float
), f"No emission factor found for default parameters of {default_parameters.__name__}"


def test_defaults_plane():
"""Test if default parameters are available in the csv files"""

Expand Down
28 changes: 22 additions & 6 deletions tests/unit/api/test_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ def test_calculation_electricity_with_share():

def test_calculation_heating():
"""Test whether heating emissions are calculated correctly"""
energy = (
Energy(consumption=300, fuel_type="gas")
.from_heating(unit="m^3")
.calculate_co2e()
)
energy = Energy(consumption=300, fuel_type="gas").from_heating().calculate_co2e()
assert isinstance(energy, Emissions)
assert isinstance(energy.co2e, float)


def test_calculation_heating_with_share():
"""Test whether heating emissions are calculated correctly"""
energy_inst = Energy(consumption=300, fuel_type="gas", own_share=0.5).from_heating()
assert energy_inst.own_share == 0.5
energy = energy_inst.calculate_co2e()
assert isinstance(energy, Emissions)
assert isinstance(energy.co2e, float)

Expand All @@ -60,8 +65,19 @@ def test_calculation_heating_pellets():
"""Test whether heating emissions are calculated correctly"""
energy = (
Energy(consumption=300, fuel_type="wood pellets")
.from_heating(unit="kg")
.from_heating()
.calculate_co2e()
)
assert isinstance(energy, Emissions)
assert energy.co2e == pytest.approx(17.3988, rel=0.01)


def test_calculation_heating_in_kwh():
"""Test whether heating emissions are calculated correctly"""
energy = (
Energy(consumption=300, fuel_type="gas")
.from_heating(in_kwh=True)
.calculate_co2e()
)
assert isinstance(energy, Emissions)
assert energy.co2e == pytest.approx(54.0, rel=0.01)
4 changes: 2 additions & 2 deletions tests/unit/test_calculate_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
@pytest.mark.parametrize(
"consumption,func_options,co2e_kg_expected",
[
pytest.param(250, {"fuel_type": "wood chips", "unit": "kwh"}, 2.685),
pytest.param(250, {"fuel_type": "wood chips", "unit": "kg"}, 13.962),
pytest.param(250, {"fuel_type": "wood chips", "in_kwh": True}, 2.685),
pytest.param(250, {"fuel_type": "wood chips"}, 13.962),
],
)
def test_heating_woodchips(
Expand Down

0 comments on commit f3d8913

Please sign in to comment.