Example #1
0
def getdata1(imgname):
    """Return all good data from a CASA image as a compressed 1D numpy array.
 
       The tb.open() access method, although faster, didn't seem to carry
       the mask (or at least in an easy way).  ma.masked_invalid(data)
       still returned all pixels ok.
  
       NOTE:  since this routine grabs all data in a single numpy
       array, this routine should only be used for 2D images
       or small 3D cubes.
  
       Parameters
       ----------
       imagename : str 
           The (absolute) CASA image filename 
  
       Returns
       -------
       array 
          data in a 1D numpy array
    """
    ia = taskinit.iatool()
    ia.open(imgname)
    d = ia.getchunk(blc=[0,0,0,0],trc=[-1,-1,0,0],getmask=False)
    m = ia.getchunk(blc=[0,0,0,0],trc=[-1,-1,0,0],getmask=True)
    ia.close()
    # note CASA and MA have their mask logic reversed
    # casa: true means a good point
    #   ma: true means a masked/bad point
    dm = ma.masked_array(d,mask=~m)
    return dm.compressed()
Example #2
0
def estimate_noise(imagename, linefree_range,
                   noise_function=np.nanstd):
    '''
    Calculate the noise in a cube in line-free channels.

    Should be run on *non-*pb corrected images so the spatial noise
    is flat!

    Estimate will also skip empty channels that do not have any data.
    '''

    if imagename.endswith(".image"):
        try:
            # CASA 6
            import casatools
            ia = casatools.image()
        except ImportError:
            try:
                from taskinit import iatool
                iatool = iatool()
            except ImportError:
                raise ImportError("Could not import CASA (casac).")


        ia.open(imagename)
        data = ia.getchunk().squeeze()
        ia.close()
        ia.done()

    elif imagename.lower().endswith(".fits"):

        cube = SpectralCube.read(imagename)
        data = cube.filled_data[:].value
        del cube

    else:
        raise ValueError("Unsure what kind of file this is.")

    noise_data = np.empty((1,))

    for chan_range in linefree_range:
        for chan in range(chan_range[0], chan_range[1]):
            if chan >= data.shape[-1]:
                continue

            # Skip empty channels
            if np.isnan(data[..., chan]).all():
                continue

            if (data[..., chan] == 0.).all():
                continue

            valid_mask = np.logical_and(data[..., chan] != 0.,
                                        np.isfinite(data[..., chan]))

            noise_data = np.append(noise_data, data[..., chan][valid_mask])

    return noise_function(noise_data)
Example #3
0
def map_to_slit(fname, clip=0.0, gamma=1.0):
    """take all values from a map over clip, compute best slit for PV Slice
    """
    ia = taskinit.iatool()
    ia.open(fname)
    imshape = ia.shape()
    pix = ia.getchunk().squeeze()  # this should now be a numpy pix[ix][iy] map
    pixmax = pix.max()
    pixrms = pix.std()
    if False:
        pix1 = pix.flatten()
        rpix = stats.robust(pix1)
        logging.debug("stats: mean: %g %g" % (pix1.mean(), rpix.mean()))
        logging.debug("stats: rms: %g %g" % (pix1.std(), rpix.std()))
        logging.debug("stats: max: %g %g" % (pix1.max(), rpix.max()))
        logging.debug('shape: %s %s %s' %
                      (str(pix.shape), str(pix1.shape), str(imshape)))
    ia.close()
    nx = pix.shape[0]
    ny = pix.shape[1]
    x = np.arange(pix.shape[0]).reshape((nx, 1))
    y = np.arange(pix.shape[1]).reshape((1, ny))
    if clip > 0.0:
        nmax = nx * ny
        clip = clip * pixrms
        logging.debug("Using initial clip=%g for rms=%g" % (clip, pixrms))
        m = ma.masked_less(pix, clip)
        while m.count() == 0:
            clip = 0.5 * clip
            logging.debug("no masking...trying lower clip=%g" % clip)
            m = ma.masked_less(pix, clip)
        else:
            logging.debug("Clip=%g now found %d/%d points" %
                          (clip, m.count(), nmax))

    else:
        #@ todo   sigma-clipping with iterations?  see also astropy.stats.sigma_clip()
        rpix = stats.robust(pix.flatten())
        r_mean = rpix.mean()
        r_std = rpix.std()
        logging.info("ROBUST MAP mean/std: %f %f" % (r_mean, r_std))
        m = ma.masked_less(pix, -clip * r_std)
    logging.debug("Found > clip=%g : %g" % (clip, m.count()))
    if m.count() == 0:
        logging.warning("Returning a dummy slit, no points above clip %g" %
                        clip)
        edge = 3.0
        #slit = [edge,0.5*ny,nx-1.0-edge,0.5*ny]          # @todo    file a bug, this failed
        #  RuntimeError: (/var/rpmbuild/BUILD/casa-test/casa-test-4.5.7/code/imageanalysis/ImageAnalysis/PVGenerator.cc : 334) Failed AlwaysAssert abs( (endPixRot[0] - startPixRot[0]) - sqrt(xdiff*xdiff + ydiff*ydiff) ) < 1e-6
        slit = [edge, 0.5 * ny - 0.1, nx - 1.0 - edge, 0.5 * ny + 0.1]
    else:
        slit = convert_to_slit(m, x, y, nx, ny, gamma)
    return (slit, clip)
Example #4
0
def getdata(imgname, chans=[], zeromask=False):
    """Return all good data from a CASA image as a masked numpy array.

       The tb.open() access method, although faster, didn't seem to carry
       the mask (or at least in an easy way).  ma.masked_invalid(data)
       still returned all pixels ok.

       NOTE:  since this routine grabs all data in a single numpy
       array, this routine should only be used for 2D images
       or small 3D cubes, things with little impact on memory

       Parameters
       ----------
       imagename : str 
           The (absolute) CASA image filename 

       Returns
       -------
       array 
           data in a masked numpy array
    """
    ia = taskinit.iatool()    
    ia.open(imgname)
    if len(chans) == 0:
        d = ia.getchunk(blc=[0,0,0,0],trc=[-1,-1,-1,0],getmask=False).squeeze()
        m = ia.getchunk(blc=[0,0,0,0],trc=[-1,-1,-1,0],getmask=True).squeeze()
    else:
        d = ia.getchunk(blc=[0,0,chans[0],0],trc=[-1,-1,chans[1],0],getmask=False).squeeze()
        m = ia.getchunk(blc=[0,0,chans[0],0],trc=[-1,-1,chans[1],0],getmask=True).squeeze()
    ia.close()
    # note CASA and MA have their mask logic reversed
    # casa: true means a good point
    #   ma: true means a masked/bad point
    if zeromask:
        shape = d.shape
        ndim = len(d.shape)
        if ndim == 2:
            (x,y) = ma.where(~m)
            d[x,y] = 0.0
        elif ndim == 3:
            (x,y,z) = ma.where(~m)
            d[x,y,z] = 0.0
        else:
            raise Exception,"getdata: cannot handle data of dimension %d" % ndim
    dm = ma.masked_array(d,mask=~m)
    return dm
def fits_to_template(fitsfilename):
    """
    Extract a CASA "template" header from a FITS file
    """
    
    if not isinstance(fitsfilename,str):
        raise ValueError("FITS file name must be a filename string")

    ia = iatool()
    ia.fromfits(infile=fitsfilename, outfile=td, overwrite=True)

    td = tempfile.mkdtemp()

    ia.open(td)
    csys = ia.coordsys()
    shape = ia.shape()

    ia.close()

    outheader = {'csys':csys.torecord(),
                 'shap':shape}

    return outheader
Example #6
0
def putdata_raw(imgname, data, clone=None):
    """Store (overwrite) data in an existing CASA image.
       See getdata_raw(imgname) for the reverse operation.

       Parameters
       ----------
       imagename : str
           The (absolute) CASA image filename.  It should exist
           already, unless **clone** was given.

       data : 2D numpy array or a list of 2D numpy arrays
           The data...

       clone : str, optional
           An optional filename from which to clone the image
           for output. It needs to be an absolute filename.
  
    """
    ia = taskinit.iatool()    
    if clone != None:
        ia.fromimage(infile=clone,outfile=imgname,overwrite=True) 
        ia.close()
    # @todo this seems circumvent to have to borrow the odd dimensions (nx,ny,1,1,1) shape was seen
    if type(data) == type([]):
        # @todo since this needs to extend the axes, the single plane clone and replace data doesn't work here
        raise Exception,"Not Implemented Yet"
        bigim = ia.imageconcat(outfile=imgname, infiles=infiles, axis=2, relax=T, tempclose=F, overwrite=T)
        bigim.close()
    else:
        tb = taskinit.tbtool()
        tb.open(imgname,nomodify=False)
        d = tb.getcol('map')
        pdata = ma.getdata(data).reshape(d.shape)
        tb.putcol('map',pdata)
        tb.flush()
        tb.close()
    return
Example #7
0
def mapdim(imgname, dim=None):
     """Return the dimensionality of the map, or if dim is given.
     
     Returns True if the dimensionality matches this value, or
     False if not.

     Warning: opens and closes the map via ia.open()

     Parameters
     ----------
     imagename : str (a filename)

     dim : integer (or None)

     """
     ia = taskinit.iatool()     
     ia.open(imgname)
     s = ia.summary()
     shape = s['shape']
     ia.close()
     #
     rdim = -1
     for d in range(len(shape)):
         if shape[d] == 0 or shape[d] == 1:
             rdim = d 
             break
     if rdim < 0:
       rdim = len(shape)
     #print "MAPDIM",imgname,shape,rdim
     #
     if dim == None:
         return rdim
     if dim == rdim:
         return True
     else:
         return False
Example #8
0
    def run(self):
        """ The run method creates the BDP

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        dt = utils.Dtime("SFind2D")  # tagging time
        self._summary = {}
        # get key words that user input
        nsigma = self.getkey("numsigma")
        sigma = self.getkey("sigma")
        region = self.getkey("region")
        robust = self.getkey("robust")
        snmax = self.getkey("snmax")
        nmax = self.getkey("nmax")
        ds9 = True  # writes a "ds9.reg" file
        mpl = True  # aplot.map1() plot
        dynlog = 20.0  # above this value of dyn range finder chart is log I-scaled
        bpatch = True  # patch units to Jy/beam for ia.findsources()

        # get the input casa image from bdp[0]
        bdpin = self._bdp_in[0]
        infile = bdpin.getimagefile(bt.CASA)
        if mpl:
            data = np.flipud(np.rot90(casautil.getdata(self.dir(infile)).data))

        # check if there is a 2nd image (which will be a PB)
        for i in range(len(self._bdp_in)):
            print 'BDP', i, type(self._bdp_in[i])

        if self._bdp_in[2] != None:
            bdpin_pb = self._bdp_in[1]
            bdpin_cst = self._bdp_in[2]
            print "Need to process PB"
        else:
            bdpin_pb = None
            bdpin_cst = self._bdp_in[1]
            print "No PB given"

        # get the output bdp basename
        slbase = self.mkext(infile, 'sl')

        # make sure it's a 2D map
        if not casautil.mapdim(self.dir(infile), 2):
            raise Exception, "Input map dimension not 2: %s" % infile

        # arguments for imstat call if required
        args = {"imagename": self.dir(infile)}
        if region != "":
            args["region"] = region
        dt.tag("start")

        # The following code sets the sigma level for searching for sources using
        # the sigma and snmax keyword as appropriate
        # if no CubeStats BDP was given and no sigma was specified:
        # find a noise level via casa.imstat()
        # if a CubeStat_BDP is given get it from there.
        if bdpin_cst == None:
            # get statistics from input image with imstat because no CubeStat_BDP
            stat = casa.imstat(**args)
            dmin = float(
                stat["min"]
                [0])  # these would be wrong if robust were used already
            dmax = float(stat["max"][0])
            args.update(casautil.parse_robust(
                robust))  # only now add robust keywords for the sigma
            stat = casa.imstat(**args)
            if sigma <= 0.0:
                sigma = float(stat["sigma"][0])
            dt.tag("imstat")
        else:
            # get statistics from CubeStat_BDP
            sigma = bdpin_cst.get("sigma")
            dmin = bdpin_cst.get("minval")
            dmax = bdpin_cst.get("maxval")

        self.setkey("sigma", sigma)
        # calculate cutoff based either on RMS or dynamic range limitation
        drange = dmax / (nsigma * sigma)
        if snmax < 0.0:
            snmax = drange
        if drange > snmax:
            cutoff = 1.0 / snmax
        else:
            cutoff = 1.0 / drange
        logging.info("sigma, dmin, dmax, snmax, cutoff %g %g %g %g %g" %
                     (sigma, dmin, dmax, snmax, cutoff))
        # define arguments for call to findsources
        args2 = {"cutoff": cutoff}
        args2["nmax"] = nmax
        if region != "":
            args2["region"] = region
        #args2["mask"] = ""
        args2["point"] = False
        args2["width"] = 5
        args2["negfind"] = False
        # set-up for SourceList_BDP
        slbdp = SourceList_BDP(slbase)

        # connect to casa image and call casa ia.findsources tool
        ia = taskinit.iatool()
        ia.open(self.dir(infile))

        # findsources() cannot deal with  'Jy/beam.km/s' ???
        # so for the duration of findsources() we patch it
        bunit = ia.brightnessunit()
        if bpatch and bunit != 'Jy/beam':
            logging.warning(
                "Temporarely patching your %s units to Jy/beam for ia.findsources()"
                % bunit)
            ia.setbrightnessunit('Jy/beam')
        else:
            bpatch = False
        atab = ia.findsources(**args2)
        if bpatch:
            ia.setbrightnessunit(bunit)

        taskargs = "nsigma=%4.1f sigma=%g region=%s robust=%s snmax=%5.1f nmax=%d" % (
            nsigma, sigma, str(region), str(robust), snmax, nmax)
        dt.tag("findsources")
        nsources = atab["nelements"]
        xtab = []
        ytab = []
        logscale = False
        sumflux = 0.0
        if nsources > 0:
            # @TODO: Why are Xpix, YPix not stored in the table?
            #        -> PJT: I left them out since they are connected to an image which may not be available here
            #                but we should store the frequency of the observation here for later bandmerging
            logging.debug("%s" % str(atab['component0']['shape']))
            logging.info(
                "Right Ascen.  Declination   X(pix)   Y(pix)      Peak       Flux    Major   Minor    PA    SNR"
            )
            funits = atab['component0']['flux']['unit']
            if atab['component0']['shape'].has_key('majoraxis'):
                sunits = atab['component0']['shape']['majoraxis']['unit']
                aunits = atab['component0']['shape']['positionangle']['unit']
            else:
                sunits = "n/a"
                aunits = "n/a"
            punits = ia.summary()['unit']
            logging.info(
                "                                               %s       %s    %s   %s   %s"
                % (punits, funits, sunits, sunits, aunits))
            #
            # @todo future improvement is to look at image coordinates and control output appropriately
            #
            if ds9:
                # @todo variable name
                regname = self.mkext(infile, 'ds9.reg')
                fp9 = open(self.dir(regname), "w!")
            sn0 = -1.0
            for i in range(nsources):
                c = "component%d" % i
                name = "%d" % (i + 1)
                r = atab[c]['shape']['direction']['m0']['value']
                d = atab[c]['shape']['direction']['m1']['value']
                pixel = ia.topixel([r, d])
                xpos = pixel['numeric'][0]
                ypos = pixel['numeric'][1]
                rd = ia.toworld([xpos, ypos], 's')
                ra = rd['string'][0][:12]
                dec = rd['string'][1][:12]
                flux = atab[c]['flux']['value'][0]
                sumflux = sumflux + flux
                if atab[c]['shape'].has_key('majoraxis'):
                    smajor = atab[c]['shape']['majoraxis']['value']
                    sminor = atab[c]['shape']['minoraxis']['value']
                    sangle = atab[c]['shape']['positionangle']['value']
                else:
                    smajor = 0.0
                    sminor = 0.0
                    sangle = 0.0
                peakstr = ia.pixelvalue([xpos, ypos, 0, 0])
                if len(peakstr) == 0:
                    logging.warning("Problem with source %d @ %d,%d" %
                                    (i, xpos, ypos))
                    continue
                peakf = peakstr['value']['value']
                snr = peakf / sigma
                if snr > dynlog:
                    logscale = True
                if snr > sn0:
                    sn0 = snr
                logging.info(
                    "%s %s %8.2f %8.2f %10.3g %10.3g %7.3f %7.3f %6.1f %6.1f" %
                    (ra, dec, xpos, ypos, peakf, flux, smajor, sminor, sangle,
                     snr))

                xtab.append(xpos)
                ytab.append(ypos)
                slbdp.addRow(
                    [name, ra, dec, flux, peakf, smajor, sminor, sangle])
                if ds9:
                    ras = ra
                    des = dec.replace('.', ':', 2)
                    msg = 'ellipse(%s,%s,%g",%g",%g) # text={%s}' % (
                        ras, des, smajor, sminor, sangle + 90.0, i + 1)
                    fp9.write("%s\n" % msg)
            if ds9:
                fp9.close()
                logging.info("Wrote ds9.reg")
            dt.tag("table")
        logging.regression("CONTFLUX: %d %g" % (nsources, sumflux))

        summary = ia.summary()
        beammaj = summary['restoringbeam']['major']['value']
        beammin = summary['restoringbeam']['minor']['value']
        beamunit = summary['restoringbeam']['minor']['unit']
        beamang = summary['restoringbeam']['positionangle']['value']
        angunit = summary['restoringbeam']['positionangle']['unit']
        # @todo add to table comments?
        logging.info(" Fitted Gaussian size; NOT deconvolved source size.")
        logging.info(
            " Restoring Beam: Major axis: %10.3g %s , Minor axis: %10.3g %s , PA: %5.1f %s"
            % (beammaj, beamunit, beammin, beamunit, beamang, angunit))
        # form into a xml table

        # output is a table_bdp
        self.addoutput(slbdp)

        # instantiate a plotter for all plots made herein
        myplot = APlot(ptype=self._plot_type,
                       pmode=self._plot_mode,
                       abspath=self.dir())

        # make output png with circles marking sources found
        if mpl:
            circles = []
            nx = data.shape[1]  # data[] array was already flipud(rot90)'d
            ny = data.shape[0]  #
            for (x, y) in zip(xtab, ytab):
                circles.append([x, y, 1])
            # @todo variable name
            if logscale:
                logging.warning("LogScaling applied")
                data = data / sigma
                data = np.where(data < 0, -np.log10(1 - data),
                                +np.log10(1 + data))
            if nsources == 0:
                title = "SFind2D: 0 sources above S/N=%.1f" % (nsigma)
            elif nsources == 1:
                title = "SFind2D: 1 source (%.1f < S/N < %.1f)" % (nsigma, sn0)
            else:
                title = "SFind2D: %d sources (%.1f < S/N < %.1f)" % (
                    nsources, nsigma, sn0)
            myplot.map1(data,
                        title,
                        slbase,
                        thumbnail=True,
                        circles=circles,
                        zoom=self.getkey("zoom"))

        #---------------------------------------------------------
        # Get the figure and thumbmail names and create a caption
        #---------------------------------------------------------
        imname = myplot.getFigure(figno=myplot.figno, relative=True)
        thumbnailname = myplot.getThumbnail(figno=myplot.figno, relative=True)
        caption = "Image of input map with sources found by SFind2D overlayed in green."
        slbdp.table.description = "Table of source locations and sizes (not deconvolved)"

        #---------------------------------------------------------
        # Add finder image to the BDP
        #---------------------------------------------------------
        image = Image(images={bt.PNG: imname},
                      thumbnail=thumbnailname,
                      thumbnailtype=bt.PNG,
                      description=caption)
        slbdp.image.addimage(image, "finderimage")

        #-------------------------------------------------------------
        # Create the summary entry for the table and image
        #-------------------------------------------------------------
        self._summary["sources"] = SummaryEntry(
            [slbdp.table.serialize(),
             slbdp.image.serialize()], "SFind2D_AT", self.id(True), taskargs)

        dt.tag("done")
        dt.end()
Example #9
0
def ksc_sim_gauss(projname='sim_7m_array_1GHz_gaus',
                  antennalist='ksc-7m.cfg',
                  dishdiam=7,
                  imagename=None,
                  indirection='J2000 14h26m46.0s -14d31m22.0s',
                  incell='1arcsec',
                  frequency='1.0GHz',
                  inwidth='1MHz',
                  radec_offset=[100., 100.],
                  flux=50.,
                  majoraxis='40arcsec',
                  minoraxis='27arcsec',
                  positionangle='45.0deg',
                  imsize=[512, 512]):
    """
    Simulate observation of an input Gaussian model
    :param projname: project name for simobserve
    :param antennalist: cfg file of the array configuration
    :param dishdiam: diameter of each dish, in meters
    :param imagename: name (and path) for output clean image, psf, etc.
    :param indirection: phase center of the observation
    :param incell: pixel scale of the model/simulated image
    :param frequency: central frequency
    :param inwidth: frequency bandwidth
    :param radec_offset: offset of the Gaussian source from the phasecenter, in arcsec
    :param flux: total flux of the Gaussian source, in solar flux unit (sfu)
    :param majoraxis: FWHM size of the Gaussian source along the major axis
    :param minoraxis: FWHM size of the Gaussian source along the minor axis
    :param positionangle: position angle of the Gaussian source
    :param imsize: size of the model/simulated image, in pixels (x and y)
    :return:
    """

    # set voltage patterns and primary beams for KSC 7 m. This is a placeholder for now (but required for PB correction)
    vp = vptool()
    if len(vp.getvp(telescope='KSC').keys()) == 0:
        vprec = vp.setpbairy(telescope='KSC',
                             dishdiam='{0:.1f}m'.format(dishdiam),
                             blockagediam='0.75m',
                             maxrad='1.784deg',
                             reffreq='1.0GHz',
                             dopb=True)

    # make a Gaussian source
    cl = cltool()
    ia = iatool()
    cl.addcomponent(dir=indirection,
                    flux=flux * 1e4,
                    fluxunit='Jy',
                    freq=frequency,
                    shape="Gaussian",
                    majoraxis=majoraxis,
                    minoraxis=minoraxis,
                    positionangle=positionangle)
    ia.fromshape("Gaussian.im", imsize + [1, 1], overwrite=True)
    cs = ia.coordsys()
    cs.setunits(['rad', 'rad', '', 'Hz'])
    cell_rad = qa.convert(qa.quantity(incell), "rad")['value']
    cs.setincrement([-cell_rad, cell_rad], 'direction')
    ra_ref = qa.toangle(indirection.split(' ')[1])
    dec_ref = qa.toangle(indirection.split(' ')[2])
    ra = ra_ref['value'] - radec_offset[0] / 3600.
    dec = dec_ref['value'] - radec_offset[1] / 3600.
    cs.setreferencevalue([
        qa.convert('{0:.4f}deg'.format(ra), 'rad')['value'],
        qa.convert('{0:.4f}deg'.format(dec), 'rad')['value']
    ],
                         type="direction")
    cs.setreferencevalue("1.0GHz", 'spectral')
    cs.setincrement('10MHz', 'spectral')
    ia.setcoordsys(cs.torecord())
    ia.setbrightnessunit("Jy/pixel")
    ia.modify(cl.torecord(), subtract=False)
    ia.close()

    simobserve(project=projname,
               skymodel='Gaussian.im',
               indirection=indirection,
               incell=incell,
               incenter=frequency,
               inwidth=inwidth,
               hourangle='transit',
               refdate='2014/11/01',
               totaltime='120s',
               antennalist=antennalist,
               obsmode='int',
               overwrite=True)

    if not imagename:
        imagename = projname + '/tst'
    tclean(vis=projname + '/' + projname + '.' + antennalist.split('.')[0] +
           '.ms',
           imagename=imagename,
           imsize=imsize,
           cell=incell,
           phasecenter=indirection,
           niter=200,
           interactive=False)

    viewer(projname + '/' + projname + '.' + antennalist.split('.')[0] +
           '.skymodel')
    viewer(imagename + '.psf')
    viewer(imagename + '.image')
Example #10
0
def load_casa_image(filename, skipdata=False,
                    skipvalid=False, skipcs=False, **kwargs):
    """
    Load a cube (into memory?) from a CASA image. By default it will transpose
    the cube into a 'python' order and drop degenerate axes. These options can
    be suppressed. The object holds the coordsys object from the image in
    memory.
    """

    try:
        import casatools
        ia = casatools.image()
    except ImportError:
        try:
            from taskinit import iatool
            ia = iatool()
        except ImportError:
            raise ImportError("Could not import CASA (casac) and therefore cannot read CASA .image files")

    # use the ia tool to get the file contents
    ia.open(filename)

    # read in the data
    if not skipdata:
        # CASA data are apparently transposed.
        data = ia.getchunk().transpose()

    # CASA stores validity of data as a mask
    if not skipvalid:
        valid = ia.getchunk(getmask=True).transpose()

    # transpose is dealt with within the cube object

    # read in coordinate system object
    casa_cs = ia.coordsys()

    wcs = wcs_casa2astropy(casa_cs)

    unit = ia.brightnessunit()

    beam_ = ia.restoringbeam()
    if 'major' in beam_:
        beam = Beam(major=u.Quantity(beam_['major']['value'], unit=beam_['major']['unit']),
                    minor=u.Quantity(beam_['minor']['value'], unit=beam_['minor']['unit']),
                    pa=u.Quantity(beam_['positionangle']['value'], unit=beam_['positionangle']['unit']),
                   )
    elif 'beams' in beam_:
        bdict = beam_['beams']
        if beam_['nStokes'] > 1:
            raise NotImplementedError()
        nbeams = len(bdict)
        assert nbeams == beam_['nChannels']
        stokesidx = '*0'

        majors = [u.Quantity(bdict['*{0}'.format(ii)][stokesidx]['major']['value'],
                             bdict['*{0}'.format(ii)][stokesidx]['major']['unit']) for ii in range(nbeams)]
        minors = [u.Quantity(bdict['*{0}'.format(ii)][stokesidx]['minor']['value'],
                             bdict['*{0}'.format(ii)][stokesidx]['minor']['unit']) for ii in range(nbeams)]
        pas = [u.Quantity(bdict['*{0}'.format(ii)][stokesidx]['positionangle']['value'],
                          bdict['*{0}'.format(ii)][stokesidx]['positionangle']['unit']) for ii in range(nbeams)]

        beams = Beams(major=u.Quantity(majors),
                      minor=u.Quantity(minors),
                      pa=u.Quantity(pas))
    else:
        warnings.warn("No beam information found in CASA image.",
                      BeamWarning)


    # don't need this yet
    # stokes = get_casa_axis(temp_cs, wanttype="Stokes", skipdeg=False,)

    #    if stokes == None:
    #        order = np.arange(self.data.ndim)
    #    else:
    #        order = []
    #        for ax in np.arange(self.data.ndim+1):
    #            if ax == stokes:
    #                continue
    #            order.append(ax)

    #    self.casa_cs = ia.coordsys(order)

        # This should work, but coordsys.reorder() has a bug
        # on the error checking. JIRA filed. Until then the
        # axes will be reversed from the original.

        # if transpose == True:
        #    new_order = np.arange(self.data.ndim)
        #    new_order = new_order[-1*np.arange(self.data.ndim)-1]
        #    print new_order
        #    self.casa_cs.reorder(new_order)

    # close the ia tool
    ia.close()

    meta = {'filename': filename,
            'BUNIT': unit}


    if wcs.naxis == 3:
        mask = BooleanArrayMask(np.logical_not(valid), wcs)
        if 'beam' in locals():
            cube = SpectralCube(data, wcs, mask, meta=meta, beam=beam)
        elif 'beams' in locals():
            cube = VaryingResolutionSpectralCube(data, wcs, mask, meta=meta, beams=beams)
        else:
            cube = SpectralCube(data, wcs, mask, meta=meta)
        # we've already loaded the cube into memory because of CASA
        # limitations, so there's no reason to disallow operations
        cube.allow_huge_operations = True

    elif wcs.naxis == 4:
        data, wcs = cube_utils._split_stokes(data, wcs)
        mask = {}
        for component in data:
            data_, wcs_slice = cube_utils._orient(data[component], wcs)
            mask[component] = LazyMask(np.isfinite, data=data[component],
                                       wcs=wcs_slice)

            if 'beam' in locals():
                data[component] = SpectralCube(data_, wcs_slice, mask[component],
                                               meta=meta, beam=beam)
            elif 'beams' in locals():
                data[component] = VaryingResolutionSpectralCube(data_,
                                                                wcs_slice,
                                                                mask[component],
                                                                meta=meta,
                                                                beams=beams)
            else:
                data[component] = SpectralCube(data_, wcs_slice, mask[component],
                                               meta=meta)

            data[component].allow_huge_operations = True


        cube = StokesSpectralCube(stokes_data=data)

    return cube
Example #11
0
def imfit_iter(imgfiles, doreg, tims, msinfofile, ephem, box, region, chans,
               stokes, mask, includepix, excludepix, residual, model,
               estimates, logfile, append, newestimates, complist, overwrite,
               dooff, offset, fixoffset, stretch, rms, noisefwhm, summary,
               imidx):
    from taskinit import iatool, rg
    import pdb
    try:
        from astropy.io import fits as pyfits
    except:
        try:
            import pyfits
        except ImportError:
            raise ImportError(
                'Neither astropy nor pyfits exists in this CASA installation')
    img = imgfiles[imidx]

    if doreg:
        # check if ephemfile and msinfofile exist
        if not ephem:
            print("ephemeris info does not exist!")
            return
        if not tims:
            print("timestamp of the image does not exist!")
            return
        try:
            tim = tims[imidx]
            helio = vla_prep.ephem_to_helio(msinfo=msinfofile,
                                            ephem=ephem,
                                            reftime=tim)
            fitsfile = [img.replace('.image', '.fits')]
            vla_prep.imreg(imagefile=[img],
                           fitsfile=fitsfile,
                           helio=helio,
                           toTb=False,
                           scl100=True)
            img = img.replace('.image', '.fits')
        except:
            print 'Failure in vla_prep. Skipping this image file: ' + img

    myia = iatool()
    try:
        if (not myia.open(img)):
            raise Exception, "Cannot create image analysis tool using " + img
        print('Processing image: ' + img)
        hdr = pyfits.getheader(img)
        pols = DButil.polsfromfitsheader(hdr)
        ndx, ndy, nchans, npols = myia.shape()
        results = {}
        for itpp in pols:
            results[itpp] = {}
        for pp, itpp in enumerate(pols):
            r = rg.box(blc=[0, 0, 0, pp], trc=[ndx, ndy, nchans, pp])
            myiapol = myia.subimage(region=r, dropdeg=True)
            results[itpp] = myiapol.fitcomponents(box=box,
                                                  region=region,
                                                  chans=chans,
                                                  stokes=stokes,
                                                  mask=mask,
                                                  includepix=includepix,
                                                  excludepix=excludepix,
                                                  residual=residual,
                                                  model=model,
                                                  estimates=estimates,
                                                  logfile=logfile,
                                                  append=append,
                                                  newestimates=newestimates,
                                                  complist=complist,
                                                  overwrite=overwrite,
                                                  dooff=dooff,
                                                  offset=offset,
                                                  fixoffset=fixoffset,
                                                  stretch=stretch,
                                                  rms=rms,
                                                  noisefwhm=noisefwhm,
                                                  summary=summary)
        # update timestamp

        timstr = hdr['date-obs']
        return [True, timstr, img, results]
    except Exception, instance:
        casalog.post(str('*** Error in imfit ***') + str(instance))
        # raise instance
        return [False, timstr, img, {}]
Example #12
0
    def run(self):
        """ The run method creates the BDP

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        dt = utils.Dtime("CubeSum")  # tagging time
        self._summary = {}  # an ADMIT summary will be created here

        numsigma = self.getkey("numsigma")  # get the input keys
        sigma = self.getkey("sigma")
        use_lines = self.getkey("linesum")
        pad = self.getkey("pad")

        b1 = self._bdp_in[0]  # spw image cube
        b1a = self._bdp_in[1]  # cubestats (optional)
        b1b = self._bdp_in[2]  # linelist  (optional)

        ia = taskinit.iatool()

        f1 = b1.getimagefile(bt.CASA)
        ia.open(self.dir(f1))
        s = ia.summary()
        nchan = s['shape'][2]

        if b1b != None:
            ch0 = b1b.table.getFullColumnByName("startchan")
            ch1 = b1b.table.getFullColumnByName("endchan")
            s = Segments(ch0, ch1, nchan=nchan)
            # @todo something isn't merging here as i would have expected,
            #       e.g. test0.fits [(16, 32), (16, 30), (16, 29)]
            if pad > 0:
                for (c0, c1) in s.getsegmentsastuples():
                    s.append([c0 - pad, c0])
                    s.append([c1, c1 + pad])
            s.merge()
            s.recalcmask()
            # print "PJT segments:",s.getsegmentsastuples()
            ns = len(s.getsegmentsastuples())
            chans = s.chans(not use_lines)
            if use_lines:
                msum = s.getmask()
            else:
                msum = 1 - s.getmask()
            logging.info("Read %d segments" % ns)
            # print "chans",chans
            # print "msum",msum

        #  from a deprecated keyword, but kept here to pre-smooth the spectrum before clipping
        #  examples are:  ['boxcar',3]    ['gaussian',7]    ['hanning',5]
        smooth = []

        sig_const = False  # figure out if sigma is taken as constant in the cube
        if b1a == None:  # if no 2nd BDP was given, sigma needs to be specified
            if sigma <= 0.0:
                raise Exception, "Neither user-supplied sigma nor CubeStats_BDP input given. One is required."
            else:
                sig_const = True  # and is constant
        else:
            if sigma > 0:
                sigma = b1a.get("sigma")
                sig_const = True

        if sig_const:
            logging.info("Using constant sigma = %f" % sigma)
        else:
            logging.info("Using varying sigma per plane")

        infile = b1.getimagefile(bt.CASA)  # ADMIT filename of the image (cube)
        bdp_name = self.mkext(
            infile, 'csm'
        )  # morph to the new output name with replaced extension 'csm'
        image_out = self.dir(bdp_name)  # absolute filename

        args = {
            "imagename": self.dir(infile)
        }  # assemble arguments for immoments()
        args["moments"] = 0  # only need moments=0 (or [0] is ok as well)
        args["outfile"] = image_out  # note full pathname

        dt.tag("start")

        if sig_const:
            args["excludepix"] = [-numsigma * sigma,
                                  numsigma * sigma]  # single global sigma
            if b1b != None:
                # print "PJT: ",chans
                args["chans"] = chans
        else:
            # @todo    in this section bad channels can cause a fully masked cubesum = bad
            # cubestats input
            sigma_array = b1a.table.getColumnByName(
                "sigma")  # channel dependent sigma
            sigma_pos = sigma_array[np.where(sigma_array > 0)]
            smin = sigma_pos.min()
            smax = sigma_pos.max()
            logging.info("sigma varies from %f to %f" % (smin, smax))
            maxval = b1a.get("maxval")  # max in cube
            nzeros = len(np.where(sigma_array <= 0.0)[0])  # check bad channels
            if nzeros > 0:
                logging.warning("There are %d NaN channels " % nzeros)
                # raise Exception,"need to recode CubeSum or use constant sigma"
            dt.tag("grab_sig")

            if len(smooth) > 0:
                # see also LineID and others
                filter = Filter1D.Filter1D(
                    sigma_array, smooth[0],
                    **Filter1D.Filter1D.convertargs(smooth))
                sigma_array = filter.run()
                dt.tag("smooth_sig")
            # create a CASA image copy for making the mirror sigma cube to mask against
            file = self.dir(infile)
            mask = file + "_mask"
            ia.fromimage(infile=file, outfile=mask)
            nx = ia.shape()[0]
            ny = ia.shape()[1]
            nchan = ia.shape()[2]
            ia.fromshape(shape=[nx, ny, 1])
            plane = ia.getchunk(
                [0, 0, 0],
                [-1, -1, 0])  # convenience plane for masking operation
            dt.tag("mask_sig")

            ia.open(mask)
            dt.tag("open_mask")

            count = 0
            for i in range(nchan):
                if sigma_array[i] > 0:
                    if b1b != None:
                        if msum[i]:
                            ia.putchunk(plane * 0 + sigma_array[i],
                                        blc=[0, 0, i, -1])
                            count = count + 1
                        else:
                            ia.putchunk(plane * 0 + maxval, blc=[0, 0, i, -1])
                    else:
                        ia.putchunk(plane * 0 + sigma_array[i],
                                    blc=[0, 0, i, -1])
                        count = count + 1
                else:
                    ia.putchunk(plane * 0 + maxval, blc=[0, 0, i, -1])
            ia.close()
            logging.info("%d/%d channels used for CubeSum" % (count, nchan))
            dt.tag("close_mask")

            names = [file, mask]
            tmp = file + '.tmp'
            if numsigma == 0.0:
                # hopefully this will also make use of the mask
                exp = "IM0[IM1<%f]" % (0.99 * maxval)
            else:
                exp = "IM0[abs(IM0/IM1)>%f]" % (numsigma)
            # print "PJT: exp",exp
            casa.immath(mode='evalexpr',
                        imagename=names,
                        expr=exp,
                        outfile=tmp)
            args["imagename"] = tmp
            dt.tag("immath")

        if nchan == 1:
            logging.info("Assumed continuum - see issue 34")
            args[
                "moments"] = -1  # force continuum   @todo   should we force this via a keyword?
        casa.immoments(**args)
        dt.tag("immoments")

        if sig_const is False:
            # get rid of temporary files
            utils.remove(tmp)
            utils.remove(mask)

        # get the flux
        ia.open(image_out)
        st = ia.statistics()
        ia.close()
        dt.tag("statistics")
        # report that flux, but there's no way to get the units from casa it seems
        # ia.summary()['unit'] is usually 'Jy/beam.km/s' for ALMA
        # imstat() does seem to know it.
        if st.has_key('flux'):
            rdata = [st['flux'][0], st['sum'][0]]
            logging.info("Total flux: %f (sum=%f)" % (st['flux'], st['sum']))
        else:
            rdata = [st['sum'][0]]
            logging.info("Sum: %f (beam parameters missing)" % (st['sum']))
        logging.regression("CSM: %s" % str(rdata))

        # Create two output images for html and their thumbnails, too
        implot = ImPlot(ptype=self._plot_type,
                        pmode=self._plot_mode,
                        abspath=self.dir())
        implot.plotter(rasterfile=bdp_name,
                       figname=bdp_name,
                       colorwedge=True,
                       zoom=self.getkey("zoom"))
        figname = implot.getFigure(figno=implot.figno, relative=True)
        thumbname = implot.getThumbnail(figno=implot.figno, relative=True)

        dt.tag("implot")

        thumbtype = bt.PNG  # really should be correlated with self._plot_type!!

        # 2. Create a histogram of the map data
        # get the data for a histogram
        data = casautil.getdata(image_out, zeromask=True).compressed()
        dt.tag("getdata")

        # get the label for the x axis
        bunit = casa.imhead(imagename=image_out, mode="get", hdkey="bunit")

        # Make the histogram plot
        # Since we give abspath in the constructor, figname should be relative
        myplot = APlot(ptype=self._plot_type,
                       pmode=self._plot_mode,
                       abspath=self.dir())
        auxname = bdp_name + "_histo"
        auxtype = bt.PNG  # really should be correlated with self._plot_type!!
        myplot.histogram(columns=data,
                         figname=auxname,
                         xlab=bunit,
                         ylab="Count",
                         title="Histogram of CubeSum: %s" % (bdp_name),
                         thumbnail=True)
        auxname = myplot.getFigure(figno=myplot.figno, relative=True)
        auxthumb = myplot.getThumbnail(figno=myplot.figno, relative=True)

        images = {bt.CASA: bdp_name, bt.PNG: figname}
        casaimage = Image(images=images,
                          auxiliary=auxname,
                          auxtype=auxtype,
                          thumbnail=thumbname,
                          thumbnailtype=thumbtype)

        if hasattr(b1, "line"):  # SpwCube doesn't have Line
            line = deepcopy(getattr(b1, "line"))
            if type(line) != type(Line):
                line = Line(name="Undetermined")
        else:
            line = Line(name="Undetermined")  # fake a Line if there wasn't one

        self.addoutput(
            Moment_BDP(xmlFile=bdp_name,
                       moment=0,
                       image=deepcopy(casaimage),
                       line=line))
        imcaption = "Integral (moment 0) of all emission in image cube"
        auxcaption = "Histogram of cube sum for image cube"
        taskargs = "numsigma=%.1f sigma=%g smooth=%s" % (numsigma, sigma,
                                                         str(smooth))
        self._summary["cubesum"] = SummaryEntry([
            figname, thumbname, imcaption, auxname, auxthumb, auxcaption,
            bdp_name, infile
        ], "CubeSum_AT", self.id(True), taskargs)

        ia.done()

        dt.tag("done")
        dt.end()
Example #13
0
    def run(self):
        """ The run method creates the BDP

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        self._summary = {}
        dt = utils.Dtime("Smooth")
        dt.tag("start")
        # get the input keys
        bmaj = self.getkey("bmaj")
        bmin = self.getkey("bmin")
        bpa = self.getkey("bpa")
        velres = self.getkey("velres")

        # take care of potential issues in the unit strings
        # @todo  if not provided?
        bmaj['unit'] = bmaj['unit'].lower()
        bmin['unit'] = bmin['unit'].lower()
        velres['unit'] = velres['unit'].lower()
        taskargs = "bmaj=%s bmin=%s bpa=%s velres=%s" % (bmaj, bmin, bpa,
                                                         velres)

        ia = taskinit.iatool()
        qa = taskinit.qatool()

        bdpnames = []
        for ibdp in self._bdp_in:
            istem = ibdp.getimagefile(bt.CASA)
            image_in = ibdp.baseDir() + istem

            bdp_name = self.mkext(istem, 'sim')
            image_out = self.dir(bdp_name)

            ia.open(image_in)
            h = casa.imhead(image_in, mode='list')
            pix_scale = np.abs(h['cdelt1'] *
                               206265.0)  # pix scale in asec @todo QA ?
            CC = 299792458.0  # speed of light  @todo somewhere else   [utils.c , but in km/s]

            rest_freq = h['crval3']
            # frequency pixel scale in km/s
            vel_scale = np.abs(CC * h['cdelt3'] / rest_freq / 1000.0)

            # unit conversion to arcsec (spatial) or km/s
            # (velocity) or some flavor of Hz.

            if (bmaj['unit'] == 'pixel'):
                bmaj = bmaj['value'] * pix_scale
            else:
                bmaj = bmaj['value']
            if (bmin['unit'] == 'pixel'):
                bmin = bmin['value'] * pix_scale
            else:
                bmin = bmin['value']

            hertz_input = False
            if velres['unit'] == 'pixel':
                velres['value'] = velres['value'] * vel_scale
                velres['unit'] = 'km/s'
            elif velres['unit'] == 'm/s':
                velres['value'] = velres['value'] / 1000.0
                velres['unit'] = 'km/s'
            elif velres['unit'][-2:] == 'hz':
                hertz_input = True
            elif velres['unit'] == 'km/s':
                pass
            else:
                logging.error("Unknown units in velres=%s" % velres['unit'])

            rdata = bmaj

            # we smooth in velocity first. if smoothing in velocity
            # the cube apparently must be closed afterwards and
            # then reopened if spatial smoothing is to be done.

            if velres['value'] > 0:
                # handle the different units allowed. CASA doesn't
                # like lowercase for hz units...
                if not hertz_input:
                    freq_res = str(
                        velres['value'] * 1000.0 / CC * rest_freq) + 'Hz'
                else:
                    freq_res = str(velres['value'])
                    # try to convert velres to km/s for debug purposes
                    velres['value'] = velres['value'] / rest_freq * CC / 1000.0
                    if (velres['unit'] == 'khz'):
                        velres['value'] = velres['value'] * 1000.0
                        velres['unit'] = 'kHz'
                    elif (velres['unit'] == 'mhz'):
                        velres['value'] = velres['value'] * 1E6
                        velres['unit'] = 'MHz'
                    elif (velres['unit'] == 'ghz'):
                        velres['value'] = velres['value'] * 1E9
                        velres['unit'] = 'GHz'
                    freq_res = freq_res + velres['unit']

                # NB: there is apparently a bug in CASA. only smoothing along the frequency
                # axis does not work. sepconvolve gives a unit error (says axis unit is radian rather
                # than Hz). MUST smooth in 2+ dimensions if you want this to work.

                if (velres['value'] < vel_scale):
                    raise Exception, "Desired velocity resolution %g less than pixel scale %g" % (
                        velres['value'], vel_scale)
                image_tmp = self.dir('tmp.smooth')
                im2=ia.sepconvolve(outfile=image_tmp,axes=[0,1,2], types=["boxcar","boxcar","gauss"],\
                                              widths=['1pix','1pix',freq_res], overwrite=True)
                im2.done()
                logging.debug("sepconvolve to %s" % image_out)
                # for some reason, doing this in memory does not seem to work, so outfile must be specified.

                logging.info(
                    "Smoothing cube to a velocity resolution of %s km/s" %
                    str(velres['value']))
                logging.info("Smoothing cube to a frequency resolution of %s" %
                             freq_res)
                ia.close()
                ia.open(image_tmp)
                dt.tag("sepconvolve")
            else:
                image_tmp = image_out

            # now do the spatial smoothing

            convolve_to_min_beam = True  # default is to convolve to a min enclosing beam

            if bmaj > 0 and bmin > 0:
                # form qa objects out of these so that casa can understand
                bmaj = qa.quantity(bmaj, 'arcsec')
                bmin = qa.quantity(bmin, 'arcsec')
                bpa = qa.quantity(bpa, 'deg')

                target_res = {}
                target_res['major'] = bmaj
                target_res['minor'] = bmin
                target_res['positionangle'] = bpa

                # throw an exception if cannot be convolved

                try:
                    # for whatever reason, if you give convolve2d a beam parameter,
                    # it complains ...
                    im2=ia.convolve2d(outfile=image_out,major = bmaj,\
                                             minor = bmin, pa = bpa,\
                                             targetres=True,overwrite=True)
                    im2.done()
                    logging.info(
                        "Smoothing cube to a resolution of %s by %s at a PA of %s"
                        % (str(bmaj['value']), str(
                            bmin['value']), str(bpa['value'])))
                    convolve_to_min_beam = False
                    achieved_res = target_res
                except:
                    # @todo   remind what you need ?
                    logging.error("Warning: Could not convolve to requested resolution of "\
                            +str(bmaj['value']) + " by " + str(bmin['value']) + \
                            " at a PA of "+ str(bpa['value']))
                    raise Exception, "Could not convolve to beam given!"
            dt.tag("convolve2d-1")

            if convolve_to_min_beam:
                restoring_beams = ia.restoringbeam()
                commonbeam = ia.commonbeam()
                # for whatever reason, setrestoringbeam does not use the same set of hashes...
                commonbeam['positionangle'] = commonbeam['pa']
                del commonbeam['pa']

                # if there's one beam, apparently the beams keyword does not exist
                if 'beams' in restoring_beams:
                    print "Smoothing cube to a resolution of "+  \
                         str(commonbeam['major']['value']) +" by "+ \
                         str(commonbeam['minor']['value'])+" at a PA of "\
                        +str(commonbeam['pa']['value'])
                    target_res = commonbeam
                    im2=ia.convolve2d(outfile=image_out,major=commonbeam['major'],\
                                               minor=commonbeam['minor'],\
                                               pa=commonbeam['positionangle'],\
                                               targetres=True,overwrite=True)
                    im2.done()
                    achieved_res = commonbeam
                    dt.tag("convolve2d-2")
                else:
                    print "One beam for all planes. Smoothing to common beam redundant."
                    achieved_res = commonbeam
                    if velres['value'] < 0:
                        ia.fromimage(outfile=image_out, infile=image_in)
                    # not really doing anything
                # else, we've already done what we needed to

                ia.setrestoringbeam(beam=achieved_res)
                rdata = achieved_res['major']['value']

            # else do no smoothing and just close the image

            ia.close()
            dt.tag("close")

            b1 = SpwCube_BDP(bdp_name)
            self.addoutput(b1)
            # need to update for multiple images.

            b1.setkey("image", Image(images={bt.CASA: bdp_name}))

            bdpnames = bdpnames.append(bdp_name)

            # and clean up the temp image before the next image
            if velres['value'] > 0:
                utils.remove(image_tmp)

        # thes are task arguments not summary entries.
        _bmaj = qa.convert(achieved_res['major'], 'rad')['value']
        _bmin = qa.convert(achieved_res['minor'], 'rad')['value']
        _bpa = qa.convert(achieved_res['positionangle'], 'deg')['value']
        vres = "%.2f %s" % (velres['value'], velres['unit'])

        logging.regression("SMOOTH: %f %f" % (rdata, velres['value']))

        self._summary["smooth"] = SummaryEntry(
            [bdp_name, convolve_to_min_beam, _bmaj, _bmin, _bpa, vres],
            "Smooth_AT", self.id(True), taskargs)
        dt.tag("done")
        dt.end()
Example #14
0
def load_casa_image(filename,
                    skipdata=False,
                    skipvalid=False,
                    skipcs=False,
                    target_cls=None,
                    **kwargs):
    """
    Load a cube (into memory?) from a CASA image. By default it will transpose
    the cube into a 'python' order and drop degenerate axes. These options can
    be suppressed. The object holds the coordsys object from the image in
    memory.
    """

    from .core import StringWrapper
    if isinstance(filename, StringWrapper):
        filename = filename.value

    try:
        import casatools
        iatool = casatools.image
    except ImportError:
        try:
            from taskinit import iatool
        except ImportError:
            raise ImportError(
                "Could not import CASA (casac) and therefore cannot read CASA .image files"
            )

    ia = iatool()

    # use the ia tool to get the file contents
    try:
        ia.open(filename, cache=False)
    except AssertionError as ex:
        if 'must be of cReqPath type' in str(ex):
            raise IOError("File {0} not found.  Error was: {1}".format(
                filename, str(ex)))
        else:
            raise ex

    # read in the data
    if not skipdata:
        arrdata = ArraylikeCasaData(filename)
        # CASA data are apparently transposed.
        data = dask.array.from_array(arrdata,
                                     chunks=arrdata.chunksize,
                                     name=filename)

    # CASA stores validity of data as a mask
    if not skipvalid:
        boolarr = ArraylikeCasaData(filename, ia_kwargs={'getmask': True})
        valid = dask.array.from_array(boolarr,
                                      chunks=boolarr.chunksize,
                                      name=filename + ".mask")

    # transpose is dealt with within the cube object

    # read in coordinate system object
    casa_cs = ia.coordsys()

    unit = ia.brightnessunit()

    beam_ = ia.restoringbeam()

    ia.done()
    ia.close()

    wcs = wcs_casa2astropy(ia, casa_cs)

    del casa_cs
    del ia

    if 'major' in beam_:
        beam = Beam(
            major=u.Quantity(beam_['major']['value'],
                             unit=beam_['major']['unit']),
            minor=u.Quantity(beam_['minor']['value'],
                             unit=beam_['minor']['unit']),
            pa=u.Quantity(beam_['positionangle']['value'],
                          unit=beam_['positionangle']['unit']),
        )
    elif 'beams' in beam_:
        bdict = beam_['beams']
        if beam_['nStokes'] > 1:
            raise NotImplementedError()
        nbeams = len(bdict)
        assert nbeams == beam_['nChannels']
        stokesidx = '*0'

        majors = [
            u.Quantity(bdict['*{0}'.format(ii)][stokesidx]['major']['value'],
                       bdict['*{0}'.format(ii)][stokesidx]['major']['unit'])
            for ii in range(nbeams)
        ]
        minors = [
            u.Quantity(bdict['*{0}'.format(ii)][stokesidx]['minor']['value'],
                       bdict['*{0}'.format(ii)][stokesidx]['minor']['unit'])
            for ii in range(nbeams)
        ]
        pas = [
            u.Quantity(
                bdict['*{0}'.format(ii)][stokesidx]['positionangle']['value'],
                bdict['*{0}'.format(ii)][stokesidx]['positionangle']['unit'])
            for ii in range(nbeams)
        ]

        beams = Beams(major=u.Quantity(majors),
                      minor=u.Quantity(minors),
                      pa=u.Quantity(pas))
    else:
        warnings.warn("No beam information found in CASA image.", BeamWarning)

    # don't need this yet
    # stokes = get_casa_axis(temp_cs, wanttype="Stokes", skipdeg=False,)

    #    if stokes == None:
    #        order = np.arange(self.data.ndim)
    #    else:
    #        order = []
    #        for ax in np.arange(self.data.ndim+1):
    #            if ax == stokes:
    #                continue
    #            order.append(ax)

    #    self.casa_cs = ia.coordsys(order)

    # This should work, but coordsys.reorder() has a bug
    # on the error checking. JIRA filed. Until then the
    # axes will be reversed from the original.

    # if transpose == True:
    #    new_order = np.arange(self.data.ndim)
    #    new_order = new_order[-1*np.arange(self.data.ndim)-1]
    #    print new_order
    #    self.casa_cs.reorder(new_order)

    meta = {'filename': filename, 'BUNIT': unit}

    if wcs.naxis == 3:
        data, wcs_slice = cube_utils._orient(data, wcs)
        valid, _ = cube_utils._orient(valid, wcs)

        mask = BooleanArrayMask(valid, wcs_slice)
        if 'beam' in locals():
            cube = SpectralCube(data, wcs_slice, mask, meta=meta, beam=beam)
        elif 'beams' in locals():
            cube = VaryingResolutionSpectralCube(data,
                                                 wcs_slice,
                                                 mask,
                                                 meta=meta,
                                                 beams=beams)
        else:
            cube = SpectralCube(data, wcs_slice, mask, meta=meta)
        # with #592, this is no longer true
        # we've already loaded the cube into memory because of CASA
        # limitations, so there's no reason to disallow operations
        # cube.allow_huge_operations = True
        assert cube.mask.shape == cube.shape

    elif wcs.naxis == 4:
        valid, _ = cube_utils._split_stokes(valid, wcs)
        data, wcs = cube_utils._split_stokes(data, wcs)
        mask = {}
        for component in data:
            data_, wcs_slice = cube_utils._orient(data[component], wcs)
            valid_, _ = cube_utils._orient(valid[component], wcs)
            mask[component] = BooleanArrayMask(valid_, wcs_slice)

            if 'beam' in locals():
                data[component] = SpectralCube(data_,
                                               wcs_slice,
                                               mask[component],
                                               meta=meta,
                                               beam=beam)
            elif 'beams' in locals():
                data[component] = VaryingResolutionSpectralCube(
                    data_, wcs_slice, mask[component], meta=meta, beams=beams)
            else:
                data[component] = SpectralCube(data_,
                                               wcs_slice,
                                               mask[component],
                                               meta=meta)

            data[component].allow_huge_operations = True

        cube = StokesSpectralCube(stokes_data=data)
        assert cube.I.mask.shape == cube.shape
        assert wcs_utils.check_equality(cube.I.mask._wcs, cube.wcs)
    else:
        raise ValueError("CASA image has {0} dimensions, and therefore "
                         "is not readable by spectral-cube.".format(wcs.naxis))

    from .core import normalize_cube_stokes
    return normalize_cube_stokes(cube, target_cls=target_cls)
Example #15
0
def getbeam(imagefile=None, beamfile=None):
    ia = iatool()
    if not imagefile:
        raise ValueError, 'Please specify input images'
    bmaj = []
    bmin = []
    bpa = []
    beamunit = []
    bpaunit = []
    chans = []
    nimg = len(imagefile)
    for n in range(nimg):
        img = imagefile[n]
        if not os.path.exists(img):
            raise ValueError, 'The input image does not exist!'
        ia.open(img)
        sum = ia.summary()
        bmaj_ = []
        bmin_ = []
        bpa_ = []
        if sum.has_key('perplanebeams'):  # beam vary with frequency
            nbeams = sum['perplanebeams']['nChannels']
            beams = sum['perplanebeams']['beams']
            chans_ = [key[1:] for key in beams.keys()]
            chans_.sort(key=float)
            for chan in chans_:
                bmaj0 = beams['*' + chan]['*0']['major']['value']
                bmaj_.append(bmaj0)
                bmin0 = beams['*' + chan]['*0']['minor']['value']
                bmin_.append(bmin0)
                bpa0 = beams['*' + chan]['*0']['positionangle']['value']
                bpa_.append(bpa0)
            beamunit_ = beams['*' + chans_[0]]['*0']['major']['unit']
            bpaunit_ = beams['*' + chans_[0]]['*0']['positionangle']['unit']
        if sum.has_key('restoringbeam'):  # only one beam
            bmaj_.append(sum['restoringbeam']['major']['value'])
            bmin_.append(sum['restoringbeam']['minor']['value'])
            bpa_.append(sum['restoringbeam']['positionangle']['value'])
            beamunit_ = sum['restoringbeam']['major']['unit']
            bpaunit_ = sum['restoringbeam']['positionangle']['unit']
            nbeams = 1
            chans_ = [0]

        bmaj.append(bmaj_)
        bmin.append(bmin_)
        bpa.append(bpa_)
        beamunit.append(beamunit_)
        bpaunit.append(bpaunit_)
        chans.append(chans_)
    if beamfile:  # write beams to ascii file
        print 'Writing beam info to ascii file...'
        f = open(beamfile, 'w')
        f.write('CHANNEL No., BMAJ (' + beamunit[0] + '), BMIN (' +
                beamunit[0] + '), BPA (' + bpaunit[0] + ')')
        f.write("\n")
        for n in range(nimg):
            f.write('----For image: ' + imagefile[n] + '----')
            f.write('\n')
            chans_ = chans[n]
            for i in range(len(chans_)):
                f.write(
                    str(chans_[i]) + ', ' + str(bmaj[n][i]) + ', ' +
                    str(bmin[n][i]) + ', ' + str(bpa[n][i]))
                f.write("\n")
        f.close()
    return bmaj, bmin, bpa, beamunit, bpaunit
Example #16
0
    def run(self):
        """ The run method creates the BDP

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        self._summary = {}
        dt = utils.Dtime("Regrid")
        dt.tag("start")

        do_spatial_regrid = self.getkey("do_spatial_regrid")
        pix_scale = self.getkey("pix_scale")
        do_freq_regrid = self.getkey("do_freq_regrid")
        chan_width = self.getkey("chan_width")

        pix_size = []
        chan_size = []
        im_size = []
        pix_wc_x = []
        pix_wc_y = []
        pix_wc_nu = []
        src_dec = []

        RADPERARCSEC = 4.848137E-6

        ia = taskinit.iatool()

        for ibdp in self._bdp_in:
            # Convert input CASA images to numpy arrays.
            istem = ibdp.getimagefile(bt.CASA)
            ifile = ibdp.baseDir() + istem

            h = casa.imhead(ifile, mode='list')
            pix_size.append(np.abs(
                h['cdelt1']))  # pix scale in asec @todo QA ?
            chan_size.append(np.abs(h['cdelt3']))
            # grab the pixels
            pix_x = h['shape'][0]
            pix_y = h['shape'][1]
            pix_nu = h['shape'][2]

            ia.open(ifile)
            mycs = ia.coordsys(axes=[0, 1, 2])
            #           getting all four corners handles the case of images where
            #           x-y axis not aligned with RA-dec
            for xpix in [0, pix_x]:
                for ypix in [0, pix_y]:
                    x = mycs.toworld([xpix, ypix])['numeric'][0]
                    y = mycs.toworld([xpix, ypix])['numeric'][1]
                    pix_wc_x.append(x)
                    pix_wc_y.append(y)

            nu = mycs.toworld([pix_x, pix_y, 0])['numeric'][2]
            pix_wc_nu.append(nu)
            nu = mycs.toworld([pix_x, pix_y, pix_nu])['numeric'][2]
            pix_wc_nu.append(nu)
            ia.close()

        min_ra = np.min(pix_wc_x)
        max_ra = np.max(pix_wc_x)
        min_dec = np.min(pix_wc_y)
        max_dec = np.max(pix_wc_y)
        mean_ra = 0.5 * (min_ra + max_ra)
        mean_dec = 0.5 * (min_dec + max_dec)

        if (pix_scale < 0):
            pix_scale = np.min(pix_size)
        else:
            pix_scale = pix_scale * RADPERARCSEC

        npix_ra = int((max_ra - min_ra) / pix_scale * np.cos(mean_dec))
        npix_dec = (max_dec - min_dec) / pix_scale
        npix_dec = (int(npix_dec)
                    if npix_dec == int(npix_dec) else int(npix_dec) + 1)
        min_nu = np.min(pix_wc_nu)
        max_nu = np.max(pix_wc_nu)
        mean_nu = 0.5 * (min_nu + max_nu)
        if (chan_width < 0):
            chan_width = np.min(chan_size)
        npix_nu = int((max_nu - min_nu) / chan_width) + 1

        # now regrid everything
        innames = []
        outnames = []
        incdelt = []
        outcdelt = []

        #=========================================================
        #@todo - check if bdp_ins refer to same input file.
        # If so, the current code will fail because the output
        # file name is fixed to $INPUT_regrid.  A valid use case
        # is "regrid the same input file different ways" -- which
        # is not supported in the current code but should be
        #=========================================================
        for ibdp in self._bdp_in:
            istem = ibdp.getimagefile(bt.CASA)
            ifile = ibdp.baseDir() + istem
            ostem = "%s_regrid/" % (istem)
            ofile = self.baseDir() + ostem
            # save the input/output file names
            innames.append(istem)
            outnames.append(ostem)

            header = casa.imregrid(imagename=ifile, template='get')
            # save the input cdelt1,2,3 for summary table
            incdelt.append(
                (header['csys']['direction0']['cdelt'][0] / RADPERARCSEC,
                 header['csys']['direction0']['cdelt'][1] / RADPERARCSEC,
                 utils.freqtovel(mean_nu,
                                 header['csys']['spectral2']['wcs']['cdelt'])))

            if (do_spatial_regrid):
                header['csys']['direction0']['cdelt'] = [
                    -1 * pix_scale, pix_scale
                ]
                header['csys']['direction0']['crval'] = [mean_ra, mean_dec]
                header['shap'][0] = npix_ra
                header['shap'][1] = npix_dec
                header['csys']['direction0']['crpix'] = [
                    npix_ra / 2, npix_dec / 2
                ]

            if (do_freq_regrid):
                header['csys']['spectral2']['wcs']['crval'] = min_nu
                header['shap'][2] = npix_nu
                chan_size = np.abs(header['csys']['spectral2']['wcs']['cdelt'])
                header['csys']['spectral2']['wcs']['cdelt'] = chan_width
                flux_correction = chan_width / chan_size

            casa.imregrid(imagename=ifile, output=ofile, template=header)
            # save the output cdelt1,2,3 for summary table
            newhead = casa.imregrid(imagename=ofile, template='get')
            outcdelt.append(
                (newhead['csys']['direction0']['cdelt'][0] / RADPERARCSEC,
                 newhead['csys']['direction0']['cdelt'][1] / RADPERARCSEC,
                 utils.freqtovel(
                     mean_nu, newhead['csys']['spectral2']['wcs']['cdelt'])))

            if (do_freq_regrid):
                ia.open(ofile)
                print flux_correction
                ia.calc(pixels=ofile.replace(r"/", r"\/") + '*' +
                        str(flux_correction))
                ia.done()
            obdp = admit.SpwCube_BDP(ostem)
            self.addoutput(obdp)

        # make a table for summary
        atable = admit.util.Table()
        atable.columns = [
            "Input Image", "cdelt1", "cdelt2", "cdelt3", "Regridded Image",
            "cdelt1", "cdelt2", "cdelt3"
        ]
        atable.units = [
            "", "arcsec", "arcsec", "km/s", "", "arcsec", "arcsec", "km/s"
        ]
        for i in range(len(innames)):
            atable.addRow([
                innames[i], incdelt[i][0], incdelt[i][1], incdelt[i][2],
                outnames[i], outcdelt[i][0], outcdelt[i][1], outcdelt[i][2]
            ])

        #keys = "pixsize=%.4g naxis1=%d naxis2=%d mean_ra=%0.4f mean_dec=%0.4f reffreq=%g chan_width=%g" % (pix_scale/(4.848137E-6), npix_ra, npix_dec, mean_ra,mean_dec,min_nu,chan_width)
        taskargs = "pix_scale = " + str(
            self.getkey("pix_scale")) + " chan_width = " + str(
                self.getkey("chan_width"))
        self._summary["regrid"] = SummaryEntry(atable.serialize(), "Regrid_AT",
                                               self.id(True), taskargs)
        dt.tag("done")
        dt.end()
Example #17
0
def load_casa_image(filename, skipdata=False,
                    skipvalid=False, skipcs=False, **kwargs):
    """
    Load a cube (into memory?) from a CASA image. By default it will transpose
    the cube into a 'python' order and drop degenerate axes. These options can
    be suppressed. The object holds the coordsys object from the image in
    memory.
    """

    try:
        from taskinit import iatool
        ia = iatool()
    except ImportError:
        raise ImportError("Could not import CASA (casac) and therefore cannot read CASA .image files")

    # use the ia tool to get the file contents
    ia.open(filename)

    # read in the data
    if not skipdata:
        data = ia.getchunk().reshape(ia.shape())

    # CASA stores validity of data as a mask
    if not skipvalid:
        valid = ia.getchunk(getmask=True).reshape(ia.shape())

    # transpose is dealt with within the cube object

    # read in coordinate system object
    casa_cs = ia.coordsys()

    wcs = wcs_casa2astropy(casa_cs)

    unit = ia.brightnessunit()

    # don't need this yet
    # stokes = get_casa_axis(temp_cs, wanttype="Stokes", skipdeg=False,)

    #    if stokes == None:
    #        order = np.arange(self.data.ndim)
    #    else:
    #        order = []
    #        for ax in np.arange(self.data.ndim+1):
    #            if ax == stokes:
    #                continue
    #            order.append(ax)

    #    self.casa_cs = ia.coordsys(order)

        # This should work, but coordsys.reorder() has a bug
        # on the error checking. JIRA filed. Until then the
        # axes will be reversed from the original.

        # if transpose == True:
        #    new_order = np.arange(self.data.ndim)
        #    new_order = new_order[-1*np.arange(self.data.ndim)-1]
        #    print new_order
        #    self.casa_cs.reorder(new_order)

    # close the ia tool
    ia.close()

    meta = {'filename': filename,
            'BUNIT': unit}


    if wcs.naxis == 3:
        mask = BooleanArrayMask(np.logical_not(valid), wcs)
        cube = SpectralCube(data, wcs, mask, meta=meta)
        # we've already loaded the cube into memory because of CASA
        # limitations, so there's no reason to disallow operations
        cube.allow_huge_operations = True

    elif wcs.naxis == 4:
        data, wcs = cube_utils._split_stokes(data.T, wcs)
        mask = {}
        for component in data:
            data_, wcs_slice = cube_utils._orient(data[component], wcs)
            mask[component] = LazyMask(np.isfinite, data=data[component],
                                       wcs=wcs_slice)

            data[component] = SpectralCube(data_, wcs_slice, mask[component],
                                           meta=meta)
            data[component].allow_huge_operations = True


        cube = StokesSpectralCube(stokes_data=data)

    return cube
Example #18
0
def make_mask_3d(imagename, thresh, fl=False, useimage=False, pixelmin=0,
                 major=0, minor=0, pixelsize=0, line=False, overwrite_old=True,
                 closing_diameter=6, pbimage=None, myresidual=None,
                 myimage=None, extension='.fullmask',
                 spectral_closing=3):
    """
    Makes a mask on any image you want it to.
    Parameters
    ----------
    imagename : {casa image without file extention}
        Name of image you want to mask, without the file extention.
    thresh : {float}
        Masking thershold in whatever units are using
    fl : {bool}
        If you want to combine the mask with a previous iteration of clean
        (True), if not (i.e. you are using the dirty image) then False.
    useimage : {bool}
        If you want to use the dirty image or the residual for the masking
        (I usually use the residual - so set to False)
    pixelmin : {float}
        Min number of pixels within a masked region to be taken into the final
        mask, i.e. if your beam size is 1arcsec and pixel size is 0.2 arcsec,
        then three beams would be pixelmin = 75
    major : {float}
        beam major axis, in arsec
    minor : {float}
        beam minor axis, in arsec
    pixelsize : {float}
        length of one side of pixel, in arcsec
    line : {bool}
        if the image is a line or continuum
    Returns
    -------
    mask : {ndarray}
        The final mask (hopefully) as the ".fullmask" image
    """

    import os
    from tasks import immath
    import numpy as np
    from scipy import ndimage
    from taskinit import iatool
    ia = iatool()

    mymask = imagename + '.mask'
    if myimage is None:
        myimage = imagename + '.image'
    maskim_nopb = imagename + '{}.nopb'.format(extension)
    maskim = imagename + extension
    threshmask = imagename + '.threshmask'
    if myresidual is None:
        myresidual = imagename + '.residual'
    if pbimage is None:
        pbimage = imagename + '.pb'

    if overwrite_old:
        os.system('rm -rf ' + maskim)
        os.system('rm -rf ' + maskim_nopb)
        os.system('rm -rf ' + threshmask)

    if useimage:
        print 'Using Image'
        immath(imagename=[myimage], outfile=threshmask,
            expr='iif(IM0 > ' + str(thresh) + ',1.0,0.0)')
    else:
        immath(imagename=[myresidual], outfile=threshmask,
            expr='iif(IM0 > ' + str(thresh) + ',1.0,0.0)')

    if fl:
        print 'Combining with previous mask..'
        immath(outfile=maskim_nopb, expr='iif(("' + threshmask + '" + "'
            + mymask + '") > 0.1,1.0,0.0)')
    else:
        print 'Making fresh new mask from image/residual'
        os.system('cp -r ' + threshmask + ' ' + maskim_nopb)

    immath(imagename=[pbimage, maskim_nopb], outfile=maskim,
        expr='iif(IM0 > 0.0, IM1, 0.0)')

    print "Using pixelmin=", pixelmin
    beamarea = (major * minor * np.pi / (4. * np.log(2.))) / (pixelsize**2)
    print 'Beam area', beamarea

    ia.open(maskim)
    mask = ia.getchunk()
    diam = closing_diameter  # Change for large beam dilation
    structure = np.ones((diam, diam))
    dist = ((np.indices((diam, diam)) - (diam - 1) / 2.)**2).sum(axis=0)**0.5
    # circularize the closing element
    structure[dist > diam / 2.] = 0

    if line:
        for k in range(mask.shape[3]):
            mask_temp = mask[:, :, 0, k]
            mask_temp = ndimage.binary_closing(mask_temp, structure=structure)
            labeled, j = ndimage.label(mask_temp)
            myhistogram = ndimage.measurements.histogram(labeled, 0, j + 1,
                                                         j + 1)
            object_slices = ndimage.find_objects(labeled)
            threshold = pixelmin

            for i in range(j):
                if myhistogram[i + 1] < threshold:
                    mask_temp[object_slices[i]] = 0.0

            mask[:, :, 0, k] = mask_temp

        # add an additional closing run, this time with a 3d (4d?) st. element
        structure_3d = np.ones((diam, diam, 1, spectral_closing))
        dist = ((np.indices((diam, diam)) - (diam - 1) / 2.)**2).sum(axis=0)**0.5
        # circularize the closing element
        dist_3d = np.repeat(dist[:, :, None, None], spectral_closing, axis=3)
        structure_3d[dist_3d > diam / 2.] = 0
        mask_closed = ndimage.binary_closing(mask, structure=structure_3d)
    else:
        raise RuntimeError("3D closing operation can only operate on cubes.")

    ia.putchunk(mask_closed.astype(int))
    ia.done()

    print 'Mask created.'
    return maskim
Example #19
0
def imfit_iter(imgfiles, doreg, tims, msinfofile, ephem, box, region, chans,
               stokes, mask, includepix, excludepix, residual, model,
               estimates, logfile, append, newestimates, complist, overwrite,
               dooff, offset, fixoffset, stretch, rms, noisefwhm, summary,
               imidx):
    from taskinit import iatool
    import pdb
    try:
        from astropy.io import fits
    except:
        try:
            import pyfits as fits
        except ImportError:
            raise ImportError(
                'Neither astropy nor pyfits exists in this CASA installation')
    img = imgfiles[imidx]

    if doreg:
        # check if ephemfile and msinfofile exist
        if not ephem:
            print("ephemeris info does not exist!")
            return
        if not tims:
            print("timestamp of the image does not exist!")
            return
        try:
            tim = tims[imidx]
            helio = vla_prep.ephem_to_helio(msinfo=msinfofile,
                                            ephem=ephem,
                                            reftime=tim)
            fitsfile = [img.replace('.image', '.fits')]
            vla_prep.imreg(imagefile=[img],
                           fitsfile=fitsfile,
                           helio=helio,
                           toTb=False,
                           scl100=True)
            img = img.replace('.image', '.fits')
        except:
            print 'Failure in vla_prep. Skipping this image file: ' + img

    myia = iatool()
    try:
        if (not myia.open(img)):
            raise Exception, "Cannot create image analysis tool using " + img
        print('Processing image: ' + img)
        result_dict = myia.fitcomponents(box=box,
                                         region=region,
                                         chans=chans,
                                         stokes=stokes,
                                         mask=mask,
                                         includepix=includepix,
                                         excludepix=excludepix,
                                         residual=residual,
                                         model=model,
                                         estimates=estimates,
                                         logfile=logfile,
                                         append=append,
                                         newestimates=newestimates,
                                         complist=complist,
                                         overwrite=overwrite,
                                         dooff=dooff,
                                         offset=offset,
                                         fixoffset=fixoffset,
                                         stretch=stretch,
                                         rms=rms,
                                         noisefwhm=noisefwhm,
                                         summary=summary)
        # update timestamp
        hdr = fits.getheader(img)
        result_dict['timestamp'] = hdr['date-obs']
        print('I am here after fitcomponents')
        return result_dict
    except Exception, instance:
        casalog.post(str('*** Error in imfit ***') + str(instance), 'SEVERE')
        raise instance
Example #20
0
    def run(self):
        #
        self._summary = {}  # prepare to make a summary here
        dt = utils.Dtime("Ingest")  # timer for debugging

        do_cbeam = True  # enforce a common beam
        #
        pb = self.getkey('pb')
        do_pb = len(pb) > 0
        use_pb = self.getkey("usepb")
        #
        create_mask = self.getkey('mask')  # create a new mask ?
        box = self.getkey("box")  # corners in Z, XY or XYZ
        edge = self.getkey("edge")  # number of edge channels to remove
        restfreq = self.getkey("restfreq")  # < 0 means not activated
        ckms = utils.c  # 299792.458 km/s

        # smooth=  could become deprecated, and/or include a decimation option to make it useful
        #          again, Smooth_AT() does this also , at the cost of an extra cube to store
        smooth = self.getkey("smooth")  #
        #
        vlsr = self.getkey(
            "vlsr")  # see also LineID, where this could be given again

        # first place a fits file in the admit project directory (symlink)
        # this is a bit involved, depending on if an absolute or relative path was
        # give to Ingest_AT(file=)
        fitsfile = self.getkey('file')
        if fitsfile[0] != os.sep:
            fitsfile = os.path.abspath(os.getcwd() + os.sep + fitsfile)
        logging.debug('FILE=%s' % fitsfile)
        if fitsfile[0] != os.sep:
            raise Exception, "Bad file=%s, expected absolute name", fitsfile

        # now determine if it could have been a CASA (or MIRIAD) image already
        # which we'll assume if it's a directory; this is natively supported by CASA
        # but there are tools where if you pass it a FITS or MIRIAD
        # MIRIAD is not recommended for serious work, especially big files, since there
        # is a performance penalty due to tiling.
        file_is_casa = casautil.iscasa(fitsfile)

        loc = fitsfile.rfind(os.sep)  # find the '/'
        ffile0 = fitsfile[loc + 1:]  # basename.fits
        basename = self.getkey(
            'basename')  # (new) basename allowed (allow no dots?)
        if len(basename) == 0:
            basename = ffile0[:ffile0.rfind('.')]  # basename
        logging.info("basename=%s" % basename)
        target = self.dir(ffile0)

        if not os.path.exists(target):
            cmd = 'ln -s "%s" "%s"' % (fitsfile, target)
            logging.debug("CMD: %s" % cmd)
            os.system(cmd)

        readonly = False
        if file_is_casa:
            logging.debug("Assuming input %s is a CASA (or MIRIAD) image" %
                          ffile0)
            bdpfile = self.mkext(basename, "im")
            if bdpfile == ffile0:
                logging.warning(
                    "No selections allowed on CASA image, since no alias was given"
                )
                readonly = True
            b1 = SpwCube_BDP(bdpfile)
            self.addoutput(b1)
            b1.setkey("image", Image(images={bt.CASA: bdpfile}))
            # @todo b2 and PB?
        else:
            # construct the output name and construct the BDP based on the CASA image name
            # this also takes care of the behind the scenes alias= substitution
            bdpfile = self.mkext(basename, "im")
            if bdpfile == basename:
                raise Exception, "basename and bdpfile are the same, Ingest_AT needs a fix for this"
            b1 = SpwCube_BDP(bdpfile)
            self.addoutput(b1)
            if do_pb:
                print "doing the PB"
                bdpfile2 = self.mkext(basename, "pb")
                b2 = Image_BDP(bdpfile2)
                self.addoutput(b2)

        # @todo    we should also set readonly=True if no box, no mask etc. and still an alias
        #          that way it will speed up and not make a copy of the image ?

        # fni and fno are full (abspath) filenames, ready for CASA
        # fni is the same as fitsfile
        fni = self.dir(ffile0)
        fno = self.dir(bdpfile)
        if do_pb: fno2 = self.dir(bdpfile2)
        dt.tag("start")

        ia = taskinit.iatool()
        rg = taskinit.rgtool()

        if file_is_casa:
            ia.open(fni)
        else:
            if do_pb and use_pb:
                # @todo   this needs a fix for the path for pb, only works if abs path is given
                # impbcor(im.fits,pb.fits,out.im,overwrite=True,mode='m')
                if False:
                    # this may seem like a nice shortcut, to have the fits->casa conversion be done
                    # internally in impbcor, but it's a terrible performance for big cubes. (tiling?)
                    # we keep this code here, perhaps at some future time (mpi?) this performs better
                    # @todo fno2
                    impbcor(fni, pb, fno, overwrite=True, mode='m')
                    dt.tag("impbcor-1")
                else:
                    # the better way is to convert FITS->CASA and then call impbcor()
                    # the CPU savings are big, but I/O overhead can still be substantial
                    _pbcor = utils.tmp_file('_pbcor_', '.')
                    _pb = utils.tmp_file('_pb_', '.')
                    ia.fromfits(_pbcor, fni, overwrite=True)
                    ia.fromfits(_pb, pb, overwrite=True)
                    dt.tag("impbcor-1f")
                    if False:
                        impbcor(_pbcor, _pb, fno, overwrite=True, mode='m')
                        # @todo fno2
                        utils.remove(_pbcor)
                        utils.remove(_pb)
                        dt.tag("impbcor-2")
                    else:
                        # immath appears to be even faster (2x in CPU)
                        # https://bugs.nrao.edu/browse/CAS-8299
                        # @todo  this needs to be confirmed that impbcor is now good to go (r36078)
                        casa.immath([_pbcor, _pb], 'evalexpr', fno, 'IM0*IM1')
                        dt.tag("immath")
                        if True:
                            # use the mean of all channels... faster may be to use the middle plane
                            # barf; edge channels can be with fewer subfields in a mosaic
                            ia.open(_pb)
                            s = ia.summary()
                            #ia1=taskinit.ia.moments(moments=[-1],drop=True,outfile=fno2)    # fails for cont maps
                            ia1 = ia.collapse(
                                outfile=fno2, function='mean',
                                axes=2)  # @todo ensure 2=freq axis
                            ia1.done()
                            ia.close()
                            dt.tag("moments")
                        utils.remove(_pbcor)
                        utils.remove(_pb)
                        dt.tag("impbcor-3")
            elif do_pb and not use_pb:
                # cheat case: PB was given, but not meant to be used
                # not implemented yet
                print "cheat case dummy PB not implemented yet"
            else:
                # no PB given
                if False:
                    # re-running this was more consistently faster in wall clock time
                    # note that zeroblanks=True will still keep the mask
                    logging.debug("casa::ia.fromfits(%s) -> %s" %
                                  (fni, bdpfile))
                    ia.fromfits(fno, fni, overwrite=True)

                    #taskinit.ia.fromfits(fno,fni,overwrite=True,zeroblanks=True)
                    dt.tag("fromfits")
                else:
                    # not working to extend 3D yet, but this would solve the impv() 3D problem
                    logging.debug("casa::importfits(%s) -> %s" %
                                  (fni, bdpfile))
                    #casa.importfits(fni,fno,defaultaxes=True,defaultaxesvalues=[None,None,None,'I'])
                    # possible bug: zeroblanks=True has no effect?
                    casa.importfits(fni, fno, zeroblanks=True, overwrite=True)
                    dt.tag("importfits")
            ia.close()
            ia.open(fno)
            if len(smooth) > 0:
                # smooth here, but Smooth_AT is another option
                # here we only allow pixel smoothing
                # spatial: gauss
                # spectral: boxcar/hanning (check for flux conservation)
                #     is the boxcar wrong, not centered, but edged?
                # @todo CASA BUG:  this will loose the object name (and maybe more?) from header, so VLSR lookup fails
                fnos = fno + '.smooth'
                ia.convolve2d(outfile=fnos,
                              overwrite=True,
                              pa='0deg',
                              major='%gpix' % smooth[0],
                              minor='%gpix' % smooth[1],
                              type='gaussian')
                ia.close()
                srcname = casa.imhead(fno, mode="get",
                                      hdkey="object")  # work around CASA bug
                #@todo use safer ia.rename() here.
                # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                utils.rename(fnos, fno)
                casa.imhead(fno, mode="put", hdkey="object",
                            hdvalue=srcname)  # work around CASA bug
                dt.tag("convolve2d")
                if len(smooth) > 2 and smooth[2] > 0:
                    if smooth[2] == 1:
                        # @todo only 1 channel option
                        specsmooth(fno,
                                   fnos,
                                   axis=2,
                                   function='hanning',
                                   dmethod="")
                    else:
                        # @todo may have the wrong center
                        specsmooth(fno,
                                   fnos,
                                   axis=2,
                                   function='boxcar',
                                   dmethod="",
                                   width=smooth[2])
                    #@todo use safer ia.rename() here.
                    # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                    utils.rename(fnos, fno)
                    dt.tag("specsmooth")
                ia.open(fno)

            s = ia.summary()
            if len(s['shape']) != 4:
                logging.warning("Adding dummy STOKES-I axis")
                fnot = fno + '_4'
                ia2 = ia.adddegaxes(stokes='I', outfile=fnot)
                ia.close()
                ia2.close()
                #@todo use safer ia.rename() here.
                # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                utils.rename(fnot, fno)
                ia.open(fno)
                dt.tag("adddegaxes")
            else:
                logging.info("SHAPE: %s" % str(s['shape']))
        s = ia.summary()
        dt.tag("summary-0")
        if s['hasmask'] and create_mask:
            logging.warning(
                "no extra mask created because input image already had one")
            create_mask = False

        # if a box= or edge= was given, only a subset of the cube needs to be ingested
        # this however complicates PB correction later on
        if len(box) > 0 or len(edge) > 0:
            if readonly:
                raise Exception, "Cannot use box= or edge=, data is read-only, or use an basename/alias"
            if len(edge) == 1: edge.append(edge[0])

            nx = s['shape'][0]
            ny = s['shape'][1]
            nz = s['shape'][2]
            logging.info("box=%s edge=%s processing with SHAPE: %s" %
                         (str(box), str(edge), str(s['shape'])))

            if len(box) == 2:
                # select zrange
                if len(edge) > 0:
                    raise Exception, "Cannot use edge= when box=[z1,z2] is used"
                r1 = rg.box([0, 0, box[0]], [nx - 1, ny - 1, box[1]])
            elif len(box) == 4:
                if len(edge) == 0:
                    # select just an XY box
                    r1 = rg.box([box[0], box[1]], [box[2], box[3]])
                elif len(edge) == 2:
                    # select an XY box, but remove some edge channels
                    r1 = rg.box([box[0], box[1], edge[0]],
                                [box[2], box[3], nz - edge[1] - 1])
                else:
                    raise Exception, "Bad edge= for len(box)=4"
            elif len(box) == 6:
                # select an XYZ box
                r1 = rg.box([box[0], box[1], box[2]], [box[3], box[4], box[5]])
            elif len(edge) == 2:
                # remove some edge channels, but keep the whole XY box
                r1 = rg.box([0, 0, edge[0]],
                            [nx - 1, ny - 1, nz - edge[1] - 1])
            else:
                raise Exception, "box=%s illegal" % box
            logging.debug("BOX/EDGE selection: %s %s" %
                          (str(r1['blc']), str(r1['trc'])))
            #if taskinit.ia.isopen(): taskinit.ia.close()

            logging.info("SUBIMAGE")
            subimage = ia.subimage(region=r1,
                                   outfile=fno + '.box',
                                   overwrite=True)
            ia.close()
            ia.done()
            subimage.rename(fno, overwrite=True)
            subimage.close()
            subimage.done()
            ia.open(fno)
            dt.tag("subimage-1")
        else:
            # the whole cube is passed onto ADMIT
            if readonly and create_mask:
                raise Exception, "Cannot use mask=True, data read-only, or use an alias"
            if file_is_casa and not readonly:
                # @todo a miriad file - which should be read only - will also create a useless copy here if no alias used
                ia.subimage(overwrite=True, outfile=fno)
                ia.close()
                ia.open(fno)
                dt.tag("subimage-0")

        if create_mask:
            if readonly:
                raise Exception, "Cannot create mask, data read-only, or use an alias"
            # also check out the 'fromfits::zeroblanks = False'
            # calcmask() will overwrite any previous pixelmask
            #taskinit.ia.calcmask('mask("%s") && "%s" != 0.0' % (fno,fno))
            ia.calcmask('"%s" != 0.0' % fno)
            dt.tag("mask")

        s = ia.summary()
        dt.tag("summary-1")

        # do a fast statistics (no median or robust)
        s0 = ia.statistics()
        dt.tag("statistics")
        if len(s0['npts']) == 0:
            raise Exception, "No statistics possible, are there valid data in this cube?"
        # There may be multiple beams per plane so we can't
        # rely on the BEAM's 'major', 'minor', 'positionangle' being present.
        # ia.commonbeam() is guaranteed to return beam parameters
        # if present
        if do_cbeam and 'perplanebeams' in s:
            # report on the beam extremities, need to loop over all,
            # first and last don't need to be extremes....
            n = s['perplanebeams']['nChannels']
            ab0 = '*0'
            bb0 = s['perplanebeams']['beams'][ab0]['*0']
            bmaj0 = bb0['major']['value']
            bmin0 = bb0['minor']['value']
            beamd = 0.0
            for i in range(n):
                ab1 = '*%d' % i
                bb1 = s['perplanebeams']['beams'][ab1]['*0']
                bmaj1 = bb1['major']['value']
                bmin1 = bb1['minor']['value']
                beamd = max(beamd, abs(bmaj0 - bmaj1), abs(bmin0 - bmin1))
            logging.warning("MAX-BEAMSPREAD %f" % (beamd))
            #
            if True:
                logging.info(
                    "Applying a commonbeam from the median beam accross the band"
                )
                # imhead is a bit slow; alternatively use ia.summary() at the half point for setrestoringbeam()
                h = casa.imhead(fno, mode='list')
                b = h['perplanebeams']['median area beam']
                ia.setrestoringbeam(remove=True)
                ia.setrestoringbeam(beam=b)
                commonbeam = ia.commonbeam()

            else:
                # @todo : this will be VERY slow - code not finished, needs renaming etc.
                #         this is however formally the better solution
                logging.warning("commmonbeam code not finished")
                cb = ia.commonbeam()
                ia.convolve2d(outfile='junk-common.im',
                              major=cb['major'],
                              minor=cb['minor'],
                              pa=cb['pa'],
                              targetres=True,
                              overwrite=True)
                dt.tag('convolve2d')
                commonbeam = {}
        else:
            try:
                commonbeam = ia.commonbeam()
            except:
                nppb = 4.0
                logging.warning(
                    "No synthesized beam found, faking one to prevent downstream problems: nppb=%f"
                    % nppb)
                s = ia.summary()
                cdelt2 = abs(s['incr'][0]) * 180.0 / math.pi * 3600.0
                bmaj = nppb * cdelt2  # use a nominal 4 points per (round) beam
                bmin = nppb * cdelt2
                bpa = 0.0
                ia.setrestoringbeam(major='%farcsec' % bmaj,
                                    minor='%farcsec' % bmin,
                                    pa='%fdeg' % bpa)
                commonbeam = {}
        logging.info("COMMONBEAM[%d] %s" % (len(commonbeam), str(commonbeam)))

        first_point = ia.getchunk(blc=[0, 0, 0, 0],
                                  trc=[0, 0, 0, 0],
                                  dropdeg=True)
        logging.debug("DATA0*: %s" % str(first_point))

        ia.close()
        logging.info('BASICS: [shape] npts min max: %s %d %f %f' %
                     (s['shape'], s0['npts'][0], s0['min'][0], s0['max'][0]))
        logging.info('S/N (all data): %f' % (s0['max'][0] / s0['rms'][0]))
        npix = 1
        nx = s['shape'][0]
        ny = s['shape'][1]
        nz = s['shape'][2]
        for n in s['shape']:
            npix = npix * n
        ngood = int(s0['npts'][0])
        fgood = (1.0 * ngood) / npix
        logging.info('GOOD PIXELS: %d/%d (%f%% good or %f%% bad)' %
                     (ngood, npix, 100.0 * fgood, 100.0 * (1 - fgood)))
        if s['hasmask']:
            logging.warning('MASKS: %s' % (str(s['masks'])))

        if not file_is_casa:
            b1.setkey("image", Image(images={bt.CASA: bdpfile}))
            if do_pb:
                b2.setkey("image", Image(images={bt.CASA: bdpfile2}))

        # cube sanity: needs to be either 4D or 2D. But p-p-v cube
        # alternative: ia.subimage(dropdeg = True)
        # see also: https://bugs.nrao.edu/browse/CAS-5406
        shape = s['shape']
        if len(shape) > 3:
            if shape[3] > 1:
                # @todo this happens when you ingest a fits or casa image which is ra-dec-pol-freq
                if nz > 1:
                    msg = 'Ingest_AT: cannot deal with real 4D cubes yet'
                    logging.critical(msg)
                    raise Exception, msg
                else:
                    # @todo this is not working yet when the input was a casa image, but ok when fits. go figure.
                    fnot = fno + ".trans"
                    if True:
                        # this works
                        #@todo use safer ia.rename() here.
                        # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                        utils.rename(fno, fnot)
                        imtrans(fnot, fno, "0132")
                        utils.remove(fnot)
                    else:
                        # this does not work, what the heck
                        imtrans(fno, fnot, "0132")
                        #@todo use safer ia.rename() here.
                        # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                        utils.rename(fnot, fno)
                    nz = s['shape'][3]
                    # get a new summary 's'
                    ia.open(fno)
                    s = ia.summary()
                    ia.close()
                    logging.warning(
                        "Using imtrans, with nz=%d, to fix axis ordering" % nz)
                    dt.tag("imtrans4")
            # @todo  ensure first two axes are position, followed by frequency
        elif len(shape) == 3:
            # the current importfits() can do defaultaxes=True,defaultaxesvalues=['', '', '', 'I']
            # but then appears to return a ra-dec-pol-freq cube
            # this branch probably never happens, since ia.fromfits() will
            # properly convert a 3D cube to 4D now !!
            # NO: when NAXIS=3 but various AXIS4's are present, that works. But not if it's pure 3D
            # @todo  box=
            logging.warning("patching up a 3D to 4D cube")
            raise Exception, "SHOULD NEVER GET HERE"
            fnot = fno + ".trans"
            casa.importfits(fni,
                            fnot,
                            defaultaxes=True,
                            defaultaxesvalues=['', '', '', 'I'])
            utils.remove(fno)  # ieck
            imtrans(fnot, fno, "0132")
            utils.remove(fnot)
            dt.tag("imtrans3")

        logging.regression(
            'CUBE: %g %g %g  %d %d %d  %f' %
            (s0['min'], s0['max'], s0['rms'], nx, ny, nz, 100.0 * (1 - fgood)))

        # if the cube has only 1 plane (e.g. continuum) , create a visual (png or so)
        # for 3D cubes, rely on something like CubeSum
        if nz == 1:
            implot = ImPlot(pmode=self._plot_mode,
                            ptype=self._plot_type,
                            abspath=self.dir())
            implot.plotter(rasterfile=bdpfile, figname=bdpfile)
            # @todo needs to be registered for the BDP, right now we only have the plot

        # ia.summary() doesn't have this easily available, so run the more expensive imhead()
        h = casa.imhead(fno, mode='list')
        telescope = h['telescope']
        # work around CASA's PIPELINE bug/feature?   if 'OBJECT' is blank, try 'FIELD'
        srcname = h['object']
        if srcname == ' ':
            logging.warning('FIELD used for OBJECT')
            srcname = casa.imhead(fno, mode='get', hdkey='field')
            if srcname == False:
                # if no FIELD either, we're doomed.  yes, this did happen.
                srcname = 'Unknown'
            casa.imhead(fno, mode="put", hdkey="object", hdvalue=srcname)
            h['object'] = srcname
        logging.info('TELESCOPE: %s' % telescope)
        if telescope == 'UNKNOWN':
            msg = 'Ingest_AT: warning, an UNKNOWN telescope often results in ADMIT failing'
            logging.warning(msg)
        logging.info('OBJECT: %s' % srcname)
        logging.info('REFFREQTYPE: %s' % h['reffreqtype'])
        if h['reffreqtype'].find('TOPO') >= 0:
            msg = 'Ingest_AT: cannot deal with cubes with TOPOCENTRIC frequencies yet - winging it'
            logging.warning(msg)
            #raise Exception,msg
        # Ensure beam parameters are available if there are multiple beams
        # If there is just one beam, then we are just overwriting the header
        # variables with their identical values.
        if len(commonbeam) != 0:
            h['beammajor'] = commonbeam['major']
            h['beamminor'] = commonbeam['minor']
            h['beampa'] = commonbeam['pa']
        # cheat add some things that need to be passed to summary....
        h['badpixel'] = 1.0 - fgood
        if vlsr < -999998.0:
            vlsr = admit.VLSR().vlsr(h['object'].upper())
            if vlsr == 0.0:
                vlsr = -999999.99
        h['vlsr'] = vlsr
        logging.info("VLSR = %f (from source catalog)" % vlsr)

        taskargs = "file=" + fitsfile
        if create_mask == True:
            taskargs = taskargs + " mask=True"
        if len(box) > 0:
            taskargs = taskargs + " " + str(box)
        if len(edge) > 0:
            taskargs = taskargs + " " + str(edge)
        r2d = 57.29577951308232
        logging.info(
            "RA   Axis 1: %f %f %f" %
            (h['crval1'] * r2d, h['cdelt1'] * r2d * 3600.0, h['crpix1']))
        logging.info(
            "DEC  Axis 2: %f %f %f" %
            (h['crval2'] * r2d, h['cdelt2'] * r2d * 3600.0, h['crpix2']))
        if nz > 1:
            # @todo check if this is really a freq axis (for ALMA it is, but...)
            t3 = h['ctype3']
            df = h['cdelt3']
            fc = h['crval3'] + (0.5 * (float(shape[2]) - 1) - h['crpix3']
                                ) * df  # center freq; 0 based pixels
            if 'restfreq' in h:
                fr = float(
                    h['restfreq'][0]
                )  # casa cheats, it may put 0 in here if FITS is missing it
                if fr == 0.0:
                    fr = fc
                else:
                    if vlsr < -999998.0:
                        vlsr = (1 - fc / fr) * ckms
                        h['vlsr'] = vlsr
            else:
                fr = fc
            fw = df * float(shape[2])
            print "PJT:", fr / 1e9, fc / 1e9, fw / 1e9
            dv = -df / fr * ckms

            logging.info("Freq Axis 3: %g %g %g" %
                         (h['crval3'] / 1e9, h['cdelt3'] / 1e9, h['crpix3']))
            logging.info(
                "Cube Axis 3: type=%s  velocity increment=%f km/s @ fc=%f fw=%f GHz"
                % (t3, dv, fc / 1e9, fw / 1e9))
        # @todo sort out this restfreq/vlsr
        # report 'reffreqtype', 'restfreq' 'telescope'
        # if the fits file has ALTRVAL/ALTRPIX, this is lost in CASA?
        # but if you do fits->casa->fits , it's back in fits (with some obvious single precision loss of digits)
        # @todo ZSOURCE is the proposed VLSR slot in the fits header, but this has frame issues (it's also optical)
        #
        # Another method to get the vlsr is to override the restfreq (f0) with an AT keyword
        # and the 'restfreq' from the header (f) is then used to compute the vlsr:   v = c (1 - f/f0)
        #
        if shape[2] > 1 and 'restfreq' in h:
            logging.info("RESTFREQ: %g %g %g" %
                         (fr / 1e9, h['restfreq'][0] / 1e9, restfreq))
            if shape[2] > 1:
                # v_radio of the center of the window w.r.t. restfreq
                vlsrc = ckms * (1 - fc / fr)  # @todo rel frame?
                vlsrw = dv * float(shape[2])
                if restfreq > 0:
                    vlsrf = ckms * (1 - fr / restfreq / 1e9)
                    h['vlsr'] = vlsrf
                else:
                    vlsrf = 0.0
                logging.info("VLSRc = %f  VLSRw = %f  VLSRf = %f VLSR = %f" %
                             (vlsrc, vlsrw, vlsrf, vlsr))
                if h['vlsr'] == 0.0:  # @todo! This fails if vlsr actually is zero. Need another magic number
                    h['vlsr'] = vlsrc
                    logging.warning(
                        "Warning: No VLSR found, substituting VLSRc = %f" %
                        vlsrc)
        else:
            msg = 'Ingest_AT: missing RESTFREQ'
            print msg
        # @todo   LINTRN  is the ALMA keyword that designates the expected line transition in a spw

        self._summarize(fitsfile, bdpfile, h, shape, taskargs)

        dt.tag("done")
        dt.end()
Example #21
0
def mk_diskmodel(outname='disk', bdwidth='325MHz', direction='J2000 10h00m00.0s 20d00m00.0s',
                 reffreq='2.8GHz', flux=660000.0, eqradius='16.166arcmin', polradius='16.166arcmin',
                 pangle='21.1deg', index=None, cell='2.0arcsec', overwrite=True):
    ''' Create a blank solar disk model image (or optionally a data cube)
        outname       String to use for part of the image and fits file names (default 'disk')
        direction     String specifying the position of the Sun in RA and Dec.  Default
                        means use the standard string "J2000 10h00m00.0s 20d00m00.0s"
        reffreq       The reference frequency to use for the disk model (the frequency at which
                        the flux level applies). Default is '2.8GHz'.
        flux          The flux density, in Jy, for the entire disk. Default is 66 sfu.
        eqradius      The equatorial radius of the disk.  Default is
                        16 arcmin + 10" (for typical extension of the radio limb)
        polradius     The polar radius of the disk.  Default is
                        16 arcmin + 10" (for typical extension of the radio limb)
        pangle        The solar P-angle (geographic position of the N-pole of the Sun) in
                        degrees E of N.  This only matters if eqradius != polradius
        index         The spectral index to use at other frequencies.  Default None means
                        use a constant flux density for all frequencies.
        cell          The cell size (assumed square) to use for the image.  The image size
                        is determined from a standard radius of 960" for the Sun, divided by
                        cell size, increased to nearest power of 512 pixels. The default is '2.0arcsec',
                        which results in an image size of 1024 x 1024.
        Note that the frequency increment used is '325MHz', which is the width of EOVSA bands
          (not the width of individual science channels)
    '''

    diskim = outname + reffreq + '.im'
    if os.path.exists(diskim):
        if overwrite:
            os.system('rm -rf {}'.format(diskim))
        else:
            return diskim

    ia = iatool()
    cl = cltool()
    cl.done()
    ia.done()

    try:
        aspect = 1.01  # Enlarge the equatorial disk by 1%
        eqradius = qa.quantity(eqradius)
        diamajor = qa.quantity(2 * aspect * eqradius['value'], eqradius['unit'])
        polradius = qa.quantity(polradius)
        diaminor = qa.quantity(2 * polradius['value'], polradius['unit'])
        solrad = qa.convert(polradius, 'arcsec')
    except:
        print('Radius', eqradius, polradius,
              'does not have the expected format, number + unit where unit is arcmin or arcsec')
        return
    try:
        cell = qa.convert(qa.quantity(cell), 'arcsec')
        cellsize = float(cell['value'])
        diskpix = solrad['value'] * 2 / cellsize
        cell_rad = qa.convert(cell, 'rad')
    except:
        print('Cell size', cell, 'does not have the expected format, number + unit where unit is arcmin or arcsec')
        return

    # Add 90 degrees to pangle, due to angle definition in addcomponent() -- it puts the majoraxis vertical
    pangle = qa.add(qa.quantity(pangle), qa.quantity('90deg'))
    mapsize = ((int(diskpix) / 512) + 1) * 512
    # Flux density is doubled because it is split between XX and YY
    cl.addcomponent(dir=direction, flux=flux * 2, fluxunit='Jy', freq=reffreq, shape='disk',
                    majoraxis=diamajor, minoraxis=diaminor, positionangle=pangle)
    cl.setrefdirframe(0, 'J2000')

    ia.fromshape(diskim, [mapsize, mapsize, 1, 1], overwrite=True)
    cs = ia.coordsys()
    cs.setunits(['rad', 'rad', '', 'Hz'])
    cell_rad_val = cell_rad['value']
    cs.setincrement([-cell_rad_val, cell_rad_val], 'direction')
    epoch, ra, dec = direction.split()
    cs.setreferencevalue([qa.convert(ra, 'rad')['value'], qa.convert(dec, 'rad')['value']], type="direction")
    cs.setreferencevalue(reffreq, 'spectral')
    cs.setincrement(bdwidth, 'spectral')
    ia.setcoordsys(cs.torecord())
    ia.setbrightnessunit("Jy/pixel")
    ia.modify(cl.torecord(), subtract=False)
    ia.close()
    ia.done()
    # cl.close()
    cl.done()
    return diskim
Example #22
0
    def run(self):
        """Runs the task.

           Parameters
           ----------
           None

           Returns
           -------
           None
        """

        self._summary = {}
        dt = utils.Dtime("CubeStats")

        #maxvrms = 2.0      # maximum variation in rms allowed (hardcoded for now)
        #maxvrms = -1.0     # turn maximum variation in rms allowed off
        maxvrms = self.getkey("maxvrms")

        psample = -1
        psample = self.getkey("psample")

        # BDP's used :
        #   b1 = input BDP
        #   b2 = output BDP

        b1 = self._bdp_in[0]
        fin = b1.getimagefile(bt.CASA)

        bdp_name = self.mkext(fin, 'cst')
        b2 = CubeStats_BDP(bdp_name)
        self.addoutput(b2)

        # PeakPointPlot
        use_ppp = self.getkey("ppp")

        # peakstats: not enabled for mortal users yet
        # peakstats = (psample=1, numsigma=4, minchan=3, maxgap=2, peakfit=False)
        pnumsigma = 4
        minchan = 3
        maxgap = 2
        peakfit = False  # True will enable a true gaussian fit

        # numsigma:  adding all signal > numsigma ; not user enabled;   for peaksum.
        numsigma = -1.0
        numsigma = 3.0

        # tools we need
        ia = taskinit.iatool()

        # grab the new robust statistics. If this is used, 'rms' will be the RMS,
        # else we will use RMS = 1.4826*MAD (MAD does a decent job on outliers as well)
        # and was the only method available before CASA 4.4 when robust was implemented
        robust = self.getkey("robust")
        rargs = casautil.parse_robust(robust)
        nrargs = len(rargs)

        if nrargs == 0:
            sumrargs = "medabsdevmed"  # for the summary, indicate the default robust
        else:
            sumrargs = str(rargs)

        self._summary["rmsmethd"] = SummaryEntry([sumrargs, fin],
                                                 "CubeStats_AT", self.id(True))
        #@todo think about using this instead of putting 'fin' in all the SummaryEntry
        #self._summary["casaimage"] = SummaryEntry(fin,"CubeStats_AT",self.id(True))

        # extra CASA call to get the freq's in GHz, as these are not in imstat1{}
        # @todo what if the coordinates are not in FREQ ?
        # Note: CAS-7648 bug on 3D cubes
        if False:
            # csys method
            ia.open(self.dir(fin))
            csys = ia.coordsys()
            spec_axis = csys.findaxisbyname("spectral")
            # ieck, we need a valid position, or else it will come back and "Exception: All selected pixels are masked"
            #freqs = ia.getprofile(spec_axis, region=rg.box([0,0],[0,0]))['coords']/1e9
            #freqs = ia.getprofile(spec_axis)['coords']/1e9
            freqs = ia.getprofile(spec_axis, unit="GHz")['coords']
            dt.tag("getprofile")
        else:
            # old imval method
            #imval0 = casa.imval(self.dir(fin),box='0,0,0,0')     # this fails on 3D
            imval0 = casa.imval(self.dir(fin))
            freqs = imval0['coords'].transpose()[2] / 1e9
            dt.tag("imval")
        nchan = len(freqs)
        chans = np.arange(nchan)

        # call CASA to get what we want
        # imstat0 is the whole cube, imstat1 the plane based statistics
        # warning: certain robust stats (**rargs) on the whole cube are going to be very slow
        dt.tag("start")
        imstat0 = casa.imstat(self.dir(fin),
                              logfile=self.dir('imstat0.logfile'),
                              append=False,
                              **rargs)
        dt.tag("imstat0")
        imstat1 = casa.imstat(self.dir(fin),
                              axes=[0, 1],
                              logfile=self.dir('imstat1.logfile'),
                              append=False,
                              **rargs)
        dt.tag("imstat1")
        # imm = casa.immoments(self.dir(fin),axis='spec', moments=8, outfile=self.dir('ppp.im'))
        if nrargs > 0:
            # need to get the peaks without rubust
            imstat10 = casa.imstat(self.dir(fin),
                                   logfile=self.dir('imstat0.logfile'),
                                   append=True)
            dt.tag("imstat10")
            imstat11 = casa.imstat(self.dir(fin),
                                   axes=[0, 1],
                                   logfile=self.dir('imstat1.logfile'),
                                   append=True)
            dt.tag("imstat11")

        # grab the relevant plane-based things from imstat1
        if nrargs == 0:
            mean = imstat1["mean"]
            sigma = imstat1[
                "medabsdevmed"] * 1.4826  # see also: astropy.stats.median_absolute_deviation()
            peakval = imstat1["max"]
            minval = imstat1["min"]
        else:
            mean = imstat1["mean"]
            sigma = imstat1["rms"]
            peakval = imstat11["max"]
            minval = imstat11["min"]

        if True:
            # work around a bug in imstat(axes=[0,1]) for last channel [CAS-7697]
            for i in range(len(sigma)):
                if sigma[i] == 0.0:
                    minval[i] = peakval[i] = 0.0

        # too many variations in the RMS ?
        sigma_pos = sigma[np.where(sigma > 0)]
        smin = sigma_pos.min()
        smax = sigma_pos.max()
        logging.info("sigma varies from %f to %f; %d/%d channels ok" %
                     (smin, smax, len(sigma_pos), len(sigma)))
        if maxvrms > 0:
            if smax / smin > maxvrms:
                cliprms = smin * maxvrms
                logging.warning(
                    "sigma varies too much, going to clip to %g (%g > %g)" %
                    (cliprms, smax / smin, maxvrms))
                sigma = np.where(sigma < cliprms, sigma, cliprms)

        nzeros = len(np.where(sigma <= 0.0)[0])
        if nzeros > 0:
            zeroch = np.where(sigma <= 0.0)[0]
            logging.warning("There are %d fully masked channels (%s)" %
                            (nzeros, str(zeroch)))

        # @todo   (and check again) for foobar.fits all sigma's became 0 when robust was selected
        #         was this with mask=True/False?

        # PeakPointPlot (can be expensive, hence the option)
        if use_ppp:
            logging.info("Computing MaxPos for PeakPointPlot")
            xpos = np.zeros(nchan)
            ypos = np.zeros(nchan)
            peaksum = np.zeros(nchan)

            ia.open(self.dir(fin))
            for i in range(nchan):
                if sigma[i] > 0.0:
                    plane = ia.getchunk(blc=[0, 0, i, -1],
                                        trc=[-1, -1, i, -1],
                                        dropdeg=True)
                    v = ma.masked_invalid(plane)
                    v_abs = np.absolute(v)
                    max = np.unravel_index(v_abs.argmax(), v_abs.shape)
                    xpos[i] = max[0]
                    ypos[i] = max[1]
                    if numsigma > 0.0:
                        peaksum[i] = ma.masked_less(v,
                                                    numsigma * sigma[i]).sum()
            peaksum = np.nan_to_num(peaksum)  # put 0's where nan's are found
            ia.close()
            dt.tag("ppp")

        # construct the admit Table for CubeStats_BDP
        # note data needs to be a tuple, later to be column_stack'd
        if use_ppp:
            labels = [
                "channel", "frequency", "mean", "sigma", "max", "maxposx",
                "maxposy", "min", "peaksum"
            ]
            units = [
                "number", "GHz", "Jy/beam", "Jy/beam", "Jy/beam", "number",
                "number", "Jy/beam", "Jy"
            ]
            data = (chans, freqs, mean, sigma, peakval, xpos, ypos, minval,
                    peaksum)

        else:
            labels = ["channel", "frequency", "mean", "sigma", "max", "min"]
            units = [
                "number", "GHz", "Jy/beam", "Jy/beam", "Jy/beam", "Jy/beam"
            ]
            data = (chans, freqs, mean, sigma, peakval, minval)

        table = Table(columns=labels, units=units, data=np.column_stack(data))
        b2.setkey("table", table)

        # get the full cube statistics, it depends if robust was pre-selected
        if nrargs == 0:
            mean0 = imstat0["mean"][0]
            sigma0 = imstat0["medabsdevmed"][0] * 1.4826
            peak0 = imstat0["max"][0]
            b2.setkey("mean", float(mean0))
            b2.setkey("sigma", float(sigma0))
            b2.setkey("minval", float(imstat0["min"][0]))
            b2.setkey("maxval", float(imstat0["max"][0]))
            b2.setkey("minpos", imstat0["minpos"]
                      [:3].tolist())  #? [] or array(..dtype=int32) ??
            b2.setkey("maxpos", imstat0["maxpos"]
                      [:3].tolist())  #? [] or array(..dtype=int32) ??
            logging.info("CubeMax: %f @ %s" %
                         (imstat0["max"][0], str(imstat0["maxpos"])))
            logging.info("CubeMin: %f @ %s" %
                         (imstat0["min"][0], str(imstat0["minpos"])))
            logging.info("CubeRMS: %f" % sigma0)
        else:
            mean0 = imstat0["mean"][0]
            sigma0 = imstat0["rms"][0]
            peak0 = imstat10["max"][0]
            b2.setkey("mean", float(mean0))
            b2.setkey("sigma", float(sigma0))
            b2.setkey("minval", float(imstat10["min"][0]))
            b2.setkey("maxval", float(imstat10["max"][0]))
            b2.setkey("minpos", imstat10["minpos"]
                      [:3].tolist())  #? [] or array(..dtype=int32) ??
            b2.setkey("maxpos", imstat10["maxpos"]
                      [:3].tolist())  #? [] or array(..dtype=int32) ??
            logging.info("CubeMax: %f @ %s" %
                         (imstat10["max"][0], str(imstat10["maxpos"])))
            logging.info("CubeMin: %f @ %s" %
                         (imstat10["min"][0], str(imstat10["minpos"])))
            logging.info("CubeRMS: %f" % sigma0)
        b2.setkey("robust", robust)
        rms_ratio = imstat0["rms"][0] / sigma0
        logging.info("RMS Sanity check %f" % rms_ratio)
        if rms_ratio > 1.5:
            logging.warning(
                "RMS sanity check = %f.  Either bad sidelobes, lotsa signal, or both"
                % rms_ratio)
        logging.regression("CST: %f %f" % (sigma0, rms_ratio))

        # plots: no plots need to be made when nchan=1 for continuum
        # however we could make a histogram, overlaying the "best" gauss so
        # signal deviations are clear?

        logging.info('mean,rms,S/N=%f %f %f' % (mean0, sigma0, peak0 / sigma0))

        if nchan == 1:
            # for a continuum/1-channel we only need to stuff some numbers into the _summary
            self._summary["chanrms"] = SummaryEntry([float(sigma0), fin],
                                                    "CubeStats_AT",
                                                    self.id(True))
            self._summary["dynrange"] = SummaryEntry(
                [float(peak0) / float(sigma0), fin], "CubeStats_AT",
                self.id(True))
            self._summary["datamean"] = SummaryEntry([float(mean0), fin],
                                                     "CubeStats_AT",
                                                     self.id(True))
        else:
            y1 = np.log10(ma.masked_invalid(peakval))
            y2 = np.log10(ma.masked_invalid(sigma))
            y3 = y1 - y2

            # Note: log10(-minval) will fail with a runtime domain error if all values
            # in minval are positive , so do a check here and only call log10 if there
            # is at least one negative value in minval.
            # RuntimeWarning: invalid value encountered in log10
            if (minval <= 0).all():
                y4 = np.log10(ma.masked_invalid(-minval))
            else:
                y4 = np.zeros(len(minval))
            y5 = y1 - y4
            y = [y1, y2, y3, y4]
            title = 'CubeStats: ' + bdp_name + '_0'
            xlab = 'Channel'
            ylab = 'log(Peak,Noise,Peak/Noise)'
            labels = [
                'log(peak)', 'log(rms noise)', 'log(peak/noise)',
                'log(|minval|)'
            ]
            myplot = APlot(ptype=self._plot_type,
                           pmode=self._plot_mode,
                           abspath=self.dir())
            segp = [[
                chans[0], chans[nchan - 1],
                math.log10(sigma0),
                math.log10(sigma0)
            ]]
            myplot.plotter(chans,
                           y,
                           title,
                           bdp_name + "_0",
                           xlab=xlab,
                           ylab=ylab,
                           segments=segp,
                           labels=labels,
                           thumbnail=True)
            imfile = myplot.getFigure(figno=myplot.figno, relative=True)
            thumbfile = myplot.getThumbnail(figno=myplot.figno, relative=True)

            image0 = Image(images={bt.PNG: imfile},
                           thumbnail=thumbfile,
                           thumbnailtype=bt.PNG,
                           description="CubeStats_0")
            b2.addimage(image0, "im0")

            if use_ppp:
                # new trial for Lee
                title = 'PeakSum: (numsigma=%.1f)' % (numsigma)
                ylab = 'Jy*N_ppb'
                myplot.plotter(chans, [peaksum],
                               title,
                               bdp_name + "_00",
                               xlab=xlab,
                               ylab=ylab,
                               thumbnail=False)

            if True:
                # hack ascii table
                y30 = np.where(sigma > 0, np.log10(peakval / sigma), 0.0)
                table2 = Table(columns=["freq", "log(P/N)"],
                               data=np.column_stack((freqs, y30)))
                table2.exportTable(self.dir("testCubeStats.tab"))
                del table2

            # the "box" for the "spectrum" is all pixels.  Don't know how to
            # get this except via shape.
            ia.open(self.dir(fin))
            s = ia.summary()
            ia.close()
            if 'shape' in s:
                specbox = (0, 0, s['shape'][0], s['shape'][1])
            else:
                specbox = ()

            caption = "Emission characteristics as a function of channel, as derived by CubeStats_AT "
            caption += "(cyan: global rms,"
            caption += " green: noise per channel,"
            caption += " blue: peak value per channel,"
            caption += " red: peak/noise per channel)."
            self._summary["spectra"] = SummaryEntry([
                0, 0,
                str(specbox), 'Channel', imfile, thumbfile, caption, fin
            ], "CubeStats_AT", self.id(True))
            self._summary["chanrms"] = SummaryEntry([float(sigma0), fin],
                                                    "CubeStats_AT",
                                                    self.id(True))

            # @todo Will imstat["max"][0] always be equal to s['datamax']?  If not, why not?
            if 'datamax' in s:
                self._summary["dynrange"] = SummaryEntry(
                    [float(s['datamax'] / sigma0), fin], "CubeStats_AT",
                    self.id(True))
            else:
                self._summary["dynrange"] = SummaryEntry(
                    [float(imstat0["max"][0] / sigma0), fin], "CubeStats_AT",
                    self.id(True))
            self._summary["datamean"] = SummaryEntry([imstat0["mean"][0], fin],
                                                     "CubeStats_AT",
                                                     self.id(True))

            title = bdp_name + "_1"
            xlab = 'log(Peak,Noise,P/N)'
            myplot.histogram([y1, y2, y3],
                             title,
                             bdp_name + "_1",
                             xlab=xlab,
                             thumbnail=True)

            imfile = myplot.getFigure(figno=myplot.figno, relative=True)
            thumbfile = myplot.getThumbnail(figno=myplot.figno, relative=True)
            image1 = Image(images={bt.PNG: imfile},
                           thumbnail=thumbfile,
                           thumbnailtype=bt.PNG,
                           description="CubeStats_1")
            b2.addimage(image1, "im1")

            # note that the 'y2' can have been clipped, which can throw off stats.robust()
            # @todo  should set a mask for those.

            title = bdp_name + "_2"
            xlab = 'log(Noise))'
            n = len(y2)
            ry2 = stats.robust(y2)
            y2_mean = ry2.mean()
            y2_std = ry2.std()
            if n > 9:
                logging.debug("NORMALTEST2: %s" %
                              str(scipy.stats.normaltest(ry2)))
            myplot.hisplot(y2,
                           title,
                           bdp_name + "_2",
                           xlab=xlab,
                           gauss=[y2_mean, y2_std],
                           thumbnail=True)

            title = bdp_name + "_3"
            xlab = 'log(diff[Noise])'
            n = len(y2)
            # dy2 = y2[0:-2] - y2[1:-1]
            dy2 = ma.masked_equal(y2[0:-2] - y2[1:-1], 0.0).compressed()
            rdy2 = stats.robust(dy2)
            dy2_mean = rdy2.mean()
            dy2_std = rdy2.std()
            if n > 9:
                logging.debug("NORMALTEST3: %s" %
                              str(scipy.stats.normaltest(rdy2)))
            myplot.hisplot(dy2,
                           title,
                           bdp_name + "_3",
                           xlab=xlab,
                           gauss=[dy2_mean, dy2_std],
                           thumbnail=True)

            title = bdp_name + "_4"
            xlab = 'log(Signal/Noise))'
            n = len(y3)
            ry3 = stats.robust(y3)
            y3_mean = ry3.mean()
            y3_std = ry3.std()
            if n > 9:
                logging.debug("NORMALTEST4: %s" %
                              str(scipy.stats.normaltest(ry3)))
            myplot.hisplot(y3,
                           title,
                           bdp_name + "_4",
                           xlab=xlab,
                           gauss=[y3_mean, y3_std],
                           thumbnail=True)

            title = bdp_name + "_5"
            xlab = 'log(diff[Signal/Noise)])'
            n = len(y3)
            dy3 = y3[0:-2] - y3[1:-1]
            rdy3 = stats.robust(dy3)
            dy3_mean = rdy3.mean()
            dy3_std = rdy3.std()
            if n > 9:
                logging.debug("NORMALTEST5: %s" %
                              str(scipy.stats.normaltest(rdy3)))
            myplot.hisplot(dy3,
                           title,
                           bdp_name + "_5",
                           xlab=xlab,
                           gauss=[dy3_mean, dy3_std],
                           thumbnail=True)

            title = bdp_name + "_6"
            xlab = 'log(Peak+Min)'
            n = len(y1)
            ry5 = stats.robust(y5)
            y5_mean = ry5.mean()
            y5_std = ry5.std()
            if n > 9:
                logging.debug("NORMALTEST6: %s" %
                              str(scipy.stats.normaltest(ry5)))
            myplot.hisplot(y5,
                           title,
                           bdp_name + "_6",
                           xlab=xlab,
                           gauss=[y5_mean, y5_std],
                           thumbnail=True)

            logging.debug("LogPeak: m,s= %f %f min/max %f %f" %
                          (y1.mean(), y1.std(), y1.min(), y1.max()))
            logging.debug(
                "LogNoise: m,s= %f %f %f %f min/max %f %f" %
                (y2.mean(), y2.std(), y2_mean, y2_std, y2.min(), y2.max()))
            logging.debug("LogDeltaNoise: RMS/sqrt(2)= %f %f " %
                          (dy2.std() / math.sqrt(2), dy2_std / math.sqrt(2)))
            logging.debug("LogDeltaP/N:   RMS/sqrt(2)= %f %f" %
                          (dy3.std() / math.sqrt(2), dy3_std / math.sqrt(2)))
            logging.debug("LogPeak+Min: robust m,s= %f %f" % (y5_mean, y5_std))

            # compute two ratios that should both be near 1.0 if noise is 'normal'
            ratio = y2.std() / (dy2.std() / math.sqrt(2))
            ratio2 = y2_std / (dy2_std / math.sqrt(2))
            logging.info("RMS BAD VARIATION RATIO: %f %f" % (ratio, ratio2))

        # making PPP plot
        if nchan > 1 and use_ppp:
            smax = 10
            gamma = 0.75

            z0 = peakval / peakval.max()
            # point sizes
            s = np.pi * (smax * (z0**gamma))**2
            cmds = ["grid", "axis equal"]
            title = "Peak Points per channel"
            pppimage = bdp_name + '_ppp'
            myplot.scatter(xpos,
                           ypos,
                           title=title,
                           figname=pppimage,
                           size=s,
                           color=chans,
                           cmds=cmds,
                           thumbnail=True)
            pppimage = myplot.getFigure(figno=myplot.figno, relative=True)
            pppthumbnail = myplot.getThumbnail(figno=myplot.figno,
                                               relative=True)
            caption = "Peak point plot: Locations of per-channel peaks in the image cube " + fin
            self._summary["peakpnt"] = SummaryEntry(
                [pppimage, pppthumbnail, caption, fin], "CubeStats_AT",
                self.id(True))
        dt.tag("plotting")

        # making PeakStats plot
        if nchan > 1 and psample > 0:
            logging.info("Computing peakstats")
            # grab peak,mean and width values for all peaks
            (pval, mval, wval) = peakstats(self.dir(fin), freqs, sigma0,
                                           pnumsigma, minchan, maxgap, psample,
                                           peakfit)
            title = "PeakStats: cutoff = %g" % (sigma0 * pnumsigma)
            xlab = 'Peak value'
            ylab = 'FWHM (channels)'
            pppimage = bdp_name + '_peakstats'
            cval = mval
            myplot.scatter(pval,
                           wval,
                           title=title,
                           xlab=xlab,
                           ylab=ylab,
                           color=cval,
                           figname=pppimage,
                           thumbnail=False)
            dt.tag("peakstats")

        # myplot.final()    # pjt debug
        # all done!
        dt.tag("done")

        taskargs = "robust=" + sumrargs
        if use_ppp:
            taskargs = taskargs + " ppp=True"
        else:
            taskargs = taskargs + " ppp=False"
        for v in self._summary:
            self._summary[v].setTaskArgs(taskargs)

        ia.done()  # is that a good habit?
        dt.tag("summary")
        dt.end()
Example #23
0
def imreg(vis=None,
          ephem=None,
          msinfo=None,
          imagefile=None,
          timerange=None,
          reftime=None,
          fitsfile=None,
          beamfile=None,
          offsetfile=None,
          toTb=None,
          scl100=None,
          verbose=False,
          p_ang=False,
          overwrite=True,
          usephacenter=True,
          deletehistory=False):
    ''' 
    main routine to register CASA images
           Required Inputs:
               vis: STRING. CASA measurement set from which the image is derived
               imagefile: STRING or LIST. name of the input CASA image
               timerange: STRING or LIST. timerange used to generate the CASA image, must have the same length as the input images. 
                          Each element should be in CASA standard time format, e.g., '2012/03/03/12:00:00~2012/03/03/13:00:00'
           Optional Inputs:
               msinfo: DICTIONARY. CASA MS information, output from read_msinfo. If not provided, generate one from the supplied vis
               ephem: DICTIONARY. solar ephem, output from read_horizons. 
                      If not provided, query JPL Horizons based on time info of the vis (internet connection required)
               fitsfile: STRING or LIST. name of the output registered fits files
               reftime: STRING or LIST. Each element should be in CASA standard time format, e.g., '2012/03/03/12:00:00'
               offsetfile: optionally provide an offset with a series of solar x and y offsets with timestamps 
               toTb: Bool. Convert the default Jy/beam to brightness temperature?
               scl100: Bool. If True, scale the image values up by 100 (to compensate VLA 20 dB attenuator)
               verbose: Bool. Show more diagnostic info if True.
               usephacenter: Bool -- if True, correct for the RA and DEC in the ms file based on solar empheris.
                                     Otherwise assume the phasecenter is correctly pointed to the solar disk center
                                     (EOVSA case)
    '''
    ia = iatool()

    if deletehistory:
        msclearhistory(vis)
    if verbose:
        import time
        t0 = time.time()
        prtidx = 1
        print('point {}: {}'.format(prtidx, time.time() - t0))
        prtidx += 1

    if not imagefile:
        raise ValueError, 'Please specify input image'
    if not timerange:
        raise ValueError, 'Please specify timerange of the input image'
    if type(imagefile) == str:
        imagefile = [imagefile]
    if type(timerange) == str:
        timerange = [timerange]
    if not fitsfile:
        fitsfile = [img + '.fits' for img in imagefile]
    if type(fitsfile) == str:
        fitsfile = [fitsfile]
    nimg = len(imagefile)
    if len(timerange) != nimg:
        raise ValueError, 'Number of input images does not equal to number of timeranges!'
    if len(fitsfile) != nimg:
        raise ValueError, 'Number of input images does not equal to number of output fits files!'
    nimg = len(imagefile)
    if verbose:
        print str(nimg) + ' images to process...'

    if verbose:
        print('point {}: {}'.format(prtidx, time.time() - t0))
        prtidx += 1

    if reftime:  # use as reference time to find solar disk RA and DEC to register the image, but not the actual timerange associated with the image
        if type(reftime) == str:
            reftime = [reftime] * nimg
        if len(reftime) != nimg:
            raise ValueError, 'Number of reference times does not match that of input images!'
        helio = ephem_to_helio(vis,
                               ephem=ephem,
                               msinfo=msinfo,
                               reftime=reftime,
                               usephacenter=usephacenter)
    else:
        # use the supplied timerange to register the image
        helio = ephem_to_helio(vis,
                               ephem=ephem,
                               msinfo=msinfo,
                               reftime=timerange,
                               usephacenter=usephacenter)

    if verbose:
        print('point {}: {}'.format(prtidx, time.time() - t0))
        prtidx += 1

    for n, img in enumerate(imagefile):
        if verbose:
            print 'processing image #' + str(n)
        fitsf = fitsfile[n]
        timeran = timerange[n]
        # obtain duration of the image as FITS header exptime
        try:
            [tbg0, tend0] = timeran.split('~')
            tbg_d = qa.getvalue(qa.convert(qa.totime(tbg0), 'd'))[0]
            tend_d = qa.getvalue(qa.convert(qa.totime(tend0), 'd'))[0]
            tdur_s = (tend_d - tbg_d) * 3600. * 24.
            dateobs = qa.time(qa.quantity(tbg_d, 'd'), form='fits', prec=10)[0]
        except:
            print 'Error in converting the input timerange: ' + str(
                timeran) + '. Proceeding to the next image...'
            continue

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        hel = helio[n]
        if not os.path.exists(img):
            raise ValueError, 'Please specify input image'
        if os.path.exists(fitsf) and not overwrite:
            raise ValueError, 'Specified fits file already exists and overwrite is set to False. Aborting...'
        else:
            p0 = hel['p0']
            ia.open(img)
            imr = ia.rotate(pa=str(-p0) + 'deg')
            imr.tofits(fitsf, history=False, overwrite=overwrite)
            imr.close()
            imsum = ia.summary()
            ia.close()

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        # construct the standard fits header
        # RA and DEC of the reference pixel crpix1 and crpix2
        (imra, imdec) = (imsum['refval'][0], imsum['refval'][1])
        # find out the difference of the image center to the CASA phase center
        # RA and DEC difference in arcseconds
        ddec = degrees((imdec - hel['dec_fld'])) * 3600.
        dra = degrees((imra - hel['ra_fld']) * cos(hel['dec_fld'])) * 3600.
        # Convert into image heliocentric offsets
        prad = -radians(hel['p0'])
        dx = (-dra) * cos(prad) - ddec * sin(prad)
        dy = (-dra) * sin(prad) + ddec * cos(prad)
        if offsetfile:
            try:
                offset = np.load(offsetfile)
            except:
                raise ValueError, 'The specified offsetfile does not exist!'
            reftimes_d = offset['reftimes_d']
            xoffs = offset['xoffs']
            yoffs = offset['yoffs']
            timg_d = hel['reftime']
            ind = bisect.bisect_left(reftimes_d, timg_d)
            xoff = xoffs[ind - 1]
            yoff = yoffs[ind - 1]
        else:
            xoff = hel['refx']
            yoff = hel['refy']
        if verbose:
            print 'offset of image phase center to visibility phase center (arcsec): ', dx, dy
            print 'offset of visibility phase center to solar disk center (arcsec): ', xoff, yoff
        (crval1, crval2) = (xoff + dx, yoff + dy)
        # update the fits header to heliocentric coordinates

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        hdu = pyfits.open(fitsf, mode='update')

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        header = hdu[0].header
        (cdelt1,
         cdelt2) = (-header['cdelt1'] * 3600., header['cdelt2'] * 3600.
                    )  # Original CDELT1, 2 are for RA and DEC in degrees
        header['cdelt1'] = cdelt1
        header['cdelt2'] = cdelt2
        header['cunit1'] = 'arcsec'
        header['cunit2'] = 'arcsec'
        header['crval1'] = crval1
        header['crval2'] = crval2
        header['ctype1'] = 'HPLN-TAN'
        header['ctype2'] = 'HPLT-TAN'
        header['date-obs'] = dateobs  # begin time of the image
        if not p_ang:
            hel['p0'] = 0
        try:
            # this works for pyfits version of CASA 4.7.0 but not CASA 4.6.0
            if tdur_s:
                header.set('exptime', tdur_s)
            else:
                header.set('exptime', 1.)
            header.set('p_angle', hel['p0'])
            header.set('dsun_obs',
                       sun.sunearth_distance(Time(dateobs)).to(u.meter).value)
            header.set(
                'rsun_obs',
                sun.solar_semidiameter_angular_size(Time(dateobs)).value)
            header.set('rsun_ref', sun.constants.radius.value)
            header.set('hgln_obs', 0.)
            header.set('hglt_obs',
                       sun.heliographic_solar_center(Time(dateobs))[1].value)
        except:
            # this works for astropy.io.fits
            if tdur_s:
                header.append(('exptime', tdur_s))
            else:
                header.append(('exptime', 1.))
            header.append(('p_angle', hel['p0']))
            header.append(
                ('dsun_obs',
                 sun.sunearth_distance(Time(dateobs)).to(u.meter).value))
            header.append(
                ('rsun_obs',
                 sun.solar_semidiameter_angular_size(Time(dateobs)).value))
            header.append(('rsun_ref', sun.constants.radius.value))
            header.append(('hgln_obs', 0.))
            header.append(
                ('hglt_obs',
                 sun.heliographic_solar_center(Time(dateobs))[1].value))

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        # update intensity units, i.e. to brightness temperature?
        if toTb:
            # get restoring beam info
            (bmajs, bmins, bpas, beamunits,
             bpaunits) = getbeam(imagefile=imagefile, beamfile=beamfile)
            bmaj = bmajs[n]
            bmin = bmins[n]
            beamunit = beamunits[n]
            data = hdu[
                0].data  # remember the data order is reversed due to the FITS convension
            dim = data.ndim
            sz = data.shape
            keys = header.keys()
            values = header.values()
            # which axis is frequency?
            faxis = keys[values.index('FREQ')][-1]
            faxis_ind = dim - int(faxis)
            if header['BUNIT'].lower() == 'jy/beam':
                header['BUNIT'] = 'K'
                header['BTYPE'] = 'Brightness Temperature'
                for i in range(sz[faxis_ind]):
                    nu = header['CRVAL' + faxis] + header['CDELT' + faxis] * (
                        i + 1 - header['CRPIX' + faxis])
                    if header['CUNIT' + faxis] == 'KHz':
                        nu *= 1e3
                    if header['CUNIT' + faxis] == 'MHz':
                        nu *= 1e6
                    if header['CUNIT' + faxis] == 'GHz':
                        nu *= 1e9
                    if len(bmaj) > 1:  # multiple (per-plane) beams
                        bmajtmp = bmaj[i]
                        bmintmp = bmin[i]
                    else:  # one single beam
                        bmajtmp = bmaj[0]
                        bmintmp = bmin[0]
                    if beamunit == 'arcsec':
                        bmaj0 = np.radians(bmajtmp / 3600.)
                        bmin0 = np.radians(bmajtmp / 3600.)
                    if beamunit == 'arcmin':
                        bmaj0 = np.radians(bmajtmp / 60.)
                        bmin0 = np.radians(bmintmp / 60.)
                    if beamunit == 'deg':
                        bmaj0 = np.radians(bmajtmp)
                        bmin0 = np.radians(bmintmp)
                    if beamunit == 'rad':
                        bmaj0 = bmajtmp
                        bmin0 = bmintmp
                    beam_area = bmaj0 * bmin0 * np.pi / (4. * log(2.))
                    k_b = qa.constants('k')['value']
                    c_l = qa.constants('c')['value']
                    factor = 2. * k_b * nu**2 / c_l**2  # SI unit
                    jy_to_si = 1e-26
                    # print nu/1e9, beam_area, factor
                    factor2 = 1.
                    if scl100:
                        factor2 = 100.
                    if faxis == '3':
                        data[:,
                             i, :, :] *= jy_to_si / beam_area / factor * factor2
                    if faxis == '4':
                        data[
                            i, :, :, :] *= jy_to_si / beam_area / factor * factor2

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1

        hdu.flush()
        hdu.close()

        if verbose:
            print('point {}: {}'.format(prtidx, time.time() - t0))
            prtidx += 1
Example #24
0
    def run(self):
        """ Main program for OverlapIntegral
        """
        dt = utils.Dtime("OverlapIntegral")
        self._summary = {}
        chans = self.getkey("chans")
        cmap = self.getkey("cmap")
        normalize = self.getkey("normalize")
        doCross = True
        doCross = False
        myplot = APlot(pmode=self._plot_mode,
                       ptype=self._plot_type,
                       abspath=self.dir())

        dt.tag("start")

        n = len(self._bdp_in)
        if n == 0:
            raise Exception, "Need at least 1 input Image_BDP "
        logging.debug("Processing %d input maps" % n)
        data = range(
            n)  # array in which each element is placeholder for the data
        mdata = range(n)  # array to hold the max in each array
        summarytable = admit.util.Table()
        summarytable.columns = ["File name", "Spectral Line ID"]
        summarytable.description = "Images used in Overlap Integral"
        for i in range(n):
            bdpfile = self._bdp_in[i].getimagefile(bt.CASA)
            if hasattr(self._bdp_in[i], "line"):
                line = getattr(self._bdp_in[i], "line")
                logging.info("Map %d: %s" % (i, line.uid))
                lineid = line.uid
            else:
                lineid = "no line"
            data[i] = casautil.getdata(self.dir(bdpfile), chans)
            mdata[i] = data[i].max()
            logging.info("shape[%d] = %s with %d good data" %
                         (i, data[i].shape, data[i].count()))
            if i == 0:
                shape = data[i].shape
                outfile = self.mkext("testOI", "oi")
            else:
                if shape != data[i].shape:
                    raise Exception, "Shapes not the same, cannot overlap them"
            # collect the file names and line identifications for the summary
            summarytable.addRow([bdpfile, lineid])
        logging.regression("OI: %s" % str(mdata))

        if len(shape) > 2 and shape[2] > 1:
            raise Exception, "Cannot handle 3D cubes yet"

        if doCross:
            # debug: produce all cross-corr's of the N input maps (expensive!)
            crossn(data, myplot)
            dt.tag("crossn")

        b1 = Image_BDP(outfile)
        self.addoutput(b1)
        b1.setkey("image", Image(images={bt.CASA: outfile}))

        ia = taskinit.iatool()

        dt.tag("open")

        useClone = True

        # to create an output dataset, clone the first input, but using the chans=ch0~ch1
        # e.g. using imsubimage(infile,outfile=,chans=
        if len(chans) > 0:
            # ia.regrid() doesn't have the chans=
            ia.open(self.dir(self._bdp_in[0].getimagefile(bt.CASA)))
            ia.regrid(outfile=self.dir(outfile))
            ia.close()
        else:
            # 2D for now
            if not useClone:
                logging.info("OVERLAP out=%s" % outfile)
                ia.fromimage(infile=self.dir(self._bdp_in[0].getimagefile(
                    bt.CASA)),
                             outfile=self.dir(outfile),
                             overwrite=True)
                ia.close()
        dt.tag("fromimage")

        if n == 3:
            # RGB
            logging.info("RGB mode")
            out = rgb1(data[0], data[1], data[2], normalize)
        else:
            # simple sum
            out = data[0]
            for i in range(1, n):
                out = out + data[i]

        if useClone:
            casautil.putdata_raw(self.dir(outfile),
                                 out,
                                 clone=self.dir(self._bdp_in[0].getimagefile(
                                     bt.CASA)))
        else:
            ia.open(self.dir(outfile))
            s1 = ia.shape()
            s0 = [0, 0, 0, 0]
            rg = taskinit.rgtool()
            r1 = rg.box(blc=s0, trc=s1)
            pixeldata = out.data
            pixelmask = ~out.mask
            ia.putregion(pixels=pixeldata, pixelmask=pixelmask, region=r1)
            ia.close()

        title = "OverlapIntegral"
        pdata = np.rot90(out.squeeze())
        logging.info("PDATA: %s" % str(pdata.shape))

        myplot.map1(pdata, title, "testOI", thumbnail=True, cmap=cmap)

        #-----------------------------
        # Populate summary information
        #-----------------------------
        taskargs = "chans=%s cmap=%s" % (chans, cmap)
        imname = ""
        thumbnailname = ""
        # uncomment when ready.
        imname = myplot.getFigure(figno=myplot.figno, relative=True)
        thumbnailname = myplot.getThumbnail(figno=myplot.figno, relative=True)
        #@todo fill in caption with more info - line names, etc.
        caption = "Need descriptive caption here"
        summaryinfo = [
            summarytable.serialize(), imname, thumbnailname, caption
        ]
        self._summary["overlap"] = SummaryEntry(summaryinfo,
                                                "OverlapIntegral_AT",
                                                self.id(True), taskargs)
        #-----------------------------
        dt.tag("done")
        dt.end()
Example #25
0
def imreg(vis=None, ephem=None, msinfo=None, reftime=None, imagefile=None, fitsfile=None, beamfile=None, \
          offsetfile=None, toTb=None, scl100=None, verbose=False, p_ang = False, overwrite = True, usephacenter=False):
    ia = iatool()
    if not imagefile:
        raise ValueError, 'Please specify input image'
    if not reftime:
        raise ValueError, 'Please specify reference time corresponding to the input image'
    if not fitsfile:
        fitsfile = [img + '.fits' for img in imagefile]
    if len(imagefile) != len(reftime):
        raise ValueError, 'Number of input images does not equal to number of helio coord headers!'
    if len(imagefile) != len(fitsfile):
        raise ValueError, 'Number of input images does not equal to number of output fits files!'
    nimg = len(imagefile)
    if verbose:
        print str(nimg) + ' images to process...'
    helio = ephem_to_helio(vis,
                           ephem=ephem,
                           msinfo=msinfo,
                           reftime=reftime,
                           usephacenter=usephacenter)
    for n, img in enumerate(imagefile):
        if verbose:
            print 'processing image #' + str(n)
        fitsf = fitsfile[n]
        hel = helio[n]
        if not os.path.exists(img):
            raise ValueError, 'Please specify input image'
        if os.path.exists(fitsf) and not overwrite:
            raise ValueError, 'Specified fits file already exists and overwrite is set to False. Aborting...'
        else:
            p0 = hel['p0']
            ia.open(img)
            imr = ia.rotate(pa=str(-p0) + 'deg')
            imr.tofits(fitsf, history=False, overwrite=overwrite)
            imr.close()
            sum = ia.summary()
            ia.close()
        # construct the standard fits header
        # RA and DEC of the reference pixel crpix1 and crpix2
        (imra, imdec) = (sum['refval'][0], sum['refval'][1])
        # find out the difference of the image center to the CASA phase center
        # RA and DEC difference in arcseconds
        ddec = degrees((imdec - hel['dec_fld'])) * 3600.
        dra = degrees((imra - hel['ra_fld']) * cos(hel['dec_fld'])) * 3600.
        # Convert into image heliocentric offsets
        prad = -radians(hel['p0'])
        dx = (-dra) * cos(prad) - ddec * sin(prad)
        dy = (-dra) * sin(prad) + ddec * cos(prad)
        if offsetfile:
            try:
                offset = np.load(offsetfile)
            except:
                raise ValueError, 'The specified offsetfile does not exist!'
            reftimes_d = offset['reftimes_d']
            xoffs = offset['xoffs']
            yoffs = offset['yoffs']
            timg_d = hel['reftime']
            ind = bisect.bisect_left(reftimes_d, timg_d)
            xoff = xoffs[ind - 1]
            yoff = yoffs[ind - 1]
        else:
            xoff = hel['refx']
            yoff = hel['refy']
        if verbose:
            print 'offset of image phase center to visibility phase center (arcsec): ', dx, dy
            print 'offset of visibility phase center to solar disk center (arcsec): ', xoff, yoff
        (crval1, crval2) = (xoff + dx, yoff + dy)
        # update the fits header to heliocentric coordinates

        hdu = pyfits.open(fitsf, mode='update')
        header = hdu[0].header
        (cdelt1,
         cdelt2) = (-header['cdelt1'] * 3600., header['cdelt2'] * 3600.
                    )  # Original CDELT1, 2 are for RA and DEC in degrees
        header['cdelt1'] = cdelt1
        header['cdelt2'] = cdelt2
        header['cunit1'] = 'arcsec'
        header['cunit2'] = 'arcsec'
        header['crval1'] = crval1
        header['crval2'] = crval2
        header['ctype1'] = 'HPLN-TAN'
        header['ctype2'] = 'HPLT-TAN'
        header['date-obs'] = hel['date-obs']  #begin time of the image
        if not p_ang:
            hel['p0'] = 0
        try:
            # this works for pyfits version of CASA 4.7.0 but not CASA 4.6.0
            header.update('exptime', hel['exptime'])
            header.update('p_angle', hel['p0'])
            header.update(
                'dsun_obs',
                sun.sunearth_distance(Time(hel['date-obs'])).to(u.meter).value)
            header.update(
                'rsun_obs',
                sun.solar_semidiameter_angular_size(Time(
                    hel['date-obs'])).value)
            header.update('rsun_ref', sun.constants.radius.value)
            header.update('hgln_obs', 0.)
            header.update(
                'hglt_obs',
                sun.heliographic_solar_center(Time(hel['date-obs']))[1].value)
        except:
            # this works for astropy.io.fits
            header.append(('exptime', hel['exptime']))
            header.append(('p_angle', hel['p0']))
            header.append(
                ('dsun_obs', sun.sunearth_distance(Time(hel['date-obs'])).to(
                    u.meter).value))
            header.append(('rsun_obs',
                           sun.solar_semidiameter_angular_size(
                               Time(hel['date-obs'])).value))
            header.append(('rsun_ref', sun.constants.radius.value))
            header.append(('hgln_obs', 0.))
            header.append(('hglt_obs',
                           sun.heliographic_solar_center(Time(
                               hel['date-obs']))[1].value))

        # header.update('comment', 'Fits header updated to heliocentric coordinates by Bin Chen')

        # update intensity units, i.e. to brightness temperature?
        if toTb:
            # get restoring beam info
            (bmajs, bmins, bpas, beamunits,
             bpaunits) = getbeam(imagefile=imagefile, beamfile=beamfile)
            bmaj = bmajs[n]
            bmin = bmins[n]
            beamunit = beamunits[n]
            data = hdu[
                0].data  # remember the data order is reversed due to the FITS convension
            dim = data.ndim
            sz = data.shape
            keys = header.keys()
            values = header.values()
            # which axis is frequency?
            faxis = keys[values.index('FREQ')][-1]
            faxis_ind = dim - int(faxis)
            if header['BUNIT'].lower() == 'jy/beam':
                header['BUNIT'] = 'K'
                for i in range(sz[faxis_ind]):
                    nu = header['CRVAL' + faxis] + header['CDELT' + faxis] * \
                                                   (i + 1 - header['CRPIX' + faxis])
                    if header['CUNIT' + faxis] == 'KHz':
                        nu *= 1e3
                    if header['CUNIT' + faxis] == 'MHz':
                        nu *= 1e6
                    if header['CUNIT' + faxis] == 'GHz':
                        nu *= 1e9
                    if len(bmaj) > 1:  # multiple (per-plane) beams
                        bmajtmp = bmaj[i]
                        bmintmp = bmin[i]
                    else:  # one single beam
                        bmajtmp = bmaj[0]
                        bmintmp = bmin[0]
                    if beamunit == 'arcsec':
                        bmaj0 = np.radians(bmajtmp / 3600.)
                        bmin0 = np.radians(bmajtmp / 3600.)
                    if beamunit == 'arcmin':
                        bmaj0 = np.radians(bmajtmp / 60.)
                        bmin0 = np.radians(bmintmp / 60.)
                    if beamunit == 'deg':
                        bmaj0 = np.radians(bmajtmp)
                        bmin0 = np.radians(bmintmp)
                    if beamunit == 'rad':
                        bmaj0 = bmajtmp
                        bmin0 = bmintmp
                    beam_area = bmaj0 * bmin0 * np.pi / (4. * log(2.))
                    k_b = qa.constants('k')['value']
                    c_l = qa.constants('c')['value']
                    factor = 2. * k_b * nu**2 / c_l**2  # SI unit
                    jy_to_si = 1e-26
                    # print nu/1e9, beam_area, factor
                    factor2 = 1.
                    if scl100:
                        factor2 = 100.
                    if faxis == '3':
                        data[:,
                             i, :, :] *= jy_to_si / beam_area / factor * factor2
                    if faxis == '4':
                        data[
                            i, :, :, :] *= jy_to_si / beam_area / factor * factor2

        hdu.flush()
        hdu.close()
Example #26
0
    def run(self):
        """ The run method, calculates the moments and creates the BDP(s)

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        self._summary = {}
        momentsummary = []
        dt = utils.Dtime("Moment")

        # variable to track if we are using a single cutoff for all moment maps
        allsame = False
        moments = self.getkey("moments")
        numsigma = self.getkey("numsigma")
        mom0clip = self.getkey("mom0clip")
        # determine if there is only 1 cutoff or if there is a cutoff for each moment
        if len(moments) != len(numsigma):
            if len(numsigma) != 1:
                raise Exception("Length of numsigma and moment lists do not match. They must be the same length or the length of the cutoff list must be 1.")
            allsame = True
        # default moment file extensions, this is information copied from casa.immoments()
        momentFileExtensions = {-1: ".average",
                                 0: ".integrated",
                                 1: ".weighted_coord",
                                 2: ".weighted_dispersion_coord",
                                 3: ".median",
                                 4: "",
                                 5: ".standard_deviation",
                                 6: ".rms",
                                 7: ".abs_mean_dev",
                                 8: ".maximum",
                                 9: ".maximum_coord",
                                10: ".minimum",
                                11: ".minimum_coord",
                                }

        logging.debug("MOMENT: %s %s %s" %  (str(moments), str(numsigma), str(allsame)))

        # get the input casa image from bdp[0]
        # also get the channels the line actually covers (if any)
        bdpin = self._bdp_in[0]
        infile = bdpin.getimagefile(bt.CASA)
        chans = self.getkey("chans")
        # the basename of the moments, we will append _0, _1, etc.
        basename = self.mkext(infile, "mom")
        fluxname = self.mkext(infile, "flux")
        # beamarea = nppb(self.dir(infile))
        beamarea = 1.0  # until we have it from the MOM0 map

        sigma0 = self.getkey("sigma")
        sigma  = sigma0

        ia = taskinit.iatool()

        dt.tag("open")

        # if no CubseStats BDP was given and no sigma was specified, find a 
        # noise level via casa.imstat()
        if self._bdp_in[1] is None and sigma <= 0.0:
            raise Exception("A sigma or a CubeStats_BDP must be input to calculate the cutoff")
        elif self._bdp_in[1] is not None:
            sigma = self._bdp_in[1].get("sigma")

        # immoments is a bit peculiar. If you give one moment, it will use 
        # exactly the outfile you picked for multiple moments, it will pick
        # extensions such as .integrated [0], .weighted_coord [1] etc.
        # we loop over the moments and will use the numeric extension instead. 
        # Might be laborious loop for big input cubes
        #
        # arguments for immoments
        args = {"imagename" : self.dir(infile),
                "moments"   : moments,
                "outfile"   : self.dir(basename)}

        # set the channels if given
        if chans != "":
            args["chans"] = chans
        # error check the mom0clip input
        if mom0clip > 0.0 and not 0 in moments:
            logging.warning("mom0clip given, but no moment0 map was requested. One will be generated anyway.")
            # add moment0 to the list of computed moments, but it has to be first
            moments.insert(0,0)
            if not allsame:
                numsigma.insert(0, 2.0*sigma)

        if allsame:
            # this is only executed now if len(moments) > 1 and len(cutoff)==1
            args["excludepix"] = [-numsigma[0] * sigma, numsigma[0] * sigma]
            casa.immoments(**args)
            dt.tag("immoments-all")
        else:
            # this is execute if len(moments)==len(cutoff) , even when len=1
            for i in range(len(moments)):
                args["excludepix"] = [-numsigma[i] * sigma, numsigma[i] * sigma]
                args["moments"] = moments[i]
                args["outfile"] = self.dir(basename + momentFileExtensions[moments[i]])
                casa.immoments(**args)
                dt.tag("immoments-%d" % moments[i])

        taskargs = "moments=%s numsigma=%s" % (str(moments), str(numsigma)) 
        if sigma0 > 0:
            taskargs = taskargs + " sigma=%.2f" % sigma0
        if mom0clip > 0:
            taskargs = taskargs + " mom0clip=%g" % mom0clip
        if chans == "": 
            taskargs = taskargs + " chans=all"
        else:
            taskargs = taskargs + " chans=%s" % str(chans)
        taskargs += '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="background-color:white">&nbsp;' + basename.split('/')[0] + '&nbsp;</span>'

        # generate the mask to be applied to all but moment 0
        if mom0clip > 0.0:
            # get the statistics from mom0 map
            # this is usually a very biased map, so unclear if mom0sigma is all that reliable
            args = {"imagename": self.dir(infile)}
            stat = casa.imstat(imagename=self.dir(basename + momentFileExtensions[0]))
            mom0sigma = float(stat["sigma"][0])
            # generate a temporary masked file, mask will be copied to other moments
            args = {"imagename" : self.dir(basename + momentFileExtensions[0]),
                    "expr"      : 'IM0[IM0>%f]' % (mom0clip * mom0sigma),
                    "outfile"   : self.dir("mom0.masked")
                    }
            casa.immath(**args)
            # get the default mask name
            ia.open(self.dir("mom0.masked"))
            defmask = ia.maskhandler('default')
            ia.close()
            dt.tag("mom0clip")

        # loop over moments to rename them to _0, _1, _2 etc.
        # apply a mask as well for proper histogram creation
        map = {}
        myplot = APlot(pmode=self._plot_mode,ptype=self._plot_type,abspath=self.dir())
        implot = ImPlot(pmode=self._plot_mode,ptype=self._plot_type,abspath=self.dir())

        for mom in moments:
            figname = imagename = "%s_%i" % (basename, mom)
            tempname = basename + momentFileExtensions[mom]
            # rename and remove the old one if there is one
            utils.rename(self.dir(tempname), self.dir(imagename))
            # copy the moment0 mask if requested; this depends on that mom0 was done before
            if mom0clip > 0.0 and mom != 0:
                #print "PJT: output=%s:%s" % (self.dir(imagename), defmask[0])
                #print "PJT: inpmask=%s:%s" % (self.dir("mom0.masked"),defmask[0])
                makemask(mode="copy", inpimage=self.dir("mom0.masked"),
                         output="%s:%s" % (self.dir(imagename), defmask[0]),
                         overwrite=True, inpmask="%s:%s" % (self.dir("mom0.masked"),
                                                            defmask[0]))
                ia.open(self.dir(imagename))
                ia.maskhandler('set', defmask)
                ia.close()
                dt.tag("makemask")
            if mom == 0:
                beamarea = nppb(self.dir(imagename))
            implot.plotter(rasterfile=imagename,figname=figname,
                           colorwedge=True,zoom=self.getkey("zoom"))
            imagepng  = implot.getFigure(figno=implot.figno,relative=True)
            thumbname = implot.getThumbnail(figno=implot.figno,relative=True)
            images = {bt.CASA : imagename, bt.PNG  : imagepng}
            thumbtype=bt.PNG
            dt.tag("implot")

            # get the data for a histogram (ia access is about 1000-2000 faster than imval())
            map[mom] = casautil.getdata(self.dir(imagename))
            data = map[mom].compressed()
            dt.tag("getdata")

            # make the histogram plot

            # get the label for the x axis
            bunit = casa.imhead(imagename=self.dir(imagename), mode="get", hdkey="bunit")
            # object for the caption
            objectname = casa.imhead(imagename=self.dir(imagename), mode="get", hdkey="object")

            # Make the histogram plot
            # Since we give abspath in the constructor, figname should be relative
            auxname = imagename + '_histo'
            auxtype = bt.PNG
            myplot.histogram(columns = data,
                             figname = auxname,
                             xlab    = bunit,
                             ylab    = "Count",
                             title   = "Histogram of Moment %d: %s" % (mom, imagename), thumbnail=True)

            casaimage = Image(images    = images,
                                    auxiliary = auxname,
                                    auxtype   = auxtype,
                                    thumbnail = thumbname,
                                    thumbnailtype = thumbtype)
            auxname = myplot.getFigure(figno=myplot.figno,relative=True)
            auxthumb = myplot.getThumbnail(figno=myplot.figno,relative=True)

            if hasattr(self._bdp_in[0], "line"):   # SpwCube doesn't have Line
                line = deepcopy(getattr(self._bdp_in[0], "line"))
                if not isinstance(line, Line):
                    line = Line(name="Unidentified")
            else:
                # fake a Line if there wasn't one
                line = Line(name="Unidentified")
            # add the BDP to the output array
            self.addoutput(Moment_BDP(xmlFile=imagename, moment=mom,
                           image=deepcopy(casaimage), line=line))
            dt.tag("ren+mask_%d" % mom)

            imcaption = "%s Moment %d map of Source %s" % (line.name, mom, objectname)
            auxcaption = "Histogram of %s Moment %d of Source %s" % (line.name, mom, objectname)
            thismomentsummary = [line.name, mom, imagepng, thumbname, imcaption,
                                 auxname, auxthumb, auxcaption, infile]
            momentsummary.append(thismomentsummary)

        if map.has_key(0) and map.has_key(1) and map.has_key(2):
            logging.debug("MAPs present: %s" % (map.keys()))

            # m0 needs a new mask, inherited from the more restricted m1 (and m2)
            m0 = ma.masked_where(map[1].mask,map[0])
            m1 = map[1]
            m2 = map[2]
            m01 = m0*m1
            m02 = m0*m1*m1
            m22 = m0*m2*m2
            sum0 = m0.sum()
            vmean = m01.sum()/sum0
            # lacking the full 3D cube, get two estimates and take the max
            sig1  = math.sqrt(m02.sum()/sum0 - vmean*vmean)
            sig2  = m2.max()
            #vsig = max(sig1,sig2)
            vsig = sig1
            
            # consider clipping in the masked array (mom0clip)
            # @todo   i can't use info from line, so just borrow basename for now for grepping
            #         this also isn't really the flux, the points per beam is still in there
            loc = basename.rfind('/')
            sum1 = ma.masked_less(map[0],0.0).sum()   # mom0clip
            # print out:   LINE,FLUX1,FLUX0,BEAMAREA,VMEAN,VSIGMA for regression
            # the linechans parameter in bdpin is not useful to print out here, it's local to the LineCube
            s_vlsr = admit.Project.summaryData.get('vlsr')[0].getValue()[0]
            s_rest = admit.Project.summaryData.get('restfreq')[0].getValue()[0]/1e9
            s_line = line.frequency
            if loc>0:
                if basename[:loc][0:2] == 'U_':
                    # for U_ lines we'll reference the VLSR w.r.t. RESTFREQ in that band
                    if abs(vmean) > vsig:
                        vwarn = '*'
                    else:
                        vwarn = ''
                    vlsr = vmean + (1.0-s_line/s_rest)*utils.c
                    msg = "MOM0FLUX: %s %g %g %g %g %g %g" % (basename[:loc],map[0].sum(),sum0,beamarea,vmean,vlsr,vsig)
                else:
                    # for identified lines we'll assume the ID was correct and not bother with RESTFREQ
                    msg = "MOM0FLUX: %s %g %g %g %g %g %g" % (basename[:loc],map[0].sum(),sum0,beamarea,vmean,vmean,vsig)
            else:
                msg = "MOM0FLUX: %s %g %g %g %g %g %g" % ("SPW_FULL"    ,map[0].sum(),sum0,beamarea,vmean,vmean,vsig)
            logging.regression(msg)
            dt.tag("mom0flux")

            # create a histogram of flux per channel

            # grab the X coordinates for the histogram, we want them in km/s
            # restfreq should also be in summary
            restfreq = casa.imhead(self.dir(infile),mode="get",hdkey="restfreq")['value']/1e9    # in GHz
            # print "PJT  %.10f %.10f" % (restfreq,s_rest)
            imval0 = casa.imval(self.dir(infile))
            freqs = imval0['coords'].transpose()[2]/1e9
            x = (1-freqs/restfreq)*utils.c
            # 
            h = casa.imstat(self.dir(infile), axes=[0,1])
            if h.has_key('flux'):
                flux0 = h['flux']
            else:
                flux0 = h['sum']/beamarea
            flux0sum = flux0.sum() * abs(x[1]-x[0])
            # @todo   make a flux1 with fluxes derived from a good mask
            flux1 = flux0 
            # construct histogram
            title = 'Flux Spectrum (%g)' % flux0sum
            xlab = 'VLSR (km/s)'
            ylab = 'Flux (Jy)'
            myplot.plotter(x,[flux0,flux1],title=title,figname=fluxname,xlab=xlab,ylab=ylab,histo=True)
            dt.tag("flux-spectrum")
            
        self._summary["moments"] = SummaryEntry(momentsummary, "Moment_AT", 
                                                self.id(True), taskargs)
        # get rid of the temporary mask
        if mom0clip > 0.0: 
            utils.rmdir(self.dir("mom0.masked"))

        dt.tag("done")
        dt.end()
Example #27
0
    def run(self):
        """ The run method creates the BDP.

            Parameters
            ----------
            None

            Returns
            -------
            None
        """
        dt = utils.Dtime("ContinuumSub")  # tagging time
        self._summary = {}  # an ADMIT summary will be created here

        contsub = self.getkey("contsub")
        pad = self.getkey("pad")
        fitorder = self.getkey("fitorder")

        # x.im -> x.cim + x.lim

        # b1  = input spw BDP
        # b1a = optional input {Segment,Line}List
        # b1b = optional input Cont Map (now deprecated)
        # b2  = output line cube
        # b3  = output cont map
        b1 = self._bdp_in[0]
        f1 = b1.getimagefile(bt.CASA)

        b1a = self._bdp_in[1]
        # b1b = self._bdp_in[2]
        b1b = None  # do not allow continuum maps to be input

        f2 = self.mkext(f1, 'lim')
        f3 = self.mkext(f1, 'cim')
        f3a = self.mkext(f1, 'cim3d')  # temporary cube name, map is needed
        b2 = SpwCube_BDP(f2)
        b3 = Image_BDP(f3)

        self.addoutput(b2)
        self.addoutput(b3)

        ia = taskinit.iatool()

        ia.open(self.dir(f1))
        s = ia.summary()
        nchan = s['shape'][
            2]  # ingest has guarenteed this to the spectral axis

        if b1a != None:  # if a LineList was given, use that
            if len(b1a.table) > 0:
                # this section of code actually works for len(ch0)==0 as well
                #
                ch0 = b1a.table.getFullColumnByName("startchan")
                ch1 = b1a.table.getFullColumnByName("endchan")
                if pad != 0:  # can widen or narrow the segments
                    if pad > 0:
                        logging.info("pad=%d to widen the segments" % pad)
                    else:
                        logging.info("pad=%d to narrow the segments" % pad)
                    ch0 = np.where(ch0 - pad < 0, 0, ch0 - pad)
                    ch1 = np.where(ch1 + pad >= nchan, nchan - 1, ch1 + pad)
                s = Segments(ch0, ch1, nchan=nchan)
                ch = s.getchannels(
                    True)  # take the complement of lines as the continuum
            else:
                ch = range(
                    nchan
                )  # no lines?  take everything as continuum (probably bad)
                logging.warning(
                    "All channels taken as continuum. Are you sure?")
        elif len(contsub) > 0:  # else if contsub[] was supplied manually
            s = Segments(contsub, nchan=nchan)
            ch = s.getchannels()
        else:
            raise Exception, "No contsub= or input LineList given"

        if len(ch) > 0:
            ia.open(self.dir(f1))
            ia.continuumsub(outline=self.dir(f2),
                            outcont=self.dir(f3a),
                            channels=ch,
                            fitorder=fitorder)
            ia.close()
            dt.tag("continuumsub")
            casa.immoments(
                self.dir(f3a), -1,
                outfile=self.dir(f3))  # mean of the continuum cube (f3a)
            utils.remove(self.dir(f3a))  # is the continuum map (f3)
            dt.tag("immoments")
            if b1b != None:
                # this option is now deprecated (see above, by setting b1b = None), no user option allowed
                # there is likely a mis-match in the beam, given how they are produced. So it's safer to
                # remove this here, and force the flow to smooth manually
                print "Adding back in a continuum map"
                f1b = b1b.getimagefile(bt.CASA)
                f1c = self.mkext(f1, 'sum')
                # @todo   notice we are not checking for conforming mapsize and WCS
                #         and let CASA fail out if we've been bad.
                casa.immath([self.dir(f3), self.dir(f1b)], 'evalexpr',
                            self.dir(f1c), 'IM0+IM1')
                utils.rename(self.dir(f1c), self.dir(f3))
                dt.tag("immath")
        else:
            raise Exception, "No channels left to determine continuum. pad=%d too large?" % pad

        # regression
        rdata = casautil.getdata(self.dir(f3)).data
        logging.regression("CSUB: %f %f" % (rdata.min(), rdata.max()))

        # Create two output images for html and their thumbnails, too
        implot = ImPlot(ptype=self._plot_type,
                        pmode=self._plot_mode,
                        abspath=self.dir())
        implot.plotter(rasterfile=f3, figname=f3, colorwedge=True)
        figname = implot.getFigure(figno=implot.figno, relative=True)
        thumbname = implot.getThumbnail(figno=implot.figno, relative=True)
        b2.setkey("image", Image(images={bt.CASA: f2}))
        b3.setkey("image", Image(images={bt.CASA: f3, bt.PNG: figname}))
        dt.tag("implot")

        if len(ch) > 0:
            taskargs = "pad=%d fitorder=%d contsub=%s" % (pad, fitorder,
                                                          str(contsub))
            imcaption = "Continuum map"
            self._summary["continuumsub"] = SummaryEntry(
                [figname, thumbname, imcaption], "ContinuumSub_AT",
                self.id(True), taskargs)

        dt.tag("done")
        dt.end()
Example #28
0
def imreg(vis=None,
          ephem=None,
          msinfo=None,
          imagefile=None,
          timerange=None,
          reftime=None,
          fitsfile=None,
          beamfile=None,
          offsetfile=None,
          toTb=None,
          sclfactor=1.0,
          verbose=False,
          p_ang=False,
          overwrite=True,
          usephacenter=True,
          deletehistory=False,
          subregion=[],
          docompress=False):
    ''' 
    main routine to register CASA images
           Required Inputs:
               vis: STRING. CASA measurement set from which the image is derived
               imagefile: STRING or LIST. name of the input CASA image
               timerange: STRING or LIST. timerange used to generate the CASA image, must have the same length as the input images. 
                          Each element should be in CASA standard time format, e.g., '2012/03/03/12:00:00~2012/03/03/13:00:00'
           Optional Inputs:
               msinfo: DICTIONARY. CASA MS information, output from read_msinfo. If not provided, generate one from the supplied vis
               ephem: DICTIONARY. solar ephem, output from read_horizons. 
                      If not provided, query JPL Horizons based on time info of the vis (internet connection required)
               fitsfile: STRING or LIST. name of the output registered fits files
               reftime: STRING or LIST. Each element should be in CASA standard time format, e.g., '2012/03/03/12:00:00'
               offsetfile: optionally provide an offset with a series of solar x and y offsets with timestamps 
               toTb: Bool. Convert the default Jy/beam to brightness temperature?
               sclfactor: scale the image values up by its value (to compensate VLA 20 dB attenuator)
               verbose: Bool. Show more diagnostic info if True.
               usephacenter: Bool -- if True, correct for the RA and DEC in the ms file based on solar empheris.
                                     Otherwise assume the phasecenter is correctly pointed to the solar disk center
                                     (EOVSA case)
               subregion: Region selection. See 'help par.region' for details.
    Usage:
    >>> from suncasa.utils import helioimage2fits as hf
    >>> hf.imreg(vis='mydata.ms', imagefile='myimage.image', fitsfile='myimage.fits',
                 timerange='2017/08/21/20:21:10~2017/08/21/20:21:18')
    The output fits file is 'myimage.fits'

    History:
    BC (sometime in 2014): function was first wrote, followed by a number of edits by BC and SY
    BC (2019-07-16): Added checks for stokes parameter. Verified that for converting from Jy/beam to brightness temperature,
                     the convention of 2*k_b*T should always be used. I.e., for unpolarized source, stokes I, RR, LL, XX, YY, 
                     etc. in the output CASA images from (t)clean should all have same values of radio intensity 
                     (in Jy/beam) and brightness temperature (in K).

    '''
    ia = iatool()

    if deletehistory:
        ms_clearhistory(vis)

    if not imagefile:
        raise ValueError('Please specify input image')
    if not timerange:
        raise ValueError('Please specify timerange of the input image')
    if type(imagefile) == str:
        imagefile = [imagefile]
    if type(timerange) == str:
        timerange = [timerange]
    if not fitsfile:
        fitsfile = [img + '.fits' for img in imagefile]
    if type(fitsfile) == str:
        fitsfile = [fitsfile]
    nimg = len(imagefile)
    if len(timerange) != nimg:
        raise ValueError(
            'Number of input images does not equal to number of timeranges!')
    if len(fitsfile) != nimg:
        raise ValueError(
            'Number of input images does not equal to number of output fits files!'
        )
    nimg = len(imagefile)
    if verbose:
        print(str(nimg) + ' images to process...')

    if reftime:  # use as reference time to find solar disk RA and DEC to register the image, but not the actual timerange associated with the image
        if type(reftime) == str:
            reftime = [reftime] * nimg
        if len(reftime) != nimg:
            raise ValueError(
                'Number of reference times does not match that of input images!'
            )
        helio = ephem_to_helio(vis,
                               ephem=ephem,
                               msinfo=msinfo,
                               reftime=reftime,
                               usephacenter=usephacenter)
    else:
        # use the supplied timerange to register the image
        helio = ephem_to_helio(vis,
                               ephem=ephem,
                               msinfo=msinfo,
                               reftime=timerange,
                               usephacenter=usephacenter)

    if toTb:
        (bmajs, bmins, bpas, beamunits,
         bpaunits) = getbeam(imagefile=imagefile, beamfile=beamfile)

    for n, img in enumerate(imagefile):
        if verbose:
            print('processing image #' + str(n) + ' ' + img)
        fitsf = fitsfile[n]
        timeran = timerange[n]
        # obtain duration of the image as FITS header exptime
        try:
            [tbg0, tend0] = timeran.split('~')
            tbg_d = qa.getvalue(qa.convert(qa.totime(tbg0), 'd'))[0]
            tend_d = qa.getvalue(qa.convert(qa.totime(tend0), 'd'))[0]
            tdur_s = (tend_d - tbg_d) * 3600. * 24.
            dateobs = qa.time(qa.quantity(tbg_d, 'd'), form='fits', prec=10)[0]
        except:
            print('Error in converting the input timerange: ' + str(timeran) +
                  '. Proceeding to the next image...')
            continue

        hel = helio[n]
        if not os.path.exists(img):
            warnings.warn('{} does not existed!'.format(img))
        else:
            if os.path.exists(fitsf) and not overwrite:
                raise ValueError(
                    'Specified fits file already exists and overwrite is set to False. Aborting...'
                )
            else:
                p0 = hel['p0']
                tb.open(img + '/logtable', nomodify=False)
                nobs = tb.nrows()
                tb.removerows([i + 1 for i in range(nobs - 1)])
                tb.close()
                ia.open(img)
                imr = ia.rotate(pa=str(-p0) + 'deg')
                if subregion is not []:
                    imr = imr.subimage(region=subregion)
                imr.tofits(fitsf, history=False, overwrite=overwrite)
                imr.close()
                imsum = ia.summary()
                ia.close()
                ia.done()

            # construct the standard fits header
            # RA and DEC of the reference pixel crpix1 and crpix2
            (imra, imdec) = (imsum['refval'][0], imsum['refval'][1])
            # find out the difference of the image center to the CASA phase center
            # RA and DEC difference in arcseconds
            ddec = degrees((imdec - hel['dec_fld'])) * 3600.
            dra = degrees((imra - hel['ra_fld']) * cos(hel['dec_fld'])) * 3600.
            # Convert into image heliocentric offsets
            prad = -radians(hel['p0'])
            dx = (-dra) * cos(prad) - ddec * sin(prad)
            dy = (-dra) * sin(prad) + ddec * cos(prad)
            if offsetfile:
                try:
                    offset = np.load(offsetfile)
                except:
                    raise ValueError(
                        'The specified offsetfile does not exist!')
                reftimes_d = offset['reftimes_d']
                xoffs = offset['xoffs']
                yoffs = offset['yoffs']
                timg_d = hel['reftime']
                ind = bisect.bisect_left(reftimes_d, timg_d)
                xoff = xoffs[ind - 1]
                yoff = yoffs[ind - 1]
            else:
                xoff = hel['refx']
                yoff = hel['refy']
            if verbose:
                print(
                    'offset of image phase center to visibility phase center (arcsec): dx={0:.2f}, dy={1:.2f}'
                    .format(dx, dy))
                print(
                    'offset of visibility phase center to solar disk center (arcsec): dx={0:.2f}, dy={1:.2f}'
                    .format(xoff, yoff))
            (crval1, crval2) = (xoff + dx, yoff + dy)
            # update the fits header to heliocentric coordinates

            hdu = pyfits.open(fitsf, mode='update')
            hdu[0].verify('fix')
            header = hdu[0].header
            dshape = hdu[0].data.shape
            ndim = hdu[0].data.ndim
            (cdelt1,
             cdelt2) = (-header['cdelt1'] * 3600., header['cdelt2'] * 3600.
                        )  # Original CDELT1, 2 are for RA and DEC in degrees
            header['cdelt1'] = cdelt1
            header['cdelt2'] = cdelt2
            header['cunit1'] = 'arcsec'
            header['cunit2'] = 'arcsec'
            header['crval1'] = crval1
            header['crval2'] = crval2
            header['ctype1'] = 'HPLN-TAN'
            header['ctype2'] = 'HPLT-TAN'
            header['date-obs'] = dateobs  # begin time of the image
            if not p_ang:
                hel['p0'] = 0
            try:
                # this works for pyfits version of CASA 4.7.0 but not CASA 4.6.0
                if tdur_s:
                    header.set('exptime', tdur_s)
                else:
                    header.set('exptime', 1.)
                header.set('p_angle', hel['p0'])
                header.set(
                    'dsun_obs',
                    sun.sunearth_distance(Time(dateobs)).to(u.meter).value)
                header.set(
                    'rsun_obs',
                    sun.solar_semidiameter_angular_size(Time(dateobs)).value)
                header.set('rsun_ref', sun.constants.radius.value)
                header.set('hgln_obs', 0.)
                header.set(
                    'hglt_obs',
                    sun.heliographic_solar_center(Time(dateobs))[1].value)
            except:
                # this works for astropy.io.fits
                if tdur_s:
                    header.append(('exptime', tdur_s))
                else:
                    header.append(('exptime', 1.))
                header.append(('p_angle', hel['p0']))
                header.append(
                    ('dsun_obs',
                     sun.sunearth_distance(Time(dateobs)).to(u.meter).value))
                header.append(
                    ('rsun_obs',
                     sun.solar_semidiameter_angular_size(Time(dateobs)).value))
                header.append(('rsun_ref', sun.constants.radius.value))
                header.append(('hgln_obs', 0.))
                header.append(
                    ('hglt_obs',
                     sun.heliographic_solar_center(Time(dateobs))[1].value))

            # check if stokes parameter exist
            exist_stokes = False
            stokes_mapper = {
                'I': 1,
                'Q': 2,
                'U': 3,
                'V': 4,
                'RR': -1,
                'LL': -2,
                'RL': -3,
                'LR': -4,
                'XX': -5,
                'YY': -6,
                'XY': -7,
                'YX': -8
            }
            if 'CRVAL3' in header.keys():
                if header['CTYPE3'] == 'STOKES':
                    stokenum = header['CRVAL3']
                    exist_stokes = True
            if 'CRVAL4' in header.keys():
                if header['CTYPE4'] == 'STOKES':
                    stokenum = header['CRVAL4']
                    exist_stokes = True
            if exist_stokes:
                stokesstr = stokes_mapper.keys()[stokes_mapper.values().index(
                    stokenum)]
                if verbose:
                    print('This image is in Stokes ' + stokesstr)
            else:
                print(
                    'STOKES Information does not seem to exist! Assuming Stokes I'
                )
                stokenum = 1

            # intensity units to brightness temperature
            if toTb:
                # get restoring beam info
                bmaj = bmajs[n]
                bmin = bmins[n]
                beamunit = beamunits[n]
                data = hdu[
                    0].data  # remember the data order is reversed due to the FITS convension
                keys = header.keys()
                values = header.values()
                # which axis is frequency?
                faxis = keys[values.index('FREQ')][-1]
                faxis_ind = ndim - int(faxis)
                # find out the polarization of this image
                k_b = qa.constants('k')['value']
                c_l = qa.constants('c')['value']
                # Always use 2*kb for all polarizations
                const = 2. * k_b / c_l**2
                if header['BUNIT'].lower() == 'jy/beam':
                    header['BUNIT'] = 'K'
                    header['BTYPE'] = 'Brightness Temperature'
                    for i in range(dshape[faxis_ind]):
                        nu = header['CRVAL' +
                                    faxis] + header['CDELT' + faxis] * (
                                        i + 1 - header['CRPIX' + faxis])
                        if header['CUNIT' + faxis] == 'KHz':
                            nu *= 1e3
                        if header['CUNIT' + faxis] == 'MHz':
                            nu *= 1e6
                        if header['CUNIT' + faxis] == 'GHz':
                            nu *= 1e9
                        if len(bmaj) > 1:  # multiple (per-plane) beams
                            bmajtmp = bmaj[i]
                            bmintmp = bmin[i]
                        else:  # one single beam
                            bmajtmp = bmaj[0]
                            bmintmp = bmin[0]
                        if beamunit == 'arcsec':
                            bmaj0 = np.radians(bmajtmp / 3600.)
                            bmin0 = np.radians(bmintmp / 3600.)
                        if beamunit == 'arcmin':
                            bmaj0 = np.radians(bmajtmp / 60.)
                            bmin0 = np.radians(bmintmp / 60.)
                        if beamunit == 'deg':
                            bmaj0 = np.radians(bmajtmp)
                            bmin0 = np.radians(bmintmp)
                        if beamunit == 'rad':
                            bmaj0 = bmajtmp
                            bmin0 = bmintmp
                        beam_area = bmaj0 * bmin0 * np.pi / (4. * log(2.))
                        factor = const * nu**2  # SI unit
                        jy_to_si = 1e-26
                        # print(nu/1e9, beam_area, factor)
                        factor2 = sclfactor
                        # if sclfactor:
                        #     factor2 = 100.
                        if faxis == '3':
                            data[:,
                                 i, :, :] *= jy_to_si / beam_area / factor * factor2
                        if faxis == '4':
                            data[
                                i, :, :, :] *= jy_to_si / beam_area / factor * factor2

            header = fu.headerfix(header)
            hdu.flush()
            hdu.close()

            if ndim - np.count_nonzero(np.array(dshape) == 1) > 3:
                docompress = False
                '''
                    Caveat: only 1D, 2D, or 3D images are currently supported by
                    the astropy fits compression. If a n-dimensional image data array
                    does not have at least n-3 single-dimensional entries,
                    force docompress to be False
                '''

                print(
                    'warning: The fits data contains more than 3 non squeezable dimensions. Skipping fits compression..'
                )
            if docompress:
                fitsftmp = fitsf + ".tmp.fits"
                os.system("mv {} {}".format(fitsf, fitsftmp))
                hdu = pyfits.open(fitsftmp)
                hdu[0].verify('fix')
                header = hdu[0].header
                data = hdu[0].data
                fu.write_compressed_image_fits(fitsf,
                                               data,
                                               header,
                                               compression_type='RICE_1',
                                               quantize_level=4.0)
                os.system("rm -rf {}".format(fitsftmp))
    if deletehistory:
        ms_restorehistory(vis)
    return fitsfile
Example #29
0
from taskinit import msmdtool, iatool, casalog, tbtool

from tclean_cli import tclean_cli as tclean
from flagdata_cli import flagdata_cli as flagdata
from ft_cli import ft_cli as ft
from gaincal_cli import gaincal_cli as gaincal
from applycal_cli import applycal_cli as applycal
from concat_cli import concat_cli as concat
from importfits_cli import importfits_cli as importfits
from imhead_cli import imhead_cli as imhead
from makemask_cli import makemask_cli as makemask
from exportfits_cli import exportfits_cli as exportfits
from importfits_cli import importfits_cli as importfits
from clearcal_cli import clearcal_cli as clearcal
from split_cli import split_cli as split
ia = iatool()
msmd = msmdtool()
tb = tbtool()


from astropy.io import fits
from astropy import wcs

mses = list(Qmses.keys())

fullpath_mses = ['../' + ms[:-3] + "_continuum.ms"
                 for ms in mses if ms in Qmses]

raw_and_corr_vis = 'continuum_concatenated_raw_and_corr.ms'
if not os.path.exists(raw_and_corr_vis):
    assert concat(vis=fullpath_mses, concatvis=raw_and_corr_vis,
Example #30
0
def implot(rasterfile, figname, contourfile=None, plottype=PlotControl.PNG,
           plotmode=PlotControl.BATCH, colorwedge=False, zoom=1):
    """Wrapper for CASA's imview, that will also create thumbnails.

       Parameters
       ----------
       rasterfile : str
         Fully-qualified image file to use as raster map.  Optional if
         contourfile is given.

       figname : str
           Fully-qualified output file name.

       contourflie : str
         Fully-qualified image file to use as contour map. Contours
         will be overlaid on rasterfile if both are given.  Optional if
         rasterfile is given.

       plottype : int
         Plotting format, one of util.PlotControl plot type (e.g., PlotControl.PNG). Default: PlotControl.PNG

       plotmode : int
         Plot mode, one of util.PlotControl plot mode (e.g., PlotControl.INTERACTIVE). Default: PlotControl.BATCH

       colorwedge  : boolean
         True - show color wedge, False - don't show color wedge

       zoom : int
         Image zoom ratio.

       Returns
       -------
       None

    """

    # axes dictionary is ignored by imview!
    #xlab=NONE,ylab=NONE 

    #can't support this until imview out= is fixed! (see below)
    #orientation=PlotControl.LANDSCAPE 


    if plotmode == PlotControl.NOPLOT:  return

    if contourfile==None and rasterfile==None:
       raise Exception, "You must provide rasterfile and/or contourfile"

    if not PlotControl.isSupportedType(plottype):
       raise Exception, "Unrecognized plot type %d. See util.PlotControl" % plottype
    if plottype == PlotControl.SVG or plottype == PlotControl.GIF:
       raise Exception, "CASA viewer does not support SVG and GIF format :-("

    if plottype == PlotControl.JPG:
       raise Exception, "CASA viewer claims to support JPG but doens't :-("

    if plottype != PlotControl.PNG:
       raise Exception, "Thumbnails not supported (by matplotlib) for types other than PNG"


    DEFAULT_SCALING = -1  # scaling power cycles.

    # Grmph. axes dictionary ignored in imview.
    #    axes={'y':ylab, 'x':xlab}

    contour = {}
    if contourfile: contour={'file':contourfile}

    raster  = {}
    if rasterfile:
        raster={'file'       : rasterfile,  
                'colorwedge' : colorwedge, 
                'scaling'    : DEFAULT_SCALING}

    # Grmph. Giving an 'out' dictionary to imview causes SEVERE error.
    #out={'file'   : figname,
    #     'format' : PlotControl.mkext(plottype,False),
    #     'scale'  : 2.0,
    #     'dpi'    : 1.0,
    #     'orient' : orientation}
         #dpi/scale - DPI used for PS/EPS, scale for others. Perhaps 
         #            we will enable these options later

    # These are completely ignored, but imview throws an exception
    # axes is not provided!
    axes = {'x':'x','y':'y','z':'z'}

    # work around this axis labeling problem?
    ia = taskinit.iatool()
    ia.open(rasterfile)
    h = ia.summary()
    ia.close()
    #print "PJT: implot axisnames:",h['axisnames'][0],h['axisnames'][1]
    if h['axisnames'][1]=='Frequency':
        axes['y'] = 'Frequency'

    # this complained about 'dimensions of axes must be strings (x is not)'
    # axes = { 'x' : h['axisnames'][0],  'y' : h['axisnames'][1] , 'z' : 'z' }

    
    casa_imview(raster=raster, contour=contour, out=figname, axes=axes,
                zoom=zoom)

    #of = PlotControl.mkext(plottype,dot=False)
    #casa.viewer(outfile=outfile, infile=imagename, gui=False, plottype=of)
    if plotmode == PlotControl.INTERACTIVE or plotmode==PlotControl.SHOW_AT_END:
        casa_imview(raster=raster, contour=contour,axes=axes)