casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
test_imcontsub.py
Go to the documentation of this file.
00001 ########################################################################3
00002 #  imcontsub_test.py
00003 #
00004 #
00005 # Copyright (C) 2008, 2009
00006 # Associated Universities, Inc. Washington DC, USA.
00007 #
00008 # This scripts free software; you can redistribute it and/or modify it
00009 # under the terms of the GNU Library General Public License as published by
00010 # the Free Software Foundation; either version 2 of the License, or (at your
00011 # option) any later version.
00012 #
00013 # This library is distributed in the hope that it will be useful, but WITHOUT
00014 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00016 # License for more details.
00017 #
00018 # You should have received a copy of the GNU Library General Public License
00019 # along with this library; if not, write to the Free Software Foundation,
00020 # Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
00021 #
00022 # Correspondence concerning AIPS++ should be adressed as follows:
00023 #        Internet email: aips2-request@nrao.edu.
00024 #        Postal address: AIPS++ Project Office
00025 #                        National Radio Astronomy Observatory
00026 #                        520 Edgemont Road
00027 #                        Charlottesville, VA 22903-2475 USA
00028 #
00029 # <author>
00030 # Shannon Jaeger (University of Calgary)
00031 # </author>
00032 #
00033 # <summary>
00034 # Test suite for the CASA imcontsub Task
00035 # </summary>
00036 #
00037 # <reviewed reviwer="" date="" tests="" demos="">
00038 # </reviewed
00039 #
00040 # <etymology>
00041 # imcontsub_test stands for image momemnts test
00042 # </etymology>
00043 #
00044 # <synopsis>
00045 # imcontsub_test.py is a Python script that tests the correctness
00046 # of the imcontsub task in CASA.
00047 #
00048 # The tests include:
00049 #    1. Incorrect input for each paramter.  Incorrect input includes
00050 #       one input of the incorrect data type, out-of-bounds (where
00051 #       applicable, and correct data type but non-sensical.
00052 #    2. A set of sample continuum subtractions with expected
00053 #       output
00054 #    3. Calculating one example for each allowed fitorder
00055 #    4. Continuum subtraction with region selection on the sky,
00056 #       channels, and stokes values, as well as using an input
00057 #       region file.
00058 #
00059 # In the imcontsub task a specified continuum channel is subtracted
00060 # from spectral line data.
00061 #
00062 # The expected input is a spectral line image. Both a continuum
00063 # image and a continuum-subtracted spectral line are created as
00064 # output, as long as the user has provided filenames for them.
00065 #
00066 # By default the continuum subtraction is applied to the whole
00067 # image, however, if a region file is given and/or the user specifies
00068 # a box range, channels, or stokes values then the subtraction is
00069 # performed on this portion of the image only.
00070 #
00071 # </synopsis> 
00072 #
00073 # <example>
00074 # # This test was designed to run in the CASA unit test system.
00075 # # This example shows who to run it manually from outside casapy.
00076 # casapy -c runUnitTest test_imcontsub
00077 #
00078 # or
00079 #
00080 # # This example shows who to run it manually from with casapy.
00081 # runUnitTest.main(['imcontsub_test'])
00082 #
00083 # </example>
00084 #
00085 # <motivation>
00086 # To provide a test standard to the imcontsub task to try and ensure
00087 # coding changes do not break the 
00088 # </motivation>
00089 #
00090 # <todo>
00091 # Almost everything!
00092 #
00093 # imcontsub doesn't return anything currently on success or failure.
00094 # If this changes the tests will need to change to check for this.
00095 # The basic design of the tests is based on the immoments test which
00096 # returns an image tool upon success.
00097 # </todo>
00098 
00099 #import random
00100 import os
00101 import shutil
00102 import casac
00103 from tasks import *
00104 from taskinit import *
00105 import unittest
00106 
00107 # Input files
00108 list = ['g192_a2.image', 'g192_a2.image-2.rgn', 
00109         'g192_a2.contfree', 'g192_a2.cont', 
00110         'g192_a2.contfree.order1', 'g192_a2.cont.order1', 
00111         'g192_a2.contfree.order2', 'g192_a2.cont.order2',
00112         'boxychans_cont.im', 'boxychans_line.im']
00113 
00114 # Tests the correctness of the imcontsub task in CASA including:
00115 #   1. Incorrect input for each paramter.  Incorrect input includes
00116 #      one input of the incorrect data type, out-of-bounds (where
00117 #      applicable, and correct data type but non-sensical.
00118 #   2. Doing a couple continuum subtractions checking the 
00119 #      results with previous results. 
00120 #   3. Doing continuum subtraction with fitorder from 1 to ?? 
00121 #      and verifying the results.
00122 #   4. Doing continuum subtraiong with region selection on the sky,
00123 #      channels, and stokes values, as well as using an input
00124 #      region file.
00125 #   
00126 #   Data used for this test includes: 
00127 #        1. g192_a2.image
00128 
00129 class imcontsub_test(unittest.TestCase):
00130 
00131     def setUp(self):
00132         if(os.path.exists(list[0])):
00133             for file in list:
00134                 os.system('rm -rf ' +file)
00135         
00136         datapath = os.environ.get('CASAPATH').split()[0]+'/data/regression/g192redux/reference/'
00137         for file in list:
00138             os.system('cp -r ' +datapath + file +' ' + file)
00139 
00140 
00141     def tearDown(self):
00142         for file in list:
00143             os.system('rm -rf ' +file)
00144             os.system('rm -rf cont_*')
00145             os.system('rm -rf input_test*')
00146             os.system('rm -rf fit_test*')
00147             os.system('rm -rf line_*')
00148             
00149             if os.path.exists( 'garbage.rgn' ):
00150                 os.remove('garbage.rgn')
00151         
00152     ####################################################################
00153     # Incorrect inputs to parameters.  The parameters are:
00154     #    imagename
00155     #    linefile
00156     #    contfile
00157     #    fitorder
00158     #    region
00159     #    box
00160     #    chans
00161     #    stokes
00162     #
00163     # Returns True if successful, and False if it has failed.
00164     ####################################################################
00165     
00166     def test_bad_imagename(self):
00167         """Test bad image name throws exception"""
00168         
00169         exception = False
00170         try:
00171             imcontsub( 'g192', contfile='input_test_cont1', linefile='input_test_line1' )
00172             exception = True
00173         except:
00174             exception = False
00175         self.assertTrue(exception)
00176         
00177     def test_bad_linefile(self):
00178         """ Test bad line file name fails"""
00179         
00180         # FIXME shouldn't a bad linefile name throw an exception?
00181         filename = 'input_test_line1'
00182         myfile = open(filename, mode="w")
00183         myfile.write("x")
00184         myfile.close()
00185         results = imcontsub( 'g192_a2.image', linefile=filename )
00186         self.assertTrue(not results)
00187             
00188     def test_bad_contfile(self):
00189         """ Test bad continuum file name fails"""
00190         
00191         # FIXME shouldn't a bad linefile name throw an exception?
00192         filename = 'input_test_cont'
00193         myfile = open(filename, mode="w")
00194         myfile.write("x")
00195         myfile.close()
00196         results = imcontsub( 'g192_a2.image', contfile=filename )
00197         self.assertTrue(not results)
00198         
00199     def test_bad_fitorder(self):
00200         """Test bad fitorder fails"""
00201             
00202         results=imcontsub( 'g192_a2.image', fitorder=-1, contfile='moment_test' )
00203         self.assertTrue(not results)
00204         
00205         results=imcontsub( 'g192_a2.image', fitorder=-2.3, contfile='moment_test' )
00206         self.assertTrue(not results)
00207         
00208     def test_bad_region(self):
00209         """ Test bad region parameter fails"""
00210         
00211         results = imcontsub( 'g192_a2.image', region=7 )
00212         self.assertTrue(not results)
00213         
00214         filename = os.getcwd()+'/garbage.rgn'
00215 
00216         results = imcontsub( 'g192_a2.image', region=filename)
00217         self.assertTrue(not results)
00218         
00219         fp=open( filename, 'w' )
00220         fp.writelines('This file does NOT contain a valid CASA region specification\n')
00221         fp.close()
00222         results = imcontsub( 'g192_a2.image', region=filename)
00223         self.assertTrue(not results)
00224 
00225 
00226     def test_bad_chans(self):
00227         """Test bad chans parameter causes failure"""
00228         
00229         results = imcontsub( 'g192_a2.image', chans='-5' )
00230         self.assertTrue(not results)
00231 
00232         results = imcontsub( 'g192_a2.image', chans='-2' )
00233         self.assertTrue(not results)
00234 
00235         results = imcontsub( 'g192_a2.image', chans='-18' )
00236         self.assertTrue(not results)
00237 
00238         results = imcontsub( 'g192_a2.image', chans='45' )
00239         self.assertTrue(not results)
00240 
00241         results = imcontsub( 'g192_a2.image', chans='40' )
00242         self.assertTrue(not results)
00243 
00244 
00245     def test_bad_box(self):
00246         """Test bad box parameter causes failure"""
00247         
00248         results = imcontsub( 'g192_a2.image', box='-3,0,511,511' )
00249         self.assertTrue(not results)
00250 
00251         results = imcontsub( 'g192_a2.image', box='0,-3,511,511' )
00252         self.assertTrue(not results)
00253 
00254         results = imcontsub( 'g192_a2.image', box='-2,0,511,511' )
00255         self.assertTrue(not results)
00256         
00257         results = imcontsub( 'g192_a2.image', box='0,-2,511,511' )
00258         self.assertTrue(not results)
00259 
00260         results = imcontsub( 'g192_a2.image', box='0,0,512,511' )
00261         self.assertTrue(not results)
00262         
00263         results = imcontsub( 'g192_a2.image', box='0,0,511,512' )
00264         self.assertTrue(not results)
00265         
00266         results = imcontsub( 'g192_a2.image', box='0, 0,525,511' )
00267         self.assertTrue(not results)
00268         
00269         results = imcontsub( 'g192_a2.image', box='0,0,511,525' )
00270         self.assertTrue(not results)
00271 
00272     def test_bad_stokes(self):
00273         """Test bad stokes parameter causes failure"""
00274         results = imcontsub( 'g192_a2.image', stokes='Q' )
00275         self.assertTrue(not results)
00276 
00277         results = imcontsub( 'g192_a2.image', stokes='yellow' )
00278         self.assertTrue(not results)
00279 
00280 
00281     
00282     ## ####################################################################
00283     ## # Continuum subtraction correctness test.
00284     ## #
00285     ## # This test subtacts the continuum from the g192 data file
00286     ## # and compares the results (both continuum and spectral line
00287     ## # with subtracted continuum files) with pervious results.
00288     ## #
00289     ## # Random values are selected in the files and compared.
00290     ## # FIXME compare the entire arrays, not bloody random values
00291     ## # FIXED - replacing it with the equivalent but better test_fitorder(0)
00292     ## # sped the suite up from 1847s to 1194s on faraday.cv.nrao.edu.
00293     ## #
00294     ## # Returns True if successful, and False if it has failed.
00295     ## ####################################################################
00296     
00297     ## def test_continuum(self):
00298     ##     '''Imcontsub: Testing continuum sub correctness'''
00299     ##     retValue = {'success': True, 'msgs': "", 'error_msgs': '' }
00300     ##     casalog.post( "Starting imcontsub CONTINUUM SUB CORRECTNESS tests.", 'NORMAL2' )
00301     
00302     ##     try:
00303     ##         results=imcontsub( 'g192_a2.image', fitorder=0, contfile='cont_test_cont1', linefile='cont_test_line1' )
00304     ##     except Exception, err:
00305     ##         retValue['success']=False
00306     ##         retValue['error_msgs']=retValue['error_msgs']\
00307     ##                  +"\nError: Unable to subtract continuum with a fit order of 0 "\
00308     ##                  +"\n\t REULTS: "+str(results)
00309     ##     else:
00310     ##         if ( not os.path.exists( 'cont_test_cont1' ) or not os.path.exists( 'cont_test_line1' ) or not results ): 
00311     ##             retValue['success']=False
00312     ##             retValue['error_msgs']=retValue['error_msgs']\
00313     ##                    +"\nError: continuum 3 output files were NOT created."
00314     ##         else:
00315     ##             # Now that we know something has been done lets check some values
00316     ##             # with previously created files to see if the values are the same.
00317     ##             # We randomly pick 50 points (almost 10%)
00318     ##             # FIXME lovely, yes let's pick random values to make sure any failures 
00319     ##             # cannot easily be reproduced, ugh
00320     ##             for count in range(0,50):
00321     ##                 x = random.randint(0,511)
00322     ##                 y = random.randint(0,511)
00323     ##                 box=str(x)+','+str(y)+','+str(x)+','+str(y)
00324     ##                 chan = str(random.randint(0,39))
00325     
00326     ##                 line_prev_value={}
00327     ##                 line_cur_value={'empty':''}
00328     ##                 try: 
00329     ##                     line_prev_value = imval( 'g192_a2.contfree', box=box, chans=chan, stokes='I' )
00330     ##                     line_cur_value  = imval( 'cont_test_line1', box=box, chans=chan, stokes='I' )
00331     ##                 except:
00332     ##                     retValue['success']=False
00333     ##                     retValue['error_msgs']=retValue['error_msgs']\
00334     ##                         +"\nError: Unable to compare spectral line files."
00335     ##                 else:
00336     ##                    #print "Spec line prev value: ", line_prev_value
00337     ##                    #print "spec line current value: ", line_cur_value  
00338     ##                    casalog.post( "*** line_prev_value " + str(line_prev_value), 'WARN')
00339     ##                    casalog.post( "*** line_cur_value " + str(line_cur_value), 'WARN')      
00340     ##                    if ( (line_prev_value['data'] != line_cur_value['data']).any() ):
00341     ##                     retValue['success']    = False
00342     ##                     retValue['error_msgs'] = '\nError: spectral line value differs with '\
00343     ##                           + "previously calculated value at: "\
00344     ##                           + "\t["+str(x)+','+str(y)+','+chan+',I].'\
00345     ##                           + "\tvalues are "+str(line_prev_value)+" and "+str(line_cur_value)
00346     ##                 try:
00347     ##                     cont_prev_value = imval( 'g192_a2.cont', box=box, chans=chan, stokes='I' )
00348     ##                     cont_cur_value  = imval( 'cont_test_cont1', box=box, chans=chan, stokes='I' )
00349     ##                 except:
00350     ##                     retValue['success']=False
00351     ##                     retValue['error_msgs']=retValue['error_msgs']\
00352     ##                         +"\nError: Unable to compare continuum files."
00353     ##                 else:
00354     ##                    #print "Continuum prev value: ", cont_prev_value
00355     ##                    #print "Continuum current value: ", cont_cur_value        
00356     ##                    if ( (cont_prev_value['data'] != cont_cur_value['data']).any() ):
00357     ##                     retValue['success']    = False
00358     ##                     retValue['error_msgs'] = '\nError: continuum value differs with '\
00359     ##                         + "previously calculated value at: "\
00360     ##                         + "\t["+str(x)+','+str(y)+','+chan+',I].'
00361     ##                         #+ "\tvalues are "+str(cont_prev_value)+" and "+str(cont_cur_value)
00362     
00363     ##     self.assertTrue(retValue['success'],retValue['error_msgs'])
00364     
00365     
00366     ####################################################################
00367     # Region selection correction test.
00368     #
00369     # This test selects a region for continuum subtraction. Checks
00370     # are done to make sure only the data in the selected region
00371     # changes.
00372     #
00373     # Returns True if successful, and False if it has failed.
00374     ####################################################################
00375     
00376 #    def test_region(self):
00377 #        '''Imcontsub: Testing region parameters'''
00378 #        retValue = {'success': True, 'msgs': "", 'error_msgs': '' }
00379 #        casalog.post( "Starting imcontsub REGION tests.", 'NORMAL2' )
00380 #    
00381 #        # First step get rid of all the old test files!
00382 #        for file in os.listdir( '.' ):
00383 #            if file.startswith( 'rgn_test_' ):
00384 #                shutil.rmtree( file )
00385 #    
00386 #    
00387 #    
00388 #        self.assertTrue(retValue['success'],retValue['error_msgs'])
00389     
00390     
00391     ####################################################################
00392     # Fitorder tests correctness test.
00393     #
00394     # This test subtacts the continuum from the g192 data file
00395     # for fitorder =1 and fitorder=2, note that fitorder=0 is
00396     # tested in the continuum test and valid/invalid inputs to
00397     # the fitorder paramter are tested in the input test.
00398     #
00399     # The results, image file contents, are compared with previously
00400     # created image files.
00401     #
00402     # Returns True if successful, and False if it has failed.
00403     ####################################################################
00404     
00405     def test_fitorder(self):
00406         '''Imcontsub: testing fitorder correctness'''
00407         retValue = {'success': True, 'msgs': "", 'error_msgs': '' }
00408         casalog.post( "Starting imcontsub INPUT/OUTPUT tests.", 'NORMAL2' )
00409     
00410         # First step get rid of all the old test files!
00411         for file in os.listdir( '.' ):
00412             if file.startswith( 'fit_test_' ):
00413                 shutil.rmtree( file )
00414     
00415         # TODO I'm just fixing the tests here to do something more reasonable since they were broken
00416         # and in bad need of rewriting (see the diff with the previous version before I made these
00417         # edits). This whole test file probably needs to be reviewed but of course time is tight so
00418         # I cannot do that now.
00419     
00420         for order in xrange(2):
00421             contfile='fit_test_cont'+str(order)
00422             linefile='fit_test_line'+str(order)
00423 
00424             if order > 0:
00425                 oldcontfile='g192_a2.cont.order'+str(order)
00426                 oldlinefile='g192_a2.contfree.order'+str(order)
00427             else:
00428                 oldcontfile = 'g192_a2.cont'
00429                 oldlinefile = 'g192_a2.contfree'
00430                 
00431             try:
00432                 results = imcontsub('g192_a2.image', fitorder=order, contfile=contfile, linefile=linefile)
00433             except Exception, err:
00434                 retValue['success']=False
00435                 retValue['error_msgs']=retValue['error_msgs']\
00436                     +"\nError: Unable to subtract continuum with a fit order="+str(order)\
00437                     +"\n\t REULTS: "+str(results)
00438             else:
00439                 if os.path.isdir(contfile) and os.path.isdir(linefile) and results:
00440                     retValue = self.cmp_images(linefile, oldlinefile,
00441                                                "Order " + str(order) + " line image",
00442                                                retValue)
00443                     retValue = self.cmp_images(contfile, oldcontfile,
00444                                                "Order " + str(order) + " continuum image",
00445                                                retValue)
00446                 else:  
00447                     retValue['success']=False
00448                     retValue['error_msgs']=retValue['error_msgs']\
00449                        +"\nError: output files were NOT created for fitorder="\
00450                        +str(order)+" test."
00451 
00452         self.assertTrue(retValue['success'],retValue['error_msgs'])
00453 
00454     def cmp_images(self, newimg, oldimg, errmsg, retval, tol=1.0e-8):
00455         """
00456         Check that newimg matches oldimg to within tol.
00457         errmsg is a descriptive label in case there is an error.
00458         retval is updated with the results and returned.
00459         """
00460         try:
00461             subtract_expr = '(\"' + newimg + '\"-\"' + oldimg + '\")'
00462             output_image = newimg + '.diff'
00463             immath(mode='evalexpr', expr=subtract_expr, outfile=output_image)
00464             ia.open(output_image)
00465             stats = ia.statistics()
00466             ia.close()
00467             absmax = max(abs(stats['min']), abs(stats['max']))
00468             # in an infinite precision utopia, the difference image would be 0, but
00469             # alas, we do not live in such a world yet.
00470             if (absmax > tol):
00471                 retval['success'] = False
00472                 retval['error_msgs'] += errmsg + ' error: Max residual is ' + str(absmax)
00473         except Exception, err:
00474             retval['success'] = False
00475             retval['error_msgs'] += errmsg + " error: Exception thrown: " + str(err)
00476         return retval
00477         
00478 
00479     def test_box_and_chans(self):
00480         '''Imcontsub: Testing box and chans'''
00481         retValue = {'success': True, 'msgs': "", 'error_msgs': '' }
00482         #casalog.post("Starting imcontsub box and chans tests.", 'NORMAL2')
00483 
00484         oldcfil = 'boxychans_cont.im'
00485         cfil = 'test_' + oldcfil
00486         oldlfil = 'boxychans_line.im'
00487         lfil = 'test_' + oldlfil
00488         bx   = [400, 420, 490, 470]
00489     
00490         try:
00491             results = imcontsub('g192_a2.image', fitorder=0, contfile=cfil,
00492                                 linefile=lfil, box=bx,
00493                                 chans='32~37')  # Purposely one-sided.  
00494         except Exception, err:
00495             retValue['success']=False
00496             retValue['error_msgs'] += "\nError: Unable to subtract continuum with box and chans "\
00497                                       +"\n\t RESULTS: "+str(results)
00498         else:
00499             if os.path.isdir(cfil) and os.path.isdir(lfil) and results:
00500                 retValue = self.cmp_images(lfil, oldlfil,
00501                                            "box and chans line image", retValue)
00502                 retValue = self.cmp_images(cfil, oldcfil,
00503                                            "box and chans continuum image",
00504                                            retValue)
00505             else:
00506                 retValue['success']=False
00507                 retValue['error_msgs'] += "\nError: box and chans output files were NOT created."
00508 
00509         self.assertTrue(retValue['success'],retValue['error_msgs'])
00510         
00511 def suite():
00512     return [imcontsub_test]
00513