Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-6c6b863
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
testWriteSFTsfromSFDBs.py
Go to the documentation of this file.
1# Copyright (C) 2020 Pep Covas, David Keitel, Rodrigo Tenorio
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 2 of the License, or (at your
6# option) any later version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11# Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17import os
18import lalpulsar
19import glob
20import subprocess
21import numpy as np
22from numpy.testing import assert_allclose
23
24# run this test with "make check TESTS=testWriteSFTsfromSFDBs"
25# everything will be automatically put into testWriteSFTsfromSFDBs.testdir
26testdir = "."
27
28
29def validate_and_load_SFTs(globstr, fmin, fmax):
30 sftnames = list(glob.glob(os.path.join(testdir, globstr)))
31 assert len(sftnames) == 1
32 sftname = sftnames[0]
33 app = "lalpulsar_SFTvalidate"
34 cl = app + " " + sftname
35 print("Executing: " + cl)
36 out = subprocess.check_output(
37 cl, shell=True, stderr=subprocess.STDOUT, universal_newlines=True
38 )
39 print("{:s}: {}".format(app, out))
40 catalog = lalpulsar.SFTdataFind(sftname, None)
41 sfts = lalpulsar.LoadSFTs(catalog, fmin, fmax)
42 return sfts, sftname
43
44
46 sfts1, sfts2, plotnamebase="plot", label1="SFTs1", label2="SFTs2", title=""
47):
48 minlen = min(sfts1.length, sfts2.length)
49 if sfts1.length != sfts2.length:
50 print(
51 "Inconsistent SFT vector lengths {:d}!={:d} for {:s}, {:s}. Will only plot the first {:d}.".format(
52 sfts1.length, sfts2.length, label1, label2, minlen
53 )
54 )
55 # note: zip will do this automagically
56 plotnamebase += "-" + label1 + "-" + label2
57 for k, (sft1, sft2) in enumerate(zip(sfts1.data, sfts2.data)):
58 if sft1.epoch != sft2.epoch:
59 raise ValueError(
60 "Inconsistent epochs {:d}!={:d} in SFT {:d} of {:s}, {:s}".format(
61 sft1.epoch.gpsSeconds, sft2.epoch.gpsSeconds, k, label1, label2
62 )
63 )
64 title_this_epoch = title + ", epoch={:d}".format(sft1.epoch.gpsSeconds)
65 plotnamebase_this_epoch = plotnamebase + "_{:d}".format(sft1.epoch.gpsSeconds)
66 freqs1 = sft1.f0 + sft1.deltaF * np.arange(0, sft1.data.length)
67 freqs2 = sft2.f0 + sft2.deltaF * np.arange(0, sft2.data.length)
68 sft1_abs = np.abs(sft1.data.data)
69 sft2_abs = np.abs(sft2.data.data)
70 plt.plot(freqs1, sft1_abs, label=label1, color="black")
71 plt.plot(freqs2, sft2_abs, label=label2, color="red")
72 plt.xlabel("frequency [Hz]")
73 plt.ylabel("|complex SFT bin|")
74 plt.title(title_this_epoch)
75 plt.legend()
76 plotfile = os.path.join(testdir, plotnamebase_this_epoch + "-comparison.png")
77 print("Plotting to " + plotfile)
78 plt.savefig(plotfile)
79 plt.close()
80 plt.plot(freqs1, np.abs(sft1.data.data - sft2.data.data), color="black")
81 plt.xlabel("frequency [Hz]")
82 plt.ylabel("|{:s}-{:s}|".format(label1, label2))
83 plt.title(title_this_epoch)
84 plotfile = os.path.join(testdir, plotnamebase_this_epoch + "-absdiff.png")
85 print("Plotting to " + plotfile)
86 plt.savefig(plotfile)
87 plt.close()
88 plt.plot(
89 freqs1, np.abs(sft1.data.data - sft2.data.data) / sft2_abs, color="black"
90 )
91 plt.xlabel("frequency [Hz]")
92 plt.ylabel("|{:s}-{:s}|/|{:s}|".format(label1, label2, label2))
93 plt.title(title_this_epoch)
94 plotfile = os.path.join(testdir, plotnamebase_this_epoch + "-reldiff.png")
95 print("Plotting to " + plotfile)
96 plt.savefig(plotfile)
97 plt.close()
98 plt.plot(sft2_abs, np.abs(sft1.data.data - sft2.data.data), ".", color="black")
99 plt.xlabel("|{:s}|".format(label2))
100 plt.ylabel("|{:s}-{:s}|".format(label1, label2))
101 plt.title(title_this_epoch)
102 plotfile = os.path.join(
103 testdir, plotnamebase_this_epoch + "-absdiff-vs-abs2.png"
104 )
105 print("Plotting to " + plotfile)
106 plt.savefig(plotfile)
107 plt.close()
108 plt.plot(
109 sft2_abs,
110 np.abs(sft1.data.data - sft2.data.data) / sft2_abs,
111 ".",
112 color="black",
113 )
114 plt.xlabel("|{:s}|".format(label2))
115 plt.ylabel("|{:s}-{:s}|/|{:s}|".format(label1, label2, label2))
116 plt.title(title_this_epoch)
117 plotfile = os.path.join(
118 testdir, plotnamebase_this_epoch + "-reldiff-vs-abs2.png"
119 )
120 print("Plotting to " + plotfile)
121 plt.savefig(plotfile)
122 plt.close()
123
124
125# files from reference tarball
126ref_gwf = "V-V1_mfdv5-1257741529-2048.gwf"
127ref_sft = "V-3_V1_1024SFT_mfdv5-1257741529-2048.sft"
128ref_sfdb = "V1-mfdv5_20191114_043831.SFDB09"
129
130# data parameters
131IFOs = ["V1"]
132fmin = 49.0
133fmax = 51.0
134startTime = 1257741529
135frameDuration = 2048
136# the following are only for the SFDBs and the SFTs we'll make to compare against,
137# ref_sft has Tsft=frameDuration and Toverlap=0 instead!
138Tsft = int(ref_sft.split("SFT")[0].split("_")[-1])
139Toverlap = int(0.5 * Tsft)
140
141
142print("\n=== Running WriteSFTsfromSFDBs on reference SFDB file ===\n")
143cl_SFDB = " ".join(
144 [
145 "lalpulsar_WriteSFTsfromSFDBs",
146 "--file-pattern",
147 ref_sfdb,
148 "--fmin",
149 str(fmin),
150 "--fmax",
151 str(fmax),
152 "--outSFTdir",
153 testdir,
154 ]
155)
156print("Executing: " + cl_SFDB)
157subprocess.check_call(cl_SFDB, shell=True)
158sfts_from_sfdb, _ = validate_and_load_SFTs(IFOs[0][0] + "*_fromSFDBs-*.sft", fmin, fmax)
159
160# TODO: also test and validate --outSFTnames, --outSingleSFT=False modes
161
162print("\n=== Reading reference SFTs from MFDv5 ===\n")
163sfts_ref, _ = validate_and_load_SFTs(ref_sft, fmin, fmax)
164# catalog_ref = lalpulsar.SFTdataFind(ref_sft, None)
165# sfts_ref = lalpulsar.LoadSFTs(catalog_ref, fmin, fmax)
166
167
168print("=== Comparing outputs of WriteSFTsfromSFDBs and MFDv5: overall properties ===\n")
169# compare overall properties
170atol_freq = 1e-6
171print(
172 "nSFTs: {} vs {}".format(sfts_from_sfdb.length, sfts_ref.length)
173) # Number of SFTs
174print(
175 "Epochs: {} vs {}".format(
176 [sft.epoch for sft in sfts_from_sfdb.data], [sft.epoch for sft in sfts_ref.data]
177 )
178)
179# lengths will not actually be the same:
180# SFDBs contain a 4th SFT starting at 1257743065 and hanging over the end of the .gwf by half
181if sfts_from_sfdb.length == sfts_ref.length + 1:
182 print(
183 "Inconsistent SFT vector lengths {:d}=={:d}+1 from SFDBs vs referenc SFT. This is expected.".format(
184 sfts_from_sfdb.length, sfts_ref.length
185 )
186 )
187assert sfts_from_sfdb.length == sfts_ref.length + 1
188mean_sfts = np.zeros(sfts_ref.length) # will reuse this for test at the end
189for k, (sft1, sft2) in enumerate(zip(sfts_from_sfdb.data, sfts_ref.data)):
190 print("Comparing SFT number {:d}...".format(k))
191 f0_sfdb = sft1.f0
192 deltaF_sfdb = sft1.deltaF
193 nBins_sfdb = sft1.data.length
194 abs_sfdb = np.abs(sft1.data.data)
195 max_sfdb = np.max(abs_sfdb)
196 maxbin_sfdb = np.argmax(abs_sfdb)
197 maxfreq_sfdb = f0_sfdb + deltaF_sfdb * maxbin_sfdb
198 without_maxbin = np.arange(nBins_sfdb) != maxbin_sfdb
199 abs_without = abs_sfdb[without_maxbin]
200 mean_sfdb = np.mean(abs_without)
201 stdev_sfdb = mean_sfdb * np.std(abs_without / mean_sfdb)
202 f0_sfts = sft2.f0
203 deltaF_sfts = sft2.deltaF
204 nBins_sfts = sft2.data.length
205 abs_sfts = np.abs(sft2.data.data)
206 max_sfts = np.max(abs_sfts)
207 maxbin_sfts = np.argmax(abs_sfts)
208 maxfreq_sfts = f0_sfts + deltaF_sfts * maxbin_sfts
209 abs_without = abs_sfts[np.arange(nBins_sfts) != maxbin_sfts]
210 mean_sfts[k] = np.mean(abs_without)
211 stdev_sfts = mean_sfts[k] * np.std(abs_without / mean_sfts[k])
212 print("IFO: {} vs {}".format(sft1.name, sft2.name)) # Name of IFO
213 print("f0: {} vs {}".format(f0_sfdb, f0_sfts)) # Starting frequency
214 print(
215 "df: {} vs {}".format(deltaF_sfdb, deltaF_sfts)
216 ) # Frequency spacing between bins
217 print(
218 "nBins: {} vs {}".format(nBins_sfdb, nBins_sfts)
219 ) # Number of frequency bins in one SFT
220 print(
221 "max in bin: {} vs {}".format(maxbin_sfdb, maxbin_sfts)
222 ) # bin index for max of |SFT|
223 print("freq of max: {:.4f} vs {:.4f}".format(maxfreq_sfdb, maxfreq_sfts))
224 print("max |value|: {:.4e} vs {:.4e}".format(max_sfdb, max_sfts))
225 print("background mean: {:.4e} vs {:.4e}".format(mean_sfdb, mean_sfts[k]))
226 print("background stdev: {:.4e} vs {:.4e}".format(stdev_sfdb, stdev_sfts))
227 assert sft1.name == sft2.name
228 assert abs(f0_sfdb - f0_sfts) < atol_freq
229 assert abs(deltaF_sfdb - deltaF_sfts) < atol_freq
230 assert nBins_sfdb == nBins_sfts
231 assert maxbin_sfdb == maxbin_sfts
232 assert abs(max_sfdb - max_sfts) / max_sfts < 0.01
233 assert abs(maxfreq_sfdb - maxfreq_sfts) < atol_freq
234 assert abs(mean_sfdb - mean_sfts[k]) / mean_sfts[k] < 0.001
235 assert abs(stdev_sfdb - stdev_sfts) / stdev_sfts < 0.001
236
237print("\n=== Optional: comparison plots ===\n")
238try:
239 import matplotlib
240
241 matplotlib.use("Agg")
242 from matplotlib import pyplot as plt
243
245 sfts_ref,
246 sfts_from_sfdb,
247 plotnamebase=IFOs[0],
248 label1="SFTs",
249 label2="SFDBs",
250 title="Tsft={:d}, Toverlap={:d}".format(Tsft, Toverlap),
251 )
252except ImportError:
253 print(
254 "Cannot import matplotlib to make comparison plots, continuing with numerical comparisons."
255 )
256except Exception as e:
257 print(
258 "Failed to create comparison plots, continuing with numerical comparisons. Exception was:"
259 )
260 print(e)
261
262
263print(
264 "\n=== Comparing outputs of WriteSFTsfromSFDBs and MFDv5: actual per-bin data ===\n"
265)
266# ignoring here the overhanging 4th SFT from the SFDBs
267# we use an atol threshold at 2% of the mean noise level
268# because rtol tends to blow up in bins with very low noise
269for k, (sft1, sft2) in enumerate(zip(sfts_from_sfdb.data, sfts_ref.data)):
270 atol = 0.02 * mean_sfts[k]
271 maxdiff = max(np.abs(sft1.data.data - sft2.data.data))
272 print(
273 "Comparing SFT number {:d}: max absolute difference is {:g} (allowed: {:g})".format(
274 k, maxdiff, atol
275 )
276 )
277 assert_allclose(sft1.data.data, sft2.data.data, rtol=0, atol=atol, verbose=True)
278
279print("All SFDB checks passed (or skipped).")
#define min(a, b)
def validate_and_load_SFTs(globstr, fmin, fmax)
def plot_SFTs_comparison(sfts1, sfts2, plotnamebase="plot", label1="SFTs1", label2="SFTs2", title="")
#define max(a, b)