LALInspiral 5.0.3.1-eeff03c
LALTrigScanCluster.c
Go to the documentation of this file.
1/*
2* Copyright (C) 2007 Anand Sengupta, Craig Robinson
3*
4* This program is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License as published by
6* the Free Software Foundation; either version 2 of the License, or
7* (at your option) any later version.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with with program; see the file COPYING. If not, write to the
16* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17* MA 02110-1301 USA
18*/
19
20/*----------------------------------------------------------------------------
21 *
22 * File Name: LALTrigScanCluster.c
23 *
24 * Author: Sengupta, Anand. S., Gupchup, Jayant A. and Robinson, C. A. K.
25 *
26 *---------------------------------------------------------------------------*/
27
28#include <lal/CoincInspiralEllipsoid.h>
29#include <lal/LIGOMetadataInspiralUtils.h>
30#include <lal/TrigScanEThincaCommon.h>
31#include <lal/LALTrigScanCluster.h>
32
33/**
34 * \author Sengupta, Anand. S. and Gupchup, Jayant A.
35 * \file
36 *
37 * \brief Functions for trigScan clustering
38 *
39 * ### Description ###
40 *
41 * <tt>XLALTrigScanClusterTriggers()</tt> is the main function for invoking
42 * trigScan clustering. It takes in the \c SnglInspiralTable to be clustered,
43 * the method to be applied, the metric scaling factor, and a flag to say whether
44 * to append stragglers (i.e. clusters of only 1 trigger). Upon success, the
45 * return value will be #XLAL_SUCCESS, with the \c SnglInspiralTable having
46 * been clustered. At present, the only clustering method implemented is #T0T3Tc.
47 *
48 * <tt>XLALTrigScanCreateCluster()</tt> takes in a \c TriggerErrorList
49 * containing the triggers, their position vectors and ellipsoid matrices. It
50 * creates the cluster by agglomerating triggers by checking for overlap of
51 * ellipsoids. Upon ellipsoid overlap, the trigger is added to the cluster, and
52 * removed from the unclustered list.
53 *
54 * <tt>XLALTrigScanRemoveStragglers()</tt> takes in a linked list of
55 * \c TrigScanCluster's. It removes all clusters of 1 element from the list.
56 *
57 * <tt>XLALTrigScanKeepLoudestTrigger()</tt> performs the actual clustering,
58 * freeing all triggers in the cluster except the loudest. Currently, loudest
59 * means the trigger with the highest SNR, but this could be extended to other
60 * statistics.
61 *
62 * <tt>XLALTrigScanReLinkLists()</tt> re-links all the inter-cluster lists after
63 * the clustering has been performed, in preparation for returning the clustered
64 * \c SnglInspiralTable to the program.
65 *
66 * <tt>XLALTrigScanDestroyCluster()</tt> frees memory associated with a cluster.
67 * It has two modes of operation, specified by the \c TrigScanStatus. If this
68 * is #TRIGSCAN_ERROR, the \c SnglInspiralTable will also be freed. If it is
69 * #TRIGSCAN_SUCCESS, the \c SnglInspiralTable will be kept for returning to the
70 * calling program.
71 *
72 */
73
75 trigScanType method,
76 REAL8 scaleFactor,
77 INT4 appendStragglers )
78
79{
80 SnglInspiralTable *tableHead = NULL;
81 SnglInspiralTable *thisTable = NULL;
82 TriggerErrorList *errorList = NULL;
83 TrigScanCluster *clusterHead = NULL;
84 TrigScanCluster *thisCluster = NULL;
85
86 /* The maximum time difference associated with an ellipsoid */
87 REAL8 tcMax;
88
89 if ( !table )
90 {
92 }
93
94 if ( (UINT4) method >= (UINT4) NUM_TRIGSCAN_TYPE )
95 {
97 }
98
99 tableHead = *table;
100
101 if ( !tableHead )
102 {
103 XLALPrintWarning( "No triggers to cluster.\n" );
104 return XLAL_SUCCESS;
105 }
106
107 if ( method == trigScanNone )
108 {
109 XLALPrintWarning( "No clustering requested.\n" );
110 return XLAL_SUCCESS;
111 }
112
113 /* TrigScan only currently implemented for tau0/tau3 */
114 if ( method != T0T3Tc )
115 {
116 XLALPrintError( "TrigScan only currently implemented for tau0/tau3!\n" );
118 }
119
120 if ( scaleFactor <= 0.0 )
121 {
122 XLALPrintError( "TrigScan metric scaling must be > 0: %e given.\n", scaleFactor );
124 }
125
126 /* TrigScan requires triggers to be time-ordered. Make sure this is the case */
127 /* and if not, sort the triggers */
128 for ( thisTable = tableHead; thisTable->next; thisTable = thisTable->next )
129 {
130 if ( XLALGPSToINT8NS( &(thisTable->end) )
131 > XLALGPSToINT8NS( &(thisTable->next->end) ) )
132 {
133 *table = tableHead = XLALSortSnglInspiral( tableHead, LALCompareSnglInspiralByTime );
134 break;
135 }
136 }
137
138 /* Firstly, create the matrices, etc required for the clustering */
139 errorList = XLALCreateTriggerErrorList( tableHead, scaleFactor, &tcMax );
140 if ( !errorList )
141 {
143 }
144
145 /* Now create the list of clusters. Keep going until errorlist is exhausted */
146 while ( errorList )
147 {
148 TrigScanCluster *newCluster = NULL;
149
150 newCluster = XLALTrigScanCreateCluster( &errorList, tcMax );
151 /* The next line is to keep track of memory in case of failure */
152 if ( errorList )
153 *table = errorList->trigger;
154 else
155 *table = NULL;
156
157 if ( !newCluster )
158 {
159
160 thisCluster = clusterHead;
161
162 while ( thisCluster )
163 {
164 TrigScanCluster *tmpCluster = thisCluster;
165 thisCluster = thisCluster->next;
167 }
168 if ( errorList ) XLALDestroyTriggerErrorList( errorList );
169
171 }
172 /* Add the cluster to the list */
173 if ( !clusterHead )
174 {
175 clusterHead = thisCluster = newCluster;
176 }
177 else
178 {
179 thisCluster = thisCluster->next = newCluster;
180 }
181 }
182
183 /* Remove stragglers if necessary */
184 if ( !appendStragglers )
185 {
186 if ( XLALTrigScanRemoveStragglers( &clusterHead ) == XLAL_FAILURE )
187 {
188 thisCluster = clusterHead;
189
190 while ( thisCluster )
191 {
192 TrigScanCluster *tmpCluster = thisCluster;
193 thisCluster = thisCluster->next;
195 }
196
198 }
199
200 if ( !clusterHead )
201 {
202 XLALPrintWarning( "All triggers were stragglers! All have been removed.\n" );
203 return XLAL_SUCCESS;
204 }
205 }
206
207 /* Keep the loudest trigger in each cluster */
208 for ( thisCluster = clusterHead; thisCluster; thisCluster = thisCluster->next )
209 {
210 if ( XLALTrigScanKeepLoudestTrigger( thisCluster ) == XLAL_FAILURE )
211 {
212 thisCluster = clusterHead;
213
214 while ( thisCluster )
215 {
216 TrigScanCluster *tmpCluster = thisCluster;
217 thisCluster = thisCluster->next;
219 }
220
222 }
223 }
224
225 /* Re-link the lists ready for returning */
226 if ( XLALTrigScanReLinkLists( clusterHead ) == XLAL_FAILURE )
227 {
228 thisCluster = clusterHead;
229
230 while ( thisCluster )
231 {
232 TrigScanCluster *tmpCluster = thisCluster;
233 thisCluster = thisCluster->next;
235 }
236
238 }
239
240 *table = clusterHead->element->trigger;
241
242 /* Since trigScan can have multiple clusters at similar times */
243 /* We sort the list to ensure time-ordering */
245 XLALPrintInfo( "Returning %d clustered triggers.\n", XLALCountSnglInspiral( *table ) );
246
247 /* Free the memory */
248 thisCluster = clusterHead;
249 while ( thisCluster )
250 {
251 TrigScanCluster *tmpCluster = thisCluster;
252 thisCluster = thisCluster->next;
254 }
255
256 return XLAL_SUCCESS;
257}
258
259
261 REAL8 tcMax )
262
263{
264 TrigScanCluster *cluster = NULL;
265
266 /* Pointers to the main trigger error list */
267 TriggerErrorList *thisErrorList = NULL;
268 TriggerErrorList *previousErrorList = NULL;
269
270 /* Pointers to the trigger error list within the cluster */
271 TriggerErrorList *thisClusterList = NULL;
272 TriggerErrorList *endClusterList = NULL;
273
274 INT8 maxTimeDiff;
275
276 /* Stuff for checking ellipsoid overlap */
277 fContactWorkSpace *workSpace = NULL;
278 REAL8 fContactValue;
279
280 if ( !errorListHead )
281 {
283 }
284
285 if ( !(*errorListHead) )
286 {
288 }
289
290 if ( tcMax <= 0 )
291 {
293 }
294
295 /* Allocate memory for the cluster */
296 cluster = LALCalloc( 1, sizeof( TrigScanCluster ) );
297 if ( !cluster )
298 {
300 }
301
302 /* Create the workspace for checking ellipsoid overlap */
303 workSpace = XLALInitFContactWorkSpace( 3, NULL, NULL, gsl_min_fminimizer_brent, 1.0e-2 );
304 if ( !workSpace )
305 {
306 LALFree( cluster );
308 }
309
310 /* Set the first trigger in the list to be part of the cluster */
311 cluster->element = *errorListHead;
312 endClusterList = cluster->element;
313 cluster->nelements = 1;
314 *errorListHead = cluster->element->next;
315 cluster->element->next = NULL;
316 cluster->element->trigger->next = NULL;
317
318 maxTimeDiff = (INT8)( (2.0 * tcMax + 1.0e-5) * 1.0e9 );
319
320 thisClusterList = cluster->element;
321 /* Now we go through the agglomeration procedure */
322 while ( thisClusterList )
323 {
324 /* Timing info of the trigger in the cluster */
325 INT8 endTimeA;
326 REAL8 originalTimeA;
327
328 /* Timing info of the trigger we wish to compare */
329 INT8 endTimeB;
330 REAL8 originalTimeB;
331
332 endTimeA = XLALGPSToINT8NS( &(thisClusterList->trigger->end) );
333 XLAL_CALLGSL( originalTimeA = gsl_vector_get( thisClusterList->position, 0 ) );
334
335 /* Reset the time to avoid precision problems */
336 XLALSetTimeInPositionVector( thisClusterList->position, 0 );
337
338 /* Loop through the list of triggers */
339 thisErrorList = *errorListHead;
340 previousErrorList = NULL;
341 while ( thisErrorList )
342 {
343
344 endTimeB = XLALGPSToINT8NS( &(thisErrorList->trigger->end) );
345 XLAL_CALLGSL( originalTimeB = gsl_vector_get( thisErrorList->position, 0 ) );
346
347 /* If the triggers are more than twice the max time error apart, no need to proceed */
348 if ( endTimeB - endTimeA > maxTimeDiff )
349 {
350 break;
351 }
352
354 (REAL8) ( ( endTimeB - endTimeA ) * 1.0e-9 ) );
355
356
357 /* check for the intersection of the ellipsoids */
358 workSpace->invQ1 = thisClusterList->err_matrix;
359 workSpace->invQ2 = thisErrorList->err_matrix;
360 fContactValue = XLALCheckOverlapOfEllipsoids( thisClusterList->position,
361 thisErrorList->position, workSpace );
362 if (XLAL_IS_REAL8_FAIL_NAN(fContactValue))
363 {
364 /* The triggers in the cluster have been removed from the main list */
365 /* so they must be freed here */
366 thisClusterList = cluster->element;
367 while ( thisClusterList )
368 {
369 TriggerErrorList *tmpClusterList = thisClusterList;
370 thisClusterList = thisClusterList->next;
371 XLALDestroySnglInspiralTableRow( tmpClusterList->trigger );
372 XLAL_CALLGSL( gsl_matrix_free( tmpClusterList->err_matrix ) );
373 XLAL_CALLGSL( gsl_vector_free( tmpClusterList->position ) );
374 LALFree( tmpClusterList );
375 }
376 XLALFreeFContactWorkSpace( workSpace );
378 }
379 /* Reset the time to its original value */
380 XLALSetTimeInPositionVector( thisErrorList->position, originalTimeB );
381
382 /* test whether we have coincidence */
383 if ( fContactValue <= 1.0 )
384 {
385 /* Add the trigger to the cluster, and pull it off the main list */
386 if ( previousErrorList )
387 {
388 if ( thisErrorList->next )
389 {
390 previousErrorList->trigger->next = thisErrorList->next->trigger;
391 }
392 else
393 {
394 previousErrorList->trigger->next = NULL;
395 }
396 previousErrorList->next = thisErrorList->next;
397 }
398 else
399 {
400 *errorListHead = thisErrorList->next;
401 }
402 endClusterList->trigger->next = thisErrorList->trigger;
403 endClusterList = endClusterList->next = thisErrorList;
404 thisErrorList = thisErrorList->next;
405 endClusterList->next = NULL;
406 endClusterList->trigger->next = NULL;
407 cluster->nelements++;
408 }
409 else
410 {
411 /* No coincidence, so we go on */
412 previousErrorList = thisErrorList;
413 thisErrorList = thisErrorList->next;
414 }
415 }
416 /* Reset the time in the cluster list trigger */
417 XLALSetTimeInPositionVector( thisClusterList->position, originalTimeA );
418 thisClusterList = thisClusterList->next;
419 }
420
421 XLALFreeFContactWorkSpace( workSpace );
422
423 /* We have now clustered the triggers - return the result */
424 XLALPrintInfo( "Returning a cluster containing %d triggers.\n", cluster->nelements );
425 return cluster;
426}
427
428
430
431{
432 TrigScanCluster *previous = NULL; /* Keeping track of the previous element */
433 TrigScanCluster *thisCluster = NULL;
434
435 if ( !clusters )
436 {
438 }
439
440 /* Loop through the list and remove all clusters containing 1 trigger */
441 while ( thisCluster )
442 {
443 if ( thisCluster->nelements == 1 )
444 {
445 TrigScanCluster *tmpCluster = thisCluster;
446
447 thisCluster = thisCluster->next;
448
449 if ( !previous )
450 {
451 *clusters = tmpCluster->next;
452 }
453 else
454 {
455 previous->next = tmpCluster->next;
456 }
458 XLAL_CALLGSL( gsl_matrix_free( tmpCluster->element->err_matrix ) );
459 XLAL_CALLGSL( gsl_vector_free( tmpCluster->element->position ) );
460 LALFree( tmpCluster );
461 }
462 else
463 {
464 previous = thisCluster;
465 thisCluster = thisCluster->next;
466 }
467 }
468
469 return XLAL_SUCCESS;
470}
471
472
474{
475 TriggerErrorList *triggerToKeep;
476 TriggerErrorList *thisTrigger;
477
478 if ( !cluster )
479 {
481 }
482
483 if ( cluster->nelements < 1 )
484 {
485 XLALPrintError( "Invalid number of triggers in cluster: %d\n", cluster->nelements );
487 }
488
489 if ( cluster->nelements == 1 )
490 {
491 /* No need to do anything */
492 return XLAL_SUCCESS;
493 }
494
495 triggerToKeep = cluster->element;
496
497 /* Find the loudest trigger */
498 for ( thisTrigger = cluster->element->next; thisTrigger; thisTrigger = thisTrigger->next )
499 {
500 if ( thisTrigger->trigger->snr > triggerToKeep->trigger->snr )
501 {
502 triggerToKeep = thisTrigger;
503 }
504 }
505
506 /* Keep only the loudest trigger */
507 thisTrigger = cluster->element;
508 while ( thisTrigger )
509 {
510 TriggerErrorList *tmpTrigger = thisTrigger;
511 thisTrigger = thisTrigger->next;
512
513 if ( tmpTrigger != triggerToKeep )
514 {
516 XLAL_CALLGSL( gsl_matrix_free( tmpTrigger->err_matrix ) );
517 XLAL_CALLGSL( gsl_vector_free( tmpTrigger->position ) );
518 LALFree( tmpTrigger );
519 }
520 }
521
522 cluster->element = triggerToKeep;
523 cluster->element->trigger->next = NULL;
524 cluster->element->next = NULL;
525
526 return XLAL_SUCCESS;
527}
528
529
531
532{
533 TrigScanCluster *thisCluster;
534
535 if ( ! clusterHead )
537
538 for ( thisCluster = clusterHead; thisCluster->next; thisCluster = thisCluster->next )
539 {
540 thisCluster->element->trigger->next = thisCluster->next->element->trigger;
541 }
542 /* Set the last one to null */
543 thisCluster->element->trigger->next = NULL;
544
545 return XLAL_SUCCESS;
546}
547
548
551 )
552
553{
554 TriggerErrorList *thisList;
555
556 if ( !cluster )
558
561
562 /* If something has failed, we need to free the SnglInspirals */
563 if ( status == TRIGSCAN_ERROR )
564 {
565 for ( thisList = cluster->element; thisList; thisList = thisList->next )
566 {
568 }
569 }
570
572 LALFree( cluster );
573
574 return;
575}
void XLALFreeFContactWorkSpace(fContactWorkSpace *workSpace)
REAL8 XLALCheckOverlapOfEllipsoids(const gsl_vector *ra, const gsl_vector *rb, fContactWorkSpace *workSpace)
fContactWorkSpace * XLALInitFContactWorkSpace(UINT4 n, const gsl_matrix *a, const gsl_matrix *b, const gsl_min_fminimizer_type *T, REAL8 conv)
#define LALCalloc(m, n)
#define LALFree(p)
int LALCompareSnglInspiralByTime(const void *a, const void *b)
SnglInspiralTable * XLALSortSnglInspiral(SnglInspiralTable *eventHead, int(*comparfunc)(const void *, const void *))
INT4 XLALCountSnglInspiral(SnglInspiralTable *head)
SnglInspiralTable * XLALDestroySnglInspiralTableRow(SnglInspiralTable *row)
#define XLAL_CALLGSL(statement)
int XLALSetTimeInPositionVector(gsl_vector *position, REAL8 timeShift)
double REAL8
int64_t INT8
uint32_t UINT4
int32_t INT4
trigScanType
UNDOCUMENTED.
int XLALTrigScanRemoveStragglers(TrigScanCluster **clusters)
int XLALTrigScanKeepLoudestTrigger(TrigScanCluster *cluster)
void XLALTrigScanDestroyCluster(TrigScanCluster *cluster, TrigScanStatus status)
TrigScanCluster * XLALTrigScanCreateCluster(TriggerErrorList **errorListHead, REAL8 tcMax)
int XLALTrigScanReLinkLists(TrigScanCluster *clusterHead)
int XLALTrigScanClusterTriggers(SnglInspiralTable **table, trigScanType method, REAL8 scaleFactor, INT4 appendStragglers)
TrigScanStatus
UNDOCUMENTED.
@ trigScanNone
UNDOCUMENTED.
@ T0T3Tc
UNDOCUMENTED.
@ NUM_TRIGSCAN_TYPE
UNDOCUMENTED.
@ TRIGSCAN_SUCCESS
UNDOCUMENTED.
@ TRIGSCAN_ERROR
UNDOCUMENTED.
@ TRIGSCAN_NUM_STATUS
UNDOCUMENTED.
TriggerErrorList * XLALCreateTriggerErrorList(SnglInspiralTable *tableHead, REAL8 scaleFactor, REAL8 *tcMax)
void XLALDestroyTriggerErrorList(TriggerErrorList *errorListHead)
#define XLAL_ERROR_VOID(...)
#define XLAL_ERROR_NULL(...)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
int int XLALPrintWarning(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_EFUNC
XLAL_EINVAL
XLAL_FAILURE
INT8 XLALGPSToINT8NS(const LIGOTimeGPS *epoch)
LIGOTimeGPS end
struct tagSnglInspiralTable * next
INT4 nelements
UNDOCUMENTED.
TriggerErrorList * element
UNDOCUMENTED.
struct tagTrigScanCluster * next
UNDOCUMENTED.
The TriggerErrorList is a linked list used within e-thinca.
struct tagTriggerErrorList * next
SnglInspiralTable * trigger
const gsl_matrix * invQ1
const gsl_matrix * invQ2