---
title: "The UV, VIS and NIR environment above vegetation"
subtitle: "Quantity, quality and timing in nature"
author:
- name: Pedro J. Aphalo
orcid: 0000-0003-3385-972X
email: pedro.aphalo@helsinki.fi
affiliation: University of Helsinki
- name: T. Matthew Robson
- name: Maxime Durand
- name: (Andreas Albert)
- name: (Lars Olof Björn)
- name: (Lasse Yliantilla)
contributor: "Andreas Albert, Lars Olof Björn, Lasse Ylianttila"
date: 2024-06-17
date-modified: 2024-07-17
format:
html:
embed-resources: false
editor: source
abstract: |
This chapter discusses the light environment of terrestrial plants.
---
::: callout-note
# First edition authors
{{< meta contributor >}}
:::
```{r, include=FALSE, message=FALSE}
library(dplyr)
library(photobiology)
library(photobiologyWavebands)
library(photobiologyInOut)
library(photobiologySun)
library(ggspectra)
library(ggpp)
library(ggpmisc)
library(gganimate)
library(ggdensity)
library(ggrepel)
library(quantreg)
library(lubridate)
library(patchwork)
theme_set(theme_minimal() +
theme(legend.position = "top",
plot.tag.position = c(0.1, 0.95),
plot.tag = element_text(hjust = 0, size = 10))
)
```
::: {.hidden}
$$
\newcommand{\Chem}[1]{{\mathrm{#1}}}
\newcommand{\Unit}[1]{{\mathrm{#1}}}
\newcommand{\mymu}{\mu}
\newcommand{\um}{\Unit{\mymu m}}
\newcommand{\ulitre}{\Unit{\mymu l}}
\newcommand{\ms}{\Unit{m\,s^{-1}}}
\newcommand{\umolflow}{\Unit{\mymu mol\,s^{-1}}}
\newcommand{\umol}{\Unit{\mymu mol\,m^{-2}\,s^{-1}}}
\newcommand{\molms}{\Unit{mol\,m^{-2}\,s^{-1}}}
\newcommand{\umolt}{\Unit{\frac{\mymu mol}{m^2\,s}}}
\newcommand{\umolnm}{\Unit{\mymu mol\,m^{-2}\,s^{-1}\,nm^{-1}}}
\newcommand{\mmol}{\Unit{mmol\,m^{-2}\,s^{-1}}}
\newcommand{\mmolt}{\Unit{\frac{mmol}{m^2\,s}}}
\newcommand{\mol}{\Unit{mol\,m^{-2}\,s^{-1}}}
\newcommand{\ppm}{\Unit{\mymu mol\,mol^{-1}}}
\newcommand{\ppmt}{\Unit{\frac{\mymu mol}{mol}}}
\newcommand{\mmolmol}{\Unit{mmol\,mol^{-1}}}
\newcommand{\mmolmolt}{\Unit{\frac{mmol}{mol}}}
\newcommand{\molday}{\Unit{mol\,m^{-2}\,d^{-1}}}
\newcommand{\kjday}{\Unit{kJ\,m^{-2}\,d^{-1}}}
\newcommand{\kjhour}{\Unit{kJ\,m^{-2}\,h^{-1}}}
\newcommand{\kjdaynm}{\Unit{kJ\,m^{-2}\,d^{-1}\,nm^{-1}}}
\newcommand{\kjmole}{\Unit{kJ\,mol^{-1}}}
\newcommand{\jsecond}{\Unit{J\,s}}
\newcommand{\msecond}{\Unit{m\,s^{-1}}}
\newcommand{\Js}{\Unit{J\,s}}
\newcommand{\watt}{\Unit{W\,m^{-2}}}
\newcommand{\wattcm}{\Unit{W\,cm^{-2}}}
\newcommand{\wattt}{\Unit{\frac{W}{m^2}}}
\newcommand{\wattsr}{\Unit{W\,sr^{-1}\,m^{-2}}}
\newcommand{\wattnm}{\Unit{W\,m^{-2}\,nm^{-1}}}
\newcommand{\mwattnm}{\Unit{mW\,cm^{-2}\,nm^{-1}}}
\newcommand{\mwattmnm}{\Unit{mW\,m^{-2}\,nm^{-1}}}
\newcommand{\wattcmnm}{\Unit{W\,cm^{-2}\,nm^{-1}}}
\newcommand{\gmcubic}{\Unit{g\,m^{-3}}}
\newcommand{\irr}[1][]{{E_{\mathrm{#1}}}}
\newcommand{\sirr}[1][]{{E_{\mathrm{#1}}(\lambda)}}
\newcommand{\pfd}[1][]{{Q_{\mathrm{#1}}}}
\newcommand{\spfd}[1][]{{Q_{\mathrm{#1}}(\lambda)}}
\newcommand{\quantum}[1][]{{q^{\mathrm{#1}}}}
\newcommand{\molequanta}{\quantum[\prime]}
\newcommand{\flrat}{\irr[0]} % fluence rate
\newcommand{\PAR}{{\mathrm{PAR}}}
\newcommand{\PPFD}{{\mathrm{PPFD}}}
\newcommand{\RAF}{{\mathrm{RAF}}}
\newcommand{\eeff}[1][]{{s_{\mathrm{#1}}}}
\newcommand{\seeff}[1][]{{s_{\mathrm{#1}}(\lambda)}}
\newcommand{\qeff}[1][]{{s_{\mathrm{#1}}^\mathrm{p}}}
\newcommand{\sqeff}[1][]{{s_{\mathrm{#1}}^\mathrm{p}(\lambda)}}
\newcommand{\intensity}[1][]{{I_{\mathrm{#1}}}}
\newcommand{\radiance}[1][]{{L_{\mathrm{#1}}}}
\newcommand{\exposure}[1][]{{H_{\mathrm{#1}}}}
\newcommand{\dose}[1][]{{H^{\mathrm{#1}}}}
\newcommand{\sdose}[1][]{{H^{\mathrm{#1}}(\lambda)}}
\newcommand{\qdose}[1][]{{H^{\mathrm{#1}}_\mathrm{p}}}
\newcommand{\sqdose}[1][]{{H^{\mathrm{#1}}_\mathrm{p}(\lambda}}
\newcommand{\rad}[1][]{{L_{\mathrm{#1}}}}
\newcommand{\trans}[1][]{{\tau_{\mathrm{#1}}}}
\newcommand{\strans}[1][]{{\tau_{\mathrm{#1}}(\lambda)}}
\newcommand{\absb}[1][]{{A_{\mathrm{#1}}}}
\newcommand{\abst}[1][]{{\alpha_{\mathrm{#1}}}}
\newcommand{\sabst}[1][]{{\alpha_{\mathrm{#1}}(\lambda)}}
\newcommand{\refl}[1][]{{\rho_{\mathrm{#1}}}}
\newcommand{\srefl}[1][]{{\rho_{\mathrm{#1}}(\lambda)}}
\newcommand{\emitt}[1][]{{\epsilon_{\mathrm{#1}}}}
\newcommand{\SZA}{{\theta}}
\newcommand{\TOthree}{{\omega}}
\newcommand{\degree}{{\mathrm{^{\circ}}}}
\newcommand{\voltage}[1][]{{U_{\mathrm{#1}}}}
\newcommand{\temperature}[1][]{{T_{\mathrm{#1}}}}
\newcommand{\Coscor}{{\varphi}}
$$
:::
## Solar radiation at ground level {#sec:solar:radiation}
### Position of the sun
When studying plants we frequently need to describe the position of the sun because of its effect on spectral irradiance and as a determinant of day length. The azimuth angle ($\phi$) is usually measured clockwise from the North on a horizontal plane. The position on the vertical plane is measured either as the zenith angle ($\theta$) downwards from the zenith, or as an elevation angle ($h$) upwards from the horizon. Consequently $h + \theta = 90^\circ = \frac{\pi}{2}\,$radians. When describing the position of the sun as in @fig-solar-position the observer is situated at the origin of the system of coordinates used to describe the angles.
{#fig-solar-position width=67%}
The daily path of the sun in the sky depends on the time of the year and the latitude. The time of solar noon depends on the actual geographic location, and nearly always differs from the local-time noon which is based on a time zone and possibly affected by a shift of one or more hours to enhance daylight use, especially in summer time.
Highly accurate astronomical formulas are available and can be used to compute the current, past and future positions of the sun in the sky [e.g., @Meeus1998]. Less accurate, but simpler to compute, formulas also exist. These formulas can also be use to compute day- and night lengths based on different twilight definitions.
::: callout-note
The accuracy of astronomical formulas is less when the sun is near the horizon because, as seen in the previous chapter, the transition from vacuum to the atmosphere introduces a small angular displacement of the light beam depending on the refractive index and angle of incidence. The difficulty is that the refractive index ($\nu$) of the air depends on its water content, which is not constant.
:::
The path of the sun in the sky determines the lengths of the day and night. The most frequently used criterion for the day to night and night to days transitions is that the upper rim of the solar disk coincides with the astronomical horizon. As the solar disk is approximately 2 degrees in diameter, the transition between day and night is in this case when the centre of the solar disk is one degree below the horizon. Alternatively, the centre of the solar disk can be used coinciding with the astronomical horizon can be used as the criterion.
 under CC-BY-SA 3.0.](images/twilight-full-day.png){#fig-twilight-full-day width=67%}
Twilight is the period before sunrise and after sunset when there is more intense diffuse light than further into the night (@fig-twilight-full-day). The definitions of twilight are based on practical considerations of human activities, and called *civil*, *naval* and *astronomical* (@fig-twilight-angles). During civil twilight artificial lighting is usually not needed in cities. For nautical twilight I have seen two descriptions: 1) before nautical dawn and after nautical dawn it is possible to navigate based on the stars, and 2) after nautical dawn and before nautical dusk navigation by sight is possible. Before astronomical dawn and after astronomical dusk stars of sixth magnitude are visible. These three descriptions are very approximate, as they do not consider the effect of clouds, light pollution from artificial sources, or other source of light.
 under CC-BY-SA 3.0., edited](images/twilight-dawn-angles.png){#fig-twilight-angles width=67%}
The day length through the year computed for three cities, one of them, Ivalo at the northern latitude within the Artic circle.
```{r, echo=FALSE}
#| label: fig-day-length
#| fig-cap: Day length through the year at different latitudes.
days <- seq(from = ymd("2024-01-01"), to = ymd("2024-12-31"), by = 1)
cities <- c("Ivalo, Finland", "Helsinki, Finland", "Athens, Greece")
geocodes <- data.frame(lon = c(27.53971, 24.93838, 23.72754),
lat = c(68.65764, 60.16986, 37.98381),
address = cities)
times <-
day_night(date = days,
geocode = geocodes,
tz = "EET",
unit.out = "hour")
ggplot(times,
aes(x = day, y = daylength, linetype=address)) +
geom_line(linewidth = 1) +
scale_y_continuous(name = "Day length (h)",
breaks=c(0,12,24), limits=c(0,24)) +
scale_x_datetime(name = "Date (yyyy-mm-dd)",
date_labels = "%Y-%m-%d") +
scale_linetype(name = "Location") +
theme_minimal() + theme(legend.position = "top")
```
If we include twilight, the estimated length of the photoperiod increases (@fig-daylength-twilight).
```{r, echo=FALSE}
#| label: fig-daylength-twilight
#| fig-cap: Photoperiod duration computed for sunrise/sunset or for dawn/dusk according to different twilight definitions. The curve describes the elevation angle of the sun through the day at Helsinki, Finalnd, on the 21 March 2024. The horizontal lines show the elevation angles used as the boundary of the "photoperiod" and the numbers above each of these lines give the corresponding length of the photoperiod in hours.
geocode_He <- data.frame(lon = 24.93838, lat = 60.16986)
hours <- seq(from = ymd_hm("2024-03-21 00:00", tz = "EET"),
by = "1 min",
length.out = 24 * 60)
angles_He <- sun_angles(hours,
geocode = geocode_He)
twilight.tb <-
data.frame(angle = c(-1, -6, -12, -18),
location = factor(c("Horizon", "Civil",
"Nautical",
"Astronomical"),
levels = c("Horizon", "Civil",
"Nautical",
"Astronomical")),
time = min(hours))
get_middle_rows <-
function(x) {
z <- x[nrow(x) %/% 2 + -1:2, ]
z$twilight <- twilight.tb$angle
daylength <- numeric(nrow(z))
for (i in 1:nrow(z)) {
daylength[i] <- day_length(date = z[i, ]$time,
geocode = geocode_He,
twilight = z[i, ]$twilight)
}
z$daylength <- round(daylength, digits = 1L)
z
}
ggplot(angles_He,
aes(x = time, y = elevation)) +
geom_line(linewidth = 1) +
geom_hline(data = twilight.tb,
aes(yintercept = angle, linetype = location),
linewidth = 0.5) +
geom_text(data = get_middle_rows,
mapping = aes(label = paste(daylength, "h"),
y = twilight + 2, x = time)) +
scale_x_datetime(date_labels = "%H:%M", expand = expansion(0.02, 0.02)) +
labs(y = "Sun elevation angle at Helsinki (degrees)",
x = "Time UTC + 2 (hh:mm)", linetype = "Dawn/dusk") +
theme_minimal() + theme(legend.position = "top")
```
::: callout-tip
Even though day length or photoperiod duration is normally based on the astronomical horizon, in many cases there are obstacles that occlude the sun from view until later at dawn or earlier at dusk: mountains, vegetation, buildings, etc. Thus, in some cases it may be more relevant for a scientific study with plants to consider sun elevation angles $h > 0$ as the criterion; even different angles can be used for day start and day end.
The question remains of what light level is actually detected by plants and other organisms as the night-day and day-night transitions. Most likely the exact value depends on the genotype and the realized phenotype. However, at dawn and dusk irradiance increases and decreases, respectively, qquite fast at low and medium latitudes. At high latitudes, at some times the year twilight lasts for a long time making the change in irradiance more gradual. At civil dawn/dusk irradiance is already very low.
:::
The dependency of the elevation angle on time of the year and latitude is illustrated in @fig-sun-elevation-world.
```{r, echo=FALSE}
#| label: fig-sun-elevation-world
#| fig-cap: Sun elevation angle through the day at five different latitudes and four dates. The horizontal dotted lines show the horizon ($h = 0$). The range limits for the _x_ and _y_ axes are consistent across all plots.
make_sun_elevation_plot <- function(date, geocode) {
# 97 points in time from midnight to midnight
t <- rep(date, 24 * 4 + 1) +
hours(c(rep(0:23, each = 4), 24)) +
minutes(c(rep(c(0, 15, 30, 45), times = 24), 0))
e <- sun_elevation(time = t, geocode = geocode)
ggplot(data.frame(t, e), aes(t, e)) +
geom_hline(yintercept = 0, linetype = "dotted") +
geom_line() +
expand_limits(y = c(-90, 90)) +
scale_x_datetime(date_labels = "%H:%m", expand = expansion()) +
scale_y_continuous(expand = expansion()) +
theme_void() +
theme(panel.background = element_rect(fill = "grey95",
color = NA),
panel.border = element_blank())
}
dates <- ymd(c("2020-02-20", "2020-03-21", "2020-6-21",
"2020-09-21", "2020-12-21", "2021-01-22"))
date.ticks <- dates[2:5]
date.ends <- dates[c(1, 6)]
geocodes <-
tibble::tibble(lon = c(0, 0, 0, 0, 0),
lat = c(66.5634, 23.4394, 0, -23.4394, -66.5634),
address = c("Northern\nPolar\nCircle", "Tropic of\nCancer", "Equator",
"Tropic of\nCapricorn", "Southern\nPolar\nCircle"))
sun_elevation.tb <-
tibble::tibble(when = rep(dates, nrow(geocodes)),
where = rep(geocodes$address, each = length(dates)),
plots = c(lapply(dates, make_sun_elevation_plot, geocode = geocodes[1, ]),
lapply(dates, make_sun_elevation_plot, geocode = geocodes[2, ]),
lapply(dates, make_sun_elevation_plot, geocode = geocodes[3, ]),
lapply(dates, make_sun_elevation_plot, geocode = geocodes[4, ]),
lapply(dates, make_sun_elevation_plot, geocode = geocodes[5, ])))
sun_elevation.tb$where <-
factor(sun_elevation.tb$where, levels = rev(geocodes$address))
ggplot(sun_elevation.tb, aes(x = when, y = where, label = plots)) +
geom_plot(data = . %>% filter(day(when) == 21),
inherit.aes = TRUE,
vp.width = 0.18, vp.height = 0.15, vjust = 0.5, hjust = 0.5) +
scale_x_date(name = "", breaks = date.ticks,
limits = date.ends,
date_labels = "%d %b") +
scale_y_discrete(name = "") +
# labs(title = "Solar elevation through the day",
# subtitle = "The dotted line indicates the horizon (h = 0)",
# caption = "Inset plots are drawn using consistent x and y scale limits.") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_blank())
```
Three images from fig 3.6 in Graedel and Crutzen, 1993. Scanned. Have to be redrawn.
{width=30%}
{width=30%}
{width=30%}
***Add links to previous chapter***
### Solar radiation at the top of the atmosphere
We can measure irradiance outside the atmosphere at a know distance from the sun and we can also measure the apparent diameter of the sun at this same distance. Based on this we can calculate the total radiant energy emitted by the sun and irradiance at arbitrary distances.
In outer space, in vacuum, no scattering takes place as the light travels, and because of this, the sky looks completely black. Planets and the Moon do reflect light allowing us to see them. As the distance to the sun increases irradiance decreases as the flux spreads into a large area represented by the surface of a sphere, without scattering, this becomes a geometric computations in 3D.
::: callout-note
# Geometric calculation
The spectral irradiance of the sun $\irr[s](\lambda)$ can be estimated assuming a homogeneous flux and using the correlation of intensity and radiance from their definitions in table [\[tab:Phys:Quants\]](#tab:Phys:Quants){reference-type="ref" reference="tab:Phys:Quants"}. The intensity of the sun $\intensity[s](\lambda)$ is given by the radiance $\radiance[s](\lambda)$ multiplied by the apparent sun surface (a non-tilted disk of radius $r_s=7\times 10^5$ km). To calculate the decreased solar irradiance at the top of the Earth's atmosphere, the distance of the sun to the Earth ($r_0=150\times 10^6$ km) has to be taken into account applying the inverse square law of irradiance of equation ([\[equ_r2law\]](#equ_r2law){reference-type="ref" reference="equ_r2law"}). Thus, the extraterrestrial solar irradiance is
$$\irr[s](\lambda)=\radiance[s](\lambda)\cdot\frac{\pi r_s^2}{r_0^2}$$
Remembering the solid angle of equation ([\[equ_solidangle\]](#equ_solidangle){reference-type="ref" reference="equ_solidangle"}), the right multiplication factor represents the solid angle of the sun's disk as seen from the Earth's surface ($\approx 6.8\times 10^{-5}$ sr).
:::
At the time scales relevant to plants and humans, the distance between the Sun and the Earth varies little, as well as the emission from the surface of the sun. Thus, we can use a "solar constant" as a useful and nearly perfect approximation. The solar "constant", the irradiance at the top of the atmosphere, varies between $1\,361$ and $1\,362~\mathrm{W m^{-2}}$ due to changes in solar activity. In addition to the irradiance, the shape of the solar spectrum at the top of the atmosphere varies very little in time, and is usually assumed constant.
::: callout-note
Radiation from the sun reaches the Earth's surface in about eight minutes ($t$ = time, $r_0$ = distance sun to earth, $c$ = velocity of light in vacuum): $$t = \frac{r_0}{c} \approx \frac{150 \times 10^9 ~{\rm m}}{3 \times 10^8 ~\frac{\rm m}{\rm s}} = 500 ~{\rm s} = 8.3 ~{\rm min}$$.
:::
The energy radiance ($L$) of radiation emitted by the sun is similar in shape, but not identical, to the emission spectrum of a *black body*. The solar spectrum has a fine structure of peaks and valleys arising from the atoms and molecules in the sun while black body emission is described by a smooth curve. A "match" based on the wavelength at which spectral _energy_ radiance is at its maximum can be used as criterion to estimate the temperature of the sun's surface. Obviously, we use the extraterrestrial spectrum for this match. We see in the AM0 curve in @fig-sun-spectra-energy that the wavelength at the maximum is near 500 nm. In reality the temperature of the sun surface is not uniform, and different sources mention as its average temperatures between 5800 K and 5500 K. Comparing the AM0 curve in @fig-sun-spectra-energy to the 5800 K curve in @fig-blackbody-emission the similary of their shapes is clear.
```{r, echo=FALSE}
#| label: fig-blackbody-emission
#| fig-cap: Radiance spectra for black bodies at three different temperatures; 5800 K (blue), 5000 K (orange) and 3700 K (red).
h <- 6.626e-34 # J s-1
c <- 2.998e8 # m s-1
kB <- 1.381e-23 # J K-1
black_body_spectrum <- function(w.length, Tabs) {
w.length <- w.length * 1e-9 # nm -> m
((2 * h * c^2) / w.length^5) *
1 / (exp((h * c / (kB * Tabs * w.length))) - 1)
}
ggplot(data=data.frame(x=c(50,3000)), aes(x)) +
stat_function(fun=black_body_spectrum,
args = list(Tabs=5800),
colour="blue") +
stat_function(fun=black_body_spectrum,
args = list(Tabs=5000),
colour="orange") +
stat_function(fun=black_body_spectrum,
args = list(Tabs=3700),
colour="red") +
labs(y=expression(Spectral~~radiance~~(W~sr^-1~m^-3)),
x="Wavelength (nm)") +
theme_minimal()
```
Planck's law describes black body emission at temperature $T$ (@fig-blackbody-emission), expressed as absolute temperature measured in degrees Kelvin (K):
$$L_s(\lambda,T) = \frac{2hc^2}{\lambda^5}\cdot\frac{1}{\mathrm{e}^{(hc/k_\mathrm{B}T\lambda)}-1}$$
with Boltzmann's constant $k_\mathrm{B}=1.381\times 10^{-23}$ .
The "equivalent temperature" of a black body is given by Wien's displacement law (@fig-wein-wl), which gives the wavelength at which the radiative energy emitted by a black body is at its maximum as a function of its absolute temperature.
$$\lambda_\mathrm{max} \cdot T = 2.898 \times 10^6 \,{\rm nm\,K}$$
```{r, echo=FALSE}
#| label: fig-wein-wl
#| fig-cap: The peak wavelength of radiation emission by a black body as a function of its absolute (surface) temperature. The approximate temperature of the sun $T_\mathrm{sun} \approx 5800\,\mathrm{K}$ is highlighted by a yellow-filled circle.
k.wein <- 2.8977721e6 # nm K
black_body_peak_wl <- function(Tabs) {
k.wein / Tabs
}
ggplot(data=data.frame(Tabs = c(2000,7000)), aes(x = Tabs)) +
stat_function(fun = black_body_peak_wl) +
labs(x = "Temperature (K)",
y = "Wavelength at peak of emission (nm)") +
annotate(geom = "point",
x = 5800, y = black_body_peak_wl(5800),
fill = "yellow", colour = "black",
shape = "circle filled", size = 3) +
theme_minimal()
```
::: callout-caution
The concept of "colour temperature" used for describing the emission of lamps used for illumination and more generaly in photography differs in that it takes into account human colour vision when determining the match between the black body emission spectrum and the spectrum of an arbitrary light source. The coordinated colour temperature (CCT) for the AM1.5 solar spectrum is `r round(photobiologyInOut::spct_CCT(photobiologySun::sun_reference.mspct$ASTM.G173.global))` K and for the AM0 spectrum is `r round(photobiologyInOut::spct_CCT(photobiologySun::sun_reference.mspct$WMO.Wehrli.AM0))` K.
:::
The spectral irradiance of the sun $\irr[s](\lambda)$ can be estimated assuming a homogeneous flux and using the correlation of intensity and radiance from their definitions in table [\[tab:Phys:Quants\]](#tab:Phys:Quants){reference-type="ref" reference="tab:Phys:Quants"}. The intensity of the sun $\intensity[s](\lambda)$ is given by the radiance $\radiance[s](\lambda)$ multiplied by the apparent sun surface (a non-tilted disk of radius $r_s=7\times 10^5$ km). To calculate the decreased solar irradiance at the moment of reaching the Earth's atmosphere, the distance of the sun to the Earth ($r_0=150\times 10^6$ km) has to be taken into account due to the inverse square law of irradiance of equation ([\[equ_r2law\]](#equ_r2law){reference-type="ref" reference="equ_r2law"}). Thus, the extraterrestrial solar irradiance is
$$\irr[s](\lambda)=\radiance[s](\lambda)\cdot\frac{\pi r_s^2}{r_0^2}$$
Remembering the solid angle of equation ([\[equ_solidangle\]](#equ_solidangle){reference-type="ref" reference="equ_solidangle"}), the right multiplication factor represents the solid angle of the sun's disk as seen from the Earth's surface ($\approx 6.8\times 10^{-5}$ sr). Figure [\[fig_sun\]](#fig_sun){reference-type="ref" reference="fig_sun"} shows the spectrum of the measured extraterrestrial solar radiation (Wehrli, 1985)[^1] and the spectrum calculated by equation 1.14 using Planck's law of equation 1.12 at a black body temperature of 5800 K. Integrated over all wavelengths, $\irr[s]$ is about 1361 to 1362 at top of the atmosphere [@Kopp2011]. This value is called the 'solar constant'. In former times, depending on different measurements, $\irr[s]$ varies by a few percent [@iqbal83]. For example, the irradiance at the top of the atmosphere (the integrated value) changes by $\pm 50$ (3.7 %) during the year due to distance variation caused by orbit excentricity [@moble94]. More accurate measurements during the last 25 years by spaceborne radiometers show a variability of the solar radiation of a few tenth of a percent. A detailed analysis is given by @froeh04. $\irr[s]$ can also be calculated by the Stefan-Boltzmann Law: the total energy emitted from the surface of a black body is proportional to the fourth power of its temperature. For an isotropically emitting source (Lambertian emitter), this means $$\radiance = \frac{\sigma}{\pi} \cdot T^4$$ with the Stefan-Boltzmann constant $\sigma=5.6705\times 10^{-8}$ W m$^{-2}$ K$^{-4}$. With $T = 5800$ K equation [\[equ_boltzmann\]](#equ_boltzmann){reference-type="ref" reference="equ_boltzmann"} gives the radiance of the solar disc. From this value, we can obtain an approximation of the solar constant, by taking into account the distance from the Earth to the Sun and the apparent size of the solar disc (see equations [\[equ_solidangle\]](#equ_solidangle){reference-type="ref" reference="equ_solidangle"} and [\[equ_r2law\]](#equ_r2law){reference-type="ref" reference="equ_r2law"}).
[^1]: Available as ASCII file at PMODWRC, <ftp://ftp.pmodwrc.ch/pub/publications/pmod615.asc>
```{r, echo=FALSE}
#| label: fig-sun-black-body
#| fig-cap: The extraterrestrial solar spectrum compared to black body emission. The black body emission spectrum has been scaled to match the energy irradiance of the solar spectrum at the top of the atmosphere (AM0) within the range of wavelengths shown.
energy_as_default()
w.lengths <- seq(200, 3000, length.out = 201)
sun.spct <-
clip_wl(
sun_reference.mspct[["WMO.Wehrli.AM0"]],
range = range(w.lengths))
black_body.spct <-
source_spct(w.length = w.lengths,
s.e.irrad = black_body_spectrum(w.lengths, 5800))
black_body.spct <-
fscale(black_body.spct,
f = e_irrad,
target = e_irrad(sun.spct))
plot.spectra <-
source_mspct(
list(AM0 = sun.spct,
"Black body at 5800K" = black_body.spct)
)
ggplot(plot.spectra, unit.out = "energy") +
aes(color = spct.idx) +
geom_line() +
scale_color_manual(name= "",
values=c("purple","blue")) +
scale_x_wl_continuous() +
scale_y_s.e.irrad_continuous(expand=c(0,0)) +
theme_minimal() +
theme(legend.position= "top")
```
The total solar irradiance covers a wide range of wavelengths. Using some of the 'colours' introduced in table [\[tab:Waverange\]](#tab:Waverange){reference-type="ref" reference="tab:Waverange"}, table [\[tab_sun\]](#tab_sun){reference-type="ref" reference="tab_sun"} lists the irradiance and fraction of $\irr[s]$ of different wavelength intervals.
```{r, include=FALSE}
AM0_e_irrads <-
round(e_irrad(sun.spct, w.band = c(UV_bands("CIE"), list(PhR()), IR_bands("CIE"))), 2)
AM0_e_irrad_tot <- sum(AM0_e_irrads)
AM0_e_irrads
AM0_e_irrad_tot
signif(AM0_e_irrads / AM0_e_irrad_tot * 100, 3)
```
::: table*
::: center
| Waveband | Wavelength $\lambda$ (nm) | Irradiance $\irr$ ($W m^{-2}$) | Irradiance $\irr$ (%) |
|:--------------:|:---------------:|:-------------:|:-------------------------:|
| UV-C | 100 -- 280 | 7 | 0.5 |
| UV-B | 280 -- 315 | 17 | 1.3 |
| UV-A2 | 315 -- 340 | 21 | 1.6 |
| UV-A1 | 340 -- 400 | 63 | 4.7 |
| PhR | 400 -- 700 | 531 | 39.6 |
| IR-A (CIE) | 700 -- 1400 | 517 | 38.6 |
| IR-B (CIE) | 1400 -- 3000 | 184 | 13.7 |
| **TOTAL** | | **1339** | **100.0** |
:::
:::
### Solar radiation at ground level
Above, we used the word *light* with its everyday meaning. Its technical definition is of radiation visible to humans. Ultraviolet, visible and infrared radiation (taken together are called in meteorolgy *shortwave radiation*) are components of solar radiation.
#### Spectrum under clear sky
**We could include a plot of scalar spectral irradiance (easy to generate with TUV) or of hemispherical spectral irradiance measured with a hemispherical diffuser (I have data). A comparison to spectral irradiance on a horizontal plane could be very informative.**
Before reaching the Earth's surface radiation from the sun travels through the atmosphere. The sun elevation angle affects the length of the path that the light arriving from the sun travels through the atmosphere. In atmospheric physics it is common to describe spectra as corresponding to *AM0* for above the atmosphere, *AM1* when the sun is at the zenith, i.e., one atmosphere thickness and *AM1.5* when the length of the air mass traversed is 1.5 times that when the sun is at the zenith.
The air mass traversed is, in reality, not exactly proportional to the distance computed by trigonometry, but not far from it. An empirical formula [@Kasten1989] can be used as a better approximation to the computation of AM values from the elevation angle ($h$) (@fig-AM-h).
```{r, echo=FALSE}
#| label: fig-AM-h
#| fig-cap: Plot of the empirical relationship between sun elevation ($h$) or zenith anglr ($\theta$) and "equivalent" atmosphere thickness traversed by solar radiation between outer space and the Earth's surface (at sea level). AM1
#| is indicated by the horizontal dotted line.
h <- 1:90
AMx <- relative_AM(h)
ggplot(data.frame(h, AMx), aes(h, AMx)) +
geom_hline(yintercept = 1, linetype = "dotted") +
geom_line(linewidth = 1) +
scale_x_continuous(name = expression("Sun elevation angle, "*italic(h)*"(degrees)"),
breaks = c(0, 30, 60, 90),
sec.axis = sec_axis(transform = function(x) {90 - x},
breaks = rev(c(0, 30, 60, 90)),
name = expression("Sun zenith angle,
"*italic(theta)*"(degrees)"))) +
scale_y_continuous(name = "AM") +
theme_minimal()
```
Light when travelling through the atmosphere interacts with the gases and particles composing it. The light is attenuated and scattered. Different wavelengths are differently affected by these processes, giving us a blue sky and warmer coloured direct sunlight. In other words, the daylight spectrum depends on the position of the sun in the sky, and on the composition of the atmosphere along this path (@fig-sun-spectra-energy and @fig-sun-spectra-photons). In addition to molecular absorption and scattering, aerosols, both liquid like water and solid like ice and dust, interact with light.
```{r, echo=FALSE}
#| label: fig-sun-spectra-energy
#| fig-cap: The spectrum of solar radiation above the atmosphere (AM0) and a typical spectrum at ground level (AM1.5). Spectral energy irradiance for the wavelength range called _shortwave radiation_ in meteorology.
ggplot(clip_wl(sun_reference.mspct[c("WMO.Wehrli.AM0",
"ASTM.G173.global")],
range=c(NA,3000)), unit.out = "energy") +
aes(color= spct.idx) +
geom_line() +
scale_color_manual(name= "",
labels=c("AM0","AM1.5"),
values=c("purple","blue")) +
scale_x_wl_continuous() +
scale_y_s.e.irrad_continuous(expand=c(0,0)) +
annotate(geom= "text",
x =c(245, 750,950,1150,1450,1900),
y =c(0.4, 0.08,0.08,0.7,0.43,0.27),
label=c("O[3]","O[2]","H[2]*O","H[2]*O","H[2]*O","H[2]*O~CO[2]"),
parse= TRUE) +
theme_minimal() +
theme(legend.position= "top")
```
```{r, echo=FALSE}
#| label: fig-sun-spectra-photons
#| fig-cap: The spectrum of solar radiation above the atmosphere (AM0) and a typical spectrum at ground level (AM1.5). Spectral photon irradiance for the range 250 to 900 nm.
ggplot(clip_wl(sun_reference.mspct[c("WMO.Wehrli.AM0",
"ASTM.G173.global")],
range=c(240,1000)), unit.out = "photon") +
aes(color = spct.idx) +
geom_line() +
scale_color_manual(name= "",
labels=c("AM0","AM1.5"),
values=c("purple","blue")) +
scale_x_wl_continuous() +
scale_y_s.q.irrad_continuous(expand=c(0,0)) +
theme_minimal() +
theme(legend.position= "top")
```
The extraterrestrial solar spectrum differs from that at ground level due to the absorption of radiation by the atmosphere, because the absorption peaks of water, and other components of the atmosphere, cause corresponding valleys to appear in the solar spectrum at ground level. The solar spectrum has a much finer structure, due to emission and absorption lines of elements, this fine structure is not observable with the spectroradiometers normally used in plant research. For plant research, finr structure of peaks and valleys is considered irrelevant because plant pigments *in vivo* have broader peaks and valleys in their spectra.
Not only total irradiance, but also the wavelength distribution and the diffuse fraction of the solar spectrum changes with the seasons of the year and time of day. This changes are in large part explained by the change in sun elevation. In what follows we describe these changes as a function of sun elevation. The increasing air mass traversed with decreasing sun elevation affects the shape of the spectrum, by stronger attenuation of the shorter wavelengths. An increased air mass also increases scattering in the atmosphere.
```{r, include=FALSE}
# prepare data for upcoming figures
sun_elevation.mspct <- subset2mspct(sun_elevation.spct, drop.idx = FALSE)
# each spectrum contains three columns with spectral data that we separate
sun_elevation_tot.mspct <- source_mspct()
sun_elevation_diff.mspct <- source_mspct()
sun_elevation_dir.mspct <- source_mspct()
for (n in names(sun_elevation.mspct)) {
sun_elevation_tot.mspct[[n]] <-
sun_elevation.mspct[[n]][ , c("w.length", "s.e.irrad")]
sun_elevation_diff.mspct[[n]] <-
with(sun_elevation.mspct[[n]],
source_spct(w.length = w.length,
s.e.irrad = s.e.irrad.diff.down))
sun_elevation_dir.mspct[[n]] <-
with(sun_elevation.mspct[[n]],
source_spct(w.length = w.length,
s.e.irrad = s.e.irrad.dir))
}
wavebands <- c(list(PAR = PAR()), Plant_bands("sensory"))
wbands_tot.tb <-
q_irrad(sun_elevation_tot.mspct, wavebands, scale.factor = 1e6)
wbands_dir.tb <-
q_irrad(sun_elevation_dir.mspct, wavebands, scale.factor = 1e6)
wbands_diff.tb <-
q_irrad(sun_elevation_diff.mspct, wavebands, scale.factor = 1e6)
wbands.tb <-
data.frame(
SZA = as.numeric(gsub("tuv-", "", wbands_tot.tb$spct.idx)),
SEA = 90 - as.numeric(gsub("tuv-", "", wbands_tot.tb$spct.idx))
)
for (col in setdiff(colnames(wbands_tot.tb), "spct.idx")) {
wbands.tb[[paste(col, "tot", sep = ".")]] <- wbands_tot.tb[[col]]
wbands.tb[[paste(col, "dir", sep = ".")]] <- wbands_dir.tb[[col]]
wbands.tb[[paste(col, "diff", sep = ".")]] <- wbands_diff.tb[[col]]
wbands.tb[[paste(col, "diff_fr", sep = ".")]] <-
wbands_diff.tb[[col]] / wbands_tot.tb[[col]]
}
# shorten column names
colnames(wbands.tb) <-
gsub("Q_]|Q_|\\.ISO|\\.CIE|\\.Sellaro|\\.Smith20", "", colnames(wbands.tb))
```
A figure of spectral irradiance is difficult to read as both the shape of the curve and the maximum spectral irradiance change in parallel [@fig-spectra-sun-elevation].
```{r, echo=FALSE}
#| label: fig-spectra-sun-elevation
#| fig-cap: Change in spectral irradiance with sun elevation angle. Based on simulations with the TUV model for an ozone column of 300 DU under clear sky.
five_angles.mspct <- sun_elevation.mspct[c("tuv-00", "tuv-30", "tuv-45", "tuv-60", "tuv-75", "tuv-85")]
names(five_angles.mspct) <-
as.character(90L - as.integer(gsub("^tuv-", "", names(five_angles.mspct))))
autoplot(five_angles.mspct,
unit.out = "photon",
annotations = c("-", "peaks"),
w.band = Plant_bands("CIE")) +
labs(linetype = "Sun elevation\n(degrees)")
```
If we rescale the curves to equal PAR irradiance the change in shape is easier to observe [@fig-uv-spectra-sun-elevation-scaled and @fig-nir-spectra-sun-elevation-scaled].
```{r, echo=FALSE}
#| label: fig-uv-spectra-sun-elevation-scaled
#| fig-cap: Change in scaled spectral irradiance with sun elevation angle. UV region of the spectrum. Based on simulations with the TUV model for an ozone column of 300 DU under clear sky. Spectra scaled to PAR = $1\,000\,\mu mol\,s^{-1}\,m^{-2}$.
autoplot(fscale(five_angles.mspct, f = q_irrad, w.band = PAR(), target = 1e-3),
range = UV(),
unit.out = "photon",
annotations = c("-", "peaks"),
w.band = Plant_bands("CIE")) +
labs(linetype = "Sun elevation\n(degrees)")
```
```{r, echo=FALSE}
#| label: fig-nir-spectra-sun-elevation-scaled
#| fig-cap: Change in scaled spectral irradiance with sun elevation angle. Red-FR region of the spectrum. Based on simulations with the TUV model for an ozone column of 300 DU under clear sky. Spectra scaled to PAR = $1\,000\,\mu mol\,s^{-1}\,m^{-2}$.
autoplot(fscale(five_angles.mspct, f = q_irrad, w.band = PAR(), target = 1e-3),
range = c(600,700),
unit.out = "photon",
annotations = c("-", "peaks"),
w.band = Plant_bands("CIE")) +
labs(linetype = "Sun elevation\n(degrees)")
```
Plotting the normalised photon irradiance for different wavebands [@fig-wbands-elevation-normalized] shows that the daily course of UV-B photon irradiance is different than that for other bands, even the adjacent UV-A2. UV-B irradiance decreases faster with decreasing sun elevation ($h$).
```{r, echo=FALSE}
#| label: fig-wbands-elevation-normalized
#| fig-cap: Change in irradiance with sun elevation angle for different regions of the spectrum. PAR. orange, UVA-1. violet, UV-A2. purple, and UV-B. black. Based on simulations with the TUV model for an ozone column of 300 DU.
ggplot(wbands.tb, aes(x = SEA)) +
geom_line(aes(y = PAR.tot / max(PAR.tot)), colour = "orange") +
geom_line(aes(y = UVA1.tot / max(UVA1.tot)), colour = "violet") +
geom_line(aes(y = UVA2.tot / max(UVA2.tot)), colour = "purple") +
geom_line(aes(y = UVB.tot / max(UVB.tot)), colour = "black") +
scale_x_continuous(name = "Sun elevation angle (degrees)",
breaks = c(-10, 0, 30, 60, 90),
sec.axis = sec_axis(name = "Sun zenith angle (degrees)",
transform = function(x) {90 - x},
breaks = c(0, 30, 60, 90, 100))) +
scale_y_continuous(name = expression("Photon irradiance, "*italic(Q)~~("rel. units"))) +
expand_limits(y = 0)
```
#### Diffuse fraction under clear sky
At the Earth's surface, the incident radiation or _global radiation_ has two distinct components, _direct radiation_ and _diffuse radiation_. Direct radiation is radiation travelling directly from the sun, while diffuse radiation is that scattered by the atmosphere. Diffuse radiation is what gives the blue colour to the sky and white colour to clouds. The relative contribution of direct and diffuse radiation to global radiation varies with wavelength and weather conditions. The contribution of diffuse radiation is more in the UV region, and in the presence of clouds (Figures [\[fig:sky:photos\]](#fig:sky:photos){reference-type="ref" reference="fig:sky:photos"} and [\[fig:diffuse:UV\]](#fig:diffuse:UV){reference-type="ref" reference="fig:diffuse:UV"}).

Simultaneous spectral measurements of direct and diffuse solar radiation are infrequent. Radiation transfer models such as TUV estimate these components, as shown in @fig-spectra-diffuse-TUV-45. The diffuse fraction is larger at shorter wavelengths. Under overcast conditions or any other situation when the solar disk is fully occluded the diffuse fraction is equal to one. However, only in outer space the diffuse fraction approaches zero.
```{r, echo=FALSE}
#| label: fig-spectra-diffuse-TUV-45
#| fig-cap: Spectral energy irradiance under a clear sky and sun elevation of 45 degrees. Simulation with the TUV radiation transfer and atmospheric chemistry model for an ozone column on 300 DU, assuming a low optical density of aerosols. The black line shown total spectral energy irradiance, the orange-filled area is the direct component and the blue-filled area is the diffuse or scattered component.
ggplot(sun_elevation.mspct[["tuv-45"]]) +
geom_spct(fill = "skyblue", colour = "black") +
geom_spct(aes(y = s.e.irrad.dir), fill = "orange") +
scale_x_wl_continuous() +
scale_y_s.e.irrad_continuous()
```
```{r, include=FALSE}
# create spline functions
# We fit splines to the diffuse fraction obtaining functions
# that can be used to obtain by interpolation estimates for
# any sun elevation.
spl_funs.ls <- list()
diff_fr_cols <- grep("diff[_]fr", colnames(wbands.tb), value = TRUE)
for (col in diff_fr_cols) {
spl_funs.ls[[col]] <- splinefun(wbands.tb[["SEA"]], wbands.tb[[col]])
}
```
In @fig-spectra-diffuse-TUV-45 the spectrum at a single sun elevation is shown. In @fig-diffuse-TUV the diffuse fraction from simulations under the same atmospheric conditions are shown as a function of sun elevation. The diffuse fraction is largest for shorter wavelengths and for each waveband it increases as the sun elevation decreases. This change is not linear, and has a similar shape as the change in air mass traversed [@fig-AM-h].
```{r, echo=FALSE}
#| label: fig-diffuse-TUV
#| fig-cap: Fraction of total radiation that is diffuse under a clear sky as a function of sun elevation angle. PAR, orange dashed; Red, red; Green, green; Blue, blue; UVA-1, violet; UV-A2, purple; UV-B, black. Based on simulations with the TUV radiation transfer and atmospheric chemistry model for an ozone column on 300 DU, assuming a low optical density of aerosols.
ggplot(wbands.tb, aes(SEA)) +
geom_hline(yintercept = c(0, 1), linetype = "dashed") +
stat_function(fun = spl_funs.ls[["UVB.diff_fr"]], xlim = c(-10, 90), colour = "black") +
stat_function(fun = spl_funs.ls[["UVA2.diff_fr"]], xlim = c(-10, 90), colour = "purple") +
stat_function(fun = spl_funs.ls[["UVA1.diff_fr"]], xlim = c(-10, 90), colour = "violet") +
stat_function(fun = spl_funs.ls[["Blue.diff_fr"]], xlim = c(-10, 90), colour = "blue") +
stat_function(fun = spl_funs.ls[["Green.diff_fr"]], xlim = c(-10, 90), colour = "green") +
stat_function(fun = spl_funs.ls[["Red.diff_fr"]], xlim = c(-10, 90), colour = "red") +
stat_function(fun = spl_funs.ls[["PAR.diff_fr"]], xlim = c(-10, 90), colour = "orange",
linetype = "dashed") +
scale_x_continuous(name = "Sun elevation angle (degrees)",
breaks = c(-10, 0, 15, 30, 45, 60, 75, 90),
sec.axis = sec_axis(name = "Sun zenith angle (degrees)",
transform = function(x) {90 - x},
breaks = c(0, 30, 60, 90, 100))) +
scale_y_continuous(name = expression("Diffuse fraction, "*Q[s] / Q[t]~~(""/1)),
sec.axis = sec_axis(name = expression("Direct fraction, "*Q[d] / Q[t]~~(""/1)),
transform = function(x) {1 - x} )) +
expand_limits(y = 0)
```
#### Ozone column
The spectral wavelength distribution is also modified by the amount of ozone in the atmosphere, known as the ozone column and measured in Dobson units (DU = mm of pure ozone) [@fig-UVB-PAR-ozone]. As expected, it is also affected by the air mass traversed, which depends on sun elevation. The UV-B:PAR photon ratior is more strongly dependent on the thickness of the ozone column at low sun elevations, when the ratio it smallest [@fig-UVB-PAR-ozone].
::: callout-tip
# Climate change and ozone depletion
An increase in the UV-B and UV-A2 irradiance is caused by depletion of the ozone layer in the stratosphere, mainly as a consequence of the release of chlorofluorocarbons (CFCs), used in cooling devices such as refrigerators and air conditioners, and in some spray cans [see @Graedel1993]. The most dramatic manifestation of this has been the seasonal formation of an "ozone hole" over Antarctica. It is controversial whether a true ozone hole has already formed in the Arctic, but strong depletion has occurred in year 2011 [@Manney2011] and atmospheric conditions needed for the formation of a "deep" ozone hole are not very different from those prevalent in recent years. Not so dramatic, but consistent, depletion has also been observed at mid-latitudes in both hemispheres. CFCs and some other halocarbons have been phased out following the Montreal agreement and later updates. However, as CFCs have a long half life in the atmosphere, of the order of 100 years, their effect on the ozone layer will persist for many years, even after their use has been drastically reduced. Model-based predictions of changes in atmospheric circulation due to global climate change have been used to derive future trends in index and ozone column thickness [@Hegglin2009]. In addition, increased cloudiness and pollution, could lead to decreased global irradiance, sometimes called 'global dimming' [e.g. @Stanhill2001].
:::
```{r, echo=FALSE}
#| label: fig-uv-spectra-ozone-scaled
#| fig-cap: Change in scaled spectral irradiance with sun elevation angle. UV-B and UV-A2 regions of the spectrum. Spectra scaled to PAR = $1\,000\,\mu mol\,s^{-1}\,m^{-2}$. Based on simulations with libradtran for 300 DU and 240 DU, a 20% depletion at Jokioinen, Finland. Data from Anders Lindfors.
six_ozone.mspct <- subset2mspct(sun_hourly_ozone.spct)
six_ozone.mspct <- six_ozone.mspct[grepl("-10$", names(six_ozone.mspct))]
autoplot(fscale(six_ozone.mspct, f = q_irrad, w.band = PAR(), target = 1e-3),
range = c(NA, 340),
unit.out = "photon",
annotations = c("-", "peaks"),
w.band = Plant_bands("CIE")) +
scale_linetype_manual(name = "Ozone column (DU)",
breaks = c("normO3-10", "m20percO3-10"),
labels = c("300", "240"),
values = c("solid", "32"))
```
```{r, echo=FALSE}
#| label: fig-UVB-PAR-ozone
#| fig-cap: Daily time course of UV-B:PAR photon ratio under a clear sky, under normal and 20% depleted ozone column. Based on simulations with libradtran for 300 DU and 240 DU, a 20% depletion at Jokioinen, Finland. Data from Anders Lindfors.
sun_ozone_irrads <- q_irrad(sun_hourly_ozone.spct, w.band = Plant_bands("CIE"))
sun_ozone_irrads$ozone <- factor(ifelse(grepl("^m20per", sun_ozone_irrads$spct.idx),
"240",
"300"),
levels = c("300", "240"))
ggplot(sun_ozone_irrads,
aes(when.measured + hours(2), `Q_]UVB.CIE` / Q_PAR * 1e3, linetype = ozone)) +
geom_line() +
expand_limits(y = 0) +
labs(x = "Time of day (hh:mm)",
y = expression("UV-B:PAR ratio "*(mmol~mol^{-1})),
linetype = "Ozone column (DU)")
```
#### Clouds
Above we have ignored the effects of clouds and relied heavily on simulations for clear-sky conditions. Next we will consider the effect of clouds and use as examples measurements and simulations that include the effect of clouds. We will consider variation at different time scales. We start with a time series of hourly mean spectra for one day, scaled to equal PAR photon irradiance to illustrate how the shape of the spectrum can change during the course of day [@fig-spectra-whole-day-anim].
```{r, include=FALSE}
# prepare data
sun_one_day.spct <- sun_hourly_august.spct
sun_one_day.spct <- subset(sun_one_day.spct, day(UTC) == 21)
sun_one_day.spct <- setIdFactor(sun_one_day.spct, idfactor = "UTC")
idfactor <- getIdFactor(sun_one_day.spct)
sun_one_day.spct[["time"]] <- sun_one_day.spct[[idfactor]]
sun_one_day.spct[[idfactor]] <- factor(sun_one_day.spct[[idfactor]])
```
```{r, echo=FALSE, warning=FALSE}
#| label: fig-spectra-whole-day-anim
#| fig-cap: Time series of scaled spectral photon irradiance during one day. Hourly mean spectra scaled to PAR = 1000 $\mu mol\,s^{-1}\,m^{-2}$ to emphasize the change in shape. Simulated with libradtran including the effect of clouds for Kumpula, Helsinki, Finland. Data from Andes Lindfors.
temp.spct <- fscale(sun_one_day.spct,
f = q_irrad,
target = 1e-3,
w.band = PAR())
temp.spct <- na.omit(temp.spct)
id_factor(temp.spct) <- "UTC"
ggplot(data = temp.spct,
unit.out = "photon") +
geom_spct() +
scale_x_wl_continuous() +
scale_y_s.q.irrad_continuous(name = s.q.irrad_label(scaled = TRUE)) +
transition_states(UTC,
transition_length = 2,
state_length = 1) +
ggtitle('{closest_state} UTC')
```
It should be noted that, through reflection, broken clouds can locally increase irradiance to values above those under clear-sky conditions [@Frederick1993; @Diaz1996]. These are transient increases of short duration and easily detected with fast measurements, and the irradiance increase can be 30% or more compared to under a clear sky. This is well illustrated by measurements at 1 min interval during four consecutive days [@fig-PAR-four-days].
```{r, echo=FALSE}
#| label: fig-PAR-four-days
#| fig-cap: Time course of PAR, UV-A and UV-B photon irradiance during four consecutive days with different cloud conditions. Measured with broad band-sensors at Viikki, Helsinki, Finland. The values plotted are 1 min means from measurements once every 5 s. The step changes at very low sun elevations are caused by shading from far-away buildings.
((ggplot(four_days_1min.data, aes(time_EET, PAR_umol)) +
geom_spct() +
annotate(geom = "text_npc", label = "PAR", npcy = 0.95, npcx = 0.05) +
labs(x = "Local time", y = "")) /
(ggplot(four_days_1min.data, aes(time_EET, UVA_umol)) +
geom_spct() +
annotate(geom = "text_npc", label = "UV-A", npcy = 0.95, npcx = 0.05) +
labs(x = "Local time", y = expression("Photon irradiance "*(mu*mol~s^{-1}~m^{-2})))) /
(ggplot(four_days_1min.data, aes(time_EET, UVB_umol)) +
geom_spct()) +
annotate(geom = "text_npc", label = "UV-B", npcy = 0.95, npcx = 0.05) +
labs(x = "Time", y = "")) +
plot_layout(axes = 'collect')
```
The photon ratios for the same four days [@fig-ratios-four-days] shows the
influence of clouds and of sun elevation, both affecting the PAR diffuse
fraction. In particular, broken clouds on
10 July cause momentary nearly doubling of the UV-A:PAR and UV-B:PAR photon
ratios. The short-term effect on the R:FR is smaller, but the differences
between days larger, possibly because of its dependence on the atmospheric
water content [@kotilainenxxxx].
```{r, echo=FALSE}
#| label: fig-ratios-four-days
#| fig-cap: Time course of red:far-red, UV-A:PAR and UV-B:PAR photon ratios during four consecutive days with different cloud conditions. Measured with broad band-sensors at Viikki, Helsinki, Finland. The values plotted are 1 min means from measurements once every 5 s; data shown for sun above the horizon. The step changes at very low sun elevations are caused by shading from far-away buildings. The PAR diffuse ratio varies with sun elevation in the range 0.12 to 1.0, as shown in @fig-diffuse-TUV for clear sky conditions. With the solar disk fully occluded by clouds or objects the diffuse ratio approches 1 at all sun elevations.
((ggplot(four_days_1min.data,
aes(time_EET,
ifelse(sun_elevation > 0, red_umol / far_red_umol, NA),
colour = PAR_diff_fr)) +
geom_point(na.rm = TRUE, size = 0.3) +
annotate(geom = "text_npc", label = "R:FR", npcy = 0.95, npcx = 0.05) +
labs(x = "Local time", y = "",
colour = expression("PAR diffuse fraction "*Q[s]/Q[t]))) /
(ggplot(four_days_1min.data,
aes(time_EET,
ifelse(sun_elevation > 0, UVA_umol / PAR_umol, NA),
colour = PAR_diff_fr)) +
geom_point(na.rm = TRUE, size = 0.3) +
annotate(geom = "text_npc", label = "UV-A:PAR", npcy = 0.95, npcx = 0.05) +
labs(x = "Local time", y = expression("Photon ratio "*(mol~mol^{-1})),
colour = expression("PAR diffuse fraction "*Q[s]/Q[t]))) /
(ggplot(four_days_1min.data, aes(time_EET,
ifelse(sun_elevation > 0, UVB_umol / PAR_umol, NA),
colour = PAR_diff_fr)) +
geom_point(na.rm = TRUE, size = 0.3) +
annotate(geom = "text_npc", label = "UV-B:PAR", npcy = 0.95, npcx = 0.05) +
labs(x = "Time", y = "",
colour = expression("PAR diffuse fraction "*Q[s]/Q[t])))) +
plot_layout(axes = 'collect', guides = 'collect')
```
In @fig-ratios-four-days we can see fast changes in the ratios towards the end of the day. The time-series of spectra measured once a minute during 5 minutes shows how the shape of the spectrum can rapidly change leading to rapid changes in the R:FR photon ratio [@fig-spectra-anim-five-min].
```{r, include=FALSE}
# prepare data
my_sun.spct <- sun_evening.spct
time <- when_measured(my_sun.spct, as.df = TRUE)
my_sun.spct$spct.idx <-
factor(my_sun.spct$spct.idx,
levels = time[["spct.idx"]],
labels = as.character(round_date(time[["when.measured"]], unit = "minute")))
```
```{r, echo=FALSE}
#| label: fig-spectra-anim-five-min
#| fig-cap: Time series of spectra near sunset. Five minutes of solar spectra measurements. Measured with an array spectrometer at Viikki, Helsinki, Finland.
ggplot(data = my_sun.spct, unit.out = "photon") +
geom_spct() +
scale_x_wl_continuous() +
scale_y_s.q.irrad_continuous() +
transition_states(spct.idx,
transition_length = 2,
state_length = 1) +
ggtitle('Now showing {closest_state} UTC',
subtitle = 'Frame {frame} of {nframes}')
```
The low irradiance periods interspersed with higher irradiance ones, caused by moving clouds intermittently occluding the sun, or cloud flecks, have a haphazard dynamics that is very dependent on the cloud type and wind conditions. One way of describing them is by estimating an empirical probability density function for their duration [@fig-cloudflecks-edf inset]. The distribution is not Normal, and skewed to the right. Semi-transparent and small clouds attenuate irradiance less that large thick clouds and at the same time create cloud flecks of shorter duration [@fig-cloudflecks-edf main panel].
```{r, echo=FALSE}
#| label: fig-cloudflecks-edf
#| fig-cap: xxxxxxxxxxxxxxx. In the main panel a quantile regression is shown, with the line repesenting the median, the dark band enclosing the two central quantiles (simmilar to the box in a box plot) and the pale band enclosing 90% of the observations. The inset shows the fitted empirical density function (EDF) for the duration of the cloudflecks. Source @Sellaro2024.
dfS <- read.csv("light-environment-data/dfCloudfleck_figure.csv")
p2 <-
ggplot(data = dfS, aes(percDiff * 100, duration)) +
stat_quant_band(formula = y ~ qss(x, lambda = 50, constraint = "I"), method = "rqss:sfnc",
quantiles = c(0.05, 0.5, 0.95), alpha = 0.2) +
stat_quant_band(formula = y ~ qss(x, lambda = 50, constraint = "I"), method = "rqss:sfnc") +
stat_quadrant_counts(quadrants = 0L,
label.x = 0.95,
label.y = 0.05) +
labs(x = "PAR attenuation during cloudfleck (%)",
y = "Cloudfleck duration (min)") +
theme_classic()
p3 <- ggplot(data = dfS, aes(duration)) +
stat_density(bw = "nrd", alpha = 0.3) +
scale_x_log10(breaks = c(2, 5, 10, 20, 50, 100, 200, 500)) +
expand_limits(x = c(1.7, 500)) +
labs(x = "Cloudfleck duration (min)",
y = "Empirical probability density")
print(p2 + scale_y_continuous(breaks = c(0, 60, 120, 180, 240, 300)) +
annotate(geom = "plot", x = I(0.05), y = I(1), label = p3, vp.width = 2/3, vp.height = 2/3))
```
```{r, echo=FALSE}
## cloudfleck spectrum
{
photon_as_default()
CSCLDY.df <- read.csv("light-environment-data/dfCSCLDY.csv")[ , -1]
names(CSCLDY.df)[names(CSCLDY.df) == "s.q.irrad"] <- "s.e.irrad" # error in data file
CSCLDY.df <- subset(CSCLDY.df, w.length > 295 & w.length < 850)
CSCLDY.df$type.label <- gsub("\\.", " ", CSCLDY.df$type)
CSCLDY.spct <- as.source_spct(CSCLDY.df,
idfactor = "type",
multiple.wl = length(unique(CSCLDY.df$type)))
CSCLDY.mspct <- e2q(subset2mspct(CSCLDY.spct))
}
```
Irradiance is much lower under cloud cover than in full sunlight under a clear sky [@fig-spectrum-cloudfleck A]. The cloud light spectrum is shifted towards shorter wavelengths, i.e., shade light is bluer (or cooler) than full sunlight, more easily seen after scaling the spectra to equal irradiance [@fig-spectrum-cloudfleck B]. For this example the coordinated colour temperature (CCT) was `r round(spct_CCT(CSCLDY.mspct[["overcast"]]), -1)` K and `r round(spct_CCT(CSCLDY.mspct[["clear.sky"]]), -1)` K for the overcast and clear sky, respectively.
```{r, echo=FALSE}
#| label: fig-spectrum-cloudfleck
#| fig-cap: Spectra during and immediately after a cloudfleck. A: photon spectral irradiance as measured, B: same data scaled to equal PAR of $1\,000 \mu mol\,s^{-1}\,m^{-2}$. Source @Sellaro2024.
{
photon_as_default()
p.cloudy.scaled <-
ggplot(data = fscale(CSCLDY.spct,
f = q_irrad , target = 1e-3, w.band = PAR()),
aes(w.length, s.q.irrad, colour = type)) +
# geom_vline(xintercept = c(400, 700), linetype = "dotted") +
stat_wl_strip(ymin = -0.015e-6, ymax = -0.003e-6) +
scale_fill_identity() +
geom_line() +
scale_y_s.q.irrad_continuous(unit.exponent = -6) +
scale_x_wl_continuous() +
scale_colour_manual(values = c("steelblue3", "grey20")) +
labs(tag = "B", colour = "")
p.cloudy.irrad <-
ggplot(data = e2q(CSCLDY.spct),
aes(w.length, s.q.irrad, colour = type)) +
# geom_vline(xintercept = c(400, 700), linetype = "dotted") +
stat_wl_strip(ymin = -0.19e-6, ymax = -0.03e-6) +
geom_line() +
scale_fill_identity() +
scale_y_s.q.irrad_continuous(unit.exponent = -6) +
scale_x_wl_continuous() +
scale_colour_manual(values = c("steelblue3", "grey20")) +
labs(tag = "A", colour = "")
p.cloudy.irrad / p.cloudy.scaled +
plot_layout(axes = "collect", guides = "collect")
}
```
#### Elevation
**We have three options: 1) generate suitable data with TUV, 2) show measured data from the literature, or 3) both.**
A simulation with the TUV model shows how attenuation by the atmosphere decreases with increasing ground altitude under a clear sky, with all other parameters kept unchanged, including surface albedo. As above, we show the actual spectral irradiance and the spectral irradiances scaled to equal PAR photon irradiance. The effect is not huge and the decrease in attenuation is stronger at shorter wavelengths [@fig-spectrum-ground-altitude].
```{r, echo=FALSE}
#| label: fig-spectrum-ground-altitude
#| fig-cap: Spectra under clear sky and a sun elevation of 45 degrees at different ground altitudes. A: photon spectral irradiance as computed with the TUV model, B: same data scaled to equal PAR of $1\,000 \mu mol\,s^{-1}\,m^{-2}$.
{
photon_as_default()
# avoid repeated calls to Quick TUV server
altitudes.file.path <- "light-environment-data/ground_altitudesx2.mspct.rda"
if (file.exists(altitudes.file.path)) {
load(altitudes.file.path)
} else {
ground_altitudes.mspct <-
qtuv_m_s.e.irrad(
ground.altitude = c(0, 8),
w.length = 290:750,
sun.elevation = 45
)
save(ground_altitudes.mspct, file = altitudes.file.path)
}
names(ground_altitudes.mspct) <- paste(c(0, 8), "km")
p.altitude.scaled <-
ggplot(data = fscale(ground_altitudes.mspct,
f = q_irrad , target = 1e-3, w.band = PAR()),
aes(w.length, s.q.irrad, colour = spct.idx)) +
# geom_vline(xintercept = c(400, 700), linetype = "dotted") +
stat_wl_strip(ymin = -0.015e-6, ymax = -0.003e-6) +
scale_fill_identity() +
geom_line() +
scale_y_s.q.irrad_continuous(unit.exponent = -6) +
scale_x_wl_continuous() +
scale_colour_manual(values = c("darkgreen", "grey20")) +
labs(tag = "B", colour = "")
p.altitude.irrad <-
ggplot(data = e2q(ground_altitudes.mspct),
aes(w.length, s.q.irrad, colour = spct.idx)) +
# geom_vline(xintercept = c(400, 700), linetype = "dotted") +
stat_wl_strip(ymin = -0.19e-6, ymax = -0.03e-6) +
geom_line() +
scale_fill_identity() +
scale_y_s.q.irrad_continuous(unit.exponent = -6) +
scale_x_wl_continuous() +
scale_colour_manual(values = c("darkgreen", "grey20")) +
labs(tag = "A", colour = "")
p.altitude.irrad / p.altitude.scaled +
plot_layout(axes = "collect", guides = "collect")
}
```
In @fig-spectrum-ground-altitude we show the spectra for two extreme altitudes, sea level and 8000 m above sea level. In @fig-irradiance-ground-altitude we show the increase in UV-B, UV-A2, UV-A1 and PAR with ground altitude.
```{r, echo=FALSE}
#| label: fig-irradiance-ground-altitude
#| fig-cap: Increase in PAR and UV irradiances at ground level under clear sky as a function of ground altitude above sea level. Based on spectral simulations with TUV for sun elevation 45 degrees. UVA-1, violet; UV-A2, purple; UV-B, black.
{
photon_as_default()
# avoid repeated calls to Quick TUV server
altitudes.file.path <- "light-environment-data/ground_altitudesx9.mspct.rda"
if (file.exists(altitudes.file.path)) {
load(altitudes.file.path)
} else {
ground_altitudesx9.mspct <-
qtuv_m_s.e.irrad(
ground.altitude = 0:8,
w.length = 290:750,
sun.elevation = 45
)
save(ground_altitudesx9.mspct, file = altitudes.file.path)
}
names(ground_altitudesx9.mspct) <- as.character(0:8)
ground_altitudesx9.spct <- rbindspct(ground_altitudesx9.mspct, idfactor = "Altitude")
q_irrads_altitude.tb <- q_irrad(ground_altitudesx9.spct, Plant_bands("CIE"), scale.factor = 1e6)
q_irrads_altitude.tb$Altitude <- as.numeric(q_irrads_altitude.tb$Altitude)
ggplot(q_irrads_altitude.tb, aes(Altitude)) +
geom_line(aes(y = ((`Q_]UVB.CIE` / `Q_]UVB.CIE`[1]) - 1) * 100), colour = "black") +
geom_line(aes(y = ((Q_UVA2.CIE / Q_UVA2.CIE[1]) - 1) * 100), colour = "purple") +
geom_line(aes(y = ((Q_UVA1.CIE / Q_UVA1.CIE[1]) - 1) * 100), colour = "violet") +
geom_line(aes(y = ((Q_PAR / Q_PAR[1]) - 1) * 100), colour = "orange") +
scale_y_continuous(name = "Increase in photon irradiance (%)") +
scale_x_continuous(name = "Ground altitude (km a.s.l.)")
}
```
In reality, the different variables shown individually in the examples in this chapter, can vary in ways that their effects add up, so the total range of variation is larger than is apparent in the examples above.
#### Seasons and latitude
The main determinant of changes in spectral irradiance with seasons and latitude is the changing path of the sun through the sky, and consequently of the sun elevation angle. However, several other variables play a role. The depth of the ozone column varies with latitude and seasonally. Clouds and the water column depth vary locally and with latitude, as does ground elevation and albedo. The result is that spectral irradiance varies in time and space. One way of summarising this is to describe the local climatology using maps with monthly averages over several years interpolated over a geographic grid. One approach is to use data retrieved by instruments on board satellites. An alternative approach is to describe terrestrial solar radiation at locations where ground-level measurements are available. We start with examples based on this second approach and observations with broadband sensors from ELDONET. From the perspective of research with plants the availability of UV-B, UV-A and PhR data measured concurrently is important.
Considering a longer time scale of years, seasonal and daily variation are combined. The seasonal pattern of variation in UV-B daily exposure is very clear but has day to day variation superimposed [@fig-UVB-Erlangen A]. The strong change in the UV-B:PAR photon ratio with sun elevation creates a strong seasonal pattern in this photon ratio [@fig-UVB-Erlangen B].
```{r, echo=FALSE, message=FALSE, warning=FALSE}
#| label: fig-UVB-Erlangen
#| fig-cap: Seasonal variation in daily UV-B energy exposure Erlangen, Germany ($54^\circ 10^\prime \mathrm{N}, 07^\circ 51^\prime \mathrm{E}$, 280 m asl). A: daily UV-B energy exposure; B: UV-B:PhR daily-energy-exposures ratio. Note: there are gaps with missing data in the time-series data. Measured with ELDONET instruments [see, @Haeder2007].
Erlangen.data <- read.csv("./light-environment-data/Erlangen_data.csv") |>
subset(!grepl("1997$", Datum))
Erlangen.data <- within(Erlangen.data, date <- as.Date(Datum, "%d/%m/%Y"))
fig <- ggplot(data=Erlangen.data,aes(x=date,y=UVB/1000)) +
geom_spct() +
# geom_point(size=1, colour="black", alpha=0.33) +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
labs(x="Time", y=expression("UV-B exposure"~~(kJ~d^{-1}~m^{-2})),
tag = "A")
fig_ratio <- ggplot(data=Erlangen.data,aes(x=date,y=UVB/PAR*1000)) +
geom_spct() +
# geom_point(size=1, colour="black", alpha=0.33) +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
labs(x="Time", y=expression("UV-B:PhR ratio"~~(1%*%10^3)),
tag = "B")
print((fig / fig_ratio) + plot_layout(axes = "collect") )
```
UV-B irradiance increases with elevation in mountains and with decreasing latitude [@fig-UVB-latitude] and is particularly high on high mountains in equatorial regions.
[^2]: *Tree line* is the highest elevation on a mountain slope at which tree species are naturally able to grow.
```{r, echo=FALSE, message=FALSE}
#| label: fig-UVB-latitude
#| fig-cap: Latitudinal variation in annual UV-B and UV-A energy exposure in the Northern Hemisphere. Measured with ELDONET instruments. The ground altitude for these locations varies [see, @Haeder2007].
latitudinal.data <-
read.csv("./light-environment-data/latitudinal_data.csv") |>
within(latitude <- degrees + minutes/60)
fig_UVB <- ggplot(data=latitudinal.data,aes(x=latitude,y=UVB)) +
stat_poly_line(linetype = "dashed", se = FALSE) +
geom_point(size=3, pch=16, colour="red")+
geom_text_repel(aes(label=Location), colour="Black", size=3.5,
position = position_nudge_line(x = 0.05, y = 0.5, direction = "split",
line_nudge = 5),
min.segment.length = 0, point.padding = 0.4, box.padding = 0.3) +
expand_limits(y = c(0,13), x = c(25,75)) +
labs(x="Latitude N (degrees)",
y=expression("Annual exposure"~~(MJ~m^{-2})),
tag = "UV-B")
fig_UVA <- ggplot(data=latitudinal.data,aes(x=latitude,y=UVA)) +
stat_poly_line(linetype = "dashed", se = FALSE) +
geom_point(size=3, pch=16, colour="red")+
geom_text_repel(aes(label=Location), colour="Black", size=3.5,
position = position_nudge_line(x = 0.03, y = 200, direction = "split",
line_nudge = 3),
min.segment.length = 0, point.padding = 0.4, box.padding = 0.3) +
expand_limits(y = c(0,3300), x = c(25,75)) +
labs(x="Latitude N (degrees)",
y=expression("Annual exposure"~~(MJ~m^{-2})),
tag = "UV-A")
print((fig_UVB / fig_UVA) + plot_layout(axes = "collect"))
```
**I have data for 2-3 years time series measured with the same sensors as the four days plot above. Could be added as they include separate measurements of UV-B, UV-A2, UV-A1, blue, red, far-red and PAR irradiances. Maybe a figure with daily averages per month or week or showing a moving average or moving median could be informative. What do you think?**
**Maps for global radiation, UV-B, and UV-A could be added to this section. The daily data are available on-line on a 1/2 degree grid for the last >10 years. Some biologically effective exposures are also available. This is a lot of data. I have already downloaded the last 12 months. Downloading the whole time series with worldwide coverage and computing long-term means would be doable I think, but time consuming. For the time being here are maps for UV-B and UV-A with monthly means of daily doses for the whole world.**
{#fig-uvb-world-map width=100%}
{#fig-uva-world-map width=100%}
{#fig-uvb-europe-map width=100%}
{#fig-uva-europe-map width=100%}