/*============================================================================= ivana - Inspiral Veto ANAlysis Written June 2002 by Peter Shawhan To compile/link on Solaris: gcc ivana.c -I$LIGOTOOLS/include -L$LIGOTOOLS/lib -ldataflow -lsocket -lnsl \ -o ivana Usage example: ivana ../data/H2_ASQ_triple_c5.xml '../data/H2_MICHCTRL_triple.xml(-0.3,0.3)' \ triple_ranges.txt =============================================================================*/ #include #include #include #include "metaio.h" #define RINGBUFSIZE 16384 #define MAXTIMERANGES 1024 #define CLUSWINDOW 0.1 /*====== Structure definitions ==============================================*/ typedef struct Range_struct { double t1, t2; /*-- Endpoints of time range (relative to timeBase) --*/ double secs; /*-- Length of time range in seconds --*/ double dead; /*-- Seconds of deadtime --*/ int nCand, nCandPass, nCandFail; int nClus, nClusPass, nClusFail; int nVeto, nVetoUsed; } Range; /*====== Function prototypes ================================================*/ void PrintUsage(); void InitRange( Range* range, double t1, double t2 ); /*===========================================================================*/ int main( int argc, char **argv ) { char candFile[256]; char vetospec[256], vetoFile[256]; char outFile[256] = ""; char intext[256], temptext[256]; char* arg; char* chptr; char* chptr2; char* tokptr; FILE* fd; int timeBase = 0; double tTemp1, tTemp2; Range range[MAXTIMERANGES]; Range *cRange = &range[0]; Range *vRange = &range[0]; Range *totals; int nRange=0, iRange=0, iRangeV=0, jRange; double vwinNeg = 0.0, vwinPos = 0.0; double tCand, tCandLast=-999.0, tDeadNeg=0.0, tDeadPos=0.0; double tdead1, tdead2; int iCandS, iCandNS, iVetoS, iVetoNS, iVetoDur; int status, status2, ostatus, candEOF=0, vetoEOF=0; int iveto, iarg, iposarg=0, ipos, pass, clusPass; char ttext[64]; double secfrac; int debug=0; double tVtemp=0.0, durVtemp; struct MetaioParseEnvironment candParseEnv, vetoParseEnv, outParseEnv; const MetaioParseEnv candEnv = &candParseEnv; const MetaioParseEnv vetoEnv = &vetoParseEnv; const MetaioParseEnv outEnv = &outParseEnv; FILE* vetoDatFile = NULL; /*-- For ".dat" output files from absGlitch --*/ /* Ring buffer of rows from the veto file */ int vbufW = 0; int vbufR = 0; double tVeto[RINGBUFSIZE]; double durVeto[RINGBUFSIZE]; int usedVeto[RINGBUFSIZE]; /*------ Beginning of code ------*/ /*------ Parse command-line arguments ------*/ for ( iarg=1; iarg 0 ) { status = sscanf( arg, "%lf", &tTemp1 ); } if ( strlen(chptr) > 0 ) { status2 = sscanf( chptr, "%lf", &tTemp2 ); } chptr--; *chptr = '-'; if ( status && status2 && (tTemp1 > 1.0e6 || (tTemp2 > 1.0e8 && tTemp2 < 1.9e9) ) ) { /*-- User specified a time range --*/ if ( tTemp2 <= tTemp1 ) { printf( "Invalid range specification (t2<=t1): %s\n", arg ); return 1; } nRange = 1; InitRange( &range[0], tTemp1, tTemp2 ); } } /*-- If nRange is still 0, interpret this as a filename (unless it is blank) --*/ strcpy( temptext, arg ); if ( nRange == 0 && strtok(temptext," ") != NULL ) { fd = fopen( arg, "r" ); if ( fd == NULL ) { printf( "Error opening range-definition file %s\n", arg ); PrintUsage(); return 1; } /*-- Read lines from the file and parse them --*/ while ( fgets(intext,sizeof(intext),fd) ) { intext[sizeof(intext)-1] = '\0'; /*-- Strip off comments --*/ chptr = strchr( intext, '#' ); if ( chptr ) { *chptr = '\0'; } /*-- If line is blank, go on to the next line --*/ if ( strtok(intext," \t\n") == NULL ) { continue; } /*-- Parse as a range --*/ chptr = strchr( intext, '-' ); if ( chptr == NULL ) { printf( "Error parsing time range in %s: %s\n", arg, intext ); fclose(fd); return 1; } *chptr = '\0'; chptr++; tTemp1 = 0.0; tTemp2 = 2.0e9; if ( strlen(intext) > 0 ) { if ( sscanf( intext, "%lf", &tTemp1 ) < 1 ) { printf( "Error parsing beginning of time range: %s\n", intext ); fclose(fd); return 1; } } if ( strlen(chptr) > 0 ) { if ( sscanf( chptr, "%lf", &tTemp2 ) < 1 ) { printf( "Error parsing end of time range: %s\n", chptr ); fclose(fd); return 1; } } if ( tTemp2 <= tTemp1 ) { chptr--; *chptr = '-'; printf( "Invalid range specification (t2<=t1): %s\n", intext ); fclose(fd); return 1; } /*-- Add this to the array of ranges --*/ InitRange( &range[nRange], tTemp1, tTemp2 ); nRange++; } fclose( fd ); } break; case 4: /*-- Output filename --*/ strncpy( outFile, arg, sizeof(outFile) ); if ( outFile[sizeof(outFile)-1] != '\0' ) { printf( "Error: output file name is too long\n" ); return 1; } break; default: printf( "Too many positional arguments\n" ); PrintUsage(); return 1; } /*-- End of switch (iposarg) --*/ } /*-- End of loop over arguments (iarg) --*/ if ( iposarg < 2 ) { printf( "Not enough arguments\n" ); PrintUsage(); return 1; } if (debug >= 1) { printf( "nRange=%d\n", nRange ); for ( jRange=0; jRange 0 ) { ostatus = MetaioCreate( outEnv, outFile ); if ( ostatus != 0 ) { printf( "Error opening output file %s\n", outFile ); MetaioAbort( vetoEnv ); MetaioAbort( candEnv ); return 2; } MetaioCopyEnv( outEnv, candEnv ); } else { outEnv->fileRec.fp = NULL; } /*---------- Loop over rows in the candidate file ----------*/ while ( candEOF == 0 ) { status = MetaioGetRow(candEnv); if ( status == 1 ) { /*-- If this is the first event, set the time base. This is a trick to retain more numerical precision in time values --*/ if ( timeBase == 0 ) { timeBase = candEnv->ligo_lw.table.elt[iCandS].data.int_4s; /*-- Modify all time ranges to be relative to the time base --*/ for ( jRange=0; jRangeligo_lw.table.elt[iCandS].data.int_4s - timeBase ) + 1.0e-9 * (double) candEnv->ligo_lw.table.elt[iCandNS].data.int_4s; pass = 1; if ( debug >= 2 ) printf( "tCand is %.4lf\n", tCand ); } else { /*-- We reached end-of-file --*/ candEOF = 1; /*-- Set a time extremely far in the future, so that we read all relevant veto events --*/ tCand = 2.0e9; } /*-- If no time range was specified by the user, set up a single range beginning at the time of the first candidate event; the end time will be set to the time of the last candidate event, later on. If the user specified one or more time ranges but the first time range did not include a start time, set it to the time of the first candidate event. --*/ if ( nRange == 0 ) { nRange = 1; InitRange( &range[0], tCand, 2.0e9 ); } else if ( range[0].t1 == 0.0 ) { range[0].t1 = tCand; } /*-- Check candidate event time against time range, and increment iRange if necessary --*/ while ( tCand >= cRange->t2 && iRange < nRange-1 ) { iRange++; cRange = &range[iRange]; if (debug>=1) printf( "Switching to range %d\n", iRange ); } /*-- Check if candidate event falls between ranges --*/ if ( tCand < cRange->t1 ) continue; if ( ! candEOF ) cRange->nCand++; /*-- Discard veto events in ring buffer which are too far in the past to be relevant --*/ while ( vbufR != vbufW && tVeto[vbufR]+durVeto[vbufR]+vwinPos < tCand ) { vbufR++; if ( vbufR == RINGBUFSIZE ) { vbufR = 0; } } /*-- Enlarge the veto ring buffer with events from the veto file, until we reach a veto event which is too far in the future to be relevant --*/ /*-- Note vwinNeg is normally negative --*/ while ( tVtemp+vwinNeg <= tCand && ! vetoEOF ) { /*-- Read a row from the veto file --*/ if ( vetoDatFile ) { while ( 1 ) { if ( fgets(intext,sizeof(intext),vetoDatFile) == NULL ) { vetoEOF = 1; break; } /*-- Ignore comment lines --*/ if ( intext[0] == '#' ) continue; /*-- Parse the line; ignore it if unparsable --*/ if ( sscanf( intext, "%*s %*s %*s %s %lf", ttext, &durVtemp ) < 2 ) continue; /*-- Parse the time text. We break it up into integer and fractional parts so that we can subtract the timeBase from the integer part before forming a floating-point number --*/ chptr = strchr( ttext, '.' ); if ( chptr == NULL ) { /*-- This is already an integer --*/ secfrac = 0.0; } else { /*-- Convert fractional part; append a 0 in case it's blank --*/ strcat( ttext, "0" ); secfrac = atof( chptr ); /*-- Re-terminate the string to just keep the integer part --*/ *chptr = '\0'; } tVtemp = (double) ( atoi(ttext) - timeBase ) + secfrac; } if ( vetoEOF ) break; } else { status2 = MetaioGetRow(vetoEnv); if ( status2 == -1 ) { printf( "Error while getting row from file %s\n", vetoFile ); if ( outEnv->fileRec.fp ) MetaioAbort(outEnv); MetaioAbort( candEnv ); MetaioAbort(vetoEnv); return 6; } else if ( status2 == 0 ) { /*-- Reached end of file --*/ vetoEOF = 1; break; } /*-- Get veto time, etc. --*/ tVtemp = (double) ( vetoEnv->ligo_lw.table.elt[iVetoS].data.int_4s - timeBase ) + 1.0e-9 * (double) vetoEnv->ligo_lw.table.elt[iVetoNS].data.int_4s; if ( iVetoDur >= 0 ) { durVtemp = (double) vetoEnv->ligo_lw.table.elt[iVetoDur].data.real_4; } else { durVtemp = 0.0; } } if ( debug >= 2 ) printf( " tVeto is %.4lf, duration %.4lf\n", tVtemp, durVtemp ); /*-- Check whether veto event is within time range of interest --*/ while ( tVtemp+vwinNeg >= vRange->t2 && iRangeV < nRange-1 ) { iRangeV++; vRange = &range[iRangeV]; } if ( tVtemp+durVtemp+vwinPos < vRange->t1 ) continue; /*-- If beyond end of last range, ignore any remaining events --*/ if ( tVtemp+vwinNeg >= vRange->t2 ) { vetoEOF = 1; break; } /*-- Now we know this veto event is relevant --*/ vRange->nVeto++; /*-- Figure out what part of veto event is within time range --*/ tdead1 = ( tVtemp+vwinNeg > vRange->t1 ? tVtemp+vwinNeg : vRange->t1 ); tdead2 = ( tVtemp+durVtemp+vwinPos < vRange->t2 ? tVtemp+durVtemp+vwinPos : vRange->t2 ); /*-- Update deadtime total --*/ if ( tdead1 > tDeadPos ) { /*-- There is a live gap between the existing veto cluster and this veto event, so start a new veto cluster --*/ tDeadNeg = tdead1; tDeadPos = tdead2; vRange->dead += (tdead2 - tdead1); } else if ( tdead2 > tDeadPos ) { /*-- This just extends the current veto cluster --*/ vRange->dead += ( tdead2 - tDeadPos ); tDeadPos = tdead2; } else { /*-- This veto event is completely contained in the earlier veto event, so do not change anything --*/ } /*-- If this veto event is too long ago to be relevant, don't add it to the ring buffer --*/ if ( tdead2 < tCand ) continue; /*-- Make sure we have more room in the ring buffer for this veto --*/ if ( vbufW == vbufR-1 || vbufW == vbufR-1+RINGBUFSIZE ) { printf( "Ran out of space in ring buffer (size=%d)\n", RINGBUFSIZE ); if ( outEnv->fileRec.fp ) MetaioAbort(outEnv); MetaioAbort( candEnv ); if ( vetoDatFile ) fclose(vetoDatFile); else MetaioAbort(vetoEnv); return 5; } /*-- Add this veto event to the ring buffer --*/ tVeto[vbufW] = tVtemp; durVeto[vbufW] = durVtemp; usedVeto[vbufW] = 0; vbufW++; if ( vbufW == RINGBUFSIZE ) { vbufW = 0; } } /*-- If we're at the end of the candidate list, exit the loop --*/ if ( candEOF ) break; /*-- See if this is part of the same "cluster" as the last candidate --*/ if ( tCand-tCandLast < CLUSWINDOW ) { /*-- Part of same candidate cluster --*/ } else { /*-- New candidate cluster --*/ cRange->nClus++; clusPass = 0; /* Will be updated if any event in cluster passes --*/ } tCandLast = tCand; /*-- Check for coincidences (within window) with items in ring buffer --*/ iveto = vbufR; while ( iveto != vbufW ) { if ( tVeto[iveto]+durVeto[iveto]+vwinPos < tCand ) { /*-- This veto event is no longer relevant; if it is the first item in the ring buffer, drop it --*/ if ( iveto == vbufR ) { vbufR++; if ( vbufR == RINGBUFSIZE ) { vbufR = 0; } } } else if ( tVeto[iveto]+vwinNeg <= tCand ) { /*-- This veto event overlaps with the candidate event! --*/ pass = 0; if ( usedVeto[iveto] == 0 ) { vRange->nVetoUsed++; usedVeto[iveto] = 1; } if ( debug >= 1 ) printf( "Coinc: cand=%.4lf, veto=%.4lf to %.4lf\n", tCand, tVeto[iveto]+vwinNeg, tVeto[iveto]+durVeto[iveto]+vwinPos ); } else { /*-- This veto event is too far in the future, and since the veto events are sorted by time, we can skip all remaining --*/ break; } iveto++; if ( iveto == RINGBUFSIZE ) { iveto = 0; } } /*-- Check whether the candidate event passes --*/ if (pass) { cRange->nCandPass++; if ( clusPass == 0 ) { cRange->nClusPass++; clusPass = 1; } if ( outEnv->fileRec.fp != NULL ) { /*-- Write out the event --*/ ostatus = MetaioCopyRow( outEnv, candEnv ); ostatus = MetaioPutRow( outEnv ); } } else { cRange->nCandFail++; } } /*-- End loop over candidate events --*/ /*------ Close files ------*/ MetaioAbort(candEnv); if ( vetoDatFile ) fclose(vetoDatFile); else MetaioAbort(vetoEnv); if ( outEnv->fileRec.fp ) MetaioClose(outEnv); /*------ Post-calculations ------*/ /*-- If the upper end of the time range was never specified, set it equal to the time of the last candidate event. Also have to adjust the deadtime total if last veto cluster extends beyond end of range. --*/ if ( cRange->t2 >= 1.9e9 ) { cRange->t2 = tCand; if ( tDeadNeg > cRange->t2 ) { vRange->dead -= (tDeadPos - tDeadNeg); } else if ( tDeadPos > cRange->t2 ) { vRange->dead -= (tDeadPos - cRange->t2 ); } } /*-- Fill cluster "fail" fields --*/ for ( jRange=0; jRangesecs += cRange->secs; totals->dead += cRange->dead; totals->nCand += cRange->nCand; totals->nCandPass += cRange->nCandPass; totals->nCandFail += cRange->nCandFail; totals->nClus += cRange->nClus; totals->nClusPass += cRange->nClusPass; totals->nClusFail += cRange->nClusFail; totals->nVeto += cRange->nVeto; totals->nVetoUsed += cRange->nVetoUsed; } /*------ Report results ------*/ printf( "Candidate clustering window = %.5f seconds\n", CLUSWINDOW ); printf( "Veto window: from %.5f (relative to beginning) to %.5f (relative to end)\n", vwinNeg, vwinPos ); /* FORMAT: ___Start__ _Dur_ __Veto__used__used% __Cand___cut___cut% _Clus___cut__cut% dead% 123456789 12345 123456 123456 12.3% 123456 123456 12.3% 12345 12345 12.3% 12.3% */ printf( "___Start__ _Dur_ __Veto__used__used%% __Cand___cut___cut%% _Clus___cut__cut%% dead%%\n" ); /*-- Loop over all ranges, PLUS the range structure used to hold totals --*/ for ( jRange=0; jRange<=nRange; jRange++ ) { cRange = &range[jRange]; if ( jRange < nRange ) { printf( "%10.0lf %5.0lf", (double)timeBase+cRange->t1, cRange->secs ); } else { printf( " Total %7.0lf", cRange->secs ); } printf( " %6d %6d", cRange->nVeto, cRange->nVetoUsed ); if ( cRange->nVetoUsed < cRange->nVeto ) { printf( " %4.1f%%", 100.0 * (double) cRange->nVetoUsed / (double) cRange->nVeto ); } else if ( cRange->nVeto == 0 ) { printf( " 0 " ); } else { printf( " 100%%" ); } printf( " %6d %6d", cRange->nCand, cRange->nCandFail ); if ( cRange->nCandFail < cRange->nCand ) { printf( " %4.1f%%", 100.0 * (double) cRange->nCandFail / (double) cRange->nCand ); } else if ( cRange->nCand == 0 ) { printf( " 0 " ); } else { printf( " 100%%" ); } printf( " %5d %5d", cRange->nClus, cRange->nClusFail ); if ( cRange->nClusFail < cRange->nClus ) { printf( " %4.1f%%", 100.0 * (double) cRange->nClusFail / (double) cRange->nClus ); } else if ( cRange->nClus == 0 ) { printf( " 0 " ); } else { printf( " 100%%" ); } printf( " %4.1f%%", 100.0 * cRange->dead / cRange->secs ); printf( "\n" ); } return 0; } /*===========================================================================*/ void PrintUsage() { printf( "Usage: ivana []\n" ); printf( " is simply the name of a LIGO_LW table file.\n" ); printf( " has the form '(,)',\n e.g. 'mich_ctrl5.xml(-0.1,+1.1)'\n" ); printf( " NOTE: Enclose in quotes to prevent interpretation by the shell!\n" ); printf( " can be a GPS time range separated by a dash,\n e.g. '693960000-693961000', or the name of a file containing a list of\n ranges of that form (one per line, optionally followed by a comment).\n If omitted, the range is taken to extend from the first candidate event\n to the last candidate event.\n" ); return; } /*===========================================================================*/ void InitRange( Range* range, double t1, double t2 ) { range->t1 = t1; range->t2 = t2; range->secs = t2 - t1; range->dead = 0.0; range->nCand = 0; range->nCandPass = 0; range->nCandFail = 0; range->nClus = 0; range->nClusPass = 0; range->nClusFail = 0; range->nVeto = 0; range->nVetoUsed = 0; return; }