tests/testthat/test-calcEto.R

testthat::test_that("calcEto warns for unrealistic temperature values", {
  ## Create mock dataset with unrealistic temperatures

  data <- data.frame(
    Tmax = c(65, -55, 25),
    Tmin = c(55, -65, 20),
    Lat = 41.89,
    Lon = 12.48,
    Elev = 100,
    Year = 2023,
    Month = c(7, 8, 9),
    Day = c(1, 15, 30)
  )

  ## Expect a warning about unrealistic temperatures

  expect_warning(calcEto(data, method = "HS"))
})

testthat::test_that("calcEto stops for missing required data (Tmax, Tmin)", {
  ## Create data with missing Tmax and Tmin

  data <- data.frame(
    Lat = 41.89,
    Lon = 12.48,
    Elev = 100,
    Year = 2023,
    Month = c(7, 8, 9),
    Day = c(1, 15, 30)
  )

  ## Expect an error about missing data

  expect_error(calcEto(data))
})

testthat::test_that("calcEto adjusts inconsistent temperature data", {
  ## Create mock data with Tmin greater than Tmax

  data <- data.frame(
    Tmax = c(25, 20, 25),
    Tmin = c(30, 22, 20),
    Lat = 41.89,
    Lon = 12.48,
    Elev = 100,
    Year = 2023,
    Month = c(7, 8, 9),
    Day = c(1, 15, 30)
  )

  ## Call the function and capture the message

  output <- testthat::capture_messages(calcEto(data, method = "HS"))

  ## Print the captured output for debugging

  print(output)

  ## Expect a message about adjusting inconsistent temperatures

  testthat::expect_true(any(stringr::str_detect(output, "Adjusted")))
})

testthat::test_that("calcEto stops for invalid method", {
  ## Create data

  data <- data.frame(
    Tmax = c(25, 20, 25),
    Tmin = c(20, 18, 22),
    Lat = 41.89,
    Lon = 12.48,
    Elev = 100,
    Year = 2023,
    Month = c(7, 8, 9),
    Day = c(1, 15, 30)
  )

  ## Test with invalid method

  expect_error(calcEto(data, method = "invalid"))
})

test_that("calcEto handles valid input correctly", {
  ## ***** Create a mock dataset

  data <- data.frame(
    Year = rep(2024, 2),
    Month = rep(9, 2),
    Day = 1:2,
    Lat = c(50, 50),
    Elev = rep(100, 2),
    Tmax = c(25, 27),
    Tmin = c(15, 17),
    Rs = c(15, 14),
    U2 = c(2, 3),
    RHmax = c(85, 80),
    RHmin = c(40, 45)
  )

  testthat::test_that("calcEto calculates Hargreaves-Samani ET (HS)", {
    # Create mock data
    data <- data.frame(
      Tmax = c(25, 20, 25),
      Tmin = c(20, 18, 22),
      Lat = 41.89,
      Lon = 12.48,
      Elev = 100,
      Year = 2023,
      Month = c(7, 8, 9),
      Day = c(1, 15, 30)
    )

    ## Calculate ET using the Hargreaves-Samani method

    result <- calcEto(data, method = "HS")

    ## Expected ET values (based on Hargreaves-Samani formula)

    expected_et <- c(4.802557, 3.361835, 2.878480)

    ## Check if the calculated ET values are approximately equal to
    ## the expected values

    testthat::expect_equal(result$ET.Daily, expected_et, tolerance = 0.001)
  })

  testthat::test_that("Universal constants and calculations are correct", {
    ## Create mock data

    data <- data.frame(
      Tmax = c(25, 28, 26),
      Tmin = c(15, 17, 16),
      Rs = c(15, 16, 14),
      Lat = 41.89,
      Lon = 12.48,
      Elev = 100,
      Year = 2023,
      Month = c(7, 8, 9),
      Day = c(1, 15, 30),
      RHmax = c(80, 75, 85),
      RHmin = c(50, 45, 55)
    )

    ## ***** Universal constants *****

    lambda <- 2.45
    Cp <- 1.013e-3
    e <- 0.622
    Sigma <- 4.903e-09
    Gsc <- 0.082
    G <- 0
    alphaPT <- 1.26
    alpha <- 0.23

    # ***** Latitude in Radians *****
    lat.rad <- data$Lat * (pi / 180)

    # ***** Date and Elevation Data *****
    date.vec <- as.Date(paste0(data$Year, "-", data$Month, "-", data$Day))
    data$J <- as.numeric(format(date.vec, "%j"))
    Elev <- unique(data$Elev)

    # Ensure only one unique Elevation
    testthat::expect_length(Elev, 1)

    # Check required variables are present
    reqVars <- c("Tmax", "Tmin", "Rs")
    misVars <- reqVars[!reqVars %in% names(data)]
    testthat::expect_equal(length(misVars), 0)

    # ***** Mean temperature calculation *****
    Tavg <- (data$Tmax + data$Tmin) / 2

    # ***** Vapor Pressure Calculations *****
    EsTmax <- 0.6108 * exp(17.27 * data$Tmax / (data$Tmax + 237.3))
    EsTmin <- 0.6108 * exp(17.27 * data$Tmin / (data$Tmin + 237.3))
    Es <- (EsTmax + EsTmin) / 2
    Ea <- (EsTmin * data$RHmax / 100 + EsTmax * data$RHmin / 100) / 2

    # ***** Atmospheric Pressure *****
    P <- 101.3 * ((293 - 0.0065 * Elev) / 293)^5.26

    # ***** Slope of Saturation Vapor Pressure Curve *****
    delta <- 4098 * (0.6108 * exp((17.27 * Tavg) / (Tavg + 237.3))) /
      ((Tavg + 237.3)^2)

    # ***** Psychrometric constant *****
    gamma <- (Cp * P) / (lambda * e)

    # ***** Inverse relative distance Earth-Sun *****
    dr <- 1 + 0.033 * cos(2 * pi / 365 * data$J)

    # ***** Solar Declination *****
    SDc <- 0.409 * sin(2 * pi / 365 * as.numeric(data$J) - 1.39)

    # ***** Sunset Hour Angle *****
    Ws <- acos(-tan(lat.rad) * tan(SDc))

    # ***** Daylight hours *****
    N <- 24 / pi * Ws

    # ***** Extraterrestrial radiation *****
    Ra <- (1440 / pi) * dr * Gsc * (Ws * sin(lat.rad) * sin(SDc) +
      cos(lat.rad) * cos(SDc) * sin(Ws))

    # ***** Clear-sky solar radiation *****
    Rso <- (0.75 + (2 * 10^-5) * Elev) * Ra

    Rs <- data$Rs

    # ***** Net Outgoing Longwave Radiation *****
    Rnl <- Sigma * (0.34 - 0.14 * sqrt(Ea)) *
      ((data$Tmax + 273.2)^4 + (data$Tmin + 273.2)^4) / 2 *
      (1.35 * Rs / Rso - 0.35)

    # ***** Net Incoming Shortwave Radiation *****
    Rnsg <- (1 - alpha) * Rs

    # ***** Net Radiation *****
    Rng <- Rnsg - Rnl

    # ***** Potential Evapotranspiration *****
    E.PT.Daily <- alphaPT * (delta / (delta + gamma) * Rng / lambda - G / lambda)

    # Run the actual function and capture the result
    result <- calcEto(data, method = "PT")

    # Compare the calculated values
    testthat::expect_equal(result$ET.Daily, E.PT.Daily, tolerance = 0.001)
    testthat::expect_equal(result$Ra.Daily, Ra, tolerance = 0.001)
    testthat::expect_equal(result$Slope.Daily, delta, tolerance = 0.001)
    testthat::expect_equal(result$Ea.Daily, Ea, tolerance = 0.001)
    testthat::expect_equal(result$Es.Daily, Es, tolerance = 0.001)

    # Check for message output (if applicable)
    testthat::expect_message(calcEto(data, method = "PT"), "Priestley-Taylor")
  })

  ## ***** Call the function with "short" crop

  result <- calcEto(data, crop = "short")

  ## ***** Check that the result is a list

  expect_s3_class(result, "PEToutList")

  ## ***** Check that all required components are present in the result

  expect_true(all(c(
    "ET.Daily", "Ra.Daily", "Slope.Daily", "Ea.Daily",
    "Es.Daily", "ET.formulation", "ET.type"
  ) %in%
    names(result)))

  ## Check for non-NA values in the output
  expect_false(any(is.na(result$ET.Daily)))
  expect_false(any(is.na(result$Ra.Daily)))
})

test_that("calcEto handles missing data correctly", {
  ## Create a mock dataset with missing Rs

  data_missing <- data.frame(
    Year = rep(2024, 2),
    Month = rep(9, 2),
    Day = 1:2,
    Lat = c(50, 50),
    Elev = rep(100, 2),
    Tmax = c(25, 27),
    Tmin = c(15, 17),
    # Rs is missing
    U2 = c(2, 3),
    RHmax = c(85, 80),
    RHmin = c(40, 45)
  )

  ## Check that the function throws an error

  expect_error(
    calcEto(data_missing, crop = "short"),
    "Required data missing for 'Rs'"
  )
})

test_that("calcEto handles different crop types correctly", {
  ## Create a mock dataset

  data <- data.frame(
    Year = rep(2024, 2),
    Month = rep(9, 2),
    Day = 1:2,
    Lat = c(50, 50),
    Elev = rep(100, 2),
    Tmax = c(25, 27),
    Tmin = c(15, 17),
    Rs = c(15, 14),
    U2 = c(2, 3),
    RHmax = c(85, 80),
    RHmin = c(40, 45)
  )

  ## Test for "tall" crop

  result_tall <- calcEto(data, crop = "tall")
  expect_s3_class(result_tall, "PEToutList")
  expect_equal(
    result_tall$ET.formulation,
    "Penman-Monteith ASCE-EWRI Standardised"
  )

  ## Test for invalid crop type

  expect_error(
    calcEto(data, crop = "invalid"),
    "Invalid crop type. Choose either 'short' or 'tall'."
  )
})

testthat::test_that("Universal constants and conversions are correct", {
  # Create mock data
  data <- data.frame(
    Tmax = c(25, 28, 26),
    Tmin = c(15, 17, 16),
    Rs = c(15, 16, 14), # Ensure Rs is present
    Lat = 41.89,
    Lon = 12.48,
    Elev = 100,
    Year = 2023,
    Month = c(7, 8, 9),
    Day = c(1, 15, 30),
    RHmax = c(80, 75, 85),
    RHmin = c(50, 45, 55),
    U2 = c(2, 3, 2.5) # Wind speed data
  )

  # Test valid calculation for "short" crop
  result_short <- calcEto(data, crop = "short", method = "PM", Zh = 10)

  # Validate results for the short crop
  testthat::expect_true(inherits(result_short, "PEToutList"))
  testthat::expect_true(all(c(
    "ET.Daily", "Ra.Daily", "Slope.Daily",
    "Ea.Daily", "Es.Daily", "ET.formulation",
    "ET.type"
  ) %in% names(result_short)))

  # Test valid calculation for "tall" crop
  result_tall <- calcEto(data, crop = "tall", method = "PM", Zh = 10)

  # Validate results for the tall crop
  testthat::expect_true(inherits(result_tall, "PEToutList"))
  testthat::expect_true(all(c(
    "ET.Daily", "Ra.Daily", "Slope.Daily",
    "Ea.Daily", "Es.Daily", "ET.formulation",
    "ET.type"
  ) %in% names(result_tall)))

  # Ensure function handles missing solar radiation data
  data_no_Rs <- data
  data_no_Rs$Rs <- NULL

  testthat::expect_error(
    {
      calcEto(data_no_Rs, crop = "short", method = "PM", Zh = 10)
    },
    "Required data missing for 'Rs'"
  )

  # Ensure function handles missing wind speed data
  data_no_U2 <- data
  data_no_U2$U2 <- NULL

  testthat::expect_error(
    {
      calcEto(data_no_U2, crop = "short", method = "PM", Zh = 10)
    },
    "Required data missing for 'Uz' or 'U2'"
  )


  # Test message output
  testthat::expect_message(calcEto(data,
    crop = "short", method = "PM",
    Zh = 10
  ), "Penman-Monteith FAO56")
})

Try the AquaBEHER package in your browser

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

AquaBEHER documentation built on Sept. 25, 2024, 1:08 a.m.