
Assembling a Multi-Extension Scenario
Multi-Extension-Scenarios.Rmdlandisutils lets you compose a LANDIS-II scenario from
one succession extension, any number of disturbance extensions, and any
number of “other” (typically output) extensions. This vignette walks
through wiring up a representative mix of extensions and writing the
full set of input files to a scenario directory.
The data here are skeletal — the goal is to show the API, not to
produce a biologically-meaningful run. Each section’s parameter values
are taken verbatim from the corresponding extension’s upstream LANDIS-II
Core8 test input on GitHub (cited in each chunk), so the demonstration
mirrors what the LANDIS-II developers themselves use to exercise each
extension. For real workflows, each prep*() helper would
draw from project-specific upstream data.
library(landisutils)
tmp_pth <- withr::local_tempdir("example_MultiExt_")Stub input files
scenario() validates that input files exist on disk. For
a demonstration we just writeLines("") empty stubs at each
path the extensions will reference.
clim_file <- file.path(tmp_pth, "climate.txt")
init_comm_files <- c(
file.path(tmp_pth, "initial-communities.csv"),
file.path(tmp_pth, "initial-communities.tif")
)
spp_file <- file.path(tmp_pth, "SpeciesData.csv")
spperd_file <- file.path(tmp_pth, "SppEcoregionData.csv")
ecoregion_files <- c(file.path(tmp_pth, "ecoregions.txt"), file.path(tmp_pth, "ecoregions.tif"))
species_input_file <- file.path(tmp_pth, "Core_species_data.txt")
stub_files <- c(
clim_file,
init_comm_files,
spp_file,
spperd_file,
ecoregion_files,
species_input_file
)
for (f in stub_files) {
writeLines("", f)
}Succession: Biomass Succession
A scenario must specify exactly one succession extension. Here we use
BiomassSuccession. Tables match the upstream Core8 test
input verbatim (https://github.com/LANDIS-II-Foundation/Extension-Biomass-Succession/blob/master/testings/CoreV8.0-BiomassSuccession7.0/biomass-succession.txt).
min_rel_b <- tibble::tribble(
~ShadeClass , ~Eco1 , ~Eco2 ,
NA_integer_ , "101" , "102" ,
1L , "25%" , "25%" ,
2L , "45%" , "45%" ,
3L , "56%" , "56%" ,
4L , "70%" , "70%" ,
5L , "90%" , "90%"
)
suff_light <- tibble::tribble(
~class , ~X0 , ~X1 , ~X2 , ~X3 , ~X4 , ~X5 ,
1L , 1.0 , 0.5 , 0.25 , 0.0 , 0.0 , 0.0 ,
2L , 1.0 , 1.0 , 0.5 , 0.25 , 0.0 , 0.0 ,
3L , 1.0 , 1.0 , 1.0 , 0.5 , 0.25 , 0.0 ,
4L , 1.0 , 1.0 , 1.0 , 1.0 , 0.5 , 0.25 ,
5L , 0.1 , 0.5 , 1.0 , 1.0 , 1.0 , 1.0
)
erp_df <- tibble::tribble(
~ecoregion , ~AET ,
"101" , 600 ,
"102" , 600
)
frp_df <- tibble::tribble(
~Severity , ~WoodLitterReduct , ~LitterReduct ,
1L , 0.0 , 0.5 ,
2L , 0.0 , 0.75 ,
3L , 0.0 , 1.0
)
hrp_df <- tibble::tribble(
~Name , ~WoodLitterReduct , ~LitterReduct , ~CohortWoodRemoval , ~CohortLeafRemoval ,
"MaxAgeClearcut" , 0.5 , 0.15 , 0.8 , 0.0 ,
"PatchCutting" , 1.0 , 1.0 , 1.0 , 0.0
)
ext_succ <- BiomassSuccession$new(
path = tmp_pth,
Timestep = 10,
SeedingAlgorithm = "WardSeedDispersal",
InitialCommunitiesFiles = init_comm_files,
ClimateConfigFile = clim_file,
SpinupCohorts = FALSE,
SpinupMortalityFraction = 0.05,
MinRelativeBiomass = min_rel_b,
SufficientLight = suff_light,
SpeciesDataFile = spp_file,
EcoregionParameters = erp_df,
SpeciesEcoregionDataFile = spperd_file,
FireReductionParameters = frp_df,
HarvestReductionParameters = hrp_df
)
ext_succ$write()Disturbance 1: Original Fire
OriginalFire writes a
FireRegionParametersTable (one row per fire regime),
references a fire-species CSV and an initial-fire-regions raster, and
accepts fuel-curve / wind-curve / fire-damage tables. Values below come
from the upstream Core8 test input (https://github.com/LANDIS-II-Foundation/Extension-Base-Fire/blob/master/testings/Core8.0-OriginalFire5.0/original-fire.txt).
fire_spp_file <- file.path(tmp_pth, "species-fire.csv")
fire_regions_map <- file.path(tmp_pth, "fire-regions-map.tif")
for (f in c(fire_spp_file, fire_regions_map)) {
writeLines("", f)
}
frp_table <- data.frame(
FireRegionName = c("eco1", "eco2"),
MapCode = c(1L, 2L),
MeanSize = c(100, 200),
MinSize = c(4, 6),
MaxSize = c(400, 600),
IgnitionProb = c(0.001, 0.001),
k = c(100L, 50L)
)
fuel_curve <- data.frame(
FireRegionName = c("eco1", "eco2"),
S1 = c(10, 5),
S2 = c(20, 15),
S3 = c(50, 20),
S4 = c(70, -1),
S5 = c(120, -1)
)
wind_curve <- data.frame(
FireRegionName = c("eco1", "eco2"),
S5 = c(-1, 1),
S4 = c(-1, 5),
S3 = c(1, 15),
S2 = c(10, 20),
S1 = c(20, 30)
)
ext_fire <- OriginalFire$new(
path = tmp_pth,
Timestep = 5L,
Species_CSV_File = fire_spp_file,
FireRegionParametersTable = frp_table,
InitialFireRegionsMap = fire_regions_map,
FuelCurveTable = fuel_curve,
WindCurveTable = wind_curve,
FireDamageTable = defaultFireDamageTable()
)
ext_fire$write()Disturbance 2: EDA with one agent
EDA wraps any number of pathogen agents. Each edaAgent()
call emits a per-agent eda/<name>.txt file referenced
from the main EDA config. Parameters and species rows below
come from the upstream Core8 Phytophthora ramorum test agent
(https://github.com/LANDIS-II-Foundation/Extension-Base-EDA/blob/master/tests/Core8.0-EDA3.0/P_ramorum.txt).
eda_sp <- data.frame(
Species = c("Umbecali", "Lithdens"),
LowAge = c(5, 5),
LowScore = c(3, 2),
MediumAge = c(15, 20),
MediumScore = c(6, 4),
HighAge = c(40, 60),
HighScore = c(10, 7),
VulnLowAge = c(999, 5),
VulnLowMortProb = c(0, 0.14),
VulnMediumAge = c(999, 15),
VulnMediumMortProb = c(0, 0.25),
VulnHighAge = c(999, 30),
VulnHighMortProb = c(0, 0.30),
CFSConifer = c("no", "yes"),
MortalityPlot = c("no", "yes")
)
ext_eda <- EDA$new(
path = tmp_pth,
Timestep = 1L,
Agents = list(edaAgent(
name = "ramorum",
SHIMode = "mean",
TransmissionRate = 1.8,
AcquisitionRate = 0.4,
DispersalType = "STATIC",
DispersalKernel = "PowerLaw",
DispersalMaxDist = 1000,
AlphaCoef = 3.55,
EDASpeciesParameters = eda_sp
))
)
ext_eda$write()
ext_eda$files
#> [1] "eda.txt" "eda/ramorum.txt"Other 1: Land Use Plus
LandUsePlus accepts a list of landUseType()
sub-objects, each containing one or more landCoverChange()
blocks. The three land-use types below are taken from the upstream Core8
test input (https://github.com/LANDIS-II-Foundation/Extension-Land-Use-Plus/blob/master/testing/Core8.0-LUC4.0/land-use-change-inputs.txt).
ext_lu <- LandUsePlus$new(
path = tmp_pth,
Timestep = 1L,
InputMaps = "landuse-{timestep}.tif",
SiteLog = "output/land-use/site-log.csv",
LandUses = list(
landUseType(
name = "forest",
mapCode = 4L,
allowHarvest = "no",
changes = landCoverChange(type = "NoChange")
),
landUseType(
name = "FIPSandHarvest",
mapCode = 3L,
allowHarvest = "yes",
changes = list(
landCoverChange(
type = "RemoveTrees",
repeatHarvest = TRUE,
cohorts = list(
cohortSelector(
species = "acerrubr",
ranges = data.frame(low = 1, high = 200, percent = 20)
),
cohortSelector(
species = "pinustro",
ranges = data.frame(low = 50, high = 300, percent = 30)
)
)
),
landCoverChange(
type = "InsectDefoliation",
cohorts = cohortSelector(
species = "querrubr",
ranges = data.frame(low = 1, high = 300, percent = 60)
)
)
)
),
landUseType(
name = "FIPS",
mapCode = 1L,
allowHarvest = "yes",
changes = landCoverChange(
type = "InsectDefoliation",
cohorts = list(
cohortSelector(
species = "querrubr",
ranges = data.frame(low = 1, high = 300, percent = 75)
),
cohortSelector(
species = "pinustro",
ranges = data.frame(low = c(1, 71), high = c(62, 200), percent = c(20, 25))
)
)
)
)
)
)
ext_lu$write()Other 2: Local Habitat Suitability output
Suitability files are passed as suitabilityFile()
sub-objects (or as a plain character vector that’s auto-wrapped). The
four files below mirror the upstream Core8 test input (https://github.com/LANDIS-II-Foundation/Extension-Local-Habitat-Suitability-Output/blob/master/test/Core8-LocalHabitat3.0/Habitat_input.txt).
ext_lh <- OutputLocalHabitat$new(
path = tmp_pth,
Timestep = 1L,
OutputTimestep = 10L,
MapFileNames = "output/habitat/{HabitatName}-{timestep}.tif",
SuitabilityFiles = c(
"AgeClass_ForestType_example.txt",
"AgeClass_TimeSinceFire_example.txt",
"ForestType_TimeSinceFire_example.txt",
"ForestType_TimeSinceHarvest_example.txt"
)
)
ext_lh$write()Assemble the scenario
scenario() partitions the supplied
extensions list by type (succession /
disturbance / other) and writes the top-level scenario .txt
referencing each extension’s primary input file.
climate_cfg <- LandisClimateConfig$new(path = tmp_pth)
climate_cfg$add_file(basename(clim_file))
scen <- scenario(
name = "multi_ext_demo",
extensions = list(ext_succ, ext_fire, ext_eda, ext_lu, ext_lh),
climate_config = climate_cfg,
path = tmp_pth,
CellLength = 100,
DisturbancesRandomOrder = FALSE,
Duration = 10,
EcoregionsFiles = ecoregion_files,
RandomNumberSeed = 147,
SpeciesInputFile = species_input_file
)
scen$path
#> /tmp/Rtmpi8YKpc/example_MultiExt_345a3855e7efInspect the generated scenario file
readLines(file.path(scen$path, "multi_ext_demo.txt")) |> cat(sep = "\n")
#> >> generated by `landisutils` (v0.0.30) on Thu Jun 4 22:35:11 2026
#> >> do not edit by hand; manual changes to this file may be overwritten
#>
#> LandisData "Scenario"
#>
#> Duration 10
#>
#> Species Core_species_data.txt
#>
#> Ecoregions ecoregions.txt
#> EcoregionsMap ecoregions.tif
#>
#> CellLength 100
#>
#> >> Succession Extension Initialization File
#> >> -------------------- -------------------
#> "Biomass Succession" biomass-succession.txt
#>
#> >> Disturbance Extensions Initialization File
#> >> ---------------------- -------------------
#> "Original Fire" original-fire.txt
#> "EDA" eda.txt
#> "Land Use Change" land-use.txt
#>
#> DisturbancesRandomOrder no
#>
#> >> Other Extensions Initialization File
#> >> ------------------------ -----------------------
#> "Local Habitat Output" output-local-habitat.txt
#>
#> RandomNumberSeed 147 << optional parameterCleanup
withr::deferred_run()
#> Ran 1/1 deferred expressions