fix: current and retrofitted status energy consumption analysis and system schematic added to the report

This commit is contained in:
Saeed Ranjbar 2024-07-15 08:51:15 -04:00
parent 7369bc65a4
commit c4f98a30c1
16 changed files with 1486 additions and 219 deletions

View File

@ -10,7 +10,7 @@ class EmissionSystem:
"""
Emission system class
"""
def __init__(self, system_id, model_name=None, system_type=None, parasitic_energy_consumption=None):
def __init__(self, system_id, model_name=None, system_type=None, parasitic_energy_consumption=0):
self._system_id = system_id
self._model_name = model_name

View File

@ -135,7 +135,7 @@ class MontrealCustomCatalog(Catalog):
equipment_id = float(equipment['@id'])
equipment_type = equipment['@type']
model_name = equipment['name']
parasitic_consumption = None
parasitic_consumption = 0
if 'parasitic_consumption' in equipment:
parasitic_consumption = float(equipment['parasitic_consumption']['#text']) / 100

View File

@ -262,7 +262,7 @@ class MontrealFutureSystemCatalogue(Catalog):
system_id = None
model_name = None
system_type = None
parasitic_energy_consumption = None
parasitic_energy_consumption = 0
emission_system = EmissionSystem(system_id=system_id,
model_name=model_name,
system_type=system_type,

View File

@ -13,7 +13,7 @@ class EmissionSystem:
def __init__(self):
self._model_name = None
self._type = None
self._parasitic_energy_consumption = None
self._parasitic_energy_consumption = 0
@property
def model_name(self):

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -136,10 +136,14 @@ class MontrealCustomEnergySystemParameters:
_distribution_system.distribution_consumption_variable_flow = \
archetype_distribution_system.distribution_consumption_variable_flow
_distribution_system.heat_losses = archetype_distribution_system.heat_losses
_emission_system = None
_generic_emission_system = None
if archetype_distribution_system.emission_systems is not None:
_emission_system = EmissionSystem()
_distribution_system.emission_systems = [_emission_system]
_emission_systems = []
for emission_system in archetype_distribution_system.emission_systems:
_generic_emission_system = EmissionSystem()
_generic_emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption
_emission_systems.append(_generic_emission_system)
_distribution_system.emission_systems = _emission_systems
_distribution_systems.append(_distribution_system)
return _distribution_systems

View File

@ -185,10 +185,14 @@ class MontrealFutureEnergySystemParameters:
_distribution_system.distribution_consumption_variable_flow = \
archetype_distribution_system.distribution_consumption_variable_flow
_distribution_system.heat_losses = archetype_distribution_system.heat_losses
_emission_system = None
_generic_emission_system = None
if archetype_distribution_system.emission_systems is not None:
_emission_system = EmissionSystem()
_distribution_system.emission_systems = [_emission_system]
_emission_systems = []
for emission_system in archetype_distribution_system.emission_systems:
_generic_emission_system = EmissionSystem()
_generic_emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption
_emission_systems.append(_generic_emission_system)
_distribution_system.emission_systems = _emission_systems
_distribution_systems.append(_distribution_system)
return _distribution_systems

View File

@ -0,0 +1,863 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56769087843276,
45.49251875903776
],
[
-73.56765050367694,
45.492560280202284
],
[
-73.5677794213865,
45.49262188364245
],
[
-73.56781916241786,
45.49258006136105
],
[
-73.56769087843276,
45.49251875903776
]
]
]
},
"id": 173347,
"properties": {
"name": "01044617",
"address": "rue Victor-Hugo (MTL) 1666",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56765050367694,
45.492560280202284
],
[
-73.56761436875776,
45.49259744179384
],
[
-73.5676075694645,
45.49260454199484
],
[
-73.56773226889548,
45.49266394156485
],
[
-73.56773726906921,
45.49266624130272
],
[
-73.5677794213865,
45.49262188364245
],
[
-73.56765050367694,
45.492560280202284
]
]
]
},
"id": 173348,
"properties": {
"name": "01044619",
"address": "rue Victor-Hugo (MTL) 1670",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56829026835214,
45.492524742569145
],
[
-73.56849646900322,
45.49262354174874
],
[
-73.56861067001111,
45.492505541343576
],
[
-73.56864076915663,
45.492519941474434
],
[
-73.56866246900178,
45.49249754209202
],
[
-73.56867696946317,
45.49250454136644
],
[
-73.56867726964143,
45.49250414255471
],
[
-73.56881486931461,
45.492362042624144
],
[
-73.56881686903772,
45.492359941181455
],
[
-73.5688004699483,
45.49235084193039
],
[
-73.56882097012145,
45.4923320417195
],
[
-73.56879846891101,
45.49232034109352
],
[
-73.56883736970825,
45.492284841271946
],
[
-73.56886806888434,
45.492256240993704
],
[
-73.56885337003277,
45.49224914198001
],
[
-73.56890226932418,
45.49219894164121
],
[
-73.56851866897392,
45.49201434154299
],
[
-73.56837326884313,
45.492163841620254
],
[
-73.56864696910176,
45.49229554163243
],
[
-73.5685268682051,
45.49241904187041
],
[
-73.56825396962694,
45.49228824183907
],
[
-73.56810906858335,
45.49243794104013
],
[
-73.56829026835214,
45.492524742569145
]
]
]
},
"id": 173403,
"properties": {
"name": "01044334",
"address": "rue Saint-Jacques (MTL) 1460",
"function": "1000",
"height": 15,
"year_of_construction": 1985
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.5683896684674,
45.491800342137736
],
[
-73.56838616878639,
45.49180414157881
],
[
-73.56850686988925,
45.49185994152571
],
[
-73.56851286844197,
45.4918626410622
],
[
-73.56855549071014,
45.49181750806087
],
[
-73.56842962331187,
45.49175738300567
],
[
-73.5683896684674,
45.491800342137736
]
]
]
},
"id": 174898,
"properties": {
"name": "01044590",
"address": "rue Victor-Hugo (MTL) 1600",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.5680637695714,
45.49212884162544
],
[
-73.56802228176146,
45.49217205619571
],
[
-73.56815668696326,
45.49223626189717
],
[
-73.56815766959974,
45.49223524178655
],
[
-73.56818746886172,
45.49224944155107
],
[
-73.56822816806918,
45.49220694186927
],
[
-73.5680637695714,
45.49212884162544
]
]
]
},
"id": 175785,
"properties": {
"name": "01044602",
"address": "rue Victor-Hugo (MTL) 1630",
"function": "1000",
"height": 12,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56850793693103,
45.49167318076048
],
[
-73.56846877951091,
45.4917152818903
],
[
-73.56859506290321,
45.491775605518725
],
[
-73.56863463503653,
45.491733702062774
],
[
-73.56850793693103,
45.49167318076048
]
]
]
},
"id": 175910,
"properties": {
"name": "01044586",
"address": "rue Victor-Hugo (MTL) 1590",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56817543449134,
45.49201384773851
],
[
-73.56813497596143,
45.49205532773507
],
[
-73.56826745951075,
45.492118613912375
],
[
-73.56830763251781,
45.49207699906335
],
[
-73.56817543449134,
45.49201384773851
]
]
]
},
"id": 176056,
"properties": {
"name": "01044599",
"address": "rue Victor-Hugo (MTL) 1620",
"function": "1000",
"height": 8,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56772876855176,
45.49247194194522
],
[
-73.56773406949068,
45.492474341387755
],
[
-73.56773125185198,
45.492477239659124
],
[
-73.56785890467093,
45.492538239964624
],
[
-73.56789966910456,
45.49249534173201
],
[
-73.56776616865103,
45.49243264153464
],
[
-73.56772876855176,
45.49247194194522
]
]
]
},
"id": 176261,
"properties": {
"name": "01044613",
"address": "rue Victor-Hugo (MTL) 1656",
"function": "1000",
"height": 10,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56802228176146,
45.49217205619571
],
[
-73.56798225825526,
45.492213743742184
],
[
-73.56811660206223,
45.49227791893211
],
[
-73.56815668696326,
45.49223626189717
],
[
-73.56802228176146,
45.49217205619571
]
]
]
},
"id": 176293,
"properties": {
"name": "01044604",
"address": "rue Victor-Hugo (MTL) 1636",
"function": "1000",
"height": 12,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56790222258577,
45.49229712328457
],
[
-73.56785996900595,
45.49234104192853
],
[
-73.56799446861396,
45.49240484193282
],
[
-73.56803643080562,
45.49236123475947
],
[
-73.56790222258577,
45.49229712328457
]
]
]
},
"id": 176296,
"properties": {
"name": "01044611",
"address": "rue Victor-Hugo (MTL) 1650",
"function": "1000",
"height": 10,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56798225825526,
45.492213743742184
],
[
-73.56794223597048,
45.4922554321734
],
[
-73.56807651582375,
45.49231957685336
],
[
-73.56811660206223,
45.49227791893211
],
[
-73.56798225825526,
45.492213743742184
]
]
]
},
"id": 176298,
"properties": {
"name": "01044607",
"address": "rue Victor-Hugo (MTL) 1640",
"function": "1000",
"height": 12,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56742736898599,
45.49184704208998
],
[
-73.56761256873325,
45.491896142437554
],
[
-73.56766926915839,
45.4917902412014
],
[
-73.56766956853903,
45.49179024192391
],
[
-73.56792966911675,
45.49183254222432
],
[
-73.56793006788594,
45.491831141828406
],
[
-73.56794526884076,
45.49174634219527
],
[
-73.56794516904765,
45.49174634225465
],
[
-73.56753896905731,
45.491638642248425
],
[
-73.56742736898599,
45.49184704208998
]
]
]
},
"id": 176918,
"properties": {
"name": "01097185",
"address": "rue Victor-Hugo (MTL) 1591",
"function": "1000",
"height": 10,
"year_of_construction": 1987
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56773125185198,
45.492477239659124
],
[
-73.56769087843276,
45.49251875903776
],
[
-73.56781916241786,
45.49258006136105
],
[
-73.56785890467093,
45.492538239964624
],
[
-73.56773125185198,
45.492477239659124
]
]
]
},
"id": 178164,
"properties": {
"name": "01044615",
"address": "rue Victor-Hugo (MTL) 1660",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56846877951091,
45.4917152818903
],
[
-73.56842962331187,
45.49175738300567
],
[
-73.56855549071014,
45.49181750806087
],
[
-73.56859506290321,
45.491775605518725
],
[
-73.56846877951091,
45.4917152818903
]
]
]
},
"id": 179679,
"properties": {
"name": "01044588",
"address": "rue Victor-Hugo (MTL) 1596",
"function": "1000",
"height": 9,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56825635009473,
45.49193088860213
],
[
-73.56821589168355,
45.491972368627906
],
[
-73.5683477837006,
45.4920353716151
],
[
-73.56838787594006,
45.49199371809223
],
[
-73.56825635009473,
45.49193088860213
]
]
]
},
"id": 179789,
"properties": {
"name": "01044595",
"address": "rue Victor-Hugo (MTL) 1610",
"function": "1000",
"height": 8,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56821589168355,
45.491972368627906
],
[
-73.56817543449134,
45.49201384773851
],
[
-73.56830763251781,
45.49207699906335
],
[
-73.5683477837006,
45.4920353716151
],
[
-73.56821589168355,
45.491972368627906
]
]
]
},
"id": 181310,
"properties": {
"name": "01044597",
"address": "rue Victor-Hugo (MTL) 1616",
"function": "1000",
"height": 8,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56809506939487,
45.49209624228538
],
[
-73.56809246893268,
45.4920988416879
],
[
-73.56821287000538,
45.49216124158406
],
[
-73.56822186852654,
45.49216584161625
],
[
-73.56826745951075,
45.492118613912375
],
[
-73.56813497596143,
45.49205532773507
],
[
-73.56809506939487,
45.49209624228538
]
]
]
},
"id": 182393,
"properties": {
"name": "01044601",
"address": "rue Victor-Hugo (MTL) 1626",
"function": "1000",
"height": 8,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56790756893894,
45.492291541967774
],
[
-73.56790222258577,
45.49229712328457
],
[
-73.56803643080562,
45.49236123475947
],
[
-73.56807651582375,
45.49231957685336
],
[
-73.56794223597048,
45.4922554321734
],
[
-73.56790756893894,
45.492291541967774
]
]
]
},
"id": 182442,
"properties": {
"name": "01044609",
"address": "rue Victor-Hugo (MTL) 1646",
"function": "1000",
"height": 11,
"year_of_construction": 1986
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-73.56829706912258,
45.49188914205178
],
[
-73.56825635009473,
45.49193088860213
],
[
-73.56838787594006,
45.49199371809223
],
[
-73.56842846901456,
45.49195154234486
],
[
-73.56829706912258,
45.49188914205178
]
]
]
},
"id": 182546,
"properties": {
"name": "01044592",
"address": "rue Victor-Hugo (MTL) 1606",
"function": "1000",
"height": 8,
"year_of_construction": 1986
}
}
]
}

View File

@ -12,17 +12,22 @@ from scripts.geojson_creator import process_geojson
from scripts import random_assignation
from hub.imports.energy_systems_factory import EnergySystemsFactory
from scripts.energy_system_sizing import SystemSizing
from scripts.energy_system_retrofit_results import system_results, new_system_results
from scripts.solar_angles import CitySolarAngles
from scripts.pv_sizing_and_simulation import PVSizingSimulation
from scripts.energy_system_retrofit_results import consumption_data
from scripts.energy_system_sizing_and_simulation_factory import EnergySystemsSimulationFactory
from scripts.costs.cost import Cost
from scripts.costs.constants import SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV
import hub.helpers.constants as cte
from hub.exports.exports_factory import ExportsFactory
from scripts.pv_feasibility import pv_feasibility
geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001)
file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson')
output_path = (Path(__file__).parent / 'out_files').resolve()
city = GeometryFactory('geojson',
simulation_results_path = (Path(__file__).parent / 'out_files' / 'simulation_results').resolve()
simulation_results_path.mkdir(parents=True, exist_ok=True)
city = GeometryFactory(file_type='geojson',
path=file_path,
height_field='height',
year_of_construction_field='year_of_construction',
@ -35,7 +40,32 @@ ExportsFactory('sra', city, output_path).export()
sra_path = (output_path / f'{city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', city, output_path).enrich()
pv_feasibility(-73.5681295982132, 45.49218262677643, 0.0001, selected_buildings=city.buildings)
energy_plus_workflow(city)
solar_angles = CitySolarAngles(city.name,
city.latitude,
city.longitude,
tilt_angle=45,
surface_azimuth_angle=180).calculate
random_assignation.call_random(city.buildings, random_assignation.residential_systems_percentage)
EnergySystemsFactory('montreal_custom', city).enrich()
SystemSizing(city.buildings).montreal_custom()
current_status_energy_consumption = consumption_data(city)
random_assignation.call_random(city.buildings, random_assignation.residential_new_systems_percentage)
EnergySystemsFactory('montreal_future', city).enrich()
for building in city.buildings:
if 'PV' in building.energy_systems_archetype_name:
ghi = [x / cte.WATTS_HOUR_TO_JULES for x in building.roofs[0].global_irradiance[cte.HOUR]]
pv_sizing_simulation = PVSizingSimulation(building,
solar_angles,
tilt_angle=45,
module_height=1,
module_width=2,
ghi=ghi)
pv_sizing_simulation.pv_output()
if building.energy_systems_archetype_name == 'PV+4Pipe+DHW':
EnergySystemsSimulationFactory('archetype13', building=building, output_path=simulation_results_path).enrich()
retrofitted_energy_consumption = consumption_data(city)
(EnergySystemRetrofitReport(city, output_path, 'PV Implementation and System Retrofit',
current_status_energy_consumption, retrofitted_energy_consumption).create_report())
(EnergySystemRetrofitReport(city, output_path, 'PV Implementation and HVAC Retrofit').
create_report(current_system=None, new_system=None))

View File

@ -9,24 +9,36 @@ import matplotlib as mpl
from matplotlib.ticker import MaxNLocator
import numpy as np
from pathlib import Path
import glob
class EnergySystemRetrofitReport:
def __init__(self, city, output_path, retrofit_scenario):
def __init__(self, city, output_path, retrofit_scenario, current_status_energy_consumption_data,
retrofitted_energy_consumption_data):
self.city = city
self.current_status_data = current_status_energy_consumption_data
self.retrofitted_data = retrofitted_energy_consumption_data
self.output_path = output_path
self.content = []
self.retrofit_scenario = retrofit_scenario
self.report = LatexReport('energy_system_retrofit_report',
'Energy System Retrofit Report', retrofit_scenario, output_path)
'Energy System Retrofit Report', self.retrofit_scenario, output_path)
self.system_schemas_path = (Path(__file__).parent.parent / 'hub' / 'data' / 'energy_systems' / 'schemas')
self.charts_path = Path(output_path) / 'charts'
self.charts_path.mkdir(parents=True, exist_ok=True)
files = glob.glob(f'{self.charts_path}/*')
for file in files:
os.remove(file)
def building_energy_info(self):
table_data = [
["Building Name", "Year of Construction", "function", "Yearly Heating Demand (MWh)",
"Yearly Cooling Demand (MWh)", "Yearly DHW Demand (MWh)", "Yearly Electricity Demand (MWh)"]
]
intensity_table_data = [["Building Name", "Total Floor Area m2", "Heating Demand Intensity kWh/m2",
"Cooling Demand Intensity kWh/m2", "Electricity Intensity kWh/m2"]]
intensity_table_data = [["Building Name", "Total Floor Area $m^2$", "Heating Demand Intensity kWh/ $m^2$",
"Cooling Demand Intensity kWh/ $m^2$", "Electricity Intensity kWh/ $m^2$"]]
peak_load_data = [["Building Name", "Heating Peak Load (kW)", "Cooling Peak Load (kW)",
"Domestic Hot Water Peak Load (kW)"]]
for building in self.city.buildings:
total_floor_area = 0
@ -52,128 +64,68 @@ class EnergySystemRetrofitReport:
(building.lighting_electrical_demand[cte.YEAR][0] + building.appliances_electrical_demand[cte.YEAR][0]) /
(3.6e6 * total_floor_area), '.2f'))
]
peak_data = [
building.name,
str(format(building.heating_peak_load[cte.YEAR][0] / 1000, '.2f')),
str(format(building.cooling_peak_load[cte.YEAR][0] / 1000, '.2f')),
str(format(
(building.lighting_electrical_demand[cte.YEAR][0] + building.appliances_electrical_demand[cte.YEAR][0]) /
(3.6e6 * total_floor_area), '.2f'))
]
table_data.append(building_data)
intensity_table_data.append(intensity_data)
peak_load_data.append(peak_data)
self.report.add_table(table_data, caption='Buildings Energy Consumption Data')
self.report.add_table(intensity_table_data, caption='Buildings Energy Use Intensity Data')
self.report.add_table(peak_load_data, caption='Buildings Peak Load Data')
def monthly_demands(self):
heating = []
cooling = []
dhw = []
lighting_appliance = []
for i in range(12):
heating_demand = 0
cooling_demand = 0
dhw_demand = 0
lighting_appliance_demand = 0
for building in self.city.buildings:
heating_demand += building.heating_demand[cte.MONTH][i] / 3.6e6
cooling_demand += building.cooling_demand[cte.MONTH][i] / 3.6e6
dhw_demand += building.domestic_hot_water_heat_demand[cte.MONTH][i] / 3.6e6
lighting_appliance_demand += building.lighting_electrical_demand[cte.MONTH][i] / 3.6e6
heating.append(heating_demand)
cooling.append(cooling_demand)
dhw.append(dhw_demand)
lighting_appliance.append(lighting_appliance_demand)
monthly_demands = {'heating': heating,
'cooling': cooling,
'dhw': dhw,
'lighting_appliance': lighting_appliance}
return monthly_demands
def plot_monthly_energy_demands(self, demands, file_name):
def plot_monthly_energy_demands(self, data, file_name, title):
# Data preparation
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
heating = demands['heating']
cooling = demands['cooling']
dhw = demands['dhw']
electricity = demands['lighting_appliance']
demands = {
'Heating': ('heating', '#2196f3'),
'Cooling': ('cooling', '#ff5a5f'),
'DHW': ('dhw', '#4caf50'),
'Electricity': ('lighting_appliance', '#ffc107')
}
# Helper function for plotting
def plot_bar_chart(ax, demand_type, color, ylabel, title):
values = data[demand_type]
ax.bar(months, values, color=color, width=0.6, zorder=2)
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
ax.set_xlabel('Month', fontsize=12, labelpad=10)
ax.set_ylabel(ylabel, fontsize=14, labelpad=10)
ax.set_title(title, fontsize=14, weight='bold', alpha=.8, pad=40)
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.set_xticks(np.arange(len(months)))
ax.set_xticklabels(months, rotation=45, ha='right')
ax.bar_label(ax.containers[0], padding=3, color='black', fontsize=12, rotation=90)
ax.spines[['top', 'left', 'bottom']].set_visible(False)
ax.spines['right'].set_linewidth(1.1)
average_value = np.mean(values)
ax.axhline(y=average_value, color='grey', linewidth=2, linestyle='--')
ax.text(len(months) - 1, average_value, f'Average = {average_value:.1f} kWh', ha='right', va='bottom',
color='grey')
# Plotting
fig, axs = plt.subplots(2, 2, figsize=(15, 10), dpi=96)
fig.suptitle('Monthly Energy Demands', fontsize=16, weight='bold', alpha=.8)
fig, axs = plt.subplots(4, 1, figsize=(20, 16), dpi=96)
fig.suptitle(title, fontsize=16, weight='bold', alpha=.8)
# Heating bar chart
axs[0, 0].bar(months, heating, color='#2196f3', width=0.6, zorder=2)
axs[0, 0].grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
axs[0, 0].grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
axs[0, 0].set_xlabel('Month', fontsize=12, labelpad=10)
axs[0, 0].set_ylabel('Heating Demand (kWh)', fontsize=12, labelpad=10)
axs[0, 0].set_title('Monthly Heating Demands', fontsize=14, weight='bold', alpha=.8)
axs[0, 0].xaxis.set_major_locator(MaxNLocator(integer=True))
axs[0, 0].yaxis.set_major_locator(MaxNLocator(integer=True))
axs[0, 0].set_xticks(np.arange(len(months)))
axs[0, 0].set_xticklabels(months, rotation=45, ha='right')
axs[0, 0].bar_label(axs[0, 0].containers[0], padding=3, color='black', fontsize=8)
axs[0, 0].spines[['top', 'left', 'bottom']].set_visible(False)
axs[0, 0].spines['right'].set_linewidth(1.1)
average_heating = np.mean(heating)
axs[0, 0].axhline(y=average_heating, color='grey', linewidth=2, linestyle='--')
axs[0, 0].text(len(months)-1, average_heating, f'Average = {average_heating:.1f} kWh', ha='right', va='bottom', color='grey')
# Cooling bar chart
axs[0, 1].bar(months, cooling, color='#ff5a5f', width=0.6, zorder=2)
axs[0, 1].grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
axs[0, 1].grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
axs[0, 1].set_xlabel('Month', fontsize=12, labelpad=10)
axs[0, 1].set_ylabel('Cooling Demand (kWh)', fontsize=12, labelpad=10)
axs[0, 1].set_title('Monthly Cooling Demands', fontsize=14, weight='bold', alpha=.8)
axs[0, 1].xaxis.set_major_locator(MaxNLocator(integer=True))
axs[0, 1].yaxis.set_major_locator(MaxNLocator(integer=True))
axs[0, 1].set_xticks(np.arange(len(months)))
axs[0, 1].set_xticklabels(months, rotation=45, ha='right')
axs[0, 1].bar_label(axs[0, 1].containers[0], padding=3, color='black', fontsize=8)
axs[0, 1].spines[['top', 'left', 'bottom']].set_visible(False)
axs[0, 1].spines['right'].set_linewidth(1.1)
average_cooling = np.mean(cooling)
axs[0, 1].axhline(y=average_cooling, color='grey', linewidth=2, linestyle='--')
axs[0, 1].text(len(months)-1, average_cooling, f'Average = {average_cooling:.1f} kWh', ha='right', va='bottom', color='grey')
# DHW bar chart
axs[1, 0].bar(months, dhw, color='#4caf50', width=0.6, zorder=2)
axs[1, 0].grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
axs[1, 0].grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
axs[1, 0].set_xlabel('Month', fontsize=12, labelpad=10)
axs[1, 0].set_ylabel('DHW Demand (kWh)', fontsize=12, labelpad=10)
axs[1, 0].set_title('Monthly DHW Demands', fontsize=14, weight='bold', alpha=.8)
axs[1, 0].xaxis.set_major_locator(MaxNLocator(integer=True))
axs[1, 0].yaxis.set_major_locator(MaxNLocator(integer=True))
axs[1, 0].set_xticks(np.arange(len(months)))
axs[1, 0].set_xticklabels(months, rotation=45, ha='right')
axs[1, 0].bar_label(axs[1, 0].containers[0], padding=3, color='black', fontsize=8)
axs[1, 0].spines[['top', 'left', 'bottom']].set_visible(False)
axs[1, 0].spines['right'].set_linewidth(1.1)
average_dhw = np.mean(dhw)
axs[1, 0].axhline(y=average_dhw, color='grey', linewidth=2, linestyle='--')
axs[1, 0].text(len(months)-1, average_dhw, f'Average = {average_dhw:.1f} kWh', ha='right', va='bottom', color='grey')
# Electricity bar chart
axs[1, 1].bar(months, electricity, color='#ffc107', width=0.6, zorder=2)
axs[1, 1].grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
axs[1, 1].grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
axs[1, 1].set_xlabel('Month', fontsize=12, labelpad=10)
axs[1, 1].set_ylabel('Electricity Demand (kWh)', fontsize=12, labelpad=10)
axs[1, 1].set_title('Monthly Electricity Demands', fontsize=14, weight='bold', alpha=.8)
axs[1, 1].xaxis.set_major_locator(MaxNLocator(integer=True))
axs[1, 1].yaxis.set_major_locator(MaxNLocator(integer=True))
axs[1, 1].set_xticks(np.arange(len(months)))
axs[1, 1].set_xticklabels(months, rotation=45, ha='right')
axs[1, 1].bar_label(axs[1, 1].containers[0], padding=3, color='black', fontsize=8)
axs[1, 1].spines[['top', 'left', 'bottom']].set_visible(False)
axs[1, 1].spines['right'].set_linewidth(1.1)
average_electricity = np.mean(electricity)
axs[1, 1].axhline(y=average_electricity, color='grey', linewidth=2, linestyle='--')
axs[1, 1].text(len(months)-1, average_electricity * 0.95, f'Average = {average_electricity:.1f} kWh', ha='right', va='top', color='grey')
plot_bar_chart(axs[0], 'heating', demands['Heating'][1], 'Heating Demand (kWh)', 'Monthly Heating Demand')
plot_bar_chart(axs[1], 'cooling', demands['Cooling'][1], 'Cooling Demand (kWh)', 'Monthly Cooling Demand')
plot_bar_chart(axs[2], 'dhw', demands['DHW'][1], 'DHW Demand (kWh)', 'Monthly DHW Demand')
plot_bar_chart(axs[3], 'lighting_appliance', demands['Electricity'][1], 'Electricity Demand (kWh)',
'Monthly Electricity Demand')
# Set a white background
fig.patch.set_facecolor('white')
# Adjust the margins around the plot area
plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.1, wspace=0.3, hspace=0.5)
plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.1, hspace=0.5)
# Save the plot
chart_path = self.charts_path / f'{file_name}.png'
@ -182,15 +134,360 @@ class EnergySystemRetrofitReport:
return chart_path
def create_report(self, current_system, new_system):
os.chdir(self.charts_path)
self.report.add_section('Current Status')
self.report.add_subsection('City Buildings Characteristics')
def plot_monthly_energy_consumption(self, data, file_name, title):
# Data preparation
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
consumptions = {
'Heating': ('heating', '#2196f3', 'Heating Consumption (kWh)', 'Monthly Energy Consumption for Heating'),
'Cooling': ('cooling', '#ff5a5f', 'Cooling Consumption (kWh)', 'Monthly Energy Consumption for Cooling'),
'DHW': ('dhw', '#4caf50', 'DHW Consumption (kWh)', 'Monthly DHW Consumption')
}
# Helper function for plotting
def plot_bar_chart(ax, consumption_type, color, ylabel, title):
values = data[consumption_type]
ax.bar(months, values, color=color, width=0.6, zorder=2)
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
ax.set_xlabel('Month', fontsize=12, labelpad=10)
ax.set_ylabel(ylabel, fontsize=14, labelpad=10)
ax.set_title(title, fontsize=14, weight='bold', alpha=.8, pad=40)
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.set_xticks(np.arange(len(months)))
ax.set_xticklabels(months, rotation=45, ha='right')
ax.bar_label(ax.containers[0], padding=3, color='black', fontsize=12, rotation=90)
ax.spines[['top', 'left', 'bottom']].set_visible(False)
ax.spines['right'].set_linewidth(1.1)
average_value = np.mean(values)
ax.axhline(y=average_value, color='grey', linewidth=2, linestyle='--')
ax.text(len(months) - 1, average_value, f'Average = {average_value:.1f} kWh', ha='right', va='bottom',
color='grey')
# Plotting
fig, axs = plt.subplots(3, 1, figsize=(20, 15), dpi=96)
fig.suptitle(title, fontsize=16, weight='bold', alpha=.8)
plot_bar_chart(axs[0], 'heating', consumptions['Heating'][1], consumptions['Heating'][2],
consumptions['Heating'][3])
plot_bar_chart(axs[1], 'cooling', consumptions['Cooling'][1], consumptions['Cooling'][2],
consumptions['Cooling'][3])
plot_bar_chart(axs[2], 'dhw', consumptions['DHW'][1], consumptions['DHW'][2], consumptions['DHW'][3])
# Set a white background
fig.patch.set_facecolor('white')
# Adjust the margins around the plot area
plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.1, wspace=0.3, hspace=0.5)
# Save the plot
chart_path = self.charts_path / f'{file_name}.png'
plt.savefig(chart_path, bbox_inches='tight')
plt.close()
return chart_path
def fuel_consumption_breakdown(self, file_name, data):
fuel_consumption_breakdown = {}
for building in self.city.buildings:
for key, breakdown in data[f'{building.name}']['energy_consumption_breakdown'].items():
if key not in fuel_consumption_breakdown:
fuel_consumption_breakdown[key] = {sector: 0 for sector in breakdown}
for sector, value in breakdown.items():
if sector in fuel_consumption_breakdown[key]:
fuel_consumption_breakdown[key][sector] += value / 3.6e6
else:
fuel_consumption_breakdown[key][sector] = value / 3.6e6
plt.style.use('ggplot')
num_keys = len(fuel_consumption_breakdown)
fig, axs = plt.subplots(1 if num_keys <= 2 else num_keys, min(num_keys, 2), figsize=(12, 5))
axs = axs if num_keys > 1 else [axs] # Ensure axs is always iterable
for i, (fuel, breakdown) in enumerate(fuel_consumption_breakdown.items()):
labels = breakdown.keys()
values = breakdown.values()
colors = cm.get_cmap('tab20c', len(labels))
ax = axs[i] if num_keys > 1 else axs[0]
ax.pie(values, labels=labels,
autopct=lambda pct: f"{pct:.1f}%\n({pct / 100 * sum(values):.2f})",
startangle=90, colors=[colors(j) for j in range(len(labels))])
ax.set_title(f'{fuel} Consumption Breakdown')
plt.suptitle('City Energy Consumption Breakdown', fontsize=16, fontweight='bold')
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to fit the suptitle
chart_path = self.charts_path / f'{file_name}.png'
plt.savefig(chart_path, dpi=300)
plt.close()
return chart_path
def energy_system_archetype_schematic(self):
energy_system_archetypes = {}
for building in self.city.buildings:
if building.energy_systems_archetype_name not in energy_system_archetypes:
energy_system_archetypes[building.energy_systems_archetype_name] = [building.name]
else:
energy_system_archetypes[building.energy_systems_archetype_name].append(building.name)
text = ""
items = ""
for archetype, buildings in energy_system_archetypes.items():
buildings_str = ", ".join(buildings)
text += f"Figure 4 shows the schematic of the proposed energy system for buildings {buildings_str}.\n"
if archetype in ['PV+4Pipe+DHW', 'PV+ASHP+GasBoiler+TES']:
text += "This energy system archetype is formed of the following systems: \par"
items = ['Rooftop Photovoltaic System: The rooftop PV system is tied to the grid and in case there is surplus '
'energy, it is sold to Hydro-Quebec through their Net-Meterin program.',
'4-Pipe HVAC System: This systems includes a 4-pipe heat pump capable of generating heat and cooling '
'at the same time, a natural gas boiler as the auxiliary heating system, and a hot water storage tank.'
'The temperature inside the tank is kept between 40-55 C. The cooling demand is totally supplied by '
'the heat pump unit.',
'Domestic Hot Water Heat Pump System: This system is in charge of supplying domestic hot water demand.'
'The heat pump is connected to a thermal storage tank with electric resistance heating coil inside it.'
' The temperature inside the tank should always remain above 60 C.']
self.report.add_text(text)
self.report.add_itemize(items=items)
schema_path = self.system_schemas_path / f'{archetype}.jpg'
self.report.add_image(str(schema_path).replace('\\', '/'),
f'Proposed energy system for buildings {buildings_str}')
def plot_monthly_radiation(self):
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
monthly_roof_radiation = []
for i in range(len(months)):
tilted_radiation = 0
for building in self.city.buildings:
tilted_radiation += (building.roofs[0].global_irradiance_tilted[cte.MONTH][i] /
(cte.WATTS_HOUR_TO_JULES * 1000))
monthly_roof_radiation.append(tilted_radiation)
def plot_bar_chart(ax, months, values, color, ylabel, title):
ax.bar(months, values, color=color, width=0.6, zorder=2)
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
ax.set_xlabel('Month', fontsize=12, labelpad=10)
ax.set_ylabel(ylabel, fontsize=14, labelpad=10)
ax.set_title(title, fontsize=14, weight='bold', alpha=.8, pad=40)
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.set_xticks(np.arange(len(months)))
ax.set_xticklabels(months, rotation=45, ha='right')
ax.bar_label(ax.containers[0], padding=3, color='black', fontsize=12, rotation=90)
ax.spines[['top', 'left', 'bottom']].set_visible(False)
ax.spines['right'].set_linewidth(1.1)
average_value = np.mean(values)
ax.axhline(y=average_value, color='grey', linewidth=2, linestyle='--')
ax.text(len(months) - 1, average_value, f'Average = {average_value:.1f} kWh', ha='right', va='bottom',
color='grey')
# Plotting the bar chart
fig, ax = plt.subplots(figsize=(15, 8), dpi=96)
plot_bar_chart(ax, months, monthly_roof_radiation, '#ffc107', 'Tilted Roof Radiation (kWh / m2)',
'Monthly Tilted Roof Radiation')
# Set a white background
fig.patch.set_facecolor('white')
# Adjust the margins around the plot area
plt.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.1)
# Save the plot
chart_path = self.charts_path / 'monthly_tilted_roof_radiation.png'
plt.savefig(chart_path, bbox_inches='tight')
plt.close()
return chart_path
def energy_consumption_comparison(self, current_status_data, retrofitted_data, file_name, title):
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
consumptions = {
'Heating': ('heating', '#2196f3', 'Heating Consumption (kWh)', 'Monthly Energy Consumption for Heating'),
'Cooling': ('cooling', '#ff5a5f', 'Cooling Consumption (kWh)', 'Monthly Energy Consumption for Cooling'),
'DHW': ('dhw', '#4caf50', 'DHW Consumption (kWh)', 'Monthly DHW Consumption')
}
# Helper function for plotting
def plot_double_bar_chart(ax, consumption_type, color, ylabel, title):
current_values = current_status_data[consumption_type]
retrofitted_values = retrofitted_data[consumption_type]
bar_width = 0.35
index = np.arange(len(months))
ax.bar(index, current_values, bar_width, label='Current Status', color=color, alpha=0.7, zorder=2)
ax.bar(index + bar_width, retrofitted_values, bar_width, label='Retrofitted', color=color, hatch='/', zorder=2)
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
ax.set_xlabel('Month', fontsize=12, labelpad=10)
ax.set_ylabel(ylabel, fontsize=14, labelpad=10)
ax.set_title(title, fontsize=14, weight='bold', alpha=.8, pad=40)
ax.set_xticks(index + bar_width / 2)
ax.set_xticklabels(months, rotation=45, ha='right')
ax.legend()
# Adding bar labels
ax.bar_label(ax.containers[0], padding=3, color='black', fontsize=12, rotation=90)
ax.bar_label(ax.containers[1], padding=3, color='black', fontsize=12, rotation=90)
ax.spines[['top', 'left', 'bottom']].set_visible(False)
ax.spines['right'].set_linewidth(1.1)
# Plotting
fig, axs = plt.subplots(3, 1, figsize=(20, 15), dpi=96)
fig.suptitle(title, fontsize=16, weight='bold', alpha=.8)
plot_double_bar_chart(axs[0], 'heating', consumptions['Heating'][1], consumptions['Heating'][2],
consumptions['Heating'][3])
plot_double_bar_chart(axs[1], 'cooling', consumptions['Cooling'][1], consumptions['Cooling'][2],
consumptions['Cooling'][3])
plot_double_bar_chart(axs[2], 'dhw', consumptions['DHW'][1], consumptions['DHW'][2], consumptions['DHW'][3])
# Set a white background
fig.patch.set_facecolor('white')
# Adjust the margins around the plot area
plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.1, wspace=0.3, hspace=0.5)
# Save the plot
chart_path = self.charts_path / f'{file_name}.png'
plt.savefig(chart_path, bbox_inches='tight')
plt.close()
return chart_path
def yearly_consumption_comparison(self):
current_total_consumption = self.current_status_data['total_consumption']
retrofitted_total_consumption = self.retrofitted_data['total_consumption']
def pv_system(self):
self.report.add_text('The first step in PV assessments is evaluating the potential of buildings for installing '
'rooftop PV system. The benchmark value used for this evaluation is the mean yearly solar '
'incident in Montreal. According to Hydro-Quebec, the mean annual incident in Montreal is 1350'
'kWh/m2. Therefore, any building with rooftop annual global horizontal radiation of less than '
'1080 kWh/m2 is considered to be infeasible. Table 4 shows the yearly horizontal radiation on '
'buildings roofs. \par')
radiation_data = [
["Building Name", "Roof Area $m^2$", "Function", "Rooftop Annual Global Horizontal Radiation kWh/ $m^2$"]
]
pv_feasible_buildings = []
for building in self.city.buildings:
if building.roofs[0].global_irradiance[cte.YEAR][0] > 1080:
pv_feasible_buildings.append(building.name)
data = [building.name, str(format(building.roofs[0].perimeter_area, '.2f')), building.function,
str(format(building.roofs[0].global_irradiance[cte.YEAR][0] / (cte.WATTS_HOUR_TO_JULES * 1000), '.2f'))]
radiation_data.append(data)
self.report.add_table(radiation_data,
caption='Buildings Roof Characteristics')
if len(pv_feasible_buildings) == len(self.city.buildings):
buildings_str = 'all'
else:
buildings_str = ", ".join(pv_feasible_buildings)
self.report.add_text(f"From the table it can be seen that {buildings_str} buildings are good candidates to have "
f"rooftop PV system. The next step is calculating the amount of solar radiation on a tilted "
f"surface. Figure 5 shows the total monthly solar radiation on a surface with the tilt angle "
f"of 45 degrees on the roofs of those buildings that are identified to have rooftop PV system."
f"\par")
tilted_radiation = self.plot_monthly_radiation()
self.report.add_image(str(tilted_radiation).replace('\\', '/'),
caption='Total Monthly Solar Radiation on Buildings Roofs on a 45 Degrees Tilted Surface',
placement='H')
self.report.add_text('The first step in sizing the PV system is to find the available roof area. '
'Few considerations need to be made here. The considerations include space for maintenance '
'crew, space for mechanical equipment, and orientation correction factor to make sure all '
'the panel are truly facing south. After all these considerations, the minimum distance '
'between the panels to avoid shading throughout the year is found. Table 5 shows the number of'
'panles on each buildings roof, yearly PV production, total electricity consumption, and self '
'consumption. \par')
pv_output_table = [['Building Name', 'Total Surface Area of PV Panels ($m^2$)',
'Total Solar Incident on PV Modules (MWh)', 'Yearly PV production (MWh)']]
for building in self.city.buildings:
if building.name in pv_feasible_buildings:
pv_data = []
pv_data.append(building.name)
yearly_solar_incident = (building.roofs[0].global_irradiance_tilted[cte.YEAR][0] *
building.roofs[0].installed_solar_collector_area) / (cte.WATTS_HOUR_TO_JULES * 1e6)
yearly_solar_incident_str = format(yearly_solar_incident, '.2f')
yearly_pv_output = building.onsite_electrical_production[cte.YEAR][0] / (cte.WATTS_HOUR_TO_JULES * 1e6)
yearly_pv_output_str = format(yearly_pv_output, '.2f')
pv_data.append(format(building.roofs[0].installed_solar_collector_area, '.2f'))
pv_data.append(yearly_solar_incident_str)
pv_data.append(yearly_pv_output_str)
pv_output_table.append(pv_data)
self.report.add_table(pv_output_table, caption='PV System Simulation Results', first_column_width=3)
def create_report(self):
# Add sections and text to the report
self.report.add_section('Overview of the Current Status in Buildings')
self.report.add_text('In this section, an overview of the current status of buildings characteristics, '
'energy demand and consumptions are provided')
self.report.add_subsection('Buildings Characteristics')
self.building_energy_info()
monthly_demands = self.monthly_demands()
monthly_demands_path = str(Path(self.charts_path / 'monthly_demands.png'))
self.plot_monthly_energy_demands(demands=monthly_demands,
file_name='monthly_demands')
self.report.add_image('monthly_demands.png', 'Total Monthly Energy Demands in City' )
# current monthly demands and consumptions
current_monthly_demands = self.current_status_data['monthly_demands']
current_monthly_consumptions = self.current_status_data['monthly_consumptions']
# Plot and save demand chart
current_demand_chart_path = self.plot_monthly_energy_demands(data=current_monthly_demands,
file_name='current_monthly_demands',
title='Current Status Monthly Energy Demands')
# Plot and save consumption chart
current_consumption_chart_path = self.plot_monthly_energy_consumption(data=current_monthly_consumptions,
file_name='monthly_consumptions',
title='Monthly Energy Consumptions')
current_consumption_breakdown_path = self.fuel_consumption_breakdown('City_Energy_Consumption_Breakdown',
self.current_status_data)
# Add current state of energy demands in the city
self.report.add_subsection('Current State of Energy Demands in the City')
self.report.add_text('The total monthly energy demands in the city are shown in Figure 1. It should be noted '
'that the electricity demand refers to total lighting and appliance electricity demands')
self.report.add_image(str(current_demand_chart_path).replace('\\', '/'),
'Total Monthly Energy Demands in City',
placement='h')
# Add current state of energy consumption in the city
self.report.add_subsection('Current State of Energy Consumption in the City')
self.report.add_text('The following figure shows the amount of energy consumed to supply heating, cooling, and '
'domestic hot water needs in the city. The details of the systems in each building before '
'and after retrofit are provided in Section 4. \par')
self.report.add_image(str(current_consumption_chart_path).replace('\\', '/'),
'Total Monthly Energy Consumptions in City',
placement='H')
self.report.add_text('Figure 3 shows the yearly energy supplied to the city by fuel in different sectors. '
'All the values are in kWh.')
self.report.add_image(str(current_consumption_breakdown_path).replace('\\', '/'),
'Current Energy Consumption Breakdown in the City by Fuel',
placement='H')
self.report.add_section(f'{self.retrofit_scenario}')
self.report.add_subsection('Proposed Systems')
self.energy_system_archetype_schematic()
if 'PV' in self.retrofit_scenario:
self.report.add_subsection('Rooftop Photovoltaic System Implementation')
self.pv_system()
if 'System' in self.retrofit_scenario:
self.report.add_subsection('Retrofitted HVAC and DHW Systems')
self.report.add_text('Figure 6 shows a comparison between total monthly energy consumption in the selected '
'buildings before and after retrofitting.')
consumption_comparison = self.energy_consumption_comparison(self.current_status_data['monthly_consumptions'],
self.retrofitted_data['monthly_consumptions'],
'energy_consumption_comparison_in_city',
'Total Monthly Energy Consumption Comparison in '
'Buildings')
self.report.add_image(str(consumption_comparison).replace('\\', '/'),
caption='Comparison of Total Monthly Energy Consumption in City Buildings',
placement='H')
# Save and compile the report
self.report.save_report()
self.report.compile_to_pdf()

View File

@ -1,68 +1,67 @@
import hub.helpers.constants as cte
def system_results(buildings):
system_performance_summary = {}
fields = ["Energy System Archetype", "Heating Equipments", "Cooling Equipments", "DHW Equipments",
"Photovoltaic System Capacity", "Heating Fuel", "Yearly HVAC Energy Consumption (MWh)",
"DHW Energy Consumption (MWH)", "PV Yearly Production (kWh)", "LCC Analysis Duration (Years)",
"Energy System Capital Cost (CAD)", "Energy System Average Yearly Operational Cost (CAD)",
"Energy System Life Cycle Cost (CAD)"]
for building in buildings:
system_performance_summary[f'{building.name}'] = {}
for field in fields:
system_performance_summary[f'{building.name}'][field] = '-'
def consumption_data(city):
current_status_energy_consumption_data = {}
for building in city.buildings:
current_status_energy_consumption_data[f'{building.name}'] = {'heating_consumption': building.heating_consumption,
'cooling_consumption': building.cooling_consumption,
'domestic_hot_water_consumption':
building.domestic_hot_water_consumption,
'energy_consumption_breakdown':
building.energy_consumption_breakdown}
heating_demand_monthly = []
cooling_demand_monthly = []
dhw_demand_monthly = []
lighting_appliance_monthly = []
heating_consumption_monthly = []
cooling_consumption_monthly = []
dhw_consumption_monthly = []
for i in range(12):
heating_demand = 0
cooling_demand = 0
dhw_demand = 0
lighting_appliance_demand = 0
heating_consumption = 0
cooling_consumption = 0
dhw_consumption = 0
for building in city.buildings:
heating_demand += building.heating_demand[cte.MONTH][i] / 3.6e6
cooling_demand += building.cooling_demand[cte.MONTH][i] / 3.6e6
dhw_demand += building.domestic_hot_water_heat_demand[cte.MONTH][i] / 3.6e6
lighting_appliance_demand += building.lighting_electrical_demand[cte.MONTH][i] / 3.6e6
heating_consumption += building.heating_consumption[cte.MONTH][i] / 3.6e6
if building.cooling_demand[cte.YEAR][0] == 0:
cooling_consumption += building.cooling_demand[cte.MONTH][i] / (3.6e6 * 2)
else:
cooling_consumption += building.cooling_consumption[cte.MONTH][i] / 3.6e6
dhw_consumption += building.domestic_hot_water_consumption[cte.MONTH][i] / 3.6e6
heating_demand_monthly.append(heating_demand)
cooling_demand_monthly.append(cooling_demand)
dhw_demand_monthly.append(dhw_demand)
lighting_appliance_monthly.append(lighting_appliance_demand)
heating_consumption_monthly.append(heating_consumption)
cooling_consumption_monthly.append(cooling_consumption)
dhw_consumption_monthly.append(dhw_consumption)
for building in buildings:
fuels = []
system_performance_summary[f'{building.name}']['Energy System Archetype'] = building.energy_systems_archetype_name
energy_systems = building.energy_systems
for energy_system in energy_systems:
demand_types = energy_system.demand_types
for demand_type in demand_types:
if demand_type == cte.COOLING:
equipments = []
for generation_system in energy_system.generation_systems:
if generation_system.fuel_type == cte.ELECTRICITY:
equipments.append(generation_system.name or generation_system.system_type)
cooling_equipments = ", ".join(equipments)
system_performance_summary[f'{building.name}']['Cooling Equipments'] = cooling_equipments
elif demand_type == cte.HEATING:
equipments = []
for generation_system in energy_system.generation_systems:
if generation_system.nominal_heat_output is not None:
equipments.append(generation_system.name or generation_system.system_type)
fuels.append(generation_system.fuel_type)
heating_equipments = ", ".join(equipments)
system_performance_summary[f'{building.name}']['Heating Equipments'] = heating_equipments
elif demand_type == cte.DOMESTIC_HOT_WATER:
equipments = []
for generation_system in energy_system.generation_systems:
equipments.append(generation_system.name or generation_system.system_type)
dhw_equipments = ", ".join(equipments)
system_performance_summary[f'{building.name}']['DHW Equipments'] = dhw_equipments
for generation_system in energy_system.generation_systems:
if generation_system.system_type == cte.PHOTOVOLTAIC:
system_performance_summary[f'{building.name}'][
'Photovoltaic System Capacity'] = generation_system.nominal_electricity_output or str(0)
heating_fuels = ", ".join(fuels)
system_performance_summary[f'{building.name}']['Heating Fuel'] = heating_fuels
system_performance_summary[f'{building.name}']['Yearly HVAC Energy Consumption (MWh)'] = format(
(building.heating_consumption[cte.YEAR][0] + building.cooling_consumption[cte.YEAR][0]) / 3.6e9, '.2f')
system_performance_summary[f'{building.name}']['DHW Energy Consumption (MWH)'] = format(
building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6, '.2f')
return system_performance_summary
monthly_demands = {'heating': heating_demand_monthly,
'cooling': cooling_demand_monthly,
'dhw': dhw_demand_monthly,
'lighting_appliance': lighting_appliance_monthly}
monthly_consumptions = {'heating': heating_consumption_monthly,
'cooling': cooling_consumption_monthly,
'dhw': dhw_consumption_monthly}
yearly_heating = 0
yearly_cooling = 0
yearly_dhw = 0
for building in city.buildings:
yearly_heating += building.heating_consumption[cte.YEAR][0] / 3.6e6
yearly_cooling += building.cooling_consumption[cte.YEAR][0] / 3.6e6
yearly_dhw += building.domestic_hot_water_consumption[cte.YEAR][0] / 3.6e6
total_consumption = yearly_heating + yearly_cooling + yearly_dhw
current_status_energy_consumption_data['monthly_demands'] = monthly_demands
current_status_energy_consumption_data['monthly_consumptions'] = monthly_consumptions
current_status_energy_consumption_data['total_consumption'] = total_consumption
def new_system_results(buildings):
new_system_performance_summary = {}
fields = ["Energy System Archetype", "Heating Equipments", "Cooling Equipments", "DHW Equipments",
"Photovoltaic System Capacity", "Heating Fuel", "Yearly HVAC Energy Consumption (MWh)",
"DHW Energy Consumption (MWH)", "PV Yearly Production (kWh)", "LCC Analysis Duration (Years)",
"Energy System Capital Cost (CAD)", "Energy System Average Yearly Operational Cost (CAD)",
"Energy System Life Cycle Cost (CAD)"]
for building in buildings:
new_system_performance_summary[f'{building.name}'] = {}
for field in fields:
new_system_performance_summary[f'{building.name}'][field] = '-'
return new_system_performance_summary
return current_status_energy_consumption_data

View File

@ -4,13 +4,16 @@ from shapely import Point
from pathlib import Path
def process_geojson(x, y, diff):
def process_geojson(x, y, diff, expansion=False):
selection_box = Polygon([[x + diff, y - diff],
[x - diff, y - diff],
[x - diff, y + diff],
[x + diff, y + diff]])
geojson_file = Path('./data/collinear_clean 2.geojson').resolve()
output_file = Path('./input_files/output_buildings.geojson').resolve()
if not expansion:
output_file = Path('./input_files/output_buildings.geojson').resolve()
else:
output_file = Path('./input_files/output_buildings_expanded.geojson').resolve()
buildings_in_region = []
with open(geojson_file, 'r') as file:

34
scripts/pv_feasibility.py Normal file
View File

@ -0,0 +1,34 @@
from pathlib import Path
import subprocess
from hub.imports.geometry_factory import GeometryFactory
from scripts.geojson_creator import process_geojson
from hub.helpers.dictionaries import Dictionaries
from hub.imports.weather_factory import WeatherFactory
from hub.imports.results_factory import ResultFactory
from hub.exports.exports_factory import ExportsFactory
def pv_feasibility(current_x, current_y, current_diff, selected_buildings):
new_diff = current_diff * 5
geojson_file = process_geojson(x=current_x, y=current_y, diff=new_diff, expansion=True)
file_path = (Path(__file__).parent.parent / 'input_files' / 'output_buildings_expanded.geojson')
output_path = (Path(__file__).parent.parent / 'out_files').resolve()
city = GeometryFactory('geojson',
path=file_path,
height_field='height',
year_of_construction_field='year_of_construction',
function_field='function',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
WeatherFactory('epw', city).enrich()
ExportsFactory('sra', city, output_path).export()
sra_path = (output_path / f'{city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', city, output_path).enrich()
for selected_building in selected_buildings:
for building in city.buildings:
if selected_building.name == building.name:
selected_building.roofs[0].global_irradiance = building.roofs[0].global_irradiance

View File

@ -15,8 +15,8 @@ from hub.city_model_structure.building import Building
energy_systems_format = 'montreal_custom'
# parameters:
residential_systems_percentage = {'system 1 gas': 100,
'system 1 electricity': 0,
residential_systems_percentage = {'system 1 gas': 44,
'system 1 electricity': 6,
'system 2 gas': 0,
'system 2 electricity': 0,
'system 3 and 4 gas': 0,
@ -25,8 +25,8 @@ residential_systems_percentage = {'system 1 gas': 100,
'system 5 electricity': 0,
'system 6 gas': 0,
'system 6 electricity': 0,
'system 8 gas': 0,
'system 8 electricity': 0}
'system 8 gas': 44,
'system 8 electricity': 6}
residential_new_systems_percentage = {'PV+ASHP+GasBoiler+TES': 0,
'PV+4Pipe+DHW': 100,

View File

@ -15,6 +15,8 @@ class LatexReport:
self.content.append(r'\usepackage[margin=2.5cm]{geometry}')
self.content.append(r'\usepackage{graphicx}')
self.content.append(r'\usepackage{tabularx}')
self.content.append(r'\usepackage{multirow}')
self.content.append(r'\usepackage{float}')
self.content.append(r'\begin{document}')
current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@ -35,12 +37,16 @@ class LatexReport:
def add_subsection(self, subsection_title):
self.content.append(r'\subsection{' + subsection_title + r'}')
def add_subsubsection(self, subsection_title):
self.content.append(r'\subsubsection{' + subsection_title + r'}')
def add_text(self, text):
self.content.append(text)
def add_table(self, table_data, caption=None, first_column_width=None):
def add_table(self, table_data, caption=None, first_column_width=None, merge_first_column=False):
num_columns = len(table_data[0])
total_width = 0.9
first_column_width_str = ''
if first_column_width is not None:
first_column_width_str = str(first_column_width) + 'cm'
@ -51,31 +57,58 @@ class LatexReport:
self.content.append(r'\caption{' + caption + r'}')
self.content.append(r'\centering')
self.content.append(r'\begin{tabularx}{\textwidth}{|p{' + first_column_width_str + r'}|' + '|'.join(['X'] * (
num_columns - 1)) + '|}' if first_column_width is not None else r'\begin{tabularx}{\textwidth}{|' + '|'.join(
['X'] * num_columns) + '|}')
column_format = '|p{' + first_column_width_str + r'}|' + '|'.join(
['X'] * (num_columns - 1)) + '|' if first_column_width is not None else '|' + '|'.join(['X'] * num_columns) + '|'
self.content.append(r'\begin{tabularx}{\textwidth}{' + column_format + '}')
self.content.append(r'\hline')
for row in table_data:
self.content.append(' & '.join(row) + r' \\')
previous_first_column = None
rowspan_count = 1
for i, row in enumerate(table_data):
if merge_first_column and i > 0 and row[0] == previous_first_column:
rowspan_count += 1
self.content.append(' & '.join(['' if j == 0 else cell for j, cell in enumerate(row)]) + r' \\')
else:
if merge_first_column and i > 0 and rowspan_count > 1:
self.content[-rowspan_count] = self.content[-rowspan_count].replace(r'\multirow{1}',
r'\multirow{' + str(rowspan_count) + '}')
rowspan_count = 1
if merge_first_column and i < len(table_data) - 1 and row[0] == table_data[i + 1][0]:
self.content.append(r'\multirow{1}{*}{' + row[0] + '}' + ' & ' + ' & '.join(row[1:]) + r' \\')
else:
self.content.append(' & '.join(row) + r' \\')
previous_first_column = row[0]
self.content.append(r'\hline')
if merge_first_column and rowspan_count > 1:
self.content[-rowspan_count] = self.content[-rowspan_count].replace(r'\multirow{1}',
r'\multirow{' + str(rowspan_count) + '}')
self.content.append(r'\end{tabularx}')
if caption:
self.content.append(r'\end{table}')
def add_image(self, image_path, caption=None):
def add_image(self, image_path, caption=None, placement='ht'):
if caption:
self.content.append(r'\begin{figure}[htbp]')
self.content.append(r'\begin{figure}[' + placement + r']')
self.content.append(r'\centering')
self.content.append(r'\includegraphics[width=\textwidth]{' + image_path + r'}')
self.content.append(r'\caption{' + caption + r'}')
self.content.append(r'\end{figure}')
else:
self.content.append(r'\begin{figure}[htbp]')
self.content.append(r'\begin{figure}[' + placement + r']')
self.content.append(r'\centering')
self.content.append(r'\includegraphics[width=\textwidth]{' + image_path + r'}')
self.content.append(r'\end{figure}')
def add_itemize(self, items):
self.content.append(r'\begin{itemize}')
for item in items:
self.content.append(r'\item ' + item)
self.content.append(r'\end{itemize}')
def save_report(self):
self.content.append(r'\end{document}')
with open(self.file_path, 'w') as f:

View File

@ -17,8 +17,8 @@ class Archetype13:
self._heating_peak_load = building.heating_peak_load[cte.YEAR][0]
self._cooling_peak_load = building.cooling_peak_load[cte.YEAR][0]
self._domestic_hot_water_peak_load = building.domestic_hot_water_peak_load[cte.YEAR][0]
self._hourly_heating_demand = [demand / cte.HOUR_TO_SECONDS for demand in building.heating_demand[cte.HOUR]]
self._hourly_cooling_demand = [demand / cte.HOUR_TO_SECONDS for demand in building.cooling_demand[cte.HOUR]]
self._hourly_heating_demand = [demand / cte.WATTS_HOUR_TO_JULES for demand in building.heating_demand[cte.HOUR]]
self._hourly_cooling_demand = [demand / cte.WATTS_HOUR_TO_JULES for demand in building.cooling_demand[cte.HOUR]]
self._hourly_dhw_demand = [demand / cte.WATTS_HOUR_TO_JULES for demand in
building.domestic_hot_water_heat_demand[cte.HOUR]]
self._output_path = output_path
@ -125,11 +125,11 @@ class Archetype13:
m_dis[i + 1] = 0
t_ret[i + 1] = t_tank[i + 1]
else:
if demand[i + 1] > 0.5 * self._heating_peak_load / cte.HOUR_TO_SECONDS:
if demand[i + 1] > 0.5 * self._heating_peak_load:
factor = 8
else:
factor = 4
m_dis[i + 1] = self._heating_peak_load / (cte.WATER_HEAT_CAPACITY * factor * cte.HOUR_TO_SECONDS)
m_dis[i + 1] = self._heating_peak_load / (cte.WATER_HEAT_CAPACITY * factor)
t_ret[i + 1] = t_tank[i + 1] - demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY)
tes.temperature = []
hp_electricity_j = [(x * cte.WATTS_HOUR_TO_JULES) / number_of_ts for x in hp_electricity]
@ -191,11 +191,11 @@ class Archetype13:
for i in range(1, len(demand)):
if demand[i] > 0:
m[i] = self._cooling_peak_load / (cte.WATER_HEAT_CAPACITY * 5 * cte.HOUR_TO_SECONDS)
m[i] = hp.nominal_cooling_output / (cte.WATER_HEAT_CAPACITY * 5)
if t_ret[i - 1] >= 13:
if demand[i] < 0.25 * self._cooling_peak_load / cte.HOUR_TO_SECONDS:
if demand[i] < 0.25 * self._cooling_peak_load:
q_hp[i] = 0.25 * hp.nominal_cooling_output
elif demand[i] < 0.5 * self._cooling_peak_load / cte.HOUR_TO_SECONDS:
elif demand[i] < 0.5 * self._cooling_peak_load:
q_hp[i] = 0.5 * hp.nominal_cooling_output
else:
q_hp[i] = hp.nominal_cooling_output
@ -210,7 +210,7 @@ class Archetype13:
else:
m[i] = 0
q_hp[i] = 0
t_sup_hp[i] = t_ret[i -1]
t_sup_hp[i] = t_ret[i - 1]
t_ret[i] = t_ret[i - 1]
t_sup_hp_fahrenheit = 1.8 * t_sup_hp[i] + 32
t_out_fahrenheit = 1.8 * t_out[i] + 32
@ -221,7 +221,7 @@ class Archetype13:
eer_curve_coefficients[3] * t_out_fahrenheit +
eer_curve_coefficients[4] * t_out_fahrenheit ** 2 +
eer_curve_coefficients[5] * t_sup_hp_fahrenheit * t_out_fahrenheit)
hp_electricity[i] = q_hp[i] / hp_eer[i]
hp_electricity[i] = q_hp[i] / cooling_efficiency
else:
hp_eer[i] = 0
hp_electricity[i] = 0