Skip to content

Commit 6d1ab57

Browse files
committed
Merge branch 'main' into clearsky-timezone
2 parents 7bb7e02 + d53f97e commit 6d1ab57

File tree

9 files changed

+505
-77
lines changed

9 files changed

+505
-77
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
Shaded fraction of a horizontal single-axis tracker
3+
====================================================
4+
5+
This example illustrates how to calculate the 1D shaded fraction of three rows
6+
in a North-South horizontal single axis tracker (HSAT) configuration.
7+
"""
8+
9+
# %%
10+
# :py:func:`pvlib.shading.shaded_fraction1d` exposes a useful method for the
11+
# calculation of the shaded fraction of the width of a solar collector. Here,
12+
# the width is defined as the dimension perpendicular to the axis of rotation.
13+
# This method for calculating the shaded fraction only requires minor
14+
# modifications to be applicable for fixed-tilt systems.
15+
#
16+
# It is highly recommended to read :py:func:`pvlib.shading.shaded_fraction1d`
17+
# documentation to understand the input parameters and the method's
18+
# capabilities. For more in-depth information, please see the journal paper
19+
# `10.1063/5.0202220 <https://doi.org/10.1063/5.0202220>`_ describing
20+
# the methodology.
21+
#
22+
# Let's start by obtaining the true-tracking angles for each of the rows and
23+
# limiting the angles to the range of -50 to 50 degrees. This decision is
24+
# done to create an example scenario with significant shading.
25+
#
26+
# Key functions used in this example are:
27+
#
28+
# 1. :py:func:`pvlib.tracking.singleaxis` to calculate the tracking angles.
29+
# 2. :py:func:`pvlib.shading.projected_solar_zenith_angle` to calculate the
30+
# projected solar zenith angle.
31+
# 3. :py:func:`pvlib.shading.shaded_fraction1d` to calculate the shaded
32+
# fractions.
33+
#
34+
# .. sectionauthor:: Echedey Luis <echelual (at) gmail.com>
35+
36+
import pvlib
37+
38+
import numpy as np
39+
import pandas as pd
40+
import matplotlib.pyplot as plt
41+
from matplotlib.dates import DateFormatter
42+
43+
# Define the solar system parameters
44+
latitude, longitude = 28.51, -13.89
45+
altitude = pvlib.location.lookup_altitude(latitude, longitude)
46+
47+
axis_tilt = 3 # degrees, positive is upwards in the axis_azimuth direction
48+
axis_azimuth = 180 # degrees, N-S tracking axis
49+
collector_width = 3.2 # m
50+
pitch = 4.15 # m
51+
gcr = collector_width / pitch
52+
cross_axis_slope = -5 # degrees
53+
surface_to_axis_offset = 0.07 # m
54+
55+
# Generate a time range for the simulation
56+
times = pd.date_range(
57+
start="2024-01-01T05",
58+
end="2024-01-01T21",
59+
freq="5min",
60+
tz="Atlantic/Canary",
61+
)
62+
63+
# Calculate the solar position
64+
solar_position = pvlib.solarposition.get_solarposition(
65+
times, latitude, longitude, altitude
66+
)
67+
solar_azimuth = solar_position["azimuth"]
68+
solar_zenith = solar_position["apparent_zenith"]
69+
70+
# Calculate the tracking angles
71+
rotation_angle = pvlib.tracking.singleaxis(
72+
solar_zenith,
73+
solar_azimuth,
74+
axis_tilt,
75+
axis_azimuth,
76+
max_angle=(-50, 50), # (min, max) degrees
77+
backtrack=False,
78+
gcr=gcr,
79+
cross_axis_tilt=cross_axis_slope,
80+
)["tracker_theta"]
81+
82+
# %%
83+
# Once the tracker angles have been obtained, the next step is to calculate
84+
# the shaded fraction. Special care must be taken
85+
# to ensure that the shaded or shading tracker roles are correctly assigned
86+
# depending on the solar position.
87+
# This means we will have a result for each row, ``eastmost_shaded_fraction``,
88+
# ``middle_shaded_fraction``, and ``westmost_shaded_fraction``.
89+
# Switching the parameters will be based on the
90+
# sign of :py:func:`pvlib.shading.projected_solar_zenith_angle`.
91+
#
92+
# The following code is verbose to make it easier to understand the process,
93+
# but with some effort you may be able to simplify it. This verbosity also
94+
# allows to change the premises easily per case, e.g., in case of a tracker
95+
# failure or with a different system configuration.
96+
97+
psza = pvlib.shading.projected_solar_zenith_angle(
98+
solar_zenith, solar_azimuth, axis_tilt, axis_azimuth
99+
)
100+
101+
# Calculate the shaded fraction for the eastmost row
102+
eastmost_shaded_fraction = np.where(
103+
psza < 0,
104+
0, # no shaded fraction in the morning
105+
# shaded fraction in the evening
106+
pvlib.shading.shaded_fraction1d(
107+
solar_zenith,
108+
solar_azimuth,
109+
axis_azimuth,
110+
shaded_row_rotation=rotation_angle,
111+
axis_tilt=axis_tilt,
112+
collector_width=collector_width,
113+
pitch=pitch,
114+
surface_to_axis_offset=surface_to_axis_offset,
115+
cross_axis_slope=cross_axis_slope,
116+
shading_row_rotation=rotation_angle,
117+
),
118+
)
119+
120+
# Calculate the shaded fraction for the middle row
121+
middle_shaded_fraction = np.where(
122+
psza < 0,
123+
# shaded fraction in the morning
124+
pvlib.shading.shaded_fraction1d(
125+
solar_zenith,
126+
solar_azimuth,
127+
axis_azimuth,
128+
shaded_row_rotation=rotation_angle,
129+
axis_tilt=axis_tilt,
130+
collector_width=collector_width,
131+
pitch=pitch,
132+
surface_to_axis_offset=surface_to_axis_offset,
133+
cross_axis_slope=cross_axis_slope,
134+
shading_row_rotation=rotation_angle,
135+
),
136+
# shaded fraction in the evening
137+
pvlib.shading.shaded_fraction1d(
138+
solar_zenith,
139+
solar_azimuth,
140+
axis_azimuth,
141+
shaded_row_rotation=rotation_angle,
142+
axis_tilt=axis_tilt,
143+
collector_width=collector_width,
144+
pitch=pitch,
145+
surface_to_axis_offset=surface_to_axis_offset,
146+
cross_axis_slope=cross_axis_slope,
147+
shading_row_rotation=rotation_angle,
148+
),
149+
)
150+
151+
# Calculate the shaded fraction for the westmost row
152+
westmost_shaded_fraction = np.where(
153+
psza < 0,
154+
# shaded fraction in the morning
155+
pvlib.shading.shaded_fraction1d(
156+
solar_zenith,
157+
solar_azimuth,
158+
axis_azimuth,
159+
shaded_row_rotation=rotation_angle,
160+
axis_tilt=axis_tilt,
161+
collector_width=collector_width,
162+
pitch=pitch,
163+
surface_to_axis_offset=surface_to_axis_offset,
164+
cross_axis_slope=cross_axis_slope,
165+
shading_row_rotation=rotation_angle,
166+
),
167+
0, # no shaded fraction in the evening
168+
)
169+
170+
# %%
171+
# Plot the shaded fraction result for each row:
172+
plt.plot(times, eastmost_shaded_fraction, label="East-most", color="blue")
173+
plt.plot(times, middle_shaded_fraction, label="Middle", color="green",
174+
linewidth=3, linestyle="--") # fmt: skip
175+
plt.plot(times, westmost_shaded_fraction, label="West-most", color="red")
176+
plt.title(r"$shaded\_fraction1d$ of each row vs time")
177+
plt.xlabel("Time")
178+
plt.gca().xaxis.set_major_formatter(DateFormatter("%H:%M"))
179+
plt.ylabel("Shaded Fraction")
180+
plt.legend()
181+
plt.show()
Loading

docs/sphinx/source/reference/effects_on_pv_system_output/shading.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ Shading
1111
shading.masking_angle_passias
1212
shading.sky_diffuse_passias
1313
shading.projected_solar_zenith_angle
14+
shading.shaded_fraction1d
15+

docs/sphinx/source/reference/irradiance/components.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Decomposing and combining irradiance
99
irradiance.get_extra_radiation
1010
irradiance.aoi
1111
irradiance.aoi_projection
12-
irradiance.poa_horizontal_ratio
1312
irradiance.beam_component
1413
irradiance.poa_components
1514
irradiance.get_ground_diffuse

docs/sphinx/source/whatsnew/v0.11.0.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ v0.11.0 (Anticipated June, 2024)
77

88
Breaking changes
99
~~~~~~~~~~~~~~~~
10-
10+
* Remove the function and all its reference `poa_horizontal_ratio`. (:issue:`1697`, :pull:`2021`)
1111

1212
Deprecations
1313
~~~~~~~~~~~~
1414

1515

1616
Enhancements
1717
~~~~~~~~~~~~
18+
* Add function :py:func:`pvlib.shading.shaded_fraction1d`, to calculate the
19+
shade perpendicular to ``axis_azimuth``. The function is applicable to both
20+
fixed-tilt and one-axis tracking systems.
21+
(:issue:`1689`, :pull:`1725`, :pull:`1962`)
1822

1923

2024
Bug fixes

0 commit comments

Comments
 (0)