swift_too
moduleswifttools
version = 2.4The Swift_TOO class is used to submit Target of Opportunity Requests for the Swift mission. Before this required manual filling out of a web based form, but given the requirements to trigger Swift using algorithmically determined criteria, this can now be automated through the Swift TOO API, and this class. Here we give a simple example of how to submit a TOO request with this class. Note that we will enable debug mode here, so that the submission will actually complete, but importantly it will not submit an actual Swift TOO!
from swifttools.swift_too import TOO, Resolve
We start by initializing the class and giving our username and shared secret. These can be set up on the Swift TOO website. After you log in, you will find your shared secret, and can modify it as necessary under the Update Account Info link. Note that unlike other TOO API classes, you cannot use anonymous login to submit a TOO request.
username = "myuser"
shared_secret = "my_shared_secret"
OK let's set up the Swift_TOO
request (in this case we'll use the swifttools
2.3 shorthand, TOO
. Also we're setting debug mode here. Note that I'm not passing my shared_secret
here, because my computer supports keyring
. This records your shared_secret
the first time that you use it, so it's not necessary to include it in later requests.
too = TOO()
too.username = username
# too.shared_secret = shared_secret
too.debug = True
OK, so what are we going to observe, let's start with a name, oh say, SMC X-3, that's a cool object. However, darn, I can't remember the coordinates, but thankfully we have a class called Swift_Resolve
(we'll use Resolve
for short) for looking these up.
too.source_name = "SMC X-3"
res = Resolve(name=too.source_name)
print(f"RA/Dec (J2000) = {res.ra:.4f},{res.dec:.4f}")
print(f"SkyCoord = {res.skycoord}")
RA/Dec (J2000) = 13.0234,-72.4345
SkyCoord = <SkyCoord (FK5: equinox=J2000.000): (ra, dec) in deg
(13.02343792, -72.43450778)>
Swift_Resolve
reports back ra
and dec
, but it also reports back as a SkyCoord
using the skycoord
property if you have astropy
installed. Swift_TOO
can take a SkyCoord
directly.
too.skycoord = res.skycoord
If you use a SkyCoord
, it means you can also use more other coordinate systems or formats, rather than just J2000 decimal degrees. Internally all Swift TOOs are stored as decimal RA/Dec in J2000, as this is what Swift uses. Let's check what the values are.
print(f"RA/Dec (J2000) = {too.ra:.4f}, {too.dec:.4f}")
RA/Dec (J2000) = 13.0234, -72.4345
For those who don't want to use astroquery or simbad, you can just do this. Note you can give any number of decimal places, but 4 gives accuracy to less than an arc-second, and Swift isn't that accurate itself.
too.ra, too.dec = 13.0234, -72.4345
Finally, what kind of source is SMC X-3?
too.source_type = "Be/X-ray Binary"
OK, now we've determined what we're looking at, and the coordinates, let's decide what we want to do with this TOO. Well, our object SMC X-3 seems to have gone into outburst again, that's pretty interesting. It's pretty bright also, and evolves quickly, so let's say, oh, we're going to look at it for 1ks per day, every other day for two weeks. Sounds good.
too.exp_time_per_visit = 1000
too.monitoring_freq = "2 days"
too.num_of_visits = 7
Need to justify that exposure time. Not too many words.
too.exp_time_just = "1ks to measure flux and period"
We also need to explain briefly why we're requesting this. One sentence version...
too.immediate_objective = (
"Track the X-ray evolution and pulsar period in this new outburst of SMC X-3"
)
How quickly should Swift respond to this. We have 4 options.
So in our case, SMC X-3 just got detected as in outburst, so we'd like observations ASAP, but 4 hour doesn't really make sense, so....
too.urgency = 2
OK, now we have to define what sort of observation type we're doing here, i.e. what is the primary objective of the observation. There are four options.
_ = [print(f"{t[0]}: {t[1]}") for t in enumerate(too.obs_types)]
0: Spectroscopy
1: Light Curve
2: Position
3: Timing
We're interested primarily in the light curve here, so we'll select that.
too.obs_type = too.obs_types[1]
OK, now we've told the TOO_API what we're looking at, what it is, how long and often we want to look at it, and how quickly. What next? Instrument set-up.
Swift has three telescopes on-board, the UV/Optical Telescope (UVOT), X-ray Telescope (XRT) and Burst Alert Telescope (BAT). Typically TOOs are going to be focused on the first two, as BAT has an extremely large field of view. First step is to pick a primary instrument, this doesn't mean you don't use both, but it helps the teams focus on what you really want here. If you can't decide, roll a dice.
too.instrument = "XRT"
As we're saying XRT is our primary instrument, we should give an important detail for that instrument, in this case brightness.
too.xrt_countrate = 20
OK, so we're observing with XRT. This is a very simple instrument to configure, as it only has three options: Windowed Timing (WT), Photon Counting (WT) and 'Auto'. Here our choice is driven by source brightness, and at our estimated countrate of 20, we'll going to need WT mode to avoid pile-up. 'Auto' picks the mode based on source brightness, but if we know the brightness, it's best to pick ourselves.
too.xrt_mode = "WT"
Note that XRT modes are internally converted to numbers, Auto = 0, PC = 7 and WT = 6. You can use these if you like.
too.xrt_mode = 6
too.xrt_mode
'WT'
Basic validation of mode is done internally, so we can't set the mode to something wrong. The next two commands should return errors.
too.xrt_mode = "Bananas"
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [19], in <cell line: 1>()
----> 1 too.xrt_mode = "Bananas"
File ~/TOO_API/release/1.2/python/lib/python3.9/site-packages/swifttools/swift_too/common.py:762, in TOOAPI_Instruments.xrt(self, mode)
760 @xrt.setter
761 def xrt(self, mode):
--> 762 self.xrt_mode_setter("xrt", mode)
File ~/TOO_API/release/1.2/python/lib/python3.9/site-packages/swifttools/swift_too/common.py:743, in TOOAPI_Instruments.xrt_mode_setter(self, attr, mode)
741 setattr(self, f"_{attr}", modesxrt[mode])
742 else:
--> 743 raise NameError(f"Unknown mode ({mode}), should be PC, WT or Auto")
744 elif mode is None:
745 setattr(self, f"_{attr}", mode)
NameError: Unknown mode (Bananas), should be PC, WT or Auto
too.xrt_mode = 96
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [20], in <cell line: 1>()
----> 1 too.xrt_mode = 96
File ~/TOO_API/release/1.2/python/lib/python3.9/site-packages/swifttools/swift_too/common.py:762, in TOOAPI_Instruments.xrt(self, mode)
760 @xrt.setter
761 def xrt(self, mode):
--> 762 self.xrt_mode_setter("xrt", mode)
File ~/TOO_API/release/1.2/python/lib/python3.9/site-packages/swifttools/swift_too/common.py:750, in TOOAPI_Instruments.xrt_mode_setter(self, attr, mode)
748 setattr(self, f"_{attr}", mode)
749 else:
--> 750 raise ValueError(
751 f"Unknown mode ({mode}), should be PC (7), WT (6) or Auto (0)"
752 )
ValueError: Unknown mode (96), should be PC (7), WT (6) or Auto (0)
UVOT is a more complicated instrument to configure, having as it does a large number of combinations of filters. Default for UVOT is filter of the day, which is mode 0x9999. If you want to pick a filter, you can look up the correct mode on the UVOT modes web page.
I think we should look at this guy using UVOT's u filter, so let's pick that.
too.uvot_mode = 0x01AB
Note that we don't do any internal validation of the UVOT mode, because the UVOT mode table is huge, so outside the scope of this. We will check the mode is valid at the server side and report back an error if it's not. Note that although we can set uvot_mode
as an integer, when we print it, it always reports as a hex string.
too.uvot_mode
'0x01ab'
Sanity check time, is this UVOT mode the one we want? We can always check with the UVOT_mode
class.
from swifttools.swift_too import UVOT_mode
UVOT_mode(too.uvot_mode)
The following table summarizes this mode, ordered by the filter sequence:
Filter | Event FOV | Image FOV | Bin Size | Max. Exp. Time | Weighting | Comments |
---|---|---|---|---|---|---|
u | None | 17 | 1 | 3000 | 3000 | FILL THE SNAPSHOT |
Filter: The particular filter in the sequence.
Event FOV: The size of the FOV
(in arc-minutes) for UVOT event data.
Image FOV: The size of the FOV (in arc-minutes) for UVOT image
data.
Max. Exp. Time: The maximum amount of time the snapshot will spend on the particular filter in the
sequence.
Weighting: Ratio of time spent on the particular filter in the sequence.
Comments:
Additional notes that may be useful to know.
Note if you don't know what UVOT mode you want, you can just put some text in there.
Note if you don't know what UVOT mode you want, you can just put some text in there.
too.uvot_mode = (
"I think I want all UV filters for this, whatever the UVOT team recommends."
)
OK, one last sanity check before submission, do this pass the internal checks for submission?
if too.validate():
print("Good to go!")
ERROR: Missing key: science_just
Looks like we forgot something, so we should figure that out! science_just is the clue here, we didn't enter a science justification.
OK, let's write a science justification for this request. This should be persuasive as to why this object requires both rapid observations, and the interest of the Swift mission. Think of writing a proposal, but in a paragraph or two. Let's give it a go...
too.science_just = (
"SMC X-3 has been detected to be entering it's 3rd outburst since discovery."
"We wish to monitor the flux and pulsar period in order to track the evolution "
"of this Be/XRB which has previously shown to enter a Super-Eddington level outburst. "
"As the outburst has been detected in it's early stages, we request observations begin "
"ASAP, and request an initial 2 week monitoring period to accurately sample flux rise "
"and pulsar period evolution."
)
if too.validate():
print("Good to go!")
INFO: Total exposure time does not match total of individuals. Correcting.
Good to go!
One thing to note, there should have been an warning here, we never entered the total exposure time for the request, but as we gave the individual exposure time, and number of observations, so it calculated it itself. Let's check it real quick, should be 7 exposure x 1000s each....
You can also validate on the server side, this means that the TOO will be checked for any additional issues before submission. Here we go...
if too.server_validate():
print("Good to go! (server side edition)")
Good to go! (server side edition)
If the server side validation passes with no errors or warnings, we can guarantee that there are no problems with our TOO.
Let's just look at our TOO before we submit it.
too
Parameter | Value |
---|---|
Requester | myuser |
Object Name | SMC X-3 |
Type or Classification | Be/X-ray Binary |
Right Ascenscion (J2000) | 13.0234 |
Declination (J2000) | -72.4345 |
Position Error (90% confidence - arcminutes) | 0.0 |
Instrument | XRT |
Urgency | 2 |
XRT Estimated Rate (c/s) | 20 |
Immediate Objective | Track the X-ray evolution and pulsar period in this new outburst of SMC X-3 |
Science Justification | SMC X-3 has been detected to be entering it's 3rd outburst since discovery.We wish to monitor the flux and pulsar period in order to track the evolution of this Be/XRB which has previously shown to enter a Super-Eddington level outburst. As the outburst has been detected in it's early stages, we request observations begin ASAP, and request an initial 2 week monitoring period to accurately sample flux rise and pulsar period evolution. |
Exposure Time (seconds) | 7000 |
Exposure Time Justification | 1ks to measure flux and period |
Exposure Time per Visit (seconds) | 1000 |
Number of Visits | 7 |
Monitoring Cadence | 2 days |
XRT Mode | WT |
UVOT Mode | I think I want all UV filters for this, whatever the UVOT team recommends. |
Observation Strategy | multiple |
What is Driving the Exposure Time? | Light Curve |
Debug mode | True |
Validate only | False |
Now it's time to submit our TOO. Remember as we enabled debug mode, this won't submit a TOO request, but it will act like it, including sending you emails saying it's been recieved.
if too.submit():
print(f"Submitted TOO ID = {too.status.too_id}")
else:
print(f"{too.status.status}. Errors: {too.status.errors}")
Rejected. Errors: ['TOO already recently submitted. TOO ID: 13837']
if too.status:
print("Yes")
else:
print("No")
No
If the above is reports all good then you're good. You should get an email saying you submitted a TOO (yes even in debug mode). Let's check the status of the TOO anyway, this will give us the TOO ID number assigned to our TOO.
However, this is SMC X-3 we're talking about here, it's declination is -72.4345 degrees. That is pretty close to Swift's pole constraint, and we're submitting at urgency = 2
, observations in 24 hours. If you happen to be running this notebook during a period when SMC X-3 is in pole constraint, then you'll see that the request has been rejected, and you'll see an error like this:
Rejected. Errors: ['ERROR: Object currently constrained until 2021-03-20 06:36:00, so 24-hour response TOO rejected.']
If this happens, you can always try resubmitting as Urgency 3 or 4 instead, depending on how long it will be until it comes out of constraint. This will essentially ask the Swift team to please observe it when it becomes visible.
Note: If you want to resubmit a Rejected TOO after modification, you need to clear the current status. This can be done with the status.clear()
method, example below:
if not too.status:
too.urgency = 4 # Change urgency to weeks to months
too.status.clear() # Clear the 'Rejected' status of the previous attempt to submit
# Go ahead and submit again with the revised parameter
if too.submit():
print(f"Submitted TOO ID = {too.status.too_id}")
else:
print(f"{too.status.status}. Errors: {too.status.errors}")
else:
print("TOO was already accepted, so no need for modification.")
Submitted TOO ID = 13838
You might have noticed a little trick here. You can check if a TOO is accepted by simple checking the truth value of too.status
. So if the TOO has been accepted too.status == True
will be return True
.
There is another method for checking if a TOO has been accepted, complete
which is invoked as such:
too.complete
True
Note that this is different, as it actually queries the TOO API server every time it is called to query if the TOO was accepted. Generally the former method is preferred.
if too.status:
print("We are done here.")
else:
print("You shouldn't see this message.")
We are done here.
That is it! There are more configuration options, but this is a simple case. Look at the documentation for more options.