Skip to content

Commit 0d4652f

Browse files
committed
Add agrivPV gallery example
1 parent d4dbe55 commit 0d4652f

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
AgriPV with infinite sheds
3+
==========================
4+
5+
Irradiance at crop level between rows
6+
"""
7+
8+
# %%
9+
# This example demonstrates how to calculate irradiance at crop level
10+
# for an agriPV system using pvlib's infinite sheds model.
11+
# For an overview of agrivPV concepts and performance, the reader
12+
# is referred to :doi:`10.69766/XAEU5008`.
13+
14+
# The first steps is to define the plant location and calculate solar position
15+
# and clearsky irradiance for a single day as an example.
16+
#
17+
# .. figure:: ../../_images/agrivoltaics_system.jpg
18+
# :align: center
19+
# :width: 75%
20+
# :alt: Photo of an agriPV system
21+
#
22+
# Photo of a agriPV system
23+
# *Source: Adam R. Jensen*
24+
25+
import pvlib
26+
import pandas as pd
27+
import matplotlib.pyplot as plt
28+
29+
location = pvlib.location.Location(latitude=55, longitude=10)
30+
31+
times = pd.date_range('2020-06-28', periods=24*60, freq='1min', tz='UTC')
32+
33+
solpos = location.get_solarposition(times)
34+
35+
clearsky = location.get_clearsky(times, model='ineichen')
36+
37+
# %%
38+
# Next, we need to define the plant layout:
39+
40+
height = 3 # []
41+
pitch = 12 # [m] row spacing
42+
row_width = 2 * 2 # [m] two modules in portrait, each 2 m long
43+
gcr = row_width / pitch # [-]
44+
axis_azimuth = 0 # [degrees] north-south tracking axis
45+
max_angle = 50 # [degrees] maximum rotation angle
46+
47+
# %%
48+
# Before running the infinite sheds model, we need to know the orientation
49+
# of the trackers. For single-axis tracker, this can be calculated as follows:
50+
51+
tracking_orientations = pvlib.tracking.singleaxis(
52+
apparent_zenith=solpos['apparent_zenith'],
53+
apparent_azimuth=solpos['azimuth'],
54+
axis_azimuth=axis_azimuth,
55+
max_angle=max_angle,
56+
backtrack=True,
57+
gcr=gcr,
58+
)
59+
60+
# %%
61+
# For agrivPV systems, the local albedo is dependent on crop growth and thus
62+
# changes throughout the seasons. In this example, we only simulate one
63+
# day and thus use a constant value. Similarly, we will assume a constant
64+
# air temperature to avoid getting external data.
65+
66+
albedo = 0.25 # [unitless]
67+
temp_air = 18 # [degrees C]
68+
69+
# %%
70+
# Now, we are ready to calculate the front and read-side irradiance using
71+
# the pvlib infinite sheds model.
72+
73+
dni_extra = pvlib.irradiance.get_extra_radiation(times)
74+
75+
irradiance = pvlib.bifacial.infinite_sheds.get_irradiance(
76+
surface_tilt=tracking_orientations['surface_tilt'],
77+
surface_azimuth=tracking_orientations['surface_azimuth'],
78+
solar_zenith=solpos['apparent_zenith'],
79+
solar_azimuth=solpos['azimuth'],
80+
gcr=gcr,
81+
height=height,
82+
pitch=pitch,
83+
ghi=clearsky['ghi'],
84+
dhi=clearsky['dhi'],
85+
dni=clearsky['dni'],
86+
albedo=albedo,
87+
model='haydavies',
88+
dni_extra=dni_extra,
89+
bifaciality=0.8,
90+
)
91+
92+
# %%
93+
# Once the in-plane irradiance is known, we can estimate the power output.
94+
# For simplicity, we use the PVWatts model:
95+
96+
N_tables = 108
97+
modules_per_table = 14
98+
pdc0_per_module = 380 # [W] STC rating
99+
pdc0 = pdc0_per_module * modules_per_table * N_tables
100+
101+
gamma_pdc = -0.004 # [1/degrees C]
102+
103+
temp_cell = pvlib.temperature.faiman(
104+
poa_global=irradiance['poa_global'],
105+
temp_air=temp_air,
106+
)
107+
108+
power_dc = pvlib.pvsystem.pvwatts_dc(
109+
g_poa_effective=irradiance['poa_global'],
110+
temp_cell=temp_cell,
111+
pdc0=pdc0,
112+
gamma_pdc=gamma_pdc)
113+
114+
power_dc.divide(1000).plot()
115+
plt.ylabel('DC power [kW]')
116+
plt.show()
117+
118+
# %%
119+
# In addition to the power output of the PV array, we are also interested
120+
# in how much irradiance reaches the crops under the array. In this case
121+
# we calculate the average irradiance using the infinite sheds utility
122+
# functions.
123+
#
124+
# This consists of two parts. First we determine the diffuse irradiance on
125+
# ground and second we calculate the fraction of the ground that is unshaded
126+
# (i.e., receives DNI).
127+
128+
from pvlib.tools import cosd
129+
130+
vf_ground_sky = pvlib.bifacial.utils.vf_ground_sky_2d_integ(
131+
surface_tilt=tracking_orientations['surface_tilt'],
132+
gcr=gcr,
133+
height=height,
134+
pitch=pitch,
135+
)
136+
137+
unshaded_ground_fraction = pvlib.bifacial.utils._unshaded_ground_fraction(
138+
surface_tilt=tracking_orientations['surface_tilt'],
139+
surface_azimuth=tracking_orientations['surface_azimuth'],
140+
solar_zenith=solpos['apparent_zenith'],
141+
solar_azimuth=solpos['azimuth'],
142+
gcr=gcr,
143+
)
144+
145+
crop_avg_irradiance = (
146+
unshaded_ground_fraction * clearsky['dni'] * cosd(solpos['apparent_zenith'])
147+
+ vf_ground_sky * clearsky['dhi'])
148+
149+
fig, ax = plt.subplots()
150+
clearsky['ghi'].plot(ax=ax, label='Horizontal irradiance above panels')
151+
crop_avg_irradiance.plot(ax=ax, label='Horizontal irradiance at crop level')
152+
ax.legend(loc='upper center')
153+
ax.set_ylabel('Irradiance [W/m$^2$]')
154+
ax.set_ylim(-10, 1050)
155+
plt.show()
1.58 MB
Loading

0 commit comments

Comments
 (0)