Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ For more information about this file see also [Keep a Changelog](http://keepacha
## Unreleased

### Added

- Added regression coverage for SIPNET event-JSON segmentation and segmented restart chaining (#4021).
- Added a downscaling-error diagnostic to `inst/ilamb/` in PEcAn.benchmark: compares the downscaling random forest's out-of-bag RMSE against the between-member ensemble spread at the SDA sites, quantifying downscaling error the ensemble spread does not represent. (#4070)
- Added `metric_Bias()` standalone function and registered `"BIAS"` in `PEcAn.benchmark` metric registry (#4062, #4059).
- Added `metric_CRPS()` function and registry entry to `PEcAn.benchmark` for Continuous Ranked Probability Score (CRPS) ensemble skill evaluation against observations (#4062).
- Added `efi_long_to_array()` helper function to `PEcAn.benchmark` for reshaping EFI long format model outputs into wide ensemble member matrices (#4062).
- Added ensemble member spaghetti line rendering under confidence ribbons in `metric_timeseries_plot()` (#4062).
- Added `examples/benchmarks/salinas_soc_ensemble/` demonstrating multi-site SOC ensemble validation against Salinas observations out-of-the-box (#4062).
- Added `examples/benchmarks/salinas_soc_ensemble/` demonstrating multi-site SOC ensemble validation against Salinas observations out-of-the-box (#4062)
- Added ensemble calibration diagnostics to `inst/ilamb/` in PEcAn.benchmark (rank histogram, spread-skill ratio, coverage, reliability) for assessing whether an ensemble's spread is well calibrated against observations, complementing the ensemble-mean benchmarking.
- Added regional calibration diagnostics to `inst/ilamb/` in PEcAn.benchmark: break the ensemble calibration assessment down by land cover class and by EPA/CEC ecoregion, reusing the ensemble calibration diagnostics, with figures.
- Added an ESA CCI biomass benchmark extension to `inst/ilamb/` in PEcAn.benchmark: a converter for the ESACCI Biomass v7.0 product (2015-2024, with per-pixel uncertainty) to ILAMB-compatible netCDF, plus an observation-error test showing the ensemble overconfidence persists against a modern benchmark and after accounting for observation uncertainty.
Expand Down
1 change: 1 addition & 0 deletions models/sipnet/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# PEcAn.SIPNET 1.10.0.9000

* Added regression coverage for SIPNET event-JSON segmentation and segmented restart chaining (#4021).
* `write.config.SIPNET` now maps the `leafNResorptionFrac` trait to its v2
parameter, so PFT supplied values reach the param file instead of
silently keeping the template default. Completes the nitrogen cycle
Expand Down
71 changes: 71 additions & 0 deletions models/sipnet/tests/testthat/test-write_segmented_configs.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,84 @@ test_that("write_segmented_configs", {
)
}

# Each segment writes its own restart and later segments read the previous one.
segment_rundirs <- file.path(
run_path, "segments", sprintf("segment_%03d", 1:3), "run"
)
for (seg in 1:3) {
config <- readLines(file.path(segment_rundirs[seg], "1", "sipnet.in"))
restart_in <- grep("^[[:space:]]*RESTART_IN[[:space:]]*=", config, value = TRUE)
restart_out <- grep("^[[:space:]]*RESTART_OUT[[:space:]]*=", config, value = TRUE)

expect_identical(
restart_out,
paste("RESTART_OUT =", file.path(segment_rundirs[seg], "restart.out"))
)
if (seg == 1) {
expect_length(restart_in, 0)
Comment thread
infotroph marked this conversation as resolved.
} else {
expect_identical(
restart_in,
paste("RESTART_IN =", file.path(segment_rundirs[seg - 1], "restart.out"))
)
}
}

# job.sh includes calls to segment scripts
jobsh <- readLines(file.path(run_path, "job.sh"))
expect_match(jobsh, "bash .*segment_001/run/1/job.sh", all = FALSE)
expect_match(jobsh, "bash .*segment_002/run/1/job.sh", all = FALSE)
expect_match(jobsh, "bash .*segment_003/run/1/job.sh", all = FALSE)
})

test_that("segment_dataframe falls back to event_json for the configured site", {
pth <- withr::local_tempdir()
events_path <- file.path(pth, "events.json")
jsonlite::write_json(
list(
list(
site_id = "a",
events = list(
list(event_type = "planting", date = "2025-01-02", crop_code = "D12"),
list(event_type = "planting", date = "2025-01-05", crop_code = "G6")
)
),
list(
site_id = "b",
events = list(
list(event_type = "planting", date = "2025-01-03", crop_code = "P1")
)
)
),
path = events_path,
auto_unbox = TRUE
)
run_settings <- PEcAn.settings::as.Settings(list(
run = list(
site = list(id = "a", site.pft = list(veg = "pft1")),
inputs = list(event_json = list(path = events_path)),
start.date = "2025-01-01",
end.date = "2025-01-10"
)
))

result <- PEcAn.SIPNET:::segment_dataframe(run_settings)

expect_equal(nrow(result), 3)
expect_identical(result$site_id, rep("a", 3))
expect_identical(result$segment_id, c("001", "002", "003"))
expect_identical(
result$start_date,
as.Date(c("2025-01-01", "2025-01-02", "2025-01-05"))
)
expect_identical(
result$end_date,
as.Date(c("2025-01-01", "2025-01-04", "2025-01-10"))
)
expect_identical(result$crop_code, c(NA_character_, "D12", "G6"))
expect_identical(result$pft[1], "pft1")
})

test_that("segment_dataframe returns empty when run start is after all crop cycles", {
pth <- withr::local_tempdir()
crp_chg_path <- file.path(pth, "cycles-a.csv")
Expand Down
Loading