-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Agrivoltaics - PAR diffuse fraction model #2048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
90dd1e0
8cc7ffc
ae63127
5880453
5af860a
bdc2303
aec7870
02ebe81
c5d133d
149961e
eff3a25
d18827e
18619a4
feded95
b9aa23e
1c6d416
899844c
9268a44
bce50b6
2825e2c
764bf8b
0e0f836
0d23fe0
a7c84b5
d1bb64b
eeef154
6082fe7
94df738
ac9d45c
77f3ba8
a6c3c6b
f5449d7
bafe63b
1a5d0d9
189efe5
7c28be6
59faee0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Agrivoltaic Systems Modelling | ||
----------------------------- |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,145 @@ | ||||||
""" | ||||||
Calculating the diffuse PAR using Spitter's relationship | ||||||
========================================================= | ||||||
|
||||||
This example demonstrates how to calculate the diffuse photosynthetically | ||||||
active radiation (PAR) using Spitter's relationship. | ||||||
""" | ||||||
|
||||||
# %% | ||||||
# The photosynthetically active radiation (PAR) is a key component in the | ||||||
# photosynthesis process of plants. As in photovoltaic systems, the PAR is | ||||||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# divided into direct and diffuse components. The diffuse fraction of PAR | ||||||
# with respect to the total PAR is important in agrivoltaic systems, where | ||||||
# crops are grown under solar panels. The diffuse fraction of PAR can be | ||||||
# calculated using the Spitter's relationship [1]_ implemented in | ||||||
# :py:func:`~pvlib.par.spitters_relationship`. | ||||||
# This model requires the solar zenith angle and the fraction of the global | ||||||
# radiation that is diffuse as inputs. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To (perhaps) head off a discussion of whether "radiation" or "irradiance" should be used here: radiation is usually used when referring to the source, and irradiance (or irradiation) when referring to the receiver. So the sun emits radiation, and a tilted plane on the earth's surface receives solar irradiation. Either perspective seems OK to me in the context of this PR. But mixing PAR (..."radiation") with "irradiance" may be confusing to some. |
||||||
# | ||||||
# .. note:: | ||||||
# Understanding the distinction between the global radiation and the PAR is | ||||||
# a key concept. The global radiation is the total amount of solar radiation | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is global radiation limited to any particular bandwidth in [1]? If it is not, then I'd describe it as "broadband solar radiation" and not mention PV at all. Mentioning PV may imply to some that the global solar radiation means the radiation in the photovoltaic band, usually 400-1100nm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only see:
but no mention of the bandwidth. |
||||||
# that is usually accounted for in PV applications, while the PAR is a | ||||||
# measurement of a narrower range of wavelengths that are used in | ||||||
# photosynthesis. See section on *Photosynthetically Active Radiation* in | ||||||
# pp. 222-223 of [1]_. | ||||||
# | ||||||
# The key function used in this example is | ||||||
# :py:func:`pvlib.par.spitters_relationship` to calculate the diffuse PAR | ||||||
# fraction, as a function of global diffuse fraction and solar zenith. | ||||||
# | ||||||
# References | ||||||
# ---------- | ||||||
# .. [1] C. J. T. Spitters, H. A. J. M. Toussaint, and J. Goudriaan, | ||||||
# 'Separating the diffuse and direct component of global radiation and its | ||||||
# implications for modeling canopy photosynthesis Part I. Components of | ||||||
# incoming radiation', Agricultural and Forest Meteorology, vol. 38, | ||||||
# no. 1, pp. 217-229, Oct. 1986, :doi:`10.1016/0168-1923(86)90060-2`. | ||||||
# | ||||||
# Read some example data | ||||||
# ^^^^^^^^^^^^^^^^^^^^^^ | ||||||
|
||||||
import pvlib | ||||||
import pandas as pd | ||||||
import matplotlib.pyplot as plt | ||||||
from matplotlib.dates import DateFormatter | ||||||
from pathlib import Path | ||||||
|
||||||
# Read some sample data | ||||||
DATA_FILE = Path(pvlib.__path__[0]).joinpath("data", "723170TYA.CSV") | ||||||
|
||||||
tmy, metadata = pvlib.iotools.read_tmy3( | ||||||
DATA_FILE, coerce_year=1990, map_variables=True | ||||||
) | ||||||
tmy = tmy.filter( | ||||||
["ghi", "dhi", "dni", "pressure", "temp_air"] | ||||||
) # remaining data is not needed | ||||||
tmy = tmy[ | ||||||
"1990-04-11T06":"1990-04-11T22" | ||||||
] # select a single day for this example | ||||||
|
||||||
solar_position = pvlib.solarposition.get_solarposition( | ||||||
# TMY timestamp is at end of hour, so shift to center of interval | ||||||
tmy.index.shift(freq="-30T"), | ||||||
latitude=metadata["latitude"], | ||||||
longitude=metadata["longitude"], | ||||||
altitude=metadata["altitude"], | ||||||
pressure=tmy["pressure"] * 100, # convert from millibar to Pa | ||||||
temperature=tmy["temp_air"], | ||||||
) | ||||||
solar_position.index = tmy.index # reset index to end of the hour | ||||||
|
||||||
# %% | ||||||
# Calculate Photosynthetically Active Radiation | ||||||
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||||
# The total PAR can be approximated as 0.50 times the global horizontal | ||||||
# irradiance (GHI) for solar elevation higher that :math:`10^\circ`. | ||||||
# See section on *Photosynthetically Active Radiation* in pp. 222-223 of [1]_. | ||||||
|
||||||
par = pd.DataFrame({"total": 0.50 * tmy["ghi"]}, index=tmy.index) | ||||||
|
||||||
# Calculate global irradiance diffuse fraction, input of the Spitter's model | ||||||
tmy["diffuse_fraction"] = tmy["dhi"] / tmy["ghi"] | ||||||
|
||||||
# Calculate diffuse PAR fraction using Spitter's relationship | ||||||
par["diffuse_fraction"] = pvlib.par.spitters_relationship( | ||||||
solar_position["zenith"], tmy["diffuse_fraction"] | ||||||
) | ||||||
|
||||||
# Finally, calculate the diffuse PAR | ||||||
par["diffuse"] = par["total"] * par["diffuse_fraction"] | ||||||
par[solar_position["zenith"] > 80] = ( | ||||||
0 # set to zero for elevation < 10 degrees | ||||||
) | ||||||
|
||||||
# %% | ||||||
# Plot the results | ||||||
# ^^^^^^^^^^^^^^^^ | ||||||
# Irradiances on left axis, diffuse fractions on right axis | ||||||
|
||||||
fig, ax_l = plt.subplots(figsize=(12, 6)) | ||||||
ax_l.set( | ||||||
xlabel="Time", | ||||||
ylabel="Irradiance $[W/m^2]$", | ||||||
title="Diffuse PAR using Spitter's relationship", | ||||||
) | ||||||
ax_l.xaxis.set_major_formatter(DateFormatter("%H:%M", tz=tmy.index.tz)) | ||||||
ax_l.plot(tmy.index, tmy["ghi"], label="Global: total", color="deepskyblue") | ||||||
ax_l.plot( | ||||||
tmy.index, | ||||||
tmy["dhi"], | ||||||
label="Global: diffuse", | ||||||
color="skyblue", | ||||||
linestyle="-.", | ||||||
) | ||||||
ax_l.plot(tmy.index, par["total"], label="PAR: total", color="orangered") | ||||||
ax_l.plot( | ||||||
tmy.index, | ||||||
par["diffuse"], | ||||||
label="PAR: diffuse", | ||||||
color="coral", | ||||||
linestyle="-.", | ||||||
) | ||||||
ax_l.grid() | ||||||
|
||||||
ax_r = ax_l.twinx() | ||||||
ax_r.set(ylabel="Diffuse fraction") | ||||||
ax_r.plot( | ||||||
tmy.index, | ||||||
tmy["diffuse_fraction"], | ||||||
label="Global diffuse fraction", | ||||||
color="plum", | ||||||
linestyle=":", | ||||||
) | ||||||
ax_r.plot( | ||||||
tmy.index, | ||||||
par["diffuse_fraction"], | ||||||
label="PAR diffuse fraction", | ||||||
color="chocolate", | ||||||
linestyle=":", | ||||||
) | ||||||
|
||||||
lines = ax_l.get_lines() + ax_r.get_lines() | ||||||
plt.legend(lines, (line.get_label() for line in lines)) | ||||||
plt.show() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.. currentmodule:: pvlib | ||
|
||
Agrivoltaic Systems Modelling | ||
============================= | ||
|
||
.. autosummary:: | ||
:toctree: generated/ | ||
|
||
par.spitters_relationship |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,4 @@ API reference | |
bifacial | ||
scaling | ||
location | ||
agrivoltaics |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
ivtools, | ||
location, | ||
modelchain, | ||
par, | ||
pvarray, | ||
pvsystem, | ||
scaling, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
""" | ||
Photosynthetically Active Radiation (PAR) module. | ||
Utilities found here are specially interesting for agrivoltaic systems. | ||
""" | ||
|
||
from pvlib.tools import cosd, sind | ||
|
||
|
||
def spitters_relationship(solar_zenith, global_diffuse_fraction): | ||
r""" | ||
Derive the diffuse fraction of photosynthetically active radiation (PAR) | ||
respect to the global radiation diffuse fraction. | ||
|
||
The relationship is based on the work of Spitters et al. (1986) [1]_. | ||
|
||
Parameters | ||
---------- | ||
solar_zenith : numeric | ||
Solar zenith angle in degrees :math:`^{\circ}`. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
global_diffuse_fraction : numeric | ||
Fraction of the global radiation that is diffuse. Unitless. | ||
|
||
Returns | ||
------- | ||
par_diffuse_fraction : numeric | ||
Photosynthetically active radiation in W/m^2. | ||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Notes | ||
----- | ||
The relationship is given by equations (9) & (10) in [1]_ and (1) in [2]_: | ||
|
||
.. math:: | ||
|
||
k_{diffuse\_PAR}^{model} = \frac{PAR_{diffuse}}{PAR_{total}} = | ||
\frac{\left[1 + 0.3 \left(1 - \left(k_d^{model}\right) ^2\right)\right] | ||
k_d^{model}} | ||
{1 + \left(1 - \left(k_d^{model}\right)^2\right) \cos ^2 (90 - \beta) | ||
\cos ^3 \beta} | ||
|
||
where :math:`k_d^{model}` is the diffuse fraction of the global radiation, | ||
provided by some model. | ||
|
||
A comparison of different models performance for the diffuse fraction of | ||
the global irradiance can be found in [2]_ in the context of Sweden. | ||
|
||
References | ||
---------- | ||
.. [1] C. J. T. Spitters, H. A. J. M. Toussaint, and J. Goudriaan, | ||
'Separating the diffuse and direct component of global radiation and its | ||
implications for modeling canopy photosynthesis Part I. Components of | ||
incoming radiation', Agricultural and Forest Meteorology, vol. 38, | ||
no. 1, pp. 217-229, Oct. 1986, :doi:`10.1016/0168-1923(86)90060-2`. | ||
.. [2] S. Ma Lu et al., 'Photosynthetically active radiation decomposition | ||
models for agrivoltaic systems applications', Solar Energy, vol. 244, | ||
pp. 536-549, Sep. 2022, :doi:`10.1016/j.solener.2022.05.046`. | ||
""" | ||
# notation change: | ||
# cosd(90-x) = sind(x) and 90-solar_elevation = solar_zenith | ||
sind_solar_zenith = sind(solar_zenith) | ||
cosd_solar_elevation = cosd(90 - solar_zenith) | ||
par_diffuse_fraction = ( | ||
(1 + 0.3 * (1 - global_diffuse_fraction**2)) | ||
* global_diffuse_fraction | ||
/ ( | ||
1 | ||
+ (1 - global_diffuse_fraction**2) | ||
* sind_solar_zenith**2 | ||
* cosd_solar_elevation**3 | ||
) | ||
) | ||
return par_diffuse_fraction |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
""" | ||
Test Photosynthetically Active Radiation (PAR) submodule. | ||
""" | ||
|
||
from pvlib import par | ||
|
||
echedey-ls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import numpy as np | ||
from numpy.testing import assert_allclose | ||
|
||
|
||
def test_spitters_relationship(): | ||
solar_zenith, global_diffuse_fraction = np.meshgrid( | ||
[90, 85, 75, 60, 40, 30, 10, 0], [0.01, 0.1, 0.3, 0.6, 0.8, 0.99] | ||
) | ||
solar_zenith = solar_zenith.ravel() | ||
global_diffuse_fraction = global_diffuse_fraction.ravel() | ||
result = par.spitters_relationship(solar_zenith, global_diffuse_fraction) | ||
expected = np.array([ | ||
0.00650018, 0.00656213, 0.00706211, 0.00874170, 0.01171437, 0.01260581, | ||
0.01299765, 0.01299970, 0.06517588, 0.06579393, 0.07077986, 0.08750105, | ||
0.11699064, 0.12580782, 0.12967973, 0.12970000, 0.19994764, 0.20176275, | ||
0.21635259, 0.26460255, 0.34722693, 0.37134002, 0.38184514, 0.38190000, | ||
0.43609756, 0.43933488, 0.46497584, 0.54521789, 0.66826809, 0.70117647, | ||
0.71512774, 0.71520000, 0.65176471, 0.65503875, 0.68042968, 0.75414541, | ||
0.85271445, 0.87653894, 0.88634962, 0.88640000, 0.97647838, 0.97683827, | ||
0.97952006, 0.98634857, 0.99374028, 0.99529135, 0.99590717, 0.9959103 | ||
]) | ||
assert_allclose(result, expected, atol=1e-8) |
Uh oh!
There was an error while loading. Please reload this page.