Source Injector

The source injector can produce mock simulated data independent of the MEGAlib software.

Standard data simulation requires the users to install and use MEGAlib to convolve the source model with the detector effects to generate data. The source injector utilizes the response generated by intensive simulation, which contains the statistical detector effects. With the source injector, you can convolve response, source model, and orientation to gain the mock data quickly.

The advantages of using the source injector include:

  • No need to install and use MEGAlib

  • Get the data much faster than the standard simulation pipeline

  • The generated data are in the format that can be used for spectral fitting, localization, imaging, etc.

The disadvantages are:

  • The data are binned based on the binning of the response, which means that you lost the unbinned event distribution as you will get from the MEGAlib pipeline.

  • If the response is coarse, the data you generated might not be precise.

[160]:
%%capture
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
import astropy.units as u
from pathlib import Path
from astropy.coordinates import SkyCoord
from astromodels.functions.function import Function1D, FunctionMeta
from cosipy import SpacecraftFile, SourceInjector
from histpy import Histogram
from threeML import Powerlaw, Band
from cosipy.threeml.custom_functions import SpecFromDat
from cosipy.util import fetch_wasabi_file
import shutil
import os

%matplotlib inline
[161]:
data_dir = Path("")  # Current directory by default. Modify if you want a different path

Get the data

The data can be downloaded by running the cells below. Each respective cell also gives the wasabi file path and file size.

[162]:
%%capture
zipped_response_path = data_dir/"SMEXv12.Continuum.HEALPixO3_10bins_log_flat.binnedimaging.imagingresponse.nonsparse_nside8.area.good_chunks_unzip.earthocc.zip"
response_path = data_dir/"SMEXv12.Continuum.HEALPixO3_10bins_log_flat.binnedimaging.imagingresponse.nonsparse_nside8.area.good_chunks_unzip.earthocc.h5"

# download response file ~839.62 MB
if not response_path.exists():

    fetch_wasabi_file("COSI-SMEX/DC2/Responses/SMEXv12.Continuum.HEALPixO3_10bins_log_flat.binnedimaging.imagingresponse.nonsparse_nside8.area.good_chunks_unzip.earthocc.zip", zipped_response_path)

    # unzip the response file
    shutil.unpack_archive(zipped_response_path)

    # delete the zipped response to save space
    os.remove(zipped_response_path)
[163]:
%%capture
orientation_path = data_dir/"20280301_3_month.ori"

# download orientation file ~684.38 MB
if not orientation_path.exists():
    fetch_wasabi_file("COSI-SMEX/DC2/Data/Orientation/20280301_3_month.ori", orientation_path)

Inject a source response

Method 1 : Define the point source

In this method, we are setting up an analytical function (eg: a power law model) to simulate the spectral characteristics of a point source:

[164]:
# Defind the Crab spectrum

alpha_inj = -1.99
beta_inj = -2.32
E0_inj = 531. * (alpha_inj - beta_inj) * u.keV
xp_inj = E0_inj * (alpha_inj + 2) / (alpha_inj - beta_inj)
piv_inj = 100. * u.keV
K_inj = 7.56e-4 / u.cm / u.cm / u.s / u.keV

spectrum_inj = Band()

spectrum_inj.alpha.min_value = -2.14
spectrum_inj.alpha.max_value = 3.0
spectrum_inj.beta.min_value = -5.0
spectrum_inj.beta.max_value = -2.15
spectrum_inj.xp.min_value = 1.0

spectrum_inj.alpha.value = alpha_inj
spectrum_inj.beta.value = beta_inj
spectrum_inj.xp.value = xp_inj.value
spectrum_inj.K.value = K_inj.value
spectrum_inj.piv.value = piv_inj.value

spectrum_inj.xp.unit = xp_inj.unit
spectrum_inj.K.unit = K_inj.unit
spectrum_inj.piv.unit = piv_inj.unit
21:42:22 WARNING   The current value of the parameter beta (-2.0) was above the new maximum -2.15. parameter.py:794

Read orientation file

[165]:
# Read the 3-month orientation
# It is the pointing of the spacecraft during the the mock simlulation
ori = SpacecraftFile.parse_from_file(orientation_path)

Get the expected counts and save to a data file

[166]:
# Define the coordinate of the point source
source_coord = SkyCoord(l = 184.5551, b = -05.7877, frame = "galactic", unit = "deg")
[167]:
# Define an injector by the response
injector = SourceInjector(response_path=response_path)
[168]:
%%time

file_path = "test_injected.h5"

# Check if the file exists and remove it if it does
if os.path.exists(file_path):
    os.remove(file_path)

# Get the data of the injected source
injector.inject_point_source(spectrum = spectrum_inj, coordinate = source_coord, orientation = ori, source_name = "point_source",
                             make_spectrum_plot = True, data_save_path = "test_injected.h5", project_axes = None)
CPU times: user 5.17 s, sys: 2.89 s, total: 8.05 s
Wall time: 8.34 s
[168]:
<cosipy.response.PointSourceResponse.PointSourceResponse at 0x308d0bbe0>
../../_images/tutorials_source_injector_Point_source_injector_16_2.png

Method 2: Read the spectrum from a file

In this method, we’re loading spectral data from a file (crab_spec.dat) and visualizing it:

[169]:
# Load data from the text file, skipping the index column
dataFlux = np.genfromtxt("crab_spec.dat",comments = "#",usecols = (2),skip_footer=1,skip_header=5)
dataEn = np.genfromtxt("crab_spec.dat",comments = "#",usecols = (1),skip_footer=1,skip_header=5)

[170]:
# Plot the data
plt.figure(figsize=(10, 6))
plt.plot(dataEn, dataFlux, marker="o", linestyle="-")
plt.xscale("log")
plt.yscale("log")
plt.xlabel("energies (keV)")
# plt.ylabel("Flux (keV cm^-2 s^-1)")
plt.ylabel("Flux_ph (ph/cm^2/s/keV)")
plt.title("Energy Flux Data")
plt.grid(True)
plt.show()
../../_images/tutorials_source_injector_Point_source_injector_20_0.png

We use a custom class SpecFromDat to define a spectrum from data loaded from a CSV file crab_spec.dat

SpecFromDat Class Explanation

The SpecFromDat class represents a spectrum loaded from a data file (dat, txt, csv etc.,). It provides methods to handle spectral data and evaluate the spectrum at specified energy values (x).

Class Description

  • Description:

    • A spectrum loaded from a data file (dat).

Parameters

  • K:

    • Description: Normalization factor.

    • Initial Value: 1.0

    • Is Normalization: True

    • Transformation: log10

    • Min: 1e-30

    • Max: 1e3

    • Delta: 0.1

    • Units: ph/cm2/s

Properties

  • dat:

    • Description: The data file from which the spectrum is loaded.

    • Initial Value: test.dat

    • Defer: True

    • Units:

      • Energy: keV.

      • Flux: ph/cm2/s/keV.

Functionality

  • Loads flux (dataFlux) and energy (dataEn) from the specified data file (self.dat.value).

  • Normalizes dataFlux using the widths of energy bins.

  • Interpolates (interp1d) the normalized data to create a function (fun) for evaluating the spectrum.

  • Evaluates the spectrum (fun(x)) at given energy values (x), scaled by the normalization factor (K).

[177]:
spectrum = SpecFromDat(K=1/18, dat="crab_spec.dat")

Read orientation file

[178]:
# Read the 3-month orientation
# It is the pointing of the spacecraft during the the mock simlulation
ori = SpacecraftFile.parse_from_file(orientation_path)

Get the expected counts and save to a data file

[179]:
# Define an injector by the response
injector = SourceInjector(response_path=response_path)
[180]:
# Define the coordinate of the point source
source_coord = SkyCoord(l=184.56, b=-5.78, frame="galactic", unit="deg")
[181]:
%%time

file_path = "crab_piecewise_injected.h5"

# Check if the file exists and remove it if it does
if os.path.exists(file_path):
    os.remove(file_path)

# Get the data of the injected source
injector.inject_point_source(spectrum=spectrum, coordinate=source_coord, orientation=ori, source_name="point_source",
                             make_spectrum_plot=True, data_save_path=file_path, project_axes=None)
CPU times: user 7.5 s, sys: 3.21 s, total: 10.7 s
Wall time: 10.9 s
[181]:
<cosipy.response.PointSourceResponse.PointSourceResponse at 0x30f2ca290>
../../_images/tutorials_source_injector_Point_source_injector_29_2.png

(Optional) Compare simulated data with existing models

[182]:
model_injected = Histogram.open("model_injected.h5").project("Em")
piecewise_injected = Histogram.open("crab_piecewise_injected.h5").project("Em")

ax, plot = model_injected.draw(label="injected with model", color="green")

piecewise_injected.draw(
    ax, label="injected with piesewise function", color="orange", linestyle="dashed"
)


ax.set_xscale("log")
ax.set_yscale("log")
ax.legend()
ax.set_ylabel("Counts")
ax.set_title("Comparison b/w model and piecewise injected counts")
[182]:
Text(0.5, 1.0, 'Comparison b/w model and piecewise injected counts')
../../_images/tutorials_source_injector_Point_source_injector_31_1.png