Skip to content

virgil.observables

Spectro-interferometric observables that follow the visibilities and phases in an OIData data vector: OI_FLUX spectra (absolute up to a grey scale, or normalized), correlated fluxes, |V| beside V², triple amplitudes, and continuum-normalized differential phases. Read them with read_oifits(..., extras=...) or OIData(path, extras=...); the guide is Spectro-interferometric observables.

Spectro-interferometric observables beside V² and closure phase.

OIData keeps its visibilities (vis) and phases (phi) as before. The observables here are further blocks of the same data vector, in OIData.extras, read with read_oifits(..., extras=...) (see read_oifits). Each block predicts its data from the model and whitens its own residuals, so the likelihood keeps one whitened residual vector:

  • "flux": OI_FLUX as a spectrum known up to a grey scale, F(λ) = k Σ fᵢ(λ) from total_spectrum (FluxSpectrum);
  • "nflux": OI_FLUX normalized to its continuum, F(λ)/F_c(λ), the same with the model normalized over the continuum channels;
  • "corrflux": correlated fluxes (VISAMP with AMPTYP = 'correlated flux'), k |Σ fᵢ(λ) Vᵢ|, up to a grey scale;
  • "visamp": |V| (VISAMP, AMPTYP = 'absolute') beside V² (VisibilityAmplitude);
  • "t3amp": triple amplitudes |V_ab V_bc V_ac| (TripleAmplitude);
  • "visphi": differential phases, the exact arg V(λ) with the pipeline's continuum normalization applied as a linear operator (DifferentialPhase).

Grey scales are marginalized, not fitted. A spectrum's scale k (and an optional polynomial in λ times the spectrum) enters linearly, so with a Gaussian prior it integrates out in closed form (Luger, Foreman-Mackey & Hogg 2017; virgil._linear): the data are Gaussian with covariance D + A Λ Aᵀ about A μ, whitened by the same successive rank-one steps as the 6d gains, with the log-determinant (which depends on the model's spectral shape) kept in the effective errors. The conditional posterior of k is flux_scale_posterior.

The prior on k is stated, never taken from the data. Give it as scale=(mean, sd) in the data's units (e.g. Jy), with OIData.with_flux_scale. Normalized spectra ("nflux") default to (1, 0.1). The Gaussian is a proposal: k is a positive scale, whose Jeffreys prior is 1/k on stated bounds. The two differ by about σ_k/k, which is negligible for a well-measured spectrum. For the Jeffreys posterior, reweight samples of k from the conditional posterior by 1 / (k N(k; mean, sd²)) within the bounds, or sample log k under a log-uniform prior directly, as a model parameter rather than marginalized. Make no evidence claims that depend on the Gaussian's width.

Differential phases. A pipeline's differential phase is arg V minus a fit of a + b/λ (an offset and a delay) over continuum channels, per baseline and frame. That fit is a linear operator N = I − L on the phases of one row (continuum_operator), so virgil applies the same N to the exact model phase, never the photocentre approximation, which fails for resolved structure. If our basis contains the pipeline's, N N_pipe = N, so the data may be re-normalized safely. Their covariance is N D Nᵀ, which is whitened as a dense block per frame. Projecting out a linear basis is the flat-prior limit of marginalizing those nuisances: the likelihood of N d with covariance N D Nᵀ is the same whichever projection with that null space is used (restricted maximum likelihood).

Without double counting closure phases (the default of the design note, S §2.3): with closure phases in the data, each frame's baseline phases are also projected onto the telescope-differenced subspace φ_ab = a_a − a_b, orthogonal to every closure, and only channels in the line windows are kept. The closure phases (everywhere) and this closure-free part of the differential phase (in the lines) then measure different things. The cross-covariance between them is neglected as an approximation: it is exactly zero only when the baseline errors of a frame and channel are equal. For unequal errors it is nonzero, and the independent-block likelihood is not exact. The joint covariance is preferred when available.

FluxSpectrum

Bases: _Block

A spectrum known up to a grey scale: OI_FLUX or correlated fluxes.

The model of sample i (wavelength λᵢ, scale group gᵢ) is

mᵢ = Σ_j w_j tᵢ xᵢʲ,   w ~ N((μ, 0, ...), diag(s, τ_1 μ, ...)²),

with t the model's template: the total spectrum Σ fᵢ(λ) ("flux"), the same divided by its continuum fit per row ("nflux"), or the total spectrum times |V| ("corrflux"), normalized to a mean of 1 per scale group (except "nflux", which is already normalized). x is λ scaled to [-1, 1] across the group, so w_0 = k is the grey scale and w_j (j ≥ 1) an optional polynomial. (μ, s) is the stated prior on k, in the data's units (the same for every group; (1, 0.1) by default for "nflux"), and τ the polynomial's widths relative to μ. The prior is a proposal for the Jeffreys 1/k (see the module notes). The weights are marginalized analytically.

Choice of prior. k is a scale parameter. Under rescaling of k the invariant (Jeffreys) prior is ∝ 1/k, uniform in log k, but that prior is improper, so an evidence computed with it is undefined. The broad Gaussian used here instead approximates a prior uniform in k. The only claim made is local: when k is sharply measured (σ_k/k ≪ 1, as for any useful OI_FLUX spectrum), the factor 1/k varies by only a fraction σ_k/k across the likelihood's width, so the posteriors of k and of the other parameters are insensitive to the choice. Evidence comparisons need a proper prior: finite positive bounds [k_min, k_max], with density 1 / (k ln(k_max/k_min)). With such bounds, that log-uniform prior is the Jeffreys choice under the rule for scale groups. Marginalizing in log k is not linear and is not done here (a follow-up).

Build with :meth:build; change the prior, groups and widths with OIData.with_flux_scale.

wavel instance-attribute

sample instance-attribute

row instance-attribute

frame instance-attribute

station instance-attribute

mjd instance-attribute

group instance-attribute

members instance-attribute

count instance-attribute

poly instance-attribute

mu instance-attribute

cont_rows instance-attribute

cont_basis instance-attribute

cont_fit instance-attribute

kind = eqx.field(static=True) class-attribute instance-attribute

widths = eqx.field(static=True) class-attribute instance-attribute

scale = eqx.field(static=True) class-attribute instance-attribute

poly_width = eqx.field(static=True) class-attribute instance-attribute

per = eqx.field(static=True) class-attribute instance-attribute

continuum = eqx.field(static=True) class-attribute instance-attribute

continuum_order = eqx.field(static=True) class-attribute instance-attribute

model_dependent_covariance = True class-attribute instance-attribute

build(kind, values, errors, wavel, row, frame, station, sample=None, mjd=None, per='dataset', scale=None, poly_order=0, poly_width=0.1, continuum=None, continuum_order=0) classmethod

Build from per-sample arrays (see the class notes).

Parameters:

Name Type Description Default
kind (flux, nflux, corrflux)
"flux"
values array - like

The data and their uncertainties, one per sample.

required
errors array - like

The data and their uncertainties, one per sample.

required
wavel array - like

Each sample's wavelength (metres).

required
row array - like

Each sample's row (one spectrum), frame (exposure) and station (a telescope's STA_INDEX, or a baseline's pair).

required
frame array - like

Each sample's row (one spectrum), frame (exposure) and station (a telescope's STA_INDEX, or a baseline's pair).

required
station array - like

Each sample's row (one spectrum), frame (exposure) and station (a telescope's STA_INDEX, or a baseline's pair).

required
sample array-like of int

For "corrflux", the visibility sample of each value.

None
mjd array - like

Each sample's time (days), for splitting by epoch.

None
per (dataset, row, frame, station)

One grey scale for all the samples (default), or one per row, frame or station (telescope or baseline).

"dataset"
scale (float, float)

The prior (mean, sd) of the grey scale k, in the data's units, stated rather than taken from the data. Required for "flux" and "corrflux" before the likelihood is evaluated; (1, 0.1) by default for "nflux".

None
poly_order int

Also marginalize a polynomial in λ of this order times the template (default 0: a grey scale only).

0
poly_width float

Prior width of each polynomial coefficient, relative to the scale's prior mean.

0.1
continuum sequence of (lo, hi)

For "nflux": the continuum ranges (metres) its model is normalized over, per row (default: every channel).

None
continuum_order int

For "nflux": 0 (a mean, the default) or 1 (a mean and a slope in wavenumber).

0

rebuild(values=None, errors=None, keep=None, **settings)

The same block with new settings, data, or only some samples.

subset(keep, new_index, flux_keep=None)

predict(model, cvis)

whiten(prediction, data, errors)

posterior(prediction, data, errors)

Conditional posterior of the weights w per group: mean and cov.

mean[g, 0] is the grey scale k of group g, multiplying the template; mean[g, j] (j ≥ 1) the polynomial coefficients. The Gaussian prior is a proposal for k's Jeffreys prior (see the module notes).

simulated(prediction, cvis, noise, key=None)

VisibilityAmplitude

Bases: _Block

|V| at samples, e.g. VISAMP beside VIS2DATA ("visamp").

V² and |V| from the same measurement are not independent; reading both counts it twice unless the pipeline measured them separately.

sample instance-attribute

kind = eqx.field(static=True, default='visamp') class-attribute instance-attribute

predict(model, cvis)

subset(keep, new_index, flux_keep=None)

TripleAmplitude

Bases: _Block

|V_ab V_bc V_ac| of closure triangles (T3AMP, "t3amp").

i1 instance-attribute

i2 instance-attribute

i3 instance-attribute

kind = eqx.field(static=True, default='t3amp') class-attribute instance-attribute

predict(model, cvis)

subset(keep, new_index, flux_keep=None)

DifferentialPhase

Bases: _Block

Continuum-normalized phases (VISPHI, "visphi").

values and errors are the per-sample phases (radians, in the orientation of the visibility samples) and their uncertainties, as read. Per frame, the phases of each baseline (a row of channels) are unwrapped along wavelength and mapped by

Y = Qᵀ Φ Wᵀ,

where W holds the line-window rows of N = I − L (continuum_operator) rotated onto independent combinations, and Q is an orthonormal basis of the telescope-differenced phases (closure-free; with closure phases in the data) or the identity. The data vector is Y of the data, the covariance (Q ⊗ W) D (Q ⊗ W)ᵀ, whitened by its Cholesky factor per frame. Build with :meth:build, or change the windows with OIData.with_continuum.

With prior_width, the offset and slope of every baseline and frame are instead marginalized under a finite Gaussian prior (the projection is its flat limit): W keeps the channels of both windows unchanged, each channel's closure-free combinations are whitened for their covariance Qᵀ D Q, and the offsets and slopes are whitened out as low-rank modes per frame, by the 6d gains' blocks (virgil.gains). Their log-determinant does not depend on the model.

Residuals are treated as linear (Gaussian), which holds while the differential phases are well below π, as they are after removing the continuum.

sample instance-attribute

wavel instance-attribute

frame instance-attribute

stations instance-attribute

row instance-attribute

grid instance-attribute

chan instance-attribute

q instance-attribute

w instance-attribute

valid instance-attribute

keep instance-attribute

basis instance-attribute

continuum = eqx.field(static=True) class-attribute instance-attribute

lines = eqx.field(static=True) class-attribute instance-attribute

order = eqx.field(static=True) class-attribute instance-attribute

closure_free = eqx.field(static=True) class-attribute instance-attribute

prior_width = eqx.field(static=True, default=None) class-attribute instance-attribute

kind = eqx.field(static=True, default='visphi') class-attribute instance-attribute

chol = None class-attribute instance-attribute

n_independent property

build(values, errors, sample, wavel, frame, stations, row=None, continuum=None, lines=None, order=1, closure_free=True, prior_width=None) classmethod

Build from per-sample phases (see the class notes).

Parameters:

Name Type Description Default
values array - like

Phases (radians) and uncertainties, one per sample.

required
errors array - like

Phases (radians) and uncertainties, one per sample.

required
sample array-like of int

The visibility sample of each phase.

required
wavel array - like

Each phase's wavelength (metres), frame, and station pair.

required
frame array - like

Each phase's wavelength (metres), frame, and station pair.

required
stations array - like

Each phase's wavelength (metres), frame, and station pair.

required
row array-like of int

Which phases form one spectrum (one baseline of one frame); by default, those with the same frame and station pair.

None
continuum sequence of (lo, hi)

Wavelength ranges (metres). The continuum is fitted over the continuum channels and the line channels are kept. Either defaults to the complement of the other; with neither, every channel is both.

None
lines sequence of (lo, hi)

Wavelength ranges (metres). The continuum is fitted over the continuum channels and the line channels are kept. Either defaults to the complement of the other; with neither, every channel is both.

None
order int

1 (default): subtract a mean and a slope in wavenumber (an offset and a delay); 0: a mean.

1
closure_free bool

Keep only the telescope-differenced part of each frame's phases, orthogonal to the closure phases (default True; use it whenever the data have closure phases).

True
prior_width float or (float, float)

Marginalize the offset (and slope, per unit of the scaled wavenumber, which spans 1 across the channels) of each baseline and frame under Gaussian priors of these widths (radians), over the channels of both windows, instead of projecting them out (None, the default, as the pipeline does).

None

rebuild(values=None, errors=None, keep=None, **settings)

The same block with new settings, data, or only some samples.

subset(keep, new_index, flux_keep=None)

data()

predict(model, cvis)

covariance(errors=None)

Per frame, the covariance of the outputs, (F, K R, K R).

Padded outputs get unit variance, so the matrices are invertible.

with_errors(errors)

data_errors()

whiten(prediction, data, errors)

simulated(prediction, cvis, noise, key=None)

continuum_operator(wavel, continuum, order=1)

The continuum fit of one spectrum, as a matrix.

Parameters:

Name Type Description Default
wavel array - like

The channels' wavelengths (metres).

required
continuum array-like of bool

Which channels are continuum.

required
order int

0 fits a mean; 1 (the default) a mean and a slope in wavenumber 1/λ, which is an offset and a delay for a phase.

1

Returns:

Type Description
ndarray

L of shape (n, n): L @ x is the least-squares fit of the polynomial to x over the continuum channels, evaluated at every channel. A differential phase is (I - L) @ φ; a normalized spectrum is F / (L @ F).

in_ranges(wavel, ranges)

Whether each wavelength lies in one of ranges, [(lo, hi), ...].