-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Martinez shading factor #2070
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
Martinez shading factor #2070
Changes from 165 commits
c87885c
4fc6cd8
2a72f33
d6e8067
13a56d4
f012aae
6dd9a3b
e8743db
6030774
6276382
509699f
5ff8fd8
04126e0
607a246
240b551
997def6
7912055
f068e3e
a5f4a85
a854e27
2d82375
5f24721
049a8cc
a3185df
b9f1a76
f7d1d80
1880f6a
d9f242a
379ca95
62e99ad
4f584ea
238d123
98a254d
4616175
33ffd28
8526bc6
bde3656
51e3750
35738a4
8d679c3
4a907c1
234288d
fb0e5d1
aa4b6e8
18b2187
4163a72
1ebf8ea
de2144f
6aa43f0
c34a2f6
6b9f478
c3a9569
4bb02ab
78c481e
0ce0d68
74b4215
909cc23
e3e73f6
cbb0b04
96b61a8
d2b9291
7ec4d64
bbc6115
39a3ba9
a3ccf3e
e5fffca
457d4b4
f11858e
f321cc1
0ab3720
e6da6e8
4ce1655
60b7e2e
2d23cb2
78a451c
28128f6
f4acf39
104d185
894ff9f
f147881
4d11294
69c76cf
758f5e7
6882dfa
4ef4567
574a1a2
efac0b3
3a726b8
300b912
497a62e
2a34f43
c1b3978
db8edd6
d65e64d
51a2dfa
9038097
5652801
31affba
80ab494
facde21
5a807b7
25a0f19
8bdd469
a2e8be7
9f1fa97
37ab496
6303583
e785292
9b98d9e
3726e4d
4e7e36f
59465c8
9fa2bb4
1be1d3e
0d521ee
f01bd6f
28f8bb4
6d1755e
60030ca
6dcb517
55b89bc
fd4eded
b640c24
ba15ab1
beee0f1
56a4f46
7a57310
adf6dcc
ec717a9
a357c11
de46cc0
1d5a9e6
cdb50ba
d697e76
bc8598e
6153c46
bc79bb8
74e1e44
76eb1ca
6db8f7c
8db5e55
b4d255b
5e0677c
2e52cf9
847ae5c
19734dc
da32fd3
cc7ce5d
15ec5c4
610fd97
d6430bb
161f828
bccd3da
0f2fb31
48be1c8
6f2ed39
9d5c918
c54020a
6116c64
b20b9cc
9b84ed5
b20c535
820ffbf
46df544
d3343a5
d7a5cad
6e030aa
b0b4e7c
dee5d49
14fe993
94c2d23
e70090f
69e6771
dc4fbc0
b071ba4
5cd4f9b
192a039
91862f5
c444639
48a9004
e4076f7
9da4834
6ef91b8
37d4ca0
329fe23
cf9acec
8180654
874c0a4
46cedd8
954b05f
00f1eff
ec3931f
8ed87b6
da0b75d
95769bf
8841020
9483fda
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 |
---|---|---|
@@ -0,0 +1,240 @@ | ||
""" | ||
Modelling irradiance loss due to of direct irradiance in modules with bypass diodes | ||
=================================================================================== | ||
""" # noqa: E501 | ||
|
||
# %% | ||
# This example illustrates how to use the proposed by Martinez et al. [1]_. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# The model adjusts the incident direct and circumsolar beam irradiance of a | ||
# PV module based on the number | ||
# of shaded *blocks*. A *block* is defined as a group of cells protected by a | ||
# bypass diode. More information on the *blocks* can be found in the original | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# paper [1]_ and in :py:func:`pvlib.shading.direct_martinez` | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# documentation. | ||
# | ||
# The following key functions are used in this example: | ||
# | ||
# 1. :py:func:`pvlib.shading.direct_martinez` to calculate the adjustment | ||
# factor for the output power. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# 2. :py:func:`pvlib.shading.shaded_fraction1d` to calculate the fraction of | ||
# shaded surface and consequently the number of shaded *blocks* due to | ||
# row-to-row shading. | ||
# 3. :py:func:`pvlib.tracking.singleaxis` to calculate the rotation angle of | ||
# the trackers. | ||
# | ||
# .. sectionauthor:: Echedey Luis <echelual (at) gmail.com> | ||
# | ||
# References | ||
# ---------- | ||
# .. [1] F. Martínez-Moreno, J. Muñoz, and E. Lorenzo, 'Experimental model | ||
# to estimate shading losses on PV arrays', Solar Energy Materials and | ||
# Solar Cells, vol. 94, no. 12, pp. 2298-2303, Dec. 2010, | ||
# :doi:`10.1016/j.solmat.2010.07.029`. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# | ||
# Problem description | ||
# ------------------- | ||
# Let's consider a PV system with the following characteristics: | ||
# | ||
# - Two north-south single-axis tracker with 6 modules each one. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# - The rows have the same true-tracking tilt angles. Let's consider | ||
# true-tracking so shade is significant for this example. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# - Terrain slope is 7 degrees downward to the east. | ||
# - Rows' axes are horizontal. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# - The modules are comprised of multiple cells. We will compare these cases: | ||
# - modules with one bypass diode | ||
# - modules with three bypass diodes | ||
# - half-cut cell modules with three bypass diodes on portrait and landscape | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# | ||
# Setting up the system | ||
# ---------------------- | ||
# Let's start by defining the system characteristics, location and the time | ||
# range for the analysis. | ||
|
||
import pvlib | ||
import pandas as pd | ||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
from matplotlib.dates import ConciseDateFormatter | ||
|
||
pitch = 4 # meters | ||
width = 1.5 # meters | ||
gcr = width / pitch | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
N_modules_per_row = 6 | ||
axis_azimuth = 180 # N-S axis | ||
axis_tilt = 0 # flat because the axis is perpendicular to the slope | ||
cross_axis_tilt = -7 # 7 degrees downward to the east | ||
|
||
latitude, longitude = 40.2712, -3.7277 | ||
locus = pvlib.location.Location( | ||
latitude, | ||
longitude, | ||
tz="Europe/Madrid", | ||
altitude=pvlib.location.lookup_altitude(latitude, longitude), | ||
) | ||
|
||
times = pd.date_range("2001-04-11T04", "2001-04-11T20", freq="10min") | ||
|
||
# %% | ||
# True-tracking algorithm and shaded fraction | ||
# ------------------------------------------- | ||
# Since this model is about row-to-row shading, we will use the true-tracking | ||
# algorithm to calculate the trackers rotation. Back-tracking reduces the | ||
# shading between rows, but since this example is about shading, we will not | ||
# use it. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# | ||
# Then, the next step is to calculate the fraction of shaded surface. This is | ||
# done using :py:func:`pvlib.shading.shaded_fraction1d`. Using this function is | ||
# straightforward with the variables we already have defined. | ||
# Then, we can calculate the number of shaded blocks by rounding up the shaded | ||
# fraction by the number of blocks along the shaded length. | ||
|
||
# Calculate solar position to get single-axis tracker rotation and irradiance | ||
solar_pos = locus.get_solarposition(times) | ||
solar_apparent_zenith, solar_azimuth = ( | ||
solar_pos["apparent_zenith"], | ||
solar_pos["azimuth"], | ||
) # unpack for better readability | ||
|
||
tracking_result = pvlib.tracking.singleaxis( | ||
apparent_zenith=solar_apparent_zenith, | ||
apparent_azimuth=solar_azimuth, | ||
axis_tilt=axis_tilt, | ||
axis_azimuth=axis_azimuth, | ||
max_angle=(-90 + cross_axis_tilt, 90 + cross_axis_tilt), # (min, max) | ||
backtrack=False, | ||
gcr=gcr, | ||
cross_axis_tilt=cross_axis_tilt, | ||
) | ||
|
||
tracker_theta, aoi, surface_tilt, surface_azimuth = ( | ||
tracking_result["tracker_theta"], | ||
tracking_result["aoi"], | ||
tracking_result["surface_tilt"], | ||
tracking_result["surface_azimuth"], | ||
) # unpack for better readability | ||
|
||
# Calculate the shade fraction | ||
shaded_fraction = pvlib.shading.shaded_fraction1d( | ||
solar_apparent_zenith, | ||
solar_azimuth, | ||
axis_azimuth, | ||
axis_tilt=axis_tilt, | ||
shaded_row_rotation=tracker_theta, | ||
shading_row_rotation=tracker_theta, | ||
collector_width=width, | ||
pitch=pitch, | ||
cross_axis_slope=cross_axis_tilt, | ||
) | ||
|
||
# %% | ||
# Number of shaded blocks | ||
# ----------------------- | ||
# The number of shaded blocks depends on the module configuration and number | ||
# of bypass diodes. For example, | ||
# modules with one bypass diode will behave like one block. | ||
# On the other hand, modules with three bypass diodes will have three blocks, | ||
# except for the half-cut cell modules, which will have six blocks; 2x3 blocks | ||
# where the two rows are along the longest side of the module. | ||
# We can argue that the dimensions of the system changes when you switch from | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# portrait to landscape, but for this example, we will consider it the same. | ||
# | ||
# The number of shaded blocks is calculated by rounding up the shaded fraction | ||
# by the number of blocks along the shaded length. So let's define the number | ||
# of blocks for each module configuration: | ||
# | ||
# - 1 bypass diode: 1 block | ||
# - 3 bypass diodes: 3 blocks in landscape; 1 in portrait | ||
# - 3 bypass diodes half-cut cells: | ||
# - 2 blocks in portrait | ||
# - 3 blocks in landscape | ||
# | ||
# .. figure:: ../../_images/PV_module_layout_cesardd.jpg | ||
# :align: center | ||
# :width: 75% | ||
# :alt: Normal and half-cut cells module layouts | ||
# | ||
# Left: common module layout. Right: half-cut cells module layout. | ||
# Each module has three bypass diodes. On the left, they connect cell | ||
# columns 1-2, 2-3 & 3-4. On the right, they connect cell rows 1-2, 3-4 & | ||
# 5-6. | ||
# *Source: César Domínguez. CC BY-SA 4.0, Wikimedia Commons* | ||
# | ||
# In the upper image, each orange U-shaped section of the string is a block. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# By symmetry, the yellow inverted-U's of the subcircuit are also blocks. | ||
# For this reason, the half-cut cell modules have 6 blocks in total: two along | ||
# the longest side and three along the shortest side. | ||
|
||
blocks_per_module = { | ||
"1 bypass diode": 1, | ||
"3 bypass diodes": 3, | ||
"3 bypass diodes half-cut, portrait": 2, | ||
"3 bypass diodes half-cut, landscape": 3, | ||
} | ||
|
||
# Calculate the number of shaded blocks during the day | ||
shaded_blocks_per_module = { | ||
k: np.ceil(blocks_N * shaded_fraction) | ||
for k, blocks_N in blocks_per_module.items() | ||
} | ||
|
||
# %% | ||
# Results | ||
# ------- | ||
# Now that we have the number of shaded blocks for each module configuration, | ||
# we can apply the model and estimate the power loss due to shading. | ||
# | ||
# Note this model is not linear with the shaded blocks ratio, so there is a | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# difference between applying it to just a module or a whole row. | ||
|
||
shade_factor_per_module = { | ||
k: pvlib.shading.direct_martinez( | ||
shaded_fraction, module_shaded_blocks, blocks_per_module[k] | ||
) | ||
for k, module_shaded_blocks in shaded_blocks_per_module.items() | ||
} | ||
|
||
shade_factor_per_row = { | ||
k: pvlib.shading.direct_martinez( | ||
shaded_fraction, | ||
module_shaded_blocks * N_modules_per_row, | ||
blocks_per_module[k] * N_modules_per_row, | ||
) | ||
for k, module_shaded_blocks in shaded_blocks_per_module.items() | ||
} | ||
|
||
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) | ||
fig.suptitle("Martinez power correction factor due to shading") | ||
for k, shade_factor in shade_factor_per_module.items(): | ||
linestyle = "--" if k == "3 bypass diodes half-cut, landscape" else "-" | ||
ax1.plot(times, shade_factor, label=k, linestyle=linestyle) | ||
ax1.legend() | ||
ax1.grid() | ||
ax1.set_xlabel("Time") | ||
ax1.xaxis.set_major_formatter( | ||
ConciseDateFormatter("%H:%M", tz="Europe/Madrid") | ||
) | ||
ax1.set_ylabel("Power correction factor") | ||
ax1.set_title("Per module") | ||
|
||
for k, shade_factor in shade_factor_per_row.items(): | ||
linestyle = "--" if k == "3 bypass diodes half-cut, landscape" else "-" | ||
ax2.plot(times, shade_factor, label=k, linestyle=linestyle) | ||
ax2.legend() | ||
ax2.grid() | ||
ax2.set_xlabel("Time") | ||
ax2.xaxis.set_major_formatter( | ||
ConciseDateFormatter("%H:%M", tz="Europe/Madrid") | ||
) | ||
ax2.set_ylabel("Power correction factor") | ||
ax2.set_title("Per row") | ||
fig.tight_layout() | ||
fig.show() | ||
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 guess the code plotting the figure could be hidden (or collapsed) to make the example more compact 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 prefer leaving it as is, so in case of doubt it can be consulted without digging into the repo files. If I knew how to collapse it I would do it, but I think that's impossible. In any case, I see both pros and cons regarding that suggestion, so feedback is more than welcome @AdamRJensen @kandersolar. |
||
|
||
# %% | ||
# Note how the half-cut cell module in portrait behaves worse that the | ||
# normal module with three bypass diodes. This is because the number of shaded | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# blocks is less along the shaded length. | ||
# This is the reason why half-cut cell modules are preferred in portrait | ||
# orientation. | ||
|
||
# %% |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -553,3 +553,101 @@ def shaded_fraction1d( | |||||||||
) | ||||||||||
|
||||||||||
return np.clip(t_asterisk, 0, 1) | ||||||||||
|
||||||||||
|
||||||||||
def direct_martinez(shaded_fraction, shaded_blocks, total_blocks): | ||||||||||
r""" | ||||||||||
A shading correction factor for the direct and circumsolar incident | ||||||||||
irradiance of non-monolithic Silicon | ||||||||||
modules and arrays with an arbitrary number of bypass diodes. | ||||||||||
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.
Suggested change
|
||||||||||
|
||||||||||
.. versionadded:: 0.11.0 | ||||||||||
|
||||||||||
Parameters | ||||||||||
---------- | ||||||||||
shaded_fraction : numeric | ||||||||||
Fraction of module surface area that is shaded. Unitless. | ||||||||||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
shaded_blocks : numeric | ||||||||||
Number of blocks affected by the shadow. Unitless. | ||||||||||
If a floating point number is provided, it will be rounded up. | ||||||||||
total_blocks : int | ||||||||||
Number of total blocks. Unitless. | ||||||||||
|
||||||||||
Returns | ||||||||||
------- | ||||||||||
shading_correction_factor : numeric | ||||||||||
Multiply direct and circumsolar irradiance by this factor. | ||||||||||
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.
Suggested change
|
||||||||||
|
||||||||||
Notes | ||||||||||
----- | ||||||||||
The implemented equation is (6) from [1]_: | ||||||||||
|
||||||||||
.. math:: | ||||||||||
|
||||||||||
(1 - F_{ES}) = (1 - F_{GS}) (1 - \frac{N_{SB}}{N_{TB} + 1}) | ||||||||||
|
||||||||||
Where :math:`(1 - F_{ES})` is the correction factor to be multiplied by | ||||||||||
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 direct and circumsolar irradiance, :math:`F_{GS}` is the shaded | ||||||||||
fraction of the collector, :math:`N_{SB}` is the number of shaded blocks | ||||||||||
and :math:`N_{TB}` is the number of total blocks. | ||||||||||
|
||||||||||
Blocks terminology | ||||||||||
^^^^^^^^^^^^^^^^^^ | ||||||||||
[1]_ defines a *block* as a group of solar cells protected by a bypass | ||||||||||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
diode. Also, a *block* is shaded when at least one of its cells is shaded. | ||||||||||
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. If one of its cells is "partly" shaded or "completely" shaded? 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 paper does not specify it, but from the original tests spreadsheet I confirm they are considered shaded if a minimal amount of shade impacts on them. 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 added partially to the docstring, thou maybe I shouldn't. 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 like it! Especially if you've confirmed with the spreadsheet |
||||||||||
|
||||||||||
How many blocks and their layout depend on the module(s) used. Many | ||||||||||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
manufacturers don't specify this information explicitly. However, we can | ||||||||||
infer these values from: | ||||||||||
|
||||||||||
- the number of bypass diodes | ||||||||||
- where and how many junction boxes are present on the back of the module | ||||||||||
- whether or not the module is comprised of *half-cut cells* | ||||||||||
|
||||||||||
The latter two are heavily correlated. | ||||||||||
|
||||||||||
For example: | ||||||||||
|
||||||||||
1. A module with 1 bypass diode behaves as 1 block. | ||||||||||
2. A module with 3 bypass diodes and 1 junction box is likely to have 3 | ||||||||||
blocks. | ||||||||||
3. A half-cut module with 3 junction boxes (split junction boxes) is | ||||||||||
likely to have 3x2 blocks. The number of blocks along the longest | ||||||||||
side of the module is 2 and along the shortest side is 3. | ||||||||||
4. A module without bypass diodes doesn't constitute a block, but may be | ||||||||||
part of one. | ||||||||||
|
||||||||||
Examples | ||||||||||
-------- | ||||||||||
Minimal example. For a complete example, see | ||||||||||
`this gallery example <sphx_glr_gallery_plot_martinez_shade_loss.py>`_. | ||||||||||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
>>> import numpy as np | ||||||||||
>>> from pvlib import shading | ||||||||||
>>> total_blocks = 3 # blocks along the vertical of the module | ||||||||||
>>> POA_direct = 600 # W | ||||||||||
>>> POA_diffuse = 80 # W | ||||||||||
>>> shaded_fraction = shading.shaded_fraction1d( | ||||||||||
>>> 80, 180, 90, 25, | ||||||||||
>>> collector_width=0.5, pitch=1, surface_to_axis_offset=0, | ||||||||||
>>> cross_axis_slope=5.711, shading_row_rotation=50) | ||||||||||
>>> shaded_blocks = np.ceil(total_blocks*shaded_fraction) | ||||||||||
>>> loss_correction = shading.direct_martinez( | ||||||||||
>>> shaded_fraction, shaded_blocks, total_blocks) | ||||||||||
>>> POA_total = POA_direct * loss_correction + POA_diffuse | ||||||||||
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.
Suggested change
This is not exactly what the paper does; it multiplies DC power by 1 - loss. It's reasonable to extrapolate that the irradiance reduction is also 1 - loss but that's only because the authors use a linear model for DC power:
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
See Also | ||||||||||
-------- | ||||||||||
shaded_fraction1d : to calculate 1-dimensional shaded fraction | ||||||||||
|
||||||||||
References | ||||||||||
---------- | ||||||||||
.. [1] F. Martínez-Moreno, J. Muñoz, and E. Lorenzo, 'Experimental model | ||||||||||
to estimate shading losses on PV arrays', Solar Energy Materials and | ||||||||||
Solar Cells, vol. 94, no. 12, pp. 2298-2303, Dec. 2010, | ||||||||||
:doi:`10.1016/j.solmat.2010.07.029`. | ||||||||||
""" # Contributed by Echedey Luis, 2024 | ||||||||||
return ( # Eq. (6) of [1] | ||||||||||
(1 - shaded_fraction) | ||||||||||
* (1 - np.ceil(shaded_blocks) / (1 + total_blocks)) | ||||||||||
) |
Uh oh!
There was an error while loading. Please reload this page.