#! /bin/sh # This is the LHEA perl script: /cvmfs/extras-fp7.egi.eu/extras/heasoft/suzaku/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxdwampipeline # The purpose of this special block is to make this script work with # the user's local perl, regardless of where that perl is installed. # The variable LHEAPERL is set by the initialization script to # point to the local perl installation. #------------------------------------------------------------------------------- eval ' if [ "x$LHEAPERL" = x ]; then echo "Please run standard LHEA initialization before attempting to run /cvmfs/extras-fp7.egi.eu/extras/heasoft/suzaku/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxdwampipeline." exit 3 elif [ "$LHEAPERL" = noperl ]; then echo "During LHEA initialization, no acceptable version of Perl was found." echo "Cannot execute script /cvmfs/extras-fp7.egi.eu/extras/heasoft/suzaku/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxdwampipeline." exit 3 elif [ `$LHEAPERL -v < /dev/null 2> /dev/null | grep -ic "perl"` -eq 0 ]; then echo "LHEAPERL variable does not point to a usable perl." exit 3 else # Force Perl into 32-bit mode (to match the binaries) if necessary: if [ "x$HD_BUILD_ARCH_32_BIT" = xyes ]; then if [ `$LHEAPERL -V 2> /dev/null | grep -ic "USE_64_BIT"` -ne 0 ]; then VERSIONER_PERL_PREFER_32_BIT=yes export VERSIONER_PERL_PREFER_32_BIT fi fi exec $LHEAPERL -x $0 ${1+"$@"} fi ' if(0); # Do not delete anything above this comment from an installed LHEA script! #------------------------------------------------------------------------------- #!/usr/bin/perl -w =head1 SYNOPSIS hxdwampipeline =head1 DESCRIPTION HXD WAM PIPELINE processing ftool =head1 PROCESSING SUBROUTINES =cut ########## # Pragmas ########## use strict; ############################# # Required core perl modules ############################# use File::Find; use File::Basename; use File::Spec::Functions; use File::Path; use File::Copy; use Cwd 'abs_path'; ########################### # Required HEASoft modules ########################### use HEACORE::HEAINIT; use HEACORE::HEAUTILS; use HEAGen::HEAGen qw( :all ); use Astro::FITS::CFITSIO qw( :longnames :constants ); $| = 1; ################### # Global variables ################### my %Task = ( start => scalar( localtime ), name => "hxdwampipeline", mission => "Suzaku", version => "1.0", releasedate => "2010-12-01", autoparams => [ qw( indir outdir steminputs stemoutputs entrystage exitstage eventmode ) ] ); # input parameters my %params = ( ############################ # Not parameters, but close ############################ taskchatter => 0, # chatter to pass to FTOOLS ); my %paramsStr = ( indir => '', # Input directory outdir => '', # Output directory steminputs => '', # Stem for finding input files stemoutputs => '', # Root filename for output files eventmode => '', # Event modes to process hxdhk_fname => '', # HXD HK fits file name mkf_fname => '', # Makefilter file leapfile => '', # Leap second file name tim_fname => '', # Input .tim FITS file name trn_mkfexpr => '', # HXD TRN makefilter time screening expression trn_gti_fname => '', # HXD TRN additional GTI file trn_lc_mode => '', # TRN mode = PH or TH trn_trigger_set => '', # trigger set to use for TRN (if trn_use_trigger_set) trn_det_alg => '', # Burst detection algorithm for TRN light curvs bst_trigger_set => '', # trigger set to use for BST (if bst_use_trigger_set) bst_det_alg => '', # Burst detection algorithm for BST light curvs ); my %paramsInt = ( entrystage => 0, # Pipeline entry stage exitstage => 0, # Pipeline exit stage time_convert_mode => 0, # HxdTime2aetime convert mode rand_seed => 0, # Seed for random number generator (for hxdwampi) th_mode => 0, # Data mode for BST light curves trn_min_channel => 0, # Minimum channel number for TRN/PH light curves trn_max_channel => 0, # Maximum channel number for TRN/PH light curves bst_energy_mode => 0, # BST light curve production mode for eng chans bst_energy_channel => 0, # BST eng chan for the 1-channel production mode bst_min_channel => 0, # BST min eng chan for the accum production mode bst_max_channel => 0, # BST max eng chan for the accum production mode trn_bgd_integ_time => 0, # Bkg integration time for TRN light curves trn_step_window => 0, # Window size in seconds for step fitting for TRN bst_bgd_integ_time => 0, # Bkg integration time for BST light curves event_freq => 0, # Event printout frequency anl_verbose => 0, # ANL verbose level chatter => 2, # Terminal chattiness level ); my %paramsFlt = ( trn_gtitrim => 0.0, # HXD TRN seconds to cut from GTI START/STOP trn_dt_clk => 0.0, # TRN deadtime clock frequency bst_dt_clk => 0.0, # BST deadtime clock frequency bst_dt_ph => 0.0, # Deadtime for BST/PH data trn_delta_t => 0.0, # Burst integration time for TRN light curves trn_sigma => 0.0, # Burst detection threshold for TRN light curves trn_bgd_early_gap => 0.0, # Gap b/tw early bkg and foregnd intervals for TRN trn_bgd_late_gap => 0.0, # Gap b/tw late bkg and foregnd intervals for TRN trn_step_delchi => 0.0, # Change in delta chi-squared to be considered '1-sigma' trn_gaptol => 0.0, # Gap tolerance (s) for TRN trn_overlaptol => 0.0, # Burst detection overlap tolerance (s) for TRN trn_maxdur => 0.0, # Maximum allowed burst duration for TRN bst_delta_t => 0.0, # Burst integration time for BST light curves bst_sigma => 0.0, # Burst detection threshold for BST light curves bst_bgd_early_gap => 0.0, # Gap b/tw early bkg and foregnd intervals for BST bst_bgd_late_gap => 0.0, # Gap b/tw late bkg and foregnd intervals for BST bst_gaptol => 0.0, # Gap tolerance (s) for BST bst_overlaptol => 0.0, # Burst detection overlap tolerance (s) for BST bst_maxdur => 0.0, # Maximum allowed burst duration for BST ); my %paramsBool = ( create_bstidt => 0, # Create new Burst ID files? trn_exclude_scan => 0, # HXD TRN exclude "WAM scans" in selection crit? trn_dt_cor => 0, # Do deadtime correction for TRN light curves? bst_dt_cor => 0, # Do deadtime correction for BST light curves? trn_use_trigger_set=> 0, # Use HETE2 trigger set for TRN detection trn_durest => 0, # Try to refine burst duration estimate (T50/T90 are always calculated) for TRN? bst_use_trigger_set=> 0, # Use HETE2 trigger set for BST detection bst_durest => 0, # Try to refine burst duration estimate (T50/T90 are always calculated) for BST? anl_profile => 0, # Enable ANL module profiling? clobber => 0, # Overwrite existing output files? history => 0, # Write task HISTORY keywords to output files? cleanup => 0, # Cleanup temporary files? ); my %paramsList = ( trn_tpu_board => [ ], # HXD WAM boards to include in TRN light curves bst_tpu_board => [ ], # HXD WAM boards to include in BST light curves ); # file name extensions and other properties my %fileExts = ( trnufevt => { many => 1, entry => 1, exit => 2, mode => 'TRN', desc => 'TRN unscreened event file', ext => 'hxd_[0-9]_wam_uf.evt', arch => 'hxd/event_uf' }, trnclevt => { many => 0, entry => 3, exit => 3, creat => 2, mode => 'TRN', desc => 'TRN screened event file', ext => 'hxd_0_wam_cl.evt', arch => '' }, bstufevt => { many => 1, entry => 1, exit => 3, mode => 'BST', desc => 'BST unscreened event file', ext => 'hxd_[0-9]_bst[0-9][0-9]_uf.evt', arch => 'hxd/event_uf' }, # NO BST CLEANED EVENTS! #bstclevt => { # many => 1, # entry => 3, # exit => 3, # mode => 'BST', # desc => 'BST screened event file', # ext => 'hxd_[0-9]_bst[0-9][0-9]_cl.evt' #}, bstidt => { many => 1, entry => 1, exit => 1, creat => 1, mode => 'BST', desc => 'WAM Burst ID table', ext => 'hxd_[0-9]_bstidt.fits', creat_par => 'create_bstidt', arch => 'hxd/event_uf' }, hxdhk_fname => { many => 0, entry => 1, exit => 1, desc => 'HXD HK file', ext => 'hxd_0.hk', auxil => 1, arch => 'hxd/hk' }, tim_fname => { many => 0, entry => 1, exit => 2, desc => 'timing packets file', ext => '.tim', auxil => 1, arch => 'auxil' }, mkf_fname => { many => 0, entry => 2, exit => 2, desc => 'makefilter file', ext => '.mkf', auxil => 1, arch => 'auxil' } ); # found input files my %infiles = ( ); # global list of files to remove after run my @clean = ( ); # valid compressed file extensions my $zip = qr/(\.Z|\.z|\.gzip|\.GZIP|\.gz|\.GZ|\.zip\.ZIP)?/; # defaults my %Defaults = ( ##################### # screening criteria ##################### screening => { # TRN mode TRN => join ( '&&', 'SAA_HXD==0', 'T_SAA_HXD>500', 'TN_SAA_HXD>180', 'COR>6', 'HXD_HV_T0_CAL>700', 'HXD_HV_T1_CAL>700', 'HXD_HV_T2_CAL>700', 'HXD_HV_T3_CAL>700', 'HXD_HV_W0_CAL>700', 'HXD_HV_W1_CAL>700', 'HXD_HV_W2_CAL>700', 'HXD_HV_W3_CAL>700' ), # BST mode - no screening necessary #BST => join ( '&&', 'SAA_HXD==0', 'T_SAA_HXD>500', 'TN_SAA_HXD>180', # '!isnull(HXD_HV_T0_CAL)', 'HXD_HV_T0_CAL>700', # '!isnull(HXD_HV_T1_CAL)', 'HXD_HV_T1_CAL>700', # '!isnull(HXD_HV_T2_CAL)', 'HXD_HV_T2_CAL>700', # '!isnull(HXD_HV_T3_CAL)', 'HXD_HV_T3_CAL>700', # '!isnull(HXD_HV_W0_CAL)', 'HXD_HV_W0_CAL>700', # '!isnull(HXD_HV_W1_CAL)', 'HXD_HV_W1_CAL>700', # '!isnull(HXD_HV_W2_CAL)', 'HXD_HV_W2_CAL>700', # '!isnull(HXD_HV_W3_CAL)', 'HXD_HV_W3_CAL>700' ), }, # light curve channels lc_channels => { TRN => { th0 => [ 2, 3 ], th1 => [ 4, 7 ], th2 => [ 8, 16 ], th3 => [ 17, 54 ], thall => [ 2, 54 ] } }, ); # END globals #------------------------------------------------------------------------------ ###################################### # wrap the whole thing in headas_main ###################################### exit( headas_main( \&hxdwampipeline ) ); =head3 hxdwampipeline =over Main subroutine. Does everything. Calls: getParams checkParams findInputFiles setupOutputDirectory createBSTIDTable calibrateWAMTRN calibrateWAMBST filterWAM extractWAMLC runSub dumpTimingReport =back =cut sub hxdwampipeline { my $stat = 0; my $timingReport = [ ]; ############################################## # set task name and version and chatter stuff ############################################## set_toolname( $Task{name} ); set_toolversion( $Task{version} ); setTask( $Task{name}, $Task{version} ); setChat( 2 ); setSysChat( 2 ); ########################################################################### # Initialization # # getting parameters, finding input files etc. # intro( ); ( $stat = getParams( ) ) == 0 || goto CLEANUP; setChat( $params{chatter} ); my @stage0 = ( "checking input parameters" => \&checkParams, "finding input files" => \&findInputFiles, "setting up output dir" => \&setupOutputDirectory, "preparing auxil files" => \&setupAuxilFiles, ); while ( my ( $desc, $proc ) = ( ( shift @stage0 ), ( shift @stage0 ) ) ) { last unless $desc; ( $stat = runSub( $timingReport, $desc, $proc ) ) == 0 || goto CLEANUP; } goto STAGE2 if $params{entrystage} == 2; goto STAGE3 if $params{entrystage} == 3; #-------------------------------------------------------------------------- ########################################################################### # Stage I # # calibration # headingnp( 2, "========= STAGE I =========" ); my @stage1 = ( "checking required files" => [ \&checkStageRequiredFiles, 1 ], "creating BSTID table" => \&createBSTIDTable, "calibrating WAM TRN events" => \&calibrateWAMTRN, "calibrating WAM BST events" => \&calibrateWAMBST, ); while ( my ( $desc, $proc ) = ( ( shift @stage1 ), ( shift @stage1 ) ) ) { last unless $desc; ( $stat = runSub( $timingReport, $desc, $proc ) ) == 0 || goto CLEANUP; } goto CLEANUP if $params{exitstage} == 1; #-------------------------------------------------------------------------- ########################################################################### # Stage II # # filtering event lists # STAGE2: headingnp( 2, "========= STAGE II =========" ); my @stage2 = ( "checking required files" => [ \&checkStageRequiredFiles, 2 ], "creating screened events" => \&filterWAM, ); while ( my ( $desc, $proc ) = ( ( shift @stage2 ), ( shift @stage2 ) ) ) { last unless $desc; ( $stat = runSub( $timingReport, $desc, $proc ) ) == 0 || goto CLEANUP; } goto CLEANUP if $params{exitstage} == 2; #-------------------------------------------------------------------------- ########################################################################### # Stage III # # product generation # STAGE3: headingnp( 2, "========= STAGE III =========" ); my @stage3 = ( "checking required files" => [ \&checkStageRequiredFiles, 3 ], "extracting light curves" => \&extractWAMLC, "detecting event bursts" => \&detectBursts, ); while ( my ( $desc, $proc ) = ( ( shift @stage3 ), ( shift @stage3 ) ) ) { last unless $desc; ( $stat = runSub( $timingReport, $desc, $proc ) ) == 0 || goto CLEANUP; } #-------------------------------------------------------------------------- ################### # do cleanup tasks ################### CLEANUP: if ( $params{cleanup} ) { cleanup( @clean ); } if ( $stat == 0 ) { dumpTimingReport( $timingReport ); } outtro( $stat ); return $stat; } =head3 getParams =over Gets all input parameters. Calls: getStrParam getFltParam getBoolParam getIntParam getListParam getFileParam =back =cut sub getParams { my $stat = 0; ########################### # get automatic parameters ########################### my @reqparm = ( ); if ( exists $Task{autoparams} && ref $Task{autoparams} eq 'ARRAY' ) { @reqparm = @{$Task{autoparams}}; foreach my $reqparm ( @reqparm ) { if ( exists $paramsStr{$reqparm} ) { $stat = getStrParam( $reqparm, \$paramsStr{$reqparm} ); return $stat unless $stat == 0; } elsif ( exists $paramsInt{$reqparm} ) { $stat = getIntParam( $reqparm, \$paramsInt{$reqparm} ); return $stat unless $stat == 0; } elsif ( exists $paramsFlt{$reqparm} ) { $stat = getFltParam( $reqparm, \$paramsFlt{$reqparm} ); return $stat unless $stat == 0; } elsif ( exists $paramsBool{$reqparm} ) { $stat = getBoolParam( $reqparm, \$paramsBool{$reqparm} ); return $stat unless $stat == 0; } elsif ( exists $paramsList{$reqparm} ) { $stat = getListParam( $reqparm, \$paramsList{$reqparm} ); return $stat unless $stat == 0; } else { errmsg( "Unknown parameter $reqparm" ); return -1; } } } ########################### # get hidden string params ########################### foreach my $par ( keys %paramsStr ) { next if grep /^$par$/, @reqparm; $stat = getStrParam( $par, \$paramsStr{$par} ); return $stat unless $stat == 0; } map { $params{$_} = $paramsStr{$_}; } keys %paramsStr; ######################## # get hidden int params ######################## foreach my $par ( keys %paramsInt ) { next if grep /^$par$/, @reqparm; $stat = getIntParam( $par, \$paramsInt{$par} ); return $stat unless $stat == 0; } map { $params{$_} = $paramsInt{$_}; } keys %paramsInt; ########################## # get hidden float params ########################## foreach my $par ( keys %paramsFlt ) { next if grep /^$par$/, @reqparm; $stat = getFltParam( $par, \$paramsFlt{$par} ); return $stat unless $stat == 0; } map { $params{$_} = $paramsFlt{$_}; } keys %paramsFlt; ############################ # get hidden boolean params ############################ foreach my $par ( keys %paramsBool ) { next if grep /^$par$/, @reqparm; $stat = getBoolParam( $par, \$paramsBool{$par} ); return $stat unless $stat == 0; } map { $params{$_} = $paramsBool{$_}; } keys %paramsBool; ######################### # get hidden list params ######################### foreach my $par ( keys %paramsList ) { next if grep /^$par$/, @reqparm; $stat = getListParam( $par, \@{$paramsList{$par}} ); return $stat unless $stat == 0; } map { $params{$_} = $paramsList{$_}; } keys %paramsList; return $stat; } =head3 checkParams =over Checks input parameters for sanity. =back =cut sub checkParams { my $stat = 0; chat( 2, "Checking input parameters\n" ); ####################################### # make eventmode parameter into a list ####################################### if ( $params{eventmode} =~ /^ALL$/i ) { $params{eventmode} = [ qw( BST TRN ) ]; } else { $params{eventmode} = [ $params{eventmode} ]; } ######################### # check entry/exit stage ######################### if ( $params{entrystage} > $params{exitstage} ) { error( 1, "entrystage must be <= exitstage\n" ); $stat = $stat == 0 ? -1 : $stat; } ########################## # check stem input/output ########################## if ( $params{steminputs} eq "" ) { error( 1, "empty steminputs parameter!\n" ); $stat = $stat == 0 ? -1 : $stat; } if ( $params{stemoutputs} eq "" ) { error( 1, "empty stemoutputs parameter!\n" ); $stat = $stat == 0 ? -1 : $stat; } elsif ( $params{stemoutputs} =~ /^default$/i ) { $params{stemoutputs} = basename( $params{steminputs} ); } ################ # check clobber ################ if ( -d $params{outdir} && !$params{clobber} ) { error( 1, "output dir $params{outdir} exists and clobber not set!\n" ); $stat = $stat == 0 ? -1 : $stat; } ############################ # check TPU board parameter ############################ foreach my $mode ( @{$params{eventmode}} ) { my $stat2 = checkTPUBoardParam( $mode ); $stat = $stat == 0 ? $stat2 : $stat; } ####################################### # check TRN light curve mode parameter ####################################### if ( $params{trn_lc_mode} ne 'PH' && $params{trn_lc_mode} ne 'TH' ) { error( 1, "Invalid mode %s specified for trn_lc_mode parameter\n", $params{trn_lc_mode} ); $stat = $stat == 0 ? -1 : $stat; } ##################### # setup task chatter ##################### $params{taskchatter} = $params{chatter} == 0 ? 0 : $params{chatter} - 1; return $stat; } =head3 checkTPUBoardParam =over Checks *_tpu_board parameter for valid values. Makes sorted, unique list of boards in @{$params{*_tpu_board}}. Inputs: datamode (TRN or BST) Outputs: status variable updates *_tpu_board parameters =back =cut sub checkTPUBoardParam { my $mode = shift; my $stat = 0; $mode = lc( $mode ); if ( grep /-1/, @{$params{"${mode}_tpu_board"}} ) { if ( @{$params{"${mode}_tpu_board"}} > 1 ) { warnlo( 2, "ignoring extra elements in " . "${mode}_tpu_board parameter, using -1\n" ); } $params{"${mode}_tpu_board"} = [ -1 ]; } my @tpu_boards = ( ); foreach my $tpu_board ( @{$params{"${mode}_tpu_board"}} ) { if ( $tpu_board < -1 || $tpu_board > 3 ) { error( 1, "${mode}_tpu_board parameter list " . "($tpu_board) out of range\n" ); $stat = $stat == 0 ? -1 : $stat; } if ( !grep /$tpu_board/, @tpu_boards ) { push @tpu_boards, $tpu_board; } } @{$params{"${mode}_tpu_board"}} = sort { $a <=> $b } @tpu_boards; return $stat; } =head3 findInputFiles =over Searches input directory (indir parameter) for suitable input files. =back =cut sub findInputFiles { my $stat = 0; ############### # run the find ############### chat( 2, "Finding input files\n" ); find( { wanted => \&matchInputFiles, no_chdir => 1 }, $params{indir} ); ################ # chat a little ################ foreach my $type ( sort keys %infiles ) { if ( $fileExts{$type}->{many} && exists $infiles{$type} ) { chatnp( 2, "Found $fileExts{$type}->{desc}:\n" ); foreach my $file ( sort @{$infiles{$type}} ) { chatnp( 2, " $file\n" ); } } elsif ( exists $infiles{$type} ) { chatnp( 2, "Found $fileExts{$type}->{desc}:\n" ); chatnp( 2, " $infiles{$type}\n" ); } } return $stat; } =head3 matchInputFiles =over Matches files found with File::Find::find() to patterns defined in %fileExts =back =cut sub matchInputFiles { foreach my $type ( keys %fileExts ) { my $patt = qr/$params{steminputs}$fileExts{$type}->{ext}$zip$/; if ( $File::Find::name =~ /$patt/ ) { if ( $fileExts{$type}->{many} && exists $infiles{$type} ) { if ( fileInList( $File::Find::name, $infiles{$type} ) ) { warnlo( 1, "Duplicate %s found in input dir:\n", $fileExts{$type}->{desc} ); my $bestfile = getBestFileOfType( $File::Find::name, $type, $infiles{$type} ); die unless defined $bestfile; @{$infiles{$type}} = ( $bestfile, listWithoutFile( $bestfile ) ); } else { push @{$infiles{$type}}, $File::Find::name; } @{$infiles{$type}} = sort @{$infiles{$type}}; } elsif ( $fileExts{$type}->{many} ) { $infiles{$type} = [ $File::Find::name ]; } else { if ( $infiles{$type} ) { warnlo( 1, "Duplicate %s found in input dir:\n", $fileExts{$type}->{desc} ); my $bestfile = getBestFileOfType( $File::Find::name, $type, [ $infiles{$type} ] ); die unless defined $bestfile; $infiles{$type} = $bestfile; } else { $infiles{$type} = $File::Find::name; } } last; } } } sub fileInList { my ( $infile, $filelist ) = @_; my $base = sub { return fileparse( $_[ 0 ], $zip ); }; return grep { $base->( $infile ) eq $base->( $_ ) } @$filelist; } sub listWithoutFile { my ( $infile, $filelist ) = @_; my $base = sub { return fileparse( $_[ 0 ], $zip ); }; return grep { $base->( $infile ) ne $base->( $_ ) } @$filelist; } =head3 getBestFileOfType Determines best file of a given type =cut sub getBestFileOfType { my ( $filename, $filetype, $filelist ) = @_; my $bestfile = $filename; my $archdir = $fileExts{$filetype}->{arch}; my $newpatt = catfile( $params{indir}, $archdir, $params{steminputs} . $fileExts{$filetype}->{ext} ); $newpatt = qr/$newpatt/; my @duplfiles = ( ); foreach my $listfile ( $filename, fileInList( $filename, $filelist ) ) { if ( $listfile =~ m#$newpatt# ) { $bestfile = $listfile; last; } push @duplfiles, $listfile; } if ( $bestfile eq $filename && $filename !~ m#$newpatt# ) { my $basefile = basename( $filename, $zip ); error( 1, "Duplicate files named $basefile found in input directory:" ); map( error( 1, " $_" ), @duplfiles ); error( 1, "Neither conforms to orignal archive directory structure." ); error( 1, "Cannot determine best file to use." ); return undef; } return $bestfile; } =head3 setupOutputDirectory =over Creates output directory if not extant. =back =cut sub setupOutputDirectory { if ( !-d $params{outdir} ) { eval { mkpath( $params{outdir} ); }; if ( $@ ) { error( 1, "Failed to create output directory %s\ncheck path\n", $params{outdir} ); return -1; } } if ( CheckInclusion( $params{indir}, $params{outdir} ) ) { error( 1, "The output directory cannot be contained " . "within the input directory.\n" ); error( 1, "Please specify another directory for output.\n" ); return -1; } return 0; } ##################################################################### # Common section for CheckInclusion subroutine # This was borrowed from aepipeline, which borrowed from xrtpipeline ##################################################################### { ################################################ # Build a hash of the subdirectories of $outdir ################################################ my ( $indir, $outdir ); my %suboutdirs; my $found = 0; sub CheckInclusion { ( $indir, $outdir ) = @_; $indir = abs_path( $indir ); $outdir = abs_path( $outdir ); ####################################################### # Traverse the $outdir filesystems, noting the subdirs ####################################################### File::Find::find( { wanted => \&buildsub, follow => 1, follow_skip => 2 }, $outdir ); ################################## # Traverse the $indir filesystems ################################## File::Find::find( { wanted => \&lookout, follow => 1, follow_skip => 2 }, $indir ); return $found; } sub buildsub { my ( $dev, $ino ); unless ( defined $File::Find::fullname ) { return; } if ( -d $_ || -d $File::Find::fullname ) { ( $dev, $ino ) = stat _; $suboutdirs{$File::Find::fullname} = [ $_, $dev, $ino ]; } } sub lookout { my ( $dev, $ino ); unless ( defined $File::Find::fullname ) { return; } if ( -d $_ || -d $File::Find::fullname ) { if ( $suboutdirs{$File::Find::fullname} ) { ( ( $dev, $ino ) = stat _ ); if ( $dev == $suboutdirs{$File::Find::fullname}->[ 1 ] && $ino == $suboutdirs{$File::Find::fullname}->[ 2 ] ) { $File::Find::prune = 1; $found = 1; } } } } } =head3 setupAuxilFiles =over Sets up HXD HK file, makefilter and .tim file based on found files and input parameters. If a file is required given the entry and exit stages, then it is copied to the output directory. =back =cut sub setupAuxilFiles { my $stat = 0; headingnp( 2, "Determining auxiliary files to use" ); foreach my $type ( keys %fileExts ) { next unless $fileExts{$type}->{auxil}; chat( 2, "Checking for $fileExts{$type}->{desc}\n" ); ########################################### # check the entry exit stage for this file # and whether we should use the default ########################################### my $stagematch = ( $fileExts{$type}->{entry} >= $params{entrystage} && $fileExts{$type}->{entry} <= $params{exitstage} ) || ( $fileExts{$type}->{exit} >= $params{entrystage} && $fileExts{$type}->{exit} <= $params{exitstage} ); if ( $stagematch && $params{$type} =~ /^default$/i ) { if ( !exists $infiles{$type} ) { error( 1, "Could not determine %s to use. Check inputs.\n", $fileExts{$type}->{desc} ); return -1; } my $copy = filenameToOutput( $infiles{$type} ); $stat = ftcopy( $infiles{$type}, $copy, 1 ); return $stat unless $stat == 0; $params{$type} = $infiles{$type} = $copy; chat( 2, "Using %s for %s\n", $copy, $fileExts{$type}->{desc} ); } elsif ( $stagematch ) { chat( 2, "Using %s for %s\n", $params{$type}, $fileExts{$type}->{desc} ); } else { chat( 2, "%s not needed\n", $fileExts{$type}->{desc} ); } } return $stat; } =head3 checkStageRequiredFiles =over Checks that required files for the given input stage are found =back =cut sub checkStageRequiredFiles { my $stage = shift; my $stat = 0; headingnp( 2, "Checking for required files for Stage $stage" ); foreach my $type ( sort keys %fileExts ) { ######################################### # check if this type of file is required ######################################### my $stagematch = $fileExts{$type}->{entry} <= $stage && $fileExts{$type}->{exit} >= $stage; if ( $stagematch ) { chat( 2, "Checking for required %s\n", $fileExts{$type}->{desc} ); my $found = 0; # check if we'll create the file here # don't force check if so if ( ( exists $fileExts{$type}->{creat_par} && exists $params{$fileExts{$type}->{creat_par}} && $params{$fileExts{$type}->{creat_par}} ) || ( exists $fileExts{$type}->{creat} && $fileExts{$type}->{creat} >= $stage ) ) { chat( 2, "%s will be created, checking disabled\n", $fileExts{$type}->{desc}); next; } if ( exists $fileExts{$type}->{mode} && grep( /$fileExts{$type}->{mode}/, @{$params{eventmode}} ) ) { if ( !exists $infiles{$type} ) { error( 1, "Failed to find usable %s\n", $fileExts{$type}->{desc} ); $stat = $stat == 0 ? -1 : $stat; $found = 0; } else { $found = 1; } } elsif ( !exists $fileExts{$type}->{mode} && !exists $infiles{$type} ) { error( 1, "Failed to find usable %s\n", $fileExts{$type}->{desc} ); $stat = $stat == 0 ? -1 : $stat; $found = 0; } elsif ( !exists $fileExts{$type}->{mode} && exists $infiles{$type} ) { $found = 1; } ###################################### # make sure they're in the output dir ###################################### if ( $stat == 0 && $found ) { if ( $fileExts{$type}->{many} ) { foreach my $file ( @{$infiles{$type}} ) { my $copy = filenameToOutput( $file ); if ( $copy ne $file ) { $stat = ftcopy( $file, $copy, 1 ); return $stat unless $stat == 0; } $file = $copy; } } else { my $copy = filenameToOutput( $infiles{$type} ); if ( $copy ne $infiles{$type} ) { $stat = ftcopy( $infiles{$type}, $copy, 1 ); return $stat unless $stat == 0; } $infiles{$type} = $copy; } } } } return $stat; } =head3 createBSTIDTable =over Creates a burst ID table using hxdwambstid =back =cut sub createBSTIDTable { my $stat = 0; ##################################################### # if not creating a new set of tables, create a hash # (hashed on RPT number) of bstidt files ##################################################### if ( !$params{create_bstidt} ) { my %bstidts = ( ); foreach my $bstidt ( @{$infiles{bstidt}} ) { $bstidt =~ /hxd_(\d)_bstidt\.fits/; $bstidts{$1} = $bstidt; } $infiles{bstidt} = \%bstidts; return $stat; } headingnp( 2, "Creating Burst ID tables from TRN events" ); ########################### # see if we _can_ continue ########################### if ( !@{$infiles{trnufevt}} ) { error( 1, "At least one TRN event file required " . "for bstidt_fname=CREATE\n" ); return -1; } ################## # make the tables ################## $infiles{bstidt} = { }; foreach my $ufevt ( @{$infiles{trnufevt}} ) { ############################### # calculate the BSTID filename ############################### my $bstidt = filenameToOutput( $ufevt ); $bstidt =~ s/_(\d)_wam_uf\.evt/_$1_bstidt.fits/; my $rpt = $1; $infiles{bstidt}->{$rpt} = $bstidt; ################## # run hxdwambstid ################## chat( 2, "Creating Burst ID table for RPT #$rpt\n" ); chat( 2, "Burst ID table will be created as:\n %s\n", $bstidt ); $stat = runSystem( 'hxdwambstid', { read_iomode => 'readonly', time_change => 'n', pi_change => 'n', quality_change => 'n', gtimode => 'y', gti_time => 'TIME', input_name => $ufevt, create_name => 'none', hklist_name => $infiles{hxdhk_fname}, create_bstidt_name => $bstidt . '.tmp', leapfile => $params{leapfile}, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter} } ); return $stat unless $stat == 0; rename $bstidt . '.tmp', $bstidt; ##################################### # processing complete for this file, # stamp the history ##################################### histStamp( $bstidt ) if $params{history}; } return $stat; } =head3 calibrateWAMTRN =over Calibrates WAM TRN data. Runs the following Suzaku FTOOLS in order: hxdwamtime ... =back =cut sub calibrateWAMTRN { my $stat = 0; return $stat unless grep /TRN/, @{$params{eventmode}}; headingnp( 2, "Calibrating TRN mode event files" ); foreach my $ufevt ( @{$infiles{trnufevt}} ) { ########################################################### # compute the calibrated file name and remove it if extant ########################################################### chat( 2, "Calibrating TRN event file:\n $ufevt\n" ); ################# # run hxdwamtime ################# $stat = runSystem( 'hxdwamtime', { read_iomode => 'create', time_change => 'y', pi_change => 'n', quality_change => 'n', gtimode => 'y', gti_time => 'S_TIME', input_name => $ufevt, create_name => $ufevt . '.tmp', hklist_name => $params{hxdhk_fname}, leapfile => $params{leapfile}, tim_filename => $params{tim_fname}, time_convert_mode => $params{time_convert_mode}, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter} } ); return $stat unless $stat == 0; rename $ufevt . '.tmp', $ufevt; ##################################### # processing complete for this file, # stamp the history ##################################### histStamp( $ufevt ) if $params{history}; } return $stat; } =head3 calibrateWAMBST =over Calibrates WAM BST data. Runs the following Suzaku FTOOLS in order: hxdbsttime =back =cut sub calibrateWAMBST { my $stat = 0; return $stat unless grep /BST/, @{$params{eventmode}}; headingnp( 2, "Calibrating BST mode event files" ); foreach my $ufevt ( @{$infiles{bstufevt}} ) { ########################################################### # compute the calibrated file name and remove it if extant ########################################################### chat( 2, "Calibrating BST event file:\n $ufevt\n" ); ##################################### # lookup the name of the bstidt file ##################################### $ufevt =~ /hxd_(\d)_bst\d\d_uf\.evt/; my $bstidt = $infiles{bstidt}->{$1}; if ( !-f $bstidt ) { error( 1, "\nCould not find bstidt file $bstidt for $ufevt\n" ); error( 1, "$ufevt will not be calibrated!\n\n" ); next; } ################# # run hxdbsttime ################# $stat = runSystem( 'hxdbsttime', { read_iomode => 'create', input_name => $ufevt, create_name => $ufevt . '.tmp', hklist_name => $params{hxdhk_fname}, leapfile => $params{leapfile}, tim_filename => $params{tim_fname}, bstidt_fname => $bstidt, time_convert_mode => $params{time_convert_mode}, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter} } ); return $stat unless $stat == 0; rename $ufevt . '.tmp', $ufevt; ##################################### # processing complete for this file, # stamp the history ##################################### histStamp( $ufevt ) if $params{history}; } return $stat; } =head3 filterWAM =over Creates a GTI based on input parameters, possibly excluding WAM "scan" times. Then runs extractor to create cleaned event files. =back =cut sub filterWAM { my $stat = 0; return $stat unless grep /TRN/, @{$params{eventmode}}; ######################### # ONLY FILTER TRN EVENTS ######################### my $scanGTI = undef; my $scanGTI_run = 0; headingnp( 2, "Screening TRN mode data" ); ################################################################## # Wipe the screened events, in case we found one in the input dir ################################################################## delete $infiles{trnclevt}; ######################## # list of GTIs to merge ######################## my @mgtimelist = ( ); if ( $params{trn_gti_fname} !~ /^none$/i ) { push @mgtimelist, $params{trn_gti_fname}; } ########################################################### # calculate total gti filename and screened event filename ########################################################### my $cleangti = filenameToOutput( $infiles{trnufevt}->[ 0 ] ); $cleangti =~ s/_uf\.evt$/_cl.evt/; $cleangti =~ s/hxd_[0-9]_wam/hxd_0_wam/; $cleangti =~ s/\.evt$/.gti/; ########################################### # get the screening criteria for this mode ########################################### my $mkfexpr = getMKFCriteria( "TRN" ); ################################# # get the WAM-SCAN GTI if needed ################################# if ( $params{trn_exclude_scan} ) { if ( !defined $scanGTI && !$scanGTI_run ) { ( $stat, $scanGTI ) = createWAMScanGTI( $infiles{tim_fname} ); return $stat unless $stat == 0; $scanGTI_run = 1; } if ( $scanGTI ) { push @mgtimelist, $scanGTI; } } ################################################################ # if no criteria, warn the user and use the uf events from here ################################################################ if ( !$mkfexpr && !$scanGTI && $params{trn_gti_fname} =~ /^none$/i ) { warnlo( 1, "No screening specified for TRN mode data\n" ); warnlo( 1, "Cleaned events will be un-screened!\n" ); } ########################################### # run maketime to generate a screening GTI ########################################### my $screengti; if ( $mkfexpr ) { $screengti = getTmpFile( 'gti' ); push @clean, $screengti; $stat = maketime( $params{mkf_fname}, $screengti, $mkfexpr ); return $stat unless $stat == 0; ##################### # check for any rows ##################### my $fptr = Astro::FITS::CFITSIO::open_file( $screengti . '[1]', READONLY, $stat ); return $stat unless $stat == 0; my $nrows; $fptr->get_num_rows( $nrows, $stat ); $fptr->close_file( $stat ); if ( $nrows > 0 ) { push @mgtimelist, $screengti; } else { unlink $screengti; warnhi( 1, "No good time in $screengti.\n" ); warnhi( 1, "No screened event will be created!\n" ); return 0; } } ############################ # handle multiple GTI files ############################ if ( @mgtimelist > 1 ) { my $ingtis = join ',', @mgtimelist; $stat = runSystem( 'mgtime', { ingtis => $ingtis, outgti => "!$cleangti", merge => 'AND', instarts => 'START', instops => 'STOP', indates => '-', outstart => 'START', outstop => 'STOP' } ); return $stat unless $stat == 0; } elsif ( @mgtimelist > 0 ) { if ( !copy( $mgtimelist[ 0 ], $cleangti ) ) { error( 1, "failed to copy $mgtimelist[ 0 ] to $cleangti!\n" ); return -1; } } else { $cleangti = 'NONE'; } ############################################################## # shrink the GTI's by $params{trn_gtitrim} to get rid of spurious # artifacts around SAA/WAM scan passages ############################################################## if ( $cleangti ne 'NONE' && $params{trn_gtitrim} > 0.0 ) { chat( 2, "Trimming clean GTI by $params{trn_gtitrim}s\n" ); my $newgti = getTmpFile( 'gti' ); my $filter = "[1][col START=START+$params{trn_gtitrim};" . "STOP=STOP-$params{trn_gtitrim}]"; $stat = ftcopy( $cleangti . $filter, $newgti, 1 ); return $stat unless $stat == 0; if ( !move( $newgti, $cleangti ) ) { error( 1, "failed to move $newgti to $cleangti\n" ); return -1; } } ##################### # check for any rows ##################### if ( $cleangti ne 'NONE' ) { my $fptr = Astro::FITS::CFITSIO::open_file( $cleangti . '[1]', READONLY, $stat ); return $stat unless $stat == 0; my $nrows; $fptr->get_num_rows( $nrows, $stat ); $fptr->close_file( $stat ); if ( $nrows <= 0 ) { unlink $cleangti; warnhi( 1, "No good time in $cleangti.\n" ); warnhi( 1, "No screened event will be created!\n" ); return 0; } } ################################ # calculate the output filename ################################ my $clevt = filenameToOutput( $infiles{trnufevt}->[ 0 ] ); $clevt =~ s/hxd_[0-9]_(wam|bst)_uf\.evt/hxd_0_$1_cl.evt/; ##################################################################### # run extractor for all uf files at once, creating one cleaned event ##################################################################### my $filename = dumpListToTxt( @{$infiles{trnufevt}} ); $stat = runSystem( 'extractor', { exitnow => 'no', filename => '@' . $filename, eventsout => $clevt, imgfile => 'NONE', binf => 1, fullimage => 'yes', phafile => 'NONE', specbin => 1, wtmapb => 'no', swmapx => 'no', swmapy => 'no', binh => 1, wmapver => 2, fitsbinlc => 'NONE', qdpfile => 'NONE', binlc => 1.0, lcthresh => 0.0, lcthwarn => 3.0, lctzero => 'yes', unbinlc => 'NONE', regionfile => 'NONE', timefile => $cleangti, adjustgti => 'yes', gtinam => 'GTI', xcolf => 'TRN_DE_MODULE', ycolf => 'TRN_DE_MODULE', zcolf => 'NONE', xint => 1.0, yint => 1.0, tcol => 'TIME', ecol => "TRN_PI", ccol => 'CCD_ID', gcol => "TRN_QUALITY", gstring => 'NONE', xcolh => 'TRN_DE_MODULE', ycolh => 'TRN_DE_MODULE', gtitxt => 'NONE', xronwn => 'NONE', events => 'EVENTS', gti => 'GTI', timeorder => 'no', timeref => 40000.0, eventkey => 'NONE', phamax => 'PHA_BINS', xfkey => 'NONE', yfkey => 'NONE', xhkey => 'NONE', yhkey => 'NONE', copyall => 'yes', clobber => 'yes' } ); unlink $filename; return $stat unless $stat == 0; ############################ # time sort the output file ############################ $stat = runSystem( 'ftsort', { infile => $clevt . '[EVENTS]', outfile => $clevt . '.sort', columns => 'TIME', method => 'heap', memory => 'yes', unique => 'no', copyall => 'yes', clobber => 'yes', chatter => $params{taskchatter}, history => bs( $params{history} ) } ); return $stat unless $stat == 0; if ( !move( $clevt . '.sort', $clevt ) ) { error( 1, "failed to move $clevt.sort to $clevt\n" ); return -1; } ################################### # save the cleaned event file name ################################### $infiles{trnclevt} = $clevt; ##################################### # processing complete for this file, # stamp the history ##################################### histStamp( $clevt ) if $params{history}; ################################## # wipe the list of uf event files ################################## if ( exists $fileExts{trnufevt} && $fileExts{trnufevt}->{many} ) { $infiles{trnufevt} = [ ]; } else { $infiles{trnufevt} = undef; } return $stat; } =head3 extractWAMLC =over Extracts TRN and BST light curves using hxdmkwamlc and hxdmkbstlc. Actually calls subroutines extractTRNLC() or extractBSTLC(), depending on the modes desired by the user. =back =cut sub extractWAMLC { my $stat = 0; ######################## # loop over event modes ######################## foreach my $mode ( @{$params{eventmode}} ) { if ( $mode eq 'TRN' ) { $stat = extractTRNLC( ); return $stat unless $stat == 0; } else { $stat = extractBSTLC( ); return $stat unless $stat == 0; } } return $stat; } =head3 extractTRNLC =over Runs hxdmkwamlc for cleaned TRN event file. =back =cut sub extractTRNLC { my $stat = 0; headingnp( 2, "Extracting TRN light curves" ); ############################################################### # calculate the output root and output merged light curve name ############################################################### my $outroot = filenameToOutput( $infiles{trnclevt} ); $outroot =~ s/_wam_(uf|cl)\.evt$//; my $outlc = $outroot . '_wam.lc'; ####################### # loop over TPU boards ####################### my @mergelcs = ( ); foreach my $tpu_board ( sort { $a <=> $b } @{$params{trn_tpu_board}} ) { ##################### # chat about the TPU ##################### if ( $tpu_board == -1 ) { chat( 2, "extracting light curves for ALL TPUs\n" ); } else { chat( 2, "extracting light curves for TPU #%d\n", $tpu_board + 1 ); } ################### # setup hxdmkwamlc ################### my $hxdmkwamlc = { read_iomode => 'readonly', time_change => 'y', pi_change => 'n', quality_change => 'n', gtimode => 'y', gti_time => 'TIME', leapfile => $params{leapfile}, input_name => $infiles{trnclevt}, outroot => "!$outroot", tpu_board => $tpu_board, dt_cor => bs( $params{trn_dt_cor} ), dt_clk => $params{trn_dt_clk}, ph_mode => 1, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter} }; ####################################################################### # extract light curves in two ways, depending on trn_lc_mode parameter # if == 'PH', then extract from trn_min_channel to trn_max_channel # if == 'TH', then extract TH mode 4-channel (has to be "hacky") ####################################################################### my @newlcs = ( ); if ( $params{trn_lc_mode} eq 'PH' ) { $hxdmkwamlc->{min_channel} = $params{trn_min_channel}; $hxdmkwamlc->{max_channel} = $params{trn_max_channel}; $stat = runSystem( 'hxdmkwamlc', $hxdmkwamlc ); return $stat unless $stat == 0; @newlcs = getWAMLCFilenames( $outroot, 'TRN', $tpu_board, 1, 1, $params{trn_min_channel}, $params{trn_max_channel} ); } else { ############################## # loop over TH channel groups ############################## foreach my $th ( keys %{$Defaults{lc_channels}->{TRN}} ) { $hxdmkwamlc->{min_channel} = $Defaults{lc_channels}->{TRN}->{$th}->[ 0 ]; $hxdmkwamlc->{max_channel} = $Defaults{lc_channels}->{TRN}->{$th}->[ 1 ]; $stat = runSystem( 'hxdmkwamlc', $hxdmkwamlc ); return $stat unless $stat == 0; ########################################## # get the created output files and rename ########################################## my @oldlcs = getWAMLCFilenames( $outroot, 'TRN', $tpu_board, 1, 1, $hxdmkwamlc->{min_channel}, $hxdmkwamlc->{max_channel} ); foreach my $oldlc ( @oldlcs ) { my $newlc = $oldlc; $newlc =~ s/ph\d+_\d+\.lc$/$th.lc/; rename $oldlc, $newlc; push @newlcs, $newlc; } } } ################################# # filter out rows with FRACEXP<1 ################################# foreach my $lc ( @newlcs ) { my $filtlc = $lc . ".filt"; $stat = ftcopy( $lc . "[1][FRACEXP>=1.0]", $filtlc, 1 ); return $stat unless $stat == 0; if ( !move( $filtlc, $lc ) ) { error( 1,"failed to move $filtlc to $lc\n" ); return -1; } } push @mergelcs, @newlcs } ########################## # merge and stamp history ########################## @mergelcs = sort @mergelcs; my @trnlcs = map( $outlc . '[' . (2*$_+1) . ']', 0..$#mergelcs ); if ( exists $infiles{trnlc} ) { push @{$infiles{trnlc}}, @trnlcs; } else { $infiles{trnlc} = [ @trnlcs ]; } $stat = mergeWAMLCs( \@mergelcs, $outlc ); unlink @mergelcs; return $stat unless $stat == 0; histStamp( $outlc ) if $params{history}; $stat = runSystem( 'ftchecksum', { infile => $outlc, update => 'yes', datasum => 'yes', chatter => $params{taskchatter} } ); return $stat; } =head3 extractBSTLC =over Runs hxdmkbstlc for BST event file(s) =back =cut sub extractBSTLC { my $stat = 0; headingnp( 2, "Extracting BST light curves" ); ############################ # loop over BST event files ############################ my @evts = ( ); if ( !exists $infiles{bstclevt} || !@{$infiles{bstclevt}} ) { if ( exists $infiles{bstufevt} ) { @evts = @{$infiles{bstufevt}}; } } elsif ( !exists $infiles{bstufevt} || !@{$infiles{bstufevt}} ) { if ( exists $infiles{bstclevt} ) { @evts = @{$infiles{bstclevt}}; } } if ( !@evts ) { warnlo( 1, "No BST events to process!\n" ); return $stat; } foreach my $bstevt ( sort @evts ) { chat( 2, "Extracting BST light curves from $bstevt\n" ); ############################################################## # calculate the output root and the merged output light curve ############################################################## my $outroot = filenameToOutput( $bstevt ); $outroot =~ s/_(cl|uf)\.evt$//; my $outlc = $outroot . '.lc'; ####################### # loop over TPU boards ####################### my @mergelcs = ( ); foreach my $tpu_board ( @{$params{bst_tpu_board}} ) { ##################### # chat about the TPU ##################### if ( $tpu_board == -1 ) { chat( 2, "extracting light curves for ALL TPUs\n" ); } else { chat( 2, "extracting light curves for TPU #%d\n", $tpu_board + 1 ); } ########################################### # run the light curve extractor hxdmkbstlc ########################################### $stat = runSystem( 'hxdmkbstlc', { read_iomode => 'readonly', input_name => $bstevt, outroot => "!$outroot", tpu_board => $tpu_board, th_mode => $params{th_mode}, dt_cor => bs( $params{bst_dt_cor} ), dt_clk => $params{bst_dt_clk}, dt_ph => $params{bst_dt_ph}, energy_mode => $params{bst_energy_mode}, energy_channel => $params{bst_energy_channel}, min_channel => $params{bst_min_channel}, max_channel => $params{bst_max_channel}, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter} } ); return $stat unless $stat == 0; ################################# # save the light curve filenames ################################# my @newlcs = getWAMLCFilenames( $outroot, 'BST', $tpu_board, $params{th_mode}, $params{bst_energy_mode}, $params{bst_min_channel}, $params{bst_max_channel}, $params{bst_energy_channel} ); push @mergelcs, @newlcs; } ############################################# # merge all light curves for this event file ############################################# @mergelcs = sort @mergelcs; my @bstlcs = map( $outlc . '[' . (2*$_+1) . ']', 0..$#mergelcs ); if ( exists $infiles{bstlc} ) { push @{$infiles{bstlc}}, @bstlcs; } else { $infiles{bstlc} = [ @bstlcs ]; } $stat = mergeWAMLCs( \@mergelcs, $outlc ); unlink @mergelcs; return $stat unless $stat == 0; histStamp( $outlc ) if $params{history}; $stat = runSystem( 'ftchecksum', { infile => $outlc, update => 'yes', datasum => 'yes', chatter => $params{taskchatter} } ); return $stat unless $stat == 0; } return $stat; } =head3 detectBursts =over Runs hxdbstjudge on each light curve file to detect bursts. =back =cut sub detectBursts { my $stat = 0; ################################# # loop over modes (TRN then BST) ################################# my @detfiles = ( ); foreach my $mode ( reverse @{$params{eventmode}} ) { my $lcmode = lc( $mode ); next unless exists $infiles{"${lcmode}lc"}; ################################################ # detect bursts in every light curve we created ################################################ foreach my $lc ( @{$infiles{"${lcmode}lc"}} ) { next if $lc =~ /all\.lc$zip$/; ######################################## # calculate the name of the output file ######################################## my $detfile = getTmpFile( 'fits' ); #################################################################### # STEP detection parameters for BST mode do not exist so fudge them #################################################################### my ( $step_window, $step_delchi ); if ( $lcmode eq 'bst' ) { $step_window = 240.0; $step_delchi = 2.71; } else { $step_window = $params{"${lcmode}_step_window"}; $step_delchi = $params{"${lcmode}_step_delchi"}; } ################## # run hxdbstjudge ################## $stat = runSystem( 'hxdbstjudge', { input_name => $lc, outfile => $detfile, det_alg => $params{"${lcmode}_det_alg"}, use_trigger_set => bs( $params{"${lcmode}_use_trigger_set"} ), trigger_set => $params{"${lcmode}_trigger_set"}, bgd_integ_time => $params{"${lcmode}_bgd_integ_time"}, delta_t => $params{"${lcmode}_delta_t"}, sigma => $params{"${lcmode}_sigma"}, bgd_early_gap => $params{"${lcmode}_bgd_early_gap"}, bgd_late_gap => $params{"${lcmode}_bgd_late_gap"}, gaptol => $params{"${lcmode}_gaptol"}, overlaptol => $params{"${lcmode}_overlaptol"}, maxdur => $params{"${lcmode}_maxdur"}, durest => bs( $params{"${lcmode}_durest"} ), step_window => $step_window, step_delchi => $step_delchi, outtype => 0, # FITS output only leapfile => $params{leapfile}, num_event => -1, event_freq => $params{event_freq}, anl_verbose => $params{anl_verbose}, anl_profile => bs( $params{anl_profile} ), chatter => $params{taskchatter}, clobber => 'yes' } ); if ( $stat != 0 ) { warnhi( 1, "hxdbstjudge failed on $lc\n" ); warnhi( 1, "Light curve may be invalid\n" ); $stat = 0; } if ( -e $detfile ) { push @detfiles, $detfile; } } } push @clean, @detfiles; return $stat unless @detfiles; ############################################# # merge all detect files into a single table # and remove some header keywords ############################################# my $tmpfile = dumpListToTxt( @detfiles ); push @clean, $tmpfile; my $outfile = catfile( $params{outdir}, $params{stemoutputs} . "hxd_0_wam_bst.det" ); $stat = runSystem( 'ftmerge', { infile => '@' . $tmpfile, outfile => $outfile, columns => '*', insertrow => 0, lastkey => "", copyall => 'yes', skipbadfiles => 'yes', clobber => 'yes', chatter => $params{taskchatter}, history => 'no' } ); return $stat unless $stat == 0; foreach my $ext ( 0, 1 ) { $stat = runSystem( 'fthedit', { infile => $outfile . "[$ext]", operation => 'a', keyword => 'DETNAM', value => 'WAM_ANTI', chatter => $params{taskchatter}, history => 'no' } ); return $stat unless $stat == 0; foreach my $key (qw( DATAMODE TLM_FILE )) { $stat = runSystem( 'fthedit', { infile => $outfile . "[$ext]", operation => 'd', keyword => $key, chatter => $params{taskchatter}, history => 'no' } ); $stat = 0; } } $stat = runSystem( 'ftsort', { infile => $outfile, outfile => $outfile . '.sort', columns => 'TIMESTART', method => 'heap', memory => 'yes', unique => 'no', copyall => 'yes', clobber => 'yes', chatter => $params{taskchatter}, history => 'no' } ); return $stat unless $stat == 0; rename $outfile . '.sort', $outfile; unlink @detfiles, $tmpfile; return $stat; } =head1 SUPPORT SUBROUTINES =cut =head3 getWAMLCFilenames =over Given a file root name, an event mode, TPU board number, th/ph mode, min/max channels (TRN only), returns a list of expected output filenames. BST light curve filenames are like: ae100039010hxd_1_bst01_wam1_th0.lc ae100039010hxd_1_bst01_wam1_th1.lc ae100039010hxd_1_bst01_wam1_th2.lc ae100039010hxd_1_bst01_wam1_th3.lc ae100039010hxd_1_bst01_wam1_thall.lc TRN light curve filenames are like: ae404081010hxd_3_wam2_ph0_54.lc =back =cut sub getWAMLCFilenames { my ( $base, $mode, $tpu, $thmode, $enmode, $minchan, $maxchan, $enchan ) = @_; my @tpus = ( ); if ( $tpu == -1 ) { @tpus = ( 0, 1, 2, 3 ); } else { @tpus = ( $tpu ); } my @filenames = ( ); foreach my $tpu ( @tpus ) { my $tpuname = "wam" . $tpu; if ( $mode eq 'BST' ) { if ( $thmode == 0 ) { if ( $enmode == -1 ) { push @filenames, map( join( '_', $base, $tpuname, "ph$_.lc" ), 0..54 ); } elsif ( $enmode == 0 ) { push @filenames, join( '_', $base, $tpuname, "ph${enchan}.lc" ); } elsif ( $enmode == 1 ) { push @filenames, join( '_', $base, $tpuname, "ph${minchan}_${maxchan}.lc" ); } } else { if ( $enmode == -1 ) { push @filenames, map( join( '_', $base, $tpuname, "th$_.lc" ), ( 0..3, "all" ) ); } elsif ( $enmode == 0 ) { push @filenames, join( '_', $base, $tpuname, "th${enchan}.lc" ); } elsif ( $enmode == 1 ) { push @filenames, join( '_', $base, $tpuname, "th${minchan}_${maxchan}.lc" ); } } } else { push @filenames, join( '_', $base, $tpuname, "ph${minchan}_${maxchan}.lc" ); } } return @filenames; } =head3 mergeWAMLCs =over Merges all light curves for a given detector (wam[0-3]) and a given mode (BST/TRN) =back =cut sub mergeWAMLCs { my ( $lcs, $lcout ) = @_; my $stat = 0; my @lcs = sort @$lcs; return $stat unless @lcs; ################################################ # copy the first one to the new file and update # the primary header to be reasonably generic ################################################ my $lc0 = shift @lcs; $stat = ftcopy( $lc0 . "[1][col #EXTNAME='RATE1';*]", $lcout, 0 ); return $stat unless $stat == 0; $stat = ftappend( $lc0 . "[2][col #EXTNAME='GTI1';*]", $lcout ); return $stat unless $stat == 0; my $keysfile = getTmpFile( 'txt' ); push @clean, $keysfile; my @copykeys = qw( TELESCOP INSTRUME OBS_MODE DETNAM DATAMODE OBS_ID DATE-OBS TIME-OBS DATE-END TIME-END OBSERVER OBJECT OBS_REM RA_OBJ DEC_OBJ RA_PNT DEC_PNT RA_NOM DEC_NOM PA_NOM MEAN_EA1 MEAN_EA2 MEAN_EA3 RADECSYS EQUINOX ); $stat = runSystem( 'ftlist', { infile => $lc0 . "[0][col #DETNAM='WAM_ANTI']", option => 'K', outfile => $keysfile, clobber => 'yes', include => join( ',', @copykeys ) } ); return $stat unless $stat == 0; $stat = runSystem( 'fthedit', { infile => $lcout . "[0]", keyword => '@' . $keysfile, chatter => $params{taskchatter}, history => 'no' } ); return $stat unless $stat == 0; unlink $keysfile; ###################### # now append the rest ###################### my $i = 1; foreach my $lc ( sort @lcs ) { $i++; $stat = ftappend( $lc . "[1][col #EXTNAME='RATE$i';*]", $lcout ); return $stat unless $stat == 0; $stat = ftappend( $lc . "[2][col #EXTNAME='GTI$i';*]", $lcout ); return $stat unless $stat == 0; } return $stat; } =head3 filenameToOutput =over Takes input filename and gives corresponding output filename (relative path). Inputs: input filename Outputs: corresponding file in output dir =back =cut sub filenameToOutput { my $infile = shift; ############### # get basename ############### my $outfilebase = ( fileparse( $infile, qr/$zip/ ) )[ 0 ]; ###################################### # replace steminputs with stemoutputs ###################################### my $outfile = $outfilebase; my $replace = basename( $params{steminputs} ); $outfile =~ s/$replace/$params{stemoutputs}/; ##################### # construct new name ##################### $outfile = catfile( $params{outdir}, $outfile ); return $outfile; } =head3 getMKFCriteria =over Returns makefilter file screening criteria given an event mode. Inputs: event mode Outputs: selection criteria for maketime =back =cut sub getMKFCriteria { my $mode = shift; my $lcmode = lc( $mode ); my $mkfexpr; if ( $params{"${lcmode}_mkfexpr"} =~ /^default/i ) { $mkfexpr = $Defaults{screening}->{$mode}; } else { $mkfexpr = $params{"${lcmode}_mkfexpr"}; } return $mkfexpr; } =head3 createWAMScanGTI =over Creates a GTI that excludes periods where the WAM is in "scan". This is typically done once per day by the operations team just after an SAA passage. The .tim file contains a list of operations commands in the 5th extension, OG_NAME column. The command hxd_t?, where ? is 0-3 marks the start of the scan, and the 'hxd_tpu_normal' command marks the end of the scan. NOTE: This method does not exclude all WAM scans for unknown reasons. Inputs: .tim filename to use Outputs: Status variable Name of WAM Scan GTI =back =cut sub createWAMScanGTI { my $tim = shift; my $stat = 0; ############################### # setup various filters to use ############################### my $colfilt1 = <<EOF; WAM_SCAN = ( OG_NAME == 'hxd_t0' || OG_NAME == 'hxd_t1' || OG_NAME == 'hxd_t2' || OG_NAME == 'hxd_t3' ) ? 2 : ( OG_NAME == 'hxd_tpu_normal' ? 1 : 0 ); * EOF my $colfilt2 = <<EOF; WAM_SCAN2(1B) = ( WAM_SCAN == 1 || WAM_SCAN == 0 ) ? 0 : 1; * EOF my $rowfilt1 = <<EOF; #row == 1 || WAM_SCAN == 1 || WAM_SCAN == 2 || #row == NAXIS2 EOF ######################################################################## # run ftcopy to create a table containing a new column called WAM_SCAN, # which is 0 when OG_NAME=='hxd_tpu_normal', 1 when OG_NAME=='hxd_t?' # and 2 otherwise. ######################################################################## my $colfile1 = dumpListToTxt( $colfilt1 ); my $tmpfile1 = getTmpFile( 'fits' ); $stat = ftcopy( $tim . '[5]' . "[col \@$colfile1]", $tmpfile1, 0 ); unlink $colfile1; if ( $stat != 0 ) { unlink $tmpfile1; return $stat; } ######################################################################## # run ftcopy again to create another table with a new column WAM_SCAN2, # which is 1 during the WAM scans, and 0 otherwise ######################################################################## my $colfile2 = dumpListToTxt( $colfilt2 ); my $rowfile2 = dumpListToTxt( $rowfilt1 ); my $tmpfile2 = getTmpFile( 'fits' ); $stat = ftcopy( $tmpfile1 . '[1]' . "[\@$rowfile2][col \@$colfile2]", $tmpfile2, 0 ); unlink $colfile2, $rowfile2; if ( $stat != 0 ) { unlink $tmpfile1, $tmpfile2; return $stat; } ########################################## # compute the name of the output GTI file ########################################## my $ext = '_wam_scan.gti'; my $scanGTI = $tim; $scanGTI =~ s/\.tim$zip$/$ext/; if ( $scanGTI eq $tim ) { $scanGTI .= $ext; } ################################# # run maketime to create the GTI ################################# $stat = maketime( $tmpfile2, $scanGTI, 'WAM_SCAN2==0', 0.0, 1.0 ); ##################### # check for any rows ##################### my $fptr = Astro::FITS::CFITSIO::open_file( $scanGTI . '[1]', READONLY, $stat ); return ( $stat, $scanGTI ) unless $stat == 0; my $nrows; $fptr->get_num_rows( $nrows, $stat ); $fptr->close_file( $stat ); if ( $nrows <= 0 ) { $scanGTI = undef; } ############################################################### # if there are rows, adjust the earliest START and latest STOP # to coincide with the TSTART and TSTOP from the 1st extension # in the .tim file ############################################################### if ( $scanGTI ) { $fptr = Astro::FITS::CFITSIO::open_file( $tim . '[1]', READONLY, $stat ); return ( $stat, $scanGTI ) unless $stat == 0; ######################## # read the TSTART/TSTOP ######################## my ( $tstart, $tstop ); $fptr->read_key_dbl( "TSTART", $tstart, undef, $stat ); $fptr->read_key_dbl( "TSTOP", $tstop, undef, $stat ); $fptr->close_file( $stat ); ############################### # run ftedit to fixup scan GTI ############################### $stat = runSystem( 'ftedit', { infile => $scanGTI . '[1]', column => 'START', row => 1, value => $tstart, chatter => $params{taskchatter}, history => 'no' } ); return ( $stat, $scanGTI ) unless $stat == 0; $stat = runSystem( 'ftedit', { infile => $scanGTI . '[1]', column => 'STOP', row => $nrows, value => $tstop, chatter => $params{taskchatter}, history => 'no' } ); return ( $stat, $scanGTI ) unless $stat == 0; } ####### # done ####### unlink $tmpfile1, $tmpfile2; return ( $stat, $scanGTI ); } =head3 runSub =over Runs a subroutine using the timeSub subroutine timing wrapper. Timing data is stored to the array reference passed as the first argument, along with the subroutine description (second argument). The third argument can either be a 'CODE' reference, or a list reference. If the former, the code reference is passed as is to timeSub. If the latter, the list is expanded, and sent to timeSub. So if a list reference is given as the third argument, the first element in the list should be a reference to a subroutine, and the remaining elements should be the arguments to pass to that subroutine. Inputs: array reference (timing report) subroutine description subroutine definition Calls: timeSub =back =cut sub runSub { my ( $timingReport, $descrip, $proc ) = @_; my $stat = 0; if ( defined $proc ) { if ( ref $proc eq 'ARRAY' ) { my $sub = $proc->[ 0 ]; my @args = @{$proc}[ 1..$#$proc ]; $stat = timeSub( $timingReport, $descrip, $sub, @args ); } elsif ( ref $proc eq 'CODE' ) { $stat = timeSub( $timingReport, $descrip, $proc ); } } return $stat; } =head3 timeSub =over Times a subroutine and adds to input timing report list ref (first arg). The second argument should be a 'CODE' reference to a subroutine to run. Any remaining arguments are passed to that subroutine. Inputs: array reference (timing report) subroutine description subroutine reference additional arguments to subroutine =back =cut sub timeSub( ) { use Time::HiRes qw( gettimeofday ); my $rept = shift; my $desc = shift; my $subr = shift; my @args = ( ); if ( @_ ) { @args = @_; } ############################################## # run the sub, recording start and stop times ############################################## my ( $start_sec, $start_usec ) = gettimeofday( ); my @start = times; my @ret = $subr->( @args ); my @stop = times; my ( $stop_sec, $stop_usec ) = gettimeofday( ); #################################### # calculate the timing of this step #################################### my @elapsed = map( $stop[ $_ ] - $start[ $_ ], 0..$#stop ); my $telapse = ( $stop_sec + $stop_usec * 1.0e-6 ) - ( $start_sec + $start_usec * 1.0e-6 ); my @start_vals = localtime( $start_sec ); my $tstart = sprintf( "%02d:%02d:%02d", $start_vals[ 2 ], $start_vals[ 1 ], $start_vals[ 0 ] ); my @stop_vals = localtime( $stop_sec ); my $tstop = sprintf( "%02d:%02d:%02d", $stop_vals[ 2 ], $stop_vals[ 1 ], $stop_vals[ 0 ] ); ############################ # save the report for later ############################ my $report = { USER => $elapsed[ 0 ], CUSER => $elapsed[ 2 ], SYSTEM => $elapsed[ 1 ], CSYSTEM => $elapsed[ 3 ], START => $tstart, STOP => $tstop, ELAPSE => $telapse, DESCRIP => $desc }; push @$rept, $report; if ( @ret > 1 ) { return @ret; } elsif ( @ret == 1 ) { return $ret[ 0 ]; } } =head3 dumpTimingReport =over Prints the timing report. The timing report is expected to be one generated by timeSub. Inputs: timing report =back =cut sub dumpTimingReport { my $rept = shift; headingnp( 2, "$Task{name} v$Task{version} Run-time Analysis\n" ); format STDOUT_TOP= @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @||||||| @||||||| @>>>>>> @>>>>>> @>>>>> "Analysis Step", "Start", "Stop", "User", "System", "\%Tot" ----------------------------------------------------------------------------- . ###################################### # calculate total times and fractions ###################################### my %telapses = ( USER => 0.0, CUSER => 0.0, SYSTEM => 0.0, CSYSTEM => 0.0, ELAPSE => 0.0 ); foreach my $report ( @$rept ) { foreach my $key ( keys %telapses ) { $telapses{$key} += $report->{$key}; } } foreach my $report ( @$rept ) { foreach my $key ( keys %telapses ) { my $key1 = $key . "FRAC"; my $key2 = $key . "FRACTOT"; if ( $telapses{$key} != 0.0 ) { $report->{$key1} = 100.0 * $report->{$key} / $telapses{$key}; } else { $report->{$key1} = 0.0; } if ( $telapses{ELAPSE} != 0.0 ) { $report->{$key2} = 100.0 * $report->{$key} / $telapses{ELAPSE}; } else { $report->{$key2} = 0.0; } } $report->{USER} += $report->{CUSER}; $report->{SYSTEM} += $report->{CSYSTEM}; } ################################# # now get totals for totals line ################################# $telapses{DESCRIP} = "Total"; $telapses{START} = $rept->[ 0 ]->{START}; $telapses{STOP} = $rept->[ $#$rept ]->{STOP}; $telapses{USER} += $telapses{CUSER}; $telapses{SYSTEM} += $telapses{CSYSTEM}; $telapses{ELAPSEFRACTOT} = 100.0; my $report; format STDOUT = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<< @<<<<<<< @###.#s @###.#s @##.#% $report->{DESCRIP}, $report->{START}, $report->{STOP}, $report->{USER}, $report->{SYSTEM}, $report->{ELAPSEFRACTOT} . foreach $report ( @$rept ) { if ( $params{chatter} >= 2 ) { write STDOUT; } } if ( $params{chatter} >= 2 ) { $report = \%telapses; print STDOUT "----------------------------------------" . "-------------------------------------\n"; write STDOUT; } } =head1 MODIFICATION HISTORY =over 2010-01-11 - Initial Version =back =head1 AUTHOR =over Alex Padgett (Charles.A.Padgett@nasa.gov) =back =head1 BUGS =over None, of course. =back =cut