Module tests for decompose_OSLcurve()

Author

Dirk Mittelstraß

Published

August 31, 2025

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

Sourcing

Is the function successfully sourced?

library(OSLdecomposition)
source("../R/decompose_OSLcurve.R")

# For measuring execution time
library(tictoc)

# For printing tables
library(knitr)

Basic algorithm test

Create test curve

# Some common quartz CW-OSL measurement with a PMT
sim_components <- data.frame(name = c("Fast", "Medium", "Slow"),
                             lambda = c(1.5, 0.2, 0.01), 
                             n = c(1000, 1000, 20000),
                             n.error = NA)

# Simulate the curve, add shot noise, detector noise and detector background
curve <- simulate_OSLcomponents(sim_components, 
                                simulate.curve = TRUE, 
                                add.poisson.noise = TRUE,
                                add.gaussian.noise = 2,
                                add.background = 10)

# How does the curve look like?
plot(x = curve$time, y = curve$signal, type = "l")

Cycle algorithms, without background correction

results <- data.frame(NULL)

for (arg_algorithm in c("det", 
                        "nls", 
                        "det+nls")) {
  for (arg_error_est in c("empiric", 
                          "poisson", 
                          "only.bin.RSS", 
                          "none", 
                          2)) {
    
    case_title <- paste("Algorithm =", arg_algorithm,
                        "| Error est. =", arg_error_est)
    cat("\n", case_title, "\n")
    
    # Function call with return
    components <- decompose_OSLcurve(curve,
                                     sim_components,
                                     background.fitting = FALSE,
                                     algorithm = arg_algorithm,
                                     error.estimation = arg_error_est,
                                     verbose = TRUE)
    
    
    # Call the function now 1000 times to test performance and see if errors occur
    tic("Execution time")
    for (i in 1:1000) {
      suppressWarnings({
        decompose_OSLcurve(curve,
                           sim_components,
                           background.fitting = FALSE,
                           algorithm = arg_algorithm,
                           error.estimation = arg_error_est,
                           verbose = FALSE)
      })
    }
    tictoc <- toc()
    elapsed <- as.numeric((tictoc$toc - tictoc$tic)/1000)
    
    result <- data.frame(test = case_title,
                         t(components$n),
                         t(components$n.error),
                         time = elapsed)
    
    results <- rbind(results, result)
    
  }
}

 Algorithm = det | Error est. = empiric 
   name          n   n.error  bin.RSS bin.error initial.signal n.residual
   Fast   954.4813  50.82649 1143.165  34.91956         0.7559          0
 Medium  1127.6177 114.88515 3481.258  59.21104         0.1269          0
   Slow 20620.8451 333.07933 3379.889  58.25730         0.1172      13823
Execution time: 0.33 sec elapsed

 Algorithm = det | Error est. = poisson 
   name          n   n.error bin.error initial.signal n.residual
   Fast   954.4813  54.69045  38.78144         0.7559          0
 Medium  1127.6177 117.68928  59.53150         0.1269          0
   Slow 20620.8451 353.84590  61.90315         0.1172      13823
Execution time: 0.29 sec elapsed

 Algorithm = det | Error est. = only.bin.RSS 
   name          n n.error  bin.RSS
   Fast   954.4813      NA 1143.165
 Medium  1127.6177      NA 3481.258
   Slow 20620.8451      NA 3379.889
Execution time: 0.22 sec elapsed

 Algorithm = det | Error est. = none 
   name          n n.error
   Fast   954.4813      NA
 Medium  1127.6177      NA
   Slow 20620.8451      NA
Execution time: 0.16 sec elapsed

 Algorithm = det | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting =
FALSE, : Invalid error.estimation argument.
   name          n n.error initial.signal n.residual
   Fast   954.4813      NA         0.7559          0
 Medium  1127.6177      NA         0.1269          0
   Slow 20620.8451      NA         0.1172      13823
Execution time: 0.2 sec elapsed

 Algorithm = nls | Error est. = empiric 
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.55 sec elapsed

 Algorithm = nls | Error est. = poisson 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting =
FALSE, : Specific error.estimation argument without effect when algorithm = nls
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.57 sec elapsed

 Algorithm = nls | Error est. = only.bin.RSS 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting =
FALSE, : Specific error.estimation argument without effect when algorithm = nls
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.58 sec elapsed

 Algorithm = nls | Error est. = none 
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.64 sec elapsed

 Algorithm = nls | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting =
FALSE, : Specific error.estimation argument without effect when algorithm = nls
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.66 sec elapsed

 Algorithm = det+nls | Error est. = empiric 
   name          n   n.error  bin.RSS bin.error initial.signal n.residual
   Fast   938.0487  50.47796 1124.097  34.62712         0.7555          0
 Medium  1083.6310 114.47553 3442.138  58.87742         0.1241          0
   Slow 20831.4080 333.33745 3385.314  58.30404         0.1204      13964
Execution time: 1.86 sec elapsed

 Algorithm = det+nls | Error est. = poisson 
   name          n   n.error bin.error initial.signal n.residual
   Fast   938.0487  54.69045  38.78144         0.7555          0
 Medium  1083.6310 117.68928  59.53150         0.1241          0
   Slow 20831.4080 353.84590  61.90315         0.1204      13964
Execution time: 1.83 sec elapsed

 Algorithm = det+nls | Error est. = only.bin.RSS 
   name          n   n.error  bin.RSS
   Fast   938.0487  22.05404 1124.097
 Medium  1083.6310  71.89500 3442.138
   Slow 20831.4080 339.38346 3385.314
Execution time: 1.82 sec elapsed

 Algorithm = det+nls | Error est. = none 
   name          n   n.error
   Fast   938.0487  22.05404
 Medium  1083.6310  71.89500
   Slow 20831.4080 339.38346
Execution time: 1.57 sec elapsed

 Algorithm = det+nls | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting =
FALSE, : Invalid error.estimation argument.
   name          n   n.error initial.signal n.residual
   Fast   938.0487  22.05404         0.7555          0
 Medium  1083.6310  71.89500         0.1241          0
   Slow 20831.4080 339.38346         0.1204      13964
Execution time: 1.78 sec elapsed
kable(results)
test X1 X2 X3 X1.1 X2.1 X3.1 time
Algorithm = det | Error est. = empiric 954.4813 1127.618 20620.85 50.82649 114.8851 333.0793 0.00033
Algorithm = det | Error est. = poisson 954.4813 1127.618 20620.85 54.69045 117.6893 353.8459 0.00029
Algorithm = det | Error est. = only.bin.RSS 954.4813 1127.618 20620.85 NA NA NA 0.00022
Algorithm = det | Error est. = none 954.4813 1127.618 20620.85 NA NA NA 0.00016
Algorithm = det | Error est. = 2 954.4813 1127.618 20620.85 NA NA NA 0.00020
Algorithm = nls | Error est. = empiric 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00155
Algorithm = nls | Error est. = poisson 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00157
Algorithm = nls | Error est. = only.bin.RSS 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00158
Algorithm = nls | Error est. = none 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00164
Algorithm = nls | Error est. = 2 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00166
Algorithm = det+nls | Error est. = empiric 938.0487 1083.631 20831.41 50.47796 114.4755 333.3375 0.00186
Algorithm = det+nls | Error est. = poisson 938.0487 1083.631 20831.41 54.69045 117.6893 353.8459 0.00183
Algorithm = det+nls | Error est. = only.bin.RSS 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00182
Algorithm = det+nls | Error est. = none 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00157
Algorithm = det+nls | Error est. = 2 938.0487 1083.631 20831.41 22.05404 71.8950 339.3835 0.00178

With background correction

Now do the same, with background correction.

# Background fittings needs pre-defined integration intervals
sim_components <- optimise_OSLintervals(sim_components, curve, 
                                        background.component = TRUE)
Maximum determinant = 0.1092 with interval dividing channels at i = 13, 81, 250 
results <- data.frame(NULL)

for (arg_algorithm in c("det", 
                        "nls", 
                        "det+nls")) {
  for (arg_error_est in c("empiric", 
                          "poisson", 
                          "only.bin.RSS", 
                          "none", 
                          2)) {
    
    case_title <- paste("Algorithm =", arg_algorithm,
                        "| Error est. =", arg_error_est)
    cat("\n", case_title, "\n")
    
    # Function call with return
    components <- decompose_OSLcurve(curve,
                                     sim_components,
                                     background.fitting = TRUE,
                                     algorithm = arg_algorithm,
                                     error.estimation = arg_error_est,
                                     verbose = TRUE)
    
    
    # Call the function now 1000 times to test performance and see if errors occur
    tic("Execution time")
    for (i in 1:1000) {
      suppressWarnings({
        decompose_OSLcurve(curve,
                           sim_components,
                           background.fitting = TRUE,
                           algorithm = arg_algorithm,
                           error.estimation = arg_error_est,
                           verbose = FALSE)
      })
    }
    tictoc <- toc()
    elapsed <- as.numeric((tictoc$toc - tictoc$tic)/1000)
    
    result <- data.frame(test = case_title,
                         t(components$n),
                         t(components$n.error),
                         time = elapsed)
    
    results <- rbind(results, result)
  }
}

 Algorithm = det | Error est. = empiric 
       name           n n.error bin.RSS bin.error initial.signal n.residual
       Fast  1023.58020      NA      NA        NA         0.7899          0
     Medium   761.14579      NA      NA        NA         0.0835          0
       Slow 29156.35126      NA      NA        NA         0.1615      19544
 Background   -62.91838      NA      NA        NA        -0.0349         NA
Execution time: 0.45 sec elapsed

 Algorithm = det | Error est. = poisson 
       name           n    n.error bin.error initial.signal n.residual
       Fast  1023.58020   54.72146  36.70150         0.7899          0
     Medium   761.14579  158.69096  45.23273         0.0835          0
       Slow 29156.35126 4704.43965  57.12268         0.1615      19544
 Background   -62.91838   35.98264  47.15930        -0.0349         NA
Execution time: 0.45 sec elapsed

 Algorithm = det | Error est. = only.bin.RSS 
       name           n n.error bin.RSS
       Fast  1023.58020      NA      NA
     Medium   761.14579      NA      NA
       Slow 29156.35126      NA      NA
 Background   -62.91838      NA      NA
Execution time: 0.24 sec elapsed

 Algorithm = det | Error est. = none 
       name           n n.error
       Fast  1023.58020      NA
     Medium   761.14579      NA
       Slow 29156.35126      NA
 Background   -62.91838      NA
Execution time: 0.15 sec elapsed

 Algorithm = det | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting = TRUE,
: Invalid error.estimation argument.
       name           n n.error initial.signal n.residual
       Fast  1023.58020      NA         0.7899          0
     Medium   761.14579      NA         0.0835          0
       Slow 29156.35126      NA         0.1615      19544
 Background   -62.91838      NA        -0.0349         NA
Execution time: 0.2 sec elapsed

 Algorithm = nls | Error est. = empiric 
       name           n    n.error initial.signal n.residual
       Fast   949.72749   23.51746         0.7626          0
     Medium   965.69785  109.91764         0.1102          0
       Slow 26526.32724 4033.06206         0.1528      17781
 Background   -44.50749   31.40807        -0.0257         NA
Execution time: 1.98 sec elapsed

 Algorithm = nls | Error est. = poisson 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting = TRUE,
: Specific error.estimation argument without effect when algorithm = nls
       name           n    n.error initial.signal n.residual
       Fast   949.72749   23.51746         0.7626          0
     Medium   965.69785  109.91764         0.1102          0
       Slow 26526.32724 4033.06206         0.1528      17781
 Background   -44.50749   31.40807        -0.0257         NA
Execution time: 1.94 sec elapsed

 Algorithm = nls | Error est. = only.bin.RSS 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting = TRUE,
: Specific error.estimation argument without effect when algorithm = nls
       name           n    n.error initial.signal n.residual
       Fast   949.72749   23.51746         0.7626          0
     Medium   965.69785  109.91764         0.1102          0
       Slow 26526.32724 4033.06206         0.1528      17781
 Background   -44.50749   31.40807        -0.0257         NA
Execution time: 2.06 sec elapsed

 Algorithm = nls | Error est. = none 
       name           n    n.error initial.signal n.residual
       Fast   949.72749   23.51746         0.7626          0
     Medium   965.69785  109.91764         0.1102          0
       Slow 26526.32724 4033.06206         0.1528      17781
 Background   -44.50749   31.40807        -0.0257         NA
Execution time: 1.88 sec elapsed

 Algorithm = nls | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting = TRUE,
: Specific error.estimation argument without effect when algorithm = nls
       name           n    n.error initial.signal n.residual
       Fast   949.72749   23.51746         0.7626          0
     Medium   965.69785  109.91764         0.1102          0
       Slow 26526.32724 4033.06206         0.1528      17781
 Background   -44.50749   31.40807        -0.0257         NA
Execution time: 2.12 sec elapsed

 Algorithm = det+nls | Error est. = empiric 
       name          n n.error bin.RSS bin.error initial.signal n.residual
       Fast   949.7275      NA      NA        NA         0.7626          0
     Medium   965.6979      NA      NA        NA         0.1102          0
       Slow 26526.3275      NA      NA        NA         0.1528      17781
 Background   -44.5075      NA      NA        NA        -0.0257         NA
Execution time: 2.36 sec elapsed

 Algorithm = det+nls | Error est. = poisson 
       name          n    n.error bin.error initial.signal n.residual
       Fast   949.7275   54.72146  36.70150         0.7626          0
     Medium   965.6979  158.69096  45.23273         0.1102          0
       Slow 26526.3275 4704.43965  57.12268         0.1528      17781
 Background   -44.5075   35.98264  47.15930        -0.0257         NA
Execution time: 2.15 sec elapsed

 Algorithm = det+nls | Error est. = only.bin.RSS 
       name          n    n.error bin.RSS
       Fast   949.7275   23.51746      NA
     Medium   965.6979  109.91762      NA
       Slow 26526.3275 4033.06187      NA
 Background   -44.5075   31.40807      NA
Execution time: 1.95 sec elapsed

 Algorithm = det+nls | Error est. = none 
       name          n    n.error
       Fast   949.7275   23.51746
     Medium   965.6979  109.91762
       Slow 26526.3275 4033.06187
 Background   -44.5075   31.40807
Execution time: 1.72 sec elapsed

 Algorithm = det+nls | Error est. = 2 
Warning in decompose_OSLcurve(curve, sim_components, background.fitting = TRUE,
: Invalid error.estimation argument.
       name          n    n.error initial.signal n.residual
       Fast   949.7275   23.51746         0.7626          0
     Medium   965.6979  109.91762         0.1102          0
       Slow 26526.3275 4033.06187         0.1528      17781
 Background   -44.5075   31.40807        -0.0257         NA
Execution time: 2.09 sec elapsed
kable(results)
test X1 X2 X3 X4 X1.1 X2.1 X3.1 X4.1 time
Algorithm = det | Error est. = empiric 1023.5802 761.1458 29156.35 -62.91838 NA NA NA NA 0.00045
Algorithm = det | Error est. = poisson 1023.5802 761.1458 29156.35 -62.91838 54.72146 158.6910 4704.440 35.98264 0.00045
Algorithm = det | Error est. = only.bin.RSS 1023.5802 761.1458 29156.35 -62.91838 NA NA NA NA 0.00024
Algorithm = det | Error est. = none 1023.5802 761.1458 29156.35 -62.91838 NA NA NA NA 0.00015
Algorithm = det | Error est. = 2 1023.5802 761.1458 29156.35 -62.91838 NA NA NA NA 0.00020
Algorithm = nls | Error est. = empiric 949.7275 965.6979 26526.33 -44.50749 23.51746 109.9176 4033.062 31.40807 0.00198
Algorithm = nls | Error est. = poisson 949.7275 965.6979 26526.33 -44.50749 23.51746 109.9176 4033.062 31.40807 0.00194
Algorithm = nls | Error est. = only.bin.RSS 949.7275 965.6979 26526.33 -44.50749 23.51746 109.9176 4033.062 31.40807 0.00206
Algorithm = nls | Error est. = none 949.7275 965.6979 26526.33 -44.50749 23.51746 109.9176 4033.062 31.40807 0.00188
Algorithm = nls | Error est. = 2 949.7275 965.6979 26526.33 -44.50749 23.51746 109.9176 4033.062 31.40807 0.00212
Algorithm = det+nls | Error est. = empiric 949.7275 965.6979 26526.33 -44.50750 NA NA NA NA 0.00236
Algorithm = det+nls | Error est. = poisson 949.7275 965.6979 26526.33 -44.50750 54.72146 158.6910 4704.440 35.98264 0.00215
Algorithm = det+nls | Error est. = only.bin.RSS 949.7275 965.6979 26526.33 -44.50750 23.51746 109.9176 4033.062 31.40807 0.00195
Algorithm = det+nls | Error est. = none 949.7275 965.6979 26526.33 -44.50750 23.51746 109.9176 4033.062 31.40807 0.00172
Algorithm = det+nls | Error est. = 2 949.7275 965.6979 26526.33 -44.50750 23.51746 109.9176 4033.062 31.40807 0.00209

Input checks

Try out a variety of possible cases for the lambda input

component_inputs <- list(as_usual = sim_components,
                        lambda_only = sim_components$lambda,
                        named_vector = c(Schnell = 1,
                                         Mittel = 0.1,
                                         Langsam = 0.01),
                        single_lambda = 0.5,
                        many_lambda = c(2,1,0.5,0.2,0.1,0.05, 0.01))

for (i in 1:length(component_inputs)) {
  
  cat("\nTest case:", names(component_inputs)[i], "\n")
   decompose_OSLcurve(curve, component_inputs[[i]])
}

Test case: as_usual 
Warning in signal + n[k] * diff(exp(-lambda[k] * c(0, curve$time))): Länge des längeren Objektes
     ist kein Vielfaches der Länge des kürzeren Objektes
   name          n   n.error  bin.RSS bin.error initial.signal n.residual
   Fast  1011.9862  54.11282 1353.377  38.29045         0.7838          0
 Medium   874.1546 110.77269 1836.248  43.17007         0.0962          0
   Slow 21590.2630 479.43393 3102.832  55.86860         0.1200      14472

Test case: lambda_only 
        name          n   n.error  bin.RSS bin.error initial.signal n.residual
 Component 1   954.4813  50.82649 1143.165  34.91956         0.7559          0
 Component 2  1127.6177 114.88515 3481.258  59.21104         0.1269          0
 Component 3 20620.8451 333.07933 3379.889  58.25730         0.1172      13823

Test case: named_vector 
    name         n   n.error  bin.RSS bin.error initial.signal n.residual
 Schnell  1217.363  65.84634 2394.428  49.90196         0.7863          0
  Mittel  1183.630 186.50176 4568.671  67.73936         0.0799         22
 Langsam 19718.174 489.82669 1748.545  41.96156         0.1338      13217

Test case: single_lambda 
        name    n  n.error bin.RSS bin.error initial.signal n.residual
 Component 1 8880 1015.382 1028423  1015.382              1          0

Test case: many_lambda 
        name           n   n.error   bin.RSS bin.error initial.signal
 Component 1    37.34566  377.8692  560.7364  25.57719         0.0415
 Component 2  1525.98218 1334.5621  396.6770  21.51255         0.8911
 Component 3  -402.79441 2267.0120  870.8476  30.41834        -0.1205
 Component 4  -155.15307 4563.7322  711.1425  27.01586        -0.0189
 Component 5   993.77184 8230.8651 1566.6287  39.87063         0.0607
 Component 6  1225.08172 8110.3724 2838.6232  53.40980         0.0375
 Component 7 17713.27711 6730.8286  774.7833  28.08236         0.1086
 n.residual
          0
          0
          0
          0
         18
        166
      11874