lc run¶
Materialize outputs declared in astra.yaml. Generates a Snakefile
and dispatches through Snakemake on a Dask cluster.
Synopsis¶
OUTPUTS is zero or more output ids. With no arguments, materializes
everything (Snakemake's rule all).
Options¶
| Option | Default | Effect |
|---|---|---|
--universe, -u NAME |
all universes in universes/*.yaml (or ["default"] if none exist) |
Restrict to one universe. |
--jobs, -j N |
os.cpu_count() |
Parallel jobs / Dask submission concurrency. Passed as both --cores and --jobs to Snakemake. |
--rerun-triggers TRIGGERS |
code,input,mtime,params |
Comma-separated rerun triggers (forwarded to Snakemake). |
--force, -f |
off | --force when targets are named, --forceall otherwise. |
--verbose, -v |
off | Show the underlying Snakemake / executor chatter and the spawned snakemake invocation. |
What happens, step by step¶
- Find the project (walk up looking for
astra.yaml). - Discover universes from
universes/*.yaml(default to["default"]). - Resolve the container runtime via
lightcone.engine.container.load_runtime. Ifautofalls back tononewhile the spec declares containers, print a loud provenance warning. - Pre-flight the images. Every distinct
container:in the spec is built (Containerfile) or pulled (registry ref) before the DAG starts, so the first run after a Containerfile edit doesn't die mid-DAG on a missing image. This is the same passlc buildruns — already-present images are skipped. - Generate
.lightcone/Snakefileand.lightcone/snakefile-config.jsonfor the selected universes. - Translate any explicit
OUTPUTSinto Snakemake target paths (<output_dir>/.lightcone-manifest.json) — this is what tells Snakemake "build that specific output." -
Open a Dask cluster context. Four branches, checked in this order:
Condition Cluster DASK_SCHEDULER_ADDRESSsetAttach to that scheduler; never torn down. DASK_GATEWAY__ADDRESSsetCreate a run-scoped Dask Gateway cluster with the project's image, adaptive from 1 to --jobsworkers, culled on exit.SLURM_JOB_IDsetIn-process scheduler + one dask workerper allocated node viasrun.none of the above LocalClustersized to the machine. -
Spawn
snakemake -s … -d … --cores N --jobs N --executor dask --shared-fs-usage persistence input-output sources storage-local-copies source-cache --rerun-triggers …, with the cluster's connection info in the environment (DASK_SCHEDULER_ADDRESS, or the cluster name for the Gateway branch, which is rejoined through the authenticated Gateway API rather than dialled by address). - In the default (non-verbose) path, filter the executor's banner
chatter so the output reads as lightcone's, not Snakemake's. Real
error content always passes through; on failure the last of
snakemake's stderr is written to
snakemake-stderr-<pid>.logunder the scratch root.
--shared-fs-usage deliberately omits software-deployment. With it,
spawned job commands would embed the driver's sys.executable — a
path that doesn't exist inside a Dask Gateway worker image. Without it,
workers invoke plain python from their own environment, which is
correct on every backend.
Output qualification¶
When the same output_id appears in multiple sub-analyses, you must
qualify it as <analysis_id>.<output_id>:
How recipes get containerized¶
On docker, podman, and podman-hpc, each rule's body wraps the
recipe in a <runtime> run --rm --pull=never -v "$PWD":"$PWD" -w "$PWD"
<image> bash -c '<recipe>' shell.
On the kubernetes runtime this wrapping is a passthrough: the Dask
worker pod executing the recipe was already started from the project's
image, so wrapping would containerize twice. The image still flows into
code_version and into the manifest — and the pod reports back the
image it actually ran as the manifest's worker_image field. With
runtime: none, or a recipe with no container:, the recipe also runs
unwrapped.
After the recipe shell exits, the Snakefile calls write_manifest()
host-side and the validation snippet emits warnings for empty /
all-NaN / wrong-extension outputs.
Examples¶
lc run # all outputs, all universes
lc run --universe baseline # one universe
lc run accuracy # one output
lc run accuracy precision --universe baseline # several
lc run --jobs 4 --verbose # parallel, with stack noise
lc run --force --universe baseline # rebuild everything
lc run --rerun-triggers params,input # tighter staleness
Inside SLURM¶
lc run detects SLURM_JOB_ID, binds the Dask scheduler to the
driver's hostname, and launches one dask worker per node via srun.
Workers advertise cpus, memory, and gpus resources. Per-rule
resource hints (cpus_per_task, mem_mb, gpus_per_task) constrain
which workers can pick up which jobs.
Inside a JupyterHub deployment¶
On a lightcone JupyterHub (Kubernetes + Dask Gateway), lc run needs no
arguments beyond the usual — the deployment injects
DASK_GATEWAY__ADDRESS, LIGHTCONE_REGISTRY, and
LIGHTCONE_BUILD_BUCKET into your session and lc picks them up:
- The pre-flight image pass builds through GCP Cloud Build (there is
no local OCI runtime in your session) and pushes
<registry>/lc-<project>:<hash>. Unchanged files never rebuild — freshness is a single registry check. - A run-scoped Gateway cluster is created with that image, adaptive
from 1 to
--jobsworkers, and culled when the run finishes. A Gateway cluster's image is fixed at creation, so create-per-run is what makes image updates seamless. lc runwaits for the first worker before dispatching, bounded byLIGHTCONE_GATEWAY_WORKER_TIMEOUT(seconds, default600) — an unpullable image would otherwise hang at zero workers forever. It then asserts that workers advertise thecpus+memoryresource contract, since Dask would silently never schedule a rule otherwise.
One image per run on this backend
The worker pod is the container, so the whole run executes in a
single image. If astra.yaml resolves to more than one distinct
container image, lc run refuses to start and asks you to
consolidate on a single Containerfile (or one shared prebuilt
image). Every other backend wraps per rule and is unaffected.
Provenance gotcha¶
If ~/.lightcone/config.yaml says runtime: auto and no runtime is
on PATH, lc run falls back to running recipes on the host. Because
each manifest still records the declared container_image, this is a
provenance lie. lc run prints a yellow warning telling you to either
install a runtime or set container.runtime: none explicitly.
See api/dask_cluster for the cluster-shape decision and Architecture for the full execution flow.