# Import modules
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
66 Solar Radiation
Solar radiation is the lifeblood of agriculture, driving the fundamental processes of photosynthesis and plant growth. Measuring solar irradiance is therefore essential for estiamting yield potential and modeling agricultural crops. The incident solar radiation on given location is highly influenced by the latitudinal position. In this exercise we examine a method to derive the estimated maximum solar irradiance for each day of the year from a time series of solar radiation observations. Before we start, there are a few terms that need to be defined:
Extraterrestrial solar irradiance: This is the solar radiation received outside the Earth’s atmosphere on a surface perpendicular to the sun’s rays. It represents the maximum potential solar energy that could be received by the planet without atmospheric interference.
Clear sky solar irradiance: This refers to the amount of solar radiation that reaches the Earth’s surface when the sky is clear, without clouds. It is the solar irradiance under ideal clear conditions, accounting for the scattering and absorption by the atmospheric components but excluding the impact of clouds.
Solar irradiance observed at the land surface: This is the actual solar radiation that reaches the ground, taking into account cloud cover, air pollution, and the angle of the sun. This measurement reflects the true solar energy available for crops at specific locations and times.
# Load dataset
= pd.read_csv("../datasets/KS_Manhattan_6_SSW.csv", na_values=[-9999])
df df.head()
WBANNO | LST_DATE | CRX_VN | LONGITUDE | LATITUDE | T_DAILY_MAX | T_DAILY_MIN | T_DAILY_MEAN | T_DAILY_AVG | P_DAILY_CALC | ... | SOIL_MOISTURE_5_DAILY | SOIL_MOISTURE_10_DAILY | SOIL_MOISTURE_20_DAILY | SOIL_MOISTURE_50_DAILY | SOIL_MOISTURE_100_DAILY | SOIL_TEMP_5_DAILY | SOIL_TEMP_10_DAILY | SOIL_TEMP_20_DAILY | SOIL_TEMP_50_DAILY | SOIL_TEMP_100_DAILY | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 53974 | 20031001 | 1.201 | -96.61 | 39.1 | NaN | NaN | NaN | NaN | NaN | ... | -99.0 | -99.0 | -99.0 | -99 | -99 | NaN | NaN | NaN | NaN | NaN |
1 | 53974 | 20031002 | 1.201 | -96.61 | 39.1 | 18.9 | 2.5 | 10.7 | 11.7 | 0.0 | ... | -99.0 | -99.0 | -99.0 | -99 | -99 | NaN | NaN | NaN | NaN | NaN |
2 | 53974 | 20031003 | 1.201 | -96.61 | 39.1 | 22.6 | 8.1 | 15.4 | 14.8 | 0.0 | ... | -99.0 | -99.0 | -99.0 | -99 | -99 | NaN | NaN | NaN | NaN | NaN |
3 | 53974 | 20031004 | 1.201 | -96.61 | 39.1 | 22.6 | 3.8 | 13.2 | 14.0 | 0.0 | ... | -99.0 | -99.0 | -99.0 | -99 | -99 | NaN | NaN | NaN | NaN | NaN |
4 | 53974 | 20031005 | 1.201 | -96.61 | 39.1 | 25.0 | 10.6 | 17.8 | 17.3 | 0.0 | ... | -99.0 | -99.0 | -99.0 | -99 | -99 | NaN | NaN | NaN | NaN | NaN |
5 rows × 28 columns
# Convert date string to pandas datetime format
"LST_DATE"] = pd.to_datetime(df["LST_DATE"], format="%Y%m%d")
df["LST_DATE"].head() # Check our conversion. df[
0 2003-10-01
1 2003-10-02
2 2003-10-03
3 2003-10-04
4 2003-10-05
Name: LST_DATE, dtype: datetime64[ns]
# Add year, month, and day of the year to summarize data in future steps.
"YEAR"] = df["LST_DATE"].dt.year
df["MONTH"] = df["LST_DATE"].dt.month
df["DOY"] = df["LST_DATE"].dt.dayofyear df[
Inspect timeseries
# Observe trends in solar radiation data
=(12,4))
plt.figure(figsize"LST_DATE"], df["SOLARAD_DAILY"], linewidth=0.5)
plt.plot(df["Solar radiation (MJ/m$^2$/day)" )
plt.ylabel( plt.show()
Clear sky solar irradiance: empirical method
To approximate the clear sky solar irradiance we will select the highest records from our observations. To do this we will use a moving/rolling percentile filter. Alternatively, we can use the .max()
function instead of the .quantile(0.99)
function.
# clear sky solar radiation from observations
"Rso_obs"] = df["SOLARAD_DAILY"].rolling(window=15, center=True).quantile(0.99)
df[
# Observe trends in solar radiation data
=(12,4))
plt.figure(figsize"LST_DATE"], df["SOLARAD_DAILY"], '-k', alpha=0.25)
plt.plot(df["LST_DATE"], df["Rso_obs"], '-r')
plt.plot(df["Solar radiation (MJ/m$^2$/day)" )
plt.ylabel( plt.show()
Clear sky solar irradiance: latitude and elevation method
Another alternative is to compute the clear sky solar irradiance based on the latitude and elevation of the location of interest. The first step consists of computing the extraterrestrial radiation for daily periods as defined in Eq. 21 of the FAO-56 manual, and in a subsequent step we compute the clear sky solar radiation.
Ra = 24(60)/\pi \hspace{2mm}Gsc \hspace{2mm} dr(\omega\sin(\phi)\sin(\delta)+\cos(\phi)\cos(\delta)\sin(\omega))
Ra = extraterrestrial radiation (MJ / m2 /day)
Gsc = 0.0820 solar constant (MJ/m2/min)
dr = 1 + 0.033\cos(\frac{2\pi J}{365}) is the inverse relative distance Earth-Sun
J = day of the year
\phi = \pi/180 Lat latitude in radians
\delta = 0.409\sin((2\pi J/365)-1.39)\hspace{5mm} is the solar decimation (rad)
\omega = \pi/2-(\arccos(-\tan(\phi)\tan(\delta)) \hspace{5mm} is the sunset hour angle (radians)
= 39.1949 # Latitude in decimal degrees (North)
latitude = 300 # elevation in meters above sea level
elevation = df["DOY"] # Use J to match manual notation
J
# Step 1: Extraterrestrial solar radiation
= np.pi/180 * latitude # Eq. 22, FAO-56
phi = 1 + 0.033 * np.cos(2*np.pi*J/365) # Eq. 23, FAO-56
dr = 0.409*np.sin((2*np.pi * J/365) - 1.39)
d = (np.arccos(-np.tan(phi)*np.tan(d)))
omega = 0.0820
Gsc = 24*(60)/np.pi * Gsc * dr * (omega*np.sin(phi)*np.sin(d) + np.cos(phi)*np.cos(d)*np.sin(omega))
Ra
# Step 2: Clear Sky Radiation: Rso (MJ/m2/day)
"Rso_lat"] = (0.75 + (2*10**-5)*elevation)*Ra # Eq. 37, FAO-56
df[
# Plot clear sky using latitude and elevation
=(12,4))
plt.figure(figsize"LST_DATE"], df["SOLARAD_DAILY"], '-k', alpha=0.25)
plt.plot(df["LST_DATE"], df["Rso_lat"], '-r', linewidth=2)
plt.plot(df["Solar radiation (MJ/m$^2$/day)" )
plt.ylabel( plt.show()
Actual solar irradiance from air temperature
# Clear sky from air temperature observations
"Rso_temp"] = np.minimum(0.16*Ra*(df["T_DAILY_MAX"]-df["T_DAILY_MIN"])**0.5, df['Rso_lat']) # Eq. 50, FAO-56
df[
=(12,4))
plt.figure(figsize"LST_DATE"], df["SOLARAD_DAILY"], '-k', alpha=0.25)
plt.plot(df["LST_DATE"], df["Rso_temp"], '-r', alpha=0.5)
plt.plot(df["Solar radiation (MJ/m$^2$/day)" )
plt.ylabel( plt.show()
Clear sky solar radiation for each DOY
# Summarize previous variables for each DOY
= df.groupby("DOY")[["SOLARAD_DAILY","Rso_temp","Rso_obs","Rso_lat"]].mean()
Rso_doy Rso_doy.head()
SOLARAD_DAILY | Rso_temp | Rso_obs | Rso_lat | |
---|---|---|---|---|
DOY | ||||
1 | 7.840714 | 7.915730 | 10.522923 | 10.834404 |
2 | 7.135000 | 8.016919 | 10.558046 | 10.876334 |
3 | 6.400769 | 8.236705 | 10.634077 | 10.921631 |
4 | 6.673077 | 6.975503 | 10.738108 | 10.970287 |
5 | 7.897692 | 7.595476 | 10.751031 | 11.022290 |
# Create figure comparing the three methods with the observed solar radaition data
=(8,4))
plt.figure(figsize"SOLARAD_DAILY"], '-k',label="Mean observed solar radiation")
plt.plot(Rso_doy.index, Rso_doy["Rso_temp"], '-g', label="Mean estimated solar radiation")
plt.plot(Rso_doy.index, Rso_doy[
"Rso_obs"], '--r', label="Clear sky observed solar radiation")
plt.plot(Rso_doy.index, Rso_doy["Rso_lat"], ':b', label="Clear sky estimated solar radiation")
plt.plot(Rso_doy.index, Rso_doy[
"Day of the year")
plt.xlabel("Solar radiation (MJ/m$^2$/day)")
plt.ylabel(
plt.legend() plt.show()