diff --git a/Utilities/Shading.py b/Utilities/Shading.py index db0e4e7..656fe4b 100644 --- a/Utilities/Shading.py +++ b/Utilities/Shading.py @@ -1,6 +1,7 @@ import numpy as np import pandas as pd import logging +import math from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D from ladybug_geometry.geometry3d.plane import Plane @@ -86,13 +87,16 @@ def create_panels(coordinates, c): panel_length = c["panel"]["dimensions"]["length"] panel_thickness = c["panel"]["dimensions"]["thickness"] + # if viewed from above, and assumming the roof is a rectangle, the + # global origin is at the bottom left corner of the roof + # For a vertical panel: # - The vertical direction (panel height) is along the Z-axis. - y_axis = Vector3D(0, 0, 1) # points upward + y_axis = Vector3D(0, 1, 0) # points east, therefore front face is east facing # - The horizontal direction along the panel's width. # Here, we assume the width runs in the positive X-direction. - x_axis = Vector3D(1, 0, 0) # points east + x_axis = Vector3D(1, 0, 0) # points north panels = [] for index, row in coordinates.iterrows(): @@ -100,7 +104,7 @@ def create_panels(coordinates, c): panel_origin = Point3D(row["x"], row["y"], row["z"]) # Create the plane for the panel - panel_plane = Plane(origin=panel_origin, y_axis=y_axis, x_axis=x_axis) + panel_plane = Plane(o=panel_origin, n=y_axis, x=x_axis) # Create the panel geometry panel = Polyface3D.from_box( @@ -137,3 +141,75 @@ def get_solar_data(c): solar_positions = pvlib.solarposition.get_solarposition(times, latitude, longitude) return solar_positions + + +def calculate_sun_vector(solar_zenith, solar_azimuth): + """ + Calculate the sun vector from solar zenith and azimuth angles. + Args: + solar_zenith (float): Solar zenith angle in degrees. + solar_azimuth (float): Solar azimuth angle in degrees. + + Returns: + Vector3D: Sun vector as a 3D vector. + """ + # Convert angles from degrees to radians + zenith_rad = math.radians(solar_zenith) + azimuth_rad = math.radians(solar_azimuth) + + # Calculate the sun vector components + x = math.sin(zenith_rad) * math.cos(azimuth_rad) + y = math.sin(zenith_rad) * math.sin(azimuth_rad) + z = math.cos(zenith_rad) + + return Vector3D(x, y, z) + + +def compute_array_shading(panels, sun_vector, n_samples=25): + """ + Given a list of panel geometries (Polyface3D) and the sun vector, + compute the shading fraction for each panel and return the overall average shading. + + Parameters: + panels: List of Polyface3D objects representing the PV panels. + sun_vector: Unit Vector3D in the direction of the sun. + n_samples: Number of sample points per panel. + + Returns: + Dictionary mapping panel index to its shading fraction, and the overall average. + """ + shading_results = {} + for i, panel in enumerate(panels): + # Define obstacles as all other panels in the array + obstacles = [pan for j, pan in enumerate(panels) if j != i] + shading_frac = calculate_shading_fraction( + panel, sun_vector, obstacles, n_samples=n_samples + ) + shading_results[i] = shading_frac + # Compute the overall average shading fraction across all panels: + overall_avg = np.mean(list(shading_results.values())) + return shading_results, overall_avg + + +def calculate_shading_fraction(c): + coordinates = define_grid_layout(c) + panels = create_panels(coordinates, c) + solar_positions = get_solar_data(c) + + shading_fractions = [] + for panel in panels: + shading_fraction = [] + for index, row in solar_positions.iterrows(): + # Get the solar position for the current time step + # in a sphere, azimuth is the angle in the x-y plane from the north + # and zenith is the angle from the vertical axis + solar_zenith = row["apparent_zenith"] + solar_azimuth = row["apparent_azimuth"] + sun_vector = calculate_sun_vector(solar_zenith, solar_azimuth) + + # Calculate the shading fraction using the panel and solar position + shading_fraction.append(panel.shading_fraction(solar_zenith, solar_azimuth)) + + shading_fractions.append(shading_fraction) + + return shading_fractions diff --git a/main.py b/main.py index 8ea5c27..18beee3 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ # %% import yaml import logging -from Utilities.Shading import define_grid_layout +from Utilities.Shading import calculate_shading_fraction logging.basicConfig( level=logging.INFO, @@ -24,6 +24,6 @@ with open(config_path, "r") as file: logger.info("Configuration loaded successfully.") logger.debug(f"Configuration: {c}") -coordinates = define_grid_layout(c) +calculate_shading_fraction(c) # %%