Repository navigation
Releases
virgil 0.4.1
virgil 0.4.1
pip install --upgrade virgil-astro==0.4.10.4.1 is a consolidation release with no new model families. The code is shorter to read and safer by default on
aperture-masking and DISCO data, and virgil can now write the OIFITS files it reads. The full radio work
is planned for 0.4.2.
What's new
- Write OIFITS from an
OIData.write_oifits(data, path)round-trips throughread_oifits, including
closure-only (AMI) data. The pipelines now writedata/processed.oifitsfor closure-only data and for data with
flagged visibilities. - No more alias-copy detections by default. On lattice uv coverage (aperture masks, DISCO),
BinaryPipelineandOrbitPipelinecap the default search box at half an alias cell. An explicit
max_sep_masis never capped. OrbitPipelinechecks its own verdict. It drops the night with the worst χ²/N, refits, and records
whether the verdict and the winning alias band survive inmodes.robustness.- Better-conditioned fits.
fit(precondition="diag")(provisional).- The L-BFGS history defaults to 100 vectors, up from 50.
- Maximum-entropy imaging takes 30–40% fewer steps.
- Radio CLEAN (
virgil.radio_clean, provisional). This is a Cotton–Schwab CLEAN ofComplexVisData, with
Högbom and multi-scale minor cycles, masks and a fitted restoring beam.
See the guide. - A public References page lists every source that
virgil cites or ports.
Upgrading from 0.4.0
Most code runs unchanged. Check these points:
| If your code… | In 0.4.1… | Do this |
|---|---|---|
fits with CalibrationNuisance and relies on its default priors |
gains and error scales have Jeffreys priors (fit coordinate ln x). Offsets are uniform over offset_beams beams (default 3): a narrower box, now enforced. |
Check info["at_bound"]. To restore the old boxes, pass priors(gain=, sigma=, offset=). |
calls fit with L-BFGS and no lbfgs_memory |
the history is 100 vectors (was 50), cut to stay under 1 GiB, with a warning. LM ignores it. | Pass lbfgs_memory= to choose a value. |
runs BinaryPipeline or OrbitPipeline on lattice coverage without max_sep_mas |
the default box is capped at half an alias cell, recorded in summary["search"]["alias_cap_mas"]. |
Pass max_sep_mas to search wider. |
calls write_oifits(tables=...) |
the argument is named data. |
Rename it. tables= warns until 0.5. |
calls OrbitPipeline(epochs, path) with the output path positional |
output is keyword-only. |
Write OrbitPipeline(epochs, output=path). The positional form warns until 0.5. |
calls ComplexVisData.dirty_beam(nuisance, values) |
values now comes before nuisance, as in dirty_image (a provisional API, no shim). |
Swap the arguments. |
reads virgil.name |
it is removed. | Use virgil.__version__. |
relies on import virgil loading matplotlib |
it no longer does. virgil.plotting and virgil.oifits load on first use. |
Import matplotlib yourself. |
sets t_ref = 0 on an orbit class |
it still warns, and the warning now says it becomes an error in 0.5. | Set a real reference epoch. |
Results and summary.json are otherwise unchanged.
Added
write_oifits(OIData). Errors, flags, wavelengths, (u, v) and times survive a round trip.- Closure phases without station indices get placeholder stations.
- Channel widths are written as
EFF_BAND; unknown widths are estimated and markedBANDEST. - Data whose channel widths differ between baselines of one channel are skipped, with the reason in
processed_oifits_skipped. 0.4.0 wrote them withEFF_BAND = 0.
OrbitPipelinedrop-a-night check (night_dropped). It is skipped below 3 nights or with
drop_outlier_night=False.fit(precondition="diag")andgauss_newton_diag(both provisional).MaxEntropy's entropy metric andMaxEntropy.residualslet LM fit an image, in float64 only.- Preconditioning does not make weak-weight fits converge in float32.
virgil.radio_clean(provisional):clean,RadioCleanResult,fit_beamandrestore.CalibrationNuisance(sigmas="profile")andComplexVisData.sigma_hat.lattice_alias_limitworks per channel.
Fixed
fit_orbit_aliasesno longer runs out of memory on data with gains and many nights. It accumulates the
Hessian in groups of nights that fitmemory_budget, and results agree to rounding.ImproperUniformpriors with anevent_shapeno longer raise ingauss_newton_mass.
Known issues
- Closure phases near a visibility null (#309). A closure
phase jumps by π where a model visibility passes through zero, so its gradient is unbounded there. An image fit
that drifts to such a null can stall. V² and the regularizers are unaffected. This was accepted for 0.4.0 and is
carried into 0.4.1; a fix is planned. - Radio is provisional.
virgil.radio_cleanand the radio visibility classes are not yet independently
checked against CASA. That work is the focus of 0.4.2.
Smaller and easier to read
Measured on src/virgil:
| 0.4.0 | 0.4.1 | |
|---|---|---|
| Docstrings over 60 lines | 19 | 0 (CI gate) |
| Docstrings over 40 lines | 63 | 30 |
| Docstring lines in total | 15,503 | 14,040 (−9%) |
| Private helpers called from one place | 456 | 418 (−8%) |
| Source lines in total | 56,804 | 56,790 |
- Docstrings.
- Every public docstring is 60 lines or fewer, enforced in CI, and is cut to its calling contract.
- Derivations moved to the docs (
orbit_methods, the guides). - Every caveat a caller needs survives, either as one docstring line or as a
# deliberate:comment at the
line it explains. - Sources are cited in one
Source:line linking to the References page.
- Private helpers. About 40 single-use helpers were inlined and duplicate helpers merged, with no change in
results. - Size. The source stayed the same length, although about 1,400 lines of features were added.
- Next. 0.4.2 goes further on single-use helpers.
Other
to_keplerruns as one jitted kernel, 2–6× faster for an eager call.- CI is faster: the orbit tests are sharded and timings come from CI runs.
Full details are in CHANGELOG.md.
v0.4.0
What's Changed
- Development carbon page, ledger and README badge by @benjaminpope in #253
- Add Claude PR review workflow by @benjaminpope in #254
- Design note: one argument order (model before data) for 0.4 by @benjaminpope in #255
- Add the private _deprecate shim for the 0.4 argument order by @benjaminpope in #256
- Orbits fitted to visibilities across epochs: design and stage 1 (Epochs, OrbitalBinary) by @benjaminpope in #257
- Add virgil.metrics: image-recovery scores for reconstructions by @benjaminpope in #258
- Add virgil.ensemble: PYRA/MYTHRA-style reconstruction ensembles by @benjaminpope in #259
- AGENTS.md: relevant tests locally, zensical docs, direct venv python by @benjaminpope in #260
- Model before data in the grid, limit and detection tools (argument order PR 2) by @benjaminpope in #261
- Model before data in the likelihoods; one name per argument (argument order PR 3) by @benjaminpope in #263
- Visibility orbits stage 2: rank_orbits, start from a positions fit, per-chain starts by @benjaminpope in #262
- Carry argument order PR 3 (#263) to main by @benjaminpope in #266
- CI: stop the py3.11 lowest job running out of memory by @benjaminpope in #264
- OIData.select by wavelength windows and observables; audit of the spectro-interferometry plan by @benjaminpope in #265
- Design note: plate-scale nuisances can absorb inter-instrument motion by @benjaminpope in #267
- ensemble: judge the running mean on the members' own grids by @benjaminpope in #272
- Argument order PR 4: last call sites and pytest filters (stacked on #266) by @benjaminpope in #269
- CI: drop the legacy mkdocs workflow; lint script uses the venv python by @benjaminpope in #271
- metrics: rms_convolved without pixel scale; clarify Lawson normalisation by @benjaminpope in #274
- Orbit tutorial: executed outputs for visibility orbits stage 2 by @benjaminpope in #273
- CI: two xdist workers on the lowest-deps job by @benjaminpope in #275
- mwe_sparse_imaging: match the summary to the latest run by @benjaminpope in #276
- Tests: clear JAX caches after each module (lowest-deps CI OOM) by @benjaminpope in #277
- Faster CI: lighter test_epochs starts, factorized TruncatedCone render by @benjaminpope in #278
- Design note: automatic binary orbits from multi-epoch interferometry by @benjaminpope in #279
- PR B0: orbit benchmark harness (benchmarks/orbits) by @benjaminpope in #280
- Orbits: noise-marginalised gap and scale-aware ranking (#268) by @benjaminpope in #281
- Avoid zero-size gain arrays that crash jax.pmap (parallel chains) by @benjaminpope in #282
- Fix main CI: NaN-robust ensemble selection, macOS-safe memory sampler by @benjaminpope in #283
- Orbit tutorial: outputs re-executed on main (0 divergences) by @benjaminpope in #284
- Orbit tutorial: outputs from rerun at 33e1390 by @benjaminpope in #285
- Add OzSTAR sync gate and stale-notebook-output check by @benjaminpope in #286
- Design: orbit-fitting review (published practice vs virgil's plan) by @benjaminpope in #287
- Drop unused limits.chi2ppf by @benjaminpope in #293
- epochs.marginal_loglike: adaptive ln s integral at a steep s_max bound (F18) by @benjaminpope in #295
- limits.chi2ppf: work for any df without tensorflow_probability (F17) by @benjaminpope in #291
- Fit budgets and guards; phase-coherent period grid (orbit review PR B) by @benjaminpope in #288
- epoch_positions: peak catalogue, refine the top grid peaks before committing (PR C) by @benjaminpope in #289
- Orbit search design (§14 revision) by @benjaminpope in #292
- read_oifits: place a T3 leg with no visibility row at the T3 coordinates by @benjaminpope in #300
- TruncatedCone: azimuthal modulation of the walls (leading-edge brightening) by @benjaminpope in #303
- ensemble: keep weights below the L-curve corner; widen weight ranges; fields 1–4× by @benjaminpope in #304
- SVI: virgil.svi.variational, flow guides started from a fit by @benjaminpope in #302
- Orbit search D1: shared-nuisance scorer (score_orbits) by @benjaminpope in #298
- virgil.pipeline PR 1: core, BinaryPipeline, docs and tutorial sections by @benjaminpope in #305
- pipeline: capture stage warnings as checks, quieten quicklook, clean tutorial sections by @benjaminpope in #308
- One import convention: jax.numpy as np, numpy as onp (#306 s1) by @benjaminpope in #312
- Use model/data in public signatures (#306 s2) by @benjaminpope in #313
- Oxford -ize spelling in names, with deprecated -ise keywords (#306 s3) by @benjaminpope in #314
- Warnings, precision and small inconsistencies (#306 s4, s5, s8) by @benjaminpope in #315
- Dead code and trivial dedups (part of #310) by @benjaminpope in #311
- Drifted copies unified, with regression tests (part of #310) by @benjaminpope in #316
- Render dev carbon badge as an image by @benjaminpope in #318
- StarPipeline: stellar diameter and limb darkening by @benjaminpope in #307
- Period aliases of undersampled orbits (fit_orbit_aliases) by @benjaminpope in #319
- aliases: robust to singular Hessians, per-band checkpoints, flat table scales by @benjaminpope in #321
- numpyro_model: sample each prior in its flat coordinate, as fit does by @benjaminpope in #301
- Import zodiax as zdx by @benjaminpope in #322
- Pre-0.4.0 review: StarPipeline model choice, pipeline and alias robustness, 0.3 regressions, de-spaghettification by @DePrinsT in #323
- Fix the slow-test failures on main; faster, steadier CI by @benjaminpope in #325
- Alias fit: sign-free seeds for near-equal binaries, Newton polish by @benjaminpope in #324
- L-BFGS restarts: fix the macOS-only error_scale="fit" test failure by @benjaminpope in #327
- Docs: comparison with other packages by @benjaminpope in #326
- Evidence helpers take several field Images (path=None or a tuple) by @benjaminpope in #330
- aliases: UD primary, per-epoch V² amplitude, flux flip, init warm start by @benjaminpope in #331
- Provenance: references file, ADS fetch script, docstring citations by @benjaminpope in #332
- contributors: indent nested bullets by 4 spaces so they render nested by @benjaminpope in #333
- docs: remove 'Using these docs' section from index by @benjaminpope in #334
- P1: PolarDisk engine (M=0) by @benjaminpope in #335
- PolarDisk P2: height, linear harmonics, TruncatedCone on the engine by @benjaminpope in #336
- CI: review-response label for unanswered Claude review points by @benjaminpope in #341
- Design note: composite scenes (identifiability toy) by @benjaminpope in #342
- Broadcast time-dependent models over samples; batch orbit candidates by default by @benjaminpope in #339
- spectra.Scaled, chromatic companion-search tests and tutorial by @benjaminpope in #346
- GN1: dense damped-Newton kernel (_gauss_newton.py) and the Gauss–Newton plan b...
virgil-astro 0.3.0
See CHANGELOG.md, section 0.3.0 (2026-10-06).
v0.2.0 - rename drpangloss to virgil
This is an extensively updated release with major changes - the first of which is the renaming of drpangloss to virgil, which better reflects the scope of our project beyond just a rehashing of CANDID.
New Features
What's changed
This release covers PRs #62–#68. It reorganises the package, adds composable and chromatic source models, and fixes a large batch of bugs found in code review.
⚠️ Breaking changes. Several functions were renamed or removed, andBinaryModelAngularnow takesflux(companion/primary) in place ofcontrast. There is no deprecation period. See Migration notes below.
Chromatic scenes and likelihood options (#68)
- Multi-file data:
read_oifitsandOIDataaccept a list of files and concatenate them. - Chromatic fluxes: new
drpangloss.spectramodule withPowerLaw(ratio, index, wavel0)as a component flux, giving SPARCO-styleV = Σ fᵢ(λ) Vᵢ / Σ fᵢ(λ). Resolvedcomponent: an over-resolved background flux that only adds to the normalisation.- Physicality checks:
SourceModel.is_physical()andSystem.is_physical()check flux positivity and non-negative rim profiles, and work under tracing. - Likelihood options:
model_loglike,loglikeandnumpyro_modeltakevis_error_rel,phi_error(added in quadrature) andreject_unphysical. render()now works underjax.jit, so you can render posterior samples inside jitted code.
Package restructure (#66, #67)
- One job per module:
likelihood.py,limits.py,bessel.py,amigo.py, with shared helpers in_geometry.py,_utils.pyand_grid.py. Legacy I/O (oifits_implaneia,savefits) moved todrpangloss.legacy. - Everyday names can be imported from the top-level package.
- One flux convention:
fluxalways means companion/primary. Reports use contrast = primary/companion and Δmag = 2.5 log₁₀(contrast), throughflux_to_contrast,flux_to_delta_magandunits="flux" | "contrast" | "delta_mag"in plots. - One grid-plot function:
plot_grid_map(values, grid, kind=...)andplot_contrast_curvereplace six older plotting functions. - Grid and limit functions take a
batch_size=keyword (default 256) to bound memory. - Bessel derivatives (#67):
j0,j1andbessel_jnhave custom JVP rules built on the recurrence identities. Gradients, Jacobians and Hessians are exact and stable, including at x = 0.
Code-review fixes, OIFITS I/O, docs and CI (#65)
- New OIFITS reader/writer:
drpangloss.oifits.read_oifits/write_oifitsneed only astropy. They support multiple wavelength channels, tables, epochs and targets, FLAG columns, absolute phases from OI_VIS, and T3-only files.OIDataaccepts a path or anyHDUList, withtarget=. - Data handling: flagged and non-finite samples are dropped from the observables, while their baselines stay available to closure phases. Phase residuals are wrapped into [-π, π) in every likelihood and χ². Pre-projected closure phases are no longer projected twice.
- Grid fitting: a single optimiser core with scaled flux, a Newton polish step, and warnings where points do not converge.
nsigmastays finite in float32.ruffio_upperlimitbroadcasts. - Inference: checks work under
jit, the Fisher matrix usesjacfwd, and a Fisher matrix with no information gets finite steps. - Plotting: importing no longer changes global rcParams (call
set_style()to opt in). Extents are pixel-centred and figures are returned. Applying the style no longer closes open Jupyter figures. cvis_binary(u, v, dra, ddec, flux)argument order now matches the othercvis_*functions.- CI tests Python 3.11 and 3.12 and builds the docs on every PR. Docs use mkdocstrings cross-references, and all tutorials were re-run.
Composable source models (#64)
Includes edits by Toon De Prins.
- Composable components combined into an explicit
System, with aflux_paramargument, positivity constraints and traced templates. There is a new composition tutorial. - New source models:
UniformDiskModelandModulatedGaussianRimModel. - Sky orientation: fixed the
BinaryModelAngularposition-angle sign (North-to-East), grid-plot orientation (East-left/North-up), inclined-rim uv deprojection, and a swapped-argument PA bug in the HMC/Fisher helpers. render()mixes components by integrated flux, not by peak height, and its Fourier transforms are tested againstmodel()visibilities.- Fewer recompiles and fast paths for composed models. Added a
numpyro_modelhelper.
Maintenance (#62, #63)
- Fixed the fork-PR lint job's checkout ref and resynced the tutorial docs.
Migration notes
| Old | New |
|---|---|
optimized_contrast_grid |
optimized_flux_grid |
laplace_contrast_uncertainty_grid(best_idx, data, model, grid) |
laplace_flux_uncertainty_grid(data, model, grid, flux=None) |
absil_limits(...) |
absil_limits(data, model, grid, sigma) |
ruffio_upperlimit (in grid_fit) |
drpangloss.limits.ruffio_upperlimit |
azimuthalAverage, radial_limit_summary |
limits.radial_profile |
plot_likelihood_grid, plot_contrast_limit_map, plot_optimized_and_grid, plot_optimized_and_sigma |
plot_grid_map(values, grid, kind=...) |
plot_radial_limit_summary, plot_contrast_limits |
plot_contrast_curve |
BinaryModelAngular(..., contrast=c) |
BinaryModelAngular(..., flux=1/c) |
OIData.standardize_data / standardize_errors / flatten_model / unpack_all |
flatten_data, standardize_model |
drpangloss.oifits_implaneia, drpangloss.savefits |
drpangloss.legacy.* (or the new drpangloss.oifits) |
GRID_BATCH_SIZE global |
batch_size= keyword |
models.HarmonixAdapter, model_class alias, models re-exports |
removed |
Grid tools now find the flux parameter as the one key ending in flux, or you can pass flux_param=. The old key-order fallback is gone.