Example #1
0
def __combineResiduals(residualImages,fluxImages,iteration=0):

    from casa import immath
    imageList = []
    num = '('
    den = '('
    for i in range(len(residualImages)):
        imageList.append(residualImages[i])
        imageList.append(fluxImages[i])
        tempNum = 'IM'+str(2*i)+'*IM'+str(2*i+1)
        tempDen = 'IM'+str(2*i+1)
        if i < len(residualImages) -1:
            num = num + tempNum + '+'
            den = den + tempDen + '+'
        else:
            num = num + tempNum + ')'
            den = den + tempDen + ')'
    expr = num + '/' + den

    filename = 'residualCombine.' + str(iteration) + '.im'

    try:
       with open(filename + '/table.f0'):
           combine = False
           print 'File ' + filename + ' exists'
    except IOError:
        combine = True

    if combine:
        immath(imagename=imageList,
           outfile=filename,
           expr=expr)
    return filename
Example #2
0
def __combineModels(modelImages,iteration=0):
    from casa import immath

    filename = 'modelCombine.' + str(iteration)
    try:
       with open(filename + '/table.f0'):
           combine = False
           print 'File ' + filename + ' exists'
    except IOError:
        combine = True

    if combine:
        immath(imagename=modelImages,
            outfile=filename,
            expr = 'IM0 + IM1')
    return filename
Example #3
0
def make_SBimage(image='',cellsize=None,beamsize=None,distance=None,
                 extension='.image'):
    from casa import immath,imhead

    beamarea = 1.13*beamsize**2/cellsize**2
    massCoeff = 2.36e5 * distance**2 / beamarea
    pcPerArcsec = distance *1e6 / 206265.
    pcPerPixel = cellsize * pcPerArcsec
    pixSB = massCoeff / pcPerPixel**2 # units in Msun per pixel

    immath(imagename=image + extension,
       outfile=image + '.sb.image',
       mode='evalexpr',
       expr='IM0*' + str(pixSB))
    imhead(imagename=image + '.sb.image',
           mode='put',
           hdkey='bunit',
           hdvalue='',
           hdcomment='Units Msun/pc^-2')
Example #4
0
def make_SBimage(image='',
                 cellsize=None,
                 beamsize=None,
                 distance=None,
                 extension='.image'):
    from casa import immath, imhead

    beamarea = 1.13 * beamsize**2 / cellsize**2
    massCoeff = 2.36e5 * distance**2 / beamarea
    pcPerArcsec = distance * 1e6 / 206265.
    pcPerPixel = cellsize * pcPerArcsec
    pixSB = massCoeff / pcPerPixel**2  # units in Msun per pixel

    immath(imagename=image + extension,
           outfile=image + '.sb.image',
           mode='evalexpr',
           expr='IM0*' + str(pixSB))
    imhead(imagename=image + '.sb.image',
           mode='put',
           hdkey='bunit',
           hdvalue='',
           hdcomment='Units Msun/pc^-2')
Example #5
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)

        taskinit.ia.open(self.dir(f1))
        s = taskinit.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:
            taskinit.ia.open(self.dir(f1))
            taskinit.ia.continuumsub(outline=self.dir(f2),
                                     outcont=self.dir(f3a),
                                     channels=ch,
                                     fitorder=fitorder)
            taskinit.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 #6
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)

        taskinit.ia.open(self.dir(f1))
        s = taskinit.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:
            taskinit.ia.open(self.dir(f1))
            taskinit.ia.continuumsub(outline=self.dir(f2),outcont=self.dir(f3a),channels=ch,fitorder=fitorder)
            taskinit.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 #7
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)

        f1 =  b1.getimagefile(bt.CASA)
        taskinit.ia.open(self.dir(f1))
        s = taskinit.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"
            taskinit.ia.fromimage(infile=file, outfile=mask)
            nx = taskinit.ia.shape()[0]
            ny = taskinit.ia.shape()[1]
            nchan = taskinit.ia.shape()[2]
            taskinit.ia.fromshape(shape=[nx,ny,1])
            plane = taskinit.ia.getchunk([0,0,0],[-1,-1,0])     # convenience plane for masking operation
            dt.tag("mask_sig")

            taskinit.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]:
                            taskinit.ia.putchunk(plane*0+sigma_array[i],blc=[0,0,i,-1])
                            count = count + 1
                        else:
                            taskinit.ia.putchunk(plane*0+maxval,blc=[0,0,i,-1])                            
                    else:
                        taskinit.ia.putchunk(plane*0+sigma_array[i],blc=[0,0,i,-1])
                        count = count + 1
                else:
                    taskinit.ia.putchunk(plane*0+maxval,blc=[0,0,i,-1])
            taskinit.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")

        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
        taskinit.ia.open(image_out)
        st = taskinit.ia.statistics()
        taskinit.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)
        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)
        
        dt.tag("done")
        dt.end()
Example #8
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 #9
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

        # 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")

        if file_is_casa:
            taskinit.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
                    taskinit.ia.fromfits('_pbcor',fni,overwrite=True)
                    taskinit.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 
                            taskinit.ia.open('_pb')
                            taskinit.ia.summary()
                            ia1=taskinit.ia.moments(moments=[-1],drop=True,outfile=fno2)
                            ia1.done()
                            taskinit.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 True:
                    # 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))
                    taskinit.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)
                    dt.tag("importfits")
            taskinit.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'
                taskinit.ia.convolve2d(outfile=fnos, overwrite=True, pa='0deg',
                                       major='%gpix' % smooth[0], minor='%gpix' % smooth[1], type='gaussian')
                taskinit.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")
                taskinit.ia.open(fno)

            s = taskinit.ia.summary()
            if len(s['shape']) != 4:
                logging.warning("Adding dummy STOKES-I axis")
                fnot = fno + '_4'
                taskinit.ia.adddegaxes(stokes='I',outfile=fnot)
                taskinit.ia.close()
                #@todo use safer ia.rename() here.
                # https://casa.nrao.edu/docs/CasaRef/image.rename.html
                utils.rename(fnot,fno)
                taskinit.ia.open(fno)
                dt.tag("adddegaxes")
            else:
                logging.info("SHAPE: %s" % str(s['shape']))
        s = taskinit.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 = taskinit.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 = taskinit.rg.box([box[0],box[1]] , [box[2],box[3]])
                elif len(edge) == 2:
                    # select an XY box, but remove some edge channels
                    r1 = taskinit.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 = taskinit.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 = taskinit.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 = taskinit.ia.subimage(region=r1,outfile=fno+'.box',overwrite=True)
            taskinit.ia.close()
            taskinit.ia.done()
            subimage.rename(fno,overwrite=True)
            subimage.close()
            subimage.done()
            taskinit.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
                taskinit.ia.subimage(overwrite=True,outfile=fno)
                taskinit.ia.close()
                taskinit.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))
            taskinit.ia.calcmask('"%s" != 0.0' % fno)
            dt.tag("mask")

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

        # do a fast statistics (no median or robust)
        s0 = taskinit.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 s.has_key('perplanebeams'):
            # 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']
                taskinit.ia.setrestoringbeam(remove=True)
                taskinit.ia.setrestoringbeam(beam=b)
                commonbeam = taskinit.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 = taskinit.ia.commonbeam()
                taskinit.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 = taskinit.ia.commonbeam()
            except:
                nppb = 4.0
                logging.warning("No synthesized beam found, faking one to prevent downstream problems: nppb=%f" % nppb)
                s = taskinit.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
                taskinit.ia.setrestoringbeam(major='%farcsec' % bmaj, minor='%farcsec' % bmin, pa='%fdeg' % bpa)
                commonbeam = {}
        logging.info("COMMONBEAM[%d] %s" % (len(commonbeam),str(commonbeam)))

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

        taskinit.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'
                    taskinit.ia.open(fno)
                    s = taskinit.ia.summary()
                    taskinit.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)
        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()) 
        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 h.has_key('restfreq'):
                fr = float(h['restfreq'][0])
            else:
                fr = fc
            fw = df*float(shape[2])
            dv = -df/fr*utils.c 
            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 h.has_key('restfreq'):
            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
                c = utils.c             # 299792.458 km/s
                vlsrc = c*(1-fc/fr)     # @todo rel frame?
                vlsrw = dv*float(shape[2])
                if restfreq > 0:
                    vlsrf = c*(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 #10
0
def blankcube(image,
              dummyMS,
              smooth=True,
              verbose=True,
              region='centerbox[[10h21m45s,18.05.14.9],[15arcmin,15arcmin]]',
              ruthless=False,
              extension='.image',
              beamround='int',
              blankThreshold=2.5,
              moments=[0]):
    '''
    Parameters
    ----------
    image : string
        Base name of image. If target image has extension other than '.image',
        change the extension keyword.

    dummyMS : string
        MS file required for blanking process in CASA

    smooth : bool, optional
        Smooth the image to a circular beam before blanking?
        Default True

    verbose : bool, optional

    region : string, optional
        region parameter featured in CASA

    ruthless : bool, optional
        Delete previous outputs from blankcube

    extension : string, optional
        Extension of the target image. Must include the '.', e.g., '.restored'
        Default '.image'

    beamround : string,float
        P

    blankThreshold : float, optional
        Initial blanking threshold of all pixels scaled by the standard
        deviation times the blankingthreshold
        Default = 2.5 (Walter et al. 2008)

    moments : list, optional
        Moments to calculate from cube. Options are 0,1,2.
        Example: [0,1,2]
        Default: [0]

    Returns
    -------
    out : null

    Examples
    --------

    '''

    from casa import immath, imsmooth, immoments
    from casa import image as ia
    from casa import imager as im
    from casa import quanta as qa
    import os
    import numpy as np

    # Delete files associated with previous runs
    if ruthless:
        os.system('rm -rf ' + image + '.smooth.blk.image')
        os.system('rm -rf ' + image + '.smooth.image')
        os.system('rm -rf ' + image + '.blk.image')
        os.system('rm -rf ' + image + '.mask')

    imageDir = './'

    # Create moment maps
    mom0, mom1, mom2 = False, False, False
    if len(moments) > 0:
        for i, moment in enumerate(moments):
            if moment == 0:
                mom0 = True
            if moment == 1:
                mom1 = True
            if moment == 2:
                mom2 = True
    if mom1 == True or mom2 == True:
        beamScale = 1.01
    elif mom0 == True:
        beamScale = 2.

    # determine beamsize of cube
    ia.open(imageDir + image + extension)
    beamsizes = np.zeros(ia.shape()[2])
    for i in range(ia.shape()[2]):
        beamsizes[i] = ia.restoringbeam(channel=i)['major']['value']
    beamsizeUnit = ia.restoringbeam(channel=i)['major']['unit']
    beamsize = qa.convert(str(beamsizes.max()) + beamsizeUnit,
                          'arcsec')['value']
    if type(beamround) == float:
        beamsize_smooth = beamround * beamsize
    else:
        beamsize_smooth = np.ceil(beamsize)  #beamsize_smooth = 1.01 * beamsize
    ia.close()

    if verbose:
        print 'Max beamsize is ' + str(beamsize) + '"'

    if not os.path.exists(image + '.blk.image'):
        # create cube for blanking
        if smooth:  # smooth to a larger beam if the user desires
            if verbose:
                print 'Convolving to ' + str(beamsize_smooth) + '"'

            imsmooth(imagename=image + extension,
                     outfile=image + '.blk.image',
                     major=str(beamsize_smooth) + 'arcsec',
                     minor=str(beamsize_smooth) + 'arcsec',
                     region=region,
                     pa='0deg',
                     targetres=True)
        else:  # do no smooth
            immath(imagename=image + extension,
                   outfile=image + '.blk.image',
                   mode='evalexpr',
                   region=region,
                   expr='IM0')

    if not os.path.exists(image + '.smooth.image'):
        # convolve cube to 2X beam for blanking
        imsmooth(imagename=image + extension,
                 outfile=image + '.smooth.image',
                 major=str(beamsize * beamScale) + 'arcsec',
                 minor=str(beamsize * beamScale) + 'arcsec',
                 pa='0deg',
                 region=region,
                 targetres=True)

    # determine threshold of cube
    ia.open(image + '.smooth.image')
    threshold = ia.statistics()['sigma'][0] * blankThreshold
    ia.close()

    # blank the cube at threshold*sigma
    ia.open(image + '.smooth.image')
    ia.calcmask(mask=image + '.smooth.image > ' + str(threshold), name='mask1')
    wait = 'waits for calcmask to close'
    ia.close()

    # hand blank the cube
    im.open(dummyMS)
    pause = None
    while pause is None:
        im.drawmask(image=image + '.smooth.image', mask=image + '.mask')
        pause = 0
    im.close

    # mask contains values of 0 and 1, change to a mask with only values of 1
    ia.open(image + '.mask')
    ia.calcmask(image + '.mask' + '>0.5')
    ia.close()

    # apply mask on smoothed image
    immath(imagename=image + '.smooth.image',
           outfile=image + '.smooth.blk.image',
           mode='evalexpr',
           mask='mask(' + image + '.mask)',
           expr='IM0')

    # apply mask on image
    ia.open(imageDir + image + '.blk.image')
    ia.maskhandler('copy', [image + '.smooth.blk.image:mask0', 'newmask'])
    ia.maskhandler('set', 'newmask')
    ia.done()

    cube = '.blk.image'  # specify name of cube for moment calculation

    # create moment 0 map
    if mom0:
        if ruthless:
            os.system('rm -rf ' + image + '.mom0.image')
        immoments(imagename=image + cube,
                  moments=[0],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom0.image')

    # create moment 1 map
    if mom1:
        if ruthless:
            os.system('rm -rf ' + image + '.mom1.image')
        immoments(imagename=image + cube,
                  moments=[1],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom1.image')

    # create moment 2 map
    if mom2:
        if ruthless:
            os.system('rm -rf ' + image + '.mom2.image')
        immoments(imagename=image + cube,
                  moments=[2],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom2.image')

    if verbose and mom0:
        from casa import imstat
        flux = imstat(image + '.mom0.image')['flux'][0]
        ia.open(image + '.mom0.image')
        beammaj = ia.restoringbeam(channel=0)['major']['value']
        beammin = ia.restoringbeam(channel=0)['minor']['value']
        beamsizeUnit = ia.restoringbeam(channel=0)['major']['unit']
        ia.close()
        print 'Moment Image: ' + str(image) + '.mom0.image'
        print 'Beamsize: ' + str(beammaj) + '" X ' + str(beammin) + '"'
        print 'Flux: ' + str(flux) + ' Jy km/s'
Example #11
0
def blankcube(image,
              dummyMS,
              smooth=True,
              verbose=True,
              region='centerbox[[10h21m45s,18.05.14.9],[15arcmin,15arcmin]]',
              ruthless=False,
              extension='.image',
              beamround='int',
              blankThreshold=2.5,
              moments=[0]):

    '''
    Parameters
    ----------
    image : string
        Base name of image. If target image has extension other than '.image',
        change the extension keyword.

    dummyMS : string
        MS file required for blanking process in CASA

    smooth : bool, optional
        Smooth the image to a circular beam before blanking?
        Default True

    verbose : bool, optional

    region : string, optional
        region parameter featured in CASA

    ruthless : bool, optional
        Delete previous outputs from blankcube

    extension : string, optional
        Extension of the target image. Must include the '.', e.g., '.restored'
        Default '.image'

    beamround : string,float
        P

    blankThreshold : float, optional
        Initial blanking threshold of all pixels scaled by the standard
        deviation times the blankingthreshold
        Default = 2.5 (Walter et al. 2008)

    moments : list, optional
        Moments to calculate from cube. Options are 0,1,2.
        Example: [0,1,2]
        Default: [0]

    Returns
    -------
    out : null

    Examples
    --------

    '''

    from casa import immath,imsmooth,immoments
    from casa import image as ia
    from casa import imager as im
    from casa import quanta as qa
    import os
    import numpy as np

    # Delete files associated with previous runs
    if ruthless:
        os.system('rm -rf ' + image + '.smooth.blk.image')
        os.system('rm -rf ' + image + '.smooth.image')
        os.system('rm -rf ' + image + '.blk.image')
        os.system('rm -rf ' + image + '.mask')

    imageDir = './'

    # Create moment maps
    mom0,mom1,mom2 = False,False,False
    if len(moments) > 0:
        for i, moment in enumerate(moments):
            if moment == 0:
                mom0 = True
            if moment == 1:
                mom1 = True
            if moment == 2:
                mom2 = True
    if mom1 == True or mom2 == True:
        beamScale = 1.01
    elif mom0 == True:
        beamScale = 2.

    # determine beamsize of cube
    ia.open(imageDir + image + extension)
    beamsizes = np.zeros(ia.shape()[2])
    for i in range(ia.shape()[2]):
        beamsizes[i] = ia.restoringbeam(channel=i)['major']['value']
    beamsizeUnit = ia.restoringbeam(channel=i)['major']['unit']
    beamsize = qa.convert(str(beamsizes.max()) + beamsizeUnit,'arcsec')['value']
    if type(beamround) == float:
        beamsize_smooth = beamround*beamsize
    else:
        beamsize_smooth = np.ceil(beamsize)   #beamsize_smooth = 1.01 * beamsize
    ia.close()

    if verbose:
        print 'Max beamsize is ' + str(beamsize) + '"'

    if not os.path.exists(image + '.blk.image'):
        # create cube for blanking
        if smooth: # smooth to a larger beam if the user desires
            if verbose:
                print 'Convolving to ' + str(beamsize_smooth) + '"'

            imsmooth(imagename=image + extension,
                     outfile=image + '.blk.image',
                     major=str(beamsize_smooth) + 'arcsec',
                     minor=str(beamsize_smooth) + 'arcsec',
                     region=region,
                     pa='0deg',
                     targetres=True)
        else: # do no smooth
            immath(imagename=image + extension,
               outfile=image + '.blk.image',
                mode='evalexpr',
                region=region,
                expr='IM0')

    if not os.path.exists(image + '.smooth.image'):
        # convolve cube to 2X beam for blanking
        imsmooth(imagename=image + extension,
             outfile=image + '.smooth.image',
             major=str(beamsize*beamScale) + 'arcsec',
             minor=str(beamsize*beamScale) + 'arcsec',
             pa='0deg',
             region=region,
             targetres=True)

    # determine threshold of cube
    ia.open(image + '.smooth.image')
    threshold = ia.statistics()['sigma'][0] * blankThreshold
    ia.close()

    # blank the cube at threshold*sigma
    ia.open(image + '.smooth.image')
    ia.calcmask(mask=image + '.smooth.image > ' + str(threshold),
             name='mask1')
    wait = 'waits for calcmask to close'
    ia.close()

    # hand blank the cube
    im.open(dummyMS)
    pause = None
    while pause is None:
        im.drawmask(image=image + '.smooth.image',
                           mask=image + '.mask')
        pause = 0
    im.close

    # mask contains values of 0 and 1, change to a mask with only values of 1
    ia.open(image + '.mask')
    ia.calcmask(image + '.mask' + '>0.5')
    ia.close()

    # apply mask on smoothed image
    immath(imagename=image + '.smooth.image',
       outfile=image + '.smooth.blk.image',
       mode='evalexpr',
       mask='mask(' + image + '.mask)',
       expr='IM0')

    # apply mask on image
    ia.open(imageDir + image + '.blk.image')
    ia.maskhandler('copy',[image + '.smooth.blk.image:mask0', 'newmask'])
    ia.maskhandler('set','newmask')
    ia.done()

    cube = '.blk.image' # specify name of cube for moment calculation


    # create moment 0 map
    if mom0:
        if ruthless:
            os.system('rm -rf ' + image + '.mom0.image')
        immoments(imagename=image + cube,
                  moments=[0],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom0.image')

    # create moment 1 map
    if mom1:
        if ruthless:
            os.system('rm -rf ' + image + '.mom1.image')
        immoments(imagename=image + cube,
                  moments=[1],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom1.image')

    # create moment 2 map
    if mom2:
        if ruthless:
            os.system('rm -rf ' + image + '.mom2.image')
        immoments(imagename=image + cube,
                  moments=[2],
                  axis='spectra',
                  chans='',
                  mask='mask(' + image + cube + ')',
                  outfile=image + '.mom2.image')

    if verbose and mom0:
	from casa import imstat
        flux = imstat(image + '.mom0.image')['flux'][0]
        ia.open(image + '.mom0.image')
        beammaj = ia.restoringbeam(channel=0)['major']['value']
        beammin = ia.restoringbeam(channel=0)['minor']['value']
        beamsizeUnit = ia.restoringbeam(channel=0)['major']['unit']
        ia.close()
        print 'Moment Image: ' + str(image) + '.mom0.image'
        print 'Beamsize: ' + str(beammaj) + '" X ' + str(beammin) + '"'
        print 'Flux: ' + str(flux) + ' Jy km/s'
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)

        f1 = b1.getimagefile(bt.CASA)
        taskinit.ia.open(self.dir(f1))
        s = taskinit.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"
            taskinit.ia.fromimage(infile=file, outfile=mask)
            nx = taskinit.ia.shape()[0]
            ny = taskinit.ia.shape()[1]
            nchan = taskinit.ia.shape()[2]
            taskinit.ia.fromshape(shape=[nx, ny, 1])
            plane = taskinit.ia.getchunk(
                [0, 0, 0],
                [-1, -1, 0])  # convenience plane for masking operation
            dt.tag("mask_sig")

            taskinit.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]:
                            taskinit.ia.putchunk(plane * 0 + sigma_array[i],
                                                 blc=[0, 0, i, -1])
                            count = count + 1
                        else:
                            taskinit.ia.putchunk(plane * 0 + maxval,
                                                 blc=[0, 0, i, -1])
                    else:
                        taskinit.ia.putchunk(plane * 0 + sigma_array[i],
                                             blc=[0, 0, i, -1])
                        count = count + 1
                else:
                    taskinit.ia.putchunk(plane * 0 + maxval, blc=[0, 0, i, -1])
            taskinit.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")

        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
        taskinit.ia.open(image_out)
        st = taskinit.ia.statistics()
        taskinit.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)
        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)

        dt.tag("done")
        dt.end()
Example #13
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

        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
            taskinit.ia.open(self.dir("mom0.masked"))
            defmask = taskinit.ia.maskhandler('default')
            taskinit.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]))
                taskinit.ia.open(self.dir(imagename))
                taskinit.ia.maskhandler('set', defmask)
                taskinit.ia.close()
                dt.tag("makemask")
            if mom == 0:
                beamarea = nppb(self.dir(imagename))
            implot.plotter(rasterfile=imagename,figname=figname,colorwedge=True)
            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()