From c3f4c278118a619f4588cd827d33f09bc562fd23 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Mon, 20 Feb 2023 18:36:34 -0500 Subject: [PATCH] Added heat pump energy demand and fossil fuel consumption results --- .../energy_systems/heat_pump.py | 32 ++++++++ .../results/insel_heatpump_energy_demand.py | 38 ++++++++++ hub/imports/results_factory.py | 22 +++++- hub/unittests/test_heat_pump_results.py | 74 +++++++++++++++++++ 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 hub/imports/results/insel_heatpump_energy_demand.py create mode 100644 hub/unittests/test_heat_pump_results.py diff --git a/hub/city_model_structure/energy_systems/heat_pump.py b/hub/city_model_structure/energy_systems/heat_pump.py index bb78dc72..120155c5 100644 --- a/hub/city_model_structure/energy_systems/heat_pump.py +++ b/hub/city_model_structure/energy_systems/heat_pump.py @@ -4,6 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ +from typing import List +from pandas.core.series import Series class HeatPump: @@ -13,6 +15,8 @@ class HeatPump: def __init__(self): self._model = None + self._hp_monthly_fossil_consumption = None + self._hp_monthly_electricity_demand = None @property def model(self) -> str: @@ -30,3 +34,31 @@ class HeatPump: """ if self._model is None: self._model = value + + @property + def hp_monthly_fossil_consumption(self) -> List: + """ + Fossil fuel consumption that results from insel simulation + ":return: [] + :return: + """ + return self._hp_monthly_fossil_consumption + + @hp_monthly_fossil_consumption.setter + def hp_monthly_fossil_consumption(self, value): + if type(value) is Series: + self._hp_monthly_fossil_consumption = value + + @property + def hp_monthly_electricity_demand(self) -> List: + """ + Electricity demand that results from insel simulation + ":return: [] + :return: + """ + return self._hp_monthly_electricity_demand + + @hp_monthly_electricity_demand.setter + def hp_monthly_electricity_demand(self, value): + if type(value) == Series: + self._hp_monthly_electricity_demand = value diff --git a/hub/imports/results/insel_heatpump_energy_demand.py b/hub/imports/results/insel_heatpump_energy_demand.py new file mode 100644 index 00000000..98cb439f --- /dev/null +++ b/hub/imports/results/insel_heatpump_energy_demand.py @@ -0,0 +1,38 @@ +""" +Insel Heap pump energy demand and fossil fuel consumption +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder peter.yefi@gmail.cm +""" + +from pathlib import Path +import pandas as pd + + +class InselHeatPumpEnergyDemand: + """ + Import Energy demand and fossil fuel consumption results + """ + def __init__(self, city, base_path, hp_model): + """ + :param city: the city + :param base_path: the insel simulation output file + :param hp_model: the heatpump model for both air source and water to water + """ + self._city = city + self._hp_model = hp_model + with open(Path(base_path).resolve()) as csv_file: + df = pd.read_csv(csv_file) + self._monthly_electricity_demand = df.iloc[:, 1] + self._monthly_fossil_fuel_consumption = df.iloc[:, 2] + + def enrich(self): + for energy_system in self._city.energy_systems: + if energy_system.air_source_hp is not None: + if energy_system.air_source_hp.model == self._hp_model: + energy_system.air_source_hp.hp_monthly_fossil_consumption = self._monthly_fossil_fuel_consumption + + if energy_system.water_to_water_hp is not None: + if energy_system.water_to_water_hp.model == self._hp_model: + energy_system.water_to_water_hp.hp_monthly_electricity_demand = self._monthly_electricity_demand + diff --git a/hub/imports/results_factory.py b/hub/imports/results_factory.py index afa90fb6..62adf124 100644 --- a/hub/imports/results_factory.py +++ b/hub/imports/results_factory.py @@ -11,13 +11,23 @@ from hub.helpers.utils import validate_import_export_type from hub.hub_logger import logger from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance +from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand class ResultFactory: """ - UsageFactory class + ResultFactory class """ - def __init__(self, handler, city, base_path=None): + + def __init__(self, handler, city, base_path=None, hp_model=None): + """ + + :param handler: pointer to results class to be called + :param city: the city object + :param base_path: the path to result output file + :param hp_model: (optional) the heat pump model for which + results are being retrieved + """ if base_path is None: base_path = Path(Path(__file__).parent.parent / 'data/results') self._handler = '_' + handler.lower().replace(' ', '_') @@ -28,6 +38,7 @@ class ResultFactory: raise Exception(err_msg) self._city = city self._base_path = base_path + self._hp_model = hp_model def _sra(self): """ @@ -35,6 +46,13 @@ class ResultFactory: """ SimplifiedRadiosityAlgorithm(self._city, self._base_path).enrich() + def _heat_pump(self): + """ + Enrich the city (energy system specifically) with heat pump insel simulation + results + """ + InselHeatPumpEnergyDemand(self._city, self._base_path, self._hp_model).enrich() + def _insel_meb(self): """ Enrich the city with insel monthly energy balance results diff --git a/hub/unittests/test_heat_pump_results.py b/hub/unittests/test_heat_pump_results.py new file mode 100644 index 00000000..fa7d2d15 --- /dev/null +++ b/hub/unittests/test_heat_pump_results.py @@ -0,0 +1,74 @@ +""" +Test EnergySystemsFactory and various heatpump models +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Peter Yefi peteryefi@gmail.com +""" +from unittest import TestCase +from hub.imports.geometry_factory import GeometryFactory +from hub.imports.energy_systems_factory import EnergySystemsFactory +from hub.exports.energy_systems_factory import EnergySystemsExportFactory +from hub.imports.results_factory import ResultFactory +import os +from pandas.core.series import Series + +# User defined paramenters +user_input = { + 'StartYear': 2020, + 'EndYear': 2021, + 'MaximumHPEnergyInput': 8000, + 'HoursOfStorageAtMaxDemand': 1, + 'BuildingSuppTemp': 40, + 'TemperatureDifference': 15, + 'FuelLHV': 47100, + 'FuelPrice': 0.12, + 'FuelEF': 1887, + 'FuelDensity': 0.717, + 'HPSupTemp': 60 +} + + +class TestHeatPumpResults(TestCase): + """ + TestHeatPumpResults + """ + + def setUp(self) -> None: + """ + Test setup + :return: None + """ + city_file = "tests_data/C40_Final.gml" + self._output_path = "tests_data/as_user_output.csv" + self._city = GeometryFactory('citygml', path=city_file).city + EnergySystemsFactory('air source hp', self._city).enrich() + + def test_air_source_series_heat_pump_012_results(self): + EnergySystemsExportFactory(city=self._city, user_input=user_input, hp_model='012', + output_path=self._output_path).export() + ResultFactory('heat pump', self._city, self._output_path, '012').enrich() + + for energy_system in self._city.energy_systems: + self.assertIsNone(energy_system.water_to_water_hp) + if energy_system.air_source_hp.model == '012': + self.assertIsInstance(energy_system.air_source_hp.hp_monthly_fossil_consumption, Series) + self.assertEqual(energy_system.air_source_hp.hp_monthly_fossil_consumption.iloc[5], 1.51325583) + self.assertEqual(energy_system.air_source_hp.hp_monthly_fossil_consumption.iloc[12], 35.853598782915) + + def test_air_source_series_heat_pump_015_results(self): + EnergySystemsExportFactory(city=self._city, user_input=user_input, hp_model='140', + output_path=self._output_path).export() + ResultFactory('heat pump', self._city, self._output_path, '140').enrich() + + for energy_system in self._city.energy_systems: + self.assertIsNone(energy_system.water_to_water_hp) + if energy_system.air_source_hp.model == '140': + self.assertIsInstance(energy_system.air_source_hp.hp_monthly_fossil_consumption, Series) + self.assertEqual(energy_system.air_source_hp.hp_monthly_fossil_consumption.iloc[0], 7.91282225) + self.assertEqual(energy_system.air_source_hp.hp_monthly_fossil_consumption.iloc[2], 0.068873927) + + def tearDown(self) -> None: + try: + os.remove(self._output_path) + except OSError: + pass