-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Linear shade gh1690 #1725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Linear shade gh1690 #1725
Changes from 12 commits
1bb4e7d
778b3eb
9ecdb0e
eae3bcc
e5a7beb
90b4488
ea14adb
5f01bfe
cd3df97
15c197c
76eefbd
dee9f12
ffd9590
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -191,3 +191,117 @@ def sky_diffuse_passias(masking_angle): | |||||||
Available at https://www.nrel.gov/docs/fy18osti/67399.pdf | ||||||||
""" | ||||||||
return 1 - cosd(masking_angle/2)**2 | ||||||||
|
||||||||
|
||||||||
def tracker_shaded_fraction(tracker_theta, gcr, projected_solar_zenith, | ||||||||
cross_axis_slope=0): | ||||||||
r""" | ||||||||
Shade fraction (FS) for trackers with a common angle on an east-west slope. | ||||||||
|
||||||||
Parameters | ||||||||
---------- | ||||||||
tracker_theta : numeric | ||||||||
The tracker rotation angle in degrees from horizontal. | ||||||||
gcr : float | ||||||||
The ground coverage ratio as a fraction equal to the collector width | ||||||||
over the horizontal row-to-row pitch. | ||||||||
projected_solar_zenith : numeric | ||||||||
Zenith angle in degrees of the solar vector projected into the plane | ||||||||
perpendicular to the tracker axes. | ||||||||
Comment on lines
+208
to
+210
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like it would be a lot easier for the user if the parameter was simply There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The calculation for psz requires at least one additional argument, the solar azimuth and is not a trivial calculation: sp = pvl.solarposition.get_solarposition(times, latitude, longitude, ...)
proj_zenith = np.degrees(np.arctan2(
np.sin(np.radians(sp.azimuth))*np.sin(np.radians(sp.apparent_zenith)),
np.cos(np.radians(sp.apparent_zenith)))) It is already in pvlib here: pvlib-python/pvlib/tracking.py Line 432 in 275e671
which made me discover a typo in the comments here: pvlib-python/pvlib/tracking.py Line 421 in 275e671
should be the (x, z) plane, which is corrected lower down here: pvlib-python/pvlib/tracking.py Line 429 in 275e671
Anyway, I agree, it would be helpful to have PSZ as it's own function. And if it already exists in addition to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've created #1734 to propose a stand alone PSZ function, but in the process, realized that the existing use in I'm coming around to this idea. One instant advantage of this standalone function would be generalization of this linear shade loss function to arbitrary slopes that have both NS and EW tilt! Thanks! However, I still think it's best to address separately, then return to this function to enhance it with the axis tilt and the solar azimuth. |
||||||||
cross_axis_slope : float, default 0 | ||||||||
Angle of the plane containing the tracker axes in degrees from | ||||||||
horizontal. | ||||||||
|
||||||||
Returns | ||||||||
------- | ||||||||
shade_fraction : numeric | ||||||||
mikofski marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
The fraction of the collector width shaded by an adjacent row. A | ||||||||
value of 1 is completely shaded and zero is no shade. | ||||||||
|
||||||||
See also | ||||||||
-------- | ||||||||
pvlib.shading.linear_shade_loss | ||||||||
|
||||||||
|
||||||||
The shaded fraction is derived using trigonometery and similar triangles | ||||||||
from the tracker rotation :math:`\beta`, the ground slope :math:`\theta_g`, | ||||||||
the projected solar zenith (psz) :math:`\theta`, the collector width | ||||||||
:math:`L`, the row-to-row pitch :math:`P`, and the shadow length :math:`z` | ||||||||
as shown in the image below. | ||||||||
|
||||||||
.. image:: /_images/FSLR_irrad_shade_loss_slope_terrain.png | ||||||||
|
||||||||
The ratio of the shadow length to the pitch, :math:`z/P`, is given by the | ||||||||
following relation where the ground coverage ratio (GCR) is :math:`L/P`: | ||||||||
|
||||||||
.. math:: | ||||||||
\frac{z/P}{\sin{\left(\frac{\pi}{2}-\beta+\theta\right)}} | ||||||||
= \frac{GCR}{\sin{\left(\frac{\pi}{2}-\theta-\theta_g\right)}} | ||||||||
|
||||||||
Then the shaded fraction :math:`w/L` is derived from :math:`z/P` as | ||||||||
follows: | ||||||||
|
||||||||
.. math:: | ||||||||
\frac{w}{L} = 1 - \frac{P}{z\cos{\theta_g}} | ||||||||
|
||||||||
Finally, shade is zero if :math:`z\cos{\theta_g}/P \le 1`. | ||||||||
|
||||||||
References | ||||||||
---------- | ||||||||
Mark A. Mikofski, "First Solar Irradiance Shade Losses on Sloped Terrain," | ||||||||
PVPMC, 2023 | ||||||||
""" | ||||||||
theta_g_rad = np.radians(cross_axis_slope) | ||||||||
# angle opposite shadow cast on the ground, z | ||||||||
angle_z = ( | ||||||||
np.pi / 2 - np.radians(tracker_theta) | ||||||||
+ np.radians(projected_solar_zenith)) | ||||||||
# angle opposite the collector width, L | ||||||||
angle_gcr = ( | ||||||||
np.pi / 2 - np.radians(projected_solar_zenith) | ||||||||
- theta_g_rad) | ||||||||
# ratio of shadow, z, to pitch, P | ||||||||
zp = gcr * np.sin(angle_z) / np.sin(angle_gcr) | ||||||||
# there's only row-to-row shade loss if the shadow on the ground, z, is | ||||||||
# longer than row-to-row pitch projected on the ground, P*cos(theta_g) | ||||||||
zp_cos_g = zp*np.cos(theta_g_rad) | ||||||||
# shade fraction | ||||||||
fs = np.where(zp_cos_g <= 1, 0, 1 - 1/zp_cos_g) | ||||||||
return fs | ||||||||
|
||||||||
|
||||||||
def linear_shade_loss(shade_fraction, diffuse_fraction): | ||||||||
""" | ||||||||
Fraction of power lost to linear shade loss applicable to monolithic thin | ||||||||
film modules like First Solar CdTe, where the shadow is perpendicular to | ||||||||
cell scribe lines. | ||||||||
|
||||||||
Parameters | ||||||||
---------- | ||||||||
shade_fraction : numeric | ||||||||
The fraction of the collector width shaded by an adjacent row. A | ||||||||
value of 1 is completely shaded and zero is no shade. | ||||||||
diffuse_fraction : numeric | ||||||||
The ratio of diffuse plane of array (poa) irradiance to global poa. | ||||||||
A value of 1 is completely diffuse and zero is no diffuse. | ||||||||
|
||||||||
Returns | ||||||||
------- | ||||||||
linear_shade_loss : numeric | ||||||||
The fraction of power lost due to linear shading. A value of 1 is all | ||||||||
power lost and zero is no loss. | ||||||||
|
||||||||
See also | ||||||||
-------- | ||||||||
pvlib.shading.tracker_shaded_fraction | ||||||||
|
||||||||
Example | ||||||||
------- | ||||||||
>>> from pvlib import shading | ||||||||
>>> fs = shading.tracker_shaded_fraction(45.0, 0.8, 45.0, 0) | ||||||||
>>> loss = shading.linear_shade_loss(fs, 0.2) | ||||||||
>>> P_no_shade = 100 # [kWdc] DC output from modules | ||||||||
>>> P_linear_shade = P_no_shade * (1-loss) # [kWdc] output after loss | ||||||||
# 90.71067811865476 [kWdc] | ||||||||
""" | ||||||||
return shade_fraction * (1 - diffuse_fraction) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The acronym is never used anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They differ in that the new function allows the array to be on a cross axis slope. If we adapt the infinite sheds model to allow arbitrary orientation, then we could reuse this function, but that work was delayed due to some questions about where the horizon should be and what the sky and ground view factors would include. Until those issues are resolve and that enhancement is implemented, this new shaded fraction function is more generalized.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was the acronym that @williamhobbs used in #1689 and it's used on L269 in the source and L301 in the docstring example. Anyway, I'm not attached to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used "FS" for shaded fraction because Lorenzo 2011 used it, but I think it is confusing - should it be Fs, it's easily confused with "First Solar" in this context, etc.