
Run a LANDIS-II simulation inside a Docker container
landis_run_docker.RdRuns LANDIS-II in an ephemeral Docker container, blocking until the
simulation completes. The scenario directory is bind-mounted to /sim
inside the container. Stdout and stderr are written to
<scenario_dir>/log/docker_stdout.log and docker_stderr.log. Wall-clock
elapsed time and peak memory use (polled every 2 s from docker stats) are
reported on completion and written to
<scenario_dir>/log/docker_resources.log.
The image's immutable sha256 digest is captured into
<scenario_dir>/log/docker_image.log so downstream provenance tools can
pin runs to a specific image regardless of subsequent tag movement.
Usage
landis_run_docker(
scenario_dir,
scenario_file = "scenario.txt",
image = NULL,
console = NULL,
pull = FALSE,
cpu_limit = 2,
mem_limit = "8g",
mem_margin = 1.5,
post_completion_timeout_sec = 300,
startup_jitter = NULL,
stream_to = NULL,
stream_every_sec = 600,
stream_lag_steps = 2L,
stream_jitter_frac = 0.25,
stream_patterns = .default_stream_patterns()
)Arguments
- scenario_dir
Character. Path to the scenario directory (resolved to absolute before mounting).
- scenario_file
Character. Scenario filename relative to
scenario_dir.- image
Character or
NULL. Docker image reference. Defaults togetOption("landisutils.docker.image")(set by.onLoad()to"ghcr.io/landis-ii-foundation/landis-ii-v8-release:main").- console
Character or
NULL. Path toLandis.Console.dllinside the container. Defaults toNULL, which callslandis_find_docker()at run time (readsgetOption("landisutils.docker.console")).- pull
Logical. When
TRUE, rundocker pull <image>before the simulation so the recorded digest reflects the current registry rather than a stale local copy. Defaults toFALSEto keep runs reproducible across iterations of an already-cached image.- cpu_limit
Numeric or
NULL. Hard CPU cap for the container (docker run --cpus). Default2: LANDIS-II compute is effectively single-threaded – empirical measurement across 90 concurrent ForCS + Dynamic Fire + Dynamic Fuels containers shows a median of 1.00 cores used per container, p99 = 1.11 cores, max = 1.11 cores. The .NET runtime hosts 9-11 OS threads but only the simulator thread is compute-bound; the GC / threadpool helpers occasionally peek above 1.0 cores in brief bursts.2covers that 99th-percentile burst with ~80% headroom;1is tight enough that the .NET GC helper would contend with the simulator thread for cycles. PassNULLfor no CPU limit.- mem_limit
Numeric byte count, character (e.g.
"8g","512m"),NULL, orInf. Baseline RAM cap (docker run --memory). Default"8g". When a prior<rep_dir>/log/*_resources.logexists with a recordedpeak_mem_bytes, the cap is raised topeak_mem_bytes * mem_marginif that exceeds the baseline – a rep that fit last time will never be killed by this cap on a rerun. When no prior log exists for the rep (first run, or rep dir freshly deleted), the cap is dropped entirely so the first run can discover what it needs.- mem_margin
Numeric. Headroom factor applied to a previously observed peak when auto-raising
mem_limit. Default1.5.- post_completion_timeout_sec
Numeric. Grace period (seconds) after the string
"Model run is complete."first appears in the container's stdout before the watchdog SIGTERMs the container. Some long ForCS + Dynamic Fire scenarios with many output extensions log this completion marker but then spin in the .NET runtime shutdown path indefinitely (outputs are already on disk, so the sim itself completed cleanly). On timeout the container is stopped and exit codes 137/143 are remapped to0. Set toInfto disable the watchdog. Default300(5 min).- startup_jitter
Numeric seconds or
NULL. Upper bound on a random start delay applied before the function touches Docker, to stagger container launches and avoid a thundering-herd surge on the Docker daemon (and on the disk backing the image layers / renv library) when many replicates start at once undercrew. Each call sleepsrunif(1, 0, startup_jitter)seconds. The delay cannot affect simulation results (identical seed and inputs), so when run fromtar_landis()it is deliberately not baked into the{targets}command – changing it never invalidates completed replicates.NULL(the default) reads theLANDIS_STARTUP_JITTERenvironment variable (so it can be set once in a project.Rprofilethatcrewworkers inherit); an unset/invalid value means0(no stagger).- stream_to
Character or
NULL. When set, completed output maps are moved here while the simulation runs, so local scratch holds only the working set instead of the whole replicate. Nothing is discarded – files are relocated, not pruned. Point this at the<final>.partialstaging directory used bylandis_archive_rep()so the atomic-rename publish, and therefore the all-or-nothing appearance of the final directory, is preserved.NULL(default) disables streaming.- stream_every_sec
Numeric. Seconds between sync attempts (default
600). Actual intervals are jittered; seestream_jitter_frac.- stream_lag_steps
Integer. How many timesteps behind the simulation to stay (default
2). LANDIS-II writes several rasters per timestep, so a file for steptcan still be open when the log already reportsCurrent time: t; the lag is what makes "written" mean "closed".- stream_jitter_frac
Numeric in
[0, 1]. Fraction by which each interval is randomly stretched or shrunk (default0.25). Replicates launched together would otherwise sync in lockstep for the whole run, turning a steady trickle into periodic bursts against one shared filesystem.- stream_patterns
Character vector of regexes matching output maps that are safe to move mid-run. Defaults to write-once fire and fuels maps; deliberately excludes
TimeOfLastFire-*, which may be simulation state.
Value
Named list with exit_code (integer), elapsed_sec (numeric), and
peak_mem_bytes (numeric), returned invisibly.
Details
Single container per replicate. The container name is derived
deterministically from the (real) scenario directory, so docker's own name
uniqueness acts as a cross-worker mutex: if the same replicate is dispatched
to two workers at once (for example when targets re-runs a branch after a
false-positive worker crash while the original container is still running),
the second call cannot start a parallel container and instead adopts the
running one: it waits for that container to finish (applying the same
post-completion watchdog) and returns its result. A duplicate dispatch thus
becomes a harmless wait rather than two dotnet processes truncating each
other's outputs.
See also
landis_find_docker(), landis_find(), landis_run(), landis_run_local(), tar_landis()
Other LANDIS-II execution helpers:
host_cpu_info(),
landis_archive_rep(),
landis_assert_version(),
landis_find(),
landis_find_docker(),
landis_pool_exec(),
landis_pool_restart_one(),
landis_pool_start(),
landis_pool_stop(),
landis_replicate(),
landis_run_local(),
landis_target_version(),
landis_version(),
read_landis_resource_logs(),
tar_landis(),
validate_landis_scenario(),
write_landis_scenario_file()