The term “self-shading” usually refers to adjacent rows blocking direct irradiance and casting shadows on each other. However, the concept also applies to diffuse irradiance because rows block a portion of the sky dome even when the sun is high in the sky. The irradiance loss fraction depends on how tightly the rows are packed and where on the module the loss is evaluated – a point near the top of edge of a module will see more of the sky than a point near the bottom edge.

This example uses the approach presented by Passias and Källbäck in 1 and recreates two figures from that paper using `pvlib.shading.masking_angle_passias()` and `pvlib.shading.sky_diffuse_passias()`.

## References#

1(1,2)

D. Passias and B. Källbäck, “Shading effects in rows of solar cell panels”, Solar Cells, Volume 11, Pages 281-291. 1984. DOI: 10.1016/0379-6787(84)90017-6

```from pvlib import shading, irradiance
import matplotlib.pyplot as plt
import numpy as np
```

First we’ll recreate Figure 4, showing how the average masking angle varies with array tilt and array packing. The masking angle of a given point on a module is the angle from horizontal to the next row’s top edge and represents the portion of the sky dome blocked by the next row. Because it changes from the bottom to the top of a module, the average across the module is calculated. In 1, `k` refers to the ratio of row pitch to row slant height (i.e. 1 / GCR).

```surface_tilt = np.arange(0, 90, 0.5)

plt.figure()
for k in [1, 1.5, 2, 2.5, 3, 4, 5, 7, 10]:
gcr = 1/k
plt.plot(surface_tilt, psi, label=f'k={k}')

plt.xlabel('Inclination angle [degrees]')
plt.legend()
plt.show()
```

So as the array is packed tighter (decreasing `k`), the average masking angle increases.

Next we’ll recreate Figure 5. Note that the y-axis here is the ratio of diffuse plane of array irradiance (after accounting for shading) to diffuse horizontal irradiance. This means that the deviation from 100% is due to the combination of self-shading and the fact that being at a tilt blocks off the portion of the sky behind the row. The first effect is modeled with `pvlib.shading.sky_diffuse_passias()` and the second with `pvlib.irradiance.isotropic()`.

```plt.figure()
for k in [1, 1.5, 2, 10]:
gcr = 1/k
relative_diffuse = transposition_ratio * (1-shading_loss) * 100  # %
plt.plot(surface_tilt, relative_diffuse, label=f'k={k}')

plt.xlabel('Inclination angle [degrees]')
As `k` decreases, GCR increases, so self-shading loss increases and collected diffuse irradiance decreases.