1. Creating polarization images using CASA

If you need more explanation, you can also read into this CASA Guide and scroll down to the section Constructing Polarization Intensity and Angle Images.

Once you have created Stokes I, Q, and U images, you can try the following CASA scripts. I sometimes use it to produce quicklook polarization intensity (PI), polarization percentage (Per), and polarization position angle (PA) images. The input images have to be in CASA image format. If you only have FITS images, you need to first import them to CASA. You can find some instruction about importing in this page.

Usually, I create the PI, Per, and PA images using the Stokes I, Q, and U images without primary beam corrections. This makes it easier to set a noise threshold (i.e., ignoring the pixels that the values are below that threshold). We need to remember to perform primary beam correction for the Stokes I, Q, U, and PI images using the CASA task impbcor before making other quantitative analyses.

mystep = 1
if(mystep in thesteps):
  casalog.post('Step '+str(mystep)+' '+step_title[mystep],'INFO')
  print 'Step ', mystep, step_title[mystep]

  stokes      = 'I'
  Iimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'Q'
  Qimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'U'
  Uimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'V'
  Vimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'PI'
  PIimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'Per'
  Perimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)
  stokes      = 'PA'
  PAimagename   = '%s.rob%s.%s.image.tt0'%(outname, str(robust), stokes)


  # generate the linear polarization intensity image
  command = 'rm -rf ' + PIimagename
  os.system(command)
  mode = 'poli'
  imagename = [Qimagename, Uimagename]
  sigma     = '0.000027Jy/beam'
  immath(
         outfile=PIimagename,
         mode=mode,
         imagename=imagename,
         sigma=sigma
        )

  # derive the polarization position angle
  command = 'rm -rf ' + PAimagename
  os.system(command)
  mode = 'pola'
  imagename = [Qimagename, Uimagename]
  mask      = PIimagename + '>' + '0.0'
  immath(
         outfile=PAimagename,
         mode=mode,
         imagename=imagename,
#         mask=mask
        )

  # derive polarization percentage
  command = 'rm -rf ' + Perimagename
  os.system(command)
  mode  = 'evalexpr'
  expr  = 'IM0/IM1'
  imagename = [PIimagename, Iimagename]
  immath(
         outfile=Perimagename,
         mode=mode,
         imagename=imagename,
         expr=expr,
         mask=mask
        )

2. Creating polarization images using Miriad

Here I provide a Miriad script that I often adopt to create polarization PI, Per, and PA images. It first imports FITS images to Miriad format and then derives the polarization quantities with the impol Miriad task.

#!/bin/bash
  


filehead='G31p4_Qband_D.rob2'
fileend='image.tt0'
Stokes='I Q U'
for stoke in $Stokes
  do
    inname=$filehead.$stoke.$fileend'.fits'
    outname=$filehead.$stoke.$fileend'.miriad'
    rm -rf $outname
    echo 'importing '$inname
    fits in=$inname op=xyin out=$outname
  done


# evaluating polarization images
poli=$filehead'.PI.'$fileend'.miriad'
polm=$filehead'.Per.'$fileend'.miriad'
pa=$filehead'.PA.'$fileend'.miriad'

for file in $poli $polm $pa
  do
    rm -rf $file
  done


# derive polarization images (Stokes I, Q, U stddev: 27e-6 Jy)
QUIimg='G31p4_Qband_D.rob2.Q.image.tt0.miriad,G31p4_Qband_D.rob2.U.image.tt0.miriad,G31p4_Qband_D.rob2.I.image.tt0.miriad'
impol in=$QUIimg sigma=0.000050,0.000050 sncut=3,20 poli=$poli polm=$polm pa=$pa
cgdisp in=$filehead.I.$fileend'.miriad',$poli,$polm,$pa \
       type=c,p,amp,ang device=/xw \
       region='arcsec,box(-5,-5,5,5)' \
       options=full,beambl,wedge,blacklab \
       lines=1,2,4 vecfac=3.5,2 labtyp=hms,dms


# Export FITS image
for file in $poli $polm $pa
  do
    outfitsname=$file'.fits'
    rm -rf $outfitsname
    fits in=$file op=xyout out=$outfitsname
  done

Again, we have to perform primary beam correction before making any quantitative analyses. You can either import the FITS images to CASA and do the primary beam correction using CASA. Or we can do it within Miriad, using the commands like the following:

linmos in=$linename.acamodel.regrid.temp \
       out=$linename.acamodel.regrid.pbcor.temp

This usually works. However, if Miriad does not find the primary beam information correction, you can also manually include it in the header (e.g. ,approximate it with a Gaussian primary beam), for example:

#!/bin/csh

set pbfwhm = (27.327382 27.327382 45.54) # in arcsecond unit
set linerestfreq = 230.53800000 # in GHz unit
 
setheader:

foreach fileid ($fileids)

   set pb = '"gaus('$pbfwhm[$fileid]')"'
   puthd in=$linename'_'$fileid'.uv.miriad'/telescop \
         value='single' \
         type=a

   puthd in=$linename'_'$fileid'.uv.miriad'/pbtype \
         value=$pb \
         type=a

   puthd in=$linename'_'$fileid'.uv.miriad'/restfreq \
         value=$linerestfreq \
         type=d

end

3. Plotting

If you are handling the polarization data using Python, especially, if you are creating figures using the APLpy package, you will sometimes face a problem that the package(s) you use cannot recognize the Stokes dimension. In this case you can try to import the images to CASA and then drop the dummy dimension(s) when you are exporting back to FITS, for example:

import os
  
path = './'
Imap = path + 'G31p4_Qband_D.rob2.I.image.tt0'
Qmap = path + 'G31p4_Qband_D.rob2.Q.image.tt0'
Umap = path + 'G31p4_Qband_D.rob2.U.image.tt0'
PImap  = path + 'G31p4_Qband_D.rob2.PI.image.tt0.miriad'
pamap  = path + 'G31p4_Qband_D.rob2.PA.image.tt0.miriad'
Permap = path + 'G31p4_Qband_D.rob2.Per.image.tt0.miriad'


for imname in [Imap, PImap, pamap, Permap]:
    importfits(
                fitsimage = imname + '.fits',
                imagename = imname + '.image',
                overwrite = True
               )

    exportfits(
                imagename = imname + '.image',
                fitsimage = imname + '.dropdeg.fits',
                overwrite = True, dropdeg = True, dropstokes = True
              )

I use the following code to plot the polarization line segments (you need to create your own code to define sampling positions). This is a function in a big class. I am reluctant to edit it now. You should be able to more or less know how it works and edit to the way that fits your code the best, given that you are likely already experienced. You can feel free to contact my student, Greta Siu, in case of any problem.

    def plot_segments(self, length_arcsec=-999.0, color=(0,0,0), linestyle='solid', linewidth=2.0, delraperyr_mas=0.0, deldecperyr_mas=0.0, delyr=0.0):
        '''
        Plot the polarization line segments.

        Input :

        length_arc    [float] : Default: 1/3 of the synthesized beam FWHM of the intensity map

        Keywords :
        
        delraperyr_mas    [float] : For correcting proper motions (delta ra per year). Units: mas. Default: 0.0
        deldecperyr_mas   [float] : For correcting proper motions (delta dec per year). Units: mas. Default: 0.0
        delyr             [float] : For correcting proper motions. (date-to-go-to minus data-of-observation)

        '''

        delra_deg  = delraperyr_mas  * delyr * 0.001 / 3600.0
        deldec_deg = deldecperyr_mas * delyr * 0.001 / 3600.0
        delra_pix  = delra_deg  / self.cdelt1
        deldec_pix = deldec_deg / self.cdelt2


        if (length_arcsec == -999.0):
            length_arcsec = ( np.sqrt( self.bmaj * self.bmin ) * 3600.0 ) / 3.0

        halflength_pix = 0.5 * length_arcsec / (self.cdelt2 * 3600.0)

        for i in range(0, self.num_samplings):

            xpix = int(round(self.xpixgrid_list[i] ))
            ypix = int(round(self.ypixgrid_list[i] ))

            pa = self.paimage[ypix][xpix]

            xbeg = self.xpixgrid_list[i] + delra_pix  - halflength_pix * np.sin( np.pi * (pa/180.0) )
            ybeg = self.ypixgrid_list[i] + deldec_pix + halflength_pix * np.cos( np.pi * (pa/180.0) )
            xend = self.xpixgrid_list[i] + delra_pix  + halflength_pix * np.sin( np.pi * (pa/180.0) )
            yend = self.ypixgrid_list[i] + deldec_pix - halflength_pix * np.cos( np.pi * (pa/180.0) )

            world1 = self.hduwcs.wcs_pix2world([ [ xbeg, ybeg ] ], 0)
            world2 = self.hduwcs.wcs_pix2world([ [ xend, yend ] ], 0)

            linelist = [np.array([
                                  [  world1[0][0], world2[0][0]  ],
                                  [  world1[0][1], world2[0][1]  ]
                                 ])]

            if ( np.isfinite(pa) == True ):
                self.fig.show_lines(linelist,
                                    color=color, linestyle=linestyle, linewidth=linewidth)