
Changelog
landisutils 0.0.30
Dyadic season proportions in insertSeasonTable()
-
insertSeasonTable()now quantises theProportionFirecolumn to dyadic1/128fractions before writing. The Dynamic Fire System reads these proportions as single-precision floats and rejects the table (“Season Probabilities don’t add to 1.0”) unless they sum to 1.0 with essentially zero tolerance, so arbitrary decimal proportions (e.g. observed fire counts divided by the total, which only sum to 1.0 in exact or double-precision arithmetic) failed the check unpredictably depending on the data. Dyadic1/128fractions are exactly representable in float, sum to exactly 1.0 regardless of summation order, and round-trip through the table formatting; the largest season absorbs the rounding so the values still sum to 1.0. Callers can now pass raw normalised season proportions and rely on the table writer for LANDIS-II compatibility (1/128 is ~0.8% granularity).
landisutils 0.0.29
Optional SpinupCohorts / SpinupMortalityFraction in BiomassSuccession
-
BiomassSuccession$new()no longer force-writesSpinupCohortsandSpinupMortalityFraction: these keywords are absent from the Core8CoreV8.0-BiomassSuccession7.0grammar, and the LANDIS-II v8 parser aborts (“Found the name "SpinupCohorts" but expected "MinRelativeBiomass"”) when they appear. They are now optional and emitted only when set to a non-NULLvalue, so a defaultBiomassSuccessionconfig parses and runs against the stocklandis-ii-v8-releaseimage.
Request throttling + backoff for BioSIM weather fetches
- BioSIM weather retrieval now staggers requests and retries with exponential backoff. The
BioSIMweb API can be slow or transiently unavailable under load, and theBioSIMR client exposes no timeout/retry knobs, soget_clim_monthly(),get_clim_daily(), andget_fwi_daily()route theirgenerateWeather()calls through a shared wrapper that adds a random pre-request delay, exponential-backoff retries, and an optional per-attempt timeout (resetting the J4R client on timeout). Behaviour is tunable via thelandisutils.biosim.request_delay,landisutils.biosim.max_attempts,landisutils.biosim.backoff_base, andlandisutils.biosim.timeoutoptions; set them where the fetch process can see them (e.g. a worker-inherited.Rprofile).
landisutils 0.0.28
Enable DOM spinup in calibration scenario template
-
build_calibration_scenario_template()now patches the calibration scenario’sforc-succession.txtSpinUprow to1 0 1 20(DOM spinup ON, biomass spinup OFF), instead of the prior0 0 1 20(both OFF). Biomass spinup stays off so the pre-calibration snapshot IC’sCohortBiomassvalues are preserved verbatim, but the DOM spinup pass equilibrates ForCS’s soil-pool state viaSpinupSoils(). Without it,DisturbFireFromBiomassPoolsis left in a partly-initialised state and the first cohort that Dynamic Fire damages triggers aNullReferenceExceptioninExtension-ForCS-Succession/src/Soil.cs:DisturbanceImpactsBiomass, aborting every calibration trial. Cost: ~30-60s extra startup per LANDIS-II trial.
landisutils 0.0.27
Parallel pool teardown in landis_pool_stop()
-
landis_pool_stop()now passes every container name to a singledocker stop --time T <name1> <name2> ...and a singledocker rm -f <name1> <name2> ..., instead of looping one-container-at-a-time. The Docker daemon parallelises stops internally, so a 90-container pool teardown drops from ~15 minutes (90 x 10s SIGTERM deadlines, sequential) to roughlytimeout_secwall time. The function remains idempotent and tolerant of already-removed containers.
landisutils 0.0.26
cfg$scratch_root override for calibrate_dynamic_fire()
- The warm Docker pool’s bind-mount source defaults to
<out_dir>/scratch, but this fails whenout_dirlives on a filesystem the Docker daemon cannot see (e.g. user-space autofs / sshfs / NFS mounts). The daemon errors withmkdir <mount-root>: permission deniedwhile resolving the bind-mount path.cfg$scratch_rootlets callers route the pool’s scratch onto docker-visible local storage while keepingout_diron the project mount so the calibration trace CSV and final outputs land alongside the rest of the project.
landisutils 0.0.25
Connection-aware default n_cores
-
calibrate_dynamic_fire()now picks its default cluster size fromparallelly::availableCores(constraints = "connections", omit = 2)when parallelly is installed, falling back toparallel::detectCores() - 2Lotherwise.detectCores()reports the logical core count and ignores R’s per-session connection cap (~125 on default builds), so on very large hosts (e.g. 256-core machines) a naive default silently over-provisions the FORK cluster beyond what the R session can support. Explicitly settingcfg$n_coresshort-circuits both defaults.parallellyis now aSuggests.
Work around DEoptim 2.2.8 cluster-cleanup bug
-
calibrate_dynamic_fire()no longer passesparallelType = 1LtoDEoptim::DEoptim()when supplying its own FORK cluster viacontrol$cluster. DEoptim 2.2.8’sctrl$clusterbranch uses the supplied cluster without binding a localclvariable, but its post-loop cleanup unconditionally evaluatesparallel::stopCluster(cl)wheneverparallelType == "parallel", which errors withobject 'cl' not found. LeavingparallelTypeat its default (“none”) skips that cleanup path while still triggering the parallelparApply(cl = ctrl$cluster, ...)evaluation in DEoptim’s body. The FORK cluster lifecycle is fully managed bycalibrate_dynamic_fire()viaon.exit.
Clamp IgnProb to LANDIS-II’s permitted range
-
patch_fire_config()andapply_calibrated_ignprob()now clamp the per-fuelIgnProbvalue to[0, 1]after applying the calibration multiplier. The Dynamic Fire System parser rejects anyIgnProboutside this range withError with the input value for Fuel type initiation probability: Value must be between 0 and 1.0and aborts the run, which previously caused every DEoptim trial whose multiplier pushed the product above 1.0 (e.g.,IgnProb_Conifer = 1.5against the defaultIgnProb = 1.0for Conifer surfaces C1-C5/C7/M1-M4) to fail immediately. The multiplier ranges in the smoke test andcalibration_par_names()are unchanged; clamping just makes the search-space boundary explicit instead of relying on every multiplier being <= 1.0.
Retain failed-trial scratch directories for post-mortem
-
sim_landis()now keeps a failing trial’s per-rep scratch directory on disk (and prints its path) when the LANDIS-II invocation errors. The scratch was previously deleted unconditionally by anon.exitcleanup, which made it impossible to inspect<trial_dir>/rep01/log/for the underlying LANDIS-II stderr/stdout from a failed calibration trial. Successful trials are still cleaned up as before, so this only adds disk usage on failure. The behaviour can still be opted out of by passingkeep_scratch = TRUE(which now retains the dir on both success and failure – the priorkeep_scratch = FALSEdefault still means “clean up after success only”).
landisutils 0.0.24
Per-file input overrides on build_calibration_scenario_template()
- New
overrides = list()argument lets callers substitute individual template files post-copy without touching the production scenario. Useful for cropping / aggregating specific inputs for calibration (e.g., a coarser fuel raster, a smaller weather DB, a substitute slope raster) without forking the whole template. Accepted keys:ground_slope.tif,uphill_slope_azimuth.tif,fire-ecoregions.tif,initial_weather_database.csv,DynamicFire_Spp_Table.csv,species.txt,ecoregions.txt,ecoregions.tif,climate.txt..tifoverrides also carry their.aux.xml/.tfwsidecars. Backward compatible:overrides = list()(the default) preserves the original “copy everything fromtemplate_dir” behaviour.
Warm Docker pool resilience
- New exported
landis_pool_restart_one(pool, idx)– stops + removes the container at indexidxand starts a fresh replacement with identical config (image, scratch_root bind-mount, user, cpu_limit, mem_limit) using a new auto-generated container name. Pool state ($names[idx]) is updated in place and also propagated to the caller’s frame so loops can use the current container name on the next iteration. -
landis_pool_exec()gainsretries = 0L. When > 0 and the exec command fails with non-zero status, the container is restarted vialandis_pool_restart_one()and the command retried, up toretriesadditional attempts. Useful for long calibrations that occasionally hit OOM kills or daemon hiccups without wanting the whole DEoptim run to abort. Returns an additionalattemptsfield counting actual attempts (1 = no retry needed; >1 = some retries consumed). - Pool object now carries the start-time args (
user_args,cpu_args,mem_args) so restarts produce containers with matching config. - Refactor: container-creation logic moved into the internal
.landis_pool_start_one()solandis_pool_start()andlandis_pool_restart_one()share one code path.
Pre-flight checks in calibrate_dynamic_fire()
The driver now runs a battery of cheap pre-flight checks at function entry – before starting the warm Docker pool or FORK cluster – to catch common config / scenario / payload errors fast. Hard errors include:
-
cfg$lower >= cfg$upperfor any parameter (lists the offending names). -
cfg$NP < 4(DEoptim minimum),cfg$itermax < 1,cfg$n_reps < 1. -
cfg$weightsall zero (DEoptim would have nothing to optimise). - Unknown
cfg$simulator(one of"landis","r_reimpl","mock"). - For
simulator = "landis": the calibration scenario template missing any of the LANDIS-II input files normally produced bybuild_calibration_scenario_template(). - Observed-targets payload missing required
$primaryshape. - Docker not available when
method = "docker"; LANDIS console not findable whenmethod = "local". - Scratch root not writable.
Soft signals (warnings / messages):
-
NP < 10 * length(par_names)(DEoptim’s own advisory, surfaced earlier). -
cfg$weights['area_fuel'] > 0but observed lacksarea_by_fuel_ha/fuel_code_to_base; orcfg$weights['severity'] > 0but observed lacksseverity_dist. In both cases the corresponding loss component contributes 0.
Calibration smoke-test script
-
New
inst/scripts/calibration_smoke_test.R– a complete 5-stage smoke test of the calibration plumbing (observed targets, spinup, scenario template, sim_landis trial, DEoptim loop) at minimal scale (NP=4, itermax=2, n_reps=1, n_cores=2). Useful for first-time setup, post- upgrade verification, and confirming Docker + DEoptim are installed correctly. Runs in ~5-20 min via the warm Docker pool.Usage:
source(system.file("scripts/calibration_smoke_test.R", package = "landisutils"))
Tier 2 calibration loss components
loss_from_stats() now computes L_area_fuel and L_severity when the corresponding observed components are present in the payload; previously both were stubbed at zero. Components are still gated by their weights entry, so projects that aren’t ready to use them stay on the Tier 1 (count + size) loss by default.
L_area_fuelis a chi-squared distance between simulated and observed burn-area-by-base-fuel-type proportions. Simulated area-by-fuel is derived from each event’sinit_fuel(the ignition cell’s fuel code) times itsDamagedSites, mapped to the five base fuel types viaobserved$fuel_code_to_base. Activated whenobserved$primary$area_by_fuel_haANDobserved$fuel_code_to_baseare both set; contributes 0 otherwise.L_severityis a chi-squared distance between simulated and observed severity-class proportions. Simulated severities come from each event’sMeanSeverity(binned into integer classes 1..5 at half-integer boundaries); observed comes fromobserved$primary$severity_dist, a named numeric vector summing to 1. Activated whenseverity_distis non-NULL.save_observed_fire_targets()gains aseverity_dist = NULLargument that callers can pass to embed a prior in the observed payload (e.g., the newdefault_severity_prior_sturtevant2009()).New exported helper:
default_severity_prior_sturtevant2009()returns a named 5-element vector of severity-class proportions derived from Sturtevant et al. 2009. Intended as a starting point; projects should override with empirical priors when available.
Dynamic Fire System extension calibration
A new function family for calibrating the LANDIS-II Dynamic Fire System extension. The calibration tunes SeverityCalibrationFactor, per-season FMC HiProp values, and per-base-fuel-type IgnProb multipliers so simulated fires match observed regional fire statistics; the empirically-fit parameters (Mu, Sigma, Max, NumFires, seasonal PropFire) are fit at the data layer upstream and are not part of the optimisation.
-
Pure-data helpers (no LANDIS-II invocation):
-
calibration_par_names()– canonical 9-name parameter vector. -
parse_dynamic_fire_logs()– readsfire/dynamic-fire-event-log.csv+fire/dynamic-fire-summary-log.csvinto a small per-rep stats list. -
patch_fire_config()– surgical text patch ofdynamic-fire.txt(SeverityCalibrationFactor scalar; FireSizesTable HiProp columns; FuelTypeTable IgnProb column multiplied per base type). -
loss_from_stats()– multi-component weighted loss (count + KS-on-size in Tier 1; area_by_fuel + severity stubbed at weight 0 for Tier 2). -
apply_calibrated_ignprob()/apply_calibrated_hi_prop()– table-level helpers used downstream of calibration to feed calibrated values into production fire-config writers.
-
-
Observed-target builder (one-time NFDB-derived summaries):
-
save_observed_fire_targets()– writes a small.rdsof per-ecoregion observed summaries. Project-agnostic: primary / secondary ecoregion SpatVectors +fuel_code_to_basemapping are all caller-provided. -
bc_fuel_code_to_base()– default fuel-code mapping for BCFUEL_TYPE_CDfactor encoding (codes 1..13); pass a custom mapping if your project’s fuel-classification raster uses a different encoding.
-
-
Scenario builders for the static-landscape calibration scenario:
-
build_calibration_spinup_scenario()– builds a one-off LANDIS-II scenario that runs ForCS with both spinup flags ON and emits a snapshot of the spun-up cohort community via the Output Biomass Community extension. The year-0 snapshot becomes the calibration IC. -
run_calibration_spinup()– blocking single-trial LANDIS-II invocation for the spinup; dispatches tolandis_run_local()/landis_run_docker(). -
build_calibration_scenario_template()– copies a production fire scenario, swaps the IC for the spinup snapshot, patches ForCS for calibration (spinup off + frozen succession), optionally writes a fresh baselinedynamic-fire.txtinline (breaks the cycle between acalibrated_fire_params-aware production fire config and the calibration loop). -
write_landis_scenario_file()(inscenarios.R) – lower-levelscenario.txtwriter that takes already-written extension config-file paths rather than R6 extension objects. Useful when project pipelines write extension configs in separate steps.
-
-
Warm Docker pool for calibration:
-
landis_pool_start()/landis_pool_exec()/landis_pool_stop()– a pool of detached LANDIS-II containers thatdocker execs per DEoptim trial instead ofdocker run --rmper trial. Per-call env overrides (HOME=/tmp,DOTNET_BUNDLE_EXTRACT_BASE_DIR=...) keep dotnet from accreting per-user-cache state between trials. Designed foron.exit()teardown from calibration drivers.
-
-
Simulator backends for the calibration loop:
-
sim_landis()– per-trial LANDIS-II invocation. Takes file paths only (FORK-safe), copies template -> scratch dir, patchesdynamic-fire.txt, runs LANDIS-II either via the warm pool or a one-offlandis_run_docker()/landis_run_local(), parses logs. -
sim_r_reimpl()– reserved slot for a future pure-R reimplementation; currently errors with a not-yet-implemented message. -
sim_mock()– plausibly-shaped output for testing the calibration driver’s control flow without Docker.
-
-
DEoptim driver:
-
calibrate_dynamic_fire()– orchestrates the calibration: starts a warm Docker pool, sets up a FORK cluster with per-worker container pinning, invokesDEoptim::DEoptim()with the multi-component loss as the objective, tears down pool + cluster viaon.exit(). Gated onrequireNamespace("DEoptim")(DEoptim is in Suggests; install viarenv::install("DEoptim")before calling).
-
Vignette:
vignette("Dynamic-Fire-Calibration", package = "landisutils")documents the end-to-end target-wiring pattern for downstream projects.Tests: 97+ testthat expectations across calibration + pool test files. Docker-gated tests
skip_if_not()when docker is unavailable; DEoptim-gated driver testsskip_if_not_installed("DEoptim").
landisutils 0.0.23
landis_run_docker() accepts resource constraints
- New arguments to
landis_run_docker()(and proxied throughtar_landis()):-
cpu_limit = 4: maps todocker run --cpus. LANDIS-II compute is single-threaded (~1 core), but the .NET runtime spins up 9-11 OS threads for GC and the thread pool, so 4 is a comfortable default. PassNULLfor no limit. -
mem_limit = "8g": baseline RAM cap (maps todocker run --memory). Accepts a numeric byte count or a string like"4g"/"512m". PassNULL(ormem_limit = Inf) for no limit. -
mem_margin = 1.5: headroom factor applied to a previously-observed peak (see auto-resolution below).
-
-
Auto-resolution from prior resource logs. Before running, the function reads any existing
<rep_dir>/log/{docker,local}_resources.logfor the rep. Ifpeak_mem_bytes * mem_marginexceeds the baselinemem_limit, the limit is raised to that value so a rep that ran fine last time is never killed by the cap on a rerun. If no prior log exists for the rep (first run, or rep dir freshly deleted), the memory cap is dropped entirely so the first run can discover what it needs; subsequent runs inherit the empirically observed peak. - The CPU limit is constant regardless of history: LANDIS-II is single-threaded and the .NET runtime doesn’t scale with available cores.
landisutils 0.0.22
Resource logs now self-describe the host
-
landis_run_docker()andlandis_run_local()now append three additional lines to each rep’sdocker_resources.log/local_resources.log:host_cpu_model: <model name> # e.g. "AMD EPYC 7702 64-Core Processor" host_cpu_cores: <N> # logical cores visible to R host_ram_bytes: <N> # total system memory in bytesThis makes each per-rep resource log self-describing: downstream provenance tooling can recover not just what the rep used (
elapsed_sec,peak_mem_bytes) but the host it ran on, important when reps are dispatched across a heterogeneous cluster. -
New exported helpers:
-
host_cpu_info()returnslist(model, n_logical, ram_bytes), cross-platform:/proc/cpuinfo+/proc/meminfoon Linux,sysctl machdep.cpu.brand_string+hw.memsizeon macOS,PROCESSOR_IDENTIFIERenv var +wmic ComputerSystem ... TotalPhysicalMemoryon Windows. Logical-core count usesparallel::detectCores()everywhere. Called automatically by the run helpers. -
read_landis_resource_logs(run_dir)parses any*_resources.logunderrun_dirand returns one data.frame row per rep with all fields. Used by downstream report tooling to summarise run-time / memory / host across replicates.
-
landisutils 0.0.21
prepTopographyFile() fills NoData with 0
-
prepTopographyFile()(and itsprepGroundSlopeFile()/prepUphillAzimuthMap()wrappers) now replaces NoData cells with0before writing the INT2S raster.terra::terrain()leaves edge cells asNaN(no full 3x3 neighbourhood), which becomes the-32768sentinel under INT2S. LANDIS-II’s Dynamic Fire reader rejects any active cell with that value:Ground Slope invalid map code: -32768The bug was latent for grids with few active cells, but is exposed whenever upstream changes (e.g. corrected non-veg masks) expand the active area into the 1-cell edge band of the terrain raster. A flat default (0 deg) is safe: a single-cell edge contributes negligibly to Dynamic Fire’s rate-of-spread.
landisutils 0.0.20
tar_landis() idempotency respects input changes
- The skip check in
tar_landis()is now input-aware. Previously the run was skipped whenever<rep_dir>/Landis-log.txtexisted and contained “Model run is complete”, regardless of whether the inputs had changed. This meant that when targets correctly re-evaluated the run target after an upstream input change (e.g. a regeneratedinitial-communities.tiforecoregions.tif), the surrounding command ran but the skip check still fired, solandis_run_docker()/landis_run_local()was never invoked and the rep dir kept stale outputs from the previous run. - The fix writes a
<rep_dir>/log/input_hash.jsonsidecar after each successful run, capturing a SHA-1 of (per-input-file MD5 +base_seed+rep_index+scenario_file). The skip check now also requires the saved hash to match the current input hash; any mismatch triggers a real rerun. - New
force = FALSEargument ontar_landis(). Settingforce = TRUEshort-circuits the skip check so LANDIS-II always runs (useful for debugging and one-off forced reruns without deleting rep dirs). -
Migration: existing rep dirs lack
log/input_hash.json, so the firsttar_make()after upgrading rebuilds every rep. This is the safe-conservative behaviour: we can’t know whether existing outputs correspond to current inputs. Users who know a rep is current can sidestep the rerun by writing the hash file manually.
landisutils 0.0.19
landis_run_docker() captures image digest
-
landis_run_docker()now writes<scenario_dir>/log/docker_image.logcontaining the immutablesha256digest of the image used for the run (<repo>@sha256:<64hex>, falling back to the localIdfor images with no registry origin). Image tags are mutable; the digest is the canonical identifier of the bytes that actually ran. Downstream provenance tooling can read this sidecar to pin a run to a specific image regardless of subsequent tag movement. - New
pull = FALSEargument; whenTRUE,docker pull <image>runs before the simulation so the captured digest reflects the current registry rather than a possibly-stale local copy. The argument is also exposed viatar_landis(pull = ...).
landisutils 0.0.18
leading_species() handles non-vegetated cells
-
leading_species()now returns"Non-vegetated"for cells where total biomass across all species is zero, instead of falling through to the alphabetical tiebreaker (which arbitrarily assigned"Ac"to defoliated cells). This matches the existingcommunity_label()behaviour and means transition / alluvial plots accurately reflect fire and harvest impacts.
landisutils 0.0.17
Per-replicate parallel branching in tar_landis()
Breaking API change:
tar_landis()no longer acceptsn_reps. It now takes an explicitrep_indexargument (an unquoted upstream target symbol) and returns a singletar_targetobject (same as the original API).-
The caller creates the rep-index target explicitly inside the module
list()alongside thetar_landis()call. This keeps both targets visible to tarborist’s static AST analysis (tarborist.additionalSingleTargetFactorieshandles the run target; the literaltar_target()call handles the rep-index target):list( tar_target(name = ..._rep_index, command = seq_len(5L), iteration = "vector"), landisutils::tar_landis(name = ..., rep_index = ..._rep_index, ..., pattern = cross(scenario_dir, ..._rep_index)) ) iteration = "vector"on the rep-index target is what enablescross()to iterate over individual elements, givingn_scenarios x n_repsindependent branches dispatched to crew workers in parallel.Each branch runs one LANDIS-II simulation and tracks only that replicate’s output files. Previously all
n_repssimulations ran inside a singleforloop within one target branch.Caching and scaling: adding replicates only creates new branches – existing replicate results remain cached. Changing one replicate’s inputs invalidates only that branch. Previously any change invalidated all replicates.
landis_replicate() single-rep mode
-
landis_replicate()gains arep_indexparameter for single-replicate creation. Passrep_index = i(instead ofn_reps = N) to create exactly one replicate directory (repNN/) without touching any others. Useful when each replicate is dispatched to its own crew worker. -
n_repsis now keyword-only; the positional second-argument form still works butn_reps =is clearer. - The function now requires exactly one of
n_repsorrep_index. - The seed assigned when
rep_indexis used (base_seed + rep_index - 1) matches the seed assigned by then_repsmode for the same index, so results are reproducible regardless of which call form was used.
landisutils 0.0.16
Vegetation dynamics: species biomass and transition plots
- New
read_biomass_c_snapshots(paths, times, run_name)reads ForCSlog_BiomassC.csvfiles (per-cohort, per-cell) for one or more replicates, filtering to requested snapshot years viaarrow::open_dataset()lazy streaming so that multi-GB files never need to be fully materialised in R. ForCS writeslog_BiomassC.csvunconditionally, so no additional output extension is required. - New
read_biomass_output_rasters(dirs, times, species, live_map_pattern, run_name)is the succession-agnostic alternative: reads per-species biomass rasters written by the Output.Biomass v4 extension (present in thelandis-ii-v8-releaseDocker image). Works with any succession extension; requires Output.Biomass to be included in the scenario configuration. - Both readers return an identical
data.tableschema (scenario, replicate, Time, row, column, [ecoregion,] species, biomassin Mg C ha^-1), so all downstream functions are source-agnostic. - New
biomass_landscape_summary(df)aggregates per-cell snapshot data to landscape-mean ± SD biomass by species per timestep. - New
leading_species(df)labels each cell at each snapshot by the species with the highest total live biomass. Ties broken alphabetically. - New
community_label(df, n_spp, min_pct)labels each cell by its top-nspecies combination (e.g."Hw-Sx"); species belowmin_pctof cell total are dropped; zero-biomass cells are labelled"Non-vegetated". - New
transition_data(label_df, times)builds the lodes-formtibblerequired byggalluvial: unique label-path combinations across all snapshot years, with cell counts averaged across replicates. - New
plot_species_biomass(summary_df, colours, title)produces a stacked area chart of landscape-mean biomass by species over time. - New
plot_transitions(lodes_df, colours, title)produces a Sankey-style alluvial diagram (viaggalluvial) showing how cells move between vegetation types across snapshot years. -
arrow,ggalluvial,ggplot2, andpurrradded toImports(previously absent or inSuggests).
landisutils 0.0.15
tar_landis() / landis_run_docker() fixes
-
landis_run_docker(): container names now include the calling process PID and a random integer suffix to prevent name collisions when multiple LANDIS replicates run simultaneously. -
tar_landis(): dependency file lists are now deduplicated before being passed tolandis_replicate(). Paths are normalised to absolute form, filtered to existing files, then deduplicated by basename; scenario-specific files (under the scenario directory) take priority over cross-scenario duplicates. Applies to both the Docker and local run paths. -
tar_landis(): replicates where LANDIS-II already completed successfully are now skipped (idempotent re-run). Completion is detected by the presence of “Model run is complete” inLandis-log.txt. Applies to both the Docker and local run paths.
landisutils 0.0.14
ForCS Succession extension
-
insertDOMPools()now wraps multi-word pool names in double-quotes, matching the LANDIS-II parser’s requirement (e.g."Fast AG"instead ofFast AG). - ForCS v4 changed four large parameter tables from inline text to CSV file references.
insertEcoSppDOMParameters(),insertANPPTimeSeries(),insertMaxBiomassTimeSeries(), andinsertEstablishProbabilities()now each write a CSV topathand emit theKeyword "filename"reference line, matching the ForCS v4.0.2 input format. -
ForCS$write()passesself$pathto the four CSV-writinginsert*()functions and registers the resulting files viaadd_file()solandis_replicate()copies them into each replicate directory. - The four ForCS CSV filenames are now prefixed with
ForCS_(ForCS_EcoSppDOMParameters.csv,ForCS_ANPPTimeSeries.csv,ForCS_MaxBiomassTimeSeries.csv,ForCS_EstablishProbabilities.csv) so their origin is unambiguous alongside other extension files.
Output file tracking
- All
LandisExtensionsubclasses that produce fixed-name output files now expose anoutput_filesactive binding listing those files as relative paths (e.g. log CSVs, summary CSVs). Extensions with no fixed outputs inherit the baseLandisExtension$output_fileswhich returnscharacter(0). -
LandisScenariogains anoutput_filesactive binding that returns the two LANDIS-II core outputs always written to the scenario directory:Landis-log.txtandMetadata/LANDIS-II v8.0/LANDIS-II v8.0.xml. -
scenario()now writesoutput_manifest.txtto the scenario directory, listing all fixed-name output files declared by the scenario and its extensions. The manifest is registered inscenario$filessolandis_replicate()copies it into each replicate directory. -
tar_landis()now readsoutput_manifest.txtfrom the base scenario directory and includes the listed files (as absolute paths per replicate) in the returned character vector alongside thelog/scan andoutput_dirscan. This ensures targets tracks log CSVs,Landis-log.txt, and the Metadata XML explicitly, without relying on glob discovery.
Resource tracking for simulation runs
-
landis_run_docker()now tracks wall-clock elapsed time and peak container memory. Docker is launched viacallr::r_bg()so the main thread can polldocker stats --no-streamevery 2 s; the maximum observed RSS is recorded as peak memory. A named container (e.g.landis-run-20260527123456) is used for stats lookup and removed automatically with--rm. Results are printed on completion and written to<scenario_dir>/log/docker_resources.log. The--user uid:gidflag is now skipped on Windows (id -u/id -gare not available there; Docker Desktop on Windows does not require it). -
landis_run_local()now tracks wall-clock elapsed time and peak process memory.system2()is replaced byprocessx::process$new()(which exposes the subprocess PID and handles the working directory directly), and the main thread pollsps::ps_memory_info()every 2 s. Thepspackage is cross-platform, so memory tracking works on Linux, macOS, and Windows without any platform-specific shell commands. Results are printed on completion and written to<scenario_dir>/log/local_resources.log. - Both functions now return a named list (
exit_code,elapsed_sec,peak_mem_bytes) instead of a bare integer exit code. -
processxandpsadded toImports(previously available only as transitive dependencies ofcallr).
landisutils 0.0.13
-
DynamicFire$write()now callsadd_file()forInitialWeatherDatabaseso it is copied into replicate directories bylandis_replicate(). Previously the weather CSV was silently absent, causing LANDIS-II to fail at runtime. -
tar_landis()output_dirnow accepts a character vector of output subdirectory names. Passc("output", "fire")when using the Dynamic Fire extension, which writes its maps and event/summary logs to afire/subdirectory inside the scenario directory.
landisutils 0.0.12
-
insertFile(),insertLandisData(),insertValue()are now exported so project-level code can build custom scenario files using the same primitives the package uses internally. -
landis_find_docker()is a new helper that returns the path toLandis.Console.dllinside the container, readinggetOption("landisutils.docker.console").landis_run_docker()now calls it instead of duplicating the lookup. -
landis_replicate()gains abase_seedargument. When set, theRandomNumberSeedin each replicate’sscenario.txtis rewritten tobase_seed + (rep_index - 1), giving every replicate a distinct but deterministic seed. Seeds are index-stable: adding more replicates later never changes the seeds of existing ones. -
tar_landis()gains abase_seedargument, passed through tolandis_replicate()and baked into the command expression at factory-call time socrewworkers receive the correct value.
landisutils 0.0.11
- Pin
santokuto its GitHub source (hughjonesd/santoku) after the package was archived on CRAN on 2026-05-15, which brokezonaldependency resolution in GitHub Actions CI.
landisutils 0.0.10
- Remove unused PredictiveEcology packages:
LandR,SpaDES.core,SpaDES.tools,reproducible,scfmutils, andmap– due to broken dependency resolution.
landisutils 0.0.9
-
landis_run(): fix invertedstopifnot()guard — the function previously rejected validLandisScenarioobjects and accepted everything else. - New
landis_run_docker()runs a LANDIS-II simulation in an ephemeral Docker container (bind-mounting the scenario directory to/sim). - New
landis_run_local()runs a LANDIS-II simulation directly viadotnet, writing stdout/stderr to<scenario_dir>/log/. - New
tar_landis()factory creates a targetsformat = "file"target that runs LANDIS-II (locally or via Docker) and returns tracked output and log files. - New package options
landisutils.docker.imageandlandisutils.run.methodare set by.onLoad().
landisutils 0.0.8
- fix issue with
climrreturning reference period rows - filter these when assembling data;
landisutils 0.0.7
-
prep_monthly_weather_climr()no longer silently advertises"srad"as a supported variable. Vignette examples updated. -
get_elevation_rast()gained atmp_dirargument (default<landisutils.cache.path>/elevatr_tiles/) so AWS Terrain Tile downloads land in the package cache instead of leaking into the R session’s globaltempdir()(elevatr’s own default). - climate test cleanup: a new
local_climate_test_cache()test helper (tests/testthat/helper-climate-cleanup.R) routes the cache option, child-processTMPDIR, and the JVMjava.io.tmpdir(used byJ4RforJ4RServer*.logandhsperfdata_<user>/) into the per-testwithr::local_tempdir(), and tears down anyfuture::plan(multisession)on exit, soBioSIM/climr/elevatrfetch tests no longer accumulate/tmpresidue across runs.
landisutils 0.0.6
- added a focused integration-test scenario
necn_scrppleexercisingNECNSuccession+SocialClimateFireplus the biomass output extensions (OutputBiomass,OutputBiomassCommunity,OutputBiomassByAge,OutputBiomassReclass); validated end-to-end on both v8 Docker images; - fixed
OutputBiomassByAge$write()emitting oneSpeciesline per element (the LANDIS-II parser only accepts oneSpecieskeyword); the species list is now joined with indented continuation lines as the format requires;
landisutils 0.0.5
- added new climate-data backends for use with the LANDIS-II Climate Library:
- daily and monthly weather from BioSIM via the
BioSIMpackage (prep_daily_weather(),prep_monthly_weather_biosim()); - monthly weather from
climr(prep_monthly_weather_climr()), including the bcgov-recommended 8-member GCM ensemble (climr_ensemble_8); - monthly weather from TerraClim via
climateR(prep_monthly_weather());
- daily and monthly weather from BioSIM via the
- exported the lower-level fetch and assembly helpers used by the above (
get_clim_daily(),get_clim_monthly(),get_clim_monthly_climr(),get_clim_monthly_terraclim(),get_fwi_daily(),get_elevation_rast(),create_locations_df(), and theassemble_climate_library_file*()family); - climate caches are now namespaced by a study-area hash so distinct study areas don’t collide;
- added
test_ecoregionPolysdataset to support examples and tests; - reworked the
climate-datavignette to demonstrate the new backends; - fixed
BiomassSuccessionR6 class name (was"DynamicFuels"); - added
cffdrsanddigesttoImports; - added
arrow,BioSIM, andclimrtoSuggests.
landisutils 0.0.3
- added support for the remaining LANDIS-II v8 extensions:
- succession: DGS Succession, NECN Succession, PnET Succession;
- disturbance: Biomass Browse, Biomass Harvest, Climate BDA, EDA (Epidemiological Disturbance Agent), Forest Roads Simulation, Hurricane, Land Use Plus, Linear Wind, Magic Harvest, Original Wind, Root Rot;
- output: Output Biomass Community, Output Biomass-PnET, Output Biomass Reclass, Output Landscape Habitat, Output Local Habitat, Output Wildlife Habitat;
- added
Multi-Extension-Scenariosvignette; - miscellaneous updates and fixes to existing extensions.
landisutils 0.0.2
- cache and batch weather data acquisition by year (#1);
- allow setting cache path using option
landisutils.cache.path; - change arguments
startandendinprep_*_weather()to be integer years; - use
R6classes to track simulation input files; - implemented scenario replication (#3);
- run LANDIS-II in background process via
callr;