Skip to content

API reference

Every public symbol re-exported from the gmat_sweep top-level package, auto-generated from docstrings via mkdocstrings.

Sweep entry points

sweep

Sweep orchestrator: owns the run iterable, backend, manifest, and output dir.

The single public class is :class:Sweep. It binds a list of :class:gmat_sweep.spec.RunSpec to a backend :class:gmat_sweep.backends.base.Pool, fans the specs out, drains the resulting outcomes in completion order, and records each one as a :class:gmat_sweep.manifest.ManifestEntry with an fsynced append so a mid-sweep Ctrl-C leaves a parseable manifest on disk.

The class does not own the pool's lifecycle — wrap the supplied :class:Pool in a with block at the call site (or call close()) so worker processes are cleaned up. The thin :func:gmat_sweep.api.sweep wrapper takes care of this for the common case.

Sweep

Sweep(
    *,
    runs: Sequence[RunSpec],
    backend: Pool,
    manifest_path: Path,
    output_dir: Path,
    script_path: Path,
    parameter_spec: Mapping[str, Any],
    sweep_seed: int | None = None,
    progress: bool = True,
)

Bind run specs, a pool, and a manifest path into a runnable orchestrator.

Parameters

runs: The :class:RunSpec instances to dispatch. run_id values must be unique. Order is preserved on the submission side; outcomes return in completion order. backend: A constructed :class:Pool. The caller owns its lifecycle — typically a with LocalJoblibPool(...) as pool: block. manifest_path: Where the JSON Lines manifest will be written. Parent directories are created on :meth:run. output_dir: Sweep root the per-run output directories live under. Used as the anchor for any relative paths the manifest records. script_path: The .script every run loads. Hashed via :func:canonical_script_sha256 for the manifest header. parameter_spec: The original sweep parameterisation (e.g. the materialised grid) — recorded verbatim in the manifest header for reproducibility. sweep_seed: Optional integer seed recorded on the manifest. Sweep does not consume it directly; the Monte Carlo and Latin hypercube wrappers in :mod:gmat_sweep.api use it to derive their per-run draws. progress: True (default) wraps the drain loop in a :mod:tqdm bar. Set to False for non-interactive use (tests, CI logs).

run

run() -> Sweep

Submit every run, drain outcomes in completion order, return self.

Builds and saves the manifest header up front (one fsync, with the parent directory created on demand). For each completed :class:RunOutcome an entry is appended via :meth:Manifest.append_entry, which fsyncs each line — a Ctrl-C between any two iterations leaves a parseable file containing exactly the runs that finished.

:exc:KeyboardInterrupt is not caught; it propagates so the caller's with-managed pool exits and cancels still-pending futures.

from_manifest classmethod

from_manifest(
    manifest_path: str | Path,
    script_path: str | Path,
    *,
    backend: Pool,
    allow_script_drift: bool = False,
    progress: bool = True,
) -> Sweep

Rebuild a :class:Sweep from a manifest written by a prior run.

Reads manifest_path, validates that the on-disk script still matches the manifest's recorded script_sha256, reconstructs the run iterable from the manifest's parameter_spec, and returns a :class:Sweep whose manifest is pre-bound to the loaded one. The returned sweep is suitable input to :meth:resume; calling :meth:run on it would re-execute every run from scratch and is not the intended flow.

Parameters

manifest_path: Path to the existing manifest.jsonl. Its parent is treated as the sweep's output_dir and must still exist on disk — successful runs' Parquet files are read from there as-is. script_path: Path to the GMAT .script to load. The file's canonical SHA-256 must equal the manifest's script_sha256 unless allow_script_drift is set. backend: A constructed :class:Pool. The caller owns its lifecycle — same contract as the regular constructor. allow_script_drift: False (default) raises :class:SweepConfigError on a hash mismatch with both hashes in the message. True proceeds anyway and emits a :class:RuntimeWarning. progress: Forwarded to the constructor — controls the :mod:tqdm bar in :meth:resume.

Raises

SweepConfigError If manifest_path's parent directory does not exist, the script hash drifted and allow_script_drift is False, or the manifest's parameter_spec carries an unknown _kind.

resume

resume() -> Sweep

Re-run only the failed and missing runs from the loaded manifest.

Submits specs for the union of manifest.find_failed() and manifest.find_missing(...) through the bound backend, appends one new :class:ManifestEntry per outcome (with the same run_id as the original), and reloads the manifest so the in-memory entries reflect the last-wins merge. Returns self for chaining.

Raises :exc:RuntimeError when called on a :class:Sweep not produced by :meth:from_manifest.

to_manifest

to_manifest() -> Manifest

Return the manifest populated by :meth:run.

to_dataframe

to_dataframe(name: str | None = None) -> DataFrame

Aggregate the sweep's ReportFile outputs into one DataFrame.

name selects which report to aggregate when the sweep produced multiple ReportFile resources per run; None (default) picks the sole report when exactly one was produced. See :func:gmat_sweep.aggregate.lazy_multiindex for the full contract.

to_ephemerides

to_ephemerides(name: str | None = None) -> DataFrame

Aggregate the sweep's EphemerisFile outputs into one DataFrame.

See :func:gmat_sweep.aggregate.lazy_ephemerides for the contract.

to_contacts

to_contacts(name: str | None = None) -> DataFrame

Aggregate the sweep's ContactLocator outputs into one DataFrame.

See :func:gmat_sweep.aggregate.lazy_contacts for the contract.

monte_carlo

monte_carlo(
    mission: str | Path,
    *,
    n: int,
    perturb: Mapping[str, DistSpec],
    seed: int | None = None,
    backend: Pool | None = None,
    out: Path | None = None,
    progress: bool = True,
) -> DataFrame

Run a Monte Carlo dispersion sweep over a GMAT mission.

Builds an explicit-row run set of n runs by independently sampling each perturb parameter from its own distribution. Per-parameter sub-seeds are derived from the parameter name via :func:derive_param_seed <gmat_sweep.distributions.derive_param_seed>, so adding a perturbed parameter to an existing sweep does not change the draws of any other parameter at any run_id.

Parameters

mission: Path to the GMAT .script file every run loads. n: Number of stochastic runs. Must be >= 1. perturb: Mapping from dotted-path field name to a distribution spec. Each value is one of the three shorthand tuples (("normal", mu, sigma), ("uniform", lo, hi), ("lognormal", mu, sigma)) or any pre-frozen :class:scipy.stats._distn_infrastructure.rv_frozen. See :data:gmat_sweep.distributions.DistSpec for the full surface. seed: Optional integer parent seed. None falls back to OS entropy and is not reproducible. With an integer seed two calls at the same (mission, n, perturb, seed) produce bit-equal DataFrames. backend: Execution backend; same semantics as :func:sweep. out: Sweep output directory; same semantics as :func:sweep. progress: Whether to draw the :mod:tqdm progress bar; same semantics as :func:sweep.

Returns

pandas.DataFrame (run_id, time)-MultiIndexed frame, one row per (run, time-step) pair, with run_id cardinality n. A failed run lands as one NaN row with __status="failed" — same contract as :func:sweep.

Raises

SweepConfigError If perturb is empty, n < 1, or any parameter spec is ill-formed.

latin_hypercube

latin_hypercube(
    mission: str | Path,
    *,
    n: int,
    perturb: Mapping[str, DistSpec],
    seed: int | None = None,
    backend: Pool | None = None,
    out: Path | None = None,
    progress: bool = True,
) -> DataFrame

Run a Latin hypercube sweep over a GMAT mission.

Backed by :class:scipy.stats.qmc.LatinHypercube: draws n unit-cube points stratified across each of len(perturb) axes and maps each column through the user's distribution via rv.ppf(...). Latin hypercube sampling typically beats plain Monte Carlo when n is small relative to the problem's dimensionality, because the coverage of each axis is enforced by construction.

Parameters

mission: Path to the GMAT .script file every run loads. n: Number of Latin hypercube points. Must be >= 1. perturb: Mapping from dotted-path field name to a distribution spec — same accepted shapes as :func:monte_carlo. seed: Optional integer seed forwarded to :class:scipy.stats.qmc.LatinHypercube. None falls back to OS entropy and is not reproducible. With an integer seed two calls at the same (mission, n, perturb, seed) produce bit-equal DataFrames. backend: Same semantics as :func:sweep. out: Same semantics as :func:sweep. progress: Same semantics as :func:sweep.

Returns

pandas.DataFrame (run_id, time)-MultiIndexed frame with run_id cardinality n. Same failure-as-row contract as :func:sweep.

Raises

SweepConfigError If perturb is empty, n < 1, or any parameter spec is ill-formed.

Sweep

Sweep(
    *,
    runs: Sequence[RunSpec],
    backend: Pool,
    manifest_path: Path,
    output_dir: Path,
    script_path: Path,
    parameter_spec: Mapping[str, Any],
    sweep_seed: int | None = None,
    progress: bool = True,
)

Bind run specs, a pool, and a manifest path into a runnable orchestrator.

Parameters

runs: The :class:RunSpec instances to dispatch. run_id values must be unique. Order is preserved on the submission side; outcomes return in completion order. backend: A constructed :class:Pool. The caller owns its lifecycle — typically a with LocalJoblibPool(...) as pool: block. manifest_path: Where the JSON Lines manifest will be written. Parent directories are created on :meth:run. output_dir: Sweep root the per-run output directories live under. Used as the anchor for any relative paths the manifest records. script_path: The .script every run loads. Hashed via :func:canonical_script_sha256 for the manifest header. parameter_spec: The original sweep parameterisation (e.g. the materialised grid) — recorded verbatim in the manifest header for reproducibility. sweep_seed: Optional integer seed recorded on the manifest. Sweep does not consume it directly; the Monte Carlo and Latin hypercube wrappers in :mod:gmat_sweep.api use it to derive their per-run draws. progress: True (default) wraps the drain loop in a :mod:tqdm bar. Set to False for non-interactive use (tests, CI logs).

run

run() -> Sweep

Submit every run, drain outcomes in completion order, return self.

Builds and saves the manifest header up front (one fsync, with the parent directory created on demand). For each completed :class:RunOutcome an entry is appended via :meth:Manifest.append_entry, which fsyncs each line — a Ctrl-C between any two iterations leaves a parseable file containing exactly the runs that finished.

:exc:KeyboardInterrupt is not caught; it propagates so the caller's with-managed pool exits and cancels still-pending futures.

from_manifest classmethod

from_manifest(
    manifest_path: str | Path,
    script_path: str | Path,
    *,
    backend: Pool,
    allow_script_drift: bool = False,
    progress: bool = True,
) -> Sweep

Rebuild a :class:Sweep from a manifest written by a prior run.

Reads manifest_path, validates that the on-disk script still matches the manifest's recorded script_sha256, reconstructs the run iterable from the manifest's parameter_spec, and returns a :class:Sweep whose manifest is pre-bound to the loaded one. The returned sweep is suitable input to :meth:resume; calling :meth:run on it would re-execute every run from scratch and is not the intended flow.

Parameters

manifest_path: Path to the existing manifest.jsonl. Its parent is treated as the sweep's output_dir and must still exist on disk — successful runs' Parquet files are read from there as-is. script_path: Path to the GMAT .script to load. The file's canonical SHA-256 must equal the manifest's script_sha256 unless allow_script_drift is set. backend: A constructed :class:Pool. The caller owns its lifecycle — same contract as the regular constructor. allow_script_drift: False (default) raises :class:SweepConfigError on a hash mismatch with both hashes in the message. True proceeds anyway and emits a :class:RuntimeWarning. progress: Forwarded to the constructor — controls the :mod:tqdm bar in :meth:resume.

Raises

SweepConfigError If manifest_path's parent directory does not exist, the script hash drifted and allow_script_drift is False, or the manifest's parameter_spec carries an unknown _kind.

resume

resume() -> Sweep

Re-run only the failed and missing runs from the loaded manifest.

Submits specs for the union of manifest.find_failed() and manifest.find_missing(...) through the bound backend, appends one new :class:ManifestEntry per outcome (with the same run_id as the original), and reloads the manifest so the in-memory entries reflect the last-wins merge. Returns self for chaining.

Raises :exc:RuntimeError when called on a :class:Sweep not produced by :meth:from_manifest.

to_manifest

to_manifest() -> Manifest

Return the manifest populated by :meth:run.

to_dataframe

to_dataframe(name: str | None = None) -> DataFrame

Aggregate the sweep's ReportFile outputs into one DataFrame.

name selects which report to aggregate when the sweep produced multiple ReportFile resources per run; None (default) picks the sole report when exactly one was produced. See :func:gmat_sweep.aggregate.lazy_multiindex for the full contract.

to_ephemerides

to_ephemerides(name: str | None = None) -> DataFrame

Aggregate the sweep's EphemerisFile outputs into one DataFrame.

See :func:gmat_sweep.aggregate.lazy_ephemerides for the contract.

to_contacts

to_contacts(name: str | None = None) -> DataFrame

Aggregate the sweep's ContactLocator outputs into one DataFrame.

See :func:gmat_sweep.aggregate.lazy_contacts for the contract.

Execution backend

Pool

Bases: ABC

Abstract execution backend.

Subclasses MUST keep :attr:subprocess_isolated set to :data:True, signalling that they implement both the per-worker-reuse and per-task- fresh-bootstrap modes correctly. Setting the attribute to anything else (False, None, a truthy non-True value) raises :class:gmat_sweep.errors.BackendError from :meth:__init_subclass__ so the error fires when the bad backend's module is imported, not at sweep time.

Subclasses accept reuse_gmat_context: bool = True as a keyword-only parameter on __init__ and store it; concrete dispatch in :meth:as_completed reads self._reuse_gmat_context to choose between calling :func:gmat_sweep.worker.run_one directly (fast path) and delegating to :func:gmat_sweep.backends._subprocess.run_spec_in_subprocess (isolation path).

submit abstractmethod

submit(spec: RunSpec) -> Future[RunOutcome]

Enqueue spec and return a future that resolves once it runs.

as_completed abstractmethod

as_completed(
    futures: Iterable[Future[RunOutcome]],
) -> Iterator[RunOutcome]

Drain futures, yielding outcomes in completion order.

close abstractmethod

close() -> None

Release backend resources. Idempotent.

LocalJoblibPool

LocalJoblibPool(
    workers: int = -1, *, reuse_gmat_context: bool = True
)

Bases: Pool

Local subprocess pool backed by joblib.Parallel(backend="loky").

Parameters

workers: Number of loky worker processes. -1 (the default) uses every available core. Any other negative value or 0 is rejected with :class:gmat_sweep.errors.BackendError. reuse_gmat_context: True (default) dispatches each task as :func:gmat_sweep.worker.run_one, which imports gmat_run once per loky worker and reuses the import across tasks — fast, but only safe when every spec dispatched through this pool loads the same script. False dispatches each task through :func:gmat_sweep.backends._subprocess.run_spec_in_subprocess, spawning a fresh Python interpreter inside the loky worker so each task bootstraps gmatpy from scratch — slower, but supports cross-script sweeps. See :class:gmat_sweep.backends.base.Pool for the contract.

DaskPool

DaskPool(
    *,
    client: Client | None = None,
    n_workers: int | None = None,
    threads_per_worker: int = 1,
    reuse_gmat_context: bool = True,
)

Bases: Pool

Distributed pool backed by dask.distributed.

Parameters

client: An existing :class:distributed.Client to dispatch through. When supplied, the pool does not create or own a cluster, and :meth:close does not shut the client down. n_workers: Number of workers in the auto-spawned :class:distributed.LocalCluster. Ignored when client is supplied. None (default) uses :func:os.cpu_count. threads_per_worker: Threads per worker for the auto-spawned :class:distributed.LocalCluster. Ignored when client is supplied. Defaults to 1 so each worker is a single-threaded subprocess shell. reuse_gmat_context: True (default) lets Dask workers reuse a single gmatpy import across tasks — fast, but only safe when every spec dispatched through this pool loads the same script. False runs each task through :func:_dask_run_one, which spawns a fresh Python interpreter per task and bootstraps gmatpy from scratch — slower, but supports cross-script sweeps. See :class:gmat_sweep.backends.base.Pool for the contract.

RayPool

RayPool(
    *,
    address: str | None = None,
    num_cpus: int | None = None,
    reuse_gmat_context: bool = True,
    **ray_init_kwargs: Any,
)

Bases: Pool

Distributed pool backed by Ray.

Parameters

address: Forwarded to :func:ray.init to connect to an existing cluster ("auto" for a local cluster, "ray://host:port" for a remote Ray Client server, or a raw GCS address). None (default) starts a local Ray runtime. num_cpus: Forwarded to :func:ray.init for the local-runtime case. Ignored when connecting to an existing cluster via address. reuse_gmat_context: True (default) lets Ray workers reuse a single gmatpy import across tasks — fast, but only safe when every spec dispatched through this pool loads the same script. False binds the Ray remote to :func:_ray_run_one_impl, which spawns a fresh Python interpreter per task and bootstraps gmatpy from scratch — slower, but supports cross-script sweeps. See :class:gmat_sweep.backends.base.Pool for the contract. **ray_init_kwargs: Extra keyword arguments forwarded verbatim to :func:ray.init.

Specs and outcomes

RunSpec dataclass

RunSpec(
    script_path: Path,
    overrides: dict[str, Any],
    output_dir: Path,
    run_id: int,
    seed: int | None,
    run_options: dict[str, Any],
)

A single run's worth of work — script + overrides + run_id + seed.

A worker reconstructs the full run from this record alone: instantiate :class:gmat_run.Mission from script_path, apply overrides via the dotted-path setter, run with run_options, write outputs under output_dir.

SweepSpec dataclass

SweepSpec(
    mission_script_path: Path,
    runs: tuple[RunSpec, ...],
    backend: str,
    backend_kwargs: dict[str, Any],
    output_dir: Path,
    manifest_path: Path,
    sweep_seed: int | None = None,
)

A whole sweep's metadata — script, runs, backend, outputs.

runs is a materialised :class:tuple of :class:RunSpec so the spec round-trips through JSON cleanly. run_id ordering is the contract the manifest and resume flow depend on: runs[i].run_id == i for every well-formed sweep.

RunOutcome dataclass

RunOutcome(
    run_id: int,
    status: RunStatus,
    output_paths: dict[str, Path],
    duration_s: float,
    stderr: str | None,
    started_at: datetime,
    ended_at: datetime,
)

The result of one run after the worker returns.

output_paths maps a worker-chosen key (e.g. the parsed ReportFile resource name) to the on-disk Parquet artefact written under :attr:RunSpec.output_dir. Empty for failed and skipped runs. stderr is None for successful runs and the captured worker stderr / traceback string for failed runs. duration_s is computed by the :meth:ok / :meth:failed helpers from the bookend timestamps so the three values cannot disagree.

Grid expansion

full_factorial

full_factorial(
    grid: Mapping[str, Iterable[Any]],
) -> Iterator[dict[str, Any]]

Yield override dicts for the cartesian product of grid.

Keys are emitted in lexicographic order; the cartesian product enumerates in :func:itertools.product order over the input iterables, so the outer loop varies the lexicographically-first key slowest and the last key fastest. For {"a": [1, 2], "b": [10, 20, 30]} the six dicts come out as (a=1, b=10), (a=1, b=20), (a=1, b=30), (a=2, b=10), (a=2, b=20), (a=2, b=30).

Each input iterable is materialised once at entry so callers may pass generators without surprising exhaustion and so empty-iterable validation can run before the cartesian product begins.

An empty mapping is valid and yields a single empty override dict — the cartesian product of nothing has one element.

Raises :class:SweepConfigError if any key is not a :class:str or any value materialises to an empty sequence.

expand_grid_to_run_specs

expand_grid_to_run_specs(
    grid: Mapping[str, Iterable[Any]],
    script_path: str | Path,
    output_dir: str | Path,
) -> list[RunSpec]

Build a list of :class:RunSpec from a full-factorial expansion of grid.

Each spec gets a sequential run_id starting at 0, script_path propagated through, output_dir set to <output_dir>/run-<run_id>, seed=None, and run_options={}. The ordering contract from :func:full_factorial carries through unchanged: specs[i].run_id == i and the override dicts appear in cartesian-product order.

Raises :class:SweepConfigError for the same reasons as :func:full_factorial.

Manifest

MANIFEST_SCHEMA_VERSION module-attribute

MANIFEST_SCHEMA_VERSION: int = 1

On-disk manifest schema version this gmat-sweep writes and reads.

:meth:Manifest.load accepts any header whose schema_version is <= MANIFEST_SCHEMA_VERSION (a missing field is treated as 1 for backwards compatibility with manifests written before the field was introduced) and rejects anything greater. See docs/manifest-schema.md for the field-by-field contract and the compatibility policy that governs future bumps.

Manifest dataclass

Manifest(
    script_sha256: str,
    gmat_sweep_version: str,
    gmat_run_version: str,
    gmat_install_version: str,
    python_version: str,
    os_platform: str,
    sweep_seed: int | None,
    parameter_spec: dict[str, Any],
    run_count: int,
    backend: str = "unknown",
    schema_version: int = MANIFEST_SCHEMA_VERSION,
    entries: list[ManifestEntry] = list(),
)

Sweep manifest — header fingerprint plus a growing list of per-run entries.

save

save(path: Path) -> None

Write header + all current entries as JSON Lines, fsyncing file and parent dir.

append_entry

append_entry(entry: ManifestEntry) -> None

Append one entry to the bound file with fsync, and to the in-memory list.

load classmethod

load(path: Path) -> Manifest

Load a manifest from disk, tolerating a single torn final line.

find_failed

find_failed() -> list[int]

Return run_ids of entries with status failed, in arrival order.

find_missing

find_missing(expected_run_ids: Iterable[int]) -> list[int]

Return run_ids in expected_run_ids with no entry recorded, in input order.

ManifestEntry dataclass

ManifestEntry(
    run_id: int,
    overrides: dict[str, Any],
    status: RunStatus,
    output_paths: dict[str, Path],
    started_at: datetime,
    ended_at: datetime,
    duration_s: float,
    stderr: str | None,
    log_path: Path | None,
)

One run's record in the manifest — overrides, status, outputs, timing.

from_outcome classmethod

from_outcome(
    outcome: RunOutcome,
    *,
    overrides: dict[str, Any],
    log_path: Path | None = None,
) -> ManifestEntry

Build a manifest entry from a worker :class:RunOutcome.

canonical_script_sha256

canonical_script_sha256(script_path: Path) -> str

SHA-256 of the script file after line-ending and trailing-newline normalisation.

Reads the file as bytes, decodes as UTF-8, replaces \r\n and lone \r with \n, and ensures exactly one trailing \n. The hash is computed over the resulting UTF-8 bytes.

Aggregation

lazy_multiindex

lazy_multiindex(
    manifest: Manifest,
    output_dir: Path,
    *,
    name: str | None = None,
    spool: bool = True,
) -> DataFrame

Assemble the (run_id, time)-indexed report DataFrame from a sweep's outputs.

Iterates manifest.entries in order. For each ok entry the Parquet listed in :attr:ManifestEntry.output_paths under the report__<name> key is read via :mod:pyarrow.dataset and tagged with its run_id. For each failed or skipped entry — and for any ok entry that did not produce the requested report — one NaN-filled row is materialised with time = NaT and __status set to the run-level status ("failed" / "skipped" for non-ok runs, "ok" for ok runs missing this report).

Relative paths in output_paths are resolved against output_dir; absolute paths are used as-is.

Parameters

manifest The sweep manifest. Drives both the set of runs and their status. output_dir Sweep output root. Used to anchor any relative paths recorded in the manifest. name Report resource name to aggregate. None (default) picks the sole report if exactly one report is present across the sweep. Sweeps that produced multiple reports per run must pass name= explicitly; the call raises :class:gmat_sweep.errors.SweepConfigError listing the available names otherwise. spool True (default) streams each run's record batches into pandas one batch at a time. False reads each run's Parquet eagerly in one shot — simpler control flow, higher peak memory.

Raises

SweepConfigError name=None was passed but the sweep produced more than one report (the exception message lists the available names), or the explicitly-named report does not appear in any ok run's outputs. ValueError An ok entry has no output_paths at all (a run that ran successfully must have produced something), or a per-run Parquet is missing the time column required for the index.

lazy_ephemerides

lazy_ephemerides(
    manifest: Manifest,
    output_dir: Path,
    *,
    name: str | None = None,
    spool: bool = True,
) -> DataFrame

Assemble the (run_id, time)-indexed ephemeris DataFrame from a sweep's outputs.

Mirrors :func:lazy_multiindex but dispatches on ephemeris__<name> keys instead of report__<name>. The worker copies the first datetime column of each ephemeris frame (Epoch for OEM, STK, and SPK formats) to a column named time before writing Parquet, so the same (run_id, time) index machinery applies.

See :func:lazy_multiindex for parameter and exception semantics.

lazy_contacts

lazy_contacts(
    manifest: Manifest,
    output_dir: Path,
    *,
    name: str | None = None,
) -> DataFrame

Assemble the (run_id, interval_id)-indexed contact DataFrame from a sweep's outputs.

Mirrors :func:lazy_multiindex but dispatches on contact__<name> keys and uses interval_id — the per-run row position the worker assigns at write time, 0..K-1 per run — as the secondary index level. ContactLocator outputs are typically tiny (one row per visibility interval), so there is no spool knob; reads are fragment-at-a-time eager.

Failed, skipped, and report-only ok runs materialise as one row with interval_id = pd.NA (cast as the nullable Int64 dtype so integer interval indices and missing values share one level).

See :func:lazy_multiindex for the rest of the parameter and exception semantics.

Exceptions

GmatSweepError

Bases: Exception

Base class for every exception raised by gmat-sweep.

SweepConfigError

Bases: GmatSweepError

Raised when a sweep configuration is invalid before any run starts.

Covers contradictory arguments, malformed grid specs, and dotted-path overrides that fail validation at sweep-construction time. The message is the only payload.

RunFailed

RunFailed(
    message: str, run_id: int, stderr: str | None = None
)

Bases: GmatSweepError

Raised when a single run fails inside a worker (raise-on-failure mode).

Per the gmat-sweep contract a failed run normally lands as a labelled row in the parent DataFrame rather than an exception, so this class is a typed sentinel for tests and for callers that opt into eager failure. The run_id attribute identifies the offending run; stderr carries the captured worker stderr or Python traceback.

BackendError

Bases: GmatSweepError

Raised when an execution backend itself fails.

Covers worker-pool initialisation failures, lost workers, and backend implementations that violate the subprocess-isolation contract enforced by the :class:Pool ABC.

ManifestCorruptError

ManifestCorruptError(message: str, path: Path)

Bases: GmatSweepError

Raised when a sweep manifest cannot be parsed.

The path attribute points at the offending file so callers can surface it in error messages without re-deriving the path.