rm_lite.utils.clean

RM-CLEAN utils

Attributes

Classes

CleanLoopResults

Results of the RM-CLEAN loop

MinorLoopArrays

Arrays for the RM-CLEAN minor loop

MinorLoopOptions

Options for the RM-CLEAN minor loop

MinorLoopResults

Results of the RM-CLEAN minor loop

MultiscaleOptions

Options for multiscale RM-CLEAN.

RMCleanOptions

Options for RM-CLEAN, shared by the 1D and 3D tools

RMCleanResults

Results of the RM-CLEAN calculation

RMSynthArrays

Arrays for RM-synthesis

ScaleKernels

Per-scale RMSF responses for one spectrum (Offringa & Smirnov 2017).

Functions

_build_scale_masks(...)

Phase-2 per-scale masks: each scale's phase-1 centres dilated by

_classify_hybrid_source(→ MultiscaleOptions)

Classify the source once on the DIRTY spectrum for "hybrid" selection.

_halfmax_width_radm2(→ float)

Half-max width of a peaked profile; on |RMSF| this is the measured

_hybrid_scale_selection(→ int)

Width-gated snr selection.

_multiscale_minor_cycles(...)

One phase of multiscale minor cycles, restricted to allowed_supports.

_offsource_rms(→ float)

Robust (MAD) RMS of the residual outside the current mask region.

_reconvolve_model(→ numpy.typing.NDArray[numpy.complex128])

Footprint of a sparse delta model in the dirty FDF: sum_i m_i * RMSF@i.

_restore_multiscale(...)

Convolve the model with a unit-peak clean beam.

_seed_mask(→ numpy.typing.NDArray[numpy.bool_])

Tight adaptive-mask seed: just the brightest allowed channel, if it clears

adaptive_scale_supports(...)

Opening-filter scale supports (eye-patch flint-crew, 1D RM analogue).

compute_scale_kernels(→ ScaleKernels)

Precompute the per-scale RMSF responses for one spectrum.

convolve_fdf_scale(...)

Convolve an FDF (complex or real) with a real scale kernel.

default_scales(→ numpy.typing.NDArray[numpy.float64])

Scales (RMSF FWHM units): explicit if given, else WSClean-style auto.

find_significant_scale(→ int)

Index of the most-significant scale (Offringa & Smirnov 2017).

gaussian_scale_kernel_function(...)

Gaussian scale kernel; sigma = (3/16) * scale (Offringa & Smirnov 2017).

hanning(→ numpy.typing.NDArray[numpy.float64])

Hanning window function.

make_fine_scales(→ numpy.typing.NDArray[numpy.float64])

Scale set: 0, 3, then geometric doubling from 6 to max_scale.

minor_cycle(→ CleanLoopResults)

Single-scale CLEAN one spectrum: an initial masked loop to threshold, then

minor_loop(→ MinorLoopResults)

Hogbom minor loop: shift-subtract the RMSF at each residual peak within the

multiscale_clean_spectrum(...)

Multiscale CLEAN one FDF spectrum (Offringa & Smirnov 2017).

restore_model(→ numpy.typing.NDArray[numpy.complex128])

Restore a delta model with a unit-peak clean beam (one pass).

rmclean(→ RMCleanResults)

Validate array shapes and CLEAN each pixel (single- or multi-scale).

shift_rmsf(→ numpy.typing.NDArray[numpy.complex128])

Roll the double-length RMSF so its peak sits at FDF channel fdf_index,

tapered_quad_kernel_function(...)

Tapered quadratic scale kernel (Offringa & Smirnov 2017, eq. 2).

Module Contents

class rm_lite.utils.clean.CleanLoopResults

Bases: NamedTuple

Results of the RM-CLEAN loop

clean_fdf_spectrum: numpy.typing.NDArray[numpy.complex128]

The cleaned Faraday dispersion function cube

iter_count: int

The number of iterations

model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128]

The clean components cube

resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128]

The residual Faraday dispersion function cube

class rm_lite.utils.clean.MinorLoopArrays

Bases: NamedTuple

Arrays for the RM-CLEAN minor loop

phi_arr_radm2: numpy.typing.NDArray[numpy.float64]

Faraday depth array in rad/m^2

phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64]

Double-length Faraday depth array in rad/m^2

resid_fdf_spectrum_mask: numpy.ma.MaskedArray

Residual Faraday dispersion function spectrum

rmsf_fwhm: float

FWHM of the RMSF

rmsf_spectrum: numpy.typing.NDArray[numpy.complex128]

RMSF spectrum

class rm_lite.utils.clean.MinorLoopOptions

Options for the RM-CLEAN minor loop

gain: float

Loop gain

mask_threshold: float

Amplitude below which a channel is not cleaned

max_iter: int

Maximum number of iterations

noise: float | None = None

FDF noise floor for the adaptive off-source auto-mask; None disables it (fixed mask, legacy behaviour).

start_iter: int = 0

Starting iteration

stopping_threshold: float

Peak amplitude at which the loop stops

update_mask: bool = True

Update the mask after each iteration

class rm_lite.utils.clean.MinorLoopResults

Bases: NamedTuple

Results of the RM-CLEAN minor loop

iter_count: int

The number of iterations

model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128]

The clean components cube

resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128]

The residual Faraday dispersion function cube

resid_fdf_spectrum_mask: numpy.ma.MaskedArray

The masked residual Faraday dispersion function cube

class rm_lite.utils.clean.MultiscaleOptions

Options for multiscale RM-CLEAN.

__post_init__() None
kernel: KernelType = 'tapered_quad'

Scale kernel shape

max_iter_sub_minor: int = 10000

Maximum sub-minor (per-scale Hogbom) iterations

n_scales: int | None = None

Cap on the auto-selected scale count (ignored if scales given)

scales: numpy.typing.NDArray[numpy.float64] | None = None

Explicit scales (RMSF FWHM units); None auto-selects WSClean-style

selection: SelectionType = 'hybrid'

Scale selector (default “hybrid”). “snr” = matched filter max|R conv K_s| / sigma_s; “hybrid” = width-gated snr: engages extended scales only when the residual peak fits wider than the measured dirty beam and the extended score competes with scale 0, else behaves as “snr”. Plain “snr” scores are near scale-degenerate under correlated FDF noise, so with the default margin it almost never engages; “hybrid” does while staying point-safe.

selection_margin: float = 0.08

relative margin in [0, 1) favouring smaller scales. Among scales within this fraction of the best score, the smallest wins, which keeps points on the delta scale. 0 = raw argmax.

Type:

SNR selector (and the hybrid fallback)

sub_minor_fraction: float = 0.5

Re-select a scale once the activated scale’s peak drops by this fraction

class rm_lite.utils.clean.RMCleanOptions

Options for RM-CLEAN, shared by the 1D and 3D tools

__post_init__() None
fdf_noise: float | None = None

Theoretical FDF noise; enables the adaptive off-source auto-mask when set.

gain: float = 0.1

Clean loop gain

mask: float

Masking threshold - pixels below this value are not cleaned

max_iter: int = 100000

Maximum clean iterations

threshold: float

Cleaning threshold - stop when all pixels are below this value

class rm_lite.utils.clean.RMCleanResults

Bases: NamedTuple

Results of the RM-CLEAN calculation

clean_fdf_arr: numpy.typing.NDArray[numpy.complex128]

The cleaned Faraday dispersion function cube

clean_iter_arr: numpy.typing.NDArray[numpy.int64]

minor iterations (one component each). Multiscale: minor cycles (scale re-selections); see sub_minor_iter_arr for the comparable component count.

Type:

CLEAN iterations per pixel. Single-scale

model_fdf_arr: numpy.typing.NDArray[numpy.complex128]

The clean components cube

resid_fdf_arr: numpy.typing.NDArray[numpy.complex128]

The residual Faraday dispersion function cube

sub_minor_iter_arr: numpy.typing.NDArray[numpy.int64]

single-scale equals clean_iter_arr; multiscale sums the sub-minor (per-scale Hogbom) iterations over all minor cycles. This is the count comparable to single-scale.

Type:

Total component-placement steps per pixel

class rm_lite.utils.clean.RMSynthArrays

Bases: NamedTuple

Arrays for RM-synthesis

dirty_fdf_arr: numpy.typing.NDArray[numpy.complex128]

Dirty Faraday dispersion function array

fdf_mask_arr: numpy.typing.NDArray[numpy.bool_] | None = None

Mask of pixels to clean

fwhm_rmsf_arr: numpy.typing.NDArray[numpy.float64]

FWHM of the RMSF array

phi_arr_radm2: numpy.typing.NDArray[numpy.float64]

Faraday depth array in rad/m^2

phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64]

Double-length Faraday depth array in rad/m^2

rmsf_arr: numpy.typing.NDArray[numpy.complex128]

RMSF array

class rm_lite.utils.clean.ScaleKernels

Bases: NamedTuple

Per-scale RMSF responses for one spectrum (Offringa & Smirnov 2017).

The scale kernel K_s (eq. 2) is the extended shape a scale-s component is built from. A scale-s structure appears in the residual as RMSF conv K_s, and in the scale-convolved residual (which the sub-minor loop works in) as RMSF conv K_s conv K_s: the RMSF picks up the scale twice, once from the structure and once from convolving the residual (Section 2.2).

fwhm_conv_scale_twice: numpy.typing.NDArray[numpy.float64]

Fitted FWHM of |RMSF conv K_s conv K_s| per scale (sub-minor restore width).

peak_response: numpy.typing.NDArray[numpy.float64]

peak of the scale-convolved response, dividing it out recovers the true scale-s amplitude (cf. the per-scale gain, eq. 4).

Type:

max|RMSF conv K_s conv K_s| per scale

point_width: float

the dirty-beam width a delta presents in the dirty |FDF|, near-in sidelobe shoulders included. Wider than the theoretical fwhm under non-uniform lambda^2 sampling; the hybrid selector’s width test calibrates against this, never the fwhm.

Type:

Measured half-max width of |RMSF| in rad/m^2

rmsf_conv_scale: list[numpy.typing.NDArray[numpy.complex128]]

the scale-s response in the full-res residual, used for the residual subtraction. On the double phi axis.

Type:

RMSF conv K_s per scale

rmsf_conv_scale_twice: list[numpy.typing.NDArray[numpy.complex128]]

the scale-s response in the scale-convolved residual, i.e. the effective RMSF the sub-minor loop cleans against (Section 2.2).

Type:

RMSF conv K_s conv K_s per scale

scales: numpy.typing.NDArray[numpy.float64]

Scales (RMSF FWHM units)

sigma_s: numpy.typing.NDArray[numpy.float64]

Per-scale FDF noise std relative to scale 0 under correlated FDF noise, for the matched-filter (snr) score inside hybrid selection: sqrt((K_s conv K_s conv RMSF)(0) / RMSF(0)). sigma_s[0] == 1.

rm_lite.utils.clean._build_scale_masks(record: list[list[int]], scales: numpy.typing.NDArray[numpy.float64], phi_len: int, rmsf_fwhm: float, d_phi: float) list[numpy.typing.NDArray[numpy.bool_]]

Phase-2 per-scale masks: each scale’s phase-1 centres dilated by scale*fwhm/2 each side (WSClean 2.2.3 scale-dependent masking); empty for a scale that cleaned nothing.

The dilation is the scale’s own extent only, with no added RMSF core: the core over-dilated the delta scale into the RMSF sidelobes on wide-RMSF narrowband cells. At scale 0 this confines phase 2 to the exact phase-1 footprint, like single-scale’s frozen deep clean.

rm_lite.utils.clean._classify_hybrid_source(dirty_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernels: ScaleKernels, multiscale_options: MultiscaleOptions) MultiscaleOptions

Classify the source once on the DIRTY spectrum for “hybrid” selection.

A peak that fits at beam width is a point source: lock the whole clean to “snr” so mid-clean sidelobe pedestals (which can fit wide while the peak is still above the engage floor) never re-open extended selection. A wide fit keeps “hybrid” with its per-cycle gates. No-op for other selectors.

rm_lite.utils.clean._halfmax_width_radm2(profile: numpy.typing.NDArray[numpy.float64], d_phi: float) float

Half-max width of a peaked profile; on |RMSF| this is the measured dirty-beam width (>> theoretical fwhm under non-uniform lambda^2 sampling).

rm_lite.utils.clean._hybrid_scale_selection(resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], scale_kernels: ScaleKernels, rmsf_fwhm: float, phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernel: KernelType, active: numpy.typing.NDArray[numpy.bool_] | None, selection_margin: float, engage_floor: float) int

Width-gated snr selection.

Engage an extended scale only when two independent tests agree, else fall back to plain snr selection with selection_margin: 1. Width: bounded half-max fit of the residual peak wider than

HYBRID_WIDTH_FACTOR x the measured dirty-beam width (a delta fits 1.0x, the narrowest genuinely extended source ~2x).

  1. Score: best active extended matched-filter score at least HYBRID_SCORE_FACTOR x the scale-0 score (kills residual junk that fits wide but scores poorly at wide scales).

Engagement takes the argmax score among active extended scales. Below engage_floor (endgame) always falls back.

rm_lite.utils.clean._multiscale_minor_cycles(resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], *, kernels: ScaleKernels, scales: numpy.typing.NDArray[numpy.float64], allowed_supports: list[numpy.typing.NDArray[numpy.bool_]], phi_arr_radm2: numpy.typing.NDArray[numpy.float64], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], rmsf_fwhm: float, clean_options: RMCleanOptions, multiscale_options: MultiscaleOptions, stop_threshold: float, record: list[list[int]] | None) tuple[numpy.typing.NDArray[numpy.complex128], numpy.typing.NDArray[numpy.complex128], int, int]

One phase of multiscale minor cycles, restricted to allowed_supports.

Cleans down to stop_threshold with the O&S divergence and stall guards. A scale that exhausts its allowed region is retired (not reselected). If record is given, appends the cleaned channel indices per scale, to build the phase-2 auto-mask. Returns (resid, model, n_iter, sub_minor_total).

rm_lite.utils.clean._offsource_rms(resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], mask_arr: numpy.typing.NDArray[numpy.bool_]) float

Robust (MAD) RMS of the residual outside the current mask region.

Sidelobe-inflated while a bright source remains, falling to the noise floor once it subtracts; this is what makes the auto-mask contract off the RMSF sidelobes. Returns 0.0 when too few off-source channels remain (the caller floors it at the FDF noise).

rm_lite.utils.clean._reconvolve_model(model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], rmsf_spectrum: numpy.typing.NDArray[numpy.complex128], phi_arr_radm2: numpy.typing.NDArray[numpy.float64], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64]) numpy.typing.NDArray[numpy.complex128]

Footprint of a sparse delta model in the dirty FDF: sum_i m_i * RMSF@i.

Same shift-and-add primitive as minor_loop, so the multiscale residual update matches how single-scale CLEAN subtracts components.

rm_lite.utils.clean._restore_multiscale(model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], rmsf_fwhm: float) numpy.typing.NDArray[numpy.complex128]

Convolve the model with a unit-peak clean beam.

Matches single-scale minor_loop, so calc_faraday_moments mom0 normalisation recovers the component flux.

rm_lite.utils.clean._seed_mask(resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], cap_arr: numpy.typing.NDArray[numpy.bool_], floor: float) numpy.typing.NDArray[numpy.bool_]

Tight adaptive-mask seed: just the brightest allowed channel, if it clears the mask floor. Below the floor (noise-only) the seed is empty, so nothing cleans. The global peak is always real emission, so seeding it is safe.

rm_lite.utils.clean.adaptive_scale_supports(dirty_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], source_region: numpy.typing.NDArray[numpy.bool_], kernels: ScaleKernels, scales: numpy.typing.NDArray[numpy.float64], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernel: KernelType, rmsf_fwhm: float, mask: float) list[numpy.typing.NDArray[numpy.bool_]]

Opening-filter scale supports (eye-patch flint-crew, 1D RM analogue).

Per scale: scale-convolve the dirty FDF, morphologically OPEN it (min then max filter of the scale width) to isolate structure at least that wide, gate at the flux-correct amplitude mask * peak_response, and confine to source_region dilated by the scale width. source_region is the tight adaptive mask from the single-scale off-source pre-pass; dilating per scale is the eye-patch beam-shape-erosion analogue and keeps extended scales from spreading onto the RMSF sidelobe islands (the pond). A scale with an empty support is inactive.

rm_lite.utils.clean.compute_scale_kernels(scales: numpy.typing.NDArray[numpy.float64], rmsf_spectrum: numpy.typing.NDArray[numpy.complex128], rmsf_fwhm: float, phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernel: KernelType) ScaleKernels

Precompute the per-scale RMSF responses for one spectrum.

Follows Offringa & Smirnov (2017), Section 2.2; see ScaleKernels.

Parameters:
  • scales (NDArray[np.float64]) – Scales in RMSF FWHM units.

  • rmsf_spectrum (NDArray[np.complex128]) – RMSF on the double-phi axis.

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • phi_double_arr_radm2 (NDArray[np.float64]) – Double-phi axis.

  • kernel (KernelType) – Scale-kernel shape.

Returns:

Per-scale responses (see that type).

Return type:

ScaleKernels

rm_lite.utils.clean.convolve_fdf_scale(scale: float, fwhm: float, fdf_arr: numpy.typing.NDArray[numpy.complex128] | numpy.typing.NDArray[numpy.float64], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernel: KernelType = 'gaussian', sum_normalised: bool = True) numpy.typing.NDArray[numpy.complex128] | numpy.typing.NDArray[numpy.float64]

Convolve an FDF (complex or real) with a real scale kernel.

The real and imaginary parts are convolved separately, since scipy.ndimage.convolve drops the imaginary part of complex input.

Parameters:
  • scale (float) – Scale in RMSF FWHM units; 0 returns the input unchanged.

  • fwhm (float) – RMSF FWHM in rad/m^2.

  • fdf_arr (NDArray) – FDF (or RMSF) to convolve.

  • phi_double_arr_radm2 (NDArray[np.float64]) – Sets the sample spacing d_phi.

  • kernel (KernelType) – Scale-kernel shape.

  • sum_normalised (bool) – Normalise the kernel to unit sum (else unit peak).

Returns:

The scale-convolved FDF, same length as fdf_arr.

Return type:

NDArray

rm_lite.utils.clean.default_scales(phi_arr_radm2: numpy.typing.NDArray[numpy.float64], rmsf_fwhm: float, multiscale_options: MultiscaleOptions, phi_max_scale_radm2: float | None = None) numpy.typing.NDArray[numpy.float64]

Scales (RMSF FWHM units): explicit if given, else WSClean-style auto.

The auto max scale is phi_max_scale_radm2 (pi / lambda_sq_min) when given, capped at the FDF phi window: a kernel wider than the window is meaningless (its response flattens, its normalisation blows up). Uses the make_fine_scales grid.

Parameters:
  • phi_arr_radm2 (NDArray[np.float64]) – Faraday depth axis (sets the window cap).

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • multiscale_options (MultiscaleOptions) – Selector, explicit scales, count cap.

  • phi_max_scale_radm2 (float | None) – Largest recoverable scale; None uses the window.

Returns:

Scales in RMSF FWHM units.

Return type:

NDArray[np.float64]

rm_lite.utils.clean.find_significant_scale(resid_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], scale_kernels: ScaleKernels, rmsf_fwhm: float, phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], kernel: KernelType, active: numpy.typing.NDArray[numpy.bool_] | None = None, selection: SelectionType = 'snr', selection_margin: float = 0.0, engage_floor: float = 0.0) int

Index of the most-significant scale (Offringa & Smirnov 2017).

“snr” scores the matched filter max|resid conv K_s| / sigma_s, with selection_margin preferring the smallest scale within that margin of the best. “hybrid” is width-gated snr: engage an extended scale only when the residual peak fits wider than the measured dirty beam AND its score competes with scale 0, else fall back to “snr” (see _hybrid_scale_selection).

Parameters:
  • resid_fdf_spectrum (NDArray[np.complex128]) – Current residual FDF.

  • scale_kernels (ScaleKernels) – Precomputed per-scale responses.

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • phi_double_arr_radm2 (NDArray[np.float64]) – Double-phi axis.

  • kernel (KernelType) – Scale-kernel shape.

  • active (NDArray[np.bool_] | None) – Scales still allowed to be selected.

  • selection ("snr" | "hybrid") – Scale selector.

  • selection_margin (float) – SNR parsimony margin (see MultiscaleOptions); for “hybrid” it applies to the snr fallback.

  • engage_floor (float) – Hybrid only: below this residual peak (absolute FDF units) always fall back to snr. 0 disables the gate.

Returns:

Index into scale_kernels.scales of the selected scale.

Return type:

int

rm_lite.utils.clean.gaussian_scale_kernel_function(phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], scale: float, rmsf_fwhm: float, sum_normalised: bool = True) numpy.typing.NDArray[numpy.float64]

Gaussian scale kernel; sigma = (3/16) * scale (Offringa & Smirnov 2017).

O&S define sigma = (3/16) * alpha with alpha the scale in physical units; here that is scale * rmsf_fwhm, matching the tapered_quad width.

Parameters:
  • phi_double_arr_radm2 (NDArray[np.float64]) – Grid to sample the kernel on.

  • scale (float) – Scale in RMSF FWHM units.

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • sum_normalised (bool) – Normalise to unit sum (else unit peak).

Returns:

The sampled kernel.

Return type:

NDArray[np.float64]

rm_lite.utils.clean.hanning(x_arr: numpy.typing.NDArray[numpy.float64], length: float) numpy.typing.NDArray[numpy.float64]

Hanning window function.

rm_lite.utils.clean.make_fine_scales(max_scale: float, n_scales: int | None = None) numpy.typing.NDArray[numpy.float64]

Scale set: 0, 3, then geometric doubling from 6 to max_scale.

The first anchor is 3, not 1.5: a point’s matched-filter score at s=1.5 is only ~1% below scale 0, so noise flipped bright points onto it.

Parameters:
  • max_scale (float) – Upper bound in RMSF FWHM units (exclusive).

  • n_scales (int | None) – Cap on the scale count; None keeps all.

Returns:

Scales in RMSF FWHM units, starting at 0.

Return type:

NDArray[np.float64]

rm_lite.utils.clean.minor_cycle(rm_synth_1d_arrays: RMSynthArrays, clean_options: RMCleanOptions) CleanLoopResults

Single-scale CLEAN one spectrum: an initial masked loop to threshold, then a deep loop frozen to the cleaned footprint.

Parameters:
  • rm_synth_1d_arrays (RMSynthArrays) – 1D dirty FDF, phi axes, RMSF, optional mask.

  • clean_options (RMCleanOptions) – Mask, threshold, gain, max_iter.

Raises:

ValueError – If any input array is not 1D.

Returns:

Clean, residual, and model spectra and the iteration count.

Return type:

CleanLoopResults

rm_lite.utils.clean.minor_loop(minor_loop_arrays: MinorLoopArrays, minor_loop_options: MinorLoopOptions) MinorLoopResults

Hogbom minor loop: shift-subtract the RMSF at each residual peak within the mask until the peak drops below threshold (or max_iter).

Parameters:
  • minor_loop_arrays (MinorLoopArrays) – Masked residual, phi axes, RMSF, FWHM.

  • minor_loop_options (MinorLoopOptions) – Gain, mask, threshold, max_iter, mask update.

Returns:

Clean, residual, model, masked residual, and iteration count.

Return type:

MinorLoopResults

rm_lite.utils.clean.multiscale_clean_spectrum(dirty_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], phi_arr_radm2: numpy.typing.NDArray[numpy.float64], phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], rmsf_spectrum: numpy.typing.NDArray[numpy.complex128], rmsf_fwhm: float, scales: numpy.typing.NDArray[numpy.float64], clean_options: RMCleanOptions, multiscale_options: MultiscaleOptions) tuple[numpy.typing.NDArray[numpy.complex128], numpy.typing.NDArray[numpy.complex128], numpy.typing.NDArray[numpy.complex128], int, int]

Multiscale CLEAN one FDF spectrum (Offringa & Smirnov 2017).

Two-phase auto-mask (Section 2.2.3): phase 1 cleans to mask and records where each scale places flux; phase 2 deep-cleans to threshold, restricting each scale to its dilated phase-1 footprint (stops a scale smearing flux across the window). Give a generous phi window, as scale kernels wider than it pick up reflect-mode boundary artefacts.

Parameters:
  • dirty_fdf_spectrum (NDArray[np.complex128]) – Dirty FDF on the phi axis.

  • phi_arr_radm2 (NDArray[np.float64]) – Faraday depth axis in rad/m^2.

  • phi_double_arr_radm2 (NDArray[np.float64]) – Double-phi axis (RMSF length).

  • rmsf_spectrum (NDArray[np.complex128]) – RMSF on the double-phi axis.

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • scales (NDArray[np.float64]) – Scales in RMSF FWHM units.

  • clean_options (RMCleanOptions) – Mask, threshold, gain, max_iter.

  • multiscale_options (MultiscaleOptions) – Kernel, selector, sub-minor settings.

Returns:

minor_iters counts minor cycles (scale re-selections); sub_minor_iters the per-scale Hogbom steps (comparable to single-scale’s iteration count).

Return type:

(clean, resid, model, minor_iters, sub_minor_iters)

rm_lite.utils.clean.restore_model(model_fdf_spectrum: numpy.typing.NDArray[numpy.complex128], phi_arr_radm2: numpy.typing.NDArray[numpy.float64], rmsf_fwhm: float) numpy.typing.NDArray[numpy.complex128]

Restore a delta model with a unit-peak clean beam (one pass).

Sum of per-component Gaussians == the delta model convolved with the clean beam; doing it once from the final model is identical to the old per-iteration restore but avoids a full-array Gaussian every minor iteration. Distinct component channels are bounded by the FDF length, so the outer sum is at most len(phi)^2.

rm_lite.utils.clean.rmclean(rm_synth_arrays: RMSynthArrays, clean_options: RMCleanOptions, multiscale_options: MultiscaleOptions | None = None, phi_max_scale_radm2: float | None = None) RMCleanResults

Validate array shapes and CLEAN each pixel (single- or multi-scale).

Parameters:
  • rm_synth_arrays (RMSynthArrays) – Dirty FDF cube, phi axes, RMSF, masks.

  • clean_options (RMCleanOptions) – Mask, threshold, gain, max_iter, FDF noise.

  • multiscale_options (MultiscaleOptions | None) – Multiscale RM-CLEAN settings (recovers Faraday-thick structure). None runs single-scale RM-CLEAN.

  • phi_max_scale_radm2 (float | None) – Largest recoverable Faraday scale (pi / lambda_sq_min); sets the auto scale range. None falls back to the phi window.

Raises:

ValueError – If the input array shapes are inconsistent.

Returns:

Per-pixel clean/model/resid cubes and iteration counts.

Return type:

RMCleanResults

rm_lite.utils.clean.shift_rmsf(rmsf_spectrum: numpy.typing.NDArray[numpy.complex128], fdf_index: int, n_phi_pad: int, max_rmsf_index: int) numpy.typing.NDArray[numpy.complex128]

Roll the double-length RMSF so its peak sits at FDF channel fdf_index, then clip to FDF length.

Shared shift-and-subtract primitive (Hogbom and multiscale); assumes integer shifts and a symmetric RMSF.

Parameters:
  • rmsf_spectrum (NDArray[np.complex128]) – Double-length RMSF.

  • fdf_index (int) – Target FDF channel for the RMSF peak.

  • n_phi_pad (int) – Half the length difference between the double and FDF axes.

  • max_rmsf_index (int) – Index of the RMSF peak.

Returns:

RMSF shifted to fdf_index, clipped to FDF length.

Return type:

NDArray[np.complex128]

rm_lite.utils.clean.tapered_quad_kernel_function(phi_double_arr_radm2: numpy.typing.NDArray[numpy.float64], scale: float, rmsf_fwhm: float, sum_normalised: bool = True) numpy.typing.NDArray[numpy.float64]

Tapered quadratic scale kernel (Offringa & Smirnov 2017, eq. 2).

Parameters:
  • phi_double_arr_radm2 (NDArray[np.float64]) – Grid to sample the kernel on.

  • scale (float) – Scale in RMSF FWHM units.

  • rmsf_fwhm (float) – RMSF FWHM in rad/m^2.

  • sum_normalised (bool) – Normalise to unit sum (else unit peak).

Returns:

The sampled kernel.

Return type:

NDArray[np.float64]

rm_lite.utils.clean.DIVERGENCE_FACTOR = 2.0
rm_lite.utils.clean.DType
rm_lite.utils.clean.HYBRID_ENGAGE_MASK_FACTOR = 2.0
rm_lite.utils.clean.HYBRID_SCORE_FACTOR = 0.85
rm_lite.utils.clean.HYBRID_WIDTH_FACTOR = 1.2
rm_lite.utils.clean.KERNEL_FUNCS: dict[str, collections.abc.Callable[Ellipsis, numpy.typing.NDArray[numpy.float64]]]
rm_lite.utils.clean.KERNEL_SUPPORT_FACTOR: dict[str, float]
type rm_lite.utils.clean.KernelType = Literal['tapered_quad', 'gaussian']
rm_lite.utils.clean.STALL_PATIENCE = 5
rm_lite.utils.clean.STALL_REL_IMPROVEMENT = 0.01
type rm_lite.utils.clean.SelectionType = Literal['snr', 'hybrid']
rm_lite.utils.clean.TQDM_OUT