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
-----------------------------------------------------