tdanalysis 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. tdanalysis/__init__.py +0 -0
  2. tdanalysis/_compat.py +173 -0
  3. tdanalysis/covariance/covariance_calc.py +811 -0
  4. tdanalysis/covariance/psd.py +987 -0
  5. tdanalysis/data/acorrs/A1_aplus_acorr_4s_4096.npy +0 -0
  6. tdanalysis/data/acorrs/A1_aplus_acorr_8s_4096.npy +0 -0
  7. tdanalysis/data/acorrs/CE20_1p0MW_Aplus_acorr_4s_4096.npy +0 -0
  8. tdanalysis/data/acorrs/CE20_1p0MW_Aplus_acorr_8s_4096.npy +0 -0
  9. tdanalysis/data/acorrs/CE20_1p0MW_aLIGO_acorr_4s_4096.npy +0 -0
  10. tdanalysis/data/acorrs/CE20_1p0MW_aLIGO_acorr_8s_4096.npy +0 -0
  11. tdanalysis/data/acorrs/CE20_1p5MW_Aplus_acorr_4s_4096.npy +0 -0
  12. tdanalysis/data/acorrs/CE20_1p5MW_Aplus_acorr_8s_4096.npy +0 -0
  13. tdanalysis/data/acorrs/CE20_1p5MW_aLIGO_acorr_4s_4096.npy +0 -0
  14. tdanalysis/data/acorrs/CE20_1p5MW_aLIGO_acorr_8s_4096.npy +0 -0
  15. tdanalysis/data/acorrs/CE20_gwforge_default_acorr_4s_4096.npy +0 -0
  16. tdanalysis/data/acorrs/CE20_gwforge_default_acorr_8s_4096.npy +0 -0
  17. tdanalysis/data/acorrs/CE40_1p0MW_Aplus_acorr_4s_4096.npy +0 -0
  18. tdanalysis/data/acorrs/CE40_1p0MW_Aplus_acorr_8s_4096.npy +0 -0
  19. tdanalysis/data/acorrs/CE40_1p0MW_aLIGO_acorr_4s_4096.npy +0 -0
  20. tdanalysis/data/acorrs/CE40_1p0MW_aLIGO_acorr_8s_4096.npy +0 -0
  21. tdanalysis/data/acorrs/CE40_1p5MW_Aplus_acorr_4s_4096.npy +0 -0
  22. tdanalysis/data/acorrs/CE40_1p5MW_Aplus_acorr_8s_4096.npy +0 -0
  23. tdanalysis/data/acorrs/CE40_1p5MW_aLIGO_acorr_4s_4096.npy +0 -0
  24. tdanalysis/data/acorrs/CE40_1p5MW_aLIGO_acorr_8s_4096.npy +0 -0
  25. tdanalysis/data/acorrs/CE40_gwforge_default_acorr_4s_4096.npy +0 -0
  26. tdanalysis/data/acorrs/CE40_gwforge_default_acorr_8s_4096.npy +0 -0
  27. tdanalysis/data/acorrs/ET1ET2ET3_ET_D_acorr_4s_4096.npy +0 -0
  28. tdanalysis/data/acorrs/ET1ET2ET3_ET_D_acorr_8s_4096.npy +0 -0
  29. tdanalysis/data/acorrs/ET1ET2ET3_cryo_10km_acorr_4s_4096.npy +0 -0
  30. tdanalysis/data/acorrs/ET1ET2ET3_cryo_10km_acorr_8s_4096.npy +0 -0
  31. tdanalysis/data/acorrs/ET1ET2ET3_cryo_15km_acorr_4s_4096.npy +0 -0
  32. tdanalysis/data/acorrs/ET1ET2ET3_cryo_15km_acorr_8s_4096.npy +0 -0
  33. tdanalysis/data/acorrs/PROVENANCE.md +1143 -0
  34. tdanalysis/data/acorrs/half_A1_aplus_acorr_4s_4096.npy +0 -0
  35. tdanalysis/data/acorrs/half_A1_aplus_acorr_8s_4096.npy +0 -0
  36. tdanalysis/data/acorrs/half_CE20_1p0MW_Aplus_acorr_4s_4096.npy +0 -0
  37. tdanalysis/data/acorrs/half_CE20_1p0MW_Aplus_acorr_8s_4096.npy +0 -0
  38. tdanalysis/data/acorrs/half_CE20_1p0MW_aLIGO_acorr_4s_4096.npy +0 -0
  39. tdanalysis/data/acorrs/half_CE20_1p0MW_aLIGO_acorr_8s_4096.npy +0 -0
  40. tdanalysis/data/acorrs/half_CE20_1p5MW_Aplus_acorr_4s_4096.npy +0 -0
  41. tdanalysis/data/acorrs/half_CE20_1p5MW_Aplus_acorr_8s_4096.npy +0 -0
  42. tdanalysis/data/acorrs/half_CE20_1p5MW_aLIGO_acorr_4s_4096.npy +0 -0
  43. tdanalysis/data/acorrs/half_CE20_1p5MW_aLIGO_acorr_8s_4096.npy +0 -0
  44. tdanalysis/data/acorrs/half_CE20_gwforge_default_acorr_4s_4096.npy +0 -0
  45. tdanalysis/data/acorrs/half_CE20_gwforge_default_acorr_8s_4096.npy +0 -0
  46. tdanalysis/data/acorrs/half_CE40_1p0MW_Aplus_acorr_4s_4096.npy +0 -0
  47. tdanalysis/data/acorrs/half_CE40_1p0MW_Aplus_acorr_8s_4096.npy +0 -0
  48. tdanalysis/data/acorrs/half_CE40_1p0MW_aLIGO_acorr_4s_4096.npy +0 -0
  49. tdanalysis/data/acorrs/half_CE40_1p0MW_aLIGO_acorr_8s_4096.npy +0 -0
  50. tdanalysis/data/acorrs/half_CE40_1p5MW_Aplus_acorr_4s_4096.npy +0 -0
  51. tdanalysis/data/acorrs/half_CE40_1p5MW_Aplus_acorr_8s_4096.npy +0 -0
  52. tdanalysis/data/acorrs/half_CE40_1p5MW_aLIGO_acorr_4s_4096.npy +0 -0
  53. tdanalysis/data/acorrs/half_CE40_1p5MW_aLIGO_acorr_8s_4096.npy +0 -0
  54. tdanalysis/data/acorrs/half_CE40_gwforge_default_acorr_4s_4096.npy +0 -0
  55. tdanalysis/data/acorrs/half_CE40_gwforge_default_acorr_8s_4096.npy +0 -0
  56. tdanalysis/data/acorrs/half_ET1ET2ET3_ET_D_acorr_4s_4096.npy +0 -0
  57. tdanalysis/data/acorrs/half_ET1ET2ET3_ET_D_acorr_8s_4096.npy +0 -0
  58. tdanalysis/data/acorrs/half_ET1ET2ET3_cryo_10km_acorr_4s_4096.npy +0 -0
  59. tdanalysis/data/acorrs/half_ET1ET2ET3_cryo_10km_acorr_8s_4096.npy +0 -0
  60. tdanalysis/data/acorrs/half_ET1ET2ET3_cryo_15km_acorr_4s_4096.npy +0 -0
  61. tdanalysis/data/acorrs/half_ET1ET2ET3_cryo_15km_acorr_8s_4096.npy +0 -0
  62. tdanalysis/data/detectors/A1.interferometer +13 -0
  63. tdanalysis/data/detectors/CE20.ifo +11 -0
  64. tdanalysis/data/detectors/CE40.ifo +11 -0
  65. tdanalysis/data/detectors/ET.ifo +12 -0
  66. tdanalysis/data/detectors/PROVENANCE.md +230 -0
  67. tdanalysis/data/noise_curves/CE20-asd.txt +1000 -0
  68. tdanalysis/data/noise_curves/CE20km_1p0MW_Aplus_coat_strain.txt +1000 -0
  69. tdanalysis/data/noise_curves/CE20km_1p0MW_aLIGO_coat_strain.txt +1000 -0
  70. tdanalysis/data/noise_curves/CE20km_1p5MW_Aplus_coat_strain.txt +1000 -0
  71. tdanalysis/data/noise_curves/CE20km_1p5MW_aLIGO_coat_strain.txt +1000 -0
  72. tdanalysis/data/noise_curves/CE40-asd.txt +1000 -0
  73. tdanalysis/data/noise_curves/CE40km_1p0MW_Aplus_coat_strain.txt +1000 -0
  74. tdanalysis/data/noise_curves/CE40km_1p0MW_aLIGO_coat_strain.txt +1000 -0
  75. tdanalysis/data/noise_curves/CE40km_1p5MW_Aplus_coat_strain.txt +1000 -0
  76. tdanalysis/data/noise_curves/CE40km_1p5MW_aLIGO_coat_strain.txt +1000 -0
  77. tdanalysis/data/noise_curves/ET-psd.txt +3000 -0
  78. tdanalysis/data/noise_curves/ET_cryo_10km_psd.txt +3000 -0
  79. tdanalysis/data/noise_curves/ET_cryo_15km_psd.txt +3000 -0
  80. tdanalysis/data/noise_curves/PROVENANCE.md +130 -0
  81. tdanalysis/data/noise_curves/aplus.txt +3000 -0
  82. tdanalysis/detectors/__init__.py +89 -0
  83. tdanalysis/detectors/xg.py +1040 -0
  84. tdanalysis/dtypes.py +75 -0
  85. tdanalysis/exe/config.py +129 -0
  86. tdanalysis/exe/runner.py +755 -0
  87. tdanalysis/exe/tdpipe.py +72 -0
  88. tdanalysis/fitting_factor.py +908 -0
  89. tdanalysis/forms/cd.py +81 -0
  90. tdanalysis/forms/gs.py +200 -0
  91. tdanalysis/forms/gs_batched.py +66 -0
  92. tdanalysis/forms/invld.py +105 -0
  93. tdanalysis/forms/llt.py +122 -0
  94. tdanalysis/forms/pcg.py +362 -0
  95. tdanalysis/gpu/gpu_matrix_multiplier.py +215 -0
  96. tdanalysis/gpu/kernels.py +14 -0
  97. tdanalysis/inference/nr_injection.py +178 -0
  98. tdanalysis/likelihoods/comparison/discriminators.py +218 -0
  99. tdanalysis/likelihoods/multi/consistency_test.py +232 -0
  100. tdanalysis/likelihoods/multi/pe.py +442 -0
  101. tdanalysis/likelihoods/reduced_batch.py +203 -0
  102. tdanalysis/likelihoods/single/consistency_test.py +443 -0
  103. tdanalysis/likelihoods/single/pe.py +2424 -0
  104. tdanalysis/priors/bbh.py +215 -0
  105. tdanalysis/priors/bilby.py +67 -0
  106. tdanalysis/utils/bilby.py +480 -0
  107. tdanalysis/utils/conditioning.py +128 -0
  108. tdanalysis/utils/io.py +117 -0
  109. tdanalysis/utils/plotting.py +434 -0
  110. tdanalysis/utils/serialize.py +62 -0
  111. tdanalysis/waveforms/__init__.py +9 -0
  112. tdanalysis/waveforms/nraasur.py +575 -0
  113. tdanalysis-1.0.0.dist-info/METADATA +174 -0
  114. tdanalysis-1.0.0.dist-info/RECORD +118 -0
  115. tdanalysis-1.0.0.dist-info/WHEEL +4 -0
  116. tdanalysis-1.0.0.dist-info/entry_points.txt +2 -0
  117. tdanalysis-1.0.0.dist-info/licenses/LICENSE +203 -0
  118. tdanalysis-1.0.0.dist-info/licenses/NOTICE.md +133 -0
@@ -0,0 +1,811 @@
1
+ from os import listdir
2
+ from sys import prefix
3
+ import numpy as np
4
+ from gwpy.timeseries import TimeSeries
5
+ from waveformtools.waveformtools import message, progressbar, roll
6
+ import os
7
+ from os.path import isfile
8
+ import pickle
9
+ from pathlib import Path
10
+ import dask
11
+ import h5py
12
+ import scipy
13
+ from tqdm import tqdm
14
+
15
+ class CovarianceCalculator:
16
+ """Carry out time-domain analysis of data.
17
+
18
+ Given a gps reference time, this allows
19
+
20
+ 1. Computation of the autocorrelation function of specified length.
21
+ This will be computed as either the median or mean over `n_cov`
22
+ auto-correlation frames.
23
+ 2. Construction of the covariance matrix from the autocorrelation function
24
+ assuming a symmetric toeplitz form.
25
+ 3. Inference over the given priors `priors` and `n_live` number of live
26
+ points with a specified model.
27
+
28
+
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ ref_gps_time,
34
+ sampling_frequency=4096,
35
+ correlation_duration=4,
36
+ n_cov=10,
37
+ noise_sampling_range=100,
38
+ gps_time_exclusion_duration=8,
39
+ auto_corr_method="mean",
40
+ detector="L1",
41
+ channel="GDS-CALIB_STRAIN_CLEAN",
42
+ analysis_duration=1,
43
+ data_type="open",
44
+ backend="dask",
45
+ chunks=10000,
46
+ window=None,
47
+ segment_choice="disjoint",
48
+ resample_method='gwpy',
49
+ direct_data_path=None,
50
+ ):
51
+ self._sampling_frequency = sampling_frequency
52
+ self._correlation_duration = correlation_duration
53
+ self._n_cov = n_cov
54
+ self._noise_sampling_range = noise_sampling_range
55
+ self._ref_gps_time = ref_gps_time
56
+ self._gps_time_exclusion_duration = gps_time_exclusion_duration
57
+ self._auto_corr_method = auto_corr_method
58
+ self._detector = detector
59
+ self.channel = channel
60
+ self.data_type = data_type
61
+ self.backend = backend
62
+ self.chunks = chunks
63
+ self.window = window
64
+ self.resample_method = resample_method
65
+
66
+ if resample_method=='gwpy':
67
+ self.resample_data = self.resample_data_gwpy
68
+ elif resample_method=='scipy':
69
+ self.resample_data = self.resample_data_scipy
70
+ else:
71
+ raise ValueError(f"Unknown resampling method {self.resample_method}")
72
+
73
+ self.direct_data_path = direct_data_path
74
+
75
+ if self.data_type == "open":
76
+ self.load_channel = self.detector
77
+ self.get_data = TimeSeries.fetch_open_data
78
+
79
+ elif self.data_type == "server":
80
+ self.load_channel = f"{self.detector}:{self.channel}"
81
+ self.get_data = TimeSeries.get
82
+
83
+ elif self.data_type == "file":
84
+ self.load_channel = f"{self.detector}:{self.channel}"
85
+ self.get_data = self.get_data_from_file
86
+
87
+ else:
88
+ self.load_channel = None
89
+
90
+ self._all_moments = {}
91
+ self._N = self.sampling_frequency * self.correlation_duration
92
+ self._analysis_duration = analysis_duration
93
+ self._N_analysis = int(self.sampling_frequency * self.analysis_duration)
94
+ self._nothing_todo = False
95
+ self._compute_Cij = True
96
+ self._download = True
97
+ self._compute_moments = True
98
+ self.setup_backend()
99
+ # self.initialize()
100
+
101
+ if self.auto_corr_method == "mean":
102
+ self.estimator = np.mean
103
+ elif self.auto_corr_method == "median":
104
+ self.estimator = np.median
105
+ else:
106
+ raise KeyError(f"Unknown estimator {self.auto_corr_method}")
107
+
108
+ if self.backend == "numpy":
109
+ self.Cij_extn = "npy"
110
+ else:
111
+ self.Cij_extn = "h5"
112
+
113
+ if window is not None:
114
+ if window=='tukey':
115
+ self.window = scipy.signal.windows.tukey(self.sampling_frequency*self.correlation_duration, alpha=0.2)
116
+ elif window=='hann':
117
+ self.window = scipy.signal.windows.hann(self.sampling_frequency*self.correlation_duration)
118
+ else:
119
+ self.window = 1
120
+
121
+ self.N_acorr = 2*(self.N-1) + 1
122
+ N_axis_minus = np.arange(1, self.N)
123
+ N_axis_plus = np.arange(1, self.N+1)[::-1]
124
+ self.acorr_N_axis = np.concatenate((N_axis_minus, N_axis_plus))
125
+
126
+ self.segment_choice = segment_choice
127
+ if segment_choice=="disjoint":
128
+ self.compute_effective_noise_segment_gps_times = self.compute_effective_noise_segment_gps_times_disjoint
129
+ elif segment_choice=="random":
130
+ self.compute_effective_noise_segment_gps_times = self.compute_effective_noise_segment_gps_times_random
131
+ elif segment_choice=="overlapping":
132
+ self.compute_effective_noise_segment_gps_times = self.compute_effective_noise_segment_gps_times_overlapping
133
+
134
+ else:
135
+ raise KeyError(f"Unknown segment choice {segment_choice}")
136
+
137
+ self.max_n_acorr = int((self.noise_sampling_range - self.gps_time_exclusion_duration)/self.correlation_duration)
138
+
139
+ if self.n_cov > self.max_n_acorr:
140
+ message(f"Max number of segments possible is {self.max_n_acorr}. Defaulting to that", message_verbosity=0)
141
+ self._n_cov = self.max_n_acorr
142
+
143
+ self.signal_start = self.ref_gps_time - int(self.gps_time_exclusion_duration / 2)
144
+ self.signal_end = self.ref_gps_time + int(self.gps_time_exclusion_duration / 2)
145
+
146
+ @property
147
+ def sampling_frequency(self):
148
+ return self._sampling_frequency
149
+
150
+ @property
151
+ def correlation_duration(self):
152
+ return self._correlation_duration
153
+
154
+ @property
155
+ def n_cov(self):
156
+ return self._n_cov
157
+
158
+ @property
159
+ def noise_sampling_range(self):
160
+ return self._noise_sampling_range
161
+
162
+ @property
163
+ def ref_gps_time(self):
164
+ return self._ref_gps_time
165
+
166
+ @property
167
+ def noise_segments(self):
168
+ return self._noise_segments
169
+
170
+ @property
171
+ def N(self):
172
+ return self._N
173
+
174
+ @property
175
+ def N_analysis(self):
176
+ return self._N_analysis
177
+
178
+ @property
179
+ def est_auto_corr(self):
180
+ return self._est_auto_corr
181
+
182
+ @property
183
+ def gps_time_exclusion_duration(self):
184
+ """The duration of the time segment to exclude
185
+ from analysis. This is specified as a single
186
+ integer number. The segment is assumed to be
187
+ centered at `gps_time`"""
188
+
189
+ return self._gps_time_exclusion_duration
190
+
191
+ @property
192
+ def auto_corr_method(self):
193
+ return self._auto_corr_method
194
+
195
+ @property
196
+ def detector(self):
197
+ return self._detector
198
+
199
+ @property
200
+ def entire_segment_gps_start_time(self):
201
+ return self._entire_segment_gps_start_time
202
+
203
+ @property
204
+ def entire_segment_gps_end_time(self):
205
+ return self._entire_segment_gps_end_time
206
+
207
+ @property
208
+ def entire_noise_ts(self):
209
+ return self._entire_noise_ts
210
+
211
+ @property
212
+ def data_file_name(self):
213
+ return self._data_file_name
214
+
215
+ @property
216
+ def all_auto_corr(self):
217
+ return self._all_auto_corr
218
+
219
+ @property
220
+ def Cij(self):
221
+ return self._Cij
222
+
223
+ @property
224
+ def Cij_file_name(self):
225
+ return self._Cij_file_name
226
+
227
+ @property
228
+ def all_moments(self):
229
+ return self._all_moments
230
+
231
+ @property
232
+ def analysis_duration(self):
233
+ return self._analysis_duration
234
+
235
+ @property
236
+ def nothing_todo(self):
237
+ return self._nothing_todo
238
+
239
+ @property
240
+ def moments_file_name(self):
241
+ return self._moments_file_name
242
+
243
+ @property
244
+ def compute_Cij(self):
245
+ return self._compute_Cij
246
+
247
+ @property
248
+ def download(self):
249
+ return self._download
250
+
251
+ @property
252
+ def compute_moments(self):
253
+ return self._compute_moments
254
+
255
+ def setup_backend(self):
256
+
257
+ if self.backend == "numpy":
258
+ from numpy import mean, median, sqrt, dot
259
+
260
+ self.zeros = np.zeros
261
+ self.array = np.array
262
+
263
+ self.mean = np.mean
264
+ self.median = np.median
265
+ self.sqrt = np.sqrt
266
+ self.dot = np.dot
267
+
268
+ elif self.backend == "dask":
269
+ from functools import partial
270
+ import dask.array
271
+
272
+ self.zeros = partial(dask.array.wrap.zeros, chunks=self.chunks)
273
+ self.array = partial(dask.array.from_array, chunks=self.chunks)
274
+
275
+ self.mean = dask.array.reductions.mean
276
+ self.median = dask.array.reductions.median
277
+ self.sqrt = dask.array.ufunc.sqrt
278
+ self.dot = dask.array.routines.dot
279
+
280
+ else:
281
+ raise KeyError(f"Unknown backend {self.backend}")
282
+
283
+ def initialize(self):
284
+
285
+ message("Initializing")
286
+ message("\tParsing GPS times...")
287
+
288
+ self._Cij_file_name = f"Cij_gpsT{self.ref_gps_time}_R{self.noise_sampling_range}_D{self.correlation_duration}_S{self.sampling_frequency}_A{self.analysis_duration}_N{self.n_cov}"
289
+ self._moments_file_name = f"moments_gpsT{self.ref_gps_time}_R{self.noise_sampling_range}_D{self.correlation_duration}_S{self.sampling_frequency}_A{self.analysis_duration}_N{self.n_cov}.pkl"
290
+ self.acorr_file_name = f"rho_gpsT{self.ref_gps_time}_R{self.noise_sampling_range}_D{self.correlation_duration}_S{self.sampling_frequency}_A{self.analysis_duration}_N{self.n_cov}"
291
+
292
+ self._entire_segment_gps_start_time = (
293
+ self.ref_gps_time - self.noise_sampling_range - self.correlation_duration
294
+ )
295
+ self._entire_segment_gps_end_time = (
296
+ self.ref_gps_time + self.noise_sampling_range + self.correlation_duration
297
+ )
298
+
299
+ if self.data_type == "open" or self.data_type == "server":
300
+ self.setup_online_data()
301
+ elif self.data_type == "file":
302
+ self.read_data_from_file()
303
+
304
+ else:
305
+ # self.find_data_segment()
306
+ # self.load_data_propreitery()
307
+ # self._data_file_name = f"Entire_noise_ts_{self.data_type}_gpsTref{self.ref_gps_time}_{self.entire_segment_gps_start_time}_{self._entire_segment_gps_end_time}_S{self.sampling_frequency}_A{self.analysis_duration}.txt"
308
+ raise NotImplementedError
309
+
310
+ def setup_online_data(self):
311
+ """Download, and read in open data"""
312
+ self._entire_segment_gps_start_time = (
313
+ self.ref_gps_time - self.noise_sampling_range - self.correlation_duration
314
+ )
315
+
316
+ self._entire_segment_gps_end_time = (
317
+ self.ref_gps_time + self.noise_sampling_range + self.correlation_duration
318
+ )
319
+ message("\tSetting file names...")
320
+ self._data_file_name = f"Entire_noise_ts_{self.data_type}_gpsT{self.ref_gps_time}_R{self.noise_sampling_range}_D{self.correlation_duration}_S{self.sampling_frequency}_A{self.analysis_duration}.txt"
321
+
322
+ message("Obtaining data")
323
+ if self.check_file_exists(self.data_file_name):
324
+ message("\tLoading data from disk", message_verbosity=1)
325
+ self.load_entire_time_segment()
326
+ self._download = False
327
+
328
+ if self.check_file_exists(f"{self.Cij_file_name}.{self.Cij_extn}"):
329
+ message("Loading Cij from disk", message_verbosity=1)
330
+ self.load_Cij()
331
+ self._compute_Cij = False
332
+
333
+ if self.check_file_exists(self.moments_file_name):
334
+ self.load_stat_moments()
335
+ self._compute_moments = False
336
+ self._nothing_todo = True
337
+
338
+ else:
339
+ message("\tDownloading data")
340
+ self.download_full_data()
341
+ self.resample_data()
342
+ message("\t Saving data to disk")
343
+ self.save_entire_time_segment()
344
+
345
+ def read_data_from_file(self):
346
+
347
+ try:
348
+ entire_noise_ts = TimeSeries.read(self.direct_data_path, self.load_channel, verbose=True)
349
+
350
+ except Exception as excep:
351
+ message(excep)
352
+ entire_noise_ts = None
353
+ raise FileNotFoundError
354
+
355
+ entire_noise_ts = self.demean_ts(entire_noise_ts)
356
+ self.entire_noise_ts_time_axis = entire_noise_ts.times
357
+ self._entire_noise_ts = entire_noise_ts
358
+
359
+ def resample_data_scipy(self):
360
+
361
+ original_sampling_f = self.entire_noise_ts.sample_rate
362
+
363
+ if int(self.sampling_frequency) != int(original_sampling_f.value):
364
+ message("Resampling using cubic spline", message_verbosity=2)
365
+ from scipy.interpolate import interp1d
366
+ new_dt = 1 / self.sampling_frequency
367
+ #print(new_dt)
368
+ new_time_axis = np.arange(
369
+ self.entire_noise_ts_time_axis[0].value,
370
+ self.entire_noise_ts_time_axis[-1].value,
371
+ new_dt,
372
+ )
373
+ new_entire_noise_ts = interp1d(
374
+ self.entire_noise_ts_time_axis, self.entire_noise_ts, kind="cubic"
375
+ )(new_time_axis)
376
+
377
+ self._entire_noise_ts = TimeSeries(
378
+ data=new_entire_noise_ts, times=new_time_axis
379
+ )
380
+ self.entire_noise_ts_time_axis = new_time_axis
381
+
382
+ def resample_data_gwpy(self):
383
+
384
+ original_sampling_f = self.entire_noise_ts.sample_rate
385
+
386
+ if int(self.sampling_frequency) != int(original_sampling_f.value):
387
+ message("Resampling using gwpy", message_verbosity=2)
388
+
389
+ new_entire_noise_ts = self.entire_noise_ts.resample(self.sampling_frequency)
390
+
391
+ self._entire_noise_ts = new_entire_noise_ts
392
+ self.entire_noise_ts_time_axis = new_entire_noise_ts.times.value
393
+
394
+ def compute_effective_noise_segment_gps_times_random(self):
395
+ """Get the noise segment GPS time endpoints of correlation duration
396
+ that do not include the gps_time_exclusion duration.
397
+ Returns a dict.
398
+ """
399
+
400
+ message("Computing noise segment GPS times")
401
+ noise_segments = {}
402
+ for seg_ind in range(self.n_cov):
403
+ not_found = True
404
+
405
+ while not_found:
406
+ seg_centre = np.random.randint(
407
+ self.ref_gps_time - self.noise_sampling_range,
408
+ self.ref_gps_time + self.noise_sampling_range,
409
+ )
410
+ seg_start = seg_centre - self.correlation_duration / 2
411
+ seg_end = seg_centre + self.correlation_duration / 2
412
+
413
+ if (self.signal_start > seg_end) or (self.signal_end < seg_start):
414
+ not_found = False
415
+
416
+ noise_segments.update(
417
+ {
418
+ seg_ind: (
419
+ seg_start,
420
+ seg_end,
421
+ )
422
+ }
423
+ )
424
+
425
+ self._noise_segments = noise_segments
426
+
427
+ def compute_effective_noise_segment_gps_times_disjoint(self):
428
+ """Get the noise segment GPS time endpoints of correlation duration
429
+ that do not include the gps_time_exclusion duration.
430
+ Returns a dict.
431
+ """
432
+ message("Computing noise segment GPS times")
433
+ noise_segments = {}
434
+
435
+ for seg_ind in range(self.n_cov):
436
+ not_found = True
437
+
438
+ while not_found:
439
+ seg_centre = self.ref_gps_time - self.noise_sampling_range + (seg_ind+0.5)*self.correlation_duration
440
+ seg_start = seg_centre - self.correlation_duration / 2
441
+ seg_end = seg_centre + self.correlation_duration / 2
442
+
443
+ if (self.signal_start > seg_end) or (self.signal_end < seg_start):
444
+ not_found = False
445
+
446
+ noise_segments.update(
447
+ {
448
+ seg_ind: (
449
+ seg_start,
450
+ seg_end,
451
+ )
452
+ }
453
+ )
454
+
455
+ self._noise_segments = noise_segments
456
+
457
+ def compute_effective_noise_segment_gps_times_overlapping(self):
458
+ """Get the noise segment GPS time endpoints of correlation duration
459
+ that do not include the gps_time_exclusion duration.
460
+ Returns a dict.
461
+ """
462
+
463
+ message("Computing noise segment GPS times")
464
+ noise_segments = {}
465
+
466
+ for seg_ind in range(self.n_cov):
467
+ not_found = True
468
+
469
+ while not_found:
470
+ seg_centre = self.ref_gps_time - self.noise_sampling_range + (seg_ind+0.5)*self.correlation_duration/2
471
+ seg_start = seg_centre - self.correlation_duration / 2
472
+ seg_end = seg_centre + self.correlation_duration / 2
473
+
474
+ if (self.signal_start > seg_end) or (self.signal_end < seg_start):
475
+ not_found = False
476
+
477
+ noise_segments.update(
478
+ {
479
+ seg_ind: (
480
+ seg_start,
481
+ seg_end,
482
+ )
483
+ }
484
+ )
485
+
486
+ self._noise_segments = noise_segments
487
+
488
+
489
+ def get_segment_gps_times(self, segment_number):
490
+
491
+ return self.noise_segments[segment_number]
492
+
493
+ def fetch_data_array(self, segment_number):
494
+
495
+ if self.check_array_exists(self.entire_noise_ts):
496
+ message("Fetching saved data", message_verbosity=3)
497
+ # print(self.get_segment_gps_times(segment_number))
498
+ seg_gps_start, seg_gps_end = self.get_segment_gps_times(segment_number)
499
+ ind_start = int((
500
+ seg_gps_start - self.entire_segment_gps_start_time
501
+ ) * self.sampling_frequency)
502
+ ind_end = int((
503
+ seg_gps_end - self.entire_segment_gps_start_time
504
+ ) * self.sampling_frequency)
505
+ noise_ts = self.entire_noise_ts[ind_start:ind_end]
506
+
507
+ else:
508
+ try:
509
+ noise_ts = self.get_data(
510
+ self.channel,
511
+ *self.get_segment_gps_times(segment_number),
512
+ verbose=True,
513
+ )
514
+
515
+ except Exception as excep:
516
+ message(excep)
517
+
518
+ return self.demean_ts(noise_ts)
519
+
520
+ def get_data_from_file(self, channel, seg_start=None, seg_end=None, verbose=False):
521
+
522
+ return TimeSeries.read(self.direct_data_path, self.load_channel)
523
+
524
+ def check_array_exists(self, array):
525
+
526
+ # if self.backend=='numpy':
527
+ return not (np.array(self.entire_noise_ts) == np.array(None)).all()
528
+ # elif self.backend=='dask':
529
+ # return not (self.entire_noise_ts == None).all().compute()
530
+ # else:
531
+ # raise KeyError(f"Unknown backend {self.backend}")
532
+
533
+ def estimate_auto_corr(self):
534
+ """Estimate the auto-correaltion function using all
535
+ the noise segments"""
536
+
537
+ message("Computing autocorrelations...\n", message_verbosity=1)
538
+
539
+ #all_auto_corr = np.zeros((self.n_cov, self.N_analysis))
540
+ all_auto_corr = np.zeros((self.n_cov, self.N_acorr))
541
+
542
+ for seg_ind in tqdm(range(self.n_cov)):
543
+ noise_ts = self.window*np.array(self.fetch_data_array(seg_ind))
544
+ one_set_of_moments = self.compute_stat_moments(noise_ts)
545
+ self._all_moments.update({seg_ind: one_set_of_moments})
546
+ noise_ts = self.demean_ts(noise_ts)
547
+ auto_corr_i = self.auto_coorelate(noise_ts)
548
+ all_auto_corr[seg_ind, :] = auto_corr_i
549
+ #progressbar(seg_ind + 1, self.n_cov)
550
+
551
+ self._all_auto_corr = all_auto_corr
552
+
553
+ self.save_array(f"all_{self.acorr_file_name}", self.all_auto_corr)
554
+ #est_auto_corr = np.array(
555
+ # [self.estimator(all_auto_corr[:, ind]) for ind in range(self.N_acorr)]
556
+ #)
557
+ est_auto_corr = self.estimator(all_auto_corr, axis=0)
558
+
559
+ #self._est_auto_corr = est_auto_corr
560
+ self._est_auto_corr = est_auto_corr
561
+ message("\n", message_verbosity=1)
562
+
563
+ def estimate_symm_toeplitz_Cij(self, N=None):
564
+ """Construct a symmetric Toeplez cross correlation
565
+ matrix from a given a noise time series"""
566
+
567
+ start = self.N_acorr//2
568
+
569
+ if N is None:
570
+ end=start+self.N_analysis
571
+ else:
572
+ end=start+N
573
+
574
+ message("\nComputing the cross correlation matrix", message_verbosity=1)
575
+ auto_corr = self.est_auto_corr[start:end]
576
+ #auto_corr_pos = self.est_auto_corr[auto_corr.size//2:N]
577
+ #N = self.N_analysis
578
+ #Cij = self.zeros((N, N))
579
+ #count = 0
580
+ #total_num_entries = N * (N + 1) / 2
581
+ #num_unique_entries = len(auto_corr)
582
+
583
+ self._Cij = scipy.linalg.toeplitz(auto_corr)
584
+
585
+ #if self.backend == "numpy":
586
+ # row_idx, col_idx = np.indices(Cij.shape)
587
+ #elif self.backend == "dask":
588
+ # row_idx, col_idx = dask.array.indices(Cij.shape)
589
+ #else:
590
+ # raise KeyError(f"Unknown backend {self.backend}")
591
+
592
+ #for rel_idx, acor_val in enumerate(auto_corr):
593
+ # Cij[abs(row_idx - col_idx) == rel_idx] = acor_val
594
+ # count += 1
595
+ # print(f"Progress: {100*count/num_unique_entries:.2f}%", end="\r")
596
+
597
+
598
+ # for row_ind in range(N):
599
+ # for col_ind in range(row_ind, N):
600
+ # rel_ind = abs(col_ind - row_ind)
601
+ # Cij[row_ind, col_ind] = auto_corr[rel_ind]
602
+ # if row_ind != col_ind:
603
+ # Cij[col_ind, row_ind] = Cij[row_ind, col_ind]
604
+ # count += 1
605
+ # print(f"Progress: {count/total_num_entries}%", end='\r')
606
+
607
+ # if count % 1000 == 0:
608
+ # progressbar(count, total_num_entries)
609
+
610
+ #self._Cij = Cij
611
+
612
+ def run(self):
613
+
614
+ if not self.nothing_todo:
615
+ self.compute_effective_noise_segment_gps_times()
616
+
617
+ if self.compute_moments:
618
+ self.estimate_auto_corr()
619
+ self.save_auto_corr()
620
+ self.save_stat_moments()
621
+
622
+ if self.compute_Cij:
623
+ self.estimate_symm_toeplitz_Cij()
624
+ self.save_Cij()
625
+
626
+
627
+ def download_full_data(self):
628
+ """Fetch the entire noise stream for the required duration"""
629
+
630
+ message("Downloading data...", message_verbosity=1)
631
+ gps0 = self.ref_gps_time - self.noise_sampling_range - self.correlation_duration
632
+ gps1 = self.ref_gps_time + self.noise_sampling_range + self.correlation_duration
633
+
634
+ try:
635
+ entire_noise_ts = self.get_data(self.load_channel, gps0, gps1, verbose=True)
636
+
637
+ except Exception as excep:
638
+ message(excep)
639
+ entire_noise_ts = None
640
+
641
+ entire_noise_ts = self.demean_ts(entire_noise_ts)
642
+ self.entire_noise_ts_time_axis = entire_noise_ts.times.value
643
+ self._entire_noise_ts = entire_noise_ts
644
+
645
+ def demean_ts(self, time_series):
646
+ # if isinstance(time_series, TimeSeries):
647
+ if self.backend == "numpy" or isinstance(time_series, np.ndarray):
648
+ return time_series - np.mean(time_series)
649
+ # elif isinstance(self, da.array):
650
+ elif self.backend == "dask" and isinstance(time_series, da.array):
651
+ return time_series - self.mean(time_series).compute()
652
+
653
+ def find_data_segment(self):
654
+ dir_suffix = int(self.ref_gps_time / 10000000)
655
+
656
+ assert (
657
+ dir_suffix > 99 and dir_suffix < 1000
658
+ ), f"The prefix dir could not be computed properly from the reference time {dir_suffix}"
659
+
660
+ self.data_file_dir = Path(
661
+ f"/cvmfs/ligo.storage.igwn.org/igwn/ligo/frames/O4/hoft_C00/{self.detector}/{self.detector[0]}-{self.detector}_HOFT_C00-{dir_suffix}"
662
+ )
663
+ self._data_file_name = self.get_dataframe_name_containing_reference_time(
664
+ self.data_file_dir
665
+ )
666
+ self.data_file_path = self.data_file_dir / self.data_file_name
667
+
668
+ def load_data_propreitery(self):
669
+ """Download propreitery LIGO data from cvmfs.
670
+
671
+ Find the segment that contains the trigger time
672
+ Usually aggregated data a large. It should be enough
673
+ for PSD estimation"""
674
+
675
+ entire_ts = TimeSeries.read(
676
+ self.data_file_path, f"{self.detector}:{self.channel}"
677
+ )
678
+ entire_ts = self.demean_ts(entire_ts)
679
+ self.entire_noise_ts_time_axis = self.entire_noise_ts.times
680
+ self._entire_noise_ts = entire_ts
681
+ self._entire_segment_gps_start_time = self.entire_noise_ts_time_axis[0]
682
+ self._entire_segment_gps_end_time = self.entire_noise_ts_time_axis[-1]
683
+
684
+ def get_dataframe_name_containing_reference_time(self, data_file_dir):
685
+ """Get available gps times"""
686
+
687
+ files = os.listdir(data_file_dir)
688
+ files = [item for item in files if ".gwf" in item]
689
+ self.available_gps_times = np.array([int(item[14:24]) for item in files])
690
+ data_file_idx = np.argmin(abs(self.available_gps_times - self.ref_gps_time))
691
+ data_file_name = files[data_file_idx]
692
+
693
+ return data_file_name
694
+
695
+ def save_entire_time_segment(self):
696
+
697
+ entire_noise_ts = TimeSeries(
698
+ data=self.entire_noise_ts, times=self.entire_noise_ts_time_axis
699
+ )
700
+ entire_noise_ts.write(self.data_file_name)
701
+
702
+ def load_entire_time_segment(self):
703
+ entire_noise_ts = TimeSeries.read(self.data_file_name)
704
+ self.entire_noise_ts_time_axis = entire_noise_ts.times
705
+ self._entire_noise_ts = entire_noise_ts
706
+
707
+ def check_file_exists(self, file_path):
708
+ flag = isfile(file_path)
709
+ return flag
710
+
711
+ def auto_coorelate(self, ts):
712
+ """Autocorrelate a astretch of data"""
713
+
714
+ #autocorr = []
715
+ #for index in range(0, self.N_analysis):
716
+ # # print(index/N)
717
+ # ts2 = roll(np.array(ts), index)
718
+ # autocorr.append(np.dot(np.array(ts), np.array(ts2)) / self.N)
719
+ #print(len(ts))
720
+ autocorr = np.correlate(np.array(ts), np.array(ts), mode='full')/self.N#/self.acorr_N_axis
721
+
722
+ return autocorr#[autocorr.size//2:autocorr.size//2+self.N_analysis]
723
+
724
+ def save_Cij(self):
725
+ """Save the covariance matrix"""
726
+
727
+ message("Saving the covariance matrix", message_verbosity=2)
728
+
729
+ if isfile(self.Cij_file_name):
730
+ raise FileExistsError(f"{self.Cij_file_name} File already exists!")
731
+
732
+ else:
733
+ self.save_array(self.Cij_file_name, self.Cij)
734
+ def save_auto_corr(self):
735
+ if isfile(self.acorr_file_name):
736
+ raise FileExistsError(f"{self.acorr_file_name} File already exists!")
737
+
738
+ else:
739
+ self.save_array(self.acorr_file_name, self.est_auto_corr)
740
+
741
+ def load_Cij(self):
742
+ if self.backend == "numpy":
743
+ self._Cij = np.load(f"{self.Cij_file_name}.npy")
744
+ elif self.backend == "dask":
745
+ fd = h5py.File(f"{self.Cij_file_name}.h5")["/Cij"]
746
+ self._Cij = dask.array.from_array(fd)
747
+
748
+ def save_array(self, file_name, array, dset_name='/Cij'):
749
+
750
+ if isinstance(array, np.ndarray):
751
+ np.save(f"{file_name}.npy", array)
752
+
753
+ elif isinstance(self, dask.array.core.Array):
754
+ return array.to_hdf5(f"{file_name}.h5", dset_name)
755
+
756
+ def compute_stat_moments(self, noise_ts):
757
+
758
+ noise_array = np.array(noise_ts)
759
+ # Mean
760
+ noise_array_mean = np.mean(noise_array)
761
+ # Variance
762
+ noise_array_variance = np.mean((noise_array - noise_array_mean) ** 2)
763
+ noise_array_std = self.sqrt(noise_array_variance)
764
+ moments = [noise_array_mean, noise_array_variance]
765
+ # Higher moments
766
+ for mindex in range(3, 13):
767
+ df = self.double_factorial(mindex - 1)
768
+ moments.append(
769
+ np.mean(((noise_array - noise_array_mean) / noise_array_std) ** mindex)
770
+ / df
771
+ )
772
+
773
+ return moments
774
+
775
+ def save_stat_moments(self):
776
+
777
+ with open(self.moments_file_name, "wb") as f:
778
+ pickle.dump(self.all_moments, f)
779
+
780
+ def load_stat_moments(self):
781
+
782
+ with open(self.moments_file_name, "rb") as f:
783
+ self._all_moments = pickle.load(f)
784
+
785
+ def get_nth_moment(self, n):
786
+
787
+ nth_moment = [self.all_moments[ind][n] for ind in self.all_moments.keys()]
788
+ return nth_moment
789
+
790
+ def plot_moments(self):
791
+
792
+ import matplotlib.pyplot as plt
793
+ fig, ax = plt.subplots(nrows=4, ncols=3, figsize=(18, 12))
794
+
795
+ for mxind in range(4):
796
+ for myind in range(3):
797
+
798
+ mn = mxind * 3 + myind
799
+ ax[mxind, myind].scatter(
800
+ self.all_moments.keys(), self.get_nth_moment(mn - 1)
801
+ )
802
+ ax[mxind, myind].set_title(f"Moment {mn+1}")
803
+
804
+ plt.show()
805
+
806
+ def double_factorial(self, n):
807
+
808
+ if n == 0 or n == 1:
809
+ return 1
810
+ else:
811
+ return n * self.double_factorial(n - 2)