use PVWatts to simulate panel output
This commit is contained in:
		
							parent
							
								
									ebae0aecfe
								
							
						
					
					
						commit
						05186bd77f
					
				| @ -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) | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										13
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user