Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# (c) Stefan Countryman 2016-2018 

2 

3""" 

4Define a FileHandler that makes draft human-readable GCN Circulars for events. 

5""" 

6 

7import logging 

8from textwrap import fill 

9from llama.filehandler import FileHandler, GenerateOnceMixin 

10from llama.files.skymap_info import SkymapInfo 

11from llama.files.team_receipts import TeamEmailReceipt 

12from llama.files.i3 import ( 

13 IceCubeNeutrinoList, 

14 IceCubeNeutrinoListCoincTxt, 

15) 

16from llama.files.i3.json import RctGdbIceCubeNeutrinoList 

17from llama.files.coinc_o2 import ( 

18 RctGdbCoincSkymapO2Large, 

19 CoincAnalysisInitialIcecubeJson, 

20) 

21from llama.files.lvc_skymap import LvcSkymapFits 

22LOGGER = logging.getLogger(__name__) 

23 

24 

25@TeamEmailReceipt.upload_this(subject=lambda s: s.UPLOAD(s).subject) 

26@FileHandler.set_class_attributes 

27class I3LvcGcnDraft(GenerateOnceMixin, FileHandler): 

28 """ 

29 A draft version of the email sent to lvccirc@caeplla2.gsfc.nasa.gov for 

30 distribution to the LVC GCN Circular list. This message is meant to be sent 

31 to a LLAMA member for verification and modifications before being 

32 manually submitted to GCN. 

33 """ 

34 

35 FILENAME = "gwhen_coinc_icecube_gcn_circular_draft.txt" 

36 DEPENDENCIES = ( 

37 SkymapInfo, 

38 IceCubeNeutrinoList, 

39 IceCubeNeutrinoListCoincTxt, 

40 RctGdbCoincSkymapO2Large, 

41 RctGdbIceCubeNeutrinoList, 

42 CoincAnalysisInitialIcecubeJson, 

43 LvcSkymapFits, 

44 ) 

45 

46 @property 

47 def temporally_coincident_neutrinos(self): 

48 """Return the list of temporally-coincident neutrinos fetched from 

49 IceCube.""" 

50 return IceCubeNeutrinoList(self).read_json() 

51 

52 @property 

53 def reconstructed_neutrinos(self): 

54 """Return the list of reconstructed neutrino directions generated by 

55 the MATLAB analysis pipeline.""" 

56 return CoincAnalysisInitialIcecubeJson(self).read_json() 

57 

58 @property 

59 def subject(self): 

60 """Get a highly-descriptive subject line for the Circular email.""" 

61 graceid = SkymapInfo(self).graceid 

62 if CoincAnalysisInitialIcecubeJson(self).best_neutrino is None: 

63 fmt = "LIGO/Virgo {}: IceCube neutrino observations" 

64 else: 

65 fmt = ("LIGO/Virgo {}: FOUND COINCIDENT " 

66 "IceCube neutrino observation") 

67 return fmt.format(graceid) 

68 

69 @property 

70 def authors(self): 

71 """Return a list of authors for this Circular.""" 

72 return fill("""I. Bartos, S. Countryman (Columbia), C. Finley (U 

73 Stockholm), E. Blaufuss (U Maryland), R. Corley, Z. Marka, 

74 S. Marka (Columbia) on behalf of the IceCube 

75 Collaboration.""") 

76 

77 @property 

78 def introduction(self): 

79 """An introductory paragraph for this Circular.""" 

80 num_temp_coinc = len(self.temporally_coincident_neutrinos) 

81 finish = CoincAnalysisInitialIcecubeJson(self).modtime() 

82 finish_time_str = finish.strftime('%c') 

83 intro = ("In an analysis completed at {}, we searched IceCube online " 

84 "track-like neutrino candidates (GFU) detected in a " 

85 "[-500,500] second interval about the LIGO-Virgo trigger " 

86 "{} found by the {} analysis " 

87 "pipeline.").format(finish_time_str, 

88 SkymapInfo(self).graceid, 

89 SkymapInfo(self).pipeline) 

90 if num_temp_coinc == 0: 

91 graceid = SkymapInfo(self).graceid 

92 intro += (" There were NO ONLINE TRACK-LIKE NEUTRINO CANDIDATES " 

93 "DETECTED by IceCube within the 500 " 

94 "second window surrounding {}.").format(graceid) 

95 else: 

96 intro += (" We compared the candidate source directions of {} " 

97 "temporally-coincident neutrino candidates with the " 

98 "below parameters to the {} " 

99 "skymap:").format(num_temp_coinc, 

100 SkymapInfo(self).skymap_filename) 

101 return intro 

102 

103 @property 

104 def neutrino_table(self): 

105 """Return the human-readable neutrino table that will go in this 

106 Circular.""" 

107 with IceCubeNeutrinoListCoincTxt(self).open() as infile: 

108 neutrino_table = infile.read() 

109 return neutrino_table 

110 

111 @property 

112 def table_legend(self): 

113 """A legend explaining the contents of the neutrino table.""" 

114 return ("(dt--time from GW in [seconds]; " 

115 "RA/Dec--sky location in [degrees]; " 

116 "E--reconstructed secondary muon energy in [TeV]; " 

117 "Sigma--uncertainty of direction reconstruction in [degrees])") 

118 

119 @property 

120 def conclusion(self): 

121 """A conclusion paragraph for this Circular.""" 

122 # pylint: disable=invalid-name 

123 n = CoincAnalysisInitialIcecubeJson(self).best_neutrino 

124 if n is None: 

125 fmt = fill("""The analysis found NO COINCIDENT ONLINE TRACK-LIKE 

126 NEUTRINO CANDIDATES detected by IceCube within the 500 

127 second window surrounding {} within the {} skymap.""") 

128 else: 

129 fmt = fill("""The analysis FOUND A COINCIDENT ONLINE TRACK-LIKE 

130 NEUTRINO CANDIDATE detected by IceCube within the 500 

131 second window surrounding {} within the {} skymap. The 

132 coordinates of the reconstructed neutrino source are 

133 below:""") 

134 return fmt.format(SkymapInfo(self).graceid, 

135 SkymapInfo(self).skymap_filename) 

136 

137 @property 

138 def reconstructed_neutrino_table(self): 

139 """A table showing the value of the reconstructed neutrino.""" 

140 # pylint: disable=invalid-name 

141 n = CoincAnalysisInitialIcecubeJson(self).best_neutrino 

142 if n is None: 

143 return '' 

144 column_titles = ['dt[s]', 'RA[deg]', 'Dec[deg]', 'E[TeV]', 

145 'Sigma[deg]'] 

146 title_fmt = '#' + (' ' * 5) + ('{: >12}' * len(column_titles)) 

147 title = title_fmt.format(*column_titles) 

148 separator = '-' * (6 + 12*len(column_titles)) 

149 n_fmt = '1. {:11.2f} {:11.1f} {:11.1f} {:11.2f} {:11.1f}' 

150 n_row = n_fmt.format(float(n['dt']), n['RA'], n['DEC'], 

151 n['energy_gev'] / 1e3, n['sigma_deg']) 

152 return '{}\n{}\n{}'.format(title, separator, n_row) 

153 

154 @property 

155 def uploaded_file_links(self): 

156 """Don't mention GraceDB uploaded files if we haven't uploaded there 

157 for whatever reason.""" 

158 fhclass = RctGdbCoincSkymapO2Large 

159 skymap_fh = fhclass(self) 

160 skymap_dict = skymap_fh.upload_dict 

161 neutrino_fh = RctGdbIceCubeNeutrinoList(self) 

162 neutrino_dict = neutrino_fh.upload_dict 

163 

164 if neutrino_dict is not None and skymap_dict is not None: 

165 fmt = ("A coincident neutrino-GW skymap has been posted to " 

166 "GraceDB (<{}>). A JSON-formatted list of the above " 

167 "neutrinos can be downloaded from GraceDB at: <{}>\n\n") 

168 # we want the human-readable API, which has the same URLs as the 

169 # REST API, but with 'api' replaced by 'apiweb' in the URL. 

170 neutrino_url = neutrino_dict['file'].replace('/api/', '/apiweb/') 

171 skymap_url = skymap_dict['file'].replace('/api/', '/apiweb/') 

172 return fmt.format(skymap_url, neutrino_url) 

173 return '' 

174 

175 @property 

176 def postscript(self): 

177 """A postscript with some extra info about the LLAMA project.""" 

178 return fill( 

179 """In addition, we are performing coincident searches with other 

180 IceCube data streams, including the high-energy starting events 

181 (HESE) and Supernova triggers. HESE events have typical energies > 

182 60 TeV and start inside the detector volume, leading to a 

183 relatively pure event sample with a high fraction of astrophysical 

184 neutrinos. The SN trigger system is sensitive to sudden increases 

185 in photomultiplier counts across the detector, which could indicate 

186 a burst of MeV neutrinos. We will submit separate GCN circulars if 

187 coincident HESE or SN triggers are found.""" 

188 ) 

189 

190 @property 

191 def description(self): 

192 """A description of IceCube.""" 

193 return fill( 

194 """The IceCube Neutrino Observatory is a cubic-kilometer neutrino 

195 detector operating at the geographic South Pole, Antarctica. For a 

196 description of the IceCube realtime alert system, please refer to 

197 <http://adsabs.harvard.edu/cgi-bin/bib_query?arXiv:1610.01814>; for 

198 more information on joint neutrino and gravitational wave searches, 

199 please refer to 

200 <http://adsabs.harvard.edu/cgi-bin/bib_query?arXiv:1602.05411>.""" 

201 ) 

202 

203 def _generate(self): # pylint: disable=arguments-differ 

204 """There are three possible structures for this GCN circular based on 

205 whether we have: 

206 

207 1. No temporally coincident neutrinos 

208 2. 1 or more temporally coincident neutrinos with no spatial 

209 coincidence 

210 3. 1 or more temporally and spatially coincident neutrinos 

211 

212 The included paragraphs and their ordering vary somewhat based on the 

213 result.""" 

214 with open(self.fullpath, 'w') as f: # pylint: disable=invalid-name 

215 # always write authors first 

216 f.write('{}\n\n'.format(self.authors)) 

217 # in case of no temporal coincidence 

218 if not self.temporally_coincident_neutrinos: 

219 # If there are no IceCube neutrinos, it is not worth having a 

220 # separate conclusion. The lack of neutrinos can just be stated 

221 # in the introduction section. 

222 f.write('{}\n\n'.format(self.introduction)) 

223 # in case of temporal coincidence, no spatial coincidence 

224 elif (CoincAnalysisInitialIcecubeJson(self).best_neutrino 

225 is None): 

226 f.write('{}\n\n'.format(self.introduction)) 

227 f.write('{}\n\n'.format(self.neutrino_table)) 

228 f.write('{}\n\n'.format(self.table_legend)) 

229 f.write('{}\n\n'.format(self.conclusion)) 

230 # in case of full temporal and spatial coincidence 

231 else: 

232 f.write('{}\n\n'.format(self.conclusion)) 

233 f.write('{}\n\n'.format(self.reconstructed_neutrino_table)) 

234 f.write('{}\n\n'.format(self.introduction)) 

235 f.write('{}\n\n'.format(self.neutrino_table)) 

236 f.write('{}\n\n'.format(self.table_legend)) 

237 # always add available uploaded file links, postscript, and 

238 # description 

239 f.write(self.uploaded_file_links) 

240 f.write('{}\n\n'.format(self.postscript)) 

241 f.write('{}\n\n'.format(self.description))