Module tests for plot_OSLcurve()

Author

Dirk Mittelstraß

Published

August 31, 2025

This document collects scripts for developing, bug-fixing and pre-release validating a specific function.

Found issues as for 2025-08-31:

ToDo:

Test sourcing

Is the function successfully sourced?

Show code
library(OSLdecomposition)
library(ggplot2)
library(Luminescence)
Welcome to the R package Luminescence version 1.1.0.9000-12 [Built: 2025-08-18 15:28:02 UTC]
A PhD supervisor: 'You are not depressive, you simply have a crappy life.'
Show code
source("../R/plot_OSLcurve.R")
source("../R/plot_MultiExponential.R") # core function

Define test cases

Show code
cases <- list()
add_component_case <- 
  function(cases, lambda, n, 
           channel_width = 0.1, 
           channel_number = 400, 
           noise = 0, 
           background = 0,
           reiterate = FALSE,
           title = "Unspecified test case"){
    
    components <- data.frame(name = paste("Component", 1:length(n)),
                             lambda = lambda, 
                             n = n)
    curve <- simulate_OSLcomponents(components, 
                                    channel.width = channel_width,
                                    channel.number = channel_number,
                                    simulate.curve = TRUE, 
                                    add.poisson.noise = TRUE,
                                    add.gaussian.noise = noise,
                                    add.background = background)
    
    # Re-iterate components to add some uncertainty
    if (reiterate) {
          components <- decompose_OSLcurve(curve, 
                                     components, 
                                     verbose = FALSE)
    }
  
    case <- list(components = components,
                 curve = curve,
                 title = title)
    
    cases <- c(cases, list(case))
    return(cases)
  }

cases <- add_component_case(cases,
                   lambda = c(1, 0.2, 0.02),
                   n = c(1000, 1000, 10000),
                   reiterate = TRUE,
                   title = "Typical quartz OSL curve")

cases <- add_component_case(cases,
                   lambda = c(2, 0.5, 0.2, 0.001),
                   n = c(200, -500, 2000, 100000),
                   channel_width = 0.875,
                   channel_number = 42,
                   title = "Atypical low-res measurement with stretched medium component")

cases <- add_component_case(cases,
                   lambda = c(0.1, 0.02),
                   n = c(-10e4, 10e5),
                   channel_width = 0.2,
                   channel_number = 200,
                   title = "Increasing signal")

cases <- add_component_case(cases,
                   lambda = c(1, 0.01),
                   n = c(10, 100),
                   noise = 10,
                   reiterate = TRUE,
                   title = "Background-corrected noise")

cases <- add_component_case(cases,
                   lambda = c(2, 0.5, 0.03, 0.001),
                   n = c(30e5, 2e6, -5e6, 5e8),
                   channel_width = 0.01,
                   channel_number = 5000,
                   title = "Sharp drop then complex, with many channels")

Test standard plotting

Default settings

Show code
for (case in cases) {
  plot_OSLcurve(case$curve, case$components,
                       title = case$title)
  cat("\n")
}

Changed on/off settings

Show code
for (case in cases) {
  plot_OSLcurve(case$curve, case$components,
                show.legend = FALSE,
                show.intervals = TRUE,
                show.crosssec = TRUE,
                show.initial = TRUE)
  cat("\n")
}
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

Test other display modes

Vary the argument “display”. Vary also the ggplot2 themes

Show code
modes <- c("lin","compare_lin","log","compare_log","loglog","LM","res","tab","raw")
themes <- list(ggplot2::theme_minimal(),
            ggplot2::theme_dark(),
            ggplot2::theme_classic(),
            ggplot2::theme_void(),
            ggplot2::theme_gray(),
            ggplot2::theme_linedraw(),
            ggplot2::theme_bw())

for (display_mode in modes) {
  
  case <- sample(cases, size = 1)[[1]]  # Random case
  cat("Display mode:", display_mode, "with case", case$title, "\n")
  

  
    tryCatch({
      plot_OSLcurve(case$curve, case$components,
                display = display_mode,
                show.legend = TRUE,
                theme.set = sample(themes, size = 1)[[1]],
                title = case$title)
    
    },error = function(e) {message("FOUND ERROR", conditionMessage(e))})
  
  cat("\n")
}
Display mode: lin with case Increasing signal 


Display mode: compare_lin with case Atypical low-res measurement with stretched medium component 


Display mode: log with case Atypical low-res measurement with stretched medium component 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.


Display mode: compare_log with case Atypical low-res measurement with stretched medium component 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.


Display mode: loglog with case Background-corrected noise 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Signal curve contains value equal/lower zero. Values below 0.1 will not be displayed.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.
Warning in transformation$transform(x): NaNs wurden erzeugt
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation introduced infinite values.
log-10 transformation introduced infinite values.
Warning in transformation$transform(x): NaNs wurden erzeugt
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.
Warning: Removed 24 rows containing missing values or values outside the scale range
(`geom_line()`).


Display mode: LM with case Background-corrected noise 


Display mode: res with case Increasing signal 


Display mode: tab with case Increasing signal 


Display mode: raw with case Increasing signal 
Input object 'curve' has more than two columns. Just the first two are used to define time and signal

Show code
# Repeat test but with 

for (display_mode in modes) {
  
  case <- sample(cases, size = 1)[[1]]  # Random case
  cat("Display mode:", display_mode, "with case", case$title, "\n")
  
  tryCatch({
    plot_OSLcurve(case$curve, case$components,
                display = display_mode,
                show.legend = FALSE,
                show.intervals = TRUE,
                theme.set = sample(themes, size = 1)[[1]])
    
    },error = function(e) {message("FOUND ERROR", conditionMessage(e))})
  
  cat("\n")
}
Display mode: lin with case Increasing signal 


Display mode: compare_lin with case Increasing signal 


Display mode: log with case Atypical low-res measurement with stretched medium component 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.


Display mode: compare_log with case Sharp drop then complex, with many channels 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.


Display mode: loglog with case Increasing signal 
Input data contains signal components with negative intensity (increasing signals). These can not be displayed with logarithmic y-axis and are therefore omitted.
Warning in ggplot2::scale_y_log10(limits = ylim): log-10 transformation
introduced infinite values.


Display mode: LM with case Typical quartz OSL curve 


Display mode: res with case Background-corrected noise 
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.


Display mode: tab with case Typical quartz OSL curve 


Display mode: raw with case Increasing signal 
Input object 'curve' has more than two columns. Just the first two are used to define time and signal

Display fit_OSLcurve output

Show code
# Create a simple curve with just one component
curve <- data.frame(
  X = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
  Y = c(377, 244, 163, 93, 59, 28, 17, 13, 10, 8, 9, 5))
# Perform fitting
components <- fit_OSLcurve(curve, F.threshold = 3, K.max = 2)

Decay rates (s^-1):
Cycle       Comp. 1     Comp. 2         RSS     F-value
K = 1        0.4594                   359.9         Inf 
K = 2        0.4594      0.4594       359.9   -4.76e-09 
Left loop because F-test value (F = -4.76e-09) fell below threshold value (F = 3)
-> The F-test suggests the K = 1 model 

Photoionisation cross sections (cm^2):
Cycle     Comp. 1              Comp. 2             
K = 1    6.47e-18 (Medium)    
K = 2    6.47e-18 (Medium)    6.47e-18 (Medium.a)  
-> Most known quartz OSL components found in the K = 2 model 
Show code
# Display results
plot_OSLcurve(curve, components)

Use RLum objects as input

Show code
# Load example data set
data_path <- system.file("examples", 
                         "FB_10Gy_SAR.bin", 
                         package = "OSLdecomposition")
data_set <- Luminescence::read_BIN2R(data_path, 
                                     fastForward = TRUE,
                                     verbose = FALSE)

# Separate components
data_set_decomposed <- RLum.OSL_decomposition(data_set, 
                                              decay_rates = c(0.8, 0.05))
STEP 2.1 ----- Define signal bin intervals -----
OSL curve template necessary but not given. Obtain and use global average curve:
[sum_OSLcurves()]: Built global average curve from arithmetic means from first 999 data points of all 154 OSL records
(time needed: 2.37 s)

Find intervals with lowest component cross correlation by maximising the denominator determinant in Cramers rule:
Maximum determinant = 0.7728 with interval dividing channels at i = 37 
(time needed: 0.12 s)

STEP 2.2 ----- Decompose each  OSL  curve -----
Calculate signal intensity n in each OSL  by ' det+nls ' algorithm with empiric error estimation
Table of input decay constants and signal bin intervals for [decompose_OSLcurve()]:
         name lambda  t.start     t.end ch.start ch.end
1 Component 1   0.80 0.000000  3.696296        1     37
2 Component 2   0.05 3.696296 99.800003       38    999


...........
Successfully decomposed 154 records
(time needed: 0.95 s)
Show code
cat("\nCase 1: Use raw RLum.Data.Curve as input\n")

Case 1: Use raw RLum.Data.Curve as input
Show code
plot_OSLcurve(data_set[[4]]@records[[1]])
Warning in plot_OSLcurve(data_set[[4]]@records[[1]]): No component curve given
neither in 'components' nor in 'curve'. Set 'display' = 'raw'

Show code
cat("\nCase 2: Use decomposed RLum.Data.Curve as input\n")

Case 2: Use decomposed RLum.Data.Curve as input
Show code
plot_OSLcurve(data_set_decomposed[[4]]@records[[1]])

Show code
cat("\nCase 3: Use data matrix as input\n")

Case 3: Use data matrix as input
Show code
plot_OSLcurve(data_set_decomposed[[4]]@records[[1]]@data)
Warning in plot_OSLcurve(data_set_decomposed[[4]]@records[[1]]@data): No
component curve given neither in 'components' nor in 'curve'. Set 'display' =
'raw'

Show code
cat("\nCase 4: Use invalid RLum object as input\n")

Case 4: Use invalid RLum object as input
Show code
tryCatch({plot_OSLcurve(data_set[[4]])}, 
         error = function(e) {
           message("TEST ERROR ", conditionMessage(e))
})
TEST ERROR [plot_OSLcurve()] Error: Input object 'curve' is not of type 'RLum.Data.Curve' or 'data.frame' or 'matrix'!

Wrong/missing input

Show code
case <- sample(cases, size = 1)[[1]]  # Random case

cat("\nCase 1: No component argument\n")

Case 1: No component argument
Show code
tryCatch({plot_OSLcurve(case$curve)}, 
         error = function(e) {
           message("TEST ERROR ", conditionMessage(e))
})
Input object 'curve' has more than two columns. Just the first two are used to define time and signal
Input object 'curve' has more than two columns. Just the first two are used to define time and signal

Show code
cat("\nCase 2: No curve argument\n")

Case 2: No curve argument
Show code
tryCatch({plot_OSLcurve(component = case$component)}, 
         error = function(e) {
           message("TEST ERROR ", conditionMessage(e))
})

Show code
cat("\nCase 3: No arguments at all\n")

Case 3: No arguments at all
Show code
tryCatch({plot_OSLcurve()}, 
         error = function(e) {
           message("TEST ERROR ", conditionMessage(e))
})
TEST ERROR [plot_OSLcurve()]: Either 'curve' or 'components' must be defined
Show code
cat("\nCase 4: Wrong argument\n")

Case 4: Wrong argument
Show code
tryCatch({plot_OSLcurve(case$component, case$curve)}, 
         error = function(e) {
           message("TEST ERROR ", conditionMessage(e))
})
TEST ERROR nicht-numerisches Argument für binären Operator

Don’t show plot

Show code
case <- sample(cases, size = 1)[[1]]  # Random case
plot_obj <- plot_OSLcurve(case$curve, case$components, hide.plot = TRUE)

# Try to display by itself
plot_obj
TableGrob (15 x 2) "arrange": 4 grobs
  z         cells    name           grob
1 1 ( 1-10, 1- 1) arrange gtable[layout]
2 2 (11-15, 1- 1) arrange gtable[layout]
3 3 ( 1-10, 2- 2) arrange gtable[layout]
4 4 (11-15, 2- 2) arrange gtable[layout]
Show code
# Try to display with print()
print(plot_obj)
TableGrob (15 x 2) "arrange": 4 grobs
  z         cells    name           grob
1 1 ( 1-10, 1- 1) arrange gtable[layout]
2 2 (11-15, 1- 1) arrange gtable[layout]
3 3 ( 1-10, 2- 2) arrange gtable[layout]
4 4 (11-15, 2- 2) arrange gtable[layout]
Show code
# Try to display with gridExtra function
gridExtra::grid.arrange(plot_obj)

Save plot as file it

The first plot is saved as PNG file and shown, the second plot is saved as PDF file and not shown. Both are saved in the working directory (D:/01_OSLdecomp/OSLdecomposition/module_tests). Please check and delete both files after executing this test.

Show code
file_name = paste0(getwd(), "/plot_OSLcurve_test.png")
cat("\nSave file:", file_name, "\n")

Save file: D:/01_OSLdecomp/OSLdecomposition/module_tests/plot_OSLcurve_test.png 
Show code
case <- sample(cases, size = 1)[[1]]  # Random case

plot_OSLcurve(case$curve, case$components,
              hide.plot = FALSE,
              filename = file_name)

Show code
cat("\n")
Show code
file_name = "plot_OSLcurve_test.pdf"
cat("\nSave file:", file_name, "\n")

Save file: plot_OSLcurve_test.pdf 
Show code
case <- sample(cases, size = 1)[[1]]  # Random case

plot_OSLcurve(case$curve, case$components,
              hide.plot = TRUE,
              filename = file_name)
cat("\n")