Skip to content

Commit 20a1b9b

Browse files
authored
Merge branch 'main' into add-examples-poa-irrad
2 parents b654f4f + eb5e8bd commit 20a1b9b

File tree

6 files changed

+115
-7
lines changed

6 files changed

+115
-7
lines changed

docs/sphinx/source/user_guide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This user guide is an overview and explains some of the key features of pvlib.
2727
modeling_topics/weather_data
2828
modeling_topics/singlediode
2929
modeling_topics/temperature
30+
modeling_topics/iam
3031

3132
.. toctree::
3233
:maxdepth: 2
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
.. _iam:
2+
3+
4+
Incidence angle modifier
5+
========================
6+
7+
Some fraction of the light incident on a PV module surface is reflected away or
8+
absorbed before it reaches the PV cell. This irradiance reduction depends
9+
on the angle at which the light strikes the module (the angle of incidence,
10+
:term:`AOI <aoi>`) and the optical properties of the module.
11+
12+
Some reduction occurs at all angles of incidence, even normal incidence.
13+
However, because PV modules are rated with irradiance at normal incidence,
14+
the reduction at normal incidence is implicit in the PV module's power rating
15+
and does not need to be accounted for separately in a performance model.
16+
Therefore, only the extra reduction at non-normal incidence should be modeled.
17+
18+
This is done using incidence angle modififer (:term:`IAM`) models.
19+
Conceptually, IAM is the fraction of incident light that is
20+
transmitted to the PV cell, normalized to the fraction transmitted at normal incidence:
21+
22+
.. math::
23+
24+
IAM(\theta) = \frac{T(\theta)}{T(0)},
25+
26+
where :math:`T(\theta)` represents the transmitted light fraction at AOI :math:`\theta`.
27+
IAM equals (by definition) 1.0 when AOI is zero and typically approaches zero
28+
as AOI approaches 90°. The shape of the IAM profile at intermediate AOI
29+
is nonlinear and depends on the module's optical properties.
30+
31+
IAM may also depend on the wavelength of the light, the polarization of the light,
32+
and which side of the module the light comes from. However, IAM models usually
33+
neglect these minor effects.
34+
35+
IAM functions in pvlib take an input angle in degrees and return a unitless ratio
36+
in the range 0–1.
37+
38+
39+
Types of models
40+
---------------
41+
42+
Because total in-plane irradiance is the combination of light from many
43+
directions, IAM values are computed for each component separately:
44+
45+
- *direct IAM*: IAM computed for the AOI of direct irradiance
46+
- *circumsolar IAM*: typically approximated as equal to the direct IAM
47+
- *diffuse IAM*: IAM integrated across the ranges of AOI spanning the sky and/or
48+
ground surfaces
49+
50+
Because diffuse light can be thought of as a field of many small beams of
51+
direct light, diffuse IAM can then be understood as the IAM averaged across
52+
those individual beams. This averaging can be done explicitly or empirically.
53+
54+
In principle, IAM should be applied to all components of incident irradiance.
55+
In practice, IAM is sometimes applied only to the direct component of in-plane
56+
irradiance, as the direct component is often the largest contributor to total
57+
in-plane irradiance and has a highly variable AOI across the day and year.
58+
59+
The IAM models currently available in pvlib are summarized in the
60+
following table:
61+
62+
+-------------------------------------------+---------+-------------------------------------------+
63+
| Model | Type | Notes |
64+
+===========================================+=========+===========================================+
65+
| :py:func:`~pvlib.iam.ashrae` | direct | Once common, now less used |
66+
+-------------------------------------------+---------+-------------------------------------------+
67+
| :py:func:`~pvlib.iam.martin_ruiz` | direct | Used in the IEC 61853 standard |
68+
+-------------------------------------------+---------+-------------------------------------------+
69+
| :py:func:`~pvlib.iam.martin_ruiz_diffuse` | diffuse | Used in the IEC 61853 standard |
70+
+-------------------------------------------+---------+-------------------------------------------+
71+
| :py:func:`~pvlib.iam.physical` | direct | Physics-based; optional AR coating |
72+
+-------------------------------------------+---------+-------------------------------------------+
73+
| :py:func:`~pvlib.iam.sapm` | direct | Can be non-monotonic and exceed 1.0 |
74+
+-------------------------------------------+---------+-------------------------------------------+
75+
| :py:func:`~pvlib.iam.schlick` | direct | Does not take module-specific parameters |
76+
+-------------------------------------------+---------+-------------------------------------------+
77+
| :py:func:`~pvlib.iam.schlick_diffuse` | diffuse | Does not take module-specific parmaeters |
78+
+-------------------------------------------+---------+-------------------------------------------+
79+
80+
In addition to the core models above, pvlib provides several other functions
81+
for IAM modeling:
82+
83+
- :py:func:`~pvlib.iam.interp`: interpolate between points on a measured IAM profile
84+
- :py:func:`~pvlib.iam.marion_diffuse` and :py:func:`~pvlib.iam.marion_integrate`:
85+
numerically integrate any IAM model across AOI to compute sky, horizon, and ground IAMs
86+
87+
88+
Model parameters
89+
----------------
90+
91+
Some IAM model functions provide default values for their parameters.
92+
However, these generic values may not be suitable for all PV modules.
93+
It should be noted that using the default parameter values for each
94+
model generally leads to different IAM profiles.
95+
96+
Module-specific values can be obtained via testing. For example, IEC 61853-2
97+
testing produces measured IAM values across the range of AOI and a corresponding
98+
parameter value for the Martin-Ruiz model. Parameter values for other models can
99+
be determined using :py:func:`pvlib.iam.fit`. Parameter values can also be approximately
100+
converted between models using :py:func:`pvlib.iam.convert`.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Documentation
2727
* Add examples for run_model_from_poa() and run_model_from_effective_irradiance()
2828
(:issue:`1043`, :pull:`2621`)
2929

30+
* Provide an overview of IAM modeling functionality in :ref:`iam`. (:pull:`2683`)
3031

3132

3233
Testing
@@ -48,4 +49,9 @@ Maintenance
4849
Contributors
4950
~~~~~~~~~~~~
5051
* Aman Srivastava (:ghuser:`aman-coder03`)
52+
* Rajiv Daxini (:ghuser:`RDaxini`)
53+
* Echedey Luis (:ghuser:`echedey-ls`)
54+
* Cliff Hansen (:ghuser:`cwhanse`)
55+
* Anton Driesse (:ghuser:`adriesse`)
56+
* Kevin Anderson (:ghuser:`kandersolar`)
5157

pvlib/tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,14 +471,14 @@ def _degrees_to_index(degrees, coordinate):
471471
inputmax = 180
472472
outputmax = 4320
473473
else:
474-
raise IndexError("coordinate must be 'latitude' or 'longitude'.")
474+
raise ValueError("coordinate must be 'latitude' or 'longitude'.")
475475

476476
inputrange = inputmax - inputmin
477477
scale = outputmax/inputrange # number of indices per degree
478478
center = inputmin + 1 / scale / 2 # shift to center of index
479479
outputmax -= 1 # shift index to zero indexing
480480
index = (degrees - center) * scale
481-
err = IndexError('Input, %g, is out of range (%g, %g).' %
481+
err = ValueError('Input, %g, is out of range (%g, %g).' %
482482
(degrees, inputmin, inputmax))
483483

484484
# If the index is still out of bounds after rounding, raise an error.

tests/test_clearsky.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -505,13 +505,13 @@ def monthly_lt_nointerp(lat, lon, time=months):
505505
monthly_lt_nointerp(-90, 180),
506506
[1.35, 1.7, 1.35, 1.35, 1.35, 1.35, 1.35, 1.35, 1.35, 1.35, 1.35, 1.7])
507507
# test out of range exceptions at corners
508-
with pytest.raises(IndexError):
508+
with pytest.raises(ValueError, match="out of range"):
509509
monthly_lt_nointerp(91, -122) # exceeds max latitude
510-
with pytest.raises(IndexError):
510+
with pytest.raises(ValueError, match="out of range"):
511511
monthly_lt_nointerp(38.2, 181) # exceeds max longitude
512-
with pytest.raises(IndexError):
512+
with pytest.raises(ValueError, match="out of range"):
513513
monthly_lt_nointerp(-91, -122) # exceeds min latitude
514-
with pytest.raises(IndexError):
514+
with pytest.raises(ValueError, match="out of range"):
515515
monthly_lt_nointerp(38.2, -181) # exceeds min longitude
516516

517517

tests/test_tools.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ def test__golden_sect_DataFrame_nans():
102102
def test_degrees_to_index_1():
103103
"""Test that _degrees_to_index raises an error when something other than
104104
'latitude' or 'longitude' is passed."""
105-
with pytest.raises(IndexError): # invalid value for coordinate argument
105+
# invalid value for coordinate argument
106+
with pytest.raises(ValueError, match="coordinate must be"):
106107
tools._degrees_to_index(degrees=22.0, coordinate='width')
107108

108109

0 commit comments

Comments
 (0)