#! /bin/sh # This is the LHEA perl script: /cvmfs/extras-fp7.egi.eu/extras/heasoft/heagen/x86_64-unknown-linux-gnu-libc2.19-0/bin/eeftable.pl # 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/heagen/x86_64-unknown-linux-gnu-libc2.19-0/bin/eeftable.pl." 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/heagen/x86_64-unknown-linux-gnu-libc2.19-0/bin/eeftable.pl." 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 # # File name: eeftable.pl # Author: Kristin Rutkowski # $Date: 2016/03/23 01:59:57 $ # Version: 1.0 # # This Perl script will create an EEF file from photon history file # # Tool dependencies: # ftcreate # ftcopy # ftappend # fthedit # # Library dependencies: # gen/lib/perl/ahapp # gen/lib/perl/ahgen # gen/lib/perl/ahlog # # Modification history: # # Ver Date Author Description # 1.0 2015-08-10 KLR New header format. # ######################### # Pragmas ######################### use strict; use warnings; ######################### # Packages ######################### # empty parenthesis force explicit use of namespaces use ahlog (); use ahgen (); use ahapp (); # $clobber $chatter $logfile $debug $history $mode use List::Util qw(first); use List::Util qw( sum ); use Math::Trig; # sin, cos, tan, deg2rad use Data::Dumper; # to output arrays for debugging ######################### # Startup ######################### my $nargs = scalar @ARGV ; # Query canonical APE parameters and start logging ahapp::startup () ; ######################### # Input Parameters ######################### # Query non-standard APE parameters. my $infile = ahapp::query_parameter("infile") ; my $outfile = ahapp::query_parameter("outfile") ; my $numRadVals = ahapp::query_parameter("numRadVals"); my $deltaRadVals = ahapp::query_parameter("deltaRadVals"); my $alpha = ahapp::query_parameter("alpha"); my $mgOffaxis = ahapp::query_parameter("mgOffaxis",1); my $mgAzimuth = ahapp::query_parameter("mgAzimuth",1); my $mgEnergy = ahapp::query_parameter("mgEnergy",1); # perl stores bools as 0 or 1. store boolean strings also, for when we're # writing these to output fits files. my $mgOffaxisBool = ($mgOffaxis) ? "yes" : "no"; my $mgAzimuthBool = ($mgAzimuth) ? "yes" : "no"; my $mgEnergyBool = ($mgEnergy) ? "yes" : "no"; my $clobberBool = ($ahapp::clobber) ? "yes" : "no"; ######################### # Other Variables ######################### my $status = 0; # name of extensions in history file my $extension1 = "PHOTONHISTORY"; my $extension2 = "INPUTPHOTONS"; # set some constants my $arcminToRad = 2.908882167604234e-04; my $radianToArcsec = 206264.8005072553933132; my $indivPathCodeSize = 4; # size of an individual path code my $maxPathCodes = 8; # history file stores up to 8 individual path codes # create a 4D array for the eef values # this will hold the radial EEF for each roll, theta, and energy. # if energy is to be averaged, then all the values will go into the first # entry of that last dimension: eefValues[i][j][k][0] # size: (numRadVals, numUnqAzim, numUnqOffaxis, numUnqEnergies) my @eefValues = (); # a corresponding array of normalized EEF values # size: (numRadVals, numUnqAzim, numUnqOffaxis, numUnqEnergies) my @normEEFValues = (); # weighted total of EEF energies, for each Roll, Theta, Energy combination. # If nothing is merged, there will be an extension for each combination and # each extension will have to be normalized by it's respective # totalWeightedEnergy. # size: (numUnqAzim, numUnqOffaxis, numUnqEnergies) my @totalWeightedEnergy = (); # This will be a 4D array, holding the final EEF values, after any merging # numAzimuth etc will be either 1 or numUnqAzimuth, based on merging # size: (numRadVals, numAzimuth, numOffaxis, numEnergies) my @finalEEF = (); # 3D array of how many photons contributed to a particular EEF, # for each eef, roll, theta, and energy. # size: (numUnqAzim, numUnqOffaxis, numUnqEnergies) my @totalpsfphotons = (); # this will be a 3D array, holding final photon count that contributed to the # EEF for a particular azim/theta/energy. This array is for after any # possible merging # size: (numAzimuth, numOffaxis, numEnergies) my @finalPSFPhotons = (); # +++ 20150806 KLR This will hold the radii of photons that are accepted # the output will be be printed to an ascii file, for diagnostic purposes # [roll][theta][energy] my @acceptedPhotonRadialValuesArcsec = (); my @numAcceptedPhotons = (); my @acceptedPhotonPathcode = (); my @acceptedPhotonRowNum = (); ######################### # Initializing ######################### ahlog::ah_out "Start tool eeftable.pl" ; # Pre-Processing $status = ahapp::begin_processing(); unless ( $status == 0 ) { ahlog::ah_err "BeginProcessing" ; ahapp::end_processing(1); } # Write all parameters to the log file. ahlog::ah_info "HIGH", ahapp::write_parameters () ; # Check if the output file already exists. Unless clobber is set, this will # cause the script to fail. unless ($ahapp::clobber) { if (-e $outfile) { ahlog::ah_err "Output file already exist but clobber was not set. Exiting." ; ahapp::end_processing(1); } } # If $outfile already exists, delete it. We have already checked that clobber # has been set. unlink $outfile if -e $outfile ; # Make sure infile exists, before getting its data if (! -e $infile) { ahlog::ah_err "File $infile does not exist. Exiting."; ahapp::end_processing(1); } ahlog::ah_info "HIGH", "Processing file: $infile"; # create a copy of the history file with ftselect my $tmpHistory = $infile . ".ftselect.tmp"; ahapp::add_temp_file($tmpHistory); # add the ! to clobber the temp outfile $status = ahgen::run_ftool("ftcopy", "infile=$infile", "outfile=!$tmpHistory", "copyall=YES", "clobber=yes"); if ($status) { ahlog::ah_err "Error copying infile."; ahlog::ah_err ahgen::get_tool_stderr; ahapp::end_processing(1); } # Check that photons were not originally from a file. If they were from a # file, there could have been multiple sources, and we don't want that. my $srcmdl = ahgen::get_keyword($tmpHistory, $extension1, "SRCMDL"); if ( (uc $srcmdl eq "PHOTONLIST") || (uc $srcmdl eq "GROUNDMODEL")) { ahlog::ah_err "History file $infile must be made from photons not from a file. When running xrtraytrace, use 'source' of point, flatcircle, or betamodel."; ahlog::ah_err ahgen::get_tool_stderr; ahapp::end_processing(1); } # current number of unique offaxis theta angles, azimuthal (roll) phi angles, and energies my $numUnqOffaxis = ahgen::get_keyword($tmpHistory, $extension2, "NOFFAXIS"); my $numUnqAzimuth = ahgen::get_keyword($tmpHistory, $extension2, "NAZIMUTH"); my $numUnqEnergies = ahgen::get_keyword($tmpHistory, $extension2, "NUMENRG"); ahlog::ah_debug "numUnqOffaxis = $numUnqOffaxis \n"; ahlog::ah_debug "numUnqAzimuth = $numUnqAzimuth \n"; ahlog::ah_debug "numUnqEnergies = $numUnqEnergies \n"; # grab each column into an array my @history2InitialTheta = ahgen::read_column($tmpHistory, $extension2, "INITIALTHETA"); my @history2InitialAzimDir = ahgen::read_column($tmpHistory, $extension2, "INITIALAZIMDIR"); my @history2Energy = ahgen::read_column($tmpHistory, $extension2, "ENERGY"); my @history2NumPhotons = ahgen::read_column($tmpHistory, $extension2, "NUMPHOTONS"); # hashes, so we can fill it with data from this file my %currOffaxisHash = (); my %currAzimHash = (); my %currEnergiesHash = (); @currOffaxisHash{@history2InitialTheta} = (); @currAzimHash {@history2InitialAzimDir} = (); @currEnergiesHash{@history2Energy} = (); # arrays of unique values from history file my @unqOffaxisArcmin; my @unqAzimDeg; my @unqEnergies; # create a sorted array of all the unique offaxis theta angles in this file foreach my $key (sort { $a <=> $b} keys %currOffaxisHash) { push @unqOffaxisArcmin, $key ; } # create a sorted array of all the unique azimuthal angles in this file foreach my $key (sort { $a <=> $b} keys %currAzimHash) { push @unqAzimDeg, $key ; } # create a sorted array of all the unique energies in this file foreach my $key (sort { $a <=> $b} keys %currEnergiesHash) { push @unqEnergies, $key ; } if (ahlog::getdebug()) { print Data::Dumper->Dump( [ \@unqOffaxisArcmin ], [ qw(*unqOffaxisArcmin) ] ); print Data::Dumper->Dump( [ \@unqAzimDeg ], [ qw(*unqAzimDeg) ] ); print Data::Dumper->Dump( [ \@unqEnergies ], [ qw(*unqEnergies) ] ); } #ahlog::ah_debug "average of unqOffaxisArcmin = average(@unqOffaxisArcmin)"; #ahlog::ah_debug "average of unqAzimDeg = average(@unqAzimDeg)"; #ahlog::ah_debug "average of unqEnergies = average(@unqEnergies)"; # now that we know number of unique azimuth, etc, initialize the # eef and totalpsfphotons arrays # (numRadVals, numUnqAzim, numUnqOffaxis, numUnqEnergies) for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { $eefValues[$iRad][$iRoll][$iTheta][$iEnergy] = 0; } } } } for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { $totalpsfphotons[$iRoll][$iTheta][$iEnergy] = 0; } } } # go to first extension # store how many total photons (rows) in history file my $numRows = ahgen::get_keyword($tmpHistory, $extension1, "NAXIS2") ; ahlog::ah_debug "numRows in first extension = $numRows \n"; # get the telescope focal length [mm], and other keywords my $telescop = ahgen::get_keyword($tmpHistory, $extension1, "TELESCOP"); my $instrume = ahgen::get_keyword($tmpHistory, $extension1, "INSTRUME"); my $detnam = ahgen::get_keyword($tmpHistory, $extension1, "DETNAM"); my $cvsd0001 = ahgen::get_keyword($tmpHistory, $extension1, "CVSD0001"); my $cvst0001 = ahgen::get_keyword($tmpHistory, $extension1, "CVST0001"); my $focalLengthmm = ahgen::get_keyword($tmpHistory, $extension1, "DFLMM"); my $fpmm2am = ahgen::get_keyword($tmpHistory, $extension1, "FPMM2AM"); ahlog::ah_debug "telescop = $telescop \n"; ahlog::ah_debug "instrume = $instrume \n"; ahlog::ah_debug "detnam = $detnam \n"; ahlog::ah_debug "cvsd0001 = $cvsd0001 \n"; ahlog::ah_debug "cvst0001 = $cvst0001 \n"; ahlog::ah_debug "focalLengthmm = $focalLengthmm \n"; # find out if the history file contain only photons that hit the results plane my $resultsPlaneOnly = ahgen::get_keyword($tmpHistory, $extension1, "RSLPONLY"); # if the history file contained only photons that hit the results plane, then # we don't need to check for the resultsplane column. my $checkForResultsPlane = ( uc $resultsPlaneOnly eq "NO" ? 1 : 0); ahlog::ah_debug "resultsPlaneOnly = $resultsPlaneOnly \n"; ahlog::ah_debug "checkForResultsPlane = $checkForResultsPlane \n"; # store the following history file columns in local arrays # +++ KLR is it practical to store this info here, or should I grab it later # when I need it and only store one row at a time? how does cfitsio do this? (history file may have ~10^9 rows) my @historyEnergy = ahgen::read_column($tmpHistory, $extension1, "energy"); my @historyInitialThetaArcmin = ahgen::read_column($tmpHistory, $extension1, "initialtheta"); my @historyInitialAzimDirDeg = ahgen::read_column($tmpHistory, $extension1, "initialazimdir"); my @historyFinalxPosmm = ahgen::read_column($tmpHistory, $extension1, "finalxpos"); my @historyFinalyPosmm = ahgen::read_column($tmpHistory, $extension1, "finalypos"); my @historyPathcode = ahgen::read_column($tmpHistory, $extension1, "pathcode"); my @historyResultsPlane = ((1) x $numRows); if ($checkForResultsPlane) { @historyResultsPlane = ahgen::read_column($tmpHistory, $extension1, "resultsplane"); } else { # else, everything in file did hit the results plane. # initialize @historyResultsPlane with all true @historyResultsPlane = (1) x $numRows; } # this will be needed for calculating the source center later my $plateScaleArcsec = $focalLengthmm / $radianToArcsec; ahlog::ah_debug "plateScaleArcsec = $plateScaleArcsec \n"; # store the initial unique energy for easy reference my $initEnergy = $unqEnergies[0]; ahlog::ah_debug "initEnergy = $initEnergy \n"; # array of each energy, with spectral weighting my @weights = (); # weigh each energy: total += W_i * E_i for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies ; ++$iEnergy) { $weights[$iEnergy] = ($unqEnergies[$iEnergy] / $initEnergy )^(-$alpha); } # +++ 20150506 KLR are these values correct? # +++ this may not be necessary if I just calculcate it on the fly later. # decide which takes longer. there may only be 5 or 100 energies, but 10e9 history file rows. # the array of radial distances. [arcsec] my @radialValues = (); for (my $ir = 0 ; $ir < $numRadVals ; ++$ir) { $radialValues[$ir] = $ir * $deltaRadVals; } if (ahlog::getdebug()) { print Data::Dumper->Dump( [ \@weights ], [ qw(*weights) ] ); #print Data::Dumper->Dump( [ \@radialValues ], [ qw(*radialValues) ] ); } # create two 2D arrays (for x and y) to hold center coords for each # offaxis and roll angle # size: (numUnqAzim, numUnqOffaxis) my @xCenters = (); my @yCenters = (); # loop through each roll angle for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth ; ++$iRoll) { my $currRollRad = deg2rad($unqAzimDeg[$iRoll]); # for each roll angle, loop through each off axis angle for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis ; ++$iTheta) { my $currOffaxisRad = $unqOffaxisArcmin[$iTheta] * $arcminToRad; # calculate the EEF center coordinate for this source my $flTanTheta = $focalLengthmm * tan($currOffaxisRad); my $xCenter = $flTanTheta * cos($currRollRad); my $yCenter = $flTanTheta * sin($currRollRad); # store those centers into the arrays $xCenters[$iRoll][$iTheta] = $xCenter; $yCenters[$iRoll][$iTheta] = $yCenter; } # end-loop through offaxis angles } # end-loop through roll angles if (ahlog::getdebug()) { print Data::Dumper->Dump( [ \@xCenters ], [ qw(*xCenters) ] ); print Data::Dumper->Dump( [ \@yCenters ], [ qw(*yCenters) ] ); # what should these be? They don't match xrtraytrace_sxt_POINT_nph1e3_4en_2off_3roll_eef.fits xcen and ycen } ######################### # Main Code Block (doWork) ######################### # count how many pathcodes we see with 8 interactions. The history file only # records up to the first 8 interactions. If we see 8 later when we're # examining the pathcode, we don't know if there were actually 8 or more # than 8. Alert the user to this. my $numEightInteractions = 0; # Normalization radius in mm. This may be requested as an input param later. # This is written to the keyword NORMRAD in the output primary HDU my $normRadmm = 1e30; # which radial bin the current photon belongs to, as loop through history file my $currRadialBin = 0; # loop through history file rows for (my $iRow = 0 ; $iRow < $numRows ; ++$iRow) { #ahlog::ah_debug "** iRow: $iRow"; # call the function to see if the current photon hit the results plane and # was a double reflection. If it wasn't, move on to the next photon. my ($isDblRefl, $currHasEightInteractions) = checkPathCode($historyPathcode[$iRow], $historyResultsPlane[$iRow]); $numEightInteractions += $currHasEightInteractions; if (!$isDblRefl) { #ahlog::ah_debug "not a double reflection \n"; next; } # if we make it here, we know this photon path was a double reflection that # hit the results plane # see which theta, roll, and energy we're at my $currThetaArcmin = $historyInitialThetaArcmin[$iRow]; my $currRollDeg = $historyInitialAzimDirDeg[$iRow]; my $currEnergy = $historyEnergy[$iRow]; # see which theta, roll, and energy bin we're in # +++ 20150629 KLR check for accuracy/tolerance here. my $currThetaBin = first { $unqOffaxisArcmin[$_] == $currThetaArcmin } 0..$#unqOffaxisArcmin; my $currRollBin = first { $unqAzimDeg[$_] == $currRollDeg } 0..$#unqAzimDeg; my $currEnergyBin = first { $unqEnergies[$_] == $currEnergy } 0..$#unqEnergies; # if the current theta or roll or energy weren't found, abort this run. # That could be because a history file was merged from multiple files, with # an inaccurate 2nd extension. if ( !defined($currThetaBin) ) { ahgen::ah_err "The offaxis value ($currThetaArcmin arcmin) in the first extension of the history file wasn't present in the second extension. This could be a result of an error in merging history files or altering a history file."; ahapp::end_processing(1); } if ( !defined($currRollBin) ) { ahgen::ah_err "The azimuthal value ($currRollDeg deg) in the first extension of the history file wasn't present in the second extension. This could be a result of an error in merging history files or altering a history file."; ahapp::end_processing(1); } if ( !defined($currEnergyBin) ) { ahgen::ah_err "The energy value ($currEnergy) in the first extension of the history file wasn't present in the second extension. This could be a result of an error in merging history files or altering a history file."; ahapp::end_processing(1); } #ahlog::ah_debug "indexof(%d) in unqOffaxisArcmin = %4d \n", $currThetaArcmin, $currThetaBin; #ahlog::ah_debug "indexof(%d) in unqAzimDeg = %4d \n", $currRollDeg, $currRollBin; #ahlog::ah_debug "indexof(%d) in unqEnergies = %4d \n", $currEnergy, $currEnergyBin; # use those bins to to grab to the center coord my $currCentralX = $xCenters[$currRollBin][$currThetaBin]; my $currCentralY = $yCenters[$currRollBin][$currThetaBin]; # calculate radius of this photon r = sqrt(x^2 + y^2) my $currPhotonRadmm = sqrt( ($historyFinalxPosmm[$iRow] + $currCentralX) * ($historyFinalxPosmm[$iRow] + $currCentralX) + ($historyFinalyPosmm[$iRow] + $currCentralY) * ($historyFinalyPosmm[$iRow] + $currCentralY) ); # calculate which radial bin this photon belongs in my $radArcsec = $currPhotonRadmm / $plateScaleArcsec; # my $eefbinindex = int( $radArcsec / $deltaRadVals); if ($iRow+1 == 4997) { ahlog::ah_debug "row 4997"; ahlog::ah_debug "historyFinalxPosmm[$iRow] = $historyFinalxPosmm[$iRow]"; ahlog::ah_debug "historyFinalyPosmm[$iRow] = $historyFinalyPosmm[$iRow]"; ahlog::ah_debug "currCentralX = $currCentralX"; ahlog::ah_debug "currCentralY = $currCentralY"; ahlog::ah_debug "currPhotonRadmm = $currPhotonRadmm"; ahlog::ah_debug "plateScaleArcsec = $plateScaleArcsec"; ahlog::ah_debug "radArcsec = $radArcsec"; } #ahlog::ah_debug "eefbinindex = $eefbinindex"; # calculate the current weighted energy # ( (E_i / E_0) ^ -alpha ) * E_i my $currWeightedEnergy = ( ($currEnergy / $initEnergy)^(-$alpha) ) * $currEnergy; # +++ is it faster to calculate this each time, or to use the weights array/hash and indexof()? # flag to see if this photon added to the EEF my $photonShouldCount = 0; # now, starting at the eefbinindex for this radial distance, add this # weighted energy to all eefbins up to the end # for (my $iRad = $eefbinindex ; $iRad < $numRadVals ; ++$iRad) { for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { # only increment if the radius is inside requested radial bins if ($radArcsec <= $radialValues[$iRad]) { #ahlog::ah_debug "add currWeightedEnergy to eefvalues: radialValues[$iRad] = %4d \n", $radialValues[$iRad]; $eefValues[$iRad][$currRollBin][$currThetaBin][$currEnergyBin] += $currWeightedEnergy; $photonShouldCount = 1; # flag that this photon (this row in the history file) should count towards totalpsfphotons } } # if we incremented the EEF because of this photon, we should count this photon towards totalpsfphotons if ($photonShouldCount) { ++$totalpsfphotons[$currRollBin][$currThetaBin][$currEnergyBin]; # +++ for debugging my $currRow = $iRow+1; #ahlog::ah_debug "photon at row $currRow counts towards totalpsfphotons"; ++$numAcceptedPhotons[$currRollBin][$currThetaBin][$currEnergyBin]; my $photonNum = $numAcceptedPhotons[$currRollBin][$currThetaBin][$currEnergyBin]; $acceptedPhotonRadialValuesArcsec[$currRollBin][$currThetaBin][$currEnergyBin][$photonNum] = $radArcsec; $acceptedPhotonPathcode[$currRollBin][$currThetaBin][$currEnergyBin][$photonNum] = $historyPathcode[$iRow]; $acceptedPhotonRowNum[$currRollBin][$currThetaBin][$currEnergyBin][$photonNum] = $currRow; } # add to the total weighted energy for this roll/theta/energy, which is # summed for every photon, until we get to the normalization radius if ($currPhotonRadmm <= $normRadmm) { $totalWeightedEnergy[$currRollBin][$currThetaBin][$currEnergyBin] += $currWeightedEnergy; } } # end for-loop through rows in history file # +++ create diagnostic files if (ahlog::getdebug()) { # +++ 20150806 KLR add radial values of accepted photons to a diagnostic file my $radialDiagFile = "eefDiagnosticRadial.dat"; my $radialDiagFileSorted = "eefDiagnosticRadialSorted.dat"; my $extNum = 0; open my $fileHandleRadialDiagData, ">", $radialDiagFile or die "Can't open '$radialDiagFile'\n"; open my $fileHandleRadialDiagDatasorted, ">", $radialDiagFileSorted or die "Can't open '$radialDiagFileSorted'\n"; for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { ++$extNum; print $fileHandleRadialDiagData "\n\n extension # $extNum \n"; print $fileHandleRadialDiagData "Azimuth $iRoll = $unqAzimDeg[$iRoll]" . "\n"; print $fileHandleRadialDiagData "Offaxis $iTheta = $unqOffaxisArcmin[$iTheta]" . "\n"; print $fileHandleRadialDiagData "Energy $iEnergy = $unqEnergies[$iEnergy]" . "\n\n"; print $fileHandleRadialDiagData "photon number \t radialValues[arcsec] \t pathcode \t row number" . "\n"; print $fileHandleRadialDiagDatasorted "\n\n extension # $extNum \n"; print $fileHandleRadialDiagDatasorted "Azimuth $iRoll = $unqAzimDeg[$iRoll]" . "\n"; print $fileHandleRadialDiagDatasorted "Offaxis $iTheta = $unqOffaxisArcmin[$iTheta]" . "\n"; print $fileHandleRadialDiagDatasorted "Energy $iEnergy = $unqEnergies[$iEnergy]" . "\n\n"; print $fileHandleRadialDiagDatasorted "photon number \t radialValues[arcsec] " . "\n"; # sort the array my @unsortedArrayArcsec; for (my $iPhoton = 1 ; $iPhoton <= $numAcceptedPhotons[$iRoll][$iTheta][$iEnergy] ; ++$iPhoton) { $unsortedArrayArcsec[$iPhoton] = $acceptedPhotonRadialValuesArcsec[$iRoll][$iTheta][$iEnergy][$iPhoton]; } my @sortedArrayArcsec = sort @unsortedArrayArcsec; # this is 1-based because of when I assigned it in the row loop for (my $iPhoton = 1 ; $iPhoton <= $numAcceptedPhotons[$iRoll][$iTheta][$iEnergy] ; ++$iPhoton) { #print $fileHandleRadialDiagData $iPhoton . " " . $acceptedPhotonRadialValuesmm[$iRoll][$iTheta][$iEnergy][$iPhoton] . "\n"; print $fileHandleRadialDiagData $iPhoton . "\t" . $acceptedPhotonRadialValuesArcsec[$iRoll][$iTheta][$iEnergy][$iPhoton] . "\t" . $acceptedPhotonPathcode[$iRoll][$iTheta][$iEnergy][$iPhoton] . "\t" . $acceptedPhotonRowNum[$iRoll][$iTheta][$iEnergy][$iPhoton] . "\n"; print $fileHandleRadialDiagDatasorted $iPhoton . "\t" . $sortedArrayArcsec[$iPhoton] . "\n"; } } } } # +++ 20150629 KLR throw the eefvalues into an output file (like IDL can put output into a file) so that # we can start plotting/manipulating/examining # just for diagnostic: just grab theta[0], roll[0], and every energy/rollbin my $diagnosticfile = "eefDiagnostic.dat"; open my $fileHandleDiagData, ">", $diagnosticfile or die "Can't open '$diagnosticfile'\n"; print $fileHandleDiagData "radialValues eefValues" . "\n"; for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { print $fileHandleDiagData "\n" . "Azimuth $iRoll = $unqAzimDeg[$iRoll]" . "\n"; for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { print $fileHandleDiagData "Offaxis $iTheta = $unqOffaxisArcmin[$iTheta]" . "\n"; for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { print $fileHandleDiagData "Energy $iEnergy = $unqEnergies[$iEnergy]" . "\n"; for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { print $fileHandleDiagData $radialValues[$iRad] . " " . $eefValues[$iRad][$iRoll][$iTheta][$iEnergy] . "\n"; } } # end-for iEnergy } # end-for iTheta } # end-for iRoll close $fileHandleDiagData; } # end-if debug, to print diagnostic ascii data files if (ahlog::getdebug()) { print Data::Dumper->Dump( [ \@totalWeightedEnergy ], [ qw(*totalWeightedEnergy) ] ); } # make sure that no entries in totalWeightedEnergy are 0, since we use that to # normalize later. If any are 0, the user likely used a history file too small for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { if ($totalWeightedEnergy[$iRoll][$iTheta][$iEnergy] == 0) { ahgen::ah_err "Some of the weighted energy values were zero. Perhaps you used a history file too small?" ; ahapp::end_processing(1); } } } } # alert the user if there were any pathcodes with 8 interactions. Since the # history file only records 8 interactions, this photon may have had # interactions we're missing. It's unlikely that a valid double reflection had # more than 8 interactions, so we are assuming that if there was a double # reflection with 8 interactions, that there wasn't another interaction that # would invalidate that photon. if ($numEightInteractions > 0) { ahlog::ah_info "LOW", "There were $numEightInteractions photons with at least $maxPathCodes interactions"; } #for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { # for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { # for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { # ahlog::ah_debug ("totalWeightedEnergy[$iRoll][$iTheta][$iEnergy] = $totalWeightedEnergy[$iRoll][$iTheta][$iEnergy]"); # } # } #} # go through all radial bins and normalize by the totalWeightedEnergy for that # roll/theta/energy. Remember that eefValues is a 4D array, # by (radial bin, roll, theta, energy) for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { $normEEFValues[$iRad][$iRoll][$iTheta][$iEnergy] = $eefValues[$iRad][$iRoll][$iTheta][$iEnergy] / $totalWeightedEnergy[$iRoll][$iTheta][$iEnergy]; } } } } # #for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { # for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { # for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { # for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies; ++$iEnergy) { # #ahlog::ah_debug "normEEFValues[$iRad][$iRoll][$iTheta][$iEnergy]=%.6f \n", $normEEFValues[$iRad][$iRoll][$iTheta][$iEnergy]; # } # } # } #} # initialize how many energies, etc we're going to use. These are # initialially set to the full number of unique values, but are reset inside # createMergedEEF() to 1 if any of these values are to be merged my $numEnergies = $numUnqEnergies; my $numOffaxis = $numUnqOffaxis; my $numAzimuth = $numUnqAzimuth; # now merge the requested dimensions (energy, offaxis, and/or azimuth) #@finalEEF = @{createMergedEEF(\@normEEFValues, $numUnqEnergies, $numUnqOffaxis, $numUnqAzimuth)}; createMergedEEF(\@normEEFValues, \@totalpsfphotons, $numUnqEnergies, $numUnqOffaxis, $numUnqAzimuth, \@finalEEF, \@finalPSFPhotons); writeOutputFile(); ######################### # Finishing ######################### # Add CHECKSUM and DATASUM, and check that file we just created is valid unless (ahgen::update_checksum_and_verify($outfile)) { ahgen::ah_err "FITS file $outfile failed FITS verification test." ; ahapp::end_processing(1); } ahlog::ah_info "HIGH", "Successfully created FITS file: $outfile" ; # We're done ahapp::end_processing(0); ############################################################################# sub average { # make sure there are elements in the array if (@_ == 0) { ahgen::ah_err "0 elements in array" ; ahapp::end_processing(1); } return sum(@_)/@_; } # end sub average ############################################################################# # \brief Analyzes the pathcode for a single photon to determine the properties # \param[in] pathcode The string containing the entire pathcode. It is an # array of three integers per interaction that record various # attributes of the interaction. The three integers are # referred to as A, BC, and D, and are defined as follows: # pathcode[0] = A = interaction type # 1 = absorption or end of path on the results plane # 2 = reflection only # 3 = reflection followed by scattering # 4 = transmission # 9 = error # pathcode[1] = BC = impacted object # 01 = results plane # 02 = inner housing # 03 = outer housing # 04 = segment/sector sidewall # 05 = pre-collimator foil # 06 = primary mirror foil # 07 = secondary mirror foil # 08 = support structure # 09 = top external object (above main telescope) # 10 = bottom external object (below main telescope) # pathcode[2] = D = impacted face # 1 = back (mirrors, pre-collimator foils) # i.e. the face oriented towards outer housing # for obstructions & focal plane, it is the top # 2 = front (mirrors, pre-collimator foils) # i.e. the face oriented towards inner housing # for obstructions it is the bottom. # 3 = mirror, pre-collimator foils: top face/edge # 4 = mirror, pre-collimator foils: bottom face/edge # 5 = mirror, pre-collimator foils: sides # 6 = Undetermined (but not back or front) # \param[in] resultsPlane True if this photon hit the results plane # \param[out] isDblRefl True if this photon was a double reflection, meaning it # reflected off the primary mirrors exactly once and the # secondary mirrors exactly once, then hit the results plane. # These reflections may include scattering. There may also be # transmission events in the pathcode, and events involving the # precollimator or external objects: these do not effect the # double reflection status. # \param[out] numEightInteractions The history file only writes up to 8 # interactions in the pathcode. If there were 8 portions of # the current pathcode, we don't know if there were exactly 8 # or more than 8. We will still count this photon (as long as # it meets other criteria), but we will increment a counter of # these photons and alert the user to it after looping through # all the photons. # # checkPathCode analyzes the pathcode of a single photon to determine if this # was a double reflection, and if this pathcode records 8 interactions. # sub checkPathCode { #ahlog::ah_debug "---- start checkPathCode ---- \n"; # ------------------------------------- # define and initialize all variables # ------------------------------------- # input parameters my $pathcode = shift; my $resultsPlane = shift; # set some constants my $indivPathCodeSize = 4; # size of an individual path code # strings that represent reflection pathcodes to search for later. # these strings are hard coded into xrtraytrace my $priRefl = "2062"; my $priReflScat = "3062"; my $secRefl = "2072"; my $secReflScat = "3072"; # count how many reflections from primary and secondary mirrors my $numPrimRefl = 0; my $numSecRefl = 0; # output parameters my $isDblRefl = 0; my $hasEightInteractions = 0; # ------------------------------------- # first, check if photon hit the results plane # if we need to check the resultsplane column, and it was false (meaning # the photon in this row did not hit the results plane), we can return from # this function and move to next photon if (!$resultsPlane) { return ($isDblRefl, $hasEightInteractions); } # next, check if this was a double reflection # Check pathcode column. Only process double reflections, which could # include transmission, scattering, and external objects. We could accept # all photons, which would be closer to real data, as real data doesn't know # which was a double reflection, but we're deciding to only process double # reflections. Don't count if more than one primary + one secondary refl. # see if there were 8 interactions in this pathcode. If so, we will alert # user later my $numPathCodeChars = length($pathcode); my $numInteractions = $numPathCodeChars / $indivPathCodeSize; if ($numInteractions = $maxPathCodes) { $hasEightInteractions++; } # break up the pathcode into individual pieces of 4 my @currPathCodes = unpack("(A4)*", $pathcode); # loop through the pathcodes, looking for a double reflection foreach my $code (@currPathCodes) { # we only care about reflections from primary and secondary. We can ignore # all other interactions. (transmission, external objects, etc) # search for reflections from primaries (can have scattering) if ( ($code == $priRefl) || ($code == $priReflScat) ) { $numPrimRefl++; } # seach for reflections from secondaries (can have scattering) if ( ($code == $secRefl) || ($code == $secReflScat) ) { $numSecRefl++; } } # end-loop through pathcode # Make sure there was exactly one primary reflection and one secondary # reflection. If not, move on to the next photon. if ( ($numPrimRefl == 1) && ($numSecRefl == 1) ) { $isDblRefl = 1; } # ahlog::ah_debug "---- end checkPathCode ---- \n"; return ($isDblRefl, $hasEightInteractions); } # end-sub checkPathCode ############################################################################# # \brief Creates merged array, if requested by user, from an array of EEF # values for each energy/offaxis/azimuth # \param[in] mgEnergy # \param[in] mgOffaxis # \param[in] mgAzimuth # \param[in] numRadVals # \param[in] normEEFValues # \param[in] totalpsfphotons # \param[in] numUnqEnergies # \param[in] numUnqOffaxis # \param[in] numUnqAzim # \param[out] finalEEF # \param[out] finalPSFPhotons # # long desc # sub createMergedEEF { ahlog::ah_debug "---- start createMergedEEF ---- \n"; # ------------------------------------- # define and initialize all variables # ------------------------------------- # input parameters my( $normEEFValuesRef, $totalpsfphotonsRef, $numUnqEnergies, $numUnqOffaxis, $numUnqAzimuth, $finalEEFRef, $finalPSFPhotonsRef ) = @_; my @normEEFValuesArr = @{$normEEFValuesRef}; my @totalpsfphotons = @{$totalpsfphotonsRef}; my @finalEEFRef = @{$finalEEFRef}; my @finalPSFPhotons = @{$finalPSFPhotonsRef}; ahlog::ah_debug("numUnqEnergies=$numUnqEnergies numUnqOffaxis=$numUnqOffaxis numUnqAzimuth=$numUnqAzimuth"); # mgEnergy, mgOffaxis, mgAzimuth, numRadVals are input params. # they were declared earlier, so didn't need to be passed in # numEnergies, numOffaxis, numAzimuth were declared earlier # these values will decide how many values we are to use, based on if # user requested to merge these values or not # These will be 4D arrays, holding the final EEF values # normEEFValues is the first 'input' - it's the (normalized) EEF array # created by treating each energy/offaxis/roll separately # energyEEF will be the 'output' from merging energy values (if not merging # energy values, then it will be an exact copy of normEEFValues). # offaxisEEF will be the 'output' from merging offaxis values, using # energyEEF as 'input' (if not merging offaxis values, then offaxisEEF will # be an exact copy of energyEEF) # finalEEF will be the 'output' from merging azimuthal values, using offaxis # as 'input' (if not merging azimuthal values, then finalEEF will be an # exact copy of offaxisEEF) # if merging on any of the values, there will be only be one entry for that # applicable dimension. For example, if merging on energy, the # 4th dimension will only be size 1. If not merging on energy, the # 4th dimension will be the original size (numUnqEnergies). This value is # stored in numEnergies. # dimen: [$iRad][$iRoll][$iTheta][$iEnergy] my @energyEEF = (); my @offaxisEEF = (); # ------------------------------------- # for each eefbin, calculate the final EEF for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { # ------- start merging energy for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { if ($mgEnergy) { # we're merging energies, so make just one EEF extension for all # the energies # (there may or may not be extensions for each theta and roll) # the last dimension (energy) will only be size 1 $numEnergies = 1; # initialize the eef for this roll/theta to 0, so we can add to it $energyEEF[$iRad][$iRoll][$iTheta][0] = 0; # sum all the energies that contributed for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies ; ++$iEnergy) { $energyEEF[$iRad][$iRoll][$iTheta][0] += $normEEFValuesArr[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies # now divide by total number energies, to get average $energyEEF[$iRad][$iRoll][$iTheta][0] /= $numUnqEnergies; } else { # we're not merging energies. Make an EEF extension for each one # leave numEnergies at the default numUnqEnergies # copy all the energies that contributed for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies ; ++$iEnergy) { $energyEEF[$iRad][$iRoll][$iTheta][$iEnergy] += $normEEFValuesArr[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-if merging energy } # end-loop through theta } # end-loop through roll # ------- end merging energy # energyEEF is now either an exact copy of normEEFValues (if not merging # energy) or all energy EEFs are combined into first entry of last # dimension (if we are merging energy) # ------- start merging offaxis # now see if we need to merge offaxis angles. The 'output' from the energy # part, energyEEF, will be the 'input' for the offaxis part. # roll is the outermost loop, we can have that outside the merging details for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { if ($mgOffaxis) { # we're merging offaxis values, so make just one EEF extension for all # the offaxis angles # (there may or may not be extensions for each energy and roll) $numOffaxis = 1; for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { # remember that numEnergies is either 1 or numUnqEnergies, # depending on if we merged energies into one field or not $offaxisEEF[$iRad][$iRoll][0][$iEnergy] += $energyEEF[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta # now divide by total number theta, to get average for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $offaxisEEF[$iRad][$iRoll][0][$iEnergy] /= $numUnqOffaxis; } # end-loop through energies } else { # we're not merging offaxis. Make an EEF extension for each one # leave numOffaxis as the default numUnqOffaxis # offaxisEEF will basically just be a copy of energyEEF for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { # remember that numEnergies is either 1 or numUnqEnergies, # depending on if we merged energies into one field or not $offaxisEEF[$iRad][$iRoll][$iTheta][$iEnergy] = $energyEEF[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-if merging offaxis } # end-loop through roll # ------- end merging offaxis # offaxisEEF is now either an exact copy of energyEEF (if not merging # offaxis) or all offaxis EEFs are combined into first entry of third # (offaxis) dimension. We will use this to create the finalEEF array, # with azimuth # ------- start merging azimuth if ($mgAzimuth) { $numAzimuth = 1; for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { # remember that numEnergies/numOffaxis are either 1 or # numUnqEnergies/numUnqOffaxis, depending on if we merged # energies/offaxis into one field or not for (my $iTheta = 0 ; $iTheta < $numOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $finalEEF[$iRad][0][$iTheta][$iEnergy] += $offaxisEEF[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-loop through roll # now divide by total number azimuth, to get average for (my $iTheta = 0 ; $iTheta < $numOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $finalEEF[$iRad][0][$iTheta][$iEnergy] /= $numUnqAzimuth; # +++ finalEEF! } # end-loop through energies } } else { # we're not merging azimuth. Make an EEF extension for each one # leave numAzim as the default numUnqAzim for (my $iRoll = 0 ; $iRoll < $numAzimuth; ++$iRoll) { # remember that numEnergies/numOffaxis are either 1 or # numUnqEnergies/numUnqOffaxis, depending on if we merged # energies/offaxis into one field or not for (my $iTheta = 0 ; $iTheta < $numOffaxis ; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $finalEEF[$iRad][$iRoll][$iTheta][$iEnergy] = $offaxisEEF[$iRad][$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-loop through roll } # end-if merging azimuth # ------- end merging azimuth } # end-loop through eefbins # done creating finalEEF, after deciding what needs to be merged # (energy, offaxis, azimuth values) # *********************************************** # now we need to possibly merge totalpsfphotons, # for each energy, offaxis, and roll # *********************************************** # +++ finish this # Similary to the arrays holding the final EEF values, these will hold the # intermittent PSFPhoton arrays # totalpsfphotons is the first 'input', created by treating each # energy/offaxis/roll separately # energyPhotons will be 'output' from merging energy values (if not merging # energy values, it will be an exact copy of totalpsfphotons). # offaxisPhotons will be the 'output' from merging offaxis values, using # energyPhotons as 'input' (if not merging offaxis values, offaxisPhotons will # be an exact copy of energyPhotons) # finalPSFPhotons will be the 'output' from merging azimuthal values, using # offaxis as 'input' (if not merging azimuthal values, finalPSFPhotons will be # exact copy of offaxisPhotons) # if merging on any of the values, there will be only be one entry for that # applicable dimension. For example, if merging on energy, the # 3rd dimension will only be size 1. If not merging on energy, the # 3rd dimension will be the original size (numUnqEnergies). This value is # already stored in numEnergies. # dimen: [$iRoll][$iTheta][$iEnergy] my @energyPhotons = (); my @offaxisPhotons = (); # ------- start merging energy for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { if ($mgEnergy) { # initialize the eef for this roll/theta to 0, so we can add to it # +++ I should probably initialize all of these (for eef too) before these if-blocks $energyPhotons[$iRoll][$iTheta][0] = 0; # sum all the energy photons that contributed for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies ; ++$iEnergy) { $energyPhotons[$iRoll][$iTheta][0] += $totalpsfphotons[$iRoll][$iTheta][$iEnergy]; } } else { # we're not merging energies # copy all the energy photons that contributed for (my $iEnergy = 0 ; $iEnergy < $numUnqEnergies ; ++$iEnergy) { $energyPhotons[$iRoll][$iTheta][$iEnergy] += $totalpsfphotons[$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-if merging energy } # end-loop through theta } # end-loop through roll # ------- end merging energy # energyPhotons is now either an exact copy of totalpsfphotons (if not # merging energy) or all energy photons are combined into first entry of last # dimension (if we are merging energy) # ------- start merging offaxis # now see if we need to merge offaxis angles. The 'output' from the energy # part, energyPhotons, will be the 'input' for the offaxis part. # roll is the outermost loop, we can have that outside the merging details for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { if ($mgOffaxis) { for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { # remember that numEnergies is either 1 or numUnqEnergies, # depending on if we merged energies into one field or not $offaxisPhotons[$iRoll][0][$iEnergy] += $energyPhotons[$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } else { # we're not merging offaxis # offaxisPhotons will basically just be a copy of energyPhotons for (my $iTheta = 0 ; $iTheta < $numUnqOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { # remember that numEnergies is either 1 or numUnqEnergies, # depending on if we merged energies into one field or not $offaxisPhotons[$iRoll][$iTheta][$iEnergy] = $energyPhotons[$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-if merging offaxis } # end-loop through roll # ------- end merging offaxis # offaxisPhotons is now either an exact copy of energyPhotons (if not merging # offaxis) or all offaxis photons are combined into first entry of third # (offaxis) dimension. We will use this to create the finalPSFPhotons array, # with azimuth # ------- start merging azimuth if ($mgAzimuth) { $numAzimuth = 1; for (my $iRoll = 0 ; $iRoll < $numUnqAzimuth; ++$iRoll) { # remember that numEnergies/numOffaxis are either 1 or # numUnqEnergies/numUnqOffaxis, depending on if we merged # energies/offaxis into one field or not for (my $iTheta = 0 ; $iTheta < $numOffaxis; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $finalPSFPhotons[0][$iTheta][$iEnergy] += $offaxisPhotons[$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-loop through roll } else { # we're not merging azimuth for (my $iRoll = 0 ; $iRoll < $numAzimuth; ++$iRoll) { # remember that numEnergies/numOffaxis are either 1 or # numUnqEnergies/numUnqOffaxis, depending on if we merged # energies/offaxis into one field or not for (my $iTheta = 0 ; $iTheta < $numOffaxis ; ++$iTheta) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { $finalPSFPhotons[$iRoll][$iTheta][$iEnergy] = $offaxisPhotons[$iRoll][$iTheta][$iEnergy]; } # end-loop through energies } # end-loop through theta } # end-loop through roll } # end-if merging azimuth # ------- end merging azimuth # done creating finalPSFPhotons, after deciding what needs to be merged # (energy, offaxis, azimuth values) ahlog::ah_debug "---- end createMergedEEF ---- \n"; # includes a return reference to the output array } # end-sub createMergedEEF() ############################################################################# # \brief Write the output EEF FITS file from the array of EEF values # \param[in] mgEnergy if user requested to merge energy values # \param[in] mgOffaxis if user requested to merge offaxis values # \param[in] mgAzimuth if user requested to merge azimuthal values # \param[in] numRadVals numnber of radial bins # \param[in] outfile name of output file # \param[in] finalEEF 4dim array of EEF values # \param[in] radialValues array of radial values # \param[in] xCenters 2D array of center x values for each azimuth and offaxis # \param[in] yCenters 2D array of center y values for each azimuth and offaxis # \param[in] numEnergies # \param[in] numOffaxis # \param[in] numAzimuth # \param[in] unqEnergies # \param[in] unqOffaxisArcmin # \param[in] unqAzimDeg # # long desc # sub writeOutputFile { ahlog::ah_debug "---- start writeOutputFile ---- \n"; # ------------------------------------- # define and initialize all variables # ------------------------------------- # mgEnergy, mgOffaxis, mgAzimuth, numRadVals are input params. # they were declared with 'my' earlier, so didn't need to be passed in # this will be updated inside each loop iteration, to print current # extension number as we create it my $extNum = 0; # a slice of the eef will be written to an extension. # size: numRadVals my @eefSlice; # in Perl, using ftcreate, can only create one file/extension at a time. Add # those names to this array. Then we'll merge them all into one file using # ftmerge after the loops my @outfiles; # for CBD10001, CBD20001, CBD30001, and OFFAXIS, AZIMUTH, Energy keywords my $cbdOffValues; my $cbdAzimValues; my $cbdEnergyValues; my $offValues; my $azimValues; my $energyValues; # write either current xCenter and yCenter for theta and roll, # or if either of those were merged, write a range my $minXCenter; my $maxXCenter; my $minYCenter; my $maxYCenter; my $xCenterToWrite; my $yCenterToWrite; # ------------------------------------- # if either offaxis or azimuth were merged, then the xCenter and yCenter # keywords don't entirely make sense. In this case, write a range: # XCENTER = min(xCenters) - max(xCenters) # YCENTER = min(yCenters) - max(yCenters) # xCenters and yCenters are 2D arrays, so do this in a 2D loop # (min and max are for 1D arrays) if ($mgOffaxis || $mgAzimuth) { $minXCenter = $xCenters[0][0]; $maxXCenter = $xCenters[0][0]; $minYCenter = $yCenters[0][0]; $maxYCenter = $yCenters[0][0]; for (my $iAzim = 1 ; $iAzim < $numAzimuth ; ++$iAzim) { for (my $iOff = 1 ; $iOff < $numOffaxis; ++$iOff) { if ($xCenters[$iAzim][$iOff] < $minXCenter) { $minXCenter = $xCenters[$iAzim][$iOff] } if ($xCenters[$iAzim][$iOff] > $maxXCenter) { $maxXCenter = $xCenters[$iAzim][$iOff] } if ($yCenters[$iAzim][$iOff] < $minYCenter) { $minYCenter = $yCenters[$iAzim][$iOff] } if ($yCenters[$iAzim][$iOff] > $maxYCenter) { $maxYCenter = $yCenters[$iAzim][$iOff] } } } } # create an extension for each requested roll/theta/energy. Any combination # of these may be merged into one extension for that variable(s). # note the end limit for the loops was initially set to the full number of # unique values (ie: $numAzimuth was initialized to $numUnqAzimuth), but was # reset to 1 inside createMergedEEF() if any were merged for (my $iAzim = 0 ; $iAzim < $numAzimuth ; ++$iAzim) { for (my $iOff = 0 ; $iOff < $numOffaxis; ++$iOff) { for (my $iEnergy = 0 ; $iEnergy < $numEnergies ; ++$iEnergy) { # we're at a new extension ++$extNum; ahlog::ah_debug "extNum=$extNum iAzim=$iAzim iOff=$iOff iEnergy=$iEnergy \n"; # current name of this EEF file (one for each extension, until combine) my $currOutfile = $outfile . ".extension" . $extNum; push @outfiles, $currOutfile; ahapp::add_temp_file($currOutfile); # name of extension in output EEF file my $extname = "EEF" . $extNum; # grab the necessary slice of the EEF for this extension, overwriting # slice from previous loop iteration for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { $eefSlice[$iRad] = $finalEEF[$iRad][$iAzim][$iOff][$iEnergy]; } # CBD10001, CBD20001, CBD30001, and OFFAXIS, AZIMUTH, Energy keywords # if merging: # CBD keywords are a range from first unique value to last # OFFAXIS, etc are the average of the unique values # if not merging: both keywords are the value at this iteration # +++ KLR add this to TRF description about output file if ($mgOffaxis) { $cbdOffValues = $unqOffaxisArcmin[0] . "-" . $unqOffaxisArcmin[$numUnqOffaxis-1]; $offValues = average(@unqOffaxisArcmin); } else { $cbdOffValues = $unqOffaxisArcmin[$iOff]; $offValues = $cbdOffValues; } if ($mgAzimuth) { $cbdAzimValues = $unqAzimDeg[0] . "-" . $unqAzimDeg[$numUnqAzimuth-1]; $azimValues = average(@unqAzimDeg); } else { $cbdAzimValues = $unqAzimDeg[$iAzim]; $azimValues = $cbdAzimValues; } if ($mgEnergy) { $cbdEnergyValues = $unqEnergies[0] . "-" . $unqEnergies[$numUnqEnergies-1]; $energyValues = average(@unqEnergies); } else { $cbdEnergyValues = $unqEnergies[$iEnergy]; $energyValues = $cbdEnergyValues; } my $cbdOffaxs = "THETA(" . $cbdOffValues . ")[arcmin]"; my $cbdAzim = "PHI(" . $cbdAzimValues . ")[deg]"; my $cbdEnergy = "ENERG(" . $cbdEnergyValues . ")[keV]"; # +++ KLR put a comment in TRF, about if file was merged what this value is my $TOTCTS = $totalpsfphotons[$iAzim][$iOff][$iEnergy]; # create text files to hold the column definitions, column data, and keywords # ftcreate needs these to create the output file my $columndeffile = "tmp_columns.txt"; my $datafile = "tmp_data.txt"; my $headfile = "tmp_head.txt"; # add these to tmparray to cleanup later ahapp::add_temp_file($columndeffile); ahapp::add_temp_file($datafile); ahapp::add_temp_file($headfile); # create the column definition file, overwriting if exists open my $fileHandleColumn, ">", $columndeffile or die "Can't open '$columndeffile'\n"; print $fileHandleColumn "Radius 1D arcsec\n"; print $fileHandleColumn "EEF 1D\n"; close $fileHandleColumn; # create the column data file, overwriting if exists open my $fileHandleData, ">", $datafile or die "Can't open '$datafile'\n"; for (my $iRad = 0 ; $iRad < $numRadVals ; ++$iRad) { print $fileHandleData $radialValues[$iRad] . " " . $eefSlice[$iRad] . "\n"; } close $fileHandleData; # write either this current xCenter and yCenter for this theta and roll, # or if either of those were merged, write a range (calculated min # and max earlier) if ($mgOffaxis || $mgAzimuth) { $xCenterToWrite = "$minXCenter - $maxXCenter"; $yCenterToWrite = "$minYCenter - $maxYCenter"; } else { # not merging. can write the actual value $xCenterToWrite = "$xCenters[$iAzim][$iOff]"; $yCenterToWrite = "$yCenters[$iAzim][$iOff]"; } # create the column definition file, overwriting if exists open my $fileHandleHeader, ">", $headfile or die "Can't open '$headfile'\n"; print $fileHandleHeader "TELESCOP $telescop / Mission name\n"; print $fileHandleHeader "INSTRUME $instrume / Instrument name\n"; print $fileHandleHeader "DETNAM $detnam / Telescope name\n"; print $fileHandleHeader "CVSD0001 $cvsd0001 / UTC date when file should be first used\n"; print $fileHandleHeader "CVST0001 $cvst0001 / UTC time when file should be first used\n"; print $fileHandleHeader "CBD10001 $cbdOffaxs / Offaxis angle\n"; print $fileHandleHeader "CBD20001 $cbdAzim / Azimuthal roll angle\n"; print $fileHandleHeader "CBD30001 $cbdEnergy / Energy values\n"; print $fileHandleHeader "OFFAXIS $offValues / [arcmin] Source off-axis angle\n"; print $fileHandleHeader "AZIMUTH $azimValues / [deg] Source rotational (azimuthal) angle\n"; print $fileHandleHeader "Energy $energyValues / [keV] Energy or energy range of image\n"; print $fileHandleHeader "NOFFAXIS $numUnqOffaxis / Number of off-axis angles\n"; print $fileHandleHeader "NAZIMUTH $numUnqAzimuth / Number of azimuthal angles\n"; print $fileHandleHeader "NORMRAD $normRadmm / [mm] Normalization radius\n"; print $fileHandleHeader "TOTCTS $TOTCTS / Total number of photons in EEF\n"; print $fileHandleHeader "FPMM2AM $fpmm2am / Focal Plane (mm) to arcminutes scale factor\n"; print $fileHandleHeader "XCENTER $xCenterToWrite / Source center X-coordinate\n"; print $fileHandleHeader "YCENTER $yCenterToWrite / Source center Y-coordinate\n"; print $fileHandleHeader "1CTYP2 'Radial distance' / Radial distance from peak\n"; print $fileHandleHeader "1CUNI1 arcsec / Units of radial distance from peak\n"; print $fileHandleHeader "1CRPX1 1 / Reference pixel corresponding to start radius\n"; print $fileHandleHeader "1CRVL1 0.0 / Radial distance at the reference pixel\n"; print $fileHandleHeader "1CDLT1 $deltaRadVals / Radial distance increment per pixel\n"; print $fileHandleHeader "MGOFFAXS $mgOffaxisBool / Offaxis angles were merged or not\n"; print $fileHandleHeader "MGAZIM $mgAzimuthBool / Azimuthal angles were merged or not\n"; print $fileHandleHeader "MGENERGY $mgEnergyBool / Energies were merged or not\n"; print $fileHandleHeader "EVTFILE '$infile' \n"; # +++ KLR update this when/if CALDB print $fileHandleHeader "COMMENT EVTFILE = Input history event file\n"; close $fileHandleHeader; # create the output file $status = ahgen::run_ftool("ftcreate", "cdfile=$columndeffile", "datafile=$datafile", "outfile=$currOutfile", "headfile=$headfile", "extname=$extname", "clobber=yes"); if ($status) { ahlog::ah_err "Error creating file $currOutfile"; ahapp::end_processing(1); } # add comments to the TTYPEn keywords $status = ahgen::run_ftool("fthedit", "infile=$currOutfile", "keyword=TTYPE1", "operation=add", "value=Radius", "comment=Enclosed Radius"); $status = ahgen::run_ftool("fthedit", "infile=$currOutfile", "keyword=TTYPE2", "operation=add", "value=EEF", "comment=Enclosed Energy Function"); if ($status) { ahlog::ah_err "Error editing TTYPEn keywords in $currOutfile"; ahapp::end_processing(1); } # +++ 20150527 KLR This step seems to take a lot of time # Add parameters as HISTORY keywords to current extension if ($ahapp::history) { #ahlog::ah_debug "before history: write_parameters \n"; ahapp::write_parameters($currOutfile, $extname); #ahlog::ah_debug "after history: write_parameters \n"; } } # end-loop through enegy } # end-loop through offaxis } # end-loop through azimuth # done writing data extensions (as separate files so far) ahlog::ah_debug "out of loops \n"; # grab the first file we made, and make it the regular outfile $status = ahgen::run_ftool("ftcopy", "infile=$outfiles[0]", "outfile=$outfile", "clobber=$clobberBool"); if ($status) { ahlog::ah_err "Error creating file $outfile with first extension"; ahapp::end_processing(1); } # now merge all the rest of the files into the outfile for (my $iFile=1 ; $iFile < $extNum ; ++$iFile) { $status = ahgen::run_ftool("ftappend", "infile=$outfiles[$iFile]", "outfile=$outfile"); if ($status) { ahlog::ah_err "Error adding extension $iFile to $outfile"; ahapp::end_processing(1); } } # go to primary extension to write some additional keywords ahgen::set_keyword($outfile, 0, "NOFFAXIS", $numUnqOffaxis . " / Number of off-axis angles"); ahgen::set_keyword($outfile, 0, "NAZIMUTH", $numUnqAzimuth . " / Number of azimuthal angles"); ahgen::set_keyword($outfile, 0, "NORMRAD", $normRadmm . " / [mm] Normalization radius"); ahlog::ah_debug "---- end writeOutputFile ---- \n"; } # end sub writeOutputFile() ############################################################################# # Revision Log: # $Log: eeftable.pl,v $ # Revision 1.6 2016/03/23 01:59:57 klrutkow # case-insensitive comparison to SRCMDL keyword and resultsPlaneOnly #id:$ & #