Finding X-ray observations for a sample of SNe 30 days either side of discovery

This case study searches for X-ray observations (from XMM, Chandra, and Swift; but it is applicable to all DAXA missions) of Type 1a Supernovae locations, within a time window around each SNe’s discovery date. This process is applicable to any search for an observation of a specific place at a specific time (or a sample of places at a set of different times).

Import Statements

[1]:
import numpy as np
import pandas as pd
from astropy.coordinates import SkyCoord
from datetime import datetime, timedelta

from daxa.mission import XMMPointed, Chandra, Swift
from daxa.archive import Archive

Other Tutorials

These case studies are meant to be highly specific examples of how you might acquire data for a particular science case, they do not provide general instruction on how to use DAXA missions or archives. We instead direct you to:

  • Using DAXA missions - Here we explain what DAXA mission classes are and how to use them to select only the data you need.

  • Creating a DAXA archive - This explains how to create an archive, load an existing archive, and the various properties and features of DAXA archives.

  • Processing telescope data - The processing tutorials for different missions are presented here, though there may not yet be processing support for all missions.

Reading through these should give you a good understanding of how DAXA can be used to acquire, organise, and process multi-mission X-ray datasets for your specific use case.

Sample

We read in a demonstrative sample, which consists of 1000 randomly selected Type 1a Supernovae from the transient name server (TNS); there are many columns, but the only ones we require are position (Right-Ascension and Declination) and the discovery date (which we will search around):

[2]:
samp = pd.read_csv("samp_files/sn1a_samp.csv")
samp.head(5)
[2]:
objid name_prefix name ra declination redshift typeid type reporting_groupid reporting_group ... source_group discoverydate discoverymag discmagfilter filter reporters time_received internal_names creationdate lastmodified
0 133295 SN 2023ock 232.474130 66.075011 0.039 3.0 SN Ia 48.0 ZTF ... ZTF 2023-07-26 08:16:47.000 19.2400 110.0 g C. Fremling (Caltech) on behalf of the Zwicky ... 2023-07-28 07:24:04 ZTF23aauqmys, ATLAS23pvm 2023-07-28 07:24:05 2023-08-06 05:49:07
1 74545 SN 2021dj 208.777227 54.304902 0.070 3.0 SN Ia 48.0 ZTF ... ZTF 2021-01-01 11:24:00.000 19.2500 111.0 r C. Fremling (Caltech) on behalf of the Zwicky ... 2021-01-03 22:01:11 ZTF21aaabucr, ATLAS21aza, PS21xg 2021-01-03 22:01:13 2021-02-07 11:04:35
2 56011 SN 2020enj 155.237795 0.573764 0.104 3.0 SN Ia 18.0 ATLAS ... ATLAS 2020-03-16 10:59:31.200 19.2350 72.0 orange J. Tonry, L. Denneau, A. Heinze, H. Weiland, H... 2020-03-16 18:48:30 ATLAS20hza, PS20agg, ZTF20aaubotx 2020-03-16 18:48:32 2020-03-16 18:48:32
3 74416 SN 2021E 62.978428 16.803008 0.050 3.0 SN Ia 74.0 ALeRCE ... ZTF 2021-01-01 03:34:56.997 19.9476 110.0 g F. Forster, F.E. Bauer, A. Munoz-Arancibia, G.... 2021-01-01 16:43:48 ZTF21aaaalaf, ATLAS21ash 2021-01-01 16:43:53 2021-01-13 00:38:47
4 49744 SN 2019xck 97.070105 23.608591 0.035 3.0 SN Ia 74.0 ALeRCE ... ZTF 2019-12-19 09:51:42.000 19.3648 110.0 g F. Forster, F.E. Bauer, G. Pignata, J. Arredon... 2019-12-19 16:00:58 ZTF19aczeomw, ATLAS19bdqm 2019-12-19 16:01:09 2019-12-19 16:01:09

5 rows × 21 columns

We read out the coordinates into an astropy coordinate object, and set up the time windows we will be searching for each SNe - for this demonstration we will search for X-ray observations 30 days either side of the disovery date:

[3]:
pos = SkyCoord(samp['ra'].values, samp['declination'].values, unit='deg')
start_times = np.array([datetime.strptime(dd, "%Y-%m-%d %H:%M:%S.%f") - timedelta(days=30)
                        for dd in samp['discoverydate'].values])
end_times = np.array([datetime.strptime(dd, "%Y-%m-%d %H:%M:%S.%f") + timedelta(days=30)
                      for dd in samp['discoverydate'].values])

Defining missions

We create instances of the XMM, Chandra, and Swift missions in order to search their archives - Swift is the most likely to have many matching observations, as it acts as a transient follow-up telescope, but XMM and Chandra are workhorses and may have some observations that we might want to explore:

[4]:
xm = XMMPointed()
ch = Chandra()
sw = Swift()
/Users/dt237/code/DAXA/daxa/mission/xmm.py:83: UserWarning: 140 of the 17697 observations located for this mission have been removed due to NaN RA or Dec values
  self._fetch_obs_info()
/Users/dt237/code/DAXA/daxa/mission/swift.py:101: UserWarning: 598 of the 353616 observations located for Swift have been removed due to all instrument exposures being zero.
  self._fetch_obs_info()
/Users/dt237/code/DAXA/daxa/mission/swift.py:101: UserWarning: 17 of the 353616 observations located for Swift have been removed due to all chosen instrument (XRT, BAT) exposures being zero.
  self._fetch_obs_info()

Searching for observations

We will make use of the DAXA filtering method that allows us to search for observations of a particular coordinate, within a particular time frame, for a whole sample. It is called the same way for the three missions we are using, we pass the positions, start times, and end times, and the keyword arguments have the following meanings:

  • return_obs_info - If True, a dataframe is returned from the method to allow the user to link specific ObsIDs to particular entries in our original sample table. The dataframe contains a ‘pos_ind’ column, which contains indexes corresponding to the input positions (i.e. the 4th entry of pos would have index 3), it also contains ObsIDs matched to that coordinate and time window.

  • over_run - If True, observations that start or end outside of the specified time window are accepted. If False, they are not.

[5]:
xm_assoc = xm.filter_on_positions_at_time(pos, start_times, end_times, return_obs_info=True, over_run=True)
ch_assoc = ch.filter_on_positions_at_time(pos, start_times, end_times, return_obs_info=True, over_run=True)
sw_assoc = sw.filter_on_positions_at_time(pos, start_times, end_times, return_obs_info=True, over_run=True)
/Users/dt237/code/DAXA/daxa/mission/base.py:1389: UserWarning: Every value in the filter array is False, meaning that no observations remain.
  self.filter_array = np.full(self.filter_array.shape, False)
/Users/dt237/code/DAXA/daxa/mission/base.py:1075: UserWarning: Chandra FoV are difficult to define, as they can be strongly dependant on observation mode; as such take these as very approximate.
  fov = self.fov
/Users/dt237/code/DAXA/daxa/mission/base.py:1389: UserWarning: Every value in the filter array is False, meaning that no observations remain.
  self.filter_array = np.full(self.filter_array.shape, False)
/Users/dt237/code/DAXA/daxa/mission/base.py:97: UserWarning: There are multiple chosen instruments XRT, BAT for swift with different FoVs, but they observe simultaneously. As such the search distance has been set to the largest FoV of the chosen instruments.
  any_ret = change_func(*args, **kwargs)
/Users/dt237/code/DAXA/daxa/mission/base.py:1389: UserWarning: Every value in the filter array is False, meaning that no observations remain.
  self.filter_array = np.full(self.filter_array.shape, False)

Identified observations

We will now use the returns from the filtering methods to highlight the observations which have been identified as fulfilling our criteria:

[6]:
xm_assoc['sn_name'] = samp.loc[xm_assoc['pos_ind'].values.astype(int), 'name'].values
xm_assoc
[6]:
pos_ind pos_ra pos_dec ObsIDs sn_name
30 414 158.42781249083353 39.49061071686476 0824030101 2018hus
[7]:
ch_assoc['sn_name'] = samp.loc[ch_assoc['pos_ind'].values.astype(int), 'name'].values
ch_assoc
[7]:
pos_ind pos_ra pos_dec ObsIDs sn_name
10 45 219.741280784 51.0827058066 21697 2020hyi
41 142 234.553792 39.732811 22528 2019rmq
44 160 161.2694701 2.31901835 22494 2020kxf
114 429 186.495176661 7.23543298389 23771 2021ita
166 664 206.843520969 26.384687742 27023,27026,27021,27022,27024,27029,27020,27806 2023hrk
174 706 222.24025 18.326689 22659 2021min
196 783 193.82175 2.897311 23563 2021pkz
219 894 222.905086576 18.9261358766 26962 2023jgq
[8]:
sw_assoc['sn_name'] = samp.loc[sw_assoc['pos_ind'].values.astype(int), 'name'].values
sw_assoc
[8]:
pos_ind pos_ra pos_dec ObsIDs sn_name
1 1 208.77722506346083 54.30490205010956 00013608061,00013608056,00013608059,0001360805... 2021dj
2 2 155.23780171544678 0.5737592936995243 00095611001,00075041008 2020enj
7 7 0.8142941780660411 16.14571038076381 00014427002,00014427004,00014427001,0001442700... 2021rhu
10 10 250.60306782108816 78.91498537452249 00095179001 2019kyz
17 17 219.49858839000424 9.388540565458925 00013556003,00013556005,00013556002,00013556004 2020lil
... ... ... ... ... ...
873 955 267.9480465961222 44.90420978372445 00074924063,00074924061,00075754012 2022ucs
879 961 295.4186780442415 -21.26268560353943 00016144004,00016144003,00016144001,00016144002 2023mvl
887 970 39.01071994163291 43.472059484607946 00010346005,00010346003,00010346008,0001034601... 2017hjy
905 990 347.3879247185516 15.65927267376877 00013713001,00013713003,00013713002,0001371300... 2020szr
909 995 355.94687610671747 51.241493712166886 00081310002,00035031184,00035031190,0003503118... 2020rsi

119 rows × 5 columns

Defining an archive

The filtered missions can then be used to define an archive containing the selected data:

[9]:
arch = Archive('sne_search', [xm, ch, sw])
arch.info()
Downloading XMM-Newton Pointed data: 100%|██████████████████████████████████████| 1/1 [00:22<00:00, 22.25s/it]
Downloading Chandra data: 100%|███████████████████████████████████████████████| 15/15 [00:38<00:00,  2.56s/it]
Downloading Swift data: 100%|███████████████████████████████████████████████| 454/454 [07:33<00:00,  1.00it/s]

-----------------------------------------------------
Number of missions - 3
Total number of observations - 470
Beginning of earliest observation - 2016-02-21 03:25:58
End of latest observation - 2024-01-05 07:08:51

-- XMM-Newton Pointed --
   Internal DAXA name - xmm_pointed
   Chosen instruments - M1, M2, PN
   Number of observations - 1
   Fully Processed - False

-- Chandra --
   Internal DAXA name - chandra
   Chosen instruments - ACIS-I, ACIS-S, HRC-I, HRC-S
   Number of observations - 15
   Fully Processed - False

-- Swift --
   Internal DAXA name - swift
   Chosen instruments - XRT, BAT
   Number of observations - 454
   Fully Processed - False
-----------------------------------------------------