Skip to contents

Introduction

Package ‘photobiologyInOut’ since 2018, versions (>= 0.4.15), has supported the import of the output files from interactively run spectral simulations done in the Quick TUV calculator, as exemplified in the User Guide.

In versions (>= 0.4.29) it is also possible to call the Quick TUV calculator server directly from within R, to get the spectral data returned in a source_spct object or in a source_mspct ready to be used with functions from the R for Photobiology suite, or used as data frames with other R code. Examples demostrating this new feature need to access the UCAR server, and because of this, they included in this article, that is part of the on-line documentation, but not distributed as part of the package.

The implementation of the interaction with the server is inspired on that used in package ‘foqat’. This is actually very simple: to construct URLs identical to those generated by the [Quick TUV calculator web interface] and use them to download the computed spectral data into a file. This file is already in a format supported by function read_qtuv_txt() avaialble in package ‘photobiologyInOut’ (>= 0.4.15).

## Loading required package: photobiology
## News at https://www.r4photobiology.info/
## Loading required package: ggplot2
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
theme_set(theme_bw() + theme(legend.position = "top"))

force_qtuv_call <- FALSE # set to TRUE to recalculate cached QTUV-generated data

In this file, we call the Quick TUV calculator server only once and save the returned value in a local file. Please, consider using this same approach in your scripts to avoid unnecessarily overloading the public server.

Simulation of a single spectrum

In the first example, using default arguments for all parameters except time, the text returned by the sever is saved into a temporary file, the data read into a source_spct() and the file deleted. The default for sun.elevation is NULL and the sun elevation is compute from the geographic coordinates and time coordinates.

# avoid repeated calls to Quick TUV server
sun_greenwich.spct.filepath <- "./sun-greenwich.spct.rda"
if (!force_qtuv_call && file.exists(sun_greenwich.spct.filepath)) {
  load(sun_greenwich.spct.filepath)
} else {
  sun_greenwich.spct <- 
    qtuv_s.e.irrad(time = ymd_hm("2024-06-23 12:00", tz = "UTC"))
  save(sun_greenwich.spct, file = sun_greenwich.spct.filepath)
}
colnames(sun_greenwich.spct)
## [1] "w.length"            "s.e.irrad"           "s.e.irrad.dir"      
## [4] "s.e.irrad.diff.down" "s.e.irrad.diff.up"

Variable s.e.irrad corresponds to the total down-welling spectral irradiance. Variable s.e.irrad.dir is direct, s.e.irrad.diff.down is down-welling diffuse radiation and s.e.irrad.diff.up is up-welling diffuse radiation.

The summary shows in addition to a summary of the data, some impotant metadata.

summary(sun_greenwich.spct)
## Summary of source_spct [140 x 5] object: sun_greenwich.spct
## Wavelength range 280.5-419.5 nm, step 1 nm
## Label: Solar spectrum (model simulation). File: file30e01a984eed 
## Measured on 2024-06-23 12:00:00 UTC 
## Measured at 51.5 N, 0 E; Greenwich 
## Variables:
##  w.length: Wavelength [nm]
##  s.e.irrad: Spectral energy irradiance [W m-2 nm-1] 
## --
##     w.length       s.e.irrad      s.e.irrad.dir     s.e.irrad.diff.down
##  Min.   :280.5   Min.   :0.0000   Min.   :0.00000   Min.   :0.0000     
##  1st Qu.:315.2   1st Qu.:0.2241   1st Qu.:0.08329   1st Qu.:0.1368     
##  Median :350.0   Median :0.5804   Median :0.27660   Median :0.2916     
##  Mean   :350.0   Mean   :0.5620   Mean   :0.30947   Mean   :0.2525     
##  3rd Qu.:384.8   3rd Qu.:0.7776   3rd Qu.:0.43155   3rd Qu.:0.3544     
##  Max.   :419.5   Max.   :1.3310   Max.   :0.86390   Max.   :0.4865     
##  s.e.irrad.diff.up
##  Min.   :0.00000  
##  1st Qu.:0.02241  
##  Median :0.05804  
##  Mean   :0.05620  
##  3rd Qu.:0.07777  
##  Max.   :0.13310
cat(comment(sun_greenwich.spct))
## SPECTRAL IRRADIANCE (W m-2 nm-1):
## computed by UCAR's Quick TUV calculator
## read from file: file30e01a984eed
## with modification date: 2024-10-04 15:48:11
## ozone column: 300 DU
## sun elevation: 61.9078712 degrees
## ground altitude: 0 km a.s.l.
## observer altitude: 0 km a.s.l.
what_measured(sun_greenwich.spct)
## [1] "Solar spectrum (model simulation). File: file30e01a984eed"
when_measured(sun_greenwich.spct)
## [1] "2024-06-23 12:00:00 UTC"
how_measured(sun_greenwich.spct)
## [1] "Computer simulation with the TUV model version 5.3 Using -2 streams."
where_measured(sun_greenwich.spct)
## # A tibble: 1 × 3
##     lon   lat address  
##   <dbl> <dbl> <chr>    
## 1     0  51.5 Greenwich
attr(sun_greenwich.spct, "qtuv.url")
## [1] "https://www.acom.ucar.edu/cgi-bin/acom/TUV/V5.3/tuv?wStart=280&wStop=420&wIntervals=140&inputMode=0&latitude=51.5&longitude=0&date=20240623&timeStamp=12:00:00&zenith=0&ozone=300&albedo=0.1&gAltitude=0&mAltitude=0&taucld=0&zbase=4&ztop=5&tauaer=0.235&ssaaer=0.99&alpha=1&time=12&outputMode=5&nStreams=-2&dirsun=1&difdn=1&difup=0"
autoplot(sun_greenwich.spct)

To save the raw output from TUV into a persistent file, we have to provide a file name. We also extend the wavelength range, and provide the coordinates of a different location.

my.geocode <- data.frame(lon = 24.96474, lat = 60.20911)
# avoid repeated calls to Quick TUV server
sun_viikki.spctfile.path <- "./sun-viikki.spct.rda"
if (!force_qtuv_call && file.exists(sun_viikki.spctfile.path)) {
  load(sun_viikki.spctfile.path)
} else {
  sun_viikki.spct <-
    qtuv_s.e.irrad(time = ymd_hm("2024-06-23 12:20", tz = "EET"),
                   w.length = 290:800,
                   geocode = my.geocode,
                   file = "qtuv-viikki-test.txt")
  save(sun_viikki.spct, file = sun_viikki.spctfile.path)
}
summary(sun_viikki.spct)
## Summary of source_spct [510 x 5] object: sun_viikki.spct
## Wavelength range 290-800 nm, step 1-1.005 nm
## Label: Solar spectrum (model simulation). File: qtuv-viikki-test.txt 
## Measured on 2024-06-23 09:20:00 UTC 
## Variables:
##  w.length: Wavelength [nm]
##  s.e.irrad: Spectral energy irradiance [W m-2 nm-1] 
## --
##     w.length       s.e.irrad         s.e.irrad.dir       s.e.irrad.diff.down
##  Min.   :290.0   Min.   :0.0000001   Min.   :0.0000001   Min.   :0.0000001  
##  1st Qu.:417.5   1st Qu.:0.8552000   1st Qu.:0.6554250   1st Qu.:0.1426250  
##  Median :545.0   Median :1.0635000   Median :0.8748000   Median :0.2267500  
##  Mean   :545.0   Mean   :0.9653408   Mean   :0.7315923   Mean   :0.2337341  
##  3rd Qu.:672.5   3rd Qu.:1.2085000   3rd Qu.:0.9469750   3rd Qu.:0.3135750  
##  Max.   :800.0   Max.   :1.4020000   Max.   :1.0090000   Max.   :0.4465000  
##  s.e.irrad.diff.up  
##  Min.   :1.000e-08  
##  1st Qu.:8.552e-02  
##  Median :1.064e-01  
##  Mean   :9.653e-02  
##  3rd Qu.:1.208e-01  
##  Max.   :1.402e-01

The file was not deleted because a file name was passed as aargument in the call above.

file.exists("qtuv-viikki-test.txt")
## [1] TRUE

The spectrum was also imported into R as a source_spct object for which an autoplot() method is defined in package ‘ggspectra’.

autoplot(sun_viikki.spct)

autoplot(sun_viikki.spct, unit.out = "photon")

Simulation of a collection of spectra

Not to abuse the use of the UCAR server, the examples in this section create collections of very few spectra. The functions do not impose any strict limit but keep in mind that the recommended maximum is 100 spectra per user and day.

Four different ozone column thicknesses.

# avoid repeated calls to Quick TUV server
sun_viikki_ozone.mspct.filepath <- "./sun_viikki_ozone.mspct.rda"
if (!force_qtuv_call && file.exists(sun_viikki_ozone.mspct.filepath)) {
  load(sun_viikki_ozone.mspct.filepath)
} else {
  sun_viikki_ozone.mspct <- 
    qtuv_m_s.e.irrad(
      time = ymd_hm("2024-06-23 12:20", tz = "EET"),
      ozone.du = c(200, 250, 300, 350),
      w.length = 290:450,
      geocode = my.geocode
    )
  save(sun_viikki_ozone.mspct, file = sun_viikki_ozone.mspct.filepath)
}

summary(sun_viikki_ozone.mspct)
## Summary of source_mspct [4 x 1] object: sun_viikki_ozone.mspct
## # A tibble: 4 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 ozone.200 sour… [160…         290.         450. <chr>              1 second   
## 2 ozone.250 sour… [160…         290.         450. <chr>              1 second   
## 3 ozone.300 sour… [160…         290.         450. <chr>              1 second   
## 4 ozone.350 sour… [160…         290.         450. <chr>              1 second
autoplot(sun_viikki_ozone.mspct, 
         range = c(NA, 340), 
         annotations = c("-", "peaks"))

Three sun elevation angles above the horizon in degrees. Geographic and time coordinates are used to compute the sun elevation angle when the angle is not supplied as in other examples, but if the angle is passed as an argument, they are ignored.

# avoid repeated calls to Quick TUV server
sun_viikki_elevation.mspct.filepath <- "./sun_viikki_elevation.mspct.rda"
if (!force_qtuv_call && file.exists(sun_viikki_elevation.mspct.filepath)) {
  load(sun_viikki_elevation.mspct.filepath)
} else {
  sun_viikki_elevation.mspct <- 
    qtuv_m_s.e.irrad(
      sun.elevation = c(60, 40, 20),
      w.length = 290:450,
      geocode = my.geocode
    )
  save(sun_viikki_elevation.mspct, file = sun_viikki_elevation.mspct.filepath)
}

summary(sun_viikki_elevation.mspct)
## Summary of source_mspct [3 x 1] object: sun_viikki_elevation.mspct
## # A tibble: 3 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 sun.elev… sour… [160…         290.         450. <chr>              1 second   
## 2 sun.elev… sour… [160…         290.         450. <chr>              1 second   
## 3 sun.elev… sour… [160…         290.         450. <chr>              1 second
autoplot(sun_viikki_elevation.mspct)

Three different times of the day.

# avoid repeated calls to Quick TUV server
sun_viikki_time.mspct.filepath <- "./sun_viikki_time.mspct.rda"
if (!force_qtuv_call && file.exists(sun_viikki_time.mspct.filepath)) {
  load(sun_viikki_time.mspct.filepath)
} else {
  sun_viikki_time.mspct <- 
    qtuv_m_s.e.irrad(
      time = ymd_hm("2024-06-23 12:20", tz = "EET") +
        hours(c(0, 3, 6)),
      w.length = 290:750,
      geocode = my.geocode
    )
  save(sun_viikki_time.mspct, file = sun_viikki_time.mspct.filepath)
}

summary(sun_viikki_time.mspct)
## Summary of source_mspct [3 x 1] object: sun_viikki_time.mspct
## # A tibble: 3 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 time.202… sour… [460…          290          750 <chr>              1 second   
## 2 time.202… sour… [460…          290          750 <chr>              1 second   
## 3 time.202… sour… [460…          290          750 <chr>              1 second
autoplot(sun_viikki_time.mspct, facets = 1)

Three different latitudes.

my.geocodes <- data.frame(lat = c(30, 50, 70),
                          lon = rep(25, 3))
my.geocodes[["address"]] <- 
  paste("lat", my.geocodes[["lat"]], sep = ".")
# avoid repeated calls to Quick TUV server
sun_latitudes.mspct.filepath <- "./sun_latitudes.mspct.rda"
if (!force_qtuv_call && file.exists(sun_latitudes.mspct.filepath)) {
  load(sun_latitudes.mspct.filepath)
} else {
  sun_latitudes.mspct <- 
    qtuv_m_s.e.irrad(
      time = ymd_hm("2024-06-23 12:20", tz = "EET"),
      w.length = 290:750,
      geocode = my.geocodes
    )
  save(sun_latitudes.mspct, file = sun_latitudes.mspct.filepath)
}

summary(sun_latitudes.mspct)
## Summary of source_mspct [3 x 1] object: sun_latitudes.mspct
## # A tibble: 3 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 geocode.… sour… [460…          290          750 <chr>              1 second   
## 2 geocode.… sour… [460…          290          750 <chr>              1 second   
## 3 geocode.… sour… [460…          290          750 <chr>              1 second
autoplot(sun_latitudes.mspct, facets = 1)

Six different ground altitudes, with measurements at ground level.

# avoid repeated calls to Quick TUV server
ground_altitudes.mspct.filepath <- "./ground_altitudes.mspct.rda"
if (!force_qtuv_call && file.exists(ground_altitudes.mspct.filepath)) {
  load(ground_altitudes.mspct.filepath)
} else {
  ground_altitudes.mspct <-
    qtuv_m_s.e.irrad(
      ground.altitude = 0:5,
      w.length = 290:450,
      sun.elevation = 45
    )
  save(ground_altitudes.mspct, file = ground_altitudes.mspct.filepath)
}

summary(ground_altitudes.mspct)
## Summary of source_mspct [6 x 1] object: ground_altitudes.mspct
## # A tibble: 6 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 altitude… sour… [160…         290.         450. <chr>              1 second   
## 2 altitude… sour… [160…         290.         450. <chr>              1 second   
## 3 altitude… sour… [160…         290.         450. <chr>              1 second   
## 4 altitude… sour… [160…         290.         450. <chr>              1 second   
## 5 altitude… sour… [160…         290.         450. <chr>              1 second   
## 6 altitude… sour… [160…         290.         450. <chr>              1 second
autoplot(ground_altitudes.mspct, 
         range = c(NA, 340), 
         annotations = c("-", "peaks"))

Two different cloud conditions.

my.clouds <- qtuv_clouds(c("clear.sky", "cirrus"))
# avoid repeated calls to Quick TUV server
sun_viikki_clouds.mspct.filepath <- "./sun_viikki_clouds.mspct.rda"
if (!force_qtuv_call && file.exists(sun_viikki_clouds.mspct.filepath)) {
  load(sun_viikki_clouds.mspct.filepath)
} else {
  sun_viikki_clouds.mspct <- 
    qtuv_m_s.e.irrad(
      time = ymd_hm("2024-06-23 12:20", tz = "EET"),
      w.length = 290:750,
      geocode = my.geocode,
      clouds = my.clouds
    )
  save(sun_viikki_clouds.mspct, file = sun_viikki_clouds.mspct.filepath)
}

summary(sun_viikki_clouds.mspct)
## Summary of source_mspct [2 x 1] object: sun_viikki_clouds.mspct
## # A tibble: 2 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 clouds.c… sour… [460…          290          750 <chr>              1 second   
## 2 clouds.c… sour… [460…          290          750 <chr>              1 second
autoplot(sun_viikki_clouds.mspct, facets = 1)

For three times, as above, but keeping the downloaded files, by passing a file name “root”.

# avoid repeated calls to Quick TUV server
sun_viikki_time.mspct.filepath <- "./sun_viikki_time.mspct.rda"
if (!force_qtuv_call && file.exists(sun_viikki_time.mspct.filepath)) {
  load(sun_viikki_time.mspct.filepath)
} else {
sun_viikki_time.mspct <- 
  qtuv_m_s.e.irrad(
    time = ymd_hm("2024-06-23 12:20", tz = "EET") +
      hours(c(0, 3, 6)),
    w.length = 290:750,
    geocode = my.geocode,
    file = "qtuv-viikki"
  )
  save(sun_viikki_time.mspct, file = sun_viikki_time.mspct.filepath)
}

summary(sun_viikki_time.mspct)
## Summary of source_mspct [3 x 1] object: sun_viikki_time.mspct
## # A tibble: 3 × 8
##   spct.idx  class dim   w.length.min w.length.max colnames multiple.wl time.unit
##   <chr>     <chr> <chr>        <dbl>        <dbl> <list>         <dbl> <chr>    
## 1 time.202… sour… [460…          290          750 <chr>              1 second   
## 2 time.202… sour… [460…          290          750 <chr>              1 second   
## 3 time.202… sour… [460…          290          750 <chr>              1 second
autoplot(sun_viikki_time.mspct, facets = 1)

list.files(pattern = "^qtuv-viikki-time.*")
## [1] "qtuv-viikki-time.2024.06.23.12.20.00.txt"
## [2] "qtuv-viikki-time.2024.06.23.15.20.00.txt"
## [3] "qtuv-viikki-time.2024.06.23.18.20.00.txt"