Astronomical Visibility Components
- class tabascal.components.ast_vis.DiscreteSkyVis[source]
Visibilities of a discrete sky by direct DFT, with the full w term.
Reads
ast_radec(n_src, 2) radians,ast_I(n_src, n_freq) Jy andast_shape(n_src, 3) radians from the state — seeFixedDiscreteSky, which must be listed before this component — and ACCUMULATES intovis_astusing the visibility equationV(u,v,w) = sum_k I_k G_k(u,v) exp(-2i pi (u l_k + v m_k + w (n_k - 1)) / lambda)
For a discrete sky the direct sum is exact, gridless, differentiable, and unaffected by field of view or baseline length; “discrete” is the set of sources, not their size, so
ImageSkyVisis reserved for a sky carried as an image.I_kenters undivided: there is no1 / n. The RIME integrand carriesB / nbecausedOmega = dl dm / n, but a source of integrated fluxSisB = S delta_Omegaanddelta_Omega = n delta(l) delta(m), so the Jacobian cancels and a source contributes its catalogue flux exactly, in every direction.(u, v, w)is the ANTENNA2 - ANTENNA1 baseline the equation above is written for, which isast.uvw_signtimes the UVW column. Which baseline that column holds is a property of whatever wrote the data, so the sign is a config key; the default,(-1, -1, -1), is the convention tab-sim writes — seesetup.G_kis the uv-plane envelope of an elliptical Gaussian source,G(u,v) = exp(-pi^2 / (4 ln 2) * (a^2 u’^2 + b^2 v’^2))
for FWHM
a(major) andb(minor) in radians, with(u', v')the baseline in wavelengths rotated into the source frame:u' = u sin(phi) + v cos(phi) along the major axis v' = u cos(phi) - v sin(phi) along the minor axis
phiis the position angle in the radio convention, measured from north (the m axis) through east (the l axis), sophi = 0puts the major axis north-south and a north-south baseline is the one that resolves the source out. A zero FWHM givesG = 1exactly, so points and Gaussians are the same code path.It accumulates rather than assigns, so it composes with the astronomical GP: with both listed,
vis_astis the GP plus the fixed sources.Modelzeroesvis_astbefore the forward chain runs, so the two may be listed in either order.The source axis is walked in blocks of
ast.source_block_sizewithjax.lax.scan(), and the block body is rematerialised: the delay array is (n_bl, n_time, n_src), which for a real catalogue is the largest array in the model, and blocking replacesn_srcin that shape with the block size at the cost of recomputing each block in the backward pass.Sources more than 90 degrees from the phase centre are modelled, not rejected: only
n - 1enters the phase andradec_to_lmn()computes it exactly over the whole sphere, so nothing here breaks down atn <= 0. Such a source is almost always a catalogue mistake rather than a real one, soFixedDiscreteSkywarns about it at setup and leaves the decision to the caller.A fixed sky exists to make a per-antenna gain identifiable (see issue #124); the flux scale it fixes the gain against is only physical if the data are calibrated to Jy.
- class tabascal.components.ast_vis.GPVisAst[source]
- build_block_signal()[source]
The latent-to-signal transform of one block of baselines.
Split out so a subclass can replace the transform and nothing else: the priors, the initialisation and the blocking above are the same whichever way the modes are turned into a signal.
- build_constants()[source]
Return arrays that do not change during the forward pass.
Returns a dict of array_name -> array_value. These will be stored in constants as “_c/<ClassName>/array_name” by Model.__init__.
- build_forward()[source]
Return pure, JIT-compatible function
The baseline axis is walked in blocks of
ast.baseline_block_sizerather than vmapped whole.latent_to_signalpads the latent block up to the padded k-grid, shifts, inverse-transforms and crops back, so a vmap over every baseline holds(n_bl, n_freq_pad, n_time_pad)three times over – at the default padding each axis is about twice the data one, so about four times the elements of the visibilities, and the crop throws all of that away. The scan replacesn_blin that shape with the block, which is the whole of the term this component contributes to peak memory.There is deliberately no
checkpointon the body, unlike the RFI components’ scans. The chain from the latent parameters tovis_astis affine – an elementwisesigma * base + mu, then pad, shift, ifftn and crop, every one of them linear – so reverse mode is its transpose and stores no primal intermediates to begin with. Issue #153 records the measurement; a remat here would be a no-op today and a silent recomputation if the chain ever stopped being linear.The affine transform runs inside the body, on the block, so that neither it nor the padded grid is ever formed for every baseline at once.
With baseline sharding, the scan runs inside each device’s shard. A global scan reshapes the baseline axis into (block, row); XLA can then gather the entire latent arrays and the visibility cotangent even if the component’s input and output layouts both divide baselines. Local padding and reshaping keep that work on the owning device.
- class tabascal.components.ast_vis.GPVisAstDFT[source]
GPVisAstwith the padded grid never formed.The prior, the initialisation and the blocking over baselines are the parent’s; only the step from the surviving Fourier modes to the visibilities differs.
latent_to_signal()pads those modes back up to the padded grid, inverse-transforms the whole of it and crops – at the default padding, about four times the elements of the visibilities formed and thrown away, per block, in the forward pass and again in reverse. The chain is linear and separable, so the same values come out of one small matrix per axis (latent_to_signal_dft()), and the largest thing formed is the size of the visibilities.The modes of this component are set by the array’s geometry rather than by a choice, so the matrices are fixed at setup and shared by every baseline.
Select it where the parent would go:
model: components: - ast_vis:GPVisAstDFT
- tabascal.components.ast_vis.radec_to_lmn(ra, dec, ra0, dec0)[source]
Direction cosines of sources at
(ra, dec)about a phase centre, in radians.Returns
(l, m, n, n - 1).n - 1is returned alongsidenbecause it is the quantity the w term actually needs and the two cannot both be computed accurately from one expression.nis the exact spherical formsin(d) sin(d0) + cos(d) cos(d0) cos(da)rather thansqrt(1 - l^2 - m^2). The square root is the cosine of the angular distance only on the near hemisphere: it is unsigned, so it folds a source more than 90 degrees from the phase centre back onto the near side instead of giving it the negativenit has.n - 1uses the haversine identityn - 1 = -2 hwithh = sin^2((d - d0)/2) + cos(d) cos(d0) sin^2(da/2), the haversine of the angular distance.hruns over[0, 1]across the whole sphere — 0 at the phase centre, 1 at the antipode — son - 1runs over[-2, 0]andn < 0exactly whenh > 1/2. Subtracting a nearbynfrom 1 cancels catastrophically: at a 40 arcsec offset1 - n ~ 2e-8, which in single precision is below the spacing of the floats either expression lands on, so the difference comes out as exactly zero and the w term disappears. The haversine form never forms the difference, so it keeps full relative accuracy at any offset.