Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
pw_fstat.py
Go to the documentation of this file.
1# Copyright (C) 2019--2023 Benjamin Grace
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## \file
18## \ingroup lalpulsar_python_piecewise_model
19"""
20Compute the F-statistic for a piecewise model template.
21"""
22
23import copy
24import heapq as hq
25import logging
26import time
27
28import numpy as np
29from tqdm import trange
30
31import lal
32import lalpulsar as lp
33
34from . import basis_functions as bf
35from . import gte_and_other_methods as gom
36from . import semicoherent_metric_methods as scmm
37from . import tbank_estimates as tbe
38
39# Returns our FstatInput object, currently uses SFTs built in pwsim
40# fmax (and fmin) should be chosen with the same reasoning outlined in the pwsim notbook. fmax = f0 + 10^-4 * f0 + 58/TSFT,
41# fmin is the same except fmin = f0 - .... AND fmin should be lower than the minimum value any given template we might use
42# over the time period of the relevant SFTs!
44 SFTfmin,
45 SFTfmax,
46 finputdata,
47 timeint=[-1, -2],
48 sft_path=".",
49 detectors=[],
50 inject_params=[],
51):
52
53 Fstat_opt_args = lp.FstatOptionalArgs(lp.FstatOptionalArgsDefaults)
54 Fstat_opt_args.FstatMethod = lp.FMETHOD_DEMOD_BEST
55 Fstat_opt_args.sourceDeltaT = finputdata.sourceDeltaT
56
57 logging.info(f"SFT path: {sft_path}")
58
59 all_minus_one = True
60
61 for elem in inject_params:
62 if elem != -1:
63 all_minus_one = False
64 break
65 logging.debug(inject_params)
66 logging.debug(all_minus_one)
67 logging.debug(not inject_params == [] and not all_minus_one)
68
69 # This code injects a signal, if inject_params is non-empty and all elements are not -1, then we inject the signal present in inject_params
70 if inject_params != [] and not all_minus_one:
71 Tdata = timeint[-1] - timeint[0]
72
73 h0 = finputdata.h0
74 cosi = finputdata.cosi
75 psi = finputdata.psi
76 phi0 = finputdata.phi0
77 tref = timeint[0] + Tdata * finputdata.trefsegfrac
78 Alpha = finputdata.Alpha
79 Delta = finputdata.Delta
80
81 # create injection parameters
82 Fstat_signal = lp.CreatePulsarParamsVector(1)
83 Fstat_signal.data[0].Amp.aPlus = 0.5 * h0 * (1.0 + cosi * cosi)
84 Fstat_signal.data[0].Amp.aCross = h0 * cosi
85 Fstat_signal.data[0].Amp.psi = psi
86 Fstat_signal.data[0].Amp.phi0 = phi0
87 Fstat_signal.data[0].Doppler.refTime = tref
88 Fstat_signal.data[0].Doppler.Alpha = Alpha
89 Fstat_signal.data[0].Doppler.Delta = Delta
90
91 logging.info(f"Injecting parameters: {inject_params} with h0: {h0}")
92
93 # Set doppler params. Done as a for loop in case inject_params changes length (it shouldn't, but just in case)
94 for i in range(len(inject_params)):
95 Fstat_signal.data[0].Doppler.fkdot[i] = inject_params[i]
96
97 Fstat_opt_args.injectSources = Fstat_signal
98
99 dfreq = finputdata.dfreq
100 emphdata = lp.InitBarycenter("earth00-40-DE405.dat.gz", "sun00-40-DE405.dat.gz")
101
102 constraints = lp.SFTConstraints()
103
104 constraints.minStartTime = lal.LIGOTimeGPS(timeint[0])
105 constraints.maxStartTime = lal.LIGOTimeGPS(timeint[1])
106
107 if len(detectors) == 0:
108 catalogue = lp.SFTdataFind(sft_path + "*.sft", constraints)
109
110 sft_path_master = sft_path + "*.sft"
111
112 elif len(detectors) == 1:
113
114 det1_path = sft_path + detectors[0] + "/*.sft"
115
116 catalogue = lp.SFTdataFind(det1_path, constraints)
117
118 sft_path_master = det1_path
119
120 elif len(detectors) == 2:
121
122 det1_path = sft_path + detectors[0] + "/*.sft"
123 det2_path = sft_path + detectors[1] + "/*.sft"
124
125 sft_path_master = det1_path + ";" + det2_path
126
127 catalogue = lp.SFTdataFind(sft_path_master, constraints)
128
129 elif len(detectors) == 3:
130
131 det1_path = sft_path + detectors[0] + "/*.sft"
132 det2_path = sft_path + detectors[1] + "/*.sft"
133 det2_path = sft_path + detectors[3] + "/*.sft"
134
135 sft_path_master = det1_path + ";" + det2_path + ";" + det3_path
136
137 catalogue = lp.SFTdataFind(sft_path_master, constraints)
138
139 logging.debug("Heya!")
140
141 logging.info(f"Path to SFT's being used: {sft_path_master}")
142
143 fstat = lp.CreateFstatInput(
144 catalogue, SFTfmin, SFTfmax, dfreq, emphdata, Fstat_opt_args
145 )
146
147 return fstat
148
149
150# Create fake sft catalog
151def SFTCatalog(tstart, Tdata, finputdata):
152
153 num_det = finputdata.detectors.length
154 Tsft = finputdata.Tsft
155
156 sft_ts = lp.MakeMultiTimestamps(tstart, Tdata, Tsft, 0, num_det)
157 sft_catalog = lp.MultiAddToFakeSFTCatalog(None, finputdata.detectors, sft_ts)
158
159 return sft_catalog
160
161
162# Builds SFTs in memory rather than files
164 SFTfmin,
165 SFTfmax,
166 sft_catalog,
167 finputdata,
168 inject_params,
169 timeint=[-1, -2],
170 trefsegfrac=0.0,
171):
172
173 Tdata = timeint[1] - timeint[0]
174 dfreq = finputdata.dfreq
175 # create default F-statistic optional arguments
176 Fstat_opt_args = lp.FstatOptionalArgs(lp.FstatOptionalArgsDefaults)
177 Fstat_opt_args.FstatMethod = lp.FMETHOD_DEMOD_BEST
178 Fstat_opt_args.sourceDeltaT = finputdata.sourceDeltaT
179
180 h0 = finputdata.h0
181 cosi = finputdata.cosi
182 psi = finputdata.psi
183 phi0 = finputdata.phi0
184 tref = timeint[0] + Tdata * trefsegfrac
185 Alpha = finputdata.Alpha
186 Delta = finputdata.Delta
187
188 # create injection parameters
189 Fstat_signal = lp.CreatePulsarParamsVector(1)
190 Fstat_signal.data[0].Amp.aPlus = 0.5 * h0 * (1.0 + cosi * cosi)
191 Fstat_signal.data[0].Amp.aCross = h0 * cosi
192 Fstat_signal.data[0].Amp.psi = psi
193 Fstat_signal.data[0].Amp.phi0 = phi0
194 Fstat_signal.data[0].Doppler.refTime = tref
195 Fstat_signal.data[0].Doppler.Alpha = Alpha
196 Fstat_signal.data[0].Doppler.Delta = Delta
197
198 logging.debug(f"Injecting parameters: {inject_params} with h0: {h0}")
199
200 # Set doppler params. Done as a for loop in case inject_params changes length (it shouldn't, but just in case)
201 for i in range(len(inject_params)):
202 Fstat_signal.data[0].Doppler.fkdot[i] = inject_params[i]
203
204 Fstat_opt_args.injectSources = Fstat_signal
205
206 Fstat_injection_noise = lp.MultiNoiseFloor()
207 Fstat_injection_noise.length = finputdata.detectors.length
208 # Fstat_injection_noise.sqrtSn = finputdata.noise_sqrt_Sh
209
210 for i, noise in enumerate(finputdata.noise_sqrt_Sh):
211 Fstat_injection_noise.sqrtSn[i] = noise
212
213 Fstat_opt_args.injectSqrtSX = Fstat_injection_noise
214
215 ephemerides = lp.InitBarycenter("earth00-19-DE405.dat.gz", "sun00-19-DE405.dat.gz")
216
217 # create F-statistic input data
218 Fstat_input = lp.CreateFstatInput(
219 sft_catalog, SFTfmin, SFTfmax, dfreq, ephemerides, Fstat_opt_args
220 )
221
222 return Fstat_input
223
224
225# Returns our fstat results for a given template (using sft's from fstatin).
227 template,
228 finputdata,
229 fstatinputarray,
230 transformmatrices,
231 trefsegfrac=0.0,
232 rtnsum=False,
233):
234
235 s = int(len(transformmatrices[0]) / 2)
236
237 segs = int(len(template) / s - 1)
238
239 dopparams = lp.PulsarDopplerParams()
240 dopparams.Alpha = finputdata.Alpha
241 dopparams.Delta = finputdata.Delta
242
243 counter = 0
244
245 fstat_res_array = []
246
247 for seg in range(segs):
248 counter += 1
249 segtemp = template[seg * s : (seg + 2) * s]
250
251 tref = (
252 bf.knotslist[seg]
253 + (bf.knotslist[seg + 1] - bf.knotslist[seg]) * trefsegfrac
254 )
255 dopparams.refTime = tref
256
257 p0 = bf.knotslist[seg]
258 p1 = bf.knotslist[seg + 1]
259
260 fstatin = fstatinputarray[seg]
261
262 tstart = bf.knotslist[0]
263 dopplerparams = np.matmul(transformmatrices[seg], segtemp)
264
265 # If you want to use conditioning and conditioning matrices, the below line is what should be used. The addition of the appropriate conditioning matrix will
266 # be required also. The conditioning matrix that should be used is also given (for appropriate choices of p0, p1 and tref).
267 # doplerparams = gom.PWParamstoTrefParamsPreComputed(segtemp, transformmatrices[seg], condmatrices[seg])
268 # condmatrix = gom.ParamTransformationConditioningMatrix(p0, p1, tref)
269
270 dopparams.fkdot[0 : len(dopplerparams)] = dopplerparams
271
272 numFreqBins = 1
273 whatToCompute = lp.FSTATQ_2F_PER_DET + lp.FSTATQ_2F
274 fstatresults = lp.FstatResults()
275 fstatresults = lp.ComputeFstat(
276 fstatresults, fstatin, dopparams, numFreqBins, whatToCompute
277 )
278
279 fstat_res_array.append(fstatresults)
280
281 return fstat_res_array
282
283
284def fstat_res_heap_to_file(filename, fstat_res_heap, fstats_first=True):
285 with open(filename, "a+") as reader:
286
287 for elem in fstat_res_heap:
288 if fstats_first:
289 mismatch = elem[1]
290 else:
291 mismatch = elem[0]
292
293 template = elem[3]
294 fstat_res_array = elem[4]
295
296 fstats_on_segs = [fstat_res.twoF[0] for fstat_res in fstat_res_array]
297 fstat = sum(fstats_on_segs) / len(fstats_on_segs)
298
299 # 2D list. Each sublist corresponds to a given detector. Each element of the sublists are the 2F's on each piecewise segment
300 twoF_on_segs_on_detectors = []
301
302 numdets = fstat_res_array[0].numDetectors
303
304 for i in range(numdets):
305 two_F_on_segs = [
306 fstat_res.twoFPerDet(i)[0] for fstat_res in fstat_res_array
307 ]
308 twoF_on_segs_on_detectors.append(two_F_on_segs)
309
310 two_F_per_det = []
311
312 for detector_2Fs in twoF_on_segs_on_detectors:
313 this_det_2F = sum(detector_2Fs) / len(detector_2Fs)
314
315 two_F_per_det.append(this_det_2F)
316
317 # Note, the addition of each templateline string starting with a space makes that string have a length of 16, not 15
318 templateline = " ".join("{:20.12E}".format(x) for x in template)
319
320 fstat_line = "{:20.10f}".format(fstat) + " "
321
322 for twoF in two_F_per_det:
323 fstat_line += "{:20.10f}".format(twoF) + " "
324
325 mismatch_line = "{:20.10f}".format(mismatch) + " "
326
327 if fstats_first:
328 line = fstat_line + mismatch_line + templateline + "\n"
329 else:
330 line = mismatch_line + fstat_line + templateline + "\n"
331
332 reader.write(line)
333
334
335def add_fstat_to_dic(fstat, dic):
336
337 base = 0
338
339 if fstat < 50:
340 base = 1
341 elif 50 <= fstat < 500:
342 base = 10
343 else:
344 base = 100
345
346 lower_round = int(fstat - fstat % base)
347
348 if lower_round in dic:
349 dic[lower_round] += 1
350 else:
351 dic[lower_round] = 1
352
353
354def dic_to_file(filename, dic):
355 with open(filename, "a+") as reader:
356
357 for k, v in dic.items():
358 line = str(k) + ", " + str(v) + "\n"
359
360 reader.write(line)
361
362
363# For a template bank associated with the parameter space associated with tbank, finds the 'tbank.maxtemps' number of templates with the greatest FStat.
364# SFTfmin and SFTfmax are the frequency range of SFTs we want to load in, tbank is the parameter space parameters we use. filename is the name of the file
365# where the F-statistics and their corresponding templates will be saved. tbank.maxtemps is the top 2F's and templates to store and saved. directory is the
366# path to where filename will be saved. trefsegfrac is how far along each segment the reference time will be defined. E.g. If set to 0, tref will be at the
367# start of each segment, if trefsegfrac = 0.5, then tref will occur half way through each segment and so on. signalparams are the signal parameters of the signal
368# injected into the SFTs and rtnsum tells us whether to save the summed 2F across all segments or the average. rtnsum = False means the average is saved,
369# True and the sum will be saved
371 SFTfmin,
372 SFTfmax,
373 tbank,
374 finputdata,
375 signalparams,
376 filename,
377 out_directory=".",
378 SFTdirectory=".",
379 trefsegfrac=0.0,
380 rtnsum=False,
381 SFTFiles=False,
382 tempsperfile=1000,
383 Fstat_mismatch=False,
384 fstat_hist=False,
385 mode="search",
386):
387
388 segs = len(bf.knotslist) - 1
389 logging.info("Knots: %s", str(bf.knotslist))
390
391 finalknot = len(bf.knotslist)
392 s = tbank.s
393
394 # The below lines are required for finding the nearest lattice point to the signal parameters
395
396 # Create LatticeTiling object
397 tiling = lp.CreateLatticeTiling(s * finalknot)
398
399 logging.debug("Doing metric")
400 metric = scmm.PreCompMetric(s)
401
402 logging.debug("Doing Bounds")
403 tbe.setbounds(tiling, tbank)
404
405 logging.debug("Setting tiling lattice and metric")
406
407 # Set metric, mismatch and lattice type
408 lp.SetTilingLatticeAndMetric(
409 tiling, lp.TILING_LATTICE_ANSTAR, metric, tbank.maxmismatch
410 )
411
412 signalpoint = lal.gsl_vector(s * finalknot)
413 signalpoint.data = signalparams
414
415 nearestpoint = lal.gsl_vector(s * finalknot)
416
417 nearestpoint.data = signalpoint.data
418 nearesttemp = nearestpoint.data
419
420 diffvec = []
421
422 for i in range(len(nearesttemp)):
423 diffvec.append(nearesttemp[i] - signalparams[i])
424
425 # The nearest point and its mismatch are calculated. An iterator is now constructed, the Finputs built and 2F is calculated for each template
426 # derived from tbank
427
428 # Create Iterator
429 iterator = lp.CreateLatticeTilingIterator(tiling, s * finalknot)
430
431 for i in range(s * finalknot):
432 stats = lp.LatticeTilingStatistics(tiling, i)
433 logging.info(f"Total points in dim={i}: {stats.total_points}")
434 logging.info(f"Minimum points in dim={i}: {stats.min_points}")
435 logging.info(f"Maximum points in dim={i}: {stats.max_points}")
436
437 fstatinputarray = []
438 transformmatrices = []
439
440 logging.info("Setting up Fstatistic input and transformation matrices")
441
442 for seg in range(segs):
443 logging.debug(f"Doing seg: {seg}")
444 p0 = bf.knotslist[seg]
445 p1 = bf.knotslist[seg + 1]
446 tref = p0 + (p1 - p0) * trefsegfrac
447 tstart = bf.knotslist[0]
448
449 # Note that inject_params are only injected into data IF h0 != 0 otherwise an 'empty' signal is injected
450
451 signalparamsseg = signalparams[seg * s : (seg + 2) * s]
452 inject_params = gom.PWParamstoTrefParams(
453 signalparamsseg, p0 - tstart, p1 - tstart, tref - tstart, s
454 )
455
456 transformmatrices.append(gom.ParamTransformationMatrix(p0, p1, tref, s))
457
458 if SFTFiles or finputdata.inject_data:
459 finput = fstatinputfromSFTFiles(
460 SFTfmin,
461 SFTfmax,
462 finputdata,
463 timeint=[p0, p1],
464 sft_path=SFTdirectory,
465 detectors=finputdata.detectors.data,
466 inject_params=inject_params,
467 )
468 else:
469 sft_catalog = SFTCatalog(p0, p1 - p0, finputdata)
470 finput = fstatinput(
471 SFTfmin,
472 SFTfmax,
473 sft_catalog,
474 finputdata,
475 inject_params,
476 timeint=[p0, p1],
477 trefsegfrac=trefsegfrac,
478 )
479 fstatinputarray.append(finput)
480
481 logging.info("Finputs done")
482
483 # 2F for the signal parameters and nearest template to them. They are then written to a file. The first two lines of data in these files are then
484 # the signal parameters and nearest point and are therefore not a part of the search. The "NearestLatticeTilingPoint" method currently selects
485 # a point well outside of the parameter space, so to prevent errors we have just set the nearestfstat value to -1.
486 sigfstat = computefstat(
487 signalparams,
488 finputdata,
489 fstatinputarray,
490 transformmatrices,
491 trefsegfrac=trefsegfrac,
492 rtnsum=rtnsum,
493 )
494
495 all_sig_twoFs = [elem.twoF[0] for elem in sigfstat]
496 sigtwoF = sum(all_sig_twoFs) / (len(all_sig_twoFs))
497
498 sig_fstat_heap = [[sigtwoF, -1, -1, signalparams, sigfstat]]
499
501 out_directory + "/" + filename, sig_fstat_heap, fstats_first=True
502 )
503
504 mismatchfilename = "Mismatch_" + filename[:-4] + ".txt"
505 # fstattemptofile(directory + "/" + mismatchfilename, signalparams, 0, sigfstat)
506 # fstattemptofile(directory + "/" + mismatchfilename, nearestpoint.data, nearestmismatch, nearestfstat)
508 out_directory + "/" + mismatchfilename, sig_fstat_heap, fstats_first=False
509 )
510
511 # fin is iterated through by the NextLatticeTilingPoint, when fin == 0, the parameter space associated with tbank has been completed tiled.
512 fin = -1
513
514 # The temp heap, which will contain the highest 2F's and their associated templates. The elements of the tempheap have form (fstat, counter, template)
515 template = lal.gsl_vector(s * finalknot)
516
517 starttime = time.time()
518
519 logging.info("Counting templates ...")
520 tempcount = lp.TotalLatticeTilingPoints(iterator)
521 logging.info(f"... finished: number of templates = {tempcount}")
522
523 if mode == "template_count":
524 return 0, 0, 0, tempcount
525
526 lowest_mismatch_metric = 1000000000000000000000000
527 lowest_mismatch_fstat = 1000000000000000000000000
528
529 tqdm_file = open(filename[:-4] + "_tqdm_Output.txt", "w+")
530
531 # If we are creating a mismatch histogram and are using the metric definition of the mismatch, we do not need to calculate any 2F's, so for efficiency
532 # we do not calculate them and return the lowest found mismatch from the generated template bank
533 if mode == "mis_hist" and not Fstat_mismatch:
534
535 for counter in trange(tempcount, file=tqdm_file):
536 fin = lp.NextLatticeTilingPoint(iterator, template)
537 thistemp = copy.copy(template.data)
538
539 tempdiffvec = [
540 signalparams[i] - thistemp[i] for i in range(len(signalparams))
541 ]
542
543 tempmismatch = np.dot(tempdiffvec, np.dot(metric, tempdiffvec))
544
545 if tempmismatch < lowest_mismatch_metric:
546 lowest_mismatch_metric = tempmismatch
547
548 return lowest_mismatch_metric, 0, 0, tempcount
549
550 fstat_res_heap = []
551 mismatch_fstat_res_heap = []
552
553 fstatmax = 0
554 lowest_mismatch = 100000000000
555
556 fstat_hist_dic = {}
557
558 # Iterate through the template bank and calculate 2F's for each as well as their mismatches. The templates with the greatest 2F's (and lowest mismatches)
559 # are saved to a heap, which is then written to a file once the entire template bank has been iterated through
560 for counter in trange(tempcount, file=tqdm_file):
561 fin = lp.NextLatticeTilingPoint(iterator, template)
562 thistemp = copy.copy(template.data)
563
564 fstat = computefstat(
565 thistemp,
566 finputdata,
567 fstatinputarray,
568 transformmatrices,
569 trefsegfrac=trefsegfrac,
570 rtnsum=rtnsum,
571 )
572
573 all_twoFs = [res.twoF[0] for res in fstat]
574
575 twoF = sum(all_twoFs) / len(all_twoFs)
576
577 if twoF > fstatmax:
578 fstatmax = twoF
579
580 tempdiffvec = [signalparams[i] - thistemp[i] for i in range(len(signalparams))]
581
582 temp_mismatch_metric = np.dot(tempdiffvec, np.dot(metric, tempdiffvec))
583 temp_mismatch_fstat = (sigtwoF - twoF) / (sigtwoF - 4)
584
585 if temp_mismatch_metric < lowest_mismatch_metric:
586 lowest_mismatch_metric = temp_mismatch_metric
587
588 if temp_mismatch_fstat < lowest_mismatch_fstat:
589 lowest_mismatch_fstat = temp_mismatch_fstat
590
591 if Fstat_mismatch:
592 this_mismatch = temp_mismatch_fstat
593 else:
594 this_mismatch = temp_mismatch_metric
595
596 if this_mismatch < lowest_mismatch:
597 lowest_mismatch = this_mismatch
598
599 # We include a counter when using heappush and heappushpop in the case that we comes acros the case where two f-statistics are equal
600 # and hence will then try and find the next 'greatest' template, which will throw an error, given that the templates are arrays.
601 if counter < tempsperfile:
602 hq.heappush(fstat_res_heap, (twoF, this_mismatch, counter, thistemp, fstat))
603 hq.heappush(
604 mismatch_fstat_res_heap,
605 (-this_mismatch, twoF, counter, thistemp, fstat),
606 )
607 else:
608 hq.heappushpop(
609 fstat_res_heap, (twoF, this_mismatch, counter, thistemp, fstat)
610 )
611 hq.heappushpop(
612 mismatch_fstat_res_heap,
613 (-this_mismatch, twoF, counter, thistemp, fstat),
614 )
615
616 if fstat_hist:
617 add_fstat_to_dic(twoF, fstat_hist_dic)
618
619 if fstat_hist:
620 fstat_hist_file_name = out_directory + "/" + "Fstat_hist_" + filename
621 dic_to_file(fstat_hist_file_name, fstat_hist_dic)
622
623 tqdm_file.close()
624
625 for i, elem in enumerate(mismatch_fstat_res_heap):
626 new_mismatch = -1 * elem[0]
627 mismatch_fstat_res_heap[i] = (new_mismatch, elem[1], elem[2], elem[3], elem[4])
628
629 # The mismatch heap is sorted to have lowest mismatches first, while the fstat heap is sorted to have highest element first
630 mismatch_fstat_res_heap.sort()
631 fstat_res_heap.sort(reverse=True)
632
633 # Check that end of template bank has been reached
634 fin = lp.NextLatticeTilingPoint(iterator, template)
635 if fin != 0:
636 raise RuntimeError("end of template bank has not been reached")
637
638 # General stats
639 logging.info(f"Length of tempheap: {len(fstat_res_heap)}")
640 logging.info(f"Templates per file is: {tempsperfile}")
641 logging.info(f"Templates counted: {counter}")
642 logging.info(f"Time Elapsed: {time.time() - starttime}")
643 logging.info(f"Templates per second: {counter / (time.time() - starttime)}")
644
645 # Minimum and maximum 2F's and mismatches
646 logging.info(f"Sig f-stat: {sigtwoF}")
647 logging.info("Running maximum f-stat: %s", str(fstatmax))
648 logging.info("Heap maximum f-stat: %s", str(fstat_res_heap[0][0]))
649 logging.info(f"Running lowest mismatch: {lowest_mismatch}")
650 logging.info(f"Heap lowest mismatch: {mismatch_fstat_res_heap[0][0]}")
651
652 # Write both heaps to files
654 out_directory + "/" + filename, fstat_res_heap, fstats_first=True
655 )
657 out_directory + "/" + mismatchfilename,
658 mismatch_fstat_res_heap,
659 fstats_first=False,
660 )
661
662 return lowest_mismatch_metric, lowest_mismatch_fstat, fstatmax, tempcount
663
664
665# As the above, but this is what should be used for doing a search. It has all the superfluous code used for testing removed for fewer over heads and greater efficiency.
667 SFTfmin,
668 SFTfmax,
669 tbank,
670 finputdata,
671 filename,
672 tbankdirectory=".",
673 SFTdirectory=".",
674 trefsegfrac=0.0,
675 rtnsum=False,
676 tempsperfile=1000,
677):
678
679 segs = len(bf.knotslist) - 1
680 logging.info("Knots: %s", str(bf.knotslist))
681
682 finalknot = len(bf.knotslist)
683 s = tbank.s
684
685 # The below lines are required for finding the nearest lattice point to the signal parameters
686
687 # Create LatticeTiling object
688 tiling = lp.CreateLatticeTiling(s * finalknot)
689
690 logging.debug("Doing metric")
691 metric = scmm.PreCompMetric(s)
692
693 logging.debug("Doing Bounds")
694 tbe.setbounds(tiling, tbank)
695
696 logging.debug("Setting tiling lattice and metric")
697
698 # Set metric, mismatch and lattice type
699 lp.SetTilingLatticeAndMetric(
700 tiling, lp.TILING_LATTICE_ANSTAR, metric, tbank.maxmismatch
701 )
702
703 # Create Iterator
704 iterator = lp.CreateLatticeTilingIterator(tiling, s * finalknot)
705
706 for i in range(s * finalknot):
707 stats = lp.LatticeTilingStatistics(tiling, i)
708 logging.info(f"Total points in dim={i}: {stats.total_points}")
709 logging.info(f"Minimum points in dim={i}: {stats.min_points}")
710 logging.info(f"Maximum points in dim={i}: {stats.max_points}")
711
712 fstatinputarray = []
713 transformmatrices = []
714
715 logging.info("Setting up Fstatistic input and transformation matrices")
716
717 for seg in range(segs):
718 logging.debug(f"Doing seg: {seg}")
719 p0 = bf.knotslist[seg]
720 p1 = bf.knotslist[seg + 1]
721 tref = p0 + (p1 - p0) * trefsegfrac
722 tstart = bf.knotslist[0]
723
724 transformmatrices.append(gom.ParamTransformationMatrix(p0, p1, tref, s))
725
726 # We assume that SFTs are follow a directory structure "GW170817/Detector_X/SFT_X.sft". To extract all sfts for our FStatInput we need to have an sft_path with
727 # the pattern Source/*/*.sft, to get all detectors and all of their SFTS. The SFTdirectory path is currently hardcoded in lalpulsar_PiecewiseSearch.py to get us
728 # to the "GW170817" directory level. So when we come to remove this hardcoded path and allow it to be a user input, that user input also necessarily needs to
729 # get to that level directory and should have the same structure. That is, the structure of "GW170817/Detector_X/SFT_X.sft".
730 sft_path = SFTdirectory
731
732 finput = fstatinputfromSFTFiles(
733 SFTfmin,
734 SFTfmax,
735 finputdata,
736 timeint=[p0, p1],
737 sft_path=sft_path,
738 detectors=tbank.detectors,
739 )
740
741 fstatinputarray.append(finput)
742
743 logging.info("Finputs done")
744
745 # fin is iterated through by the NextLatticeTilingPoint, when fin == 0, the parameter space associated with tbank has been completed tiled.
746 fin = -1
747
748 fstatmax = 0
749
750 # The temp heap, which will contain the highest 2F's and their associated templates. The elements of the heap have form (fstat, mismatch, counter, template, FStatResults())
751 template = lal.gsl_vector(s * finalknot)
752 fstat_res_heap = []
753 starttime = time.time()
754
755 logging.info("Counting templates ...")
756 tempcount = lp.TotalLatticeTilingPoints(iterator)
757 logging.info(f"... finished: number of templates = {tempcount}")
758
759 tqdm_file = open(filename[:-4] + "_tqdm_Output.txt", "w+")
760
761 # Iterate through the template bank and calculate 2F's for each as well as their mismatches. The templates with the greatest 2F's (and lowest mismatches)
762 # are saved to a heap, which is then written to a file once the entire template bank has been iterated through
763 for counter in trange(tempcount, file=tqdm_file):
764 fin = lp.NextLatticeTilingPoint(iterator, template)
765 thistemp = copy.copy(template.data)
766
767 fstat = computefstat(
768 thistemp,
769 finputdata,
770 fstatinputarray,
771 transformmatrices,
772 trefsegfrac=trefsegfrac,
773 rtnsum=rtnsum,
774 )
775
776 all_twoFs = [res.twoF[0] for res in fstat]
777
778 twoF = sum(all_twoFs) / len(all_twoFs)
779
780 if twoF > fstatmax:
781 fstatmax = twoF
782
783 # We include a counter when using heappush and heappushpop in the case that we comes acros the case where two f-statistics are equal
784 # and hence will then try and find the next 'greatest' template, which will throw an error, given that the templates are arrays.
785 if counter <= tempsperfile:
786 hq.heappush(fstat_res_heap, (twoF, 0, counter, thistemp, fstat))
787 else:
788 hq.heappushpop(fstat_res_heap, (twoF, 0, counter, thistemp, fstat))
789
790 tqdm_file.close()
791
792 # Check that end of template bank has been reached
793 fin = lp.NextLatticeTilingPoint(iterator, template)
794 if fin != 0:
795 raise RuntimeError("end of template bank has not been reached")
796
797 # General stats
798 logging.info(f"Length of tempheap: {len(fstat_res_heap)}")
799 logging.info(f"Templates per file is: {tempsperfile}")
800 logging.info(f"Templates counted: {counter}")
801 logging.info(f"Time Elapsed: {time.time() - starttime}")
802 logging.info(f"Templates per second: {counter / (time.time() - starttime)}")
803
804 # Minimum and maximum 2F's and mismatches
805 logging.info("Running maximum f-stat: %s", str(fstatmax))
806 logging.info("Heap maximum f-stat: %s", str(fstat_res_heap[-1][0]))
807
808 fstat_res_heap.sort(reverse=True)
809
810 # Write heap to file
811 fstat_res_heap_to_file(tbankdirectory + "/" + filename, fstat_res_heap)
812
813 return 0, 0, fstatmax, tempcount
def dic_to_file(filename, dic)
Definition: pw_fstat.py:354
def fstatinputfromSFTFiles(SFTfmin, SFTfmax, finputdata, timeint=[-1, -2], sft_path=".", detectors=[], inject_params=[])
Definition: pw_fstat.py:51
def semifstatcatalogue(SFTfmin, SFTfmax, tbank, finputdata, signalparams, filename, out_directory=".", SFTdirectory=".", trefsegfrac=0.0, rtnsum=False, SFTFiles=False, tempsperfile=1000, Fstat_mismatch=False, fstat_hist=False, mode="search")
Definition: pw_fstat.py:386
def pw_fstat_search_catalogue(SFTfmin, SFTfmax, tbank, finputdata, filename, tbankdirectory=".", SFTdirectory=".", trefsegfrac=0.0, rtnsum=False, tempsperfile=1000)
Definition: pw_fstat.py:677
def fstat_res_heap_to_file(filename, fstat_res_heap, fstats_first=True)
Definition: pw_fstat.py:284
def SFTCatalog(tstart, Tdata, finputdata)
Definition: pw_fstat.py:151
def computefstat(template, finputdata, fstatinputarray, transformmatrices, trefsegfrac=0.0, rtnsum=False)
Definition: pw_fstat.py:233
def add_fstat_to_dic(fstat, dic)
Definition: pw_fstat.py:335
def fstatinput(SFTfmin, SFTfmax, sft_catalog, finputdata, inject_params, timeint=[-1, -2], trefsegfrac=0.0)
Definition: pw_fstat.py:171
An "SFT-catalogue": a vector of SFTdescriptors, as returned by XLALSFTdataFind()
Definition: SFTfileIO.h:238