LALPulsar  6.1.0.1-b72065a
test_computePSD.py
Go to the documentation of this file.
1 # Copyright (C) 2020 Rodrigo Tenorio, David Keitel
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 
17 import os
18 import sys
19 import numpy as np
20 from numpy.testing import assert_allclose
21 import lal
22 import lalpulsar
23 import pytest
24 
25 # load Earth and Sun ephemerides
26 earth_ephem_file = os.path.join(
27  os.environ["LAL_TEST_PKGDATADIR"], "earth00-40-DE405.dat.gz"
28 )
29 sun_ephem_file = os.path.join(
30  os.environ["LAL_TEST_PKGDATADIR"], "sun00-40-DE405.dat.gz"
31 )
32 ephemerides = lalpulsar.InitBarycenter(earth_ephem_file, sun_ephem_file)
33 
34 # parameters to make fake data
35 IFOs = ["H1", "L1"]
36 numDet = len(IFOs)
37 sqrtSX = ["1e-23"]
38 f_min = 20
39 f_max = 21
40 T0 = 900000000
41 Tspan = 86400
42 Tsft = 1800
43 Toverlap = 0
44 dataParams = lalpulsar.CWMFDataParams()
45 dataParams.fMin = f_min
46 dataParams.Band = f_max - f_min
47 lalpulsar.ParseMultiLALDetector(dataParams.multiIFO, IFOs)
48 lalpulsar.ParseMultiNoiseFloor(dataParams.multiNoiseFloor, sqrtSX, numDet)
49 multiTS = lalpulsar.MakeMultiTimestamps(T0, Tspan, Tsft, Toverlap, numDet)
50 dataParams.multiTimestamps = multiTS
51 dataParams.randSeed = 42
52 dataParams.inputMultiTS = None
53 injectionSources = (
54  lalpulsar.PulsarParamsVector()
55 ) # empty struct, don't actually inject a signal
56 
57 # make fake data directly in memory
58 multiSFTs = 0
59 print("Generating SFTs with CWMakeFakeMultiData...")
60 multiSFTs, _ = lalpulsar.CWMakeFakeMultiData(
61  multiSFTs,
62  None,
63  injectionSources=injectionSources,
64  dataParams=dataParams,
65  edat=ephemerides,
66 )
67 assert multiSFTs.length == numDet
68 numSFTs = lal.CreateUINT4Vector(numDet)
69 numSFTs.data = [sft.length for sft in multiSFTs.data]
70 f0 = multiSFTs.data[0].data[0].f0
71 deltaF = multiSFTs.data[0].data[0].deltaF
72 numBins = multiSFTs.data[0].data[0].data.length
73 print("numIFOs:", numDet)
74 print("numSFTs per IFO:", numSFTs.data)
75 print("numBins per SFT:", numBins)
76 
77 # make an additional hard copy of the SFTs
78 # to test PSD computation with/without normalizing in place
79 print("Copying SFTs...")
80 multiSFTs_copy = lalpulsar.CreateEmptyMultiSFTVector(numSFTs)
81 for X, sftvec in enumerate(multiSFTs.data):
82  for k, sft in enumerate(sftvec.data):
83  lalpulsar.CopySFT(multiSFTs_copy.data[X].data[k], sft)
84 
85 
87  rtol = 1e-15 # float comparison tolerance
88 
89  print("Calling ComputePSDandNormSFTPower() with normalizeSFTsInPlace=False...")
90  psd, multiPSDVector, normSFT = lalpulsar.ComputePSDandNormSFTPower(
91  inputSFTs=multiSFTs,
92  returnMultiPSDVector=True,
93  returnNormSFT=True,
94  blocksRngMed=101,
95  PSDmthopSFTs=1, # arithmean
96  PSDmthopIFOs=0, # arithsum
97  nSFTmthopIFOs=1, # arithmean
98  nSFTmthopSFTs=0, # arithsum
99  normalizeByTotalNumSFTs=False,
100  FreqMin=f_min,
101  FreqBand=f_max - f_min,
102  normalizeSFTsInPlace=False,
103  )
104  print(
105  "PSD from ComputePSDandNormSFTPower() with normalizeSFTsInPlace=False:",
106  psd.data,
107  )
108  assert psd.length == numBins
109  print("normSFT:", normSFT.data)
110  assert normSFT.length == numBins
111  assert multiPSDVector.length == numDet
112  summed_average_IFO_PSD_vector = np.zeros_like(psd.data)
113  for X in range(numDet):
114  assert multiPSDVector.data[X].length == numSFTs.data[X]
115  assert multiPSDVector.data[X].data[0].data.length == psd.length
116  assert multiPSDVector.data[X].data[0].f0 == f0
117  assert multiPSDVector.data[X].data[0].deltaF == deltaF
118  average_IFO_PSD_vector = np.array(
119  [
120  np.mean(
121  [
122  multiPSDVector.data[X].data[n].data.data[k]
123  for n in range(numSFTs.data[X])
124  ]
125  )
126  for k in range(numBins)
127  ]
128  )
129  print("multiPSDVector[{:d}] averaged: {}".format(X, average_IFO_PSD_vector))
130  summed_average_IFO_PSD_vector += average_IFO_PSD_vector
131  print(
132  "sum of per-IFO averaged multiPSDVector entries:", summed_average_IFO_PSD_vector
133  )
134  assert_allclose(summed_average_IFO_PSD_vector, psd.data, rtol=rtol)
135  print("Relative agreement to within {:e}.".format(rtol))
136 
137  print(
138  "Calling ComputePSDandNormSFTPower() again, with normalizeSFTsInPlace=True..."
139  )
140  psd2, multiPSDVector, normSFT2 = lalpulsar.ComputePSDandNormSFTPower(
141  inputSFTs=multiSFTs,
142  returnMultiPSDVector=True,
143  returnNormSFT=True,
144  blocksRngMed=101,
145  PSDmthopSFTs=1, # arithmean
146  PSDmthopIFOs=0, # arithsum
147  nSFTmthopIFOs=1, # arithmean
148  nSFTmthopSFTs=0, # arithsum
149  normalizeByTotalNumSFTs=False,
150  FreqMin=f_min,
151  FreqBand=f_max - f_min,
152  normalizeSFTsInPlace=True,
153  )
154  print(
155  "PSD from ComputePSDandNormSFTPower() with normalizeSFTsInPlace=True:",
156  psd2.data,
157  )
158  assert psd2.length == numBins
159  assert_allclose(psd2.data, psd.data, rtol=rtol)
160  print("Relative PSD agreement to within {:e}.".format(rtol))
161  print("normSFT:", normSFT2.data)
162  assert normSFT2.length == numBins
163  assert_allclose(normSFT2.data, normSFT.data, rtol=rtol)
164  print("Relative normSFT agreement to within {:e}.".format(rtol))
165 
166  print("Calling ComputePSDfromSFTs() on original (now normalized) SFTs...")
167  psd3 = lalpulsar.ComputePSDfromSFTs(
168  inputSFTs=multiSFTs,
169  blocksRngMed=101,
170  PSDmthopSFTs=1, # arithmean
171  PSDmthopIFOs=0, # arithsum
172  normalizeByTotalNumSFTs=False,
173  FreqMin=f_min,
174  FreqBand=f_max - f_min,
175  )
176  print("PSD from ComputePSDfromSFTs() on normalized SFTs:", psd3.data)
177  assert psd3.length == numBins
178  # these will be different, so not comparing the output
179 
180  print("Calling ComputePSDfromSFTs() on copied SFTs...")
181  psd4 = lalpulsar.ComputePSDfromSFTs(
182  inputSFTs=multiSFTs_copy,
183  blocksRngMed=101,
184  PSDmthopSFTs=1, # arithmean
185  PSDmthopIFOs=0, # arithsum
186  normalizeByTotalNumSFTs=False,
187  FreqMin=f_min,
188  FreqBand=f_max - f_min,
189  )
190  print("PSD from ComputePSDfromSFTs() on copied SFTs:", psd4.data)
191  assert psd4.length == numBins
192  assert_allclose(psd4.data, psd.data, rtol=rtol)
193  print("Relative agreement to within {:e}.".format(rtol))
194 
195 
196 if __name__ == "__main__":
197  args = sys.argv[1:] or ["-v", "-rs", "--junit-xml=junit-computePSD.xml"]
198  sys.exit(pytest.main(args=[__file__] + args))
def test_ComputePSDandNormSFTPower()