Coverage for event_normalizer/models.py: 100%

234 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-21 20:04 +0000

1""" 

2Unified NormalizedEvent model for gravitational wave events. 

3""" 

4 

5from datetime import datetime 

6from typing import Literal 

7 

8from pydantic import BaseModel, ConfigDict, Field, computed_field 

9 

10 

11class StrictModel(BaseModel): 

12 model_config = ConfigDict(extra="ignore") 

13 

14 

15class EventCoinc(StrictModel): 

16 coinc_mass: float | None = Field(None, description="Total mass (solar masses)") 

17 coinc_mchirp: float | None = Field(None, description="Chirp mass (solar masses)") 

18 coinc_minimum_duration: float | None = Field( 

19 None, description="Minimum duration (seconds)" 

20 ) 

21 coinc_snr: float | None = Field(None, description="Signal-to-Noise Ratio") 

22 coinc_ifos: str | None = Field(None, description="Interferometers (H1,L1,V1)") 

23 coinc_end_time: int | None = Field(None, description="End time (GPS seconds)") 

24 coinc_end_time_ns: int | None = Field(None, description="End time nanoseconds") 

25 coinc_false_alarm_rate: float | None = Field(None, description="False alarm rate") 

26 coinc_combined_far: float | None = Field(None, description="Combined FAR") 

27 

28 

29class EventBurst(StrictModel): 

30 burst_duration: float | None = Field(None, description="Duration (seconds)") 

31 burst_mchirp: float | None = Field(None, description="Chirp mass (solar masses)") 

32 burst_snr: float | None = Field(None, description="Signal-to-Noise Ratio") 

33 burst_ifos: str | None = Field(None, description="Interferometers") 

34 burst_start_time: int | None = Field(None, description="Start time (GPS seconds)") 

35 burst_start_time_ns: int | None = Field(None, description="Start time nanoseconds") 

36 burst_strain: float | None = Field(None, description="Strain amplitude") 

37 burst_peak_time: int | None = Field(None, description="Peak time (GPS seconds)") 

38 burst_peak_time_ns: int | None = Field(None, description="Peak time nanoseconds") 

39 burst_central_freq: float | None = Field(None, description="Central frequency (Hz)") 

40 burst_bandwidth: float | None = Field(None, description="Bandwidth (Hz)") 

41 burst_amplitude: float | None = Field(None, description="Amplitude") 

42 burst_confidence: float | None = Field(None, description="Confidence") 

43 burst_false_alarm_rate: float | None = Field(None, description="False alarm rate") 

44 burst_ligo_axis_ra: float | None = Field(None, description="LIGO axis RA (deg)") 

45 burst_ligo_axis_dec: float | None = Field(None, description="LIGO axis Dec (deg)") 

46 burst_ligo_angle: float | None = Field(None, description="LIGO angle") 

47 burst_ligo_angle_sig: float | None = Field(None, description="LIGO angle sigma") 

48 burst_single_ifo_times: str | None = Field(None, description="Single IFO times") 

49 burst_code: str | None = Field(None, description="Pipeline code") 

50 

51 

52class EventMLyBurst(StrictModel): 

53 """MLy-specific burst fields.""" 

54 

55 mly_central_freq: float | None = Field(None, description="Central frequency (Hz)") 

56 mly_bandwidth: float | None = Field(None, description="Bandwidth (Hz)") 

57 mly_central_time: float | None = Field(None, description="Central time (GPS)") 

58 mly_detection_statistic: float | None = Field( 

59 None, description="Detection statistic" 

60 ) 

61 mly_bbh: float | None = Field(None, description="BBH probability") 

62 mly_sglf: float | None = Field(None, description="SG low frequency") 

63 mly_sghf: float | None = Field(None, description="SG high frequency") 

64 mly_background: float | None = Field(None, description="Background") 

65 mly_glitch: float | None = Field(None, description="Glitch probability") 

66 mly_freq_correlation: float | None = Field( 

67 None, description="Frequency correlation" 

68 ) 

69 mly_mass1: float | None = Field(None, description="Mass 1 (solar masses)") 

70 mly_mass2: float | None = Field(None, description="Mass 2 (solar masses)") 

71 mly_mtotal: float | None = Field(None, description="Total mass (solar masses)") 

72 mly_mchirp: float | None = Field(None, description="Chirp mass (solar masses)") 

73 mly_spin1z: float | None = Field(None, description="Spin 1 z-component") 

74 mly_spin2z: float | None = Field(None, description="Spin 2 z-component") 

75 mly_end_time: int | None = Field(None, description="End time (GPS seconds)") 

76 mly_end_time_ns: int | None = Field(None, description="End time nanoseconds") 

77 mly_template_duration: float | None = Field( 

78 None, description="Template duration (s)" 

79 ) 

80 mly_SNR: float | None = Field(None, description="Signal-to-Noise Ratio") 

81 mly_scores_coherency: float | None = Field(None, description="Coherency score") 

82 mly_scores_coincidence: float | None = Field(None, description="Coincidence score") 

83 mly_scores_combined: float | None = Field(None, description="Combined score") 

84 

85 

86class EventSingleInspiral(StrictModel): 

87 """Single Inspiral fields for one IFO.""" 

88 

89 # Identifiers 

90 single_search: str | None = Field(None, description="Search type") 

91 

92 # Time 

93 single_end_time: int | None = Field(None, description="End time (GPS seconds)") 

94 single_end_time_ns: int | None = Field(None, description="End time nanoseconds") 

95 single_end_time_gmst: float | None = Field(None, description="End time GMST") 

96 single_impulse_time: int | None = Field(None, description="Impulse time") 

97 single_impulse_time_ns: int | None = Field(None, description="Impulse time ns") 

98 single_template_duration: float | None = Field( 

99 None, description="Template duration (s)" 

100 ) 

101 single_event_duration: float | None = Field(None, description="Event duration (s)") 

102 

103 single_amplitude: float | None = Field(None, description="Amplitude") 

104 single_eff_distance: float | None = Field( 

105 None, description="Effective distance (Mpc)" 

106 ) 

107 single_coa_phase: float | None = Field(None, description="Coalescence phase") 

108 single_mass1: float | None = Field(None, description="Mass 1 (solar masses)") 

109 single_mass2: float | None = Field(None, description="Mass 2 (solar masses)") 

110 single_mchirp: float | None = Field(None, description="Chirp mass (solar masses)") 

111 single_mtotal: float | None = Field(None, description="Total mass (solar masses)") 

112 single_eta: float | None = Field(None, description="Symmetric mass ratio") 

113 single_kappa: float | None = Field(None, description="Kappa parameter") 

114 single_chi: float | None = Field(None, description="Chi parameter") 

115 

116 # Tau parameters 

117 single_tau0: float | None = Field(None, description="Tau 0") 

118 single_tau2: float | None = Field(None, description="Tau 2") 

119 single_tau3: float | None = Field(None, description="Tau 3") 

120 single_tau4: float | None = Field(None, description="Tau 4") 

121 single_tau5: float | None = Field(None, description="Tau 5") 

122 single_ttotal: float | None = Field(None, description="Total tau") 

123 

124 # Psi parameters 

125 single_psi0: float | None = Field(None, description="Psi 0") 

126 single_psi3: float | None = Field(None, description="Psi 3") 

127 

128 # Alpha parameters 

129 single_alpha: float | None = Field(None, description="Alpha") 

130 single_alpha1: float | None = Field(None, description="Alpha 1") 

131 single_alpha2: float | None = Field(None, description="Alpha 2") 

132 single_alpha3: float | None = Field(None, description="Alpha 3") 

133 single_alpha4: float | None = Field(None, description="Alpha 4") 

134 single_alpha5: float | None = Field(None, description="Alpha 5") 

135 single_alpha6: float | None = Field(None, description="Alpha 6") 

136 

137 # Beta 

138 single_beta: float | None = Field(None, description="Beta") 

139 

140 single_f_final: float | None = Field(None, description="Final frequency (Hz)") 

141 

142 # SNR e chi-square 

143 single_snr: float | None = Field(None, description="Signal-to-Noise Ratio") 

144 single_chisq: float | None = Field(None, description="Chi-square") 

145 single_chisq_dof: int | None = Field(None, description="Chi-square DOF") 

146 single_bank_chisq: float | None = Field(None, description="Bank chi-square") 

147 single_bank_chisq_dof: int | None = Field(None, description="Bank chi-square DOF") 

148 single_cont_chisq: float | None = Field(None, description="Continuous chi-square") 

149 single_cont_chisq_dof: int | None = Field( 

150 None, description="Continuous chi-square DOF" 

151 ) 

152 

153 single_sigmasq: float | None = Field(None, description="Sigma squared") 

154 single_rsqveto_duration: float | None = Field(None, description="RSQ veto duration") 

155 

156 # Gamma parameters 

157 single_Gamma0: float | None = Field(None, description="Gamma 0") 

158 single_Gamma1: float | None = Field(None, description="Gamma 1") 

159 single_Gamma2: float | None = Field(None, description="Gamma 2") 

160 single_Gamma3: float | None = Field(None, description="Gamma 3") 

161 single_Gamma4: float | None = Field(None, description="Gamma 4") 

162 single_Gamma5: float | None = Field(None, description="Gamma 5") 

163 single_Gamma6: float | None = Field(None, description="Gamma 6") 

164 single_Gamma7: float | None = Field(None, description="Gamma 7") 

165 single_Gamma8: float | None = Field(None, description="Gamma 8") 

166 single_Gamma9: float | None = Field(None, description="Gamma 9") 

167 

168 # Spin 

169 single_spin1x: float | None = Field(None, description="Spin 1 x-component") 

170 single_spin1y: float | None = Field(None, description="Spin 1 y-component") 

171 single_spin1z: float | None = Field(None, description="Spin 1 z-component") 

172 single_spin2x: float | None = Field(None, description="Spin 2 x-component") 

173 single_spin2y: float | None = Field(None, description="Spin 2 y-component") 

174 single_spin2z: float | None = Field(None, description="Spin 2 z-component") 

175 

176 

177class EventLabels(StrictModel): 

178 label_2022_LENSING_MDC: bool | None = Field( 

179 None, description="2022_LENSING_MDC label" 

180 ) 

181 label_ADVNO: bool | None = Field(None, description="ADVNO label") 

182 label_ADVOK: bool | None = Field(None, description="ADVOK label") 

183 label_ADVREQ: bool | None = Field(None, description="ADVREQ label") 

184 label_COMBINEDSKYMAP_READY: bool | None = Field( 

185 None, description="COMBINEDSKYMAP_READY label" 

186 ) 

187 label_DQOK: bool | None = Field(None, description="DQOK label") 

188 label_DQR_REQUEST: bool | None = Field(None, description="DQR_REQUEST label") 

189 label_DQV: bool | None = Field(None, description="DQV label") 

190 label_EARLY_WARNING: bool | None = Field(None, description="EARLY_WARNING label") 

191 label_EMBRIGHT_READY: bool | None = Field(None, description="EMBRIGHT_READY label") 

192 label_EM_COINC: bool | None = Field(None, description="EM_COINC label") 

193 label_EM_READY: bool | None = Field(None, description="EM_READY label") 

194 label_EM_SENT: bool | None = Field(None, description="EM_SENT label") 

195 label_EM_Selected: bool | None = Field(None, description="EM_Selected label") 

196 label_EM_SelectedConfident: bool | None = Field( 

197 None, description="EM_SelectedConfident label" 

198 ) 

199 label_EM_Superseded: bool | None = Field(None, description="EM_Superseded label") 

200 label_EM_Throttled: bool | None = Field(None, description="EM_Throttled label") 

201 label_EXT_SKYMAP_READY: bool | None = Field( 

202 None, description="EXT_SKYMAP_READY label" 

203 ) 

204 label_GCN_PRELIM_SENT: bool | None = Field( 

205 None, description="GCN_PRELIM_SENT label" 

206 ) 

207 label_GRB_OFFLINE: bool | None = Field(None, description="GRB_OFFLINE label") 

208 label_GRB_ONLINE: bool | None = Field(None, description="GRB_ONLINE label") 

209 label_H1NO: bool | None = Field(None, description="H1NO label") 

210 label_H1OK: bool | None = Field(None, description="H1OK label") 

211 label_H1OPS: bool | None = Field(None, description="H1OPS label") 

212 label_HIGH_PROFILE: bool | None = Field(None, description="HIGH_PROFILE label") 

213 label_HWINJNO: bool | None = Field(None, description="HWINJNO label") 

214 label_HWINJOK: bool | None = Field(None, description="HWINJOK label") 

215 label_HWINJREQ: bool | None = Field(None, description="HWINJREQ label") 

216 label_INJ: bool | None = Field(None, description="INJ label") 

217 label_L1NO: bool | None = Field(None, description="L1NO label") 

218 label_L1OK: bool | None = Field(None, description="L1OK label") 

219 label_L1OPS: bool | None = Field(None, description="L1OPS label") 

220 label_LENSED_CANDIDATE: bool | None = Field( 

221 None, description="LENSED_CANDIDATE label" 

222 ) 

223 label_LLAMA_COMPLETE: bool | None = Field(None, description="LLAMA_COMPLETE label") 

224 label_LOW_SIGNIF_LOCKED: bool | None = Field( 

225 None, description="LOW_SIGNIF_LOCKED label" 

226 ) 

227 label_LOW_SIGNIF_PRELIM_SENT: bool | None = Field( 

228 None, description="LOW_SIGNIF_PRELIM_SENT label" 

229 ) 

230 label_LUMIN_GO: bool | None = Field(None, description="LUMIN_GO label") 

231 label_LUMIN_NO: bool | None = Field(None, description="LUMIN_NO label") 

232 label_MOCK: bool | None = Field(None, description="MOCK label") 

233 label_NOT_GRB: bool | None = Field(None, description="NOT_GRB label") 

234 label_PASTRO_READY: bool | None = Field(None, description="PASTRO_READY label") 

235 label_PE_READY: bool | None = Field(None, description="PE_READY label") 

236 label_RAVEN_ALERT: bool | None = Field(None, description="RAVEN_ALERT label") 

237 label_SIGNIF_LOCKED: bool | None = Field(None, description="SIGNIF_LOCKED label") 

238 label_SKYMAP_READY: bool | None = Field(None, description="SKYMAP_READY label") 

239 label_SNR_OPTIMIZED: bool | None = Field(None, description="SNR_OPTIMIZED label") 

240 label_SOG_READY: bool | None = Field(None, description="SOG_READY label") 

241 label_SUBSOLAR_MASS: bool | None = Field(None, description="SUBSOLAR_MASS label") 

242 label_SWIFT_GO: bool | None = Field(None, description="SWIFT_GO label") 

243 label_SWIFT_NO: bool | None = Field(None, description="SWIFT_NO label") 

244 label_V1NO: bool | None = Field(None, description="V1NO label") 

245 label_V1OK: bool | None = Field(None, description="V1OK label") 

246 label_V1OPS: bool | None = Field(None, description="V1OPS label") 

247 label_cWB_r: bool | None = Field(None, description="cWB_r label") 

248 label_cWB_s: bool | None = Field(None, description="cWB_s label") 

249 

250 

251class EventPastro(StrictModel): 

252 p_terr: float | None = Field(None, description="Probability Terrestrial") 

253 p_bns: float | None = Field(None, description="p_astro for BNS") 

254 p_bbh: float | None = Field(None, description="p_astro for BBH") 

255 p_nsbh: float | None = Field(None, description="p_astro for NSBH") 

256 

257 

258class EventEMBright(StrictModel): 

259 has_ns: float | None = Field(None, description="Probability has neutron star") 

260 has_remnant: float | None = Field(None, description="Probability has remnant") 

261 has_mass_gap: float | None = Field(None, description="Probability in mass gap") 

262 has_ssm: float | None = Field(None, description="Probability has ssm") 

263 

264 

265class EventLinks(StrictModel): 

266 """URL links from GraceDB.""" 

267 

268 neighbors: str | None = Field(None, description="Neighbors URL") 

269 log: str | None = Field(None, description="Log URL") 

270 emobservations: str | None = Field(None, description="EM observations URL") 

271 files: str | None = Field(None, description="Files URL") 

272 labels: str | None = Field(None, description="Labels URL") 

273 self: str | None = Field(None, description="Self URL") 

274 tags: str | None = Field(None, description="Tags URL") 

275 

276 

277class NormalizedEvent(StrictModel): 

278 """ 

279 Flat representation of a gravitational wave event. 

280 """ 

281 

282 # Identifiers 

283 uid: str = Field( 

284 ..., 

285 pattern=r"^[GS]\d+(?:[a-z]+)?$", 

286 description="Unique identifier for the event (e.g G123456 or S260813fk)", 

287 ) 

288 

289 @computed_field 

290 @property 

291 def event_type(self) -> Literal["G-event", "S-event"]: 

292 return "G-event" if self.uid.startswith("G") else "S-event" 

293 

294 # COMPULSORY FIELDS 

295 group: str = Field(description="Event group (CBC, Burst)") 

296 pipeline: str = Field(description="Pipeline (gstlal, CWB, MLy, etc.)") 

297 search: str = Field(description="Search type (AllSky, etc.)") 

298 far: float = Field(description="False Alarm Rate (Hz)") 

299 far_is_upper_limit: bool | None = Field(None, description="FAR is upper limit") 

300 instruments: list[str] = Field(description="Detector list (H1,L1,V1)") 

301 H1_channel: str | None = Field("None", description="H1 channel name") 

302 L1_channel: str | None = Field("None", description="L1 channel name") 

303 V1_channel: str | None = Field("None", description="V1 channel name") 

304 K1_channel: str | None = Field("None", description="K1 channel name") 

305 reporting_latency: float = Field(description="Reporting latency (seconds)") 

306 gpstime: float = Field(description="GPS time of event (s)") 

307 

308 # OPTIONAL FIELDS - Root level 

309 alert_type: str | None = Field(None, description="Alert type (e.g., file_uploaded)") 

310 submitter: str | None = Field(None, description="User who submitted") 

311 offline: bool | None = Field(None, description="Offline analysis flag") 

312 nevents: int | None = Field(None, description="Number of events in coincidence") 

313 likelihood: float | None = Field(None, description="Log-likelihood ratio") 

314 superevent: str | None = Field(None, description="Superevent ID") 

315 created: str | None = Field(None, description="Creation time") 

316 processing_status: str | None = Field(None, description="Processing status") 

317 content_id: str | None = Field(None, description="Content ID") 

318 

319 # CBC-specific (from CoincInspiral) 

320 coinc: EventCoinc | None = Field( 

321 None, description="Fields from CoincInspiral tables" 

322 ) 

323 

324 # Burst-specific (MultiBurst) 

325 burst: EventBurst | None = Field(None, description="Fields from MultiBurst tables") 

326 

327 # MLy-specific (MLyBurst) 

328 mly: EventMLyBurst | None = Field(None, description="Fields from MLyBurst tables") 

329 

330 # Single Inspiral per IFO 

331 single_H1: EventSingleInspiral | None = Field( 

332 None, description="Single Inspiral for H1" 

333 ) 

334 single_L1: EventSingleInspiral | None = Field( 

335 None, description="Single Inspiral for L1" 

336 ) 

337 single_V1: EventSingleInspiral | None = Field( 

338 None, description="Single Inspiral for V1" 

339 ) 

340 single_K1: EventSingleInspiral | None = Field( 

341 None, description="Single Inspiral for K1" 

342 ) 

343 

344 # Labels 

345 labels: EventLabels | None = Field(None, description="Event labels") 

346 

347 # File artifacts 

348 p_astro: EventPastro | None = Field(None, description="Astrophysical probability") 

349 em_bright: EventEMBright | None = Field(None, description="EMBright probability") 

350 

351 # Links 

352 links: EventLinks | None = Field(None, description="GraceDB URL links") 

353 

354 # METADATA 

355 last_updated: datetime = Field(description="Last update time")