From b1318511a4c367d02e44285ac1d23ac6fbd3f1d8 Mon Sep 17 00:00:00 2001 From: Alex Chubaty Date: Wed, 5 Aug 2026 10:00:06 -0600 Subject: [PATCH] new parameter LCCClassesToReplaceNNMethod; require LandR (>= 1.2.0.9005) Exposes LandR::convertUnwantedLCC()'s new `method` argument, added in PredictiveEcology/LandR#197, as a module parameter. Both options allocate each LCCClassesToReplaceNN pixel a neighbouring class drawn in proportion to that class's local abundance, and differ only in reproducibility: nearestWeighted (default) -- keyed on the pixel's ground position: deterministic, needs no seed, stable under Cache(), and a grid-aligned crop of the study area gives the same answer as the full extent (so a small dev subset agrees with the scaled-up run). nearestRandom -- draws from the RNG, for when replicates should differ. The LandR floor moves to 1.2.0.9005 for two reasons. `method` does not exist before it, so an older LandR fails with an unused-argument error rather than silently doing something else. And 1.2.0.9004 had dropped the `newPossLCC` column that the block below uses to write replacement classes back into rstLCCAdj -- the is.null() guard there silently stopped firing, leaving rstLCCAdj, and hence ecoregionMap, showing the un-replaced classes. 1.2.0.9005 restores it; the comment now records the gap. Not yet exercised end-to-end: LandWeb's renv still pins LandR 1.2.0.9004, so the lockfile needs updating once LandR#197 merges before a dataPrep run will work. Co-Authored-By: Claude Opus 5 (1M context) --- Biomass_borealDataPrep.R | 22 ++++++++++++++++++---- NEWS.md | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Biomass_borealDataPrep.R b/Biomass_borealDataPrep.R index 7923031e..9a6e43f9 100644 --- a/Biomass_borealDataPrep.R +++ b/Biomass_borealDataPrep.R @@ -10,7 +10,7 @@ defineModule(sim, list( person(c("Alex", "M."), "Chubaty", email = "achubaty@for-cast.ca", role = c("aut")) ), childModules = character(0), - version = list(Biomass_borealDataPrep = "1.5.12"), + version = list(Biomass_borealDataPrep = "1.5.13"), timeframe = as.POSIXlt(c(NA, NA)), timeunit = "year", citation = list("citation.bib"), @@ -23,7 +23,7 @@ defineModule(sim, list( "archive", "assertthat", "cli", "data.table", "dplyr", "ggplot2", "httr2", "merTools", "plyr", "qs2", "rasterVis", "sf", "terra", "googledrive", "reproducible (>= 2.1.0)", "SpaDES.core (>= 2.1.0)", "SpaDES.tools (>= 2.0.0)", - "PredictiveEcology/LandR@development (>= 1.1.5.9090)", + "PredictiveEcology/LandR@development (>= 1.2.0.9005)", "PredictiveEcology/pemisc@development" ), parameters = rbind( @@ -137,6 +137,16 @@ defineModule(sim, list( "Since this is about estimating parameters for growth, it doesn't make any sense to have", "unique estimates for transient classes in most cases. If no classes are to be replaced, pass", "`'LCCClassesToReplaceNN' = numeric(0)` when supplying parameters.")), + defineParameter("LCCClassesToReplaceNNMethod", "character", "nearestWeighted", NA, NA, + paste("Passed to `LandR::convertUnwantedLCC()` as its `method` argument, controlling how", + "each `P(sim)$LCCClassesToReplaceNN` pixel picks among the available classes in its", + "neighbourhood. Both options weight the classes by their local abundance and differ", + "only in reproducibility. `'nearestWeighted'` (default) keys the draw on the pixel's", + "ground position, so it is deterministic, needs no seed, and a grid-aligned crop of", + "the study area gives the same answer as the full extent. `'nearestRandom'` draws", + "from the RNG instead, so replicates differ -- but note that `Cache()` does not key", + "on RNG state, so a cached call replays a single draw unless the seed is part of the", + "cache key. See `?LandR::convertUnwantedLCC`.")), defineParameter("minCoverThreshold", "numeric", 5, 0, 100, "Pixels with total cover that is equal to or below this number will be omitted from the dataset"), defineParameter("minRelativeBFunction", "call", quote(LandR::makeMinRelativeB(pixelCohortData)), @@ -834,14 +844,18 @@ createBiomass_coreInputs <- function(sim) { newLCCClasses <- convertUnwantedLCC( classesToReplace = P(sim)$LCCClassesToReplaceNN, rstLCC = rstLCCAdj, - availableERC_by_Sp = availableCombinations2 + availableERC_by_Sp = availableCombinations2, + method = P(sim)$LCCClassesToReplaceNNMethod ) |> Cache(userTags = c(cacheTags, "newLCCClasses", "stable")) ## adjust rstLCCAdj so that ecoregionMap will contain the last set of updated LCCClassesToReplaceNN if (nrow(newLCCClasses)) { if (!is.null(newLCCClasses$newPossLCC)) { - ## LandR versions prior to 1.1.5.9045 will not have this + ## LandR versions prior to 1.1.5.9045 do not have this, and 1.2.0.9004 dropped it + ## again -- where this guard silently stopped firing, leaving rstLCCAdj (and so + ## ecoregionMap) showing the un-replaced classes. reqdPkgs now floors LandR at + ## 1.2.0.9005, which restored it. rstLCCAdj[newLCCClasses$pixelIndex] <- newLCCClasses$newPossLCC } } diff --git a/NEWS.md b/NEWS.md index dce74a0d..6134259e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,23 @@ Known issues: +version 1.5.13 +============= + +## dependency changes +* requires `LandR (>= 1.2.0.9005)`, which reworked `convertUnwantedLCC()`: it gains a + `method` argument, and it again returns the `newPossLCC` column this module needs to + write replacement classes back into `rstLCCAdj`. That column was absent in LandR + 1.2.0.9004, where the `is.null()` guard below silently stopped firing and left + `rstLCCAdj` (hence `ecoregionMap`) showing the un-replaced classes. + +## new features +* new parameter `LCCClassesToReplaceNNMethod`, passed to `LandR::convertUnwantedLCC()` as + its `method`. Default `"nearestWeighted"` allocates each `LCCClassesToReplaceNN` pixel a + neighbouring class drawn in proportion to that class's local abundance, keyed on the + pixel's ground position so the result is deterministic without a seed and a grid-aligned + crop of the study area agrees with the full extent. `"nearestRandom"` draws from the RNG + instead, for when replicates should differ. + version 1.5.4 =============