diff --git a/Utilities/Optimisation.py b/Utilities/Optimisation.py index e33742b..bf6842b 100644 --- a/Utilities/Optimisation.py +++ b/Utilities/Optimisation.py @@ -18,16 +18,18 @@ def optimise_vertical_panel_pitch(c): """ c["array"]["spacing"] = pitch logging.info(f"Optimizing with pitch: {pitch}m") - vertical_energy = calculate_energy_production_vertical(c) + vertical_energy, _ = calculate_energy_production_vertical(c) total_energy_yield = vertical_energy.sum() logger.info(f"Total energy yield for pitch {pitch}m: {total_energy_yield}kWh") return -total_energy_yield # perform minimization initial_pitch = c["array"]["spacing"] - result = minimize(objective, initial_pitch, bounds=[(0, 90)]) + result = minimize( + objective, initial_pitch, bounds=[(0, 5)], tol=1e-8, options={"eps": 0.5} + ) optimal_pitch = result.x[0] c["array"]["spacing"] = optimal_pitch logger.info(f"Optimal pitch found: {optimal_pitch}m") - vetical_energy = calculate_energy_production_vertical(c) - return (optimal_pitch, vetical_energy) + vetical_energy, no_of_panels = calculate_energy_production_vertical(c) + return (optimal_pitch, vetical_energy, no_of_panels) diff --git a/Utilities/Shading.py b/Utilities/Shading.py index 735891a..2a2088d 100644 --- a/Utilities/Shading.py +++ b/Utilities/Shading.py @@ -132,11 +132,6 @@ def calculate_energy_production_vertical(c): panel_coordinates, no_of_panels = define_grid_layout(c, panel_tilt=90) solar_positions, clearsky_data = get_solar_data(c) - # split the solar positions data into morning and afternoon, using solar azimuth of - # 180 degrees as the threshold - morning_solar_positions = solar_positions[solar_positions["azimuth"] <= 180] - afternoon_solar_positions = solar_positions[solar_positions["azimuth"] > 180] - # the first row is always not shaded so exclude no_of_rows = np.unique(panel_coordinates["y"]).shape[0] no_of_shaded_rows = no_of_rows - 1 @@ -207,31 +202,31 @@ def calculate_energy_production_vertical(c): # drop rows with poa_global NaN values poa_rear = poa_rear.dropna(subset=["poa_global"]) - effective_front = ( - poa_front["poa_global"] - * (1 - morning_shaded_fraction) - * c["panel"]["efficiency"] - ) + effective_front = poa_front["poa_global"] * (1 - morning_shaded_fraction) effective_rear = ( poa_rear["poa_global"] * (1 - afternoon_shaded_fraction) * c["panel"]["bifaciality"] - * c["panel"]["efficiency"] ) - energy_front = effective_front * 15 / 60 / 1e3 - energy_rear = effective_rear * 15 / 60 / 1e3 - total_hourly_energy_m2 = energy_front + energy_rear - energy_total = total_hourly_energy_m2.sum() - logger.info(f"Energy yield calculated: {energy_total} kWh/m2") + total_hourly_irradiance = effective_front + effective_rear + system_size = c["panel"]["peak_power"] * no_of_panels + pdc0 = system_size + gamma_pdc = c["panel"]["temperature_coefficient"] + temp_cell = c["panel"]["nominal_operating_cell_temperature"] + pdc = pvlib.pvsystem.pvwatts_dc( + pdc0=pdc0, + gamma_pdc=gamma_pdc, + temp_cell=temp_cell, + g_poa_effective=total_hourly_irradiance, + ) + + total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh - panel_area = c["panel"]["dimensions"]["length"] * c["panel"]["dimensions"]["width"] - total_area = panel_area * no_of_panels - total_hourly_energy = total_hourly_energy_m2 * total_area total_energy = total_hourly_energy.sum() logger.info(f"Total energy yield calculated: {total_energy} kWh") - return total_hourly_energy + return total_hourly_energy, no_of_panels def calculate_energy_production_horizontal(c): @@ -263,7 +258,6 @@ def calculate_energy_production_horizontal(c): axis_tilt=axis_tilt, ) shaded_fraction = shaded_fraction * no_of_shaded_rows / no_of_rows - logger.info(f"Shaded fraction calculated for solar positions: {shaded_fraction}") poa = pvlib.irradiance.get_total_irradiance( surface_tilt=0, @@ -277,18 +271,22 @@ def calculate_energy_production_horizontal(c): ) poa = poa.dropna(subset=["poa_global"]) - effective_front = ( - poa["poa_global"] * (1 - shaded_fraction) * c["panel"]["efficiency"] + effective_front = poa["poa_global"] * (1 - shaded_fraction) + + system_size = c["panel"]["peak_power"] * no_of_panels + + pdc0 = system_size + gamma_pdc = c["panel"]["temperature_coefficient"] + temp_cell = c["panel"]["nominal_operating_cell_temperature"] + pdc = pvlib.pvsystem.pvwatts_dc( + pdc0=pdc0, + gamma_pdc=gamma_pdc, + temp_cell=temp_cell, + g_poa_effective=effective_front, ) - total_hourly_energy_m2 = effective_front * 15 / 60 / 1e3 - energy_total = total_hourly_energy_m2.sum() - logger.info(f"Energy yield calculated: {energy_total} kWh/m2") - - panel_area = c["panel"]["dimensions"]["length"] * c["panel"]["dimensions"]["width"] - total_area = panel_area * no_of_panels - total_hourly_energy = total_hourly_energy_m2 * total_area + total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh total_energy = total_hourly_energy.sum() logger.info(f"Total energy yield calculated: {total_energy} kWh") - return total_hourly_energy + return total_hourly_energy, no_of_panels diff --git a/config.yml b/config.yml index 479bc82..ade5837 100644 --- a/config.yml +++ b/config.yml @@ -1,6 +1,6 @@ array: system_size: 900 # in kWp - spacing: 1 # spacing between adjacent panel rows in m + spacing: 21.8 # spacing between adjacent panel rows in m edge_setback: 1.8 # distance from the edge of the roof to the array roof_slope: 0 slope: 0 # degrees from horizontal (+ve means shaded row is higher than the row in front) @@ -30,3 +30,6 @@ panel: length: 2.384 width: 1.303 thickness: 0.033 + temperature_coefficient: -0.0029 # /°C + nominal_operating_cell_temperature: 43 # °C + diff --git a/main.py b/main.py index 610a860..1309ab9 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,10 @@ import yaml import logging import numpy as np import matplotlib.pyplot as pl -from Utilities.Shading import calculate_energy_production_horizontal +from Utilities.Shading import ( + calculate_energy_production_horizontal, + calculate_energy_production_vertical, +) from Utilities.Optimisation import optimise_vertical_panel_pitch logging.basicConfig( @@ -27,15 +30,17 @@ with open(config_path, "r") as file: logger.info("Configuration loaded successfully.") logger.debug(f"Configuration: {c}") + # calculate energy production for horizontal and vertical panels -optimal_pitch, vertical_energy = optimise_vertical_panel_pitch(c) +optimal_pitch, vertical_energy, no_of_panels_vertical = optimise_vertical_panel_pitch(c) logger.info("Energy production for vertical panels calculated successfully.") logger.debug(f"Vertical Energy Production: {vertical_energy.sum()}") +logger.debug("Number of panels: %d", no_of_panels_vertical) -horizontal_energy = calculate_energy_production_horizontal(c) +horizontal_energy, no_of_panels_horizontal = calculate_energy_production_horizontal(c) logger.info("Energy production for horizontal panels calculated successfully.") logger.debug(f"Horizontal Energy Production: {horizontal_energy.sum()}") - +logger.debug("Number of panels: %d", no_of_panels_horizontal) NOVA_scaledown = 0.75