Extract & Re-Inject Pulse

Use burstfit to extract a pulse, then re-inject into the same file

[1]:
import tempfile
from typing import Union
from urllib.request import urlretrieve

import matplotlib.pyplot as plt
import numpy as np
from jess.dispersion import dedisperse
from jess.fitters import median_fitter
from scipy.stats import median_abs_deviation
from will import create, inject
from your import Your
[2]:
# !pip install  git+https://github.com/thepetabyteproject/burstfit
# I had to make some changes to burstfit to get the plotting to work
from burstfit.data import BurstData
from burstfit.fit import BurstFit
from burstfit.io import BurstIO
from burstfit.model import Model, SgramModel
from burstfit.utils.functions import gauss_norm, gauss_norm2, pulse_fn_vec, sgram_fn_vec
from burstfit.utils.plotter import plot_me
[3]:
def show_dynamic(
    dynamic_spectra: np.ndarray,
    title: Union[str, None] = None,
    save: Union[bool, None] = False,
) -> None:
    """
    Show a dynamic spectra by first flattening it
    in frequency. Do this by getting the medians of
    each channel and then run a median filter along the
    bandpass.

    Then set the limits of the imshow so we get good detail
    for the majority of the data.

    Args:
        dynmaic_spectra: the dynamic spectra to plot

        title: Title of plot

        save: Save the plot as `title` + `.png`

    Returns:
        None
    """
    spectra_mads = median_fitter(np.median(dynamic_spectra, axis=0))
    flat = dynamic_spectra - spectra_mads
    std = median_abs_deviation(flat, axis=None)
    med = np.median(flat)
    plt.figure(figsize=(14, 7))
    plt.imshow(flat.T, vmin=med - 3 * std, vmax=med + 6 * std, aspect="auto")
    plt.xlabel("Time Sample #", size=20)
    plt.ylabel("Channel #", size=20)
    plt.colorbar()
    plt.tight_layout()
    if title is not None:
        plt.title(title, size=28)
    if save:
        plt.savefig(title.replace(" ", "_") + ".png", dpi=75, bbox_inches="tight")

Extract the pulse using burstfit

This is copied from the burstfit documentation, https://github.com/thepetabyteproject/burstfit/blob/main/examples/BurstFit_custom_function.ipynb

[4]:
temp_dir = tempfile.TemporaryDirectory()
download_path = str(temp_dir.name) + "/FRB180417.fil"
url = "https://zenodo.org/record/3905426/files/FRB180417.fil"
urlretrieve(
    url,
    download_path,
)
fil_file = download_path

Make a simple channel mask

[5]:
yr_obj = Your(fil_file)
dynamic_spectra = yr_obj.get_data(0, yr_obj.your_header.nspectra)

stds = dynamic_spectra.std(axis=0)
stds -= np.median(stds)
np.abs(stds, out=stds)
stds /= median_abs_deviation(stds, scale="normal")
plt.plot(stds)

chan_mask = stds > 4
plt.plot(chan_mask)
[5]:
[<matplotlib.lines.Line2D at 0x7f59e0f11b50>]
../_images/examples_extract_reinject_pulse_9_1.png
[6]:
DM = 475.28400
bd = BurstData(
    fp=fil_file,
    dm=DM,
    tcand=2.0288800,
    width=2,
    snr=16.8128,
)
[7]:
# We need the indices of the bad channels
bad_chans = np.arange(0, yr_obj.your_header.nchans)[chan_mask].tolist()
[8]:
bd.prepare_data(mask_chans=bad_chans)
plot_me(bd.sgram)
../_images/examples_extract_reinject_pulse_12_0.png
[9]:
pnames = ["S", "mu_t", "sigma_t", "tau"]
pulseModel = Model(pulse_fn_vec, param_names=pnames)

# For spectra, we will use a normalized gaussian model
snames = ["mu_f", "sigma_f"]
spectraModel = Model(gauss_norm, param_names=snames)

sgramModel = SgramModel(
    pulseModel, spectraModel, sgram_fn_vec, mask=chan_mask, clip_fac=bd.clip_fac
)

snames = ["mu_f1", "sigma_f1", "mu_f2", "sigma_f2", "amp"]
spectraModel2 = Model(gauss_norm2, param_names=snames)
spectra_bounds = ([0, 0, 200, 0, 0], [100, 50, 300, 50, 1])
sgramModel2 = SgramModel(
    pulseModel, spectraModel2, sgram_fn_vec, mask=bd.mask, clip_fac=bd.clip_fac
)
[10]:
bf = BurstFit(
    sgram_model=sgramModel2,
    sgram=bd.sgram,
    width=bd.width,
    dm=bd.dm,
    foff=bd.foff,
    fch1=bd.fch1,
    tsamp=bd.tsamp,
    clip_fac=bd.clip_fac,
    mask=bd.mask,
)
[11]:
bf.validate()
bf.precalc()
[12]:
plot = True
bf.initial_profilefit(plot)
../_images/examples_extract_reinject_pulse_16_0.png
[13]:
bf.fitall(plot=True, spectra_bounds=spectra_bounds, profile_bounds=[])
WARNING:burstfit.fit:Input spectra bounds detected. Using them for component 1
../_images/examples_extract_reinject_pulse_17_1.png
../_images/examples_extract_reinject_pulse_17_2.png
/home/joseph/python/miniconda3/envs/kpe/lib/python3.9/site-packages/scipy/optimize/_minpack_py.py:1010: OptimizeWarning: Covariance of the parameters could not be estimated
  warnings.warn('Covariance of the parameters could not be estimated',
WARNING:burstfit.curvefit:Fit errors are not finite. Retrying with p0+-(0.2*p0) bounds
../_images/examples_extract_reinject_pulse_17_4.png

Create the pulse

Use WILL with above model as the probability distribution function.

[14]:
pulse_obj = create.TwoDimensionalPulse(
    bf.model.data.T, yr_obj.chan_freqs, tsamp=yr_obj.your_header.tsamp, dm=DM
)
# WILL has time on the vertical axis, burstfit on the horizontal, so transpose.
pulse = pulse_obj.sample_pulse(int(bf.model.data.sum()))
[15]:
show_dynamic(pulse, title=f"{DM} DM Pulse")
../_images/examples_extract_reinject_pulse_21_0.png

Inject the pulse

Original Dynamic Spectra

[16]:
show_dynamic(dynamic_spectra, title=f"Dynamic Spectra + {DM} dm Pulse")
../_images/examples_extract_reinject_pulse_24_0.png

The pulse is faintly visible before sample 2000.

[17]:
dynamic_spectra_w_pulse = inject.inject_constant_into_file(
    yr_input=yr_obj,
    pulse=pulse,
    start=3000,
    gulp=yr_obj.your_header.nspectra,
)
[18]:
show_dynamic(dynamic_spectra_w_pulse, title=f"Dynamic Spectra + {DM} dm Pulse")
../_images/examples_extract_reinject_pulse_27_0.png

It is difficult to see the new pulse, lets make it 100x brighter.

[19]:
pulse = pulse_obj.sample_pulse(int(100 * bf.model.data.sum()))
dynamic_spectra_w_pulse = inject.inject_constant_into_file(
    yr_input=yr_obj,
    pulse=pulse,
    start=3000,
    gulp=yr_obj.your_header.nspectra,
)
[20]:
show_dynamic(dynamic_spectra_w_pulse, title=f"Dynamic Spectra + Bright {DM} dm Pulse")
../_images/examples_extract_reinject_pulse_30_0.png

Now the re-injected pulse is visible between time samples 3000-3500.

[ ]: