gmat-sweep v0.4 — four more backends, six DataFrame helpers, Sobol sensitivity, archive bundles #12
djankov
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
gmat-sweepv0.4 is on PyPI. This one is the quality-of-life and ecosystem release: three more execution backends joinKubernetesJobPool(MPIPool,ProcessPoolExecutorPool,DebugPool), six DataFrame helpers land on top of the aggregator (sweep_summary,sweep_diff,mc_convergence,lazy_fused_reports, plusmonte_carlo_extend/latin_hypercube_extend), Sobol sensitivity and matplotlib plot helpers ship behind opt-in extras, an opt-in Polars output engine runs alongside the pandas default,Sweep.archivepacks a finished sweep into a deterministic.zipfor Zenodo / JOSS deposits, and notebook-friendly_repr_html_rendering lands onSweep/RunOutcome/ManifestEntry. CI gains a smoke-canary cell against the canonicalghcr.io/astro-tools/gmatimage; per-PR cells trim from 18 to 10 with the heavy integration work moving to a scheduledintegration.yml. Coverage gate raises from ≥ 85 % to ≥ 90 %.What's new in v0.4
KubernetesJobPool(pip install gmat-sweep[k8s]) makes every run onebatch/v1Job and every Pod a fresh interpreter, draining completions via akubernetes.watch.Watchloop. New pod-per-run recipe walks the wiring (#101).MPIPool([mpi]) wrapsmpi4py.futures.MPIPoolExecutorand works under both dynamic-spawn and pre-allocated-rank launches without any launcher detection on this side (#102).ProcessPoolExecutorPool(stdlib, Python ≥ 3.11) wrapsconcurrent.futures.ProcessPoolExecutor(max_tasks_per_child=1)— nojoblib/lokydependency, coexists withLocalJoblibPool(#104).DebugPooldispatches in-process so abreakpoint()reached during a run drops into the driver's debugger — two opt-in flags gate the deliberate isolation violation (#105).sweep_summarycollapses a(run_id, time)-MultiIndexed frame into per-bystatistics with a 2-level(statistic, field)column MultiIndex — default 5/50/95 quantiles plus mean/std feed directly into the plot helpers (#114).sweep_diffpairwise-compares two same-shape sweep DataFrames, emitting<col>__diffand/or<col>__relper shared numeric column (#119).mc_convergencereturns per-prefix running mean / std / SE-of-mean — answers "did my Monte Carlo converge?" for a chosen metric (#117).lazy_fused_reportsfuses NReportFileoutputs per run into one DataFrame with column-levelMultiIndexkeyed by(report_name, column)(#107).monte_carlo_extendandlatin_hypercube_extend(sibling typed-refusal sentinel) run only the newnsamples on top of an existing sweep — the originalrun_ids' draws are bit-equal to the same indices of a freshmonte_carlo(n=old_n + n, seed=...)(#115).gmat_sweep.sensitivitymodule behindgmat-sweep[sensitivity].sobol_sample(perturb, n, ...)builds the Saltelli/Sobol design as an explicit-row DataFrame;sobol_analyze(df, perturb, metric, ...)returns first / total / second-order indices with bootstrap confidence intervals. Newdocs/sensitivity.mdwalks the design and the metric-callable contract (#121).gmat_sweep.plottingmodule behindgmat-sweep[plot].sweep_corneris a pair plot of perturbed dotted-paths coloured by a per-run scalar metric —kind="auto"flips to hexbin past 2000 runs to avoid silent overplot saturation.sweep_heatmapis a contour-grade heatmap on a 2-axis grid sweep.matplotlibis imported lazily inside each helper, soimport gmat_sweep.plottingsucceeds without the extra (#109).Sweep.archivefor Zenodo / JOSS deposits.Sweep.archive(out, *, include_logs=False)and a matchinggmat-sweep archiveCLI subcommand pack a finished sweep — script, manifest, per-run Parquet outputs, a generated reproduce-recipe README, and asha256sum-compatibleMANIFEST.hash— into one deterministic.zipready for an archival deposit. Bundled manifestoutput_paths/log_pathare rewritten to bundle-relative form (#111).engine="polars"acrosssweep/monte_carlo/latin_hypercube/monte_carlo_extend,Sweep.to_dataframe/to_ephemerides/to_contacts(plus aSweep.to_polars()shortcut), and the standalonelazy_multiindex/lazy_ephemerides/lazy_contacts/sweep_diff/mc_convergence. Pandas remains the default; behindgmat-sweep[polars]. Marked experimental for v0.4 (#120)._repr_html_.Sweep/RunOutcome/ManifestEntryrender as compact HTML key/value tables in Jupyter instead of the default<gmat_sweep.sweep.Sweep object at 0x…>placeholder;__repr__is unchanged (#108).Sweep.archive(), inspect + re-aggregate from the unzipped bundle. Extending Monte Carlo — 100-run base +monte_carlo_extend(n=200)+ bit-for-bit determinism assertion on the original 100.sweep_summary,sweep_corner, andsweep_heatmapcells appear inline across the existing notebooks (#124).smoke-canary-imageunder the newintegration.ymlruns the four-line vision-snippet sweep and a 4-run Monte Carlo againstghcr.io/astro-tools/gmat:R2025aand:R2026aon every push tomain, nightly, and on demand (#112).mainpush; heavy backend and canary cells move to the newintegration.yml(push:main + nightly + on-demand). No coverage loss post-merge (#126).CITATION.cffat the repo root so GitHub's "Cite this repository" UI and academic tooling resolve a canonical citation for the project (#116).Hardening pass
A late-cycle round closed the v0.4-review punch list across the manifest, aggregator, worker / orchestration, every backend pool, the CLI, and the plot helpers:
Pool.imap(specs, *, in_flight=None)ABC with a chunked default;grids.iter_grid_run_specs+Sweep.__init__accepting anIterable[RunSpec]mean a 10⁵-row factorial no longer pins 10⁵ specs + 10⁵ futures in driver memory.Manifest.iter_entries(path)and classmethodfind_failed/find_missingmeangmat-sweep resumeon a 10k-run manifest no longer pays 10k JSON parses against an entry list it never reads (#137, #140)._aggregatepeak heap goes from ~67× to ~8× the final-frame size on a 1000-run fixture viapa.concat_tables(...).to_pandas(). Welford's online recurrence replaces the cumulative sum-of-squares variance identity — fixes catastrophic cancellation on km-magnitude metrics wherenp.clip(var, 0, None)was reporting zero std (#136).ProcessPoolExecutor/ Dask / MPI rank / Ray worker death,RayTaskError, pickling fault, …) into a syntheticRunOutcome.failedat the drain site instead of letting.result()raise and abort the sweep (#138).KubernetesJobPooldeadline + close cleanup. Newjob_deadline_seconds: int = 3600hang-protects stuck Jobs;close()background-deletes every in-flight Job on shutdown; spec JSON written to the PVC is unlinked on every Job-create failure path (#135).fsync_each: bool = True/fsync_batch: int = 50) amortises durability across batches when you want it.canonical_script_sha256strips a leading UTF-8 BOM before normalisation so a.scriptsaved from a BOM-emitting Windows editor hashes equal to the same script without a BOM.ManifestCorruptError.line_numberand apath:lineCLI top-level handler make corruption locatable. AManifest._migrate_headershim is a no-op for v1 → v1 but gives a future v2 bump a single hook (#137).import gmat_sweep.cliis now lazy —pandas/pyarrow/tqdm/joblibare not pulled until first use.gmat-sweep --helpdrops from ~300–800 ms to ~120 ms on a warm cache, pinned bytest_cold_start_does_not_load_heavy_dependencies(#139).duration_sis now sourced fromtime.monotonic()— an NTP step mid-run can't drive duration negative.RunOutcome.from_dict/RunSpec.from_dictvalidatestatusand field types explicitly; bad data surfaces asManifestCorruptErroror exit code_EXIT_BAD_SPEC=3(#140).Behaviour changes worth knowing about
LocalJoblibPool(workers=...)deprecated.max_workers=is the new spelling;workers=still works but emitsDeprecationWarning. CLI--workersis unaffected (#138).Manifest.find_failed()/find_missing(...)are classmethods now. Pre-1.0; pass apathargument. The only public doc snippet that called the old form has been updated (#137).grids.py,distributions.py,manifest.py, andaggregate.pyare unchanged (#113).Full notes: https://github.com/astro-tools/gmat-sweep/blob/main/CHANGELOG.md#040--2026-05-10
Install
Same baseline: Python 3.10–3.12 and a local GMAT install. R2025a and R2026a are exercised on every PR (Ubuntu / Windows / macOS × Py 3.10 / 3.11 / 3.12 — the per-PR matrix is 10 cells now, with the full 18-cell matrix and the heavy backend / canary cells running on every push to
mainand nightly).Links
Feedback wanted
KubernetesJobPoolandMPIPoolare the v0.4 cluster surface that does not exist in v0.3 — comment or open an issue with the diff and any gotcha that didn't survive the recipes / docstrings. Job-deadline tuning, PVC layout, andmpirunlauncher detection are the three places this is most likely to surprise.Sweep.archivefor a deposit? The bundle is meant to be self-describing — script + manifest + per-run Parquets + a reproduce-recipe README + asha256sum-compatible hash — but actual archival workflows are the test. If the bundle layout reads cleanly to a reviewer who has never seengmat-sweepbefore, great; if there's a missing affordance (DOI placeholder, dataset card, citation block), that's exactly the kind of feedback that earns a v0.5 entry.(run_id, time)index → column-pair translation is the conversation we want to have before v1.0. If you have a pandas → polars migration where the contract bent in an unexpected place, please flag the column shape on an issue.Beta Was this translation helpful? Give feedback.
All reactions