RFI Signal Components
- class tabascal.components.rfi_signal.BaseGPRFI[source]
- build_mask_constants() dict[source]
Constants the signal mask needs, or
{}when nothing is masked.Kept separate from each component’s own
build_constantsso the None-check lives in one place. The mask is a constant rather than a closed-over array because it is indexed byn_rfi:distributed.pyshards constants named inRFI_AXIS_NAMESalong the source axis, and a captured array would instead be replicated, pullingrfi_Aback to a full copy on every device.
- build_masked_signal() Callable[source]
Return the signal-domain mask to apply at the end of a forward.
Sibling of
masked_forward_transform(), which zeroes the padded dummy sources in the latent k-space. A time window cannot be expressed there: zeroing global Fourier modes cannot produce a time-limited signal, so the elevation mask has to be applied torfi_Aafterlatent_to_signal.Both follow the same contract – a base-class hook every component applies unconditionally, which degrades to the identity when there is nothing to mask (no elevation cut here, a single device there). Resolving the branch here rather than inside the traced function means a run without an elevation cut emits no mask op at all and pays nothing.
The returned function takes any array whose leading axis is
n_rfiand whose trailing axis isn_time_fine, so a component that keeps the antenna axis broadcast rather than materialised can mask the smaller(n_rfi, n_freq_fine, n_time_fine)array before expanding it.
- default_gammas = [3, 3]
Roll-off exponent of the RFI prior power spectrum on the frequency and time axes, used when
rfi.gp_cov.gammasis not set. Declared per component because the two Fourier components have never agreed on it: the difference looks historical rather than intentional (#111), and is preserved here rather than unified, which would be a model change for one of them. Written as ints, which is what these were before they were configurable: jax routes an integer exponent throughlax.integer_powand a float one throughlax.pow, and the two differ in the last bit. That is far below anything the model cares about, but it is the difference between “the default path is unchanged” and “the default path is nearly unchanged”.
- default_pk_cutoff = 1e-09
Relative power below which a k-mode is dropped from the latent grid, used when
rfi.cutoffis not set. Sets the latent dimension, so a change here changes the number of fitted parameters.
- gp_cov_params()[source]
(gammas, cutoff)for this component: the config’s, else its own.gammascomes from the covariance block andcutofffrom the section around it, which is where it belongs: it decides how many modes are fitted rather than what the prior believes, and the spectrum is normalised after the cut so it does not set the width either.Resolved once in
setup()rather than on each call, so that a cutoff of 1 – which cuts every mode and leaves nothing to fit – is refused alongside every other configuration error rather than on the first forward pass, now that the key no longer sits in the blockrfi_signal_config_validation()checks.
- class tabascal.components.rfi_signal.ComplexRFIConstAnt[source]
- 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__.
- default_gammas = [100.0, 100.0]
Roll-off exponent of the RFI prior power spectrum on the frequency and time axes, used when
rfi.gp_cov.gammasis not set. Declared per component because the two Fourier components have never agreed on it: the difference looks historical rather than intentional (#111), and is preserved here rather than unified, which would be a model change for one of them. Written as ints, which is what these were before they were configurable: jax routes an integer exponent throughlax.integer_powand a float one throughlax.pow, and the two differ in the last bit. That is far below anything the model cares about, but it is the difference between “the default path is unchanged” and “the default path is nearly unchanged”.
- default_pk_cutoff = 1e-06
Relative power below which a k-mode is dropped from the latent grid, used when
rfi.cutoffis not set. Sets the latent dimension, so a change here changes the number of fitted parameters.
- class tabascal.components.rfi_signal.ComplexRFIVarAnt[source]
- 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 latent-to-signal transform is scanned over antennas rather than vmapped. A double vmap over
(n_rfi, n_ant)lowers to a single batched cuFFT ofn_rfi * n_anttransforms on the zero-padded grid, and cuFFT sizes its plan work area for the whole batch. At 32 channels that reached a 12.6 GiB request which aborted the process from inside XLA – aCheck failure, not a catchable Python OOM, so there was no graceful degradation. Scanning the antenna axis reduces that batch byn_ant.checkpointon the body is load-bearing rather than decorative:lax.scanstacks the body’s residuals across iterations for reverse-mode AD, which would rebuild much of what the vmap was holding, so without it the scan fixes the cuFFT plan and not the autodiff tape.Measured on a 64-antenna / 32-channel / 4-satellite problem, single precision: peak device memory 35.80 -> 14.62 GB (2.45x) for a 4% runtime cost, with the optimised chi^2 unchanged to ~6 significant figures.
- supersample = True
Whether the inverse transform supersamples onto the fine grid. The data-grid twin,
ComplexRFIVarAntCoarse, turns it off.
- class tabascal.components.rfi_signal.ComplexRFIVarAntCoarse[source]
ComplexRFIVarAntwith the supersampling left out.The same latent, prior, parameters and initialisation; only the inverse transform differs, landing on the data grid
(n_rfi, n_ant, n_freq, n_time)instead of the fine one. The value it gives a cell is the fine-grid signal at that cell’s own sample, so where the two grids meet the two components agree exactly; the fine samples in between are rebuilt from the data grid insidePolyInterpVis.- supersample = False
Whether the inverse transform supersamples onto the fine grid. The data-grid twin,
ComplexRFIVarAntCoarse, turns it off.
- tabascal.components.rfi_signal.read_light_curves(est_path: str, norad_ids: List[int], times_mjd_utc: NDArray, freqs: NDArray) Array[source]
Read an RFI light curve estimate onto the observation grid.
File structure
A
.zarrstore (read withxarray.open_zarr()) or a.npz, holdinglight_curves(n_src, n_time, n_freq), real. One light curve per source, the magnitude|S|, in the same units the RFI visibility amplitude is squared from. A complex array is rejected rather than truncated: the complex estimate belongs inlight_curves_complex, whichtabascal light-curvewrites alongside and this reader ignores.norad_ids(n_src,). NORAD id labelling each row oflight_curves.times(n_time,). UTC Modified Julian Date, in days, strictly increasing. UTC and not “whatever the measuring observation declared”: a Julian day number is a number until a scale says what it counts, and an MS may declare TAI in itsTIMEcolumn, which is 37 s from the instant the same number names on UTC.freqs(n_freq,). Frequency in Hz, strictly increasing.
In the zarr form the last three are coordinates of
light_curves.Optionally, and written by
tabascal light-curve:time_scale"utc", stamping the scaletimesis on. A store attribute in the zarr form, an array in the npz. Any other value is refused rather than converted. A file that omits it is read as UTC with a warning: files written before the stamp existed tooktimesfrom the MS’sTIMEcolumn as declared, so one written from a TAI-declared MS is 37 s out and indistinguishable from a correct one – regenerate those.
The four are all required. The format is deliberately strict: this is the interchange standard between tabascal and whatever measures the light curves, and every loose alternative it could accept instead fails silently. Matching rows by position rather than by id attaches a curve to the wrong satellite without changing its shape; assuming the file’s sampling matches the observation’s resamples it wrongly by an unknown amount. Neither shows up as an error, only as a worse fit.
Times are absolute (MJD on a stated scale) rather than seconds from the start of a particular observation, so a light curve is interpretable on its own and can be reused across measurement sets covering the same pass. Both halves matter: an axis on no stated scale would be reusable only against a measurement set that happened to declare the same one.
Resampling
Light curves are interpolated linearly onto
times_mjd_utcandfreqs.times_mjd_utcis the observation’s own times as UTC MJD –tabascal.time.to_utc_mjd()of the MS’s column, not the column itself, which is on whatever scale the MS declared. Samples outside the file’s coverage are zero, on either axis – the file says nothing there, which is the same “no signal known” convention the elevation mask uses. An axis of length 1 is held constant instead, since a single sample carries no gradient to interpolate along.Partial coverage
Satellites with no light curve in the file are zero, so an estimate only has to cover the satellites it was actually measured for rather than every satellite in the fit. Those are named in a warning. It is an error for no configured satellite to be found, which otherwise silently degrades the whole estimate to zeros.
- returns:
Light curves on the observation grid, in
norad_idsorder, with unmatched satellites zero and NaNs replaced by zero.- rtype:
Array (n_rfi, n_freq, n_time)
- tabascal.components.rfi_signal.rfi_signal_config_validation(rfi_config: Dict, vis_obs: Array, freqs: Array, chan_width: float, times: Array, int_time: float, gain_flags: Array = None) Dict[source]
Validate and set defaults of BaseGPRFI class parameters in the configuration file.
- Parameters:
rfi_config (Dict) – RFI configuration dictionary
- Returns:
Validated configuration dictionary with defaults set.
- Return type:
Dict
- Raises:
ValueError – Raised when an invalid input is provided for one fo the configuration parameters.