Skip to content

Commit e711e9e

Browse files
AdamRJensencwhansekandersolar
authored
Add agrivPV gallery example (#2459)
* Add agrivPV gallery example * Update v0.12.1.rst * Fix rendering * Fix spelling mistakes * Fix typo * Apply suggestions from code review Co-authored-by: Cliff Hansen <[email protected]> * Fix stickler * Improve intro text * Implement feedback from Ioannis * Update input values to match European Energy's site * Fix linter * Break url in multiple lines * Update thumbnail path Co-authored-by: Kevin Anderson <[email protected]> --------- Co-authored-by: Cliff Hansen <[email protected]> Co-authored-by: Kevin Anderson <[email protected]>
1 parent 904faee commit e711e9e

File tree

3 files changed

+167
-2
lines changed

3 files changed

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

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ Documentation
5555
(:issue:`2423`, :pull:`2426`)
5656
* Clarify which SAPM coefficients are required by the ``module`` parameter in
5757
:py:func:`~pvlib.pvsystem.sapm` (:issue:`2392`, :pull:`2435`)
58-
* Update references in :py:func:`~pvlib.irradiance.get_extra_radiation`
59-
(:issue:`2333`, :pull:`2437`)
58+
* Add gallery example on calculating irradiance at crop level for agriPV systems.
59+
(:pull:`2459`)
60+
* Update references in :py:func`~pvlib.irradiance.get_extra_radiation`
61+
(:issue:`2333`, :pull:`2347`)
6062
* Update references in :py:func:`~pvlib.iotools.get_cams` and :py:func:`~pvlib.iotools.read_cams`
6163
(:issue:`2427`, :pull:`2457`)
6264
* Fix ``Edit on GitHub`` links in stable documentation so they point to the tagged repository version matching the build environment (e.g., v0.12.0). (:issue:`2456`, :pull:`2460`)

0 commit comments

Comments
 (0)