casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
listing.py
Go to the documentation of this file.
00001 #
00002 # listing.py
00003 #
00004 # Module for testing the output of listing tasks (eg. liscal and listvis).
00005 #
00006 
00007 import os, sys, re, math, decimal
00008 
00009 #=============================================================================
00010 # METHOD: compare
00011 #
00012 # Compare the listvis output (test) with the listvis STANDARD.  
00013 #
00014 # Parameters:
00015 #   test     = list of strings
00016 #   STANDARD = list of strings.  
00017 #
00018 def compare(test, standard):
00019     testFile = open(test,'r')
00020     testList = testFile.readlines()
00021     standardFile = open(standard,'r')
00022     standardList = standardFile.readlines()
00023     if (testList == standardList):
00024         testFile.close()
00025         standardFile.close()
00026         return True
00027     else:
00028         testFile.close()
00029         standardFile.close()
00030         return False
00031 #=============================================================================
00032 
00033 #=============================================================================
00034 # METHOD: diffFiles
00035 #
00036 # Run diff on two files.  Write output to a file.
00037 #
00038 # Parameters:
00039 #   test     = string; name of runtime output file
00040 #   STANDARD = string; name of standard file
00041 #   prefix   = string; prefix for diff output
00042 #
00043 def diffFiles(testOut, standardOut, prefix=""):
00044     diffOut = prefix + '.diff' # diff output
00045     print "  - Running command line utility 'diff' on output files."
00046     print "  - Writing to " + diffOut
00047     cmdstr = 'diff ' + standardOut + ' ' + testOut + ' > ' + diffOut
00048     print cmdstr
00049     os.system(cmdstr)
00050 #=============================================================================
00051 
00052 #=============================================================================
00053 # METHOD: runTests
00054 #
00055 # Automate the testing of metadata and data.
00056 #
00057 # Parameters:
00058 #   test     = string; name of runtime output file
00059 #   standard = string; name of standard file
00060 #   precision= string; lower limit of visibility precision allowed
00061 #   prefix   = string; prefix for diff output
00062 #
00063 def runTests(test, standard, precision, prefix):
00064 
00065     equal = True
00066     
00067     # Test metadata
00068     print "  1. Checking that metadata agree."
00069     if (diffMetadata(test,standard,prefix=prefix)):
00070         print "  Metadata agree"
00071     else:
00072         print "  Metadata do not agree!"
00073         equal = False
00074     
00075     # Test data (floats)
00076     print "  2. Checking that data agree to within allowed imprecision..."
00077     print "     Allowed visibility imprecision is " + precision
00078     if (diffAmpPhsFloat(test,standard,prefix,precision)):
00079         print "  Data agree"
00080     else:
00081         print "  Data do not agree!"
00082         equal = False
00083 
00084     return equal
00085 #=============================================================================
00086 
00087 
00088 #=============================================================================
00089 # METHOD: diffMetadata
00090 #
00091 # Compare all metadata.  Actually, this compares all non-float, non-space 
00092 # data in the two files passed by filename.
00093 #
00094 # Parameters:
00095 #   test     = string; name of runtime output file
00096 #   STANDARD = string; name of standard file
00097 #   prefix   = string; prefix for output file
00098 #
00099 def diffMetadata(testOut, standardOut, prefix=""):
00100     diffOut = prefix + '.diffMetadata' # output
00101 
00102     print "  - Comparing all non-floats in listing (ignore spaces)"
00103 
00104     #                     Pattern                        Substitution
00105     unwanted = ((re.compile(r"[ |]([+-]?[0-9]*\.[0-9]+)"), 'x'), # floats
00106                 (re.compile(r' '),                          ''), # spaces
00107                 (re.compile(r'-+'),                       '-+')) # dashes
00108     
00109     def filter_out_unwanted(filename, unwanted):
00110         """
00111         Given a filename and a tuple of (pattern, substitution)
00112         pairs, returns the lines of filename with the patterns
00113         replaced by the substitutions.
00114         """
00115         fileobj = open(filename, 'r')
00116         newList = []
00117         for line in fileobj:
00118             filtered = line
00119             for pat, subst in unwanted:
00120                 filtered = pat.sub(subst, filtered)
00121             newList.append(filtered)
00122         fileobj.close()
00123         return newList
00124             
00125     testList = filter_out_unwanted(testOut, unwanted)
00126     stndList = filter_out_unwanted(standardOut, unwanted)
00127 
00128     # If everything after filtering is equal, return True
00129     if testList == stndList:
00130         return True
00131     
00132     # else... do the rest
00133     print "  - Writing differences to ", diffOut
00134     
00135     sys.stdout = open(diffOut,'w') # redirect stdout
00136     printDiff('','',[standardOut,testOut]) # Print some header info
00137 
00138     countDiff = 0 # Count number of different lines
00139 
00140     nlines = min(len(testList),len(stndList))
00141  
00142     #for linenum in range(len(testList)):
00143     for linenum in range(nlines):
00144         if ( testList[linenum] != stndList[linenum] ):
00145             countDiff += 1
00146             print "- (line ", linenum, ") Non-float data differs:" 
00147             printDiff(stndList[linenum],testList[linenum])
00148 
00149     ndiffl = 0
00150     if nlines < len(stndList):
00151       ndiffl = len(stndList)-nlines
00152       print " missing %s lines" % ndiffl 
00153     if nlines == len(stndList):
00154       ndiffl = len(testList)-nlines
00155       print " extra %s lines" % ndiffl 
00156 
00157     countDiff += ndiffl
00158     print ""
00159     print "SUMMARY (diffMetadata): "
00160     print " %10i = Total number of lines with non-float differences" % countDiff 
00161 
00162     # Restore stdout
00163     sys.stdout = sys.__stdout__
00164     
00165     return False
00166 #=============================================================================
00167 
00168 #=============================================================================
00169 # METHOD: diffAmpPhsFloat
00170 #
00171 # Compare floating point values of Amplitude and Phase.
00172 #
00173 
00174 def diffAmpPhsFloat(test, standard, prefix="", precision="1e-6"):
00175 
00176     # Preliminary messages
00177     floatOut = prefix + '.diffAmpPhsFloat'  
00178     print "  - Comparing float content of output."
00179     #print "  - Assuming all floats are Amplitude-Phase pairs!"
00180     print "  - Writing to " + floatOut
00181 
00182     sys.stdout = open(floatOut,'w')
00183 
00184     # Read in files
00185     testFile = open(test,'r')
00186     standardFile = open(standard,'r')
00187     testList = testFile.readlines()
00188     standardList = standardFile.readlines()
00189 
00190     # Reg exp for matching floats
00191     floatPat = re.compile(r"[ |]([+-]?[0-9]*\.[0-9]+)")
00192 
00193     # Verify same number of lines
00194     if ( len(standardList) != len(testList) ):
00195         print "- Standard and test files do not have the same number of lines."
00196         testFile.close()
00197         standardFile.close()
00198         return False
00199 
00200     # Initialize some variables
00201     equal = True     # To start, assume floats are equal
00202     countUnequal = 0 # Count of unequal Amp-Phs pairs
00203     countBigDif  = 0 # Count of Amp-Phs pairs that differ above precision
00204     maxAmpDiff   = 0 # Maximum difference amplitude
00205     precision = decimal.Decimal(precision) # Allowed precision
00206 
00207     # For each line, compare Amp-Phs in both files
00208     
00209     for linenum in range(min(10, len(testList))):
00210         # Generate a list of all floats
00211         tFloatList = floatPat.findall(testList[linenum])
00212         sFloatList = floatPat.findall(standardList[linenum])
00213         #print 'tFloatList=', tFloatList
00214         # Test floats
00215         # Same number of floats in both lines?
00216         if ( len(tFloatList) != len(sFloatList) ):
00217             equal = False
00218             print "- (line ", linenum, ") Number of floats not equal:"
00219             printDiff(standardList[linenum],testList[linenum])
00220             continue
00221         # Number of floats per line should be even
00222         #elif ( len(tFloatList) % 2 ): 
00223             #print "- (line ", linenum, ") Odd number of floats! All must be Amp-Phs pairs!"
00224         #    continue
00225             #print "stopping listing.diffAmpPhsFloat now!"
00226             #return 
00227         
00228         # Compare all Amp-Phs pairs on this line
00229         for i in range(len(tFloatList)/2):
00230 
00231             # If the Amp or Phs not exactly equal
00232             if ( ( tFloatList[i*2] != sFloatList[i*2] ) or 
00233                  ( tFloatList[i*2+1] != sFloatList[i*2+1] ) ):
00234 
00235                 countUnequal += 1
00236 
00237                 # Calculate complex number for test file
00238                 tamp = decimal.Decimal(tFloatList[i*2])
00239                 tphs_deg = decimal.Decimal(tFloatList[i*2+1])
00240                 pi = decimal.Decimal(str(math.pi))
00241                 tphs = tphs_deg * pi / 180
00242                 def dcos(x): return decimal.Decimal(str(math.cos(x)))
00243                 def dsin(x): return decimal.Decimal(str(math.sin(x)))
00244                 tre = dcos(tphs) * tamp
00245                 tim = dsin(tphs) * tamp
00246 
00247                 # Calculate complex number for standard file
00248                 samp = decimal.Decimal(sFloatList[i*2])
00249                 sphs_deg = decimal.Decimal(sFloatList[i*2+1])
00250                 sphs = sphs_deg * pi / 180
00251                 sre = dcos(sphs) * samp
00252                 sim = dsin(sphs) * samp
00253 
00254                 # Compute number of significant figures
00255                 #   Find the minimum sig figs for both Amp-Phs pairs.
00256                 #   Here significant figures means the number of
00257                 #   digits displayed, not including an exponent.
00258                 #   Examples: '0.00' has 3 sig figs
00259                 #             '0.001' has 4 sig fig
00260                 #             '-1e-15' has 1 sig fig
00261                 def remove_exp(x): return x.split("e")[0]
00262                 digitPattern = re.compile(r"([0-9])")
00263                 minSigFigs = 8
00264                 for i in [tamp, tphs_deg, tphs, samp, sphs_deg]:
00265                     num1 = str(i)
00266                     num1 = remove_exp(num1)
00267                     sigFigs = digitPattern.findall(num1)
00268                     if (len(sigFigs) < minSigFigs): minSigFigs = len(sigFigs)
00269                                     
00270                 # Compute difference of real and imaginary parts
00271                 re_diff = (sre - tre)
00272                 im_diff = (sim - tim)
00273 
00274                 # Compute the amplitude of the difference
00275                 amp_diff = str(math.sqrt( re_diff**2 + im_diff**2 ))
00276                 amp_diff = decimal.Decimal(amp_diff)
00277                 
00278                 # If necessary, reduce amp_diff to minimum sig figs
00279                 #   I do this by taking advantage of the decimal module, 
00280                 #   which can output and input a tuple.
00281                 #   * I truncate the number. Rounding would be better,
00282                 #     I just have not implemented it yet.
00283                 adTup = amp_diff.as_tuple()
00284                 digits_adTup = len(adTup[1])
00285                 if (digits_adTup > minSigFigs):
00286                     amp_diff = decimal.Decimal( (adTup[0], 
00287                                                  adTup[1][0:minSigFigs], 
00288                                                  adTup[2]+(digits_adTup-minSigFigs) ) )
00289 
00290                 # Keep track of the maximum Amp difference between both files
00291                 if (amp_diff > maxAmpDiff): maxAmpDiff = amp_diff
00292 
00293                 # Are Amp-Phs pairs equal within precision?
00294                 if (amp_diff > precision):
00295                     if (countBigDif == 0): printDiff("","",[standard,test]) # Print header info
00296                     equal = False # test evaluates false
00297                     countBigDif += 1
00298                     print "- (line ", linenum, ") Amp,Phs differ by more than precision:" 
00299                     printDiff(standardList[linenum],testList[linenum])
00300                     print "  (Amp,Phs): (", samp,",",sphs_deg,") , (",tamp,",",tphs_deg,")"
00301                     print "  ( Re, Im): (", sre ,",",sim ,") , (",tre ,",",tim ,")"
00302                     print "  Min sig figs = ", minSigFigs
00303                     print "  Difference amplitude :", amp_diff, " > ", precision
00304                 # else:
00305                 #     print "- (line ", linenum, ") Amp,Phs within required precision:"
00306                 #     printDiff(standardList[linenum],testList[linenum])
00307                 #     print "  Difference amplitude :", amp_diff, " <= ", precision
00308 
00309     print ""
00310     print "SUMMARY (diffAmpPhsFloat): "
00311     print "  %10i = Total number of Amp-Phs pair differences" % countUnequal
00312     print "  %10i = Total number of differences above precision" % countBigDif
00313     print "  %10f = Input precision requirement" % precision
00314     print "  %10f = Largest difference amplitude" % maxAmpDiff
00315 
00316     # Restore stdout
00317     sys.stdout = sys.__stdout__
00318     
00319     # cleanup
00320     testFile.close()
00321     standardFile.close()
00322     
00323     return equal
00324 
00325 #=============================================================================
00326 
00327 #=============================================================================
00328 # METHOD: printDiff
00329 #
00330 # Print two different strings
00331 def printDiff(s1, s2, filenames=[]):
00332     if (filenames):
00333         print "Printing lines with differences for comparison:"
00334         print "  < ", filenames[0]
00335         print "  > ", filenames[1]
00336     else:
00337         print "< " + s1,
00338         print "> " + s2,
00339 
00340 #=============================================================================
00341 
00342 #=============================================================================
00343 # METHOD: removeOut
00344 #
00345 # Remove old output file if it exists
00346 #
00347 # Parameters:
00348 #   outputFile = string; file to be removed
00349 def removeOut(outputFile):
00350     if (os.path.exists(outputFile)): 
00351         print "Removing old test file " + outputFile
00352         os.remove(outputFile)
00353 #=============================================================================
00354 
00355 #=============================================================================
00356 # METHOD: resetData
00357 #
00358 # Reset local data?  Returns True or False.
00359 #
00360 # Test data is typically acquired from the local data repository, and is often
00361 # copied to the local directory (in the case of a Measurement Set) or converted
00362 # to a measurement set in the local directory (in the case of a FITS file).
00363 # This method determines whether or not the test should rebuild the data in the
00364 # working directory or use what already exists.
00365 #
00366 # Parameters:
00367 #   msname = list of strings; Names of Tables that will be created
00368 #
00369 def resetData(msname, automate=True):
00370     
00371     reset_for_test = " "
00372 
00373     # If running in automated mode, always refresh data
00374     if (automate): 
00375         return True
00376     
00377     # Does the data already exist?
00378     for dataFile in msname:
00379         if (not os.path.exists(dataFile)): return True
00380 
00381     # If data exists, prompt for user direction
00382     while ( ( reset_for_test[0] != "y" and 
00383               reset_for_test[0] != "n" and 
00384               reset_for_test[0] != "\n" ) ):
00385         for dataFile in msname:
00386             sys.stdout.write("  " + dataFile+"\n")
00387         sys.stdout.write("Reset above data from scratch? y/[n]: ")
00388         reset_for_test = sys.stdin.readline()
00389     if (reset_for_test[0] == "y"): return True
00390     else: return False
00391 #=============================================================================
00392 
00393 #=============================================================================
00394 # METHOD: listcalFix
00395 #
00396 # Remove first line of listcal output.
00397 # (First line contains hard-coded path to input files)
00398 
00399 def listcalFix(listcalOut):
00400     os.system('mv ' + listcalOut + ' ' + listcalOut + '.tmp')
00401     os.system('tail -n +2 ' + listcalOut + '.tmp > ' + listcalOut)
00402     os.system('rm -f ' + listcalOut + '.tmp')
00403     return
00404 #=============================================================================
00405 
00406 #=============================================================================
00407 # METHOD: reduce
00408 #
00409 # Reduce the size of the output listing by printing every Nth line of file.
00410 #
00411 # This is necessary because the standard listings are stored in the data
00412 # repository and we want to keep these files small.
00413 #
00414 # Parameters:
00415 #   N = integer; beginning with line one, print every Nth line of the 
00416 #                 original file
00417 def reduce(filename, N):
00418 
00419 # This command prints every 11th line of the file:
00420 # sed -n '1,${p;n;n;n;n;n;n;n;n;n;n;}' infile > outfile
00421 
00422     infile = open(filename,'r')
00423     listing = infile.readlines()
00424 
00425     rangeL = range(len(listing))
00426 
00427     # picks holds the indices of listing that I want to keep
00428     def f(x): 
00429         return x % (N-1) == 0
00430     picks = filter(f,rangeL)
00431     
00432     reducedListing = [ listing[i] for i in picks ]
00433 
00434     #for i in range(20): print reducedListing[i]
00435 
00436     infile.close()
00437     return reducedListing
00438 #=============================================================================
00439 
00440 ## #=============================================================================
00441 ## # METHOD: studyDiff
00442 ## #
00443 ## # Return: 1) the number of leading identical digits
00444 ## #         2) the order of magnitude of the difference.
00445 ## def studyDiff(sfloat, tfloat, diffDict=diffDict_template):
00446 ##     import math
00447 ##     order = 1
00448 ##     # Order of magnitude of the difference
00449 ##     diffOrd = int(round(math.log10(abs(sfloat - tfloat))))
00450 ##     # Order of magnitude of sfloat
00451 ##     ord = int(round(math.log10(abs(sfloat))+0.5))
00452 ##     # Number of digits in common, starting from left
00453 ##     digits = ord - diffOrd - 1
00454 ##     # Sign difference?
00455 ##     signDiff = ( abs(sfloat - tfloat) != abs( abs(sfloat) - abs(tfloat) ) )
00456 ##     if (signDiff): digits = 0 # correct digits
00457 ##     # Absolute difference
00458 ##     absDiff = abs(sfloat - tfloat)
00459 ##     # Relative difference
00460 ##     relDiff = abs( (sfloat - tfloat) / sfloat)
00461 ##     print "Difference order = ", diffOrd
00462 ##     print "Digits same = ", digits
00463 ##     print "Sign difference = ", signDiff
00464 ##     print "Absolute difference = ", absDiff
00465 ##     print "Relative difference = ", relDiff
00466 ## 
00467 ##     diffDict['diffOrd'].append(diffOrd)
00468 ##     diffDict['digits'].append(digits)
00469 ##     diffDict['signDiff'].append(signDiff)
00470 ##     diffDict['absDiff'].append(absDiff)
00471 ##     diffDict['relDiff'].append(relDiff)
00472 ## 
00473 ##     return 
00474 ## 
00475 ## #=============================================================================
00476