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
« 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"""
5from datetime import datetime
6from typing import Literal
8from pydantic import BaseModel, ConfigDict, Field, computed_field
11class StrictModel(BaseModel):
12 model_config = ConfigDict(extra="ignore")
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")
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")
52class EventMLyBurst(StrictModel):
53 """MLy-specific burst fields."""
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")
86class EventSingleInspiral(StrictModel):
87 """Single Inspiral fields for one IFO."""
89 # Identifiers
90 single_search: str | None = Field(None, description="Search type")
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)")
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")
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")
124 # Psi parameters
125 single_psi0: float | None = Field(None, description="Psi 0")
126 single_psi3: float | None = Field(None, description="Psi 3")
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")
137 # Beta
138 single_beta: float | None = Field(None, description="Beta")
140 single_f_final: float | None = Field(None, description="Final frequency (Hz)")
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 )
153 single_sigmasq: float | None = Field(None, description="Sigma squared")
154 single_rsqveto_duration: float | None = Field(None, description="RSQ veto duration")
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")
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")
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")
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")
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")
265class EventLinks(StrictModel):
266 """URL links from GraceDB."""
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")
277class NormalizedEvent(StrictModel):
278 """
279 Flat representation of a gravitational wave event.
280 """
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 )
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"
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)")
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")
319 # CBC-specific (from CoincInspiral)
320 coinc: EventCoinc | None = Field(
321 None, description="Fields from CoincInspiral tables"
322 )
324 # Burst-specific (MultiBurst)
325 burst: EventBurst | None = Field(None, description="Fields from MultiBurst tables")
327 # MLy-specific (MLyBurst)
328 mly: EventMLyBurst | None = Field(None, description="Fields from MLyBurst tables")
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 )
344 # Labels
345 labels: EventLabels | None = Field(None, description="Event labels")
347 # File artifacts
348 p_astro: EventPastro | None = Field(None, description="Astrophysical probability")
349 em_bright: EventEMBright | None = Field(None, description="EMBright probability")
351 # Links
352 links: EventLinks | None = Field(None, description="GraceDB URL links")
354 # METADATA
355 last_updated: datetime = Field(description="Last update time")