Implement virtual sensors"

knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
library(SamsaRaLight)
library(dplyr)
library(ggplot2)
library(grid)

Introduction

Virtual sensors allow us to estimate the amount of radiation reaching specific locations in the stand, such as the forest floor. In this vignette, we show how to define and use virtual sensors in SamsaRaLight, and how sensor outputs can be compared with field measurements of percentage of above canopy light (PACL).

Measuring light in the field

In forest ecosystems, light availability at the ground level can be measured using direct sensors (quantum sensors, dataloggers) or indirect methods such as hemispherical photography. These measurements provide estimates of transmitted radiation or PACL at specific locations. In SamsaRaLight, virtual sensors are designed to reproduce such measurements within simulated stands.

Defining sensors in a virtual stand

In the model, a sensor represents a point where incoming radiation is recorded. Sensors are defined by their spatial coordinates and height over the ground and are provided as a dedicated data.frame when creating the stand. Measurements of PACL are not required as input data for the model, only id_sensor, x, y and h_m variables.

The following example shows the sensor configuration used in the cloture20 dataset.

SamsaRaLight::data_cloture20$sensors

An input sensors data.frame structure can be checked using the function SamsaRaLight::check_sensors()

SamsaRaLight::check_sensors(SamsaRaLight::data_cloture20$sensors)

Sensors are included in the stand definition using the sensors argument of create_sl_stand().

stand_cloture <- SamsaRaLight::create_sl_stand(

  # Tree inventory
  trees_inv = SamsaRaLight::data_cloture20$trees,
  core_polygon_df = SamsaRaLight::data_cloture20$core_polygon,

  # STand geometry
  cell_size = 5,
  latitude = SamsaRaLight::data_cloture20$info$latitude,
  slope = SamsaRaLight::data_cloture20$info$slope,
  aspect = SamsaRaLight::data_cloture20$info$aspect,
  north2x = SamsaRaLight::data_cloture20$info$north2x,

  # Define sensors
  sensors = SamsaRaLight::data_cloture20$sensors
) 

Once the stand is created, their presence can be checked by printing or plotting the object. Sensors appear as red symbols in the graphical outputs and can be hidden if needed (add_sensors = F argument).

print(stand_cloture)

plot(stand_cloture)
plot(stand_cloture, top_down = TRUE)

Obtaining sensor outputs

After defining the sensors, the model is run in the usual way. When only sensor outputs are required, the argument sensors_only = TRUE can be used to reduce computation time. In this example, we compute the full output for illustration purposes.

out_cloture <- SamsaRaLight::run_sl(
  sl_stand = stand_cloture,
  monthly_radiations = SamsaRaLight::data_cloture20$radiations,

  detailed_output = TRUE,
  sensors_only = FALSE
)

Sensor-level results are stored following the same structure as cell outputs and include total, direct, and diffuse components when detailed_output = TRUE. Unlike cells, sensor energy is computed on a horizontal plane, which makes it directly comparable with most field measurements.

out_cloture$output$light$sensors

Comparing observed and simulated PACL

We can now compare simulated PACL values with field measurements collected at the sensor locations. The following figure shows the relationship between observed and predicted PACL for an initial LAD value of 0.5. When predictions are systematically higher than observations, the model simulates too much transmitted light. This indicates that foliage density is underestimated and that LAD should be increased. Conversely, systematic underestimation would suggest excessive attenuation.

dplyr::left_join(

  SamsaRaLight::data_cloture20$sensors %>% 
    dplyr::select(id_sensor, pacl) %>% 
    dplyr::rename_at(vars(-"id_sensor"), ~paste0(., "_obs")),

  out_cloture$output$light$sensors %>% 
    dplyr::select(id_sensor, pacl) %>% 
    dplyr::rename_at(vars(-"id_sensor"), ~paste0(., "_pred")),

  by = "id_sensor"
) %>% 

  dplyr::mutate(
    diff_pacl = pacl_obs - pacl_pred
  ) %>% 

  ggplot(aes(y = pacl_pred, x = pacl_obs)) +
  geom_point() +
  geom_smooth(method = "lm", formula = y ~ x, color = "salmon", linewidth = 1.1) +
  geom_abline(intercept = 0, slope = 1, color = "skyblue", linewidth = 1.1) +
  xlab("Observed PACL from field data") +
  ylab("Predicted PACL with virtual sensors in SamsaraLight") +
  labs(title = "Comparison between PACL from field sensors and predicted with SamsaraLight\nsetting a mean site LAD to 0.5",
       subtitle = "red line is a linear regression and blue line is the (1,1) control line for perfect fit") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5),
        plot.subtitle = element_text(hjust = 0.5))

In this example, PACL is on average overestimated, suggesting that the default LAD value of 0.5 is slightly too low.



Try the SamsaRaLight package in your browser

Any scripts or data that you put into this service are public.

SamsaRaLight documentation built on April 16, 2026, 5:08 p.m.