From 84bde67d0f7bf8609931128c2c758e372430ca70 Mon Sep 17 00:00:00 2001 From: s_ranjbar Date: Thu, 30 May 2024 17:26:50 -0400 Subject: [PATCH] feat: PV sizing module is added --- pv_assessment.py | 46 ++++++++++++++--- scripts/pv_sizing_and_simulation.py | 77 +++++++++++++++++++++++------ scripts/radiation_tilted.py | 7 ++- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/pv_assessment.py b/pv_assessment.py index 7d8f5ce5..7978df3a 100644 --- a/pv_assessment.py +++ b/pv_assessment.py @@ -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') + + + + + + diff --git a/scripts/pv_sizing_and_simulation.py b/scripts/pv_sizing_and_simulation.py index cd8e1673..877a1499 100644 --- a/scripts/pv_sizing_and_simulation.py +++ b/scripts/pv_sizing_and_simulation.py @@ -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() diff --git a/scripts/radiation_tilted.py b/scripts/radiation_tilted.py index 4eebf332..beb62088 100644 --- a/scripts/radiation_tilted.py +++ b/scripts/radiation_tilted.py @@ -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