Merge pull request 'feat: PV sizing module is added' (#7) from pv_sizing into main

Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/s_ranjbar/energy_system_modelling_workflow/pulls/7
This commit is contained in:
Saeed Ranjbar 2024-05-30 17:29:04 -04:00
commit 1e34687496
3 changed files with 107 additions and 23 deletions

View File

@ -1,3 +1,4 @@
import pandas as pd
from scripts.geojson_creator import process_geojson
from pathlib import Path
import subprocess
@ -8,11 +9,12 @@ from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from hub.imports.results_factory import ResultFactory
from scripts.solar_angles import CitySolarAngles
from scripts.radiation_tilted import RadiationTilted
from scripts.ep_run_enrich import energy_plus_workflow
import hub.helpers.constants as cte
from hub.exports.exports_factory import ExportsFactory
from scripts.pv_sizing_and_simulation import PVSizingSimulation
# Specify the GeoJSON file path
geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001)
geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0005)
file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson')
# Specify the output path for the PDF file
output_path = (Path(__file__).parent / 'out_files').resolve()
@ -32,8 +34,40 @@ 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()
energy_plus_workflow(city)
solar_angles = CitySolarAngles(city.name,
city.latitude,
city.longitude,
tilt_angle=45,
surface_azimuth_angle=180).calculate
df = pd.DataFrame()
df.index = ['yearly lighting (kWh)', 'yearly appliance (kWh)', 'yearly heating (kWh)', 'yearly cooling (kWh)',
'yearly dhw (kWh)', 'roof area (m2)', 'used area for pv (m2)', 'number of panels', 'pv production (kWh)']
for building in city.buildings:
roof_horizontal = [x / cte.WATTS_HOUR_TO_JULES for x in building.roofs[0].global_irradiance[cte.HOUR]]
solar_angles = (
CitySolarAngles(city.name, city.latitude, city.longitude, tilt_angle=45, surface_azimuth_angle=180).calculate)
RadiationTilted(building, solar_angles, tilt_angle=45, ghi=roof_horizontal).enrich()
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()
yearly_lighting = building.lighting_electrical_demand[cte.YEAR][0] / 1000
yearly_appliance = building.appliances_electrical_demand[cte.YEAR][0] / 1000
yearly_heating = building.heating_demand[cte.YEAR][0] / (3.6e6 * 3)
yearly_cooling = building.cooling_demand[cte.YEAR][0] / (3.6e6 * 4.5)
yearly_dhw = building.domestic_hot_water_heat_demand[cte.YEAR][0] / 1000
roof_area = building.roofs[0].perimeter_area
used_roof = pv_sizing_simulation.available_space()
number_of_pv_panels = pv_sizing_simulation.total_number_of_panels
yearly_pv = building.onsite_electrical_production[cte.YEAR][0] / 1000
df[f'{building.name}'] = [yearly_lighting, yearly_appliance, yearly_heating, yearly_cooling, yearly_dhw, roof_area,
used_roof, number_of_pv_panels, yearly_pv]
df.to_csv(output_path / 'pv.csv')

View File

@ -1,21 +1,66 @@
import math
from scripts.radiation_tilted import RadiationTilted
from scripts.solar_angles import CitySolarAngles
import hub.helpers.constants as cte
from hub.helpers.monthly_values import MonthlyValues
class PVSizingSimulation(RadiationTilted):
def __init__(self, building, solar_angles, tilt_angle, module_height, module_width, ghi):
super().__init__(building, solar_angles, tilt_angle, ghi)
self.module_height = module_height
self.module_width = module_width
self.total_number_of_panels = 0
self.enrich()
def available_space(self):
roof_area = self.building.roofs[0].perimeter_area
maintenance_factor = 0.1
orientation_factor = 0.2
if self.building.function == cte.RESIDENTIAL:
mechanical_equipment_factor = 0.2
else:
mechanical_equipment_factor = 0.3
available_roof = (maintenance_factor + orientation_factor + mechanical_equipment_factor) * roof_area
return available_roof
def inter_row_spacing(self):
winter_solstice = self.df[(self.df['AST'].dt.month == 12) &
(self.df['AST'].dt.day == 21) &
(self.df['AST'].dt.hour == 12)]
solar_altitude = winter_solstice['solar altitude'].values[0]
solar_azimuth = winter_solstice['solar azimuth'].values[0]
distance = ((self.module_height * abs(math.cos(math.radians(solar_azimuth)))) /
math.tan(math.radians(solar_altitude)))
distance = float(format(distance, '.1f'))
return distance
def number_of_panels(self, available_roof, inter_row_distance):
space_dimension = math.sqrt(available_roof)
space_dimension = float(format(space_dimension, '.2f'))
panels_per_row = math.ceil(space_dimension / self.module_width)
number_of_rows = math.ceil(space_dimension / inter_row_distance)
self.total_number_of_panels = panels_per_row * number_of_rows
return panels_per_row, number_of_rows
def pv_output(self):
radiation = self.total_radiation_tilted
pv_module_area = self.module_width * self.module_height
available_roof = self.available_space()
inter_row_spacing = self.inter_row_spacing()
self.number_of_panels(available_roof, inter_row_spacing)
system_efficiency = 0.2
pv_hourly_production = [x * system_efficiency * self.total_number_of_panels * pv_module_area for x in radiation]
self.building.onsite_electrical_production[cte.HOUR] = pv_hourly_production
self.building.onsite_electrical_production[cte.MONTH] = (
MonthlyValues.get_total_month(self.building.onsite_electrical_production[cte.HOUR]))
self.building.onsite_electrical_production[cte.YEAR] = [sum(self.building.onsite_electrical_production[cte.MONTH])]
class PVSizingSimulation:
def __init__(self, city, tilt_angle):
self.city = city
self.tilt_angle = tilt_angle
self.solar_angles = CitySolarAngles(file_name=self.city.name,
location_latitude=self.city.latitude,
location_longitude=self.city.longitude,
tilt_angle=self.tilt_angle,
surface_azimuth_angle=180,
standard_meridian=-75).calculate
self.enrich_radiation_data()
def enrich_radiation_data(self):
for building in self.city.buildings:
roof_horizontal = [x / cte.WATTS_HOUR_TO_JULES for x in building.roofs[0].global_irradiance[cte.HOUR]]
RadiationTilted(building, self.solar_angles, tilt_angle=self.tilt_angle, ghi=roof_horizontal).enrich()

View File

@ -13,9 +13,14 @@ class RadiationTilted:
self.zeniths = solar_angles['zenith'].tolist()[:-1]
self.incidents = solar_angles['incident angle'].tolist()[:-1]
self.date_time = solar_angles['DateTime'].tolist()[:-1]
data = {'DateTime': self.date_time, 'zenith': self.zeniths, 'incident angle': self.incidents, 'ghi': self.ghi}
self.ast = solar_angles['AST'].tolist()[:-1]
self.solar_azimuth = solar_angles['solar azimuth'].tolist()[:-1]
self.solar_altitude = solar_angles['solar altitude'].tolist()[:-1]
data = {'DateTime': self.date_time, 'AST': self.ast, 'solar altitude': self.solar_altitude, 'zenith': self.zeniths,
'solar azimuth': self.solar_azimuth, 'incident angle': self.incidents, 'ghi': self.ghi}
self.df = pd.DataFrame(data)
self.df['DateTime'] = pd.to_datetime(self.df['DateTime'])
self.df['AST'] = pd.to_datetime(self.df['AST'])
self.df.set_index('DateTime', inplace=True)
self.solar_constant = solar_constant
self.maximum_clearness_index = maximum_clearness_index