Source code for pclean.imaging.deconvolver

"""Deconvolution wrapper.

Provides a standalone ``Deconvolver`` class that can be used
independently of the full imaging pipeline -- for example when
the residual / PSF already exist on disk and only minor-cycle
deconvolution is needed.
"""

from __future__ import annotations

import logging

log = logging.getLogger(__name__)

_casatools = None


def _ct():
    global _casatools
    if _casatools is None:
        import casatools as ct

        _casatools = ct
    return _casatools


[docs] class Deconvolver: """Thin wrapper around ``synthesisdeconvolver``. Args: imagename: Image name prefix (images must already exist on disk). decpars: Deconvolution parameters (deconvolver, scales, nterms, ...). iterpars: Iteration control parameters. If ``None``, the caller must drive the minor cycle externally. """ def __init__( self, imagename: str, decpars: dict, iterpars: dict | None = None, ): self.imagename = imagename self.decpars = dict(decpars) self.decpars['imagename'] = imagename self.iterpars = iterpars self._sd = None self._ib = None # ------------------------------------------------------------------
[docs] def setup(self) -> None: ct = _ct() self._sd = ct.synthesisdeconvolver() self._sd.setupdeconvolution(decpars=self.decpars) if self.iterpars is not None: self._ib = ct.iterbotsink() self._ib.setupiteration(iterpars=self.iterpars)
[docs] def teardown(self) -> None: if self._sd is not None: self._sd.done() self._sd = None if self._ib is not None: self._ib.done() self._ib = None
# ------------------------------------------------------------------
[docs] def init_minor(self) -> dict: """Return peak-residual info for the iterbot.""" return self._sd.initminorcycle()
[docs] def execute_minor(self, iterbotrecord: dict | None = None) -> dict: """Run one minor cycle. If *iterbotrecord* is not given and an ``iterbotsink`` was created, the record is obtained automatically. """ if iterbotrecord is None and self._ib is not None: iterbotrecord = self._ib.getminorcyclecontrols() return self._sd.executeminorcycle(iterbotrecord=iterbotrecord)
[docs] def setup_mask(self) -> None: self._sd.setupmask()
[docs] def restore(self) -> None: self._sd.restore()
[docs] def pbcor(self) -> None: self._sd.pbcor()
# ------------------------------------------------------------------
[docs] def run_loop(self) -> dict: """Run the full minor-cycle loop (requires ``iterpars``). Returns: Summary with iteration count, peak residual, etc. """ if self._ib is None: raise RuntimeError('No iterpars — cannot run standalone loop') total_iter = 0 while True: self._ib.resetminorcycleinfo() rec = self.init_minor() self._ib.mergeinitrecord(rec) if self._ib.cleanComplete(): break exrec = self.execute_minor() self._ib.mergeexecrecord(exrec, 0) total_iter += exrec.get('iterdone', 0) return {'iterdone': total_iter}