Swift TOO API
Swift MOC

swift_too module

Swift_ObsQuery example - querying past Swift observations

API version = 1.2, swifttools version = 2.4

Author: Jamie A. Kennea (Penn State)

The Swift_ObsQuery class allows for querying the database of observations have have already been performed by Swift, otherwise known as the "As-Flown Science Timeline" (AFST). Note this will only fetch observations that have already been performed, not scheduled observations.

%matplotlib inline
import matplotlib.pyplot as plt
from swifttools.swift_too import ObsQuery
from time import sleep
import numpy as np

Constructing the query

Note: As of swifttools version 2.2, you do not need to pass username or shared_secret keywords, they will default to anonymous. This is the recommended usage for anything except a TOO request.

First example, how often has Swift observed the binary system SS 433? Well, I can't remember the RA/Dec off the top of my head, so let's look it up...

New features in swifttools 2.3

swifttools 2.3 supports a new class called Swift_Resolve. You can use this to do name resolution, i.e. converting the name of a target into coordinates. However, it's also built into classes that take RA/Dec now, so you can just pass it using the name parameters.

Another new feature of 2.3, there are now shorthand names for classes, so you can omit the Swift_ when calling the class, changing it to just ObsQuery.

query = ObsQuery()
query.name = "SS 433"

Now you can see the coordinates, as either RA/Dec or astropy's SkyCoord, using the attributes ra, dec and skycoord (if astropy is installed). For example:

query.skycoord
<SkyCoord (FK5: equinox=J2000.000): (ra, dec) in deg
    (287.95651875, 4.98272889)>

RA/Dec is stored by the TOO API in decimal degrees, in J2000 epoch as this is the epoch Swift uses

print(f"RA/Dec(J2000) = {query.ra:.4f}, {query.dec:.4f}")
RA/Dec(J2000) = 287.9565, 4.9827

Looks legit. However, we can also set RA/Dec the old fashioned way

query.ra, query.dec = 287.9565, 4.9827

Note that skycoord will remain correct even if you changed the ra and dec properties.

query.skycoord
<SkyCoord (FK5: equinox=J2000.000): (ra, dec) in deg
    (287.9565, 4.9827)>

Swift_ObsQuery has a default search radius which is....

print(f"Default search radius = {query.radius:.3f} degrees")
Default search radius = 0.197 degrees

That's 12 arcminutes, which is the approximate field of view of Swift's X-ray Telescope (XRT). We can narrow that down a bit, so we only get matches that are in the center of the field of view (FOV), and also in UVOT which has a smaller FOV.

query.radius = 5 / 60  # 5 arc-minutes, as the units for radius are degrees

Submitting the query

OK let's query the Swift timeline to see how many observations it's taken. This might take a few seconds to process. query method will return True if everything is OK, False if there is a problem. If there is a problem, simply look at the contents of the status attribute.

if query.submit():
    print("Success!")
else:
    print(f"Fail or timeout? {query.status}")
Success!

Looks like that worked, let's take a look at the status attribute anyway.

query.status
ParameterValue
usernameanonymous
jobnumber281226
statusAccepted

Examining the results of the query

So how many observations has Swift taken of this target?

print(f"This many: {len(query)}")
This many: 77

That's a lot of damage. Here's a thing to remember, every entry in this is a single snapshot of observation. As Swift is in a low Earth orbit, it means that a snapshot is typically max 30 mins, or sometimes a bit longer (~44 mins), so a long exposure will consist of multiple snapshots. Observations are grouped by obsid (a 12 digit number with leading zeros), so snapshots with the same obsid are part of the same planned observation.

query
Begin TimeEnd TimeTarget NameObservation NumberExposure (s)Slewtime (s)
2005-07-30 13:23:022005-07-30 13:27:00SS4330003519000195143
2005-07-30 13:27:022005-07-30 13:30:48SS4330003519000221016
2005-08-01 13:36:022005-08-01 13:40:00SS4330003519000595143
2005-08-01 13:40:022005-08-01 13:56:58SS43300035190006100016
2005-08-01 18:44:152005-08-01 18:46:32SS433000351900070137
2005-08-01 18:46:352005-08-01 19:08:01SS43300035190008127016
2005-08-18 00:59:022005-08-18 01:14:58SS4330003519001387086
2005-08-18 02:35:022005-08-18 02:51:58SS4330003519001393086
2006-06-30 13:45:022006-06-30 13:55:00SS 43300035190014425173
2006-06-30 15:19:022006-06-30 15:31:00SS 43300035190014545173
2006-06-30 16:56:022006-06-30 17:08:00SS 43300035190014545173
2006-06-30 18:32:022006-06-30 18:44:00SS 43300035190014545173
2006-06-30 20:09:022006-06-30 20:21:00SS 43300035190014545173
2006-06-30 21:45:012006-06-30 21:57:00SS 43300035190014545174
2006-06-30 23:22:022006-06-30 23:27:00SS 43300035190014125173
2006-07-01 02:34:022006-07-01 02:43:56SS 43300035190015435159
2006-07-01 04:11:022006-07-01 04:24:56SS 43300035190015675159
2006-07-01 05:47:022006-07-01 05:59:01SS 43300035190015560159
2006-07-01 07:24:022006-07-01 07:34:58SS 43300035190015485171
2006-07-01 09:00:022006-07-01 09:11:57SS 43300035190015535180
2006-07-01 10:36:022006-07-01 10:47:57SS 43300035190015535180
2006-07-01 12:13:022006-07-01 12:23:58SS 43300035190015485171
2006-07-01 20:15:022006-07-01 20:25:58SS 43300035190015475181
2006-07-01 21:51:022006-07-01 22:02:57SS 43300035190015535180
2006-07-02 02:41:022006-07-02 02:50:58SS 43300035190015415181
2006-07-02 04:17:022006-07-02 04:31:57SS 43300035190015715180
2006-07-02 05:53:022006-07-02 06:04:57SS 43300035190015540175
2006-07-02 07:30:022006-07-02 07:40:57SS 43300035190015480175
2006-07-02 09:06:022006-07-02 09:16:57SS 43300035190015480175
2006-07-02 10:43:022006-07-02 10:53:56SS 43300035190015480174
2006-11-03 18:42:582006-11-03 18:46:57SS4330003519001620219
2006-11-03 19:06:022006-11-03 19:28:25SS43300035190016128558
2006-11-03 20:19:352006-11-03 21:04:35SS433000351900162481219
2006-11-03 21:55:592006-11-03 22:20:08SS433000351900161230219
2007-09-12 13:31:012007-09-12 13:45:59SS43300035190020785113
2007-09-13 13:37:012007-09-13 13:47:58SS43300035190021535122
2007-09-14 13:37:012007-09-14 13:52:00SS43300035190022790109
2007-09-15 01:02:012007-09-15 01:15:56SS4330003519002378550
2007-09-16 17:11:012007-09-16 17:23:57SS4330003519002372551
2007-09-27 21:17:012007-09-27 21:34:58SS4330003519002599582
2012-10-15 23:45:202012-10-16 00:03:22SS43300035190026100082
2014-07-28 14:26:502014-07-28 14:32:27SS43300035190027185152
2014-07-28 15:24:352014-07-28 15:56:23SS43300035190027181098
2014-09-26 07:20:472014-09-26 07:25:58SS43300035190028185126
2014-09-26 08:33:022014-09-26 08:45:57SS43300035190028655120
2014-09-26 10:14:022014-09-26 10:18:32SS43300035190028150120
2014-10-01 18:00:022014-10-01 18:14:00SS4330003519003074593
2014-10-03 13:10:022014-10-03 13:26:59SS43300035190031845172
2014-10-04 03:33:022014-10-04 03:46:57SS4330008080300174095
2014-10-04 08:18:022014-10-04 08:34:57SS43300080803001865150
2014-10-04 13:07:022014-10-04 13:16:57SS43300080803001490105
2014-10-05 13:18:092014-10-05 13:33:57SS4330003519003288068
2014-10-07 00:24:022014-10-07 00:40:59SS43300035190033870147
2014-10-09 00:12:022014-10-09 00:28:59SS4330003519003493582
2014-10-17 04:47:022014-10-17 04:59:57SS43300080803002655120
2014-10-17 06:34:022014-10-17 06:52:41SS433000808030021015104
2014-10-17 14:52:022014-10-17 15:02:57SS43300080803002510145
2014-10-30 16:06:022014-10-30 16:24:58SS43300080803003107066
2014-10-30 17:46:022014-10-30 18:00:58SS4330008080300383066
2014-11-12 20:14:022014-11-12 20:35:59SS433000808030041170147
2014-11-12 21:50:022014-11-12 22:08:59SS43300080803004960177
2014-11-25 11:56:022014-11-25 12:25:58SS433000808030051585211
2014-11-25 13:32:022014-11-25 13:41:58SS43300080803005445151
2015-02-24 23:17:022015-02-24 23:46:58SS433000808030061655141
2015-03-11 05:28:012015-03-11 05:47:00SS43300080803008105584
2015-03-11 07:05:022015-03-11 07:21:59SS4330008080300892592
2015-03-23 22:11:022015-03-23 22:29:57SS43300080803009105580
2015-03-23 23:44:022015-03-23 23:59:56SS4330008080300987579
2015-04-18 19:22:022015-04-18 19:51:57SS433000808030101675120
2015-04-18 21:07:102015-04-18 21:10:29SS433000808030100199
2015-06-22 22:47:022015-06-22 23:06:59SS43300035190035113562
2015-07-05 08:54:022015-07-05 08:55:50SS433000808030111593
2015-07-05 10:49:022015-07-05 11:09:59SS43300080803011120552
2018-07-25 07:17:022018-07-25 07:24:59SS43300035190036355122
2018-07-25 16:48:022018-07-25 17:00:58SS4330003519003668096
2018-08-29 10:43:022018-08-29 10:50:59SS4330003519003739582
2018-08-29 23:31:012018-08-29 23:45:57SS4330003519003780591

Wow that is a lot of observations.

Pointing Accuracy

Here's an interesting thing about Swift, it doesn't point very accurately. This is because the ACS system sacrifices accuracy for speed. The goal is to get the object of interest into the field of view of XRT and UVOT, not at the boresight. As a result, the pointing direction be typically up to 3 arcminutes off the requested pointing direction. Note that for each entry listed above, we give an ra and dec value, so let's check out the variation:

plt.figure()
plt.plot(
    [float(entry.ra) for entry in query], [float(entry.dec) for entry in query], "+"
)
plt.plot(
    [entry.ra_object for entry in query], [entry.dec_object for entry in query], "X"
)
plt.xlabel("RA(J2000)")
plt.ylabel("Dec(J2000)")
_ = plt.title(f"{query.name} pointing scatter")

png

As you can see there's a lot of variation of the pointing direction. Each entry also has a values ra_object, dec_object, which give the decimal degrees values of the requested pointing direction for each observation. This typically will be the coordinates of the Target of the Observation, but sometimes if offsets are applied for any reason, it might differ.

The ra_object/dec_object values aren't necessarily going to be for the object you queried on. In fact there can be multiple values of ra_point/dec_point if the queried field lies inside multiple pointings.

Although the RA/Dec are returned in J2000 decimal degrees, they're also returned as skycoords. However, ra_object and dec_object are not so we will have to convert those to SkyCoords manually.

query[0].skycoord
<SkyCoord (FK5: equinox=J2000.000): (ra, dec) in deg
    (287.994, 4.972)>

Sometimes ra_object, dec_object cannot be determined, so the value will be 'None'. I'm going to filter those out so we can do some comparing.

from astropy.coordinates import SkyCoord
import astropy.units as u

sc = SkyCoord([entry.skycoord for entry in query if entry.ra_point != None])
scp = SkyCoord(
    [entry.ra_point for entry in query if entry.ra_point != None],
    [entry.dec_point for entry in query if entry.ra_point != None],
    frame="fk5",
    unit=(u.deg, u.deg),
)

I made an array of ra_point/dec_point so we can evaluate how accurately Swift actually pointed at this target. Let's make a histogram of the pointing offsets.

plt.figure()
plt.hist(sc.separation(scp).arcmin, bins=30)
plt.ylabel("Number of pointings")
plt.xlabel("Offset from requested pointing direction (arc-minutes)")
print(f"Median offset value = {np.median(sc.separation(scp).arcmin):0.2f} arc-minutes")
Median offset value = 2.17 arc-minutes

png

So you'll see that the median offset here is around 2 arc-minutes, with a pretty big scatter, and there may even be some large outliers.

Grouping Snapshots into Observations

Let's take a look at an individual entry to see what information is being returned by this query.

query[0]
Begin TimeEnd TimeTarget NameObservation NumberExposure (s)Slewtime (s)
2005-07-30 13:23:022005-07-30 13:27:00SS4330003519000195143

So some useful information here. Firstly remember each entry represents a snapshot of Swift data, that is data taken in a single orbit of observations. Typically data that you obtain from the SDC will be grouped by observation, and those observations can contain many snapshots. Observations have a unique target ID (target ID) and segment (seg) numbers. These typically are combined into a Observation Number (obsnum), which in the SDC format will look like a concatibation of the target ID, and segment, with padding zeros, e.g.:

print(
    f"Target ID = {query[0].targetid}, segment = {query[0].seg}, ObservationID = {query[0].obsnum}"
)
Target ID = 35190, segment = 1, ObservationID = 00035190001

If you're interested in all the observations under a particular Observation ID, then there's a property called "observations" that contains a dictionary of all observations on an Observation ID basis. Let's look at the summary of this dictionary by just printing out all the entries.

query.observations
Begin TimeEnd TimeTarget NameObservation NumberExposure (s)Slewtime (s)
2005-07-30 13:23:022005-07-30 13:27:00SS4330003519000195143
2005-07-30 13:27:022005-07-30 13:30:48SS4330003519000221016
2005-08-01 13:36:022005-08-01 13:40:00SS4330003519000595143
2005-08-01 13:40:022005-08-01 13:56:58SS43300035190006100016
2005-08-01 18:44:152005-08-01 18:46:32SS433000351900070137
2005-08-01 18:46:352005-08-01 19:08:01SS43300035190008127016
2005-08-18 00:59:022005-08-18 02:51:58SS433000351900131800172
2006-06-30 13:45:022006-06-30 23:27:00SS 4330003519001432751212
2006-07-01 02:34:022006-07-02 10:53:56SS 4330003519001578302600
2006-11-03 18:42:582006-11-03 22:20:08SS433000351900165016715
2007-09-12 13:31:012007-09-12 13:45:59SS43300035190020785113
2007-09-13 13:37:012007-09-13 13:47:58SS43300035190021535122
2007-09-14 13:37:012007-09-14 13:52:00SS43300035190022790109
2007-09-15 01:02:012007-09-16 17:23:57SS433000351900231510101
2007-09-27 21:17:012007-09-27 21:34:58SS4330003519002599582
2012-10-15 23:45:202012-10-16 00:03:22SS43300035190026100082
2014-07-28 14:26:502014-07-28 15:56:23SS433000351900271995250
2014-09-26 07:20:472014-09-26 10:18:32SS43300035190028990366
2014-10-01 18:00:022014-10-01 18:14:00SS4330003519003074593
2014-10-03 13:10:022014-10-03 13:26:59SS43300035190031845172
2014-10-04 03:33:022014-10-04 13:16:57SS433000808030012095350
2014-10-05 13:18:092014-10-05 13:33:57SS4330003519003288068
2014-10-07 00:24:022014-10-07 00:40:59SS43300035190033870147
2014-10-09 00:12:022014-10-09 00:28:59SS4330003519003493582
2014-10-17 04:47:022014-10-17 15:02:57SS433000808030022180369
2014-10-30 16:06:022014-10-30 18:00:58SS433000808030031900132
2014-11-12 20:14:022014-11-12 22:08:59SS433000808030042130324
2014-11-25 11:56:022014-11-25 13:41:58SS433000808030052030362
2015-02-24 23:17:022015-02-24 23:46:58SS433000808030061655141
2015-03-11 05:28:012015-03-11 07:21:59SS433000808030081980176
2015-03-23 22:11:022015-03-23 23:59:56SS433000808030091930159
2015-04-18 19:22:022015-04-18 21:10:29SS433000808030101675319
2015-06-22 22:47:022015-06-22 23:06:59SS43300035190035113562
2015-07-05 08:54:022015-07-05 11:09:59SS433000808030111220145
2018-07-25 07:17:022018-07-25 17:00:58SS433000351900361035218
2018-08-29 10:43:022018-08-29 23:45:57SS433000351900371200173

You can see that now the summary shows the details for an entire observation, with the begin and end times being those of associated with the first and last observation of that Observation ID, and the exposure time being the total. Importantly due to orbit gaps, the exposure time is not just end minus begin. Each entry in the observations dictionary contains details on the individual segments also. Note that Observation ID is a string, given as it is formatted with padding zeros. For example:

query.observations["00035190015"]
Begin TimeEnd TimeTarget NameObservation NumberExposure (s)Slewtime (s)
2006-07-01 02:34:022006-07-02 10:53:56SS 4330003519001578302600

If we want to see what snapshots make up a particular observation, it's easy:

query.observations["00035190015"].snapshots
Begin TimeEnd TimeTarget NameObservation NumberExposure (s)Slewtime (s)
2006-07-01 02:34:022006-07-01 02:43:56SS 43300035190015435159
2006-07-01 04:11:022006-07-01 04:24:56SS 43300035190015675159
2006-07-01 05:47:022006-07-01 05:59:01SS 43300035190015560159
2006-07-01 07:24:022006-07-01 07:34:58SS 43300035190015485171
2006-07-01 09:00:022006-07-01 09:11:57SS 43300035190015535180
2006-07-01 10:36:022006-07-01 10:47:57SS 43300035190015535180
2006-07-01 12:13:022006-07-01 12:23:58SS 43300035190015485171
2006-07-01 20:15:022006-07-01 20:25:58SS 43300035190015475181
2006-07-01 21:51:022006-07-01 22:02:57SS 43300035190015535180
2006-07-02 02:41:022006-07-02 02:50:58SS 43300035190015415181
2006-07-02 04:17:022006-07-02 04:31:57SS 43300035190015715180
2006-07-02 05:53:022006-07-02 06:04:57SS 43300035190015540175
2006-07-02 07:30:022006-07-02 07:40:57SS 43300035190015480175
2006-07-02 09:06:022006-07-02 09:16:57SS 43300035190015480175
2006-07-02 10:43:022006-07-02 10:53:56SS 43300035190015480174

You can query the exposure and other infomation for the combined snapshots in the Observation ID.

query.observations["00035190015"].exposure
datetime.timedelta(seconds=7830)

Note that the result here is a datetime timedelta object. You can easily get the seconds as an integer or convert to an astropy TimeDelta object.

query.observations["00035190015"].exposure.seconds
7830
from astropy.time import TimeDelta

TimeDelta(query.observations["00035190015"].exposure)
<TimeDelta object: scale='None' format='datetime' value=2:10:30>

Note that there is no RA/Dec (ra/dec) for an observation, only the requested pointing direction (ra_point/dec_point), because actual RA/Dec will be different for each snapshot, so delve into the individual snapshots for those.

query.observations["00035190015"].ra_point, query.observations["00035190015"].dec_point
(287.9565, 4.982667)

Instrument Configuration

All observations for a given Observation ID will have the same instrument configuration. Let's check those out.

print(
    f"XRT mode = {query.observations['00035190015'].xrt}, UVOT mode = {query.observations['00035190015'].uvot}"
)
XRT mode = Auto, UVOT mode = 0x20ed

In this case the XRT mode is Auto, which means that XRT itself decides whether to be in PC or WT mode, based on the brightness of sources in the central 200x200 pixels of the detector, roughly the central 8.5 arcmin x 8.5 arcmin box. Because of this we can't determine what mode XRT will have actually taken the data in without looking at it. However for many observation, the mode is fixed, here you will see results like so:

print(f"XRT mode = {query.observations['00035190037'].xrt}")
XRT mode = PC

As this is PC mode, we can guarantee that the data are taken in PC mode.

For UVOT the mode above is a hex number 0x20ed. There are a large number of modes that can be used with UVOT, given it's many different combinations of filters, exposure windows, etc. Luckily you can query what this mode means using the UVOT_mode class. We can quickly display a table showing details of the mode as follows:

from swifttools.swift_too import UVOT_mode

UVOT_mode(uvotmode=query.observations["00035190015"].uvot)

UVOT Mode: 0x20ed

Swift Mission Operations Center

The Pennsylvania State University
301 Science Park Road,
Building 2 Suite 332,
State College, PA 16801
USA
☎ +1 (814) 865-6834
📧 swiftods@swift.psu.edu

Swift MOC Team Leads

Mission Director: John Nousek
Science Operations: Jamie Kennea
Flight Operations: Mark Hilliard
UVOT: Michael Siegel
XRT: Jamie Kennea