hub/exports/energy_systems/heat_pump_export.py

181 lines
8.9 KiB
Python

"""
HeatPumpExport exports heatpump coefficient into several formats
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2021 Project Author Peter Yefi peteryefi@gmail.com
"""
import os
from typing import List, Tuple, Union, Dict
import yaml
from string import Template
import pandas as pd
class HeatPumpExport:
"""
Exports heat pump values as coefficients
of some defined function
"""
def __init__(self, base_path, city):
self._template_path = (base_path / 'heat_pumps/template.txt')
self._constants_path = (base_path / 'heat_pumps/constants.yaml')
# needed to compute max demand.
self._demand_path = (base_path / 'heat_pumps/demand.txt')
self._city = city
self._input_data = None
self._base_path = base_path
def run_insel(self, user_input: Dict, hp_model: str, data_type: str) -> None:
"""
Runs insel and write the necessary files
:param user_input: a dictionary containing the user
values necessary to run insel
:param hp_model: a string that indicates the heat
pump model to be used e.g. 012, 015
:param data_type: a string that indicates whether
insel should run for heat or cooling performance
:return:
"""
self._input_data = user_input
# update input data with other data necessary to run insel
capacity_coff, comp_power_coff = self._extract_model_coff(hp_model, data_type)
self._update_input_data_with_coff(capacity_coff, comp_power_coff)
# update input data with constants
self._update_input_data_with_constants()
# update input data with input and output files for insel
self._update_input_data_with_files()
insel_file_handler = None
insel_template_handler = None
try:
# run insel
insel_template_handler = open(self._template_path, "r")
insel_template_handler = insel_template_handler.read()
insel_template = Template(insel_template_handler).substitute(self._input_data)
# create the insel file and write the template with substituted values into it
insel_file = (self._base_path / 'heat_pumps/dompark_heat_pump.insel')
insel_file_handler = open(insel_file, "w")
insel_file_handler.write(insel_template)
# Now run insel
os.system('insel {}'.format(insel_file))
# Writer headers to csv output files generated by insel
self._write_insel_output_headers()
except IOError as err:
print("I/O exception: {}".format(err))
finally:
insel_file_handler.close()
insel_template_handler.close()
def _write_insel_output_headers(self):
"""
Write headers to the various csv file generated by insel
:return:
"""
header_data = {
self._input_data['fileOut1']: ['Year', ' Month', ' Day', 'Hour', 'Minute', 'HP Heat Output (kW)',
'HP Electricity Consumption (kW)', 'HP COP', 'TES Charging Rate (kg/s)',
'TES Discharging Rate (kg/s)', 'TES Node 1 Temperature', 'TES Node 2 Temperature',
'TES Node 3 Temperature', 'TES Node 4 Temperature', 'TES Energy Content (J)',
'TES Energy Content (kWh)', 'TES Energy Content Variation (kWh)',
'Auxiliary Heater Fuel Flow Rate (kg/s)', 'Auxiliary Heater Energy Input (kW)',
'HP Operational Cost (CAD)', 'Auxiliary Heater Operational Cost (CAD)',
'Operational CO2 Emissions of HP (g)',
'Operational CO2 Emissions of Auxiliary Heater (g)',
'Return Temperature', 'Demand (kW)'],
self._input_data['fileOut2']: ['Day', 'Operational Daily Emissions from Heat Pumps (g)',
'Operational Daily Emissions from Auxiliary Heater (g)'],
self._input_data['fileOut3']: ['Month', 'Monthly Operational Costs of Heat Pumps (CAD)',
'Monthly Operational Costs of Auxiliary Heater (CAD)'],
self._input_data['fileOut4']: ['Month', 'Monthly Fuel Consumption of Auxiliary Heater (m3)'],
self._input_data['fileOut5']: ['Month', 'Operational Monthly Emissions from Heat Pumps (g)',
'Operational Monthly Emissions from Auxiliary Heater (g)'],
self._input_data['fileOut6']: ['Day', 'Daily HP Electricity Demand (kWh)'],
self._input_data['fileOut7']: ['Day', 'Daily Operational Costs of Heat Pumps (CAD)',
'Daily Operational Costs of Auxiliary Heater (CAD)'],
self._input_data['fileOut8']: ['Month', 'Monthly HP Electricity Demand (kWh)'],
self._input_data['fileOut9']: ['Day', 'Daily Fuel Consumption of Auxiliary Heater (m3)'],
self._input_data['fileOut10']: ['Year', 'Month', 'Day', 'Hour', 'HP Electricity Demand (kWh)']
}
for file_path, header in header_data.items():
file_path = file_path.strip("'")
df = pd.read_csv(file_path, sep='\s+')
df.to_csv(file_path, header=header)
def _update_input_data_with_files(self):
"""
Updates input data for insel with some files that will
be written to after insel runs. Also specifies and input file
which is the Heating Demand (demand.txt) file
:return:
"""
self._input_data["HeatingDemand"] = f"'{str(self._demand_path)}'"
self._input_data["fileOut1"] = f"'{str((self._base_path / 'heat_pumps/technical_performance.csv'))}'"
self._input_data["fileOut2"] = f"'{str((self._base_path / 'heat_pumps/system_daily_emissions.csv'))}'"
self._input_data["fileOut3"] = f"'{str((self._base_path / 'heat_pumps/monthly_operational_costs.csv'))}'"
self._input_data["fileOut4"] = f"'{str((self._base_path / 'heat_pumps/monthly_fossil_fuel_consumptions.csv'))}'"
self._input_data["fileOut5"] = f"'{str((self._base_path / 'heat_pumps/system_monthly_emissions.csv'))}'"
self._input_data["fileOut6"] = f"'{str((self._base_path / 'heat_pumps/daily_hp_electricity_demand.csv'))}'"
self._input_data["fileOut7"] = f"'{str((self._base_path / 'heat_pumps/daily_operational_costs.csv'))}'"
self._input_data["fileOut8"] = f"'{str((self._base_path / 'heat_pumps/monthly_hp_electricity_demand.csv'))}'"
self._input_data["fileOut9"] = f"'{str((self._base_path / 'heat_pumps/daily_fossil_fuel_consumption.csv'))}'"
self._input_data["fileOut10"] = f"'{str((self._base_path / 'heat_pumps/hp_hourly_electricity_demand.csv'))}'"
def _compute_max_demand(self):
"""
Retrieves the maximum demand value from
the demands text file
:return: float
"""
max_demand = -1
with open(self._demand_path) as file_handler:
for demand in file_handler.readlines():
if float(demand) > max_demand:
max_demand = float(demand)
return max_demand
def _update_input_data_with_constants(self):
with open(self._constants_path) as file:
constants_dict = yaml.load(file, Loader=yaml.FullLoader)
for key, value in constants_dict.items():
self._input_data[key] = value
# compute maximum demand. TODO: This should come from catalog in the future
max_demand = self._compute_max_demand()
# compute TESCapacity
self._input_data["TESCapacity"] = self._input_data["HoursOfStorageAtMaxDemand"] * (max_demand * 3.6) / (
(self._input_data["Cp"] / 1000) * self._input_data["TemperatureDifference"])
def _update_input_data_with_coff(self, capacity_coff: List, comp_power_coff: List):
"""
Updates the user data with coefficients derived from imports
:param capacity_coff: heat or cooling capacity coefficients
:param comp_power_coff: heat or cooling comppressor power coefficients
:return:
"""
self._input_data["a1"] = capacity_coff[0]
self._input_data["a2"] = capacity_coff[1]
self._input_data["a3"] = capacity_coff[2]
self._input_data["a4"] = capacity_coff[3]
self._input_data["a5"] = capacity_coff[4]
self._input_data["a6"] = capacity_coff[5]
self._input_data["b1"] = comp_power_coff[0]
self._input_data["b2"] = comp_power_coff[1]
self._input_data["b3"] = comp_power_coff[2]
self._input_data["b4"] = comp_power_coff[3]
self._input_data["b5"] = comp_power_coff[4]
self._input_data["b6"] = comp_power_coff[5]
def _extract_model_coff(self, hp_model: str, data_type='heat') -> Union[Tuple[List, List], None]:
"""
Extracts heat pump coefficient data for a specific
model. e.g 012, 140
:param hp_model: the model type
:param data_type: indicates whether we're extracting cooling
or heating perfarmcn coefficients
:return:
"""
for energy_system in self._city.energy_systems:
if energy_system.heat_pump.model == hp_model:
if data_type == 'heat':
return energy_system.heat_pump.heating_capacity_coff, energy_system.heat_pump.heating_comp_power_coff
return energy_system.heat_pump.cooling_capacity_coff, energy_system.heat_pump.cooling_comp_power_coff
return None