-
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
Merged
AdamRJensen
merged 37 commits into
pvlib:main
from
echedey-ls:par-diffuse-fraction-of-global-par
Jun 20, 2024
Merged
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
90dd1e0
New PAR module with spitters_relationship
echedey-ls 8cc7ffc
Merge branch 'main' into par-diffuse-fraction-of-global-par
echedey-ls ae63127
Update v0.11.0.rst
echedey-ls 5880453
linter
echedey-ls 5af860a
API update
echedey-ls bdc2303
Example rendering
echedey-ls aec7870
Update test_par.py
echedey-ls 02ebe81
Apply suggestions from code review (Adam)
echedey-ls c5d133d
Update par.py
echedey-ls 149961e
Move function to spectrum (mismatch.py)
echedey-ls eff3a25
Improve units formatting
echedey-ls d18827e
Split legends
echedey-ls 18619a4
Remove api page, move to spectrum index
echedey-ls feded95
Update v0.11.0.rst
echedey-ls b9aa23e
Merge branch 'main' into par-diffuse-fraction-of-global-par
echedey-ls 1c6d416
Move to ``irradiance.py``
echedey-ls 899844c
Flake8 :knife:
echedey-ls 9268a44
Fix trigonometry - double testing with a spreadsheet
echedey-ls bce50b6
Move section of PAR
echedey-ls 2825e2c
Merge branch 'main' into par-diffuse-fraction-of-global-par
echedey-ls 764bf8b
I should read more carefully
echedey-ls 0e0f836
Update decomposition.rst
echedey-ls 0d23fe0
Apply suggestions from code review (Cliff)
echedey-ls a7c84b5
Merge branch 'par-diffuse-fraction-of-global-par' of https://github.c…
echedey-ls d1bb64b
More docs refurbishment
echedey-ls eeef154
Rename to `diffuse_par_spitters`
echedey-ls 6082fe7
`global` -> `broadband`
echedey-ls 94df738
Merge branch 'main' into par-diffuse-fraction-of-global-par
echedey-ls ac9d45c
Code review from Adam, first batch
echedey-ls 77f3ba8
Apply trigonometric property
echedey-ls a6c3c6b
Merge branch 'main' into par-diffuse-fraction-of-global-par
echedey-ls f5449d7
Fix merge - linter
echedey-ls bafe63b
Forgot to apply this comment
echedey-ls 1a5d0d9
Remove model from eq
echedey-ls 189efe5
Dailies, insolation instead of instant, irradiance values
echedey-ls 7c28be6
More docs refurbishment
echedey-ls 59faee0
Review from Cliff
AdamRJensen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Agrivoltaic Systems Modelling | ||
----------------------------- |
161 changes: 161 additions & 0 deletions
161
docs/examples/agrivoltaics/plot_diffuse_PAR_Spitters_relationship.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
""" | ||
Calculating daily diffuse PAR using Spitter's relationship | ||
========================================================== | ||
|
||
This example demonstrates how to calculate the diffuse photosynthetically | ||
active radiation (PAR) from diffuse fraction of broadband insolation. | ||
""" | ||
|
||
# %% | ||
# The photosynthetically active radiation (PAR) is a key metric in quantifying | ||
# the photosynthesis process of plants. As with broadband irradiance, PAR can | ||
# be 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.irradiance.diffuse_par_spitters`. | ||
# This model requires the average daily solar zenith angle and the | ||
# daily fraction of the broadband insolation that is diffuse as inputs. | ||
# | ||
# .. note:: | ||
# Understanding the distinction between the broadband insolation and the PAR | ||
# is a key concept. Broadband insolation is the total amount of solar | ||
# energy that gets to a surface, often used in PV applications, while PAR | ||
# is a measurement of a narrower spectrum of wavelengths that are involved | ||
# in photosynthesis. See section on *Photosynthetically Active insolation* | ||
# in pp. 222-223 of [1]_. | ||
# | ||
# 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 | ||
# ^^^^^^^^^^^^^^^^^^^^^^ | ||
# Let's read some weather data from a TMY3 file and calculate the solar | ||
# position. | ||
|
||
import pvlib | ||
import pandas as pd | ||
import matplotlib.pyplot as plt | ||
from matplotlib.dates import AutoDateLocator, ConciseDateFormatter | ||
from pathlib import Path | ||
|
||
# Datafile found in the pvlib distribution | ||
DATA_FILE = Path(pvlib.__path__[0]).joinpath("data", "723170TYA.CSV") | ||
|
||
tmy, metadata = pvlib.iotools.read_tmy3( | ||
DATA_FILE, coerce_year=2002, map_variables=True | ||
) | ||
tmy = tmy.filter( | ||
["ghi", "dhi", "dni", "pressure", "temp_air"] | ||
) # remaining columns are not needed | ||
tmy = tmy["2002-09-06":"2002-09-21"] # select some days | ||
|
||
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 daily values | ||
# ^^^^^^^^^^^^^^^^^^^^^^ | ||
# The daily average solar zenith angle and the daily diffuse fraction of | ||
# broadband insolation are calculated as follows: | ||
|
||
daily_solar_zenith = solar_position["zenith"].resample("D").mean() | ||
# integration over the day with a time step of 1 hour | ||
daily_tmy = tmy[["ghi", "dhi"]].resample("D").sum() * 1 | ||
daily_tmy["diffuse_fraction"] = daily_tmy["dhi"] / daily_tmy["ghi"] | ||
|
||
# %% | ||
# Calculate Photosynthetically Active Radiation | ||
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
# The total PAR can be approximated as 0.50 times the broadband horizontal | ||
# insolation (integral of GHI) for an average solar elevation higher that 10°. | ||
# See section on *Photosynthetically Active Radiation* in pp. 222-223 of [1]_. | ||
|
||
par = pd.DataFrame({"total": 0.50 * daily_tmy["ghi"]}, index=daily_tmy.index) | ||
if daily_solar_zenith.min() < 10: | ||
raise ValueError( | ||
"The total PAR can't be assumed to be half the broadband insolation " | ||
+ "for average zenith angles lower than 10°." | ||
) | ||
|
||
# Calculate broadband insolation diffuse fraction, input of the Spitter's model | ||
daily_tmy["diffuse_fraction"] = daily_tmy["dhi"] / daily_tmy["ghi"] | ||
|
||
# Calculate diffuse PAR fraction using Spitter's relationship | ||
par["diffuse_fraction"] = pvlib.irradiance.diffuse_par_spitters( | ||
solar_position["zenith"], daily_tmy["diffuse_fraction"] | ||
) | ||
|
||
# Finally, calculate the diffuse PAR | ||
par["diffuse"] = par["total"] * par["diffuse_fraction"] | ||
|
||
# %% | ||
# Plot the results | ||
# ^^^^^^^^^^^^^^^^ | ||
# Insolation on left axis, diffuse fraction on right axis | ||
|
||
fig, ax_l = plt.subplots(figsize=(12, 6)) | ||
ax_l.set( | ||
xlabel="Time", | ||
ylabel="Daily insolation $[Wh/m^2/day]$", | ||
title="Diffuse PAR using Spitter's relationship", | ||
) | ||
ax_l.xaxis.set_major_formatter( | ||
ConciseDateFormatter(AutoDateLocator(), tz=daily_tmy.index.tz) | ||
) | ||
ax_l.plot( | ||
daily_tmy.index, | ||
daily_tmy["ghi"], | ||
label="Broadband: total", | ||
color="deepskyblue", | ||
) | ||
ax_l.plot( | ||
daily_tmy.index, | ||
daily_tmy["dhi"], | ||
label="Broadband: diffuse", | ||
color="skyblue", | ||
linestyle="-.", | ||
) | ||
ax_l.plot(daily_tmy.index, par["total"], label="PAR: total", color="orangered") | ||
ax_l.plot( | ||
daily_tmy.index, | ||
par["diffuse"], | ||
label="PAR: diffuse", | ||
color="coral", | ||
linestyle="-.", | ||
) | ||
ax_l.grid() | ||
ax_l.legend(loc="upper left") | ||
|
||
ax_r = ax_l.twinx() | ||
ax_r.set(ylabel="Diffuse fraction") | ||
ax_r.plot( | ||
daily_tmy.index, | ||
daily_tmy["diffuse_fraction"], | ||
label="Broadband diffuse fraction", | ||
color="plum", | ||
linestyle=":", | ||
) | ||
ax_r.plot( | ||
daily_tmy.index, | ||
par["diffuse_fraction"], | ||
label="PAR diffuse fraction", | ||
color="chocolate", | ||
linestyle=":", | ||
) | ||
ax_r.legend(loc="upper right") | ||
|
||
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.