CityBEM-CityLayers-SaeedRay.../scripts/energy_system_analysis_report.py

339 lines
17 KiB
Python
Raw Normal View History

import os
import hub.helpers.constants as cte
import matplotlib.pyplot as plt
import random
import matplotlib.colors as mcolors
from matplotlib import cm
from scripts.report_creation import LatexReport
class EnergySystemAnalysisReport:
def __init__(self, city, output_path):
self.city = city
self.output_path = output_path
self.content = []
self.report = LatexReport('energy_system_analysis_report.tex')
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"]]
for building in self.city.buildings:
total_floor_area = 0
for zone in building.thermal_zones_from_internal_zones:
total_floor_area += zone.total_floor_area
building_data = [
building.name,
str(building.year_of_construction),
building.function,
str(format(building.heating_demand[cte.YEAR][0] / 3.6e9, '.2f')),
str(format(building.cooling_demand[cte.YEAR][0] / 3.6e9, '.2f')),
str(format(building.domestic_hot_water_heat_demand[cte.YEAR][0] / 1e6, '.2f')),
str(format(
(building.lighting_electrical_demand[cte.YEAR][0] + building.appliances_electrical_demand[cte.YEAR][0])
/ 1e6, '.2f')),
]
intensity_data = [
building.name,
str(format(total_floor_area, '.2f')),
str(format(building.heating_demand[cte.YEAR][0] / (3.6e6 * total_floor_area), '.2f')),
str(format(building.cooling_demand[cte.YEAR][0] / (3.6e6 * total_floor_area), '.2f')),
str(format(
(building.lighting_electrical_demand[cte.YEAR][0] + building.appliances_electrical_demand[cte.YEAR][0]) /
(1e3 * total_floor_area), '.2f'))
]
table_data.append(building_data)
intensity_table_data.append(intensity_data)
self.report.add_table(table_data, caption='City Buildings Energy Demands')
self.report.add_table(intensity_table_data, caption='Energy Intensity Information')
def base_case_charts(self):
save_directory = self.output_path
def autolabel(bars, ax):
for bar in bars:
height = bar.get_height()
ax.annotate('{:.1f}'.format(height),
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom')
def create_hvac_demand_chart(building_names, yearly_heating_demand, yearly_cooling_demand):
fig, ax = plt.subplots()
bar_width = 0.35
index = range(len(building_names))
bars1 = ax.bar(index, yearly_heating_demand, bar_width, label='Yearly Heating Demand (MWh)')
bars2 = ax.bar([i + bar_width for i in index], yearly_cooling_demand, bar_width,
label='Yearly Cooling Demand (MWh)')
ax.set_xlabel('Building Name')
ax.set_ylabel('Energy Demand (MWh)')
ax.set_title('Yearly HVAC Demands')
ax.set_xticks([i + bar_width / 2 for i in index])
ax.set_xticklabels(building_names, rotation=45, ha='right')
ax.legend()
autolabel(bars1, ax)
autolabel(bars2, ax)
fig.tight_layout()
plt.savefig(save_directory / 'hvac_demand_chart.jpg')
plt.close()
def create_bar_chart(title, ylabel, data, filename, bar_color=None):
fig, ax = plt.subplots()
bar_width = 0.35
index = range(len(building_names))
if bar_color is None:
# Generate a random color
bar_color = random.choice(list(mcolors.CSS4_COLORS.values()))
bars = ax.bar(index, data, bar_width, label=ylabel, color=bar_color)
ax.set_xlabel('Building Name')
ax.set_ylabel('Energy Demand (MWh)')
ax.set_title(title)
ax.set_xticks([i + bar_width / 2 for i in index])
ax.set_xticklabels(building_names, rotation=45, ha='right')
ax.legend()
autolabel(bars, ax)
fig.tight_layout()
plt.savefig(save_directory / filename)
plt.close()
building_names = [building.name for building in self.city.buildings]
yearly_heating_demand = [building.heating_demand[cte.YEAR][0] / 3.6e9 for building in self.city.buildings]
yearly_cooling_demand = [building.cooling_demand[cte.YEAR][0] / 3.6e9 for building in self.city.buildings]
yearly_dhw_demand = [building.domestic_hot_water_heat_demand[cte.YEAR][0] / 1e6 for building in
self.city.buildings]
yearly_electricity_demand = [(building.lighting_electrical_demand[cte.YEAR][0] +
building.appliances_electrical_demand[cte.YEAR][0]) / 1e6 for building in
self.city.buildings]
create_hvac_demand_chart(building_names, yearly_heating_demand, yearly_cooling_demand)
create_bar_chart('Yearly DHW Demands', 'Energy Demand (MWh)', yearly_dhw_demand, 'dhw_demand_chart.jpg', )
create_bar_chart('Yearly Electricity Demands', 'Energy Demand (MWh)', yearly_electricity_demand,
'electricity_demand_chart.jpg')
def maximum_monthly_hvac_chart(self):
save_directory = self.output_path
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October',
'November', 'December']
for building in self.city.buildings:
maximum_monthly_heating_load = []
maximum_monthly_cooling_load = []
fig, axs = plt.subplots(1, 2, figsize=(12, 6)) # Create a figure with 2 subplots side by side
for demand in building.heating_peak_load[cte.MONTH]:
maximum_monthly_heating_load.append(demand / 3.6e6)
for demand in building.cooling_peak_load[cte.MONTH]:
maximum_monthly_cooling_load.append(demand / 3.6e6)
# Plot maximum monthly heating load
axs[0].bar(months, maximum_monthly_heating_load, color='red') # Plot on the first subplot
axs[0].set_title('Maximum Monthly Heating Load')
axs[0].set_xlabel('Month')
axs[0].set_ylabel('Load (kWh)')
axs[0].tick_params(axis='x', rotation=45)
# Plot maximum monthly cooling load
axs[1].bar(months, maximum_monthly_cooling_load, color='blue') # Plot on the second subplot
axs[1].set_title('Maximum Monthly Cooling Load')
axs[1].set_xlabel('Month')
axs[1].set_ylabel('Load (kWh)')
axs[1].tick_params(axis='x', rotation=45)
plt.tight_layout() # Adjust layout to prevent overlapping
plt.savefig(save_directory / f'{building.name}_monthly_maximum_hvac_loads.jpg')
plt.close()
def load_duration_curves(self):
save_directory = self.output_path
for building in self.city.buildings:
heating_demand = [demand / 3.6e6 for demand in building.heating_demand[cte.HOUR]]
cooling_demand = [demand / 3.6e6 for demand in building.cooling_demand[cte.HOUR]]
heating_demand_sorted = sorted(heating_demand, reverse=True)
cooling_demand_sorted = sorted(cooling_demand, reverse=True)
plt.style.use('ggplot')
# Create figure and axis objects with 1 row and 2 columns
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
# Plot sorted heating demand
axs[0].plot(heating_demand_sorted, color='red', linewidth=2, label='Heating Demand')
axs[0].set_xlabel('Hour', fontsize=14)
axs[0].set_ylabel('Heating Demand (kWh)', fontsize=14)
axs[0].set_title('Heating Load Duration Curve', fontsize=16)
axs[0].grid(True)
axs[0].legend(loc='upper right', fontsize=12)
# Plot sorted cooling demand
axs[1].plot(cooling_demand_sorted, color='blue', linewidth=2, label='Cooling Demand')
axs[1].set_xlabel('Hour', fontsize=14)
axs[1].set_ylabel('Cooling Demand (kWh)', fontsize=14)
axs[1].set_title('Cooling Load Duration Curve', fontsize=16)
axs[1].grid(True)
axs[1].legend(loc='upper right', fontsize=12)
# Adjust layout
plt.tight_layout()
plt.savefig(save_directory / f'{building.name}_load_duration_curve.jpg')
plt.close()
def individual_building_info(self, building):
table_data = [
["Maximum Monthly HVAC Demands",
f"\\includegraphics[width=1\\linewidth]{{{building.name}_monthly_maximum_hvac_loads.jpg}}"],
["Load Duration Curve", f"\\includegraphics[width=1\\linewidth]{{{building.name}_load_duration_curve.jpg}}"],
]
self.report.add_table(table_data, caption=f'{building.name} Information', first_column_width=1.5)
def building_system_retrofit_results(self, building_name, current_system, new_system):
current_system_archetype = current_system[f'{building_name}']['Energy System Archetype']
current_system_heating = current_system[f'{building_name}']['Heating Equipments']
current_system_cooling = current_system[f'{building_name}']['Cooling Equipments']
current_system_dhw = current_system[f'{building_name}']['DHW Equipments']
current_system_pv = current_system[f'{building_name}']['Photovoltaic System Capacity']
current_system_heating_fuel = current_system[f'{building_name}']['Heating Fuel']
current_system_hvac_consumption = current_system[f'{building_name}']['Yearly HVAC Energy Consumption (MWh)']
current_system_dhw_consumption = current_system[f'{building_name}']['DHW Energy Consumption (MWH)']
current_pv_production = current_system[f'{building_name}']['PV Yearly Production (kWh)']
current_capital_cost = current_system[f'{building_name}']['Energy System Capital Cost (CAD)']
current_operational = current_system[f'{building_name}']['Energy System Average Yearly Operational Cost (CAD)']
current_lcc = current_system[f'{building_name}']['Energy System Life Cycle Cost (CAD)']
new_system_archetype = new_system[f'{building_name}']['Energy System Archetype']
new_system_heating = new_system[f'{building_name}']['Heating Equipments']
new_system_cooling = new_system[f'{building_name}']['Cooling Equipments']
new_system_dhw = new_system[f'{building_name}']['DHW Equipments']
new_system_pv = new_system[f'{building_name}']['Photovoltaic System Capacity']
new_system_heating_fuel = new_system[f'{building_name}']['Heating Fuel']
new_system_hvac_consumption = new_system[f'{building_name}']['Yearly HVAC Energy Consumption (MWh)']
new_system_dhw_consumption = new_system[f'{building_name}']['DHW Energy Consumption (MWH)']
new_pv_production = new_system[f'{building_name}']['PV Yearly Production (kWh)']
new_capital_cost = new_system[f'{building_name}']['Energy System Capital Cost (CAD)']
new_operational = new_system[f'{building_name}']['Energy System Average Yearly Operational Cost (CAD)']
new_lcc = new_system[f'{building_name}']['Energy System Life Cycle Cost (CAD)']
energy_system_table_data = [
["Detail", "Existing System", "Proposed System"],
["Energy System Archetype", current_system_archetype, new_system_archetype],
["Heating Equipments", current_system_heating, new_system_heating],
["Cooling Equipments", current_system_cooling, new_system_cooling],
["DHW Equipments", current_system_dhw, new_system_dhw],
["Photovoltaic System Capacity", current_system_pv, new_system_pv],
["Heating Fuel", current_system_heating_fuel, new_system_heating_fuel],
["Yearly HVAC Energy Consumption (MWh)", current_system_hvac_consumption, new_system_hvac_consumption],
["DHW Energy Consumption (MWH)", current_system_dhw_consumption, new_system_dhw_consumption],
["PV Yearly Production (kWh)", current_pv_production, new_pv_production],
["Energy System Capital Cost (CAD)", current_capital_cost, new_capital_cost],
["Energy System Average Yearly Operational Cost (CAD)", current_operational, new_operational],
["Energy System Life Cycle Cost (CAD)", current_lcc, new_lcc]
]
self.report.add_table(energy_system_table_data, caption=f'Building {building_name} Energy System Characteristics')
def building_fuel_consumption_breakdown(self, building):
save_directory = self.output_path
# Initialize variables to store fuel consumption breakdown
fuel_breakdown = {
"Heating": {"Gas": 0, "Electricity": 0},
"Domestic Hot Water": {"Gas": 0, "Electricity": 0},
"Cooling": {"Electricity": 0},
"Appliance": building.appliances_electrical_demand[cte.YEAR][0] / 1e6,
"Lighting": building.lighting_electrical_demand[cte.YEAR][0] / 1e6
}
# Iterate through energy systems of the building
for energy_system in building.energy_systems:
for demand_type in energy_system.demand_types:
if demand_type == cte.HEATING:
consumption = building.heating_consumption[cte.YEAR][0] / 3.6e9
for generation_system in energy_system.generation_systems:
if generation_system.fuel_type == cte.ELECTRICITY:
fuel_breakdown[demand_type]["Electricity"] += consumption
else:
fuel_breakdown[demand_type]["Gas"] += consumption
elif demand_type == cte.DOMESTIC_HOT_WATER:
consumption = building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6
for generation_system in energy_system.generation_systems:
if generation_system.fuel_type == cte.ELECTRICITY:
fuel_breakdown[demand_type]["Electricity"] += consumption
else:
fuel_breakdown[demand_type]["Gas"] += consumption
elif demand_type == cte.COOLING:
consumption = building.cooling_consumption[cte.YEAR][0] / 3.6e9
fuel_breakdown[demand_type]["Electricity"] += consumption
electricity_labels = ['Appliance', 'Lighting']
electricity_sizes = [fuel_breakdown['Appliance'], fuel_breakdown['Lighting']]
if fuel_breakdown['Heating']['Electricity'] > 0:
electricity_labels.append('Heating')
electricity_sizes.append(fuel_breakdown['Heating']['Electricity'])
if fuel_breakdown['Cooling']['Electricity'] > 0:
electricity_labels.append('Cooling')
electricity_sizes.append(fuel_breakdown['Cooling']['Electricity'])
if fuel_breakdown['Domestic Hot Water']['Electricity'] > 0:
electricity_labels.append('Domestic Hot Water')
electricity_sizes.append(fuel_breakdown['Domestic Hot Water']['Electricity'])
# Data for bar chart
gas_labels = ['Heating', 'Domestic Hot Water']
gas_sizes = [fuel_breakdown['Heating']['Gas'], fuel_breakdown['Domestic Hot Water']['Gas']]
# Set the style
plt.style.use('ggplot')
# Create plot grid
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
# Plot pie chart for electricity consumption breakdown
colors = cm.get_cmap('tab20c', len(electricity_labels))
axs[0].pie(electricity_sizes, labels=electricity_labels,
autopct=lambda pct: f"{pct:.1f}%\n({pct / 100 * sum(electricity_sizes):.2f})",
startangle=90, colors=[colors(i) for i in range(len(electricity_labels))])
axs[0].set_title('Electricity Consumption Breakdown')
# Plot bar chart for natural gas consumption breakdown
colors = cm.get_cmap('Paired', len(gas_labels))
axs[1].bar(gas_labels, gas_sizes, color=[colors(i) for i in range(len(gas_labels))])
axs[1].set_ylabel('Consumption (MWh)')
axs[1].set_title('Natural Gas Consumption Breakdown')
# Add grid to bar chart
axs[1].grid(axis='y', linestyle='--', alpha=0.7)
# Add a title to the entire figure
plt.suptitle('Building Energy Consumption Breakdown', fontsize=16, fontweight='bold')
# Adjust layout
plt.tight_layout()
# Save the plot as a high-quality image
plt.savefig(save_directory / f'{building.name}_energy_consumption_breakdown.png', dpi=300)
plt.close()
def create_report(self, current_system, new_system):
os.chdir(self.output_path)
self.report.add_section('Current Status')
self.building_energy_info()
self.base_case_charts()
self.report.add_image('hvac_demand_chart.jpg', caption='Yearly HVAC Demands')
self.report.add_image('dhw_demand_chart.jpg', caption='Yearly DHW Demands')
self.report.add_image('electricity_demand_chart.jpg', caption='Yearly Electricity Demands')
self.maximum_monthly_hvac_chart()
self.load_duration_curves()
for building in self.city.buildings:
self.individual_building_info(building)
self.building_system_retrofit_results(building_name=building.name, current_system=current_system, new_system=new_system)
self.building_fuel_consumption_breakdown(building)
self.report.add_image(f'{building.name}_energy_consumption_breakdown.png',
caption=f'Building {building.name} Consumption by source and sector breakdown')
self.report.save_report()
self.report.compile_to_pdf()