energy calculation including shading effects
This commit is contained in:
		
							parent
							
								
									a086431c46
								
							
						
					
					
						commit
						ca4fcd8f49
					
				
							
								
								
									
										0
									
								
								Utilities/Optimisers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								Utilities/Optimisers.py
									
									
									
									
									
										Normal file
									
								
							| @ -12,13 +12,14 @@ from Utilities.Processes import ( | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def get_location(c): | ||||
|     location = pvlib.location.Location( | ||||
|         latitude=c["environment"]["location"]["latitude"], | ||||
|         longitude=c["environment"]["location"]["longitude"], | ||||
|         tz=c["simulation_date_time"]["tz"], | ||||
|     ) | ||||
|      | ||||
| 
 | ||||
|     return location | ||||
| 
 | ||||
| 
 | ||||
| @ -85,9 +86,7 @@ def define_grid_layout(c): | ||||
|         for i in range(int(max__panels_per_row)): | ||||
|             if counter < no_of_panels: | ||||
|                 x.append(i * c["panel"]["dimensions"]["width"]) | ||||
|                 y.append( | ||||
|                     j * pitch | ||||
|                 ) | ||||
|                 y.append(j * pitch) | ||||
|                 z.append(0) | ||||
|                 counter += 1 | ||||
|             else: | ||||
| @ -121,10 +120,111 @@ def get_solar_data(c): | ||||
| 
 | ||||
|     # Get solar position data using PVLib | ||||
|     solar_positions = location.get_solarposition(times) | ||||
|     clearsky_data = location.get_clearsky(times) | ||||
|     # filter solar positions to only include times when the sun is above the horizon | ||||
|     solar_positions = solar_positions[solar_positions["apparent_elevation"] > 0] | ||||
|     # get datetime range from solar_positions | ||||
|     day_times = solar_positions.index | ||||
|     clearsky_data = location.get_clearsky(day_times) | ||||
| 
 | ||||
|     return solar_positions, clearsky_data | ||||
| 
 | ||||
| 
 | ||||
| def calculate_shading(c, coordinates, solar_positions): | ||||
|      | ||||
| def calculate_shading(c): | ||||
|     panel_coordinates, no_of_panels = define_grid_layout(c) | ||||
|     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 | ||||
| 
 | ||||
|     collector_width = c["panel"]["dimensions"]["length"] | ||||
|     # calculate delta between unique y coordinates of panels to get pitch | ||||
|     pitch = np.unique(panel_coordinates["y"])[1] - np.unique(panel_coordinates["y"])[0] | ||||
|     surface_to_axis_offset = 0 | ||||
|     shaded_row_rotation = c["array"]["tilt"] | ||||
|     shading_row_rotation = c["array"]["tilt"] | ||||
|     axis_tilt = 0 | ||||
|     axis_azimuth = c["array"]["front_face_azimuth"] | ||||
| 
 | ||||
|     morning_shaded_fraction = pvlib.shading.shaded_fraction1d( | ||||
|         solar_zenith=morning_solar_positions["zenith"], | ||||
|         solar_azimuth=morning_solar_positions["azimuth"], | ||||
|         axis_azimuth=axis_azimuth, | ||||
|         shaded_row_rotation=shaded_row_rotation, | ||||
|         shading_row_rotation=shading_row_rotation, | ||||
|         collector_width=collector_width, | ||||
|         pitch=pitch, | ||||
|         surface_to_axis_offset=surface_to_axis_offset, | ||||
|         axis_tilt=axis_tilt, | ||||
|     ) | ||||
|     morning_shaded_fraction = morning_shaded_fraction * no_of_shaded_rows / no_of_rows | ||||
| 
 | ||||
|     afternoon_shaded_fraction = pvlib.shading.shaded_fraction1d( | ||||
|         solar_zenith=afternoon_solar_positions["zenith"], | ||||
|         solar_azimuth=afternoon_solar_positions["azimuth"], | ||||
|         axis_azimuth=axis_azimuth + 180, | ||||
|         shaded_row_rotation=shaded_row_rotation, | ||||
|         shading_row_rotation=shading_row_rotation, | ||||
|         collector_width=collector_width, | ||||
|         pitch=pitch, | ||||
|         surface_to_axis_offset=surface_to_axis_offset, | ||||
|         axis_tilt=axis_tilt, | ||||
|     ) | ||||
|     afternoon_shaded_fraction = ( | ||||
|         afternoon_shaded_fraction * no_of_shaded_rows / no_of_rows | ||||
|     ) | ||||
|     logger.info( | ||||
|         f"Shaded fraction calculated for morning and afternoon solar positions." | ||||
|     ) | ||||
| 
 | ||||
|     # calculate irradiance on plane of array | ||||
|     poa_front = pvlib.irradiance.get_total_irradiance( | ||||
|         surface_tilt=c["array"]["tilt"], | ||||
|         surface_azimuth=c["array"]["front_face_azimuth"], | ||||
|         solar_zenith=morning_solar_positions["zenith"], | ||||
|         solar_azimuth=morning_solar_positions["azimuth"], | ||||
|         dni=clearsky_data["dni"], | ||||
|         ghi=clearsky_data["ghi"], | ||||
|         dhi=clearsky_data["dhi"], | ||||
|     ) | ||||
|     # drop rows with poa_global NaN values | ||||
|     poa_front = poa_front.dropna(subset=["poa_global"]) | ||||
| 
 | ||||
|     poa_rear = pvlib.irradiance.get_total_irradiance( | ||||
|         surface_tilt=180 - c["array"]["tilt"], | ||||
|         surface_azimuth=c["array"]["front_face_azimuth"] + 180, | ||||
|         solar_zenith=afternoon_solar_positions["zenith"], | ||||
|         solar_azimuth=afternoon_solar_positions["azimuth"], | ||||
|         dni=clearsky_data["dni"], | ||||
|         ghi=clearsky_data["ghi"], | ||||
|         dhi=clearsky_data["dhi"], | ||||
|     ) | ||||
|     # 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_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 | ||||
|     energy_total = energy_front.sum() + energy_rear.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_energy = energy_total * total_area | ||||
|     logger.info(f"Total energy yield calculated: {total_energy} kWh") | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| array: | ||||
|   system_size: 100 # in kWp | ||||
|   spacing: 1.5 # spacing between adjacent panel rows in m | ||||
|   system_size: 400 # in kWp | ||||
|   spacing: 0.5 # spacing between adjacent panel rows in m | ||||
|   edge_setback: 1.8 # distance from the edge of the roof to the array | ||||
|   front_face_azimuth: 90 # 90=east, 180=south, 270=west | ||||
|   tilt: 90 # just 0 and 90 are supported for now | ||||
| @ -24,6 +24,7 @@ environment: | ||||
| 
 | ||||
| panel: | ||||
|   peak_power: 710 # in Wp | ||||
|   efficiency: 0.229 # max efficiency of the front face | ||||
|   bifaciality: 0.85 # rear face efficiency relative to front face | ||||
|   # dimensions all in m | ||||
|   dimensions: | ||||
|  | ||||
							
								
								
									
										4
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.py
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | ||||
| # %% | ||||
| import yaml | ||||
| import logging | ||||
| from Utilities.Shading import define_grid_layout | ||||
| from Utilities.Shading import calculate_shading | ||||
| 
 | ||||
| logging.basicConfig( | ||||
|     level=logging.INFO, | ||||
| @ -24,7 +24,7 @@ with open(config_path, "r") as file: | ||||
| logger.info("Configuration loaded successfully.") | ||||
| logger.debug(f"Configuration: {c}") | ||||
| 
 | ||||
| shading = define_grid_layout(c) | ||||
| shading = calculate_shading(c) | ||||
| logger.info("Shading calculation completed successfully.") | ||||
| 
 | ||||
| # %% | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user