Leakage pattern deconvolution

Leakage pattern deconvolution#

[1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import os
import leakagelib

SOURCE_SIZE = 55 # pixels # 41
IMAGE_SIZE = 2.8 # arcsec # 4
PIXEL_SIZE = 2.8 # arcsec # 4

SPECTRUM = leakagelib.Spectrum.from_power_law_index(2)
>>> PyXSPEC is not installed, you will no be able to use it.
>>> Reading (in memory) /opt/homebrew/anaconda3/lib/python3.12/site-packages/ixpeobssim/caldb/ixpe/gpd/cpf/arf/ixpe_d1_obssim20240101_v013.arf...

This code uses an iterative method to deconvolve leakage patterns from an image. There’s no method yet for error bar estimation; for that, use simultaneous fitting or LeakageLib’s polarimetric fitting.

Suppose we have observed the following I, Q, U images:

[4]:
mock_is = np.load("data/prediction/mock_is.npy")
mock_qs = np.load("data/prediction/mock_qs.npy")
mock_us = np.load("data/prediction/mock_us.npy")

Now we load the source, with the flux map stored in data/prediction/pwn-i.fits.

[5]:
source = leakagelib.Source.load_file("data/prediction/pwn-i.fits", False, SOURCE_SIZE, PIXEL_SIZE)

Also load the PSFs for use in de-convolving the image.

[6]:
psfs = []
for det in range(1,4):
    psfs.append(leakagelib.PSF.sky_cal(det, source, det * np.pi / 3 * 2))

The deconvolving code is stored in fit_extended, which has several meta-parameters. We’ll run it and save the animation to figs/animation.gif

[7]:
if not os.path.exists("figs"): os.mkdir("figs")

# Perform the fit
extracted_q, extracted_u, anim = leakagelib.extended.fit_extended(
    source, psfs, SPECTRUM,                                     # Properties of the source
    mock_is, mock_qs, mock_us,                                  # Leakage-containing observations
    initial_source_pol=None,                                    # Use the default starting point for the fitter
    inertia=None,                                               # Optional argument to prevent some pixels from being numerically unstable
    num_iter=5000, max_rate=2e-2, regularize_coeff=0.4,         # Fitter settings
    report_frequency=50,                                        # Saves a snapshot every frame. Set to None to improve speed
)

# Save the gif
if not os.path.exists("figs"):
    os.mkdir("figs")
if anim is not None:
    anim.save(f"figs/animation.gif")
../_images/examples_leakage-extract_9_0.png

Check out animation.gif for the fitter’s progression over time. Now we’ll tell the source the true polarization…

[8]:
source.polarize_file("data/prediction/pwn-qu.fits") # Reset to the true polarization

… and compare the result to the deconvolved image.

[9]:
VMAX = 0.5
fig, axs = plt.subplots(figsize=(13, 12), ncols=3, nrows=3, sharex=True, sharey=True, gridspec_kw=dict(width_ratios=(1.33, 1, 1)))
c_i = axs[0,0].pcolormesh(source.pixel_centers, source.pixel_centers, source.source, norm=mpl.colors.LogNorm())
axs[0,1].pcolormesh(source.pixel_centers, source.pixel_centers, mock_is[2], norm=mpl.colors.LogNorm())
axs[0,2].axis(False)
c_qu = axs[1,0].pcolormesh(source.pixel_centers, source.pixel_centers, source.q_map, vmax=VMAX, vmin=-VMAX, cmap="RdBu")
axs[1,1].pcolormesh(source.pixel_centers, source.pixel_centers, mock_qs[2], vmax=VMAX, vmin=-VMAX, cmap="RdBu")
axs[1,2].pcolormesh(source.pixel_centers, source.pixel_centers, extracted_q, vmax=VMAX, vmin=-VMAX, cmap="RdBu")
axs[2,0].pcolormesh(source.pixel_centers, source.pixel_centers, source.u_map, vmax=VMAX, vmin=-VMAX, cmap="RdBu")
axs[2,1].pcolormesh(source.pixel_centers, source.pixel_centers, mock_us[2], vmax=VMAX, vmin=-VMAX, cmap="RdBu")
axs[2,2].pcolormesh(source.pixel_centers, source.pixel_centers, extracted_u, vmax=VMAX, vmin=-VMAX, cmap="RdBu")

for ax in axs.reshape(-1):
    ax.set_aspect("equal")
    ax.set_xlim(source.pixel_centers[-1], source.pixel_centers[0])
    ax.set_ylim(source.pixel_centers[0], source.pixel_centers[-1])
axs[2,0].set_xlabel("[arcsec]")

axs[0,0].set_title("True")
axs[0,1].set_title("Observed (DU 3)")
axs[1,2].set_title("Extracted")

axs[0,0].set_yticks([])
axs[1,0].set_yticks([])
axs[2,0].set_yticks([])

fig.colorbar(c_i , ax=axs[0,0], location='left', label="$I$")
fig.colorbar(c_qu, ax=axs[1,0], location='left', label="$q$ (normalized)")
fig.colorbar(c_qu, ax=axs[2,0], location='left', label="$u$ (normalized)")

fig.subplots_adjust(hspace=0.05, wspace=0.05)
../_images/examples_leakage-extract_13_0.png
[ ]: