Diffuse IAM Calculation#

Integrating an IAM model across angles to determine the overall reflection loss for diffuse irradiance.

The fraction of light reflected from the front of a module depends on the angle of incidence (AOI) of the light compared to the panel surface. The greater the AOI, the larger the reflected fraction is. The incident angle modifier (IAM) is defined as the ratio of light transmitted at the given AOI to transmitted light at normal incidence. Several models exist to calculate the IAM for a given incidence angle (e.g. pvlib.iam.ashrae(), pvlib.iam.martin_ruiz(), pvlib.iam.sapm(), pvlib.iam.physical()). However, evaluating the IAM for diffuse light is not as straightforward because it comes from all directions and therefore has a range of angles of incidence. Here we show how to integrate the effect of AOI reflection across this AOI range using the process described in 1. In particular, we will recreate Figures 3, 4, and 5 in that paper.



B. Marion “Numerical method for angle-of-incidence correction factors for diffuse radiation incident photovoltaic modules”, Solar Energy, Volume 147, Pages 344-348. 2017. DOI: 10.1016/j.solener.2017.03.027


Duffie, John A. & Beckman, William A. (2013). Solar Engineering of Thermal Processes. DOI: 10.1002/9781118671603

from pvlib.iam import marion_diffuse, physical
import numpy as np
import matplotlib.pyplot as plt

IAM Model#

The IAM model used to generate the figures in 1 uses Snell’s, Fresnel’s, and Beer’s laws to determine the fraction of light transmitted through the air-glass interface as a function of AOI. The function pvlib.iam.physical() implements this model, except it also includes an exponential term to model attenuation in the glazing layer. To be faithful to Marion’s implementation, we will disable this extinction term by setting the attenuation coefficient K parameter to zero. For more details on this IAM model, see 2.

Marion generated diffuse irradiance modifiers for two cases: a standard uncoated glass with index of refraction n=1.526 and a glass with anti-reflective (AR) coating with n=1.3. Comparing the IAM model across AOI recreates Figure 3 in 1:

aoi = np.arange(0, 91)
iam_no_coating = physical(aoi, n=1.526, K=0)
iam_ar_coating = physical(aoi, n=1.3, K=0)

plt.plot(aoi, iam_ar_coating, c='b', label='$F_b$, AR coated, n=1.3')
plt.plot(aoi, iam_no_coating, c='r', label='$F_b$, uncoated, n=1.526')
plt.xlabel(r'Angle-of-Incidence, AOI $(\degree)$')
plt.ylabel('Diffuse Incidence Angle Modifier')
plt.ylim([0, 1.2])
plot diffuse aoi correction

Diffuse sky, ground, and horizon IAM#

Now that we have an AOI model, we use pvlib.iam.marion_diffuse() to integrate it across solid angle and determine diffuse irradiance IAM. Marion defines three types of diffuse irradiance: sky, horizon, and ground-reflected. The diffuse IAM value is evaluated independently for each type.

tilts = np.arange(0, 91, 2.5)

# marion_diffuse calculates all three IAM values (sky, horizon, ground)
iam_no_coating = marion_diffuse('physical', tilts, n=1.526, K=0)
iam_ar_coating = marion_diffuse('physical', tilts, n=1.3, K=0)

First we recreate Figure 4 in 1, showing the dependence of the sky diffuse incidence angle modifier on module tilt.

plt.plot(tilts, iam_ar_coating['sky'], c='b', marker='^',
         label='$F_{sky}$, AR coated, n=1.3')
plt.plot(tilts, iam_no_coating['sky'], c='r', marker='x',
         label='$F_{sky}$, uncoated, n=1.526')
plt.ylim([0.9, 1.0])
plt.xlabel(r'PV Module Tilt, $\beta (\degree)$')
plt.ylabel('Diffuse Incidence Angle Modifier')
plot diffuse aoi correction

Now we recreate Figure 5 in 1, showing the dependence of the diffuse iam values for horizon and ground diffuse irradiance on module tilt. Note that pvlib.iam.marion_diffuse() defaults to using 1800 points for the horizon case (instead of 180 like the others) to match 1.

plt.plot(tilts, iam_ar_coating['horizon'], c='b', marker='^',
         label='$F_{hor}$, AR coated, n=1.3')
plt.plot(tilts, iam_no_coating['horizon'], c='r', marker='x',
         label='$F_{hor}$, uncoated, n=1.526')
plt.plot(tilts, iam_ar_coating['ground'], c='b', marker='s',
         label='$F_{grd}$, AR coated, n=1.3')
plt.plot(tilts, iam_no_coating['ground'], c='r', marker='+',
         label='$F_{grd}$, uncoated, n=1.526')
plt.xlabel(r'PV Module Tilt, $\beta (\degree)$')
plt.ylabel('Diffuse Incidence Angle Modifier')
plot diffuse aoi correction

Total running time of the script: (0 minutes 0.981 seconds)

Gallery generated by Sphinx-Gallery