tests/testthat/test-hdf5_roundtrip.R

# test-hdf5_roundtrip.R
# Tests that write calibration output to HDF5, read it back, and verify
# structure, attributes, and data values.

fin <- system.file("extdata",
  "NEON.D15.ONAQ.DP4.00200.001.nsae.2019-05.basic.packed.h5",
  package = "NEONiso", mustWork = TRUE)

# ---- Carbon round-trip ----

test_that("carbon calibration output file has correct HDF5 structure", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = TRUE)

  # Verify top-level group
  top <- NEONiso:::h5_ls(fout)
  expect_true("ONAQ" %in% top$name)

  # Verify dp01 children
  dp01_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01")
  expect_true(all(c("data", "qfqm", "ucrt") %in% dp01_children))

  # Verify isoCo2 data children include calData and ambient heights
  iso_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoCo2")
  expect_true("calData" %in% iso_children)
  # Should have at least one ambient height group
  height_groups <- iso_children[grepl("^000_", iso_children)]
  expect_gt(length(height_groups), 0)
})

test_that("carbon output file has correct site attributes", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = TRUE)

  # Read attributes from input and output files
  attrs_in <- NEONiso:::h5_read_attrs(fin, "ONAQ")
  attrs_out <- NEONiso:::h5_read_attrs(fout, "ONAQ")

  # Attributes should match between input and output
  expect_equal(sort(names(attrs_out)), sort(names(attrs_in)))
  expect_equal(attrs_out$LvlMeasTow, attrs_in$LvlMeasTow)
})

test_that("carbon output file calData has correct dataset", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = TRUE)

  # Check calData children
  cal_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoCo2/calData")
  expect_true("calRegressions" %in% cal_children)
})

test_that("carbon output file passes validate_output_file", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = TRUE)

  # This should not error
  expect_no_error(
    NEONiso:::validate_output_file(fin, fout, "ONAQ", "Co2")
  )
})

test_that("carbon gainoffset output passes validate_output_file", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  suppressWarnings({
    co2 <- calibrate_carbon(fin, fout, "ONAQ",
      method = "gainoffset", calibration_half_width = 0.5,
      write_to_file = TRUE)
  })

  expect_no_error(
    NEONiso:::validate_output_file(fin, fout, "ONAQ", "Co2")
  )
})

# ---- Water round-trip ----

test_that("water calibration output file has correct HDF5 structure", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  h2o <- calibrate_water(fin, fout, "ONAQ", write_to_file = TRUE)

  # Verify top-level group
  top <- NEONiso:::h5_ls(fout)
  expect_true("ONAQ" %in% top$name)

  # Verify dp01 children
  dp01_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01")
  expect_true(all(c("data", "qfqm", "ucrt") %in% dp01_children))

  # Verify isoH2o data children include calData
  iso_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoH2o")
  expect_true("calData" %in% iso_children)
})

test_that("water output file has correct site attributes", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  h2o <- calibrate_water(fin, fout, "ONAQ", write_to_file = TRUE)

  attrs_in <- NEONiso:::h5_read_attrs(fin, "ONAQ")
  attrs_out <- NEONiso:::h5_read_attrs(fout, "ONAQ")

  expect_equal(sort(names(attrs_out)), sort(names(attrs_in)))
  expect_equal(attrs_out$LvlMeasTow, attrs_in$LvlMeasTow)
})

test_that("water output file passes validate_output_file", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  h2o <- calibrate_water(fin, fout, "ONAQ", write_to_file = TRUE)

  expect_no_error(
    NEONiso:::validate_output_file(fin, fout, "ONAQ", "H2o")
  )
})

# ---- Standalone output function calls (fid = NULL) ----

test_that("write_carbon_calibration_data works standalone (fid = NULL)", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = FALSE)

  # Set up the output file first
  NEONiso:::setup_output_file(fin, fout, "ONAQ", "Co2")

  # Write calibration data standalone (no fid passed)
  cal_df <- co2$cal_df
  expect_no_error(
    NEONiso:::write_carbon_calibration_data(fout, "ONAQ", cal_df,
                                             method = "linreg")
  )

  # Verify data was written
  cal_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoCo2/calData")
  expect_true("calRegressions" %in% cal_children)
})

test_that("write_carbon_ambient_data works standalone (fid = NULL)", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = FALSE)

  NEONiso:::setup_output_file(fin, fout, "ONAQ", "Co2")

  # Write ambient data standalone
  expect_no_error(
    NEONiso:::write_carbon_ambient_data(fout, "ONAQ", co2$ciso_subset_cal)
  )

  # Verify ambient height groups were written
  iso_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoCo2")
  height_groups <- iso_children[grepl("^000_", iso_children)]
  expect_gt(length(height_groups), 0)
})

test_that("write_water_calibration_data works standalone (fid = NULL)", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  h2o <- calibrate_water(fin, fout, "ONAQ", write_to_file = FALSE)

  NEONiso:::setup_output_file(fin, fout, "ONAQ", "H2o")

  expect_no_error(
    NEONiso:::write_water_calibration_data(fout, "ONAQ", h2o$cal_df)
  )

  cal_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoH2o/calData")
  expect_true("calRegressions" %in% cal_children)
})

test_that("write_water_ambient_data works standalone (fid = NULL)", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  h2o <- calibrate_water(fin, fout, "ONAQ", write_to_file = FALSE)

  NEONiso:::setup_output_file(fin, fout, "ONAQ", "H2o")

  expect_no_error(
    NEONiso:::write_water_ambient_data(fout, "ONAQ", h2o$wiso_subset_cal)
  )

  iso_children <- NEONiso:::h5_ls_group(fout, "ONAQ/dp01/data/isoH2o")
  height_groups <- iso_children[grepl("^000_", iso_children)]
  expect_gt(length(height_groups), 0)
})

# ---- validate_output_file error path ----

test_that("validate_output_file errors on incomplete output file", {

  fout <- tempfile(fileext = ".h5")
  on.exit(unlink(fout), add = TRUE)

  # Create an output file with only the skeleton (no ambient data)
  NEONiso:::setup_output_file(fin, fout, "ONAQ", "Co2")

  # Write just the calibration data but skip ambient data,
  # so the output file will be missing height groups
  co2 <- calibrate_carbon(fin, fout, "ONAQ",
    method = "linreg", calibration_half_width = 0.5,
    write_to_file = FALSE)

  fid <- NEONiso:::h5_open(fout)
  NEONiso:::write_carbon_calibration_data(fout, "ONAQ", co2$cal_df,
                                           method = "linreg", fid = fid)
  NEONiso:::h5_close(fid)

  expect_error(
    NEONiso:::validate_output_file(fin, fout, "ONAQ", "Co2"),
    "Output file structure has diverged"
  )
})

Try the NEONiso package in your browser

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

NEONiso documentation built on Feb. 22, 2026, 5:07 p.m.