hub/exports/energy_systems/heat_pump_export.py

254 lines
12 KiB
Python
Raw Normal View History

"""
HeatPumpExport exports heatpump outputs into several files after insel execution
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com
"""
import os
from typing import List, Union, Dict
2021-11-08 11:33:24 -05:00
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, output_path, template, demand_path=None, water_temp=None):
self._template_path = template
self._water_temp = water_temp
2021-11-08 11:33:24 -05:00
self._constants_path = (base_path / 'heat_pumps/constants.yaml')
# needed to compute max demand.
self._demand_path = (base_path / 'heat_pumps/demand.txt') if demand_path is None else demand_path
self._city = city
2021-11-08 11:33:24 -05:00
self._input_data = None
self._base_path = base_path
self._output_path = output_path
def _run_insel(self, user_input: Dict, capacity_coeff: List, filename: str) -> Union[Dict, None]:
"""
2021-11-08 11:33:24 -05:00
Runs insel and write the necessary files
:param user_input: a dictionary containing the user
values necessary to run insel
:param capacity_coeff: a list containing capacity coefficients
:param filename: the name of the insel file to be created
2021-11-08 11:33:24 -05:00
:return:
"""
2021-11-08 11:33:24 -05:00
self._input_data = user_input
2022-03-10 12:44:04 -05:00
self._update_input_data_with_coff(capacity_coeff)
2021-11-08 11:33:24 -05:00
# 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_content = insel_template_handler.read()
insel_template = Template(insel_template_content).substitute(self._input_data)
2021-11-08 11:33:24 -05:00
# create the insel file and write the template with substituted values into it
insel_file = (self._base_path / 'heat_pumps' / filename)
2021-11-08 11:33:24 -05:00
insel_file_handler = open(insel_file, "w")
insel_file_handler.write(insel_template)
# Now run insel
2021-11-10 11:58:01 -05:00
self._delete_existing_output_files()
2022-11-23 10:10:07 -05:00
os.system('insel {}'.format(insel_file))
# Writer headers to csv output files generated by insel
self._write_insel_output_headers()
2021-11-10 11:58:01 -05:00
# User output
return self._get_user_out_put()
2021-11-08 11:33:24 -05:00
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 = [
'Year', ' Month', ' Day', 'Hour', 'Minute', 'HP Heat Output (kW)', 'Heating Demand (kW)', 'HP output flow rate',
'Building Required Flow Rate', 'TES Charging Rate (kg/s)', 'Water Flow Rate After Splitter',
'water temperature after splitter', 'TES Discharging Rate (kg/s)', 'TES discharge temperature',
'Mixer Outlet Flow Rate (kg/s)', 'Mixer outlet temperature', 'Auxiliary heater fuel flow rate',
'Auxiliary heater energy input (kW)', 'Building Inlet Flow Rate (kg/s)', 'Building inlet temperature',
'Building return temperature', 'TES Return Flow Rate (kg/s)', 'TES return temperature',
'TES Bypass Line Flow Rate (kg/s)', 'TES bypass line temperature', 'Flow Rate from TES to mixer 2 (kg/s)',
'Temperature from Tes to mixer', 'HP Inlet Flow Rate (kg/s)', 'HP Inlet temperature', 'TES Node 1 Temperature',
'TES Node 2 Temperature', 'TES Node 3 Temperature', 'TES Node 4 Temperature', 'TES Energy Content (J)',
'HP Electricity Consumption (kW)', 'HP COP', 'Ambient Temperature', 'HP Operational Cost (CAD)',
'Auxiliary Heater Operational Cost (CAD)', 'Operational CO2 Emissions of HP (g)',
'Operational CO2 Emissions of Auxiliary Heater (g)']
if 'series' in str(self._template_path):
header = [
'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)']
header_data = {
self._input_data['fileOut1']: header,
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("'")
2021-11-10 11:58:01 -05:00
df = pd.read_csv(file_path, header=None, sep='\s+')
# ignore ambient temperature for air source series run
if df.shape[1] > 25 and 'series' in str(self._template_path):
df.drop(columns=df.columns[-1],
axis=1,
inplace=True)
df.to_csv(file_path, header=header)
2021-11-08 11:33:24 -05:00
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'))}'"
# include water temperature for water to water heat pump
if self._water_temp is not None:
self._input_data['WaterTemperature'] = f"'{str(self._water_temp)}'"
2021-11-10 11:58:01 -05:00
def _delete_existing_output_files(self):
"""
Remove existing out files generated by insel before
running insel
:return:
"""
for key, file_path in self._input_data.items():
if 'fileOut' in key:
file_path = file_path.strip("'")
try:
os.remove(file_path)
except OSError:
pass
2021-11-08 11:33:24 -05:00
def _compute_max_demand(self):
"""
2021-11-08 11:33:24 -05:00
Retrieves the maximum demand value from
the demands text file
:return: float
"""
2021-11-08 11:33:24 -05:00
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
2021-11-08 11:33:24 -05:00
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():
if key in ['LowestPossibleLoadFlow', 'HighestPossibleLoadFlow'] and self._water_temp is None:
continue
2021-11-08 11:33:24 -05:00
self._input_data[key] = value
# compute water to water HP specific values
2022-03-10 12:44:04 -05:00
if 55 <= self._input_data['HPSupTemp'] <= 60:
self._input_data["HPDisactivationTemperature"] = self._input_data["HPSupTemp"] - 5
self._input_data["HPReactivationTemperature"] = self._input_data["HPSupTemp"] - 18
2022-03-10 12:44:04 -05:00
elif 50 <= self._input_data["HPSupTemp"] < 55:
self._input_data["HPDisactivationTemperature"] = self._input_data["HPSupTemp"] - 5
self._input_data["HPReactivationTemperature"] = self._input_data["HPSupTemp"] - 13
elif 45 <= self._input_data["HPSupTemp"] < 50:
self._input_data["HPDisactivationTemperature"] = self._input_data["HPSupTemp"] - 3
self._input_data["HPReactivationTemperature"] = self._input_data["HPSupTemp"] - 8
elif 35 <= self._input_data["HPSupTemp"] < 40:
self._input_data["HPDisactivationTemperature"] = self._input_data["HPSupTemp"] - 2
self._input_data["HPReactivationTemperature"] = self._input_data["HPSupTemp"] - 4
2021-11-08 11:33:24 -05:00
# 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"])
2022-03-10 12:44:04 -05:00
def _update_input_data_with_coff(self, a_coeff: List):
"""
2021-11-08 11:33:24 -05:00
Updates the user data with coefficients derived from imports
:param a_coeff: insel a coefficient values
2022-03-10 12:44:04 -05:00
Meaning of a is in the models for air source heat pump
and water to water source heat pump
:return:
"""
self._input_data["a1"] = a_coeff[0]
self._input_data["a2"] = a_coeff[1]
self._input_data["a3"] = a_coeff[2]
self._input_data["a4"] = a_coeff[3]
self._input_data["a5"] = a_coeff[4]
self._input_data["a6"] = a_coeff[5]
# additional coefficients for water to water source
if self._water_temp is not None:
self._input_data["a7"] = a_coeff[6]
self._input_data["a8"] = a_coeff[7]
self._input_data["a9"] = a_coeff[8]
self._input_data["a10"] = a_coeff[9]
self._input_data["a11"] = a_coeff[10]
2021-11-10 11:58:01 -05:00
def _get_user_out_put(self) -> Union[Dict, None]:
2021-11-10 11:58:01 -05:00
"""
Extracts monthly electricity demand and fossil fuel consumption
from output files generated by insel
:return: Dict for json output
2021-11-10 11:58:01 -05:00
"""
2022-11-22 20:20:12 -05:00
monthly_electricity_df = pd.read_csv(self._input_data['fileOut8'].strip("'")).iloc[:, 2]
monthly_fossil_df = pd.read_csv(self._input_data['fileOut4'].strip("'")).iloc[:, 2]
2021-11-10 11:58:01 -05:00
if self._output_path is None:
return {
2022-11-22 20:20:12 -05:00
'hourly_electricity_demand': pd.read_csv(self._input_data['fileOut10'].strip("'")).iloc[:, 5].tolist(),
'monthly_electricity_demand': monthly_electricity_df.tolist(),
'daily_electricity_demand': pd.read_csv(self._input_data['fileOut6'].strip("'")).iloc[:, 2].tolist(),
'daily_fossil_consumption': pd.read_csv(self._input_data['fileOut9'].strip("'")).iloc[:, 2].tolist(),
'monthly_fossil_consumption': monthly_fossil_df.tolist()
}
2021-11-10 11:58:01 -05:00
2022-11-22 20:20:12 -05:00
data = [monthly_electricity_df, monthly_fossil_df]
2021-11-10 11:58:01 -05:00
df = pd.concat(data, axis=1)
2022-02-17 13:56:42 -05:00
df = pd.concat([df, df.agg(['sum'])])
2021-11-10 11:58:01 -05:00
s = pd.Series(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec", "Total"])
df = df.set_index([s])
df.to_csv(self._output_path)