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,6 +12,7 @@ from Utilities.Processes import ( | |||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def get_location(c): | def get_location(c): | ||||||
|     location = pvlib.location.Location( |     location = pvlib.location.Location( | ||||||
|         latitude=c["environment"]["location"]["latitude"], |         latitude=c["environment"]["location"]["latitude"], | ||||||
| @ -85,9 +86,7 @@ def define_grid_layout(c): | |||||||
|         for i in range(int(max__panels_per_row)): |         for i in range(int(max__panels_per_row)): | ||||||
|             if counter < no_of_panels: |             if counter < no_of_panels: | ||||||
|                 x.append(i * c["panel"]["dimensions"]["width"]) |                 x.append(i * c["panel"]["dimensions"]["width"]) | ||||||
|                 y.append( |                 y.append(j * pitch) | ||||||
|                     j * pitch |  | ||||||
|                 ) |  | ||||||
|                 z.append(0) |                 z.append(0) | ||||||
|                 counter += 1 |                 counter += 1 | ||||||
|             else: |             else: | ||||||
| @ -121,10 +120,111 @@ def get_solar_data(c): | |||||||
| 
 | 
 | ||||||
|     # Get solar position data using PVLib |     # Get solar position data using PVLib | ||||||
|     solar_positions = location.get_solarposition(times) |     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 |     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: | array: | ||||||
|   system_size: 100 # in kWp |   system_size: 400 # in kWp | ||||||
|   spacing: 1.5 # spacing between adjacent panel rows in m |   spacing: 0.5 # spacing between adjacent panel rows in m | ||||||
|   edge_setback: 1.8 # distance from the edge of the roof to the array |   edge_setback: 1.8 # distance from the edge of the roof to the array | ||||||
|   front_face_azimuth: 90 # 90=east, 180=south, 270=west |   front_face_azimuth: 90 # 90=east, 180=south, 270=west | ||||||
|   tilt: 90 # just 0 and 90 are supported for now |   tilt: 90 # just 0 and 90 are supported for now | ||||||
| @ -24,6 +24,7 @@ environment: | |||||||
| 
 | 
 | ||||||
| panel: | panel: | ||||||
|   peak_power: 710 # in Wp |   peak_power: 710 # in Wp | ||||||
|  |   efficiency: 0.229 # max efficiency of the front face | ||||||
|   bifaciality: 0.85 # rear face efficiency relative to front face |   bifaciality: 0.85 # rear face efficiency relative to front face | ||||||
|   # dimensions all in m |   # dimensions all in m | ||||||
|   dimensions: |   dimensions: | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.py
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| # %% | # %% | ||||||
| import yaml | import yaml | ||||||
| import logging | import logging | ||||||
| from Utilities.Shading import define_grid_layout | from Utilities.Shading import calculate_shading | ||||||
| 
 | 
 | ||||||
| logging.basicConfig( | logging.basicConfig( | ||||||
|     level=logging.INFO, |     level=logging.INFO, | ||||||
| @ -24,7 +24,7 @@ with open(config_path, "r") as file: | |||||||
| logger.info("Configuration loaded successfully.") | logger.info("Configuration loaded successfully.") | ||||||
| logger.debug(f"Configuration: {c}") | logger.debug(f"Configuration: {c}") | ||||||
| 
 | 
 | ||||||
| shading = define_grid_layout(c) | shading = calculate_shading(c) | ||||||
| logger.info("Shading calculation completed successfully.") | logger.info("Shading calculation completed successfully.") | ||||||
| 
 | 
 | ||||||
| # %% | # %% | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user