Demos Package
The demos package is the supported public barrel for dataset acquisition,
download integrity, runtime flags, provenance, and viewer helpers used by
Luxar’s executable demos. The concern packages below luxar.demos._support
are implementation details and should not be imported directly by demos.
Luxar Demos - Self-contained demonstration scripts.
This package contains executable demo scripts that showcase Luxar’s capabilities. Each demo keeps the code that makes it distinctive in its own file, and reaches into this package for the shared plumbing helpers re-exported below.
- To run a demo:
hatch run python packages/luxar/src/luxar/demos/demo_lorenz.py hatch run python packages/luxar/src/luxar/demos/demo_cubic_array.py
See demos/README.md for more information on creating new demos.
- exception luxar.demos.BundleMemberNotFound[source]
Bases:
FileNotFoundErrorA requested frame is not inside an otherwise perfectly good bundle.
The bundle itself resolved, verified and opened; only the per-frame member names missed. That is a routable absence rather than a fault, because the names are DERIVED from the caller’s own parameters — NEXRAD’s per-frame cache names carry its
--dbz-floor/--splats/--grid-m, so any non-default value legitimately asks for members the shipped bundle cannot contain, and recomputing is the correct answer.It is the bundle-side counterpart of
DatasetUnavailable(#1618): both mean “the bytes are not obtainable”, so a demo may answer either with its own rebuild, while every otherFileNotFoundErroraround a fetch stays a fault that must propagate.
- exception luxar.demos.DatasetNotFound[source]
Bases:
KeyErrorThe dataset name is not present in the manifest.
Bases:
FileNotFoundErrorA hosted dataset’s bytes cannot be obtained from anywhere — yet.
The narrow, routable half of the
FileNotFoundErrorsurface: the manifest lists no files for the entry yet (pending upload), or none of the three sources holds a good copy and the record has no URL. Nothing is broken; the data simply is not there, so a demo may legitimately fall back to computing its own stand-in.Every OTHER
FileNotFoundErrorout of this module is a fault a demo must NOT route around — an unknown file name requested ofload_dataset_gsplats(), a dataset that lists no gsplat file at all, a missing packaged manifest (broken install), an in-repo copy that matches neither pinned digest with no hosted fallback. Those stay plainFileNotFoundError, soexcept DatasetUnavailablelets them through instead of disguising them as a routine multi-minute refit.Subclasses
FileNotFoundError, so a caller that does not care about the distinction keeps working.
- class luxar.demos.DependencySpec(spec: str, extra: str, note: str = '')[source]
Bases:
NamedTupleHow to install one optional dependency, and why it is bounded.
- spec: str
PEP 440 requirement to advertise, e.g.
metpy>=1.6.3,<2.0. MUST stay equivalent to the corresponding pin inpyproject.toml.
- class luxar.demos.DependencyStatus(module: str, spec: DependencySpec, installed: bool, satisfied: bool)[source]
Bases:
NamedTupleOne row of
survey(): a spec, whether it imports, and whether the installed version meets the spec’s bound.- spec: DependencySpec
Alias for field number 1
- exception luxar.demos.LocalComputeDataset(name: str, spec: dict[str, Any])[source]
Bases:
RuntimeErrorDataset is not hosted — the demo must fetch its raw source and build it.
Raised for
local-compute(not redistributable) andregenerate(cheap CPU rebuild) datasets. Carries the manifeststrategy/reasontext.
- exception luxar.demos.MissingDependencyError[source]
Bases:
ImportErrorAn optional demo dependency is not installed.
Subclasses
ImportErrorso existingexcept ImportErrorhandlers keep working, while callers that care can catch this specifically.
- class luxar.demos.ResolvedDataset(values: list[_T], input_digests: dict[str, str])[source]
-
List-compatible resolved values plus their verified input digests.
Derived list operations return plain lists and discard
input_digests.
- luxar.demos.add_demo_caption(scene: _TextScene, caption: str, citation: Mapping[str, str] | None) None[source]
Add the standard bottom-right demo caption, including attribution.
- luxar.demos.add_reference_cube_to_scene(scene: Any, grid_min: ndarray, grid_max: ndarray, *, name: str = 'Reference cube', widths: float = 0.006, opacity: float = 0.18, intensity: float = 0.35, color: tuple[float, float, float] = (0.55, 0.62, 0.8), sharpness: float = 0.8, blending_mode: str = 'additive', layer: bool = True, visible: bool = False) None[source]
Add a 12-edge wire cube as Lines geometry to a Luxar scene.
Used by flow-field demos to visualize the cubic field domain. Defaults are tuned for a faint, off-by-default reference. Override kwargs to match your scene’s intensity/exposure scaling.
- luxar.demos.cache_computed(name: str, key: str, compute_fn: Callable[[], Any], *, version: int = 1, recompute: bool = False, verbose: bool = True, cache_dir: Path | None = None) Any[source]
Cache the result of
compute_fn()under~/.cache/luxar/<name>/.For expensive deterministic results (UMAP embeddings, fitted vector fields). The on-disk file is keyed by
<key>_v<version>— bumpversion(or fold the inputs/params intokey) whenever the computation’s inputs change, so a stale cache is never silently reused. A truncated/corrupt cache file is quarantined (.corrupt) and recomputed rather than crashing the demo.- Parameters:
name – Cache namespace (the demo name).
key – Stable identifier for this result (include the sample size / params that affect the output, e.g.
f"umap3d_n{n}_feat{feat_hash}").compute_fn – Zero-arg callable producing the (picklable) result.
version – Schema/logic version; bump to invalidate all prior caches.
recompute – If True, ignore any cached file and recompute.
cache_dir – Explicit cache directory, used verbatim instead of
~/.cache/luxar/<name>/;nameis then unused. Without it, the location is derived from the cache NAMESPACE, which a demo’s--cache-dirflag cannot reach — the same limitationcached_download()has, since it too derives~/.cache/luxar/<name>/<filename>from the namespace.demo_caida_as_topologytakes such a flag and must put its derived bundles beside the raw downloads they came from, wherever the user pointed it.
- Returns:
The cached or freshly computed result.
- luxar.demos.cached_download(url: str, name: str, filename: str | None = None, *, sha256: str | None = None, expected_size: int | None = None, verbose: bool = True) Path[source]
Download
urlonce into~/.cache/luxar/<name>/<filename>.Reuses
luxar.demos.robust_download()/download_with_checksum()(retry, resume, checksum), but adds the skip-if-already-present behaviour a cache needs: a complete cached file is returned without touching the network.- Parameters:
url – Source URL.
name – Cache namespace (the demo name), e.g.
"earthquakes".filename – Destination basename; inferred from the URL when omitted.
sha256 – Optional expected SHA-256 (verified on download; a matching cached file is trusted without re-download).
expected_size – Optional expected size in bytes (skip-if-matches).
- Returns:
Path to the cached file.
- luxar.demos.create_lorenz_attractor(store_path: str | Path, n_points: int = 10000, seed: int | None = None) None[source]
Create a demo scene with a Lorenz attractor visualization.
This creates a beautiful butterfly-shaped 3D structure with colors that transition smoothly over time, demonstrating the Luxar scene format with an aesthetically pleasing mathematical visualization.
- Parameters:
store_path – Path to the Zarr store to create
n_points – Number of points to generate along the attractor
seed – Random seed for reproducible results
Example
>>> from luxar.utils import create_lorenz_attractor >>> create_lorenz_attractor('demo.zarr', n_points=50000)
- luxar.demos.create_random_spheres(store_path: str | Path, n_spheres: int = 100, points_per_sphere: int = 1000, seed: int | None = None) None[source]
Create a demo scene with random colored spheres.
- Parameters:
store_path – Path to the Zarr store to create
n_spheres – Number of spheres to generate
points_per_sphere – Points per sphere
seed – Random seed for reproducible results
- luxar.demos.create_time_series_demo(store_path: str | Path, n_timepoints: int = 10, n_points_per_time: int = 1000, seed: int | None = None) None[source]
Create a 4D time series demo scene.
- Parameters:
store_path – Path to the Zarr store to create
n_timepoints – Number of time points
n_points_per_time – Points per time step
seed – Random seed
- luxar.demos.cubic_bounds(coords: ndarray, pad_fraction: float = 0.06) tuple[ndarray, ndarray][source]
Return a symmetric cubic AABB around
coords.The cube is centered on the input cloud and sized so the longest axis-aligned side fits, plus a fractional padding. The minimum half-side is clamped to 1.0 to avoid degenerate domains for tiny point clouds.
- Parameters:
coords –
(N, 3)array of world-space points.pad_fraction – Extra padding as a fraction of the longest side.
- Returns:
(grid_min, grid_max)— both shape(3,)float32.
- luxar.demos.dataset_spec(name: str, manifest: dict[str, Any] | None = None) dict[str, Any][source]
Return the manifest entry for name (raises
DatasetNotFound).
- luxar.demos.declared_file_names(name: str, *, variant: str | None = None, manifest: dict[str, Any] | None = None) set[str][source]
Return the file names declared by the resolved dataset variant.
- luxar.demos.demo_source_fingerprint(module_file: str | Path, *, package_root: Path | None = None) str[source]
Short content hash of a demo and the local code that writes it.
Call as
demo_source_fingerprint(__file__). The hash covers that demo module’s reachable imports inside the Luxar package tree and the Zarr writer environment, so edits to its producers or encoding-environment changes invalidate the cached scene without unrelated modules doing so.- Parameters:
module_file – Path to the demo module (normally
__file__).package_root – Luxar package root. The installed package is used by default.
- Returns:
16 hex characters, or
""if any source cannot be read (in which casescene_is_current()degrades to a plain existence check rather than rebuilding a large scene on every run).
- luxar.demos.detect_device(verbose: bool = True) str[source]
Auto-detect the best available compute device (cuda > mps > cpu).
- Parameters:
verbose – If True, print detected device via arbol.
- Returns:
‘cuda’, ‘mps’, or ‘cpu’.
- Return type:
Device string
- luxar.demos.download_with_checksum(url: str, output_path: Path, expected_md5: str | None = None, expected_sha256: str | None = None, **kwargs: Any) Path[source]
Download file and verify checksum.
Combines robust_download with checksum verification for data integrity.
- Parameters:
url – URL to download from
output_path – Where to save the file
expected_md5 – Expected MD5 hash (optional)
expected_sha256 – Expected SHA256 hash (optional)
**kwargs – Additional arguments passed to robust_download
- Returns:
Path to downloaded and verified file
- Raises:
ValueError – If checksum verification fails
Example
>>> download_with_checksum( ... "https://example.com/data.zip", ... Path("data.zip"), ... expected_sha256="abc123...", ... max_retries=5 ... )
- luxar.demos.download_zip_member(url: str, member: str, output_path: Path, *, expected_size: int | None = None, max_retries: int = 3, timeout: int = 300, chunk_size: int = 1048576, extra_headers: dict | None = None, max_uncompressed_size: int | None = 274877906944) Path[source]
Extract ONE member from a remote zip via HTTP Range requests.
Downloads only the bytes of the requested member (plus a few KB of zip bookkeeping) instead of the whole archive — e.g. a 1.5 GB scene out of INRIA’s 14.7 GB
models.zip. Requires the server to honorRangerequests (Accept-Ranges: bytes); redirects (e.g. Hugging Face/resolve/→ signed CDN) are followed.Flow: ranged GET of the archive tail → EOCD (+ mandatory Zip64 records for >4 GiB archives) → ranged GET of the central directory → locate the member → ranged streaming GET of its compressed bytes → inflate (
zlibraw DEFLATE) or raw copy (STORED) to disk → CRC32 + size verification against the central directory.- Parameters:
url – Archive URL.
member – Exact member path inside the zip (POSIX separators).
output_path – Where to write the extracted (decompressed) member.
expected_size – Optional expected uncompressed size — used both to skip an already-complete download and to sanity-check the zip.
max_retries – Retry attempts for the member body download.
timeout – Per-request timeout (seconds).
chunk_size – Streaming chunk size (bytes).
extra_headers – Extra HTTP headers scoped to the original URL.
max_uncompressed_size – Absolute ceiling (bytes) on the member’s uncompressed size, independent of the archive’s own (attacker-controlled) metadata. Defaults to 256 GiB (
_MAX_MEMBER_UNCOMPRESSED_BYTES). A member whose central-directory uncompressed size exceeds it is rejected before any streaming; the inflate itself is then bounded by that (already-within-ceiling) declared size — so a self-consistent decompression bomb (one whose declared size, actual inflated size, and CRC all agree) still cannot write unbounded to disk. Pass a smaller int to tighten it, orNoneto disable the ceiling entirely (the output is then bounded only by the declared member size). The INRIA / cluster-fly demo callers pass a tightexpected_sizeand are unaffected by this default.
- Returns:
Path to the extracted member.
- luxar.demos.ensure_dataset(name: str, *, variant: str | None = None, file_names: Collection[str] | None = None, recompute: bool = False, cache_root: Path | None = None, manifest: dict[str, Any] | None = None, verbose: bool = True) ResolvedDataset[Path][source]
Ensure a
zenododataset’s files are present locally; return their paths.- Parameters:
name – Dataset key in the manifest (e.g.
"gsplats_kidney").variant – For datasets with size variants (e.g. h2afva), which to fetch; defaults to the variant flagged
default(the lighter one). An error for a dataset without variants, or an unknown variant name.file_names – Optional exact set of manifest file names to resolve. The result remains in manifest order. Unknown names and selections that split a declared
positional_pairare rejected. An empty selection returns an empty list without creating a cache directory. A non-empty selection against a dataset with no declared files is rejected as unknown; querydeclared_file_names()first when that absence should remain routable asDatasetUnavailable.recompute – If True, raise
LocalComputeDatasetfor any dataset so the caller takes its own build path (mirrors the demos’--recompute).cache_root – Override the cache root (tests). Defaults to
~/.cache/luxar.manifest – Pre-loaded manifest (tests); defaults to the packaged one. A dataset-level
base_urloverrides its provenance record’s download URL without changing that shared record.
- Returns:
Cache paths in manifest order, with a canonical
input_digestsmap.- Raises:
DatasetNotFound – unknown dataset.
LocalComputeDataset – dataset is local-compute/regenerate (or recompute=True).
ValueError – unsupported bucket or variant, an unknown selected file name, or a selection that splits a declared
positional_pair.DatasetUnavailable – data is neither cached, in-repo, nor hosted yet, or a declared
positional_paircannot resolve every member to the same generation — the conditions a caller may route around by building its own copy.FileNotFoundError – a fault, not an absence — a missing packaged manifest, or an in-repo copy that matches neither pinned digest with no hosted fallback.
DatasetUnavailablesubclasses this, so catch the subclass when you mean “not there yet”.
- luxar.demos.extras_for(rows: list[DependencyStatus]) list[str][source]
The distinct Luxar extras that would install
rows, sorted.Specs outside every extra (
extra == "") contribute nothing — they cannot be installed vialuxar[...]and must be named individually.
- luxar.demos.find_quarantined_files(target: str | Path) list[Path][source]
Return the quarantined
.corruptfiles associated with target.- Parameters:
target – A cache file (both the appended form
foo.npy.corruptthatquarantine_file()writes and the barePath.with_suffixformfoo.corruptare checked, so older hand-rolled quarantines stay discoverable) or a cache directory (every*.corruptinside it is reported).- Returns:
Existing quarantined paths, sorted and de-duplicated (empty when clean).
- luxar.demos.format_demo_caption(caption: str, citation: Mapping[str, str] | None) str[source]
Append the compact reference using
•as the caption field separator.
- luxar.demos.format_quarantine_notice(paths: list[Path], *, indent: str = ' ', action: str = 're-download the full file (or delete the quarantined copy)') str[source]
Build an actionable multi-line notice for quarantined cache files.
Returns an empty string when paths is empty, so callers can splice the result straight into a larger message.
- luxar.demos.hsv_to_rgb(h: ndarray, s: Any = 1.0, v: Any = 1.0) ndarray[source]
Vectorized HSV→RGB for arrays of hues (all in [0, 1]).
A single shared implementation for the rainbow/hue-ramp colouring several demos each re-derived by hand.
his an array (or scalar);s/vmay be scalars or broadcastable arrays.- Returns:
(..., 3)float32 RGB in [0, 1] with the same leading shape ash.
- luxar.demos.is_installed(module: str) bool[source]
Whether
modulecan be imported, WITHOUT importing it.Uses
importlib.util.find_spec(), because actually importing the table would be ruinous:torchalone costs seconds and allocates CUDA context, andesm/sentence_transformerspull model machinery. A survey must be instant and side-effect free.
- luxar.demos.is_lfs_pointer(path: Path) bool[source]
Check whether path is an unpulled Git LFS pointer file.
LFS pointers are small text files (< 1 KB) whose first line is
version https://git-lfs.github.com/spec/v1.
- luxar.demos.control_serve_args(argv: list[str] | None = None) list[str][source]
luxar servearguments for a demo’s opt-in remote-control panel.Returns
[]unless--controlis present, so remote control is OFF for every demo by default and no demo gains a listening socket a user did not ask for. Forward the result throughlaunch_viewer(serve_args=...).Generic on purpose — it takes no demo-specific argument and lives beside the other shared flag parsers, so any demo opts in with one call rather than each re-deriving the argv scanning and the LAN advice.
Recognised flags:
--controlExpose the hub and the touch panel.
--control-token TOKENRequire
TOKENon every control socket. Strongly advised with--host, which is the only configuration reachable from another machine.--host HOSTBind address. Defaults to loopback, which no tablet can reach; pass
0.0.0.0for a kiosk on a LAN.
- luxar.demos.bake_scene_environment(store: str | Path) bool[source]
Capture and attach the environment for
store. Returns whether it ran.Self-gating: returns False without complaint for a scene that declares no environment, one whose source is not a live capture, or one that already carries a baked map. Uses the scene’s OWN probe and resolution, so the baked map is the capture the viewer would have made rather than a different look.
- luxar.demos.launch_viewer(output_path: str | Path, open_browser: bool = True, serve_args: list[str] | None = None) None[source]
Launch the Luxar viewer to display a dataset.
This function uses sys.executable to ensure it works regardless of how the demo script was launched (hatch run, hatch shell, conda, etc.).
Each dataset serves on its own stable derived port pair (see
demo_ports()) rather than everyone contending for 8000/5173.- Parameters:
output_path – Path to the .zarr dataset to view
open_browser – Whether to automatically open a browser window
serve_args – Extra arguments forwarded verbatim to
luxar serve— e.g.["--profile", "3g"]for the network-simulation demo. These let a demo drive server-side behaviour (bandwidth throttling, latency, packet loss) thatservealready supports. An explicit--port/--viewer-porthere overrides the derived pair.
- Raises:
SystemExit – If the viewer fails to launch
- luxar.demos.load_dataset_bundle(name: str, bundle_name: str, file_names: list[str], *, recompute: bool = False, cache_root: Path | None = None, manifest: dict | None = None, verbose: bool = True) list | None[source]
Manifest-driven counterpart of
load_precomputed_bundle().Same contract – a list of
GSplatDatain the requested order, orNonewhen the caller must build the data itself – but the OUTER bundle is resolved throughluxar.demos.ensure_dataset(), so it is checksum- verified against the manifest (cache -> in-repo git-LFS -> Zenodo) instead of copied unverified out of the working tree.Bundle members are deliberately NOT pinned individually: the manifest addresses the bundle, which is the unit that is downloaded, and verifying it covers everything inside. Extraction then reuses the same safe-member and staleness logic as the in-repo path, so a re-migrated bundle still refreshes its extracted frames rather than pinning the first extraction — and here the staleness key is the exact digest
ensure_dataset()verified rather than the in-repo path’s(size, mtime)guess.- Parameters:
name – Manifest dataset key (e.g.
"gsplats_zebrafish").bundle_name – Basename of the outer bundle zip, a manifest file entry.
file_names – Basenames of per-frame files inside the bundle, in order.
recompute – Return
Noneimmediately (mirrors--recompute).cache_root – Override the cache root (tests).
manifest – Pre-loaded manifest (tests).
verbose – Print progress.
- Returns:
list[GSplatData], orNonewhen the caller should build the data.
- luxar.demos.load_dataset_gsplats(name: str, file_names: list[str] | None = None, *, variant: str | None = None, recompute: bool = False, cache_root: Path | None = None, manifest: dict[str, Any] | None = None, verbose: bool = True) ResolvedDataset[Any] | None[source]
Manifest-driven stand-in for
luxar.demos.load_precomputed_gsplats().Same contract as the helper it is meant to replace — a list of
GSplatDatain the requested order, orNonewhen the caller must build the data itself — but the files are resolved throughensure_dataset()(checksum-verified cache → in-repo git-LFS → Zenodo) instead of an unverifiedshutil.copy2. Migrating a demo is then a one-line swap.Noneis returned when:recomputeis True (mirrors the demos’--recomputeflag), orthe dataset is not hosted (
local-compute/regenerate): the manifest’s reason/strategy is printed and the caller’s existing “fit from scratch” branch takes over.
DatasetUnavailableis the one routable absence: the data is neither cached, in-repo, nor hosted yet, so the caller may build its own copy. Everything else raises — an unknown dataset, an unknown variant, or a checksum that will not verify. Those are faults a demo must not silently route around.- Parameters:
name – Manifest dataset key (e.g.
"gsplats_kidney").file_names – Explicit basenames, in load order. Must all be manifest entries of this dataset. Defaults to every
*.gsplats.zarr[.zip]file, in manifest order.variant – Size variant; see
ensure_dataset().recompute – Return
Noneimmediately.cache_root – Override the cache root (tests).
manifest – Pre-loaded manifest (tests).
verbose – Print progress.
- Returns:
Loaded GSplat data with a canonical
input_digestsmap for the selected files, orNonewhen the caller should build the data. The result behaves as a list, but list operations return plain lists and do not preserve the map.
- Deliberately NOT supported:
Bundle datasets.
gsplats_celegansships one outer zip holding many per-frame files; the manifest addresses the bundle, not its members. That demo stays onload_precomputed_bundle(). (gsplats_zebrafishwas one until it moved to a single stacked 4D archive, which this function serves.)Runtime-computed file lists that are not manifest entries. A subset of the manifest’s own files is fine; anything else raises rather than fetching the wrong data.
Non-gsplat payloads. Use
ensure_dataset(), which returns paths and assumes nothing about the format.Multi-part artifacts. A
kind=partitionstore — whatluxar.demos._lod_policy.save_with_lod()writes for theadaptiverecipe, and whatgsplat lod --recipe tiles|overview|adaptivewrites — has no flatGSplatDataform and is meant to be GRAFTED whole. Fetch the paths withensure_dataset()and hand them toadd_gsplats_from_file(). This function says so rather than letting the shape error surface bare.Datasets whose bucket is not ``zenodo``.
gsplats_tribolium,gsplats_acto3d_heart,gsplats_tng_cosmic_webandmilky_way_gaia_3mare markedlocal-computeand no longer ship files in-repo, so this returnsNonefor them and the demo takes its own build path: a GPU refit for the three gsplat ones, and for Gaia an opt-in--build-catalogquery of the ESA archive (or a copy the user already placed in the cache). Migrate a demo only once its dataset iszenodo.
- luxar.demos.load_local_fit_gsplats(name: str, file_names: list[str], *, variant: str | None = None, cache_root: Path | None = None, verbose: bool = True) list[Any] | None[source]
Load a previous run’s own local fit, or
Noneif the caller must build it.Same return contract as
load_dataset_gsplats()— a list ofGSplatDatain the requested order, orNonemeaning “build it yourself” — but it reads thelocal_fit_path()namespace instead of the manifest. A demo consults it AFTER the manifest fetch comes up empty and BEFORE it refits, which is what makes the “one-time” refit actually one-time.Noneis returned when any requested file is missing (a partial set is not a usable answer: the caller refits, and the fit rewrites all of them), and also when one of them fails to load.A broken local file does NOT raise. Unlike the manifest cache, these bytes have no checksum, no remote to re-fetch from and no second copy — the only recovery is the refit the caller is already able to do, so raising would strand a demo on rubble it can heal itself. It is reported loudly (⚠️, with the path and the error) rather than silently: a fit that keeps re-running is the bug this whole namespace exists to fix, so a machine that has quietly started refitting every launch must be able to see why. The bad file is left in place for inspection; the refit overwrites it.
The ONE exception is a store that is structurally unloadable — a
kind=partition/ non-leaf lod tree, which has no flatGSplatDataform at all. Seeload_local_fit_gsplats_at().An empty
file_namesraises:[]is neither a loaded set nor “rebuild it”, and returning it would break theif fits is not None: fits[0]shape every caller uses.
- luxar.demos.load_local_fit_gsplats_at(paths: list[Path], *, label: str = 'local fit', verbose: bool = True) list[Any] | None[source]
load_local_fit_gsplats()for paths the caller already holds.The name-based form re-derives its paths from the cache root, which is the right default but is NOT the same object as a demo’s module-level
LOCAL_FITconstant. A demo that publishes such a constant — and writes its refit through it — must READ through it too, or the two halves of its local door can be pointed at different files (a redirected constant that the read side silently ignores; #1618 review finding A). Those demos call this.- Parameters:
paths – The artifacts to load, in the order they should be returned.
label – What to call this set in log output (usually the dataset name).
verbose – Print progress.
- Returns:
list[GSplatData], orNonewhen the caller should rebuild.- Raises:
ValueError – paths is empty, or the store is multi-part (
kind=partitionor a lod group with non-leaf children) and therefore has no flatGSplatDataform. The latter is deliberately NOT swallowed into a rebuild: the rebuild would write the same unloadable shape, so the caller would refit on every launch — exactly the #1618 symptom this namespace exists to end. It is a recipe/loader mismatch in the demo, and only a code change fixes it.load_dataset_gsplats()translates the same error.
- luxar.demos.load_manifest(path: str | None = None) dict[str, Any][source]
Load the demo-data manifest (JSON).
Parsing is cached (the most recently requested path), but each call returns an independent deep copy: the shared cached dict must never be handed out directly, or a caller that mutates the result (or a nested
dataset_spec) would poison every later read process-wide.
- luxar.demos.load_precomputed_bundle(demo_name: str, bundle_name: str, file_names: list[str], *, recompute: bool = False) list | None[source]
Load precomputed GSplat data from a bundled zip archive in Git LFS.
For timelapse demos where many per-frame
.gsplats.zarr.zipfiles are bundled into a single outer.zipstored via Git LFS.- Parameters:
demo_name – Subdirectory name under
demos/data/(e.g."zebrafish").bundle_name – Filename of the outer bundle zip (e.g.
"zebrafish.gsplats.zarr.zip").file_names – Basenames of per-frame files inside the bundle to load, in the desired order.
recompute – If True, return None.
- Returns:
List of
GSplatData, orNonewhen the caller should recompute.
- luxar.demos.load_precomputed_gsplats(demo_name: str, file_names: list[str], *, recompute: bool = False) list | None[source]
Load precomputed GSplat data from Git LFS / local cache.
- Resolution order (per file):
If
recomputeis True, returnNoneimmediately.Check the local cache
~/.cache/luxar/<demo_name>/.Copy from
demos/data/<demo_name>/(LFS) to local cache.Load from local cache.
- Parameters:
demo_name – Subdirectory name under
demos/data/(e.g."tribolium").file_names – File basenames to load (e.g.
["tribolium.gsplats.zarr.zip"]).recompute – If True, skip precomputed data entirely and return None.
- Returns:
List of
GSplatDatain the same order as file_names, orNonewhen the caller should recompute.
- luxar.demos.local_fit_path(name: str, filename: str, *, variant: str | None = None, cache_root: Path | None = None) Path[source]
Where a demo’s OWN locally computed stand-in for a hosted file belongs.
Returns
<cache_root>/<name>/local/<filename>(plus the variant subdir when one is given, mirroringensure_dataset()’s layout).The split exists because
<name>/<filename>— with nolocal/in it — is the pathensure_dataset()resolves for that manifest entry, and step 1 of_ensure_one()treats whatever it finds there as a candidate copy of the manifest file: it hashes it against both pinned digests and QUARANTINES it when neither matches. A local fit is a different artifact that happens to answer the same need, so it can never match either digest. Storing one under the manifest name therefore guarantees it is destroyed by the next fetch, and the demo refits from scratch on every single launch (#1618/#1672).Nothing under
local/is ever hashed, quarantined or overwritten by the fetch — the cache dir is shared, the two namespaces are not.- Parameters:
name – Manifest dataset key, i.e. the cache-dir name (
"gsplats_dapi").filename – Basename of the computed artifact. Deliberately allowed to be the manifest’s own file name: reusing it documents what the local artifact stands in for, and is now safe.
variant – Size variant, for a dataset that has them; see
ensure_dataset().None(every dataset that needs this today) puts the file directly under<name>/local/.cache_root – Override the cache root (tests). Defaults to
~/.cache/luxar.
- Raises:
ValueError – if variant is
"local", which is the one name that would put a manifest destination and this namespace back on top of each other.test_no_shipped_variant_is_named_localholds the shipped manifest to it too, so the check can only fire on a hand rolled call.
- luxar.demos.parse_demo_flags() dict[source]
Parse common GSplat demo command-line flags from
sys.argv.Returns a dict with keys:
recompute,no_serve,serve_only,keep_stale.
- luxar.demos.parse_int_arg(name: str, default: int, argv: list[str] | None = None) int[source]
- luxar.demos.parse_int_arg(name: str, default: None, argv: list[str] | None = None) int | None
Parse an integer
--name=VALUEor--name VALUEflag from argv.A tiny shared replacement for the ad-hoc
sys.argvscanning every demo re-implements (--points,--sample,--grid,--frames,--resolution, …). Accepts both--name=8000and--name 8000. Returnsdefaultwhen the flag is absent. The first occurrence is decisive, even if malformed: a malformed/unparseable value (e.g.--points=abc) warns and returnsdefaultrather than raising — the shared helpers never abort a run over a mistyped flag.
- luxar.demos.parse_path_arg(name: str, argv: list[str] | None = None) Path | None[source]
Parse a path
--name=PATHor--name PATHflag from argv.Sibling of
parse_int_arg()for path-valued flags (--cache-dir,--data). Expands a leading~. ReturnsNonewhen the flag is absent. An empty value (--data=) is not treated as a hit — the scan skips it and keeps looking, so a lone--data=reads as absent instead of resolving to the current directory (and a later non-empty occurrence wins).In the space form the next token must not itself look like an option:
--data --no-tspis a missing value, not a path named--no-tsp, so it warns and keeps scanning rather than handing the demo a bogus file to open. The=form stays literal (--data=--oddreally does mean that path).
- luxar.demos.parse_str_arg(name: str, argv: list[str] | None = None) str | None[source]
Parse a string
--name=VALUEor--name VALUEflag from argv.Sibling of
parse_int_arg()for free-text values (a shared secret, a host). Same conventions: first non-empty occurrence wins, and in the space form the next token must not look like an option, so--control-token --hostreads as a missing value rather than a token spelled--host.
- luxar.demos.print_data_provenance(*, title: str, source: str, license: str, url: str, note: str = '') None[source]
Print a dataset provenance/licence notice before a runtime download.
Demos that fetch third-party data at runtime print this first, so the user sees the source and licence terms of what is about to be downloaded (Luxar itself redistributes none of it). See the DATA SOURCE & CITATION docstring block each demo also carries.
- luxar.demos.quarantine_file(path: str | Path, *, reason: str = '', verbose: bool = True) Path[source]
Rename a rejected cache artifact out of the way; return its new path.
The producer side of the
.corruptconvention thatfind_quarantined_files()andwarn_if_quarantined()already read.Quarantining rather than deleting keeps the bytes for inspection or salvage and, critically, gets them out from under the canonical name so that
robust_download()cannot mistake them for a valid cache — a file at the canonicaloutput_pathis treated as COMPLETE (in-progress bytes stage in a sibling.partfile), so a complete-but-wrong file left in place would be trusted and returned rather than re-fetched.The suffix is APPENDED (
foo.zip->foo.zip.corrupt) so the original name and extension survive intact; that is also the formluxar demo cache clearclassifies correctly. A pre-existing quarantine for the same file is REPLACED, not stacked: one slot per file, so a repeated corrupt-fetch loop cannot fill a disk with copies of a multi-gigabyte artifact, and the finders (which match the exact.corruptname) keep working.- Parameters:
path – The rejected artifact. Must be an existing regular file.
reason – Short cause (“sha256 mismatch”, “truncated”), shown in the notice.
verbose – Print the notice. The rename happens either way.
- Returns:
Path of the quarantined file.
- Raises:
FileNotFoundError – path is missing or is not a regular file.
- luxar.demos.require_local_data(path: str | Path, hint: str | None = None) Path[source]
Return
pathif it is real local data, else raise a helpful error.Guards the local-data demos (LFS-tracked parquet/npz) so an unpulled Git LFS pointer raises the clear “run git lfs pull” message instead of a cryptic downstream parse error. Wraps
_validate_lfs_files().
- luxar.demos.require_module(module: str, *, pip_name: str | None = None) Any[source]
Import an optional dependency, or raise with an actionable install hint.
Call this AT THE POINT OF USE — immediately before the work that needs the module — so a demo running off a warm cache never demands it. See the module docstring for why that ordering is a rule rather than a preference.
- Parameters:
module – Name to import (
umap, notumap-learn).pip_name – Override the advertised requirement. Defaults to the constrained spec from
INSTALL_SPECS, else the module name.
- Returns:
The imported module.
- Raises:
MissingDependencyError – If the import fails. The message names the constrained requirement, the Luxar extra that provides it (when there is one), and any explanation attached to the spec.
- luxar.demos.rk4_step(points: ndarray, step_size: float, field: FlowField) ndarray[source]
Vectorized 4-stage Runge-Kutta step for
dx/ds = unit_flow(x).Returns
(N, 3)advected points; rows that fall out of bounds at any RK stage are NaN-filled so the caller can stop integrating those streamlines.
- luxar.demos.robust_download(url: str, output_path: Path, max_retries: int = 3, timeout: int = 300, chunk_size: int = 1048576, verify_size: bool = True, expected_size: int | None = None, extra_headers: dict | None = None) Path[source]
Download a file with automatic retry, resume capability, and progress tracking.
Features:
Automatic retry on network errors (exponential backoff)
Resume partial downloads (HTTP Range requests), validated with
If-Rangeagainst the recorded ETag/Last-Modified of the staged bytes so a remote that changed is re-fetched clean, never splicedProgress tracking with ETA
File size verification
Atomic staging: bytes land in a sibling
<dest>.partand are renamed onto the destination only once complete and size-verified, so an interrupted download leaves a resumable.partrather than a truncated file under the canonical name (and a pre-existing cache is never deleted on error)A 416 (Range Not Satisfiable) while resuming at/after EOF is non-fatal: the cache is proven at-least-complete, and is returned untouched unless the 416’s authoritative total contradicts its size (one clean restart)
When the destination already exists — and a matching
expected_sizehasn’t already short-circuited the call — one or more size-probe requests (a HEAD, and possibly an unranged GET) are issued up front to decide whether to resume, restart, or return the cache as-is.- Parameters:
url – URL to download from
output_path – Where to save the downloaded file
max_retries – Maximum number of retry attempts (default: 3)
timeout – Timeout in seconds for initial connection (default: 300)
chunk_size – Size of download chunks in bytes (default: 1MB)
verify_size – Whether to verify final file size matches Content-Length
expected_size – Expected file size in bytes (optional, for validation)
extra_headers – Extra HTTP headers scoped to the original URL (e.g. an API key:
{"api-key": "..."}). Merged with the Range header. Unless it already contains anAccept-Encoding(any casing), requests default toAccept-Encoding: identityso size verification and.partresume see the raw byte stream.
- Returns:
Path to downloaded file
- Raises:
requests.HTTPError – If HTTP error occurs after all retries
requests.ConnectionError – If connection fails after all retries
ValueError – If downloaded file size doesn’t match expected size
Example
>>> from luxar.demos import robust_download >>> path = robust_download( ... "https://example.com/large_dataset.zip", ... Path("data/dataset.zip"), ... max_retries=5 ... ) >>> aprint(f"Downloaded to {path}")
- luxar.demos.run_luxar_cli(*args: str) None[source]
Run the Luxar CLI in-process and translate a failing exit.
- luxar.demos.scene_is_current(output_path: Path, fingerprint: str, *, recompute: bool = False, keep_stale: bool = False) bool[source]
True if the scene at
output_pathcan be reused as-is.Demos cache their built scene and, historically, reused it whenever the path merely EXISTED. That let a scene built by an older version of the demo be served forever: #1957 was reported against an ocean-currents scene whose missing streamlines had been fixed three weeks earlier, because the fix never rebuilt the stale store on disk.
So a scene is current only when its save finished AND it was written by this exact builder dependency closure and Zarr writer environment. A scene from before fingerprinting carries no attr and is treated as stale — one rebuild, then it stamps itself.
This gates SCENE ASSEMBLY only. Downloads, gsplat fits and precomputed bundles keep their own caches under
~/.cache/luxar, so a source edit costs a scene rebuild and never a re-download or a re-fit.- Parameters:
output_path – The
.luxar.zarrthe demo would write.fingerprint – This build’s
demo_source_fingerprint().recompute – The demo’s
--recomputeflag; forces a rebuild.keep_stale – The demo’s
--keep-staleflag; reuse whatever is on disk even when the builder changed. An escape hatch for an expensive scene the caller knows is good enough.
- Returns:
True to reuse the existing scene, False to rebuild.
- luxar.demos.stack_colorings(coords: ndarray, colorings: list[dict], keys: Sequence[str] | None = None) StackedColorings[source]
Replicate a point cloud once per coloring scheme along a categorical axis.
This is the render-proven pattern used by the census / peak-UMAP demos to give a single embedding several switchable color views: the N points are stacked K times (one block per coloring) and a leading
coloringindex column is prepended so a categoricalcoloringdimension can select which block (which colour scheme) is shown. Concentrating the stacking here keeps the per-point alignment of positions / colours / hover-labels correct and tested in one place instead of hand-rolled in each demo.- Parameters:
coords –
(N, D)spatial coordinates (Dis usually 3).colorings –
ordered list of dicts, one per color scheme. Each dict has:
"label": category name shown on thecoloringdimension."colors":(N, 3)float RGB for this scheme."labels"(optional):(N,)per-point hover strings for this scheme. If ANY coloring omits labels, the combinedlabelsisNone(hover disabled) rather than misaligned.
keys – optional
(N,)machine-readable per-point strings for link / copy templates to substitute as{hover_key}(#1917). Passed once for the whole cloud rather than per coloring, because a point’s IDENTITY does not change with the colour scheme — only its label does. Tiled K times here so it stays aligned with the stacked positions, which is the alignment this helper exists to own.
- Returns:
A
StackedColorings.positionshas shape(N*K, D+1)with column 0 = the coloring index;categoriesis the ordered label list for buildingDimension("coloring", categories=...).
- luxar.demos.stamp_input_digests(scene: _SceneWithAttrs) None[source]
Stamp every verified manifest input resolved in this process.
The basename-keyed map participates in
content_hashwhen stamped before finalization. Does nothing when the resolver has recorded no inputs.
- luxar.demos.substitutive_lod_or_flat(spec: Any, *, geometry: str = 'Points') Any[source]
Return
specwhen substitutive LOD can be built here, elseNone.Demos cache their expensive artifacts, so a warm cache is supposed to build a scene on any machine. But requesting
substitutive_lod=importsluxar.gsplats.lod(seeSUBSTITUTIVE_LOD_MODULES), so a cache-complete machine without torch/scipy died with aModuleNotFoundErrormid-build instead. Route every demo’ssubstitutive_lod=argument through here: with both modules present the spec passes through unchanged; without them the caller writes a flat leaf, which is fully viewable — it just loses the coarse levels that replace it when zoomed out.Unlike
require_module()this DEGRADES rather than raising, so it prints the one notice explaining what the scene lost and how to get it back.- Parameters:
spec – The
substitutive_lodargument the demo would pass.geometry –
PointsorLines— names the geometry in the notice.
- Returns:
specunchanged, orNonewhen a required module is missing.
- luxar.demos.survey(extra: str | None = None) list[DependencyStatus][source]
Report install status for every known optional dependency.
- Parameters:
extra – Restrict to specs ATTRIBUTED to this Luxar extra (
"demos","io","gsplats") — i.e. recorded under it in the table, not every spec installing the extra would pull in (a package pinned in two extras is attributed to only one).Nonesurveys the whole table, including the specs that deliberately belong to no extra.- Returns:
One
DependencyStatusper spec, sorted case-insensitively by module name so the CLI’s output order is stable. Each row records both whether the module imports and whether its installed version meets the spec’s bound (seeDependencyStatus.satisfied).
- luxar.demos.trilinear_vector(field: FlowField, points: ndarray) ndarray[source]
Trilinearly interpolate
field.vectorsat worldpoints.Out-of-bounds points return NaN rows so the caller can detect them via
np.isfinite.- Parameters:
field – The flow field.
points –
(M, 3)float32 world-space points.
- Returns:
(M, 3)float32 interpolated vectors. NaN rows for any sample whose lower corner index is outside[0, n-2]along any axis.
- luxar.demos.unit_flow(field: FlowField, points: ndarray) ndarray[source]
Return unit-length flow direction at
points.NaN rows for out-of-bounds samples and for zero-magnitude vectors (numerical floor 1e-7).
- luxar.demos.voxel_sampled_payload_agreement(centers: ndarray, payload: ndarray, *, min_pairs: int = 1024) float | None[source]
Fraction of same-voxel splat pairs that carry an identical payload row.
Several demos ship a per-splat payload (an organ label, a sampled RGB) as a SEPARATE sidecar file, indexed positionally against a
.gsplats.zarrfit. The data manifest marks these companions withpositional_pairso fetches keep their generations aligned; this check validates their actual row order. Nothing in either file records the correspondence, so a sidecar written in a different splat order than the store —GSplatData.saveapplies a spatial ordering, so the stored order is NOT the in-memory one — loads silently and renders plausible nonsense. This is the cheap check that catches it.The invariant it tests: the payload was produced by NEAREST-VOXEL sampling of a volume at the splat centers (
np.roundthe center, index the volume), so two splats whose centers round to the SAME voxel necessarily read the SAME value. Aligned data satisfies that exactly (agreement 1.0); a permuted sidecar pairs each voxel with unrelated rows and scores at the chance level of the payload’s own value distribution.HOW STRONG THE VERDICT IS depends on the payload’s own value diversity, not on this function: a shuffled sidecar still scores at that payload’s chance level
Σ p_v²(measured on the two shipped payloads: 0.027 for the CT’s 117 organ labels, 1.4e-05 for the Visible Human’s sampled uint8 RGB), so a payload that is NEARLY CONSTANT scores near 1.0 however badly it is permuted. A caller whose payload has little diversity must not rely on this check. NaN is likewise invisible to it:NaN == NaNis False, so a float payload using NaN as “no data” scores ~0 even when perfectly aligned (neither Luxar caller can hit that — int labels and uint8-derived colours).PRECONDITION —
centersmust be in the voxel coordinates the payload was sampled in, i.e. straight offGSplatData.load, before any centring, scaling or other transform. Recentred centers round to different voxels and the collision structure the test relies on is lost.- Parameters:
centers –
(N, D)splat centers. EVERY column takes part in the voxel key: a stacked/nD fit puts the spatial dims first and the stacked axis LAST, so keying on three columns alone would fold every timepoint of a voxel together and reject an aligned sidecar. A row that has no integer voxel — a non-finite center, or a magnitude at or above_VOXEL_KEY_LIMIT— is excluded (it cannot be judged).payload –
(N,)or(N, C)per-splat values sampled at those centers.min_pairs – Minimum number of same-voxel pairs required to return a verdict. Clamped to at least 1: with zero pairs there is nothing to divide by, so “no evidence” must stay
Nonerather than raiseZeroDivisionError.
- Returns:
The agreement fraction in
[0, 1], orNonewhen fewer thanmin_pairssame-voxel pairs exist — too little evidence to judge, which a caller must treat as “unverifiable”, NOT as a failure.- Raises:
ValueError – if
centersandpayloadhave different lengths, orcentersis not 2-D, orcentershas no columns.
- luxar.demos.warn_if_no_cuda_gpu() None[source]
Print a warning if no CUDA GPU is available.
GSplat demos require significant GPU compute for fitting. Running on CPU is orders of magnitude slower and generally impractical for production runs. This function prints a prominent warning so users understand the hardware requirements before waiting hours for a CPU run.
- luxar.demos.warn_if_quarantined(target: str | Path, *, verbose: bool = True, action: str = 're-download the full file (or delete the quarantined copy)') list[Path][source]
Print an actionable warning if target has quarantined
.corruptfiles.Called at the download chokepoint so a user who is about to re-fetch a multi-gigabyte artifact is told why — a truncated earlier copy is sitting next to it — instead of silently watching a huge download start over.
- Returns:
The quarantined paths found (empty when clean).
- luxar.demos.window_attrs(window: tuple[float, float]) dict[str, float][source]
Intensity and offset storing a Layers-panel display window.
- class luxar.demos.FlowField(vectors: ndarray, grid_min: ndarray, grid_max: ndarray, spacing: float, cache_key: str)[source]
Cubic vector field on a regular grid.
- vectors
(n, n, n, 3)float32. Vectors[i, j, k] holds the field value at grid pointgrid_min + (i, j, k) * spacing.- Type:
- grid_min
(3,)float32, world-space min corner.- Type:
- grid_max
(3,)float32, world-space max corner.- Type: