Exemple #1
0
def run_rmsynth(data,
                polyOrd=3,
                phiMax_radm2=None,
                dPhi_radm2=None,
                nSamples=10.0,
                weightType="variance",
                fitRMSF=False,
                noStokesI=False,
                phiNoise_radm2=1e6,
                nBits=32,
                showPlots=False,
                debug=False,
                verbose=False,
                log=print,
                units='Jy/beam',
                e_num=1):
    """Run RM synthesis on 1D data.

    Args:
        data (list): Contains frequency and polarization data as either:
            [freq_Hz, I, Q, U, dI, dQ, dU]
                freq_Hz (array_like): Frequency of each channel in Hz.
                I (array_like): Stokes I intensity in each channel.
                Q (array_like): Stokes Q intensity in each channel.
                U (array_like): Stokes U intensity in each channel.
                dI (array_like): Error in Stokes I intensity in each channel.
                dQ (array_like): Error in Stokes Q intensity in each channel.
                dU (array_like): Error in Stokes U intensity in each channel.
            or
            [freq_Hz, q, u,  dq, du]
                freq_Hz (array_like): Frequency of each channel in Hz.
                q (array_like): Fractional Stokes Q intensity (Q/I) in each channel.
                u (array_like): Fractional Stokes U intensity (U/I) in each channel.
                dq (array_like): Error in fractional Stokes Q intensity in each channel.
                du (array_like): Error in fractional Stokes U intensity in each channel.

    Kwargs:
        polyOrd (int): Order of polynomial to fit to Stokes I spectrum.
        phiMax_radm2 (float): Maximum absolute Faraday depth (rad/m^2).
        dPhi_radm2 (float): Faraday depth channel size (rad/m^2).
        nSamples (float): Number of samples across the RMSF.
        weightType (str): Can be "variance" or "uniform"
            "variance" -- Weight by uncertainty in Q and U.
            "uniform" -- Weight uniformly (i.e. with 1s)
        fitRMSF (bool): Fit a Gaussian to the RMSF?
        noStokesI (bool: Is Stokes I data provided?
        phiNoise_radm2 (float): ????
        nBits (int): Precision of floating point numbers.
        showPlots (bool): Show plots?
        debug (bool): Turn on debugging messages & plots?
        verbose (bool): Verbosity.
        log (function): Which logging function to use.
        units (str): Units of data.

    Returns:
        mDict (dict): Summary of RM synthesis results.
        aDict (dict): Data output by RM synthesis.

    """

    # Default data types
    dtFloat = "float" + str(nBits)
    dtComplex = "complex" + str(2 * nBits)

    # freq_Hz, I, Q, U, dI, dQ, dU
    try:
        if verbose: log("> Trying [freq_Hz, I, Q, U, dI, dQ, dU]", end=' ')
        (freqArr_Hz, IArr, QArr, UArr, dIArr, dQArr, dUArr) = data
        if verbose: log("... success.")
    except Exception:
        if verbose: log("...failed.")
        # freq_Hz, q, u, dq, du
        try:
            if verbose: log("> Trying [freq_Hz, q, u,  dq, du]", end=' ')
            (freqArr_Hz, QArr, UArr, dQArr, dUArr) = data
            if verbose: log("... success.")
            noStokesI = True
        except Exception:
            if verbose: log("...failed.")
            if debug:
                log(traceback.format_exc())
            sys.exit()
    if verbose: log("Successfully read in the Stokes spectra.")

    # If no Stokes I present, create a dummy spectrum = unity
    if noStokesI:
        if verbose: log("Warn: no Stokes I data in use.")
        IArr = np.ones_like(QArr)
        dIArr = np.zeros_like(QArr)

    # Convert to GHz for convenience
    freqArr_GHz = freqArr_Hz / 1e9
    dQUArr = (dQArr + dUArr) / 2.0

    # Fit the Stokes I spectrum and create the fractional spectra
    IModArr, qArr, uArr, dqArr, duArr, fitDict = \
             create_frac_spectra(freqArr  = freqArr_GHz,
                                 IArr     = IArr,
                                 QArr     = QArr,
                                 UArr     = UArr,
                                 dIArr    = dIArr,
                                 dQArr    = dQArr,
                                 dUArr    = dUArr,
                                 polyOrd  = polyOrd,
                                 verbose  = True,
                                 debug    = debug)

    # Plot the data and the Stokes I model fit
    if showPlots:
        if verbose: log("Plotting the input data and spectral index fit.")
        freqHirArr_Hz = np.linspace(freqArr_Hz[0], freqArr_Hz[-1], 10000)
        IModHirArr = poly5(fitDict["p"])(freqHirArr_Hz / 1e9)
        specFig = plt.figure(figsize=(12.0, 8))
        plot_Ipqu_spectra_fig(freqArr_Hz=freqArr_Hz,
                              IArr=IArr,
                              qArr=qArr,
                              uArr=uArr,
                              dIArr=dIArr,
                              dqArr=dqArr,
                              duArr=duArr,
                              freqHirArr_Hz=freqHirArr_Hz,
                              IModArr=IModHirArr,
                              fig=specFig,
                              units=units)

        # Use the custom navigation toolbar (does not work on Mac OS X)
        #        try:
        #            specFig.canvas.toolbar.pack_forget()
        #            CustomNavbar(specFig.canvas, specFig.canvas.toolbar.window)
        #        except Exception:
        #            pass

        # Display the figure
        #        if not plt.isinteractive():
        #            specFig.show()

        # DEBUG (plot the Q, U and average RMS spectrum)
        if debug:
            rmsFig = plt.figure(figsize=(12.0, 8))
            ax = rmsFig.add_subplot(111)
            ax.plot(freqArr_Hz / 1e9,
                    dQUArr,
                    marker='o',
                    color='k',
                    lw=0.5,
                    label='rms <QU>')
            ax.plot(freqArr_Hz / 1e9,
                    dQArr,
                    marker='o',
                    color='b',
                    lw=0.5,
                    label='rms Q')
            ax.plot(freqArr_Hz / 1e9,
                    dUArr,
                    marker='o',
                    color='r',
                    lw=0.5,
                    label='rms U')
            xRange = (np.nanmax(freqArr_Hz) - np.nanmin(freqArr_Hz)) / 1e9
            ax.set_xlim(
                np.min(freqArr_Hz) / 1e9 - xRange * 0.05,
                np.max(freqArr_Hz) / 1e9 + xRange * 0.05)
            ax.set_xlabel('$\\nu$ (GHz)')
            ax.set_ylabel('RMS ' + units)
            ax.set_title("RMS noise in Stokes Q, U and <Q,U> spectra")
#            rmsFig.show()

#-------------------------------------------------------------------------#

# Calculate some wavelength parameters
    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)
    dFreq_Hz = np.nanmin(np.abs(np.diff(freqArr_Hz)))
    lambdaSqRange_m2 = (np.nanmax(lambdaSqArr_m2) - np.nanmin(lambdaSqArr_m2))
    dLambdaSqMin_m2 = np.nanmin(np.abs(np.diff(lambdaSqArr_m2)))
    dLambdaSqMax_m2 = np.nanmax(np.abs(np.diff(lambdaSqArr_m2)))

    # Set the Faraday depth range
    fwhmRMSF_radm2 = 2.0 * m.sqrt(3.0) / lambdaSqRange_m2
    if dPhi_radm2 is None:
        dPhi_radm2 = fwhmRMSF_radm2 / nSamples
    if phiMax_radm2 is None:
        phiMax_radm2 = m.sqrt(3.0) / dLambdaSqMax_m2
        phiMax_radm2 = max(phiMax_radm2, 600.0)  # Force the minimum phiMax

    # Faraday depth sampling. Zero always centred on middle channel
    nChanRM = int(round(abs((phiMax_radm2 - 0.0) / dPhi_radm2)) * 2.0 + 1.0)
    startPhi_radm2 = -(nChanRM - 1.0) * dPhi_radm2 / 2.0
    stopPhi_radm2 = +(nChanRM - 1.0) * dPhi_radm2 / 2.0
    phiArr_radm2 = np.linspace(startPhi_radm2, stopPhi_radm2, nChanRM)
    phiArr_radm2 = phiArr_radm2.astype(dtFloat)
    if verbose:
        log("PhiArr = %.2f to %.2f by %.2f (%d chans)." %
            (phiArr_radm2[0], phiArr_radm2[-1], float(dPhi_radm2), nChanRM))

    # Calculate the weighting as 1/sigma^2 or all 1s (uniform)
    if weightType == "variance":
        weightArr = 1.0 / np.power(dQUArr, 2.0)
    else:
        weightType = "uniform"
        weightArr = np.ones(freqArr_Hz.shape, dtype=dtFloat)
    if verbose: log("Weight type is '%s'." % weightType)

    startTime = time.time()

    # Perform RM-synthesis on the spectrum
    dirtyFDF, lam0Sq_m2, mylist = do_rmsynth_planes(
        dataQ=qArr,
        dataU=uArr,
        lambdaSqArr_m2=lambdaSqArr_m2,
        phiArr_radm2=phiArr_radm2,
        weightArr=weightArr,
        nBits=nBits,
        verbose=verbose,
        log=log,
        e_num=e_num)

    # Calculate the Rotation Measure Spread Function
    RMSFArr, phi2Arr_radm2, fwhmRMSFArr, fitStatArr = \
        get_rmsf_planes(lambdaSqArr_m2  = lambdaSqArr_m2,
                        phiArr_radm2    = phiArr_radm2,
                        weightArr       = weightArr,
                        mskArr          = ~np.isfinite(qArr),
                        lam0Sq_m2       = lam0Sq_m2,
                        double          = True,
                        fitRMSF         = fitRMSF,
                        fitRMSFreal     = False,
                        nBits           = nBits,
                        verbose         = verbose,
                        log             = log)
    fwhmRMSF = float(fwhmRMSFArr)

    # ALTERNATE RM-SYNTHESIS CODE --------------------------------------------#

    #dirtyFDF, [phi2Arr_radm2, RMSFArr], lam0Sq_m2, fwhmRMSF = \
    #          do_rmsynth(qArr, uArr, lambdaSqArr_m2, phiArr_radm2, weightArr)

    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    if verbose: log("> RM-synthesis completed in %.2f seconds." % cputime)

    # Determine the Stokes I value at lam0Sq_m2 from the Stokes I model
    # Multiply the dirty FDF by Ifreq0 to recover the PI
    freq0_Hz = C / m.sqrt(lam0Sq_m2)
    Ifreq0 = poly5(fitDict["p"])(freq0_Hz / 1e9)
    dirtyFDF *= (Ifreq0
                 )  # FDF is in fracpol units initially, convert back to flux

    # Calculate the theoretical noise in the FDF !!Old formula only works for wariance weights!
    weightArr = np.where(np.isnan(weightArr), 0.0, weightArr)
    dFDFth = np.sqrt(
        np.sum(weightArr**2 * np.nan_to_num(dQUArr)**2) /
        (np.sum(weightArr))**2)

    # Measure the parameters of the dirty FDF
    # Use the theoretical noise to calculate uncertainties
    mDict = measure_FDF_parms(FDF=dirtyFDF,
                              phiArr=phiArr_radm2,
                              fwhmRMSF=fwhmRMSF,
                              dFDF=dFDFth,
                              lamSqArr_m2=lambdaSqArr_m2,
                              lam0Sq=lam0Sq_m2)
    mDict["Ifreq0"] = toscalar(Ifreq0)
    mDict["polyCoeffs"] = ",".join([str(x) for x in fitDict["p"]])
    mDict["IfitStat"] = fitDict["fitStatus"]
    mDict["IfitChiSqRed"] = fitDict["chiSqRed"]
    mDict["lam0Sq_m2"] = toscalar(lam0Sq_m2)
    mDict["freq0_Hz"] = toscalar(freq0_Hz)
    mDict["fwhmRMSF"] = toscalar(fwhmRMSF)
    mDict["dQU"] = toscalar(nanmedian(dQUArr))
    mDict["dFDFth"] = toscalar(dFDFth)
    mDict["units"] = units
    mDict['dQUArr'] = dQUArr

    if fitDict["fitStatus"] >= 128:
        log("WARNING: Stokes I model contains negative values!")
    elif fitDict["fitStatus"] >= 64:
        log("Caution: Stokes I model has low signal-to-noise.")

    #Add information on nature of channels:
    good_channels = np.where(np.logical_and(weightArr != 0,
                                            np.isfinite(qArr)))[0]
    mDict["min_freq"] = float(np.min(freqArr_Hz[good_channels]))
    mDict["max_freq"] = float(np.max(freqArr_Hz[good_channels]))
    mDict["N_channels"] = good_channels.size
    mDict["median_channel_width"] = float(np.median(np.diff(freqArr_Hz)))

    # Measure the complexity of the q and u spectra
    mDict["fracPol"] = mDict["ampPeakPIfit"] / (Ifreq0)
    mD, pD = measure_qu_complexity(freqArr_Hz=freqArr_Hz,
                                   qArr=qArr,
                                   uArr=uArr,
                                   dqArr=dqArr,
                                   duArr=duArr,
                                   fracPol=mDict["fracPol"],
                                   psi0_deg=mDict["polAngle0Fit_deg"],
                                   RM_radm2=mDict["phiPeakPIfit_rm2"])
    mDict.update(mD)

    # Debugging plots for spectral complexity measure
    if debug:
        tmpFig = plot_complexity_fig(xArr=pD["xArrQ"],
                                     qArr=pD["yArrQ"],
                                     dqArr=pD["dyArrQ"],
                                     sigmaAddqArr=pD["sigmaAddArrQ"],
                                     chiSqRedqArr=pD["chiSqRedArrQ"],
                                     probqArr=pD["probArrQ"],
                                     uArr=pD["yArrU"],
                                     duArr=pD["dyArrU"],
                                     sigmaAdduArr=pD["sigmaAddArrU"],
                                     chiSqReduArr=pD["chiSqRedArrU"],
                                     probuArr=pD["probArrU"],
                                     mDict=mDict)
        tmpFig.show()

    #add array dictionary
    aDict = dict()
    aDict["phiArr_radm2"] = phiArr_radm2
    aDict["phi2Arr_radm2"] = phi2Arr_radm2
    aDict["RMSFArr"] = RMSFArr
    aDict["freqArr_Hz"] = freqArr_Hz
    aDict["weightArr"] = weightArr
    aDict["dirtyFDF"] = dirtyFDF

    if verbose:
        # Print the results to the screen
        log()
        log('-' * 80)
        log('RESULTS:\n')
        log('FWHM RMSF = %.4g rad/m^2' % (mDict["fwhmRMSF"]))

        log('Pol Angle = %.4g (+/-%.4g) deg' %
            (mDict["polAngleFit_deg"], mDict["dPolAngleFit_deg"]))
        log('Pol Angle 0 = %.4g (+/-%.4g) deg' %
            (mDict["polAngle0Fit_deg"], mDict["dPolAngle0Fit_deg"]))
        log('Peak FD = %.4g (+/-%.4g) rad/m^2' %
            (mDict["phiPeakPIfit_rm2"], mDict["dPhiPeakPIfit_rm2"]))
        log('freq0_GHz = %.4g ' % (mDict["freq0_Hz"] / 1e9))
        log('I freq0 = %.4g %s' % (mDict["Ifreq0"], units))
        log('Peak PI = %.4g (+/-%.4g) %s' %
            (mDict["ampPeakPIfit"], mDict["dAmpPeakPIfit"], units))
        log('QU Noise = %.4g %s' % (mDict["dQU"], units))
        log('FDF Noise (theory)   = %.4g %s' % (mDict["dFDFth"], units))
        log('FDF Noise (Corrected MAD) = %.4g %s' %
            (mDict["dFDFcorMAD"], units))
        log('FDF Noise (rms)   = %.4g %s' % (mDict["dFDFrms"], units))
        log('FDF SNR = %.4g ' % (mDict["snrPIfit"]))
        log('sigma_add(q) = %.4g (+%.4g, -%.4g)' %
            (mDict["sigmaAddQ"], mDict["dSigmaAddPlusQ"],
             mDict["dSigmaAddMinusQ"]))
        log('sigma_add(u) = %.4g (+%.4g, -%.4g)' %
            (mDict["sigmaAddU"], mDict["dSigmaAddPlusU"],
             mDict["dSigmaAddMinusU"]))
        log()
        log('-' * 80)

    myfig = plotmylist(mylist)
    plt.show()
    myfig.show()

    # Plot the RM Spread Function and dirty FDF
    if showPlots:
        fdfFig = plt.figure(figsize=(12.0, 8))
        plot_rmsf_fdf_fig(phiArr=phiArr_radm2,
                          FDF=dirtyFDF,
                          phi2Arr=phi2Arr_radm2,
                          RMSFArr=RMSFArr,
                          fwhmRMSF=fwhmRMSF,
                          vLine=mDict["phiPeakPIfit_rm2"],
                          fig=fdfFig,
                          units=units)

        # Use the custom navigation toolbar
#        try:
#            fdfFig.canvas.toolbar.pack_forget()
#            CustomNavbar(fdfFig.canvas, fdfFig.canvas.toolbar.window)
#        except Exception:
#            pass

# Display the figure
#        fdfFig.show()

# Pause if plotting enabled
    if showPlots or debug:
        plt.show()
        #        #if verbose: print "Press <RETURN> to exit ...",


#        input()

    return mDict, aDict, mylist
Exemple #2
0
def run_rmclean(mDictS,
                aDict,
                cutoff,
                maxIter=1000,
                gain=0.1,
                nBits=32,
                showPlots=False,
                verbose=False,
                log=print):
    """Run RM-CLEAN on a complex FDF spectrum given a RMSF.

    Args:
        mDictS (dict): Summary of RM synthesis results.
        aDict (dict): Data output by RM synthesis.
        cutoff (float): CLEAN cutoff in flux units

    Kwargs:
        maxIter (int): Maximum number of CLEAN iterations per pixel.
        gain (float): CLEAN loop gain.
        nBits (int): Precision of floating point numbers.
        showPlots (bool): Show plots?
        verbose (bool): Verbosity.
        log (function): Which logging function to use.

    Returns:
        mDict (dict): Summary of RMCLEAN results.
        arrdict (dict): Data output by RMCLEAN.

    """
    phiArr_radm2 = aDict["phiArr_radm2"]
    freqArr_Hz = aDict["freqArr_Hz"]
    weightArr = aDict["weightArr"]
    dirtyFDF = aDict["dirtyFDF"]
    phi2Arr_radm2 = aDict["phi2Arr_radm2"]
    RMSFArr = aDict["RMSFArr"]

    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)

    # If the cutoff is negative, assume it is a sigma level
    if verbose:
        log("Expected RMS noise = %.4g flux units" % (mDictS["dFDFth"]))
    if cutoff < 0:
        if verbose: log("Using a sigma cutoff of %.1f." % (-1 * cutoff))
        cutoff = -1 * mDictS["dFDFth"] * cutoff
        if verbose: log("Absolute value = %.3g" % cutoff)
    else:
        if verbose:
            log("Using an absolute cutoff of %.3g (%.1f x expected RMS)." %
                (cutoff, cutoff / mDictS["dFDFth"]))

    startTime = time.time()
    # Perform RM-clean on the spectrum
    cleanFDF, ccArr, iterCountArr, residFDF = \
              do_rmclean_hogbom(dirtyFDF        = dirtyFDF,
                                phiArr_radm2    = phiArr_radm2,
                                RMSFArr         = RMSFArr,
                                phi2Arr_radm2   = phi2Arr_radm2,
                                fwhmRMSFArr     = np.array(mDictS["fwhmRMSF"]),
                                cutoff          = cutoff,
                                maxIter         = maxIter,
                                gain            = gain,
                                verbose         = verbose,
                                doPlots         = showPlots)

    # ALTERNATIVE RM_CLEAN CODE ----------------------------------------------#
    '''
    cleanFDF, ccArr, fwhmRMSF, iterCount = \
              do_rmclean(dirtyFDF     = dirtyFDF,
                         phiArr       = phiArr_radm2,
                         lamSqArr     = lamSqArr_m2,
                         cutoff       = cutoff,
                         maxIter      = maxIter,
                         gain         = gain,
                         weight       = weightArr,
                         RMSFArr      = RMSFArr,
                         RMSFphiArr   = phi2Arr_radm2,
                         fwhmRMSF     = mDictS["fwhmRMSF"],
                         doPlots      = True)
    '''
    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    if verbose: log("> RM-CLEAN completed in %.4f seconds." % cputime)

    # Measure the parameters of the deconvolved FDF
    mDict = measure_FDF_parms(
        FDF=cleanFDF,
        phiArr=phiArr_radm2,
        fwhmRMSF=mDictS["fwhmRMSF"],
        #dFDF        = mDictS["dFDFth"],
        lamSqArr_m2=lambdaSqArr_m2,
        lam0Sq=mDictS["lam0Sq_m2"])
    mDict["cleanCutoff"] = cutoff
    mDict["nIter"] = int(iterCountArr)

    # Measure the complexity of the clean component spectrum
    mDict["mom2CCFDF"] = measure_fdf_complexity(phiArr=phiArr_radm2, FDF=ccArr)

    #Calculating observed errors (based on dFDFcorMAD)
    mDict["dPhiObserved_rm2"] = mDict["dPhiPeakPIfit_rm2"] * mDict[
        "dFDFcorMAD"] / mDictS["dFDFth"]
    mDict["dAmpObserved"] = mDict["dFDFcorMAD"]
    mDict["dPolAngleFitObserved_deg"] = mDict["dPolAngleFit_deg"] * mDict[
        "dFDFcorMAD"] / mDictS["dFDFth"]

    nChansGood = np.sum(np.where(lambdaSqArr_m2 == lambdaSqArr_m2, 1.0, 0.0))
    varLamSqArr_m2 = (np.sum(lambdaSqArr_m2**2.0) -
                      np.sum(lambdaSqArr_m2)**2.0 / nChansGood) / (nChansGood -
                                                                   1)
    mDict["dPolAngle0ChanObserved_deg"] = \
    np.degrees(np.sqrt( mDict["dFDFcorMAD"]**2.0 / (4.0*(nChansGood-2.0)*mDict["ampPeakPIfit"]**2.0) *
                 ((nChansGood-1)/nChansGood + mDictS["lam0Sq_m2"]**2.0/varLamSqArr_m2) ))

    if verbose:
        # Print the results to the screen
        log()
        log('-' * 80)
        log('RESULTS:\n')
        log('FWHM RMSF = %.4g rad/m^2' % (mDictS["fwhmRMSF"]))
        log('Pol Angle = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
            (mDict["polAngleFit_deg"], mDict["dPolAngleFitObserved_deg"],
             mDict["dPolAngleFit_deg"]))
        log('Pol Angle 0 = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
            (mDict["polAngle0Fit_deg"], mDict["dPolAngle0ChanObserved_deg"],
             mDict["dPolAngle0Fit_deg"]))
        log('Peak FD = %.4g (+/-%.4g observed, +- %.4g theoretical) rad/m^2' %
            (mDict["phiPeakPIfit_rm2"], mDict["dPhiObserved_rm2"],
             mDict["dPhiPeakPIfit_rm2"]))
        log('freq0_GHz = %.4g ' % (mDictS["freq0_Hz"] / 1e9))
        log('I freq0 = %.4g %s' % (mDictS["Ifreq0"], mDictS["units"]))
        log('Peak PI = %.4g (+/-%.4g observed, +- %.4g theoretical) %s' %
            (mDict["ampPeakPIfit"], mDict["dAmpObserved"],
             mDict["dAmpPeakPIfit"], mDictS["units"]))
        log('QU Noise = %.4g %s' % (mDictS["dQU"], mDictS["units"]))
        log('FDF Noise (theory)   = %.4g %s' %
            (mDictS["dFDFth"], mDictS["units"]))
        log('FDF Noise (Corrected MAD) = %.4g %s' %
            (mDict["dFDFcorMAD"], mDictS["units"]))
        log('FDF Noise (rms)   = %.4g %s' %
            (mDict["dFDFrms"], mDictS["units"]))

        log('FDF SNR = %.4g ' % (mDict["snrPIfit"]))
        log()
        log('-' * 80)

    # Pause to display the figure
    if showPlots:
        plot_clean_spec(phiArr_radm2, dirtyFDF, cleanFDF, ccArr, residFDF,
                        cutoff, mDictS["units"])
        print("Press <RETURN> to exit ...", end=' ')
        input()

    #add array dictionary
    arrdict = dict()
    arrdict["phiArr_radm2"] = phiArr_radm2
    arrdict["freqArr_Hz"] = freqArr_Hz
    arrdict["cleanFDF"] = cleanFDF
    arrdict["ccArr"] = ccArr
    arrdict["iterCountArr"] = iterCountArr
    arrdict["residFDF"] = residFDF

    return mDict, arrdict
Exemple #3
0
def pixelwise_peak_fitting(FDF,
                           phiArr,
                           fwhmRMSF,
                           lamSqArr_m2,
                           lam0Sq,
                           product_list,
                           noiseArr=None,
                           stokesIcube=None):
    """
    Performs the 1D FDF peak fitting used in RMsynth/RMclean_1D, pixelwise on
    all pixels in a 3D FDF cube.

    Inputs: 
        FDF: FDF cube (3D array). This is assumed to be in astropy axis ordering
            (Phi, dec, ra)
        phiArr: (1D) array of phi values
        fwhmRMSF: 2D array of RMSF FWHM values
        lamSqArr_m2: 1D array of channel lambda^2 values.
        lam0Sq: scalar value for lambda^2_0, the reference wavelength squared.
        product_list: list containing the names of the fitting products to save.
        dFDF: 2D array of theoretical noise values. If not supplied, the 
            peak fitting will default to using the measured noise.
    Outputs: dictionary of 2D maps, 1 per fit output
    """

    #FDF: output by synth3d or clean3d
    #phiArr: can be generated from FDF cube
    #fwhm: 2D map produced synth3D
    #dFDFth: not currently produced (default mode not to input noise!)
    #   If not present, measure_FDF_parms uses the corMAD noise.
    #
    #lamSqArr is only needed for computing errors in derotated angles
    #   This could be compressed to a map or single value from RMsynth?
    #lam0Sq is necessary for de-rotation

    map_size = FDF.shape[1:]

    #Create pixel location arrays:
    xarr, yarr = np.meshgrid(range(map_size[0]), range(map_size[1]))
    xarr = xarr.ravel()
    yarr = yarr.ravel()

    #Create empty maps:
    map_dict = {}
    for parameter in product_list:
        map_dict[parameter] = np.zeros(map_size)

    freqArr_Hz = C / np.sqrt(lamSqArr_m2)
    freq0_Hz = C / np.sqrt(lam0Sq)
    if stokesIcube is not None:
        idx = np.abs(freqArr_Hz - freq0_Hz).argmin()
        if freqArr_Hz[idx] < freq0_Hz:
            Ifreq0Arr = interp_images(stokesIcube[idx, :, :],
                                      stokesIcube[idx + 1, :, :],
                                      f=0.5)
        elif freqArr_Hz[idx] > freq0_Hz:
            Ifreq0Arr = interp_images(stokesIcube[idx - 1, :, :],
                                      stokesIcube[idx, :, :],
                                      f=0.5)
        else:
            Ifreq0Arr = stokesIcube[idx, :, :]
    else:
        Ifreq0Arr = np.ones(map_size)
        stokesIcube = np.ones((freqArr_Hz.size, map_size[0], map_size[1]))

    #compute weights if needed:
    if noiseArr is not None:
        weightArr = 1.0 / np.power(noiseArr, 2.0)
        weightArr = np.where(np.isnan(weightArr), 0.0, weightArr)
        dFDF = Ifreq0Arr * np.sqrt(
            np.sum(weightArr**2 * np.nan_to_num(noiseArr)**2) /
            (np.sum(weightArr))**2)

    else:
        weightArr = np.ones(lamSqArr_m2.shape, dtype=np.float32)
        dFDF = None

    #Run fitting pixel-wise:
    progress(40, 0)
    for i in range(xarr.size):
        FDF_pix = FDF[:, xarr[i], yarr[i]]
        fwhmRMSF_pix = fwhmRMSF[xarr[i], yarr[i]]
        if type(dFDF) == type(None):
            dFDF_pix = None
        else:
            dFDF_pix = dFDF[xarr[i], yarr[i]]
        try:
            mDict = measure_FDF_parms(FDF_pix,
                                      phiArr,
                                      fwhmRMSF_pix,
                                      dFDF=dFDF_pix,
                                      lamSqArr_m2=lamSqArr_m2,
                                      lam0Sq=lam0Sq,
                                      snrDoBiasCorrect=5.0)
            #Add keywords not included by the above function:
            mDict['lam0Sq_m2'] = lam0Sq
            mDict['freq0_Hz'] = freq0_Hz
            mDict['fwhmRMSF'] = fwhmRMSF_pix
            mDict['Ifreq0'] = Ifreq0Arr[xarr[i], yarr[i]]
            mDict['fracPol'] = mDict["ampPeakPIfit"] / mDict['Ifreq0']
            mDict["min_freq"] = float(np.min(freqArr_Hz))
            mDict["max_freq"] = float(np.max(freqArr_Hz))
            mDict["N_channels"] = lamSqArr_m2.size
            mDict["median_channel_width"] = float(
                np.median(np.diff(freqArr_Hz)))
            if dFDF_pix is not None:
                mDict['dFDFth'] = dFDF_pix
            else:
                mDict['dFDFth'] = np.nan
            for parameter in product_list:
                map_dict[parameter][xarr[i], yarr[i]] = mDict[parameter]
        except:
            for parameter in product_list:
                map_dict[parameter][xarr[i], yarr[i]] = np.nan

        if i % 100 == 0:
            progress(40, i / xarr.size * 100)
    return map_dict
Exemple #4
0
def run_rmclean(mDictS,
                aDict,
                cutoff,
                maxIter=1000,
                gain=0.1,
                prefixOut="",
                outDir="",
                nBits=32,
                showPlots=False,
                doAnimate=False,
                verbose=False,
                log=print):
    """
    Run RM-CLEAN on a complex FDF spectrum given a RMSF.
    """
    phiArr_radm2 = aDict["phiArr_radm2"]
    freqArr_Hz = aDict["freqArr_Hz"]
    weightArr = aDict["weightArr"]
    dirtyFDF = aDict["dirtyFDF"]
    phi2Arr_radm2 = aDict["phi2Arr_radm2"]
    RMSFArr = aDict["RMSFArr"]

    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)

    # If the cutoff is negative, assume it is a sigma level
    if verbose:
        log("Expected RMS noise = %.4g mJy/beam/rmsf" %
            (mDictS["dFDFth_Jybm"] * 1e3))
    if cutoff < 0:
        log("Using a sigma cutoff of %.1f." % (-1 * cutoff))
        cutoff = -1 * mDictS["dFDFth_Jybm"] * cutoff
        log("Absolute value = %.3g" % cutoff)
    else:
        log("Using an absolute cutoff of %.3g (%.1f x expected RMS)." %
            (cutoff, cutoff / mDictS["dFDFth_Jybm"]))

    startTime = time.time()
    # Perform RM-clean on the spectrum
    cleanFDF, ccArr, iterCountArr = \
              do_rmclean_hogbom(dirtyFDF        = dirtyFDF,
                                phiArr_radm2    = phiArr_radm2,
                                RMSFArr         = RMSFArr,
                                phi2Arr_radm2   = phi2Arr_radm2,
                                fwhmRMSFArr     = np.array(mDictS["fwhmRMSF"]),
                                cutoff          = cutoff,
                                maxIter         = maxIter,
                                gain            = gain,
                                verbose         = verbose,
                                doPlots         = showPlots,
                                doAnimate       = doAnimate)

    # ALTERNATIVE RM_CLEAN CODE ----------------------------------------------#
    '''
    cleanFDF, ccArr, fwhmRMSF, iterCount = \
              do_rmclean(dirtyFDF     = dirtyFDF,
                         phiArr       = phiArr_radm2,
                         lamSqArr     = lamSqArr_m2,
                         cutoff       = cutoff,
                         maxIter      = maxIter,
                         gain         = gain,
                         weight       = weightArr,
                         RMSFArr      = RMSFArr,
                         RMSFphiArr   = phi2Arr_radm2,
                         fwhmRMSF     = mDictS["fwhmRMSF"],
                         doPlots      = True)
    '''
    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    log("> RM-CLEAN completed in %.4f seconds." % cputime)

    # Measure the parameters of the deconvolved FDF
    mDict = measure_FDF_parms(
        FDF=cleanFDF,
        phiArr=phiArr_radm2,
        fwhmRMSF=mDictS["fwhmRMSF"],
        #dFDF        = mDictS["dFDFth_Jybm"],
        lamSqArr_m2=lambdaSqArr_m2,
        lam0Sq=mDictS["lam0Sq_m2"])
    mDict["cleanCutoff"] = cutoff
    mDict["nIter"] = int(iterCountArr)

    # Measure the complexity of the clean component spectrum
    mDict["mom2CCFDF"] = measure_fdf_complexity(phiArr=phiArr_radm2, FDF=ccArr)

    #Calculating observed errors (based on dFDFcorMAD)
    mDict["dPhiObserved_rm2"] = mDict["dPhiPeakPIfit_rm2"] * mDict[
        "dFDFcorMAD_Jybm"] / mDictS["dFDFth_Jybm"]
    mDict["dAmpObserved_Jybm"] = mDict["dFDFcorMAD_Jybm"]
    mDict["dPolAngleFitObserved_deg"] = mDict["dPolAngleFit_deg"] * mDict[
        "dFDFcorMAD_Jybm"] / mDictS["dFDFth_Jybm"]

    nChansGood = np.sum(np.where(lambdaSqArr_m2 == lambdaSqArr_m2, 1.0, 0.0))
    varLamSqArr_m2 = (np.sum(lambdaSqArr_m2**2.0) -
                      np.sum(lambdaSqArr_m2)**2.0 / nChansGood) / (nChansGood -
                                                                   1)
    mDict["dPolAngle0ChanObserved_deg"] = \
    np.degrees(np.sqrt( mDict["dFDFcorMAD_Jybm"]**2.0 / (4.0*(nChansGood-2.0)*mDict["ampPeakPIfit_Jybm"]**2.0) *
                 ((nChansGood-1)/nChansGood + mDictS["lam0Sq_m2"]**2.0/varLamSqArr_m2) ))

    # Save the deconvolved FDF and CC model to ASCII files
    log("Saving the clean FDF and component model to ASCII files.")
    outFile = prefixOut + "_FDFclean.dat"
    log("> %s" % outFile)
    np.savetxt(outFile, list(zip(phiArr_radm2, cleanFDF.real, cleanFDF.imag)))
    outFile = prefixOut + "_FDFmodel.dat"
    log("> %s" % outFile)
    np.savetxt(outFile, list(zip(phiArr_radm2, ccArr.real, ccArr.imag)))

    # Save the RM-clean measurements to a "key=value" text file
    log("Saving the measurements on the FDF in 'key=val' and JSON formats.")
    outFile = prefixOut + "_RMclean.dat"
    log("> %s" % outFile)
    FH = open(outFile, "w")
    for k, v in mDict.items():
        FH.write("%s=%s\n" % (k, v))
    FH.close()
    outFile = prefixOut + "_RMclean.json"
    log("> %s" % outFile)
    json.dump(mDict, open(outFile, "w"))

    # Print the results to the screen
    log()
    log('-' * 80)
    log('RESULTS:\n')
    log('FWHM RMSF = %.4g rad/m^2' % (mDictS["fwhmRMSF"]))
    log('Pol Angle = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
        (mDict["polAngleFit_deg"], mDict["dPolAngleFitObserved_deg"],
         mDict["dPolAngleFit_deg"]))
    log('Pol Angle 0 = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
        (mDict["polAngle0Fit_deg"], mDict["dPolAngle0ChanObserved_deg"],
         mDict["dPolAngle0Fit_deg"]))
    log('Peak FD = %.4g (+/-%.4g observed, +- %.4g theoretical) rad/m^2' %
        (mDict["phiPeakPIfit_rm2"], mDict["dPhiObserved_rm2"],
         mDict["dPhiPeakPIfit_rm2"]))
    log('freq0_GHz = %.4g ' % (mDictS["freq0_Hz"] / 1e9))
    log('I freq0 = %.4g mJy/beam' % (mDictS["Ifreq0_mJybm"]))
    log('Peak PI = %.4g (+/-%.4g observed, +- %.4g theoretical) mJy/beam' %
        (mDict["ampPeakPIfit_Jybm"] * 1e3, mDict["dAmpObserved_Jybm"] * 1e3,
         mDict["dAmpPeakPIfit_Jybm"] * 1e3))
    log('QU Noise = %.4g mJy/beam' % (mDictS["dQU_Jybm"] * 1e3))
    log('FDF Noise (theory)   = %.4g mJy/beam' % (mDictS["dFDFth_Jybm"] * 1e3))
    log('FDF Noise (Corrected MAD) = %.4g mJy/beam' %
        (mDict["dFDFcorMAD_Jybm"] * 1e3))
    log('FDF Noise (rms)   = %.4g mJy/beam' % (mDict["dFDFrms_Jybm"] * 1e3))

    log('FDF SNR = %.4g ' % (mDict["snrPIfit"]))
    log()
    log('-' * 80)

    # Pause to display the figure
    if showPlots or doAnimate:
        print("Press <RETURN> to exit ...", end=' ')
        input()

    return mDict
Exemple #5
0
def run_rmsynth(dataFile,
                polyOrd=3,
                phiMax_radm2=None,
                dPhi_radm2=None,
                nSamples=10.0,
                weightType="variance",
                fitRMSF=False,
                noStokesI=False,
                phiNoise_radm2=1e6,
                nBits=32,
                showPlots=False,
                debug=False):
    """
    Read the I, Q & U data from the ASCII file and run RM-synthesis.
    """

    # Default data types
    dtFloat = "float" + str(nBits)
    dtComplex = "complex" + str(2 * nBits)

    # Output prefix is derived from the input file name
    prefixOut, ext = os.path.splitext(dataFile)

    # Read the data-file. Format=space-delimited, comments="#".
    print "Reading the data file '%s':" % dataFile
    # freq_Hz, I_Jy, Q_Jy, U_Jy, dI_Jy, dQ_Jy, dU_Jy
    try:
        print "> Trying [freq_Hz, I_Jy, Q_Jy, U_Jy, dI_Jy, dQ_Jy, dU_Jy]",
        (freqArr_Hz, IArr_Jy, QArr_Jy, UArr_Jy,
         dIArr_Jy, dQArr_Jy, dUArr_Jy) = \
         np.loadtxt(dataFile, unpack=True, dtype=dtFloat)
        print "... success."
    except Exception:
        print "...failed."
        # freq_Hz, q_Jy, u_Jy, dq_Jy, du_Jy
        try:
            print "> Trying [freq_Hz, q_Jy, u_Jy,  dq_Jy, du_Jy]",
            (freqArr_Hz, QArr_Jy, UArr_Jy, dQArr_Jy, dUArr_Jy) = \
                         np.loadtxt(dataFile, unpack=True, dtype=dtFloat)
            print "... success."
            noStokesI = True
        except Exception:
            print "...failed."
            if debug:
                print traceback.format_exc()
            sys.exit()
    print "Successfully read in the Stokes spectra."

    # If no Stokes I present, create a dummy spectrum = unity
    if noStokesI:
        print "Warn: no Stokes I data in use."
        IArr_Jy = np.ones_like(QArr_Jy)
        dIArr_Jy = np.zeros_like(QArr_Jy)

    # Convert to GHz and mJy for convenience
    freqArr_GHz = freqArr_Hz / 1e9
    IArr_mJy = IArr_Jy * 1e3
    QArr_mJy = QArr_Jy * 1e3
    UArr_mJy = UArr_Jy * 1e3
    dIArr_mJy = dIArr_Jy * 1e3
    dQArr_mJy = dQArr_Jy * 1e3
    dUArr_mJy = dUArr_Jy * 1e3
    dQUArr_mJy = (dQArr_mJy + dUArr_mJy) / 2.0
    dQUArr_Jy = dQUArr_mJy / 1e3

    # Fit the Stokes I spectrum and create the fractional spectra
    IModArr, qArr, uArr, dqArr, duArr, fitDict = \
             create_frac_spectra(freqArr  = freqArr_GHz,
                                 IArr     = IArr_mJy,
                                 QArr     = QArr_mJy,
                                 UArr     = UArr_mJy,
                                 dIArr    = dIArr_mJy,
                                 dQArr    = dQArr_mJy,
                                 dUArr    = dUArr_mJy,
                                 polyOrd  = polyOrd,
                                 verbose  = True,
                                 debug    = debug)

    # Plot the data and the Stokes I model fit
    if showPlots:
        print "Plotting the input data and spectral index fit."
        freqHirArr_Hz = np.linspace(freqArr_Hz[0], freqArr_Hz[-1], 10000)
        IModHirArr_mJy = poly5(fitDict["p"])(freqHirArr_Hz / 1e9)
        specFig = plt.figure(figsize=(12.0, 8))
        plot_Ipqu_spectra_fig(freqArr_Hz=freqArr_Hz,
                              IArr_mJy=IArr_mJy,
                              qArr=qArr,
                              uArr=uArr,
                              dIArr_mJy=dIArr_mJy,
                              dqArr=dqArr,
                              duArr=duArr,
                              freqHirArr_Hz=freqHirArr_Hz,
                              IModArr_mJy=IModHirArr_mJy,
                              fig=specFig)

        # Use the custom navigation toolbar (does not work on Mac OS X)
        try:
            specFig.canvas.toolbar.pack_forget()
            CustomNavbar(specFig.canvas, specFig.canvas.toolbar.window)
        except Exception:
            pass

        # Display the figure
        specFig.show()

        # DEBUG (plot the Q, U and average RMS spectrum)
        if debug:
            rmsFig = plt.figure(figsize=(12.0, 8))
            ax = rmsFig.add_subplot(111)
            ax.plot(freqArr_Hz / 1e9,
                    dQUArr_mJy,
                    marker='o',
                    color='k',
                    lw=0.5,
                    label='rms <QU>')
            ax.plot(freqArr_Hz / 1e9,
                    dQArr_mJy,
                    marker='o',
                    color='b',
                    lw=0.5,
                    label='rms Q')
            ax.plot(freqArr_Hz / 1e9,
                    dUArr_mJy,
                    marker='o',
                    color='r',
                    lw=0.5,
                    label='rms U')
            xRange = (np.nanmax(freqArr_Hz) - np.nanmin(freqArr_Hz)) / 1e9
            ax.set_xlim(
                np.min(freqArr_Hz) / 1e9 - xRange * 0.05,
                np.max(freqArr_Hz) / 1e9 + xRange * 0.05)
            ax.set_xlabel('$\\nu$ (GHz)')
            ax.set_ylabel('RMS (mJy bm$^{-1}$)')
            ax.set_title("RMS noise in Stokes Q, U and <Q,U> spectra")
            rmsFig.show()

    #-------------------------------------------------------------------------#

    # Calculate some wavelength parameters
    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)
    dFreq_Hz = np.nanmin(np.abs(np.diff(freqArr_Hz)))
    lambdaSqRange_m2 = (np.nanmax(lambdaSqArr_m2) - np.nanmin(lambdaSqArr_m2))
    dLambdaSqMin_m2 = np.nanmin(np.abs(np.diff(lambdaSqArr_m2)))
    dLambdaSqMax_m2 = np.nanmax(np.abs(np.diff(lambdaSqArr_m2)))

    # Set the Faraday depth range
    fwhmRMSF_radm2 = 2.0 * m.sqrt(3.0) / lambdaSqRange_m2
    if dPhi_radm2 is None:
        dPhi_radm2 = fwhmRMSF_radm2 / nSamples
    if phiMax_radm2 is None:
        phiMax_radm2 = m.sqrt(3.0) / dLambdaSqMax_m2
        phiMax_radm2 = max(phiMax_radm2, 600.0)  # Force the minimum phiMax

    # Faraday depth sampling. Zero always centred on middle channel
    nChanRM = round(abs((phiMax_radm2 - 0.0) / dPhi_radm2)) * 2.0 + 1.0
    startPhi_radm2 = -(nChanRM - 1.0) * dPhi_radm2 / 2.0
    stopPhi_radm2 = +(nChanRM - 1.0) * dPhi_radm2 / 2.0
    phiArr_radm2 = np.linspace(startPhi_radm2, stopPhi_radm2, nChanRM)
    phiArr_radm2 = phiArr_radm2.astype(dtFloat)
    print "PhiArr = %.2f to %.2f by %.2f (%d chans)." % (
        phiArr_radm2[0], phiArr_radm2[-1], float(dPhi_radm2), nChanRM)

    # Calculate the weighting as 1/sigma^2 or all 1s (natural)
    if weightType == "variance":
        weightArr = 1.0 / np.power(dQUArr_mJy, 2.0)
    else:
        weightType = "natural"
        weightArr = np.ones(freqArr_Hz.shape, dtype=dtFloat)
    print "Weight type is '%s'." % weightType

    startTime = time.time()

    # Perform RM-synthesis on the spectrum
    dirtyFDF, lam0Sq_m2 = do_rmsynth_planes(dataQ=qArr,
                                            dataU=uArr,
                                            lambdaSqArr_m2=lambdaSqArr_m2,
                                            phiArr_radm2=phiArr_radm2,
                                            weightArr=weightArr,
                                            nBits=nBits,
                                            verbose=True)

    # Calculate the Rotation Measure Spread Function
    RMSFArr, phi2Arr_radm2, fwhmRMSFArr, fitStatArr = \
        get_rmsf_planes(lambdaSqArr_m2  = lambdaSqArr_m2,
                        phiArr_radm2    = phiArr_radm2,
                        weightArr       = weightArr,
                        mskArr          = np.isnan(qArr),
                        lam0Sq_m2       = lam0Sq_m2,
                        double          = True,
                        fitRMSF         = fitRMSF,
                        fitRMSFreal     = False,
                        nBits           = nBits,
                        verbose         = True)
    fwhmRMSF = float(fwhmRMSFArr)

    # ALTERNATE RM-SYNTHESIS CODE --------------------------------------------#

    #dirtyFDF, [phi2Arr_radm2, RMSFArr], lam0Sq_m2, fwhmRMSF = \
    #          do_rmsynth(qArr, uArr, lambdaSqArr_m2, phiArr_radm2, weightArr)

    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    print "> RM-synthesis completed in %.2f seconds." % cputime

    # Determine the Stokes I value at lam0Sq_m2 from the Stokes I model
    # Multiply the dirty FDF by Ifreq0 to recover the PI in Jy
    freq0_Hz = C / m.sqrt(lam0Sq_m2)
    Ifreq0_mJybm = poly5(fitDict["p"])(freq0_Hz / 1e9)
    dirtyFDF *= (Ifreq0_mJybm / 1e3)  # FDF is in Jy

    # Calculate the theoretical noise in the FDF
    dFDFth_Jybm = np.sqrt(1. / np.sum(1. / dQUArr_Jy**2.))

    # Measure the parameters of the dirty FDF
    # Use the theoretical noise to calculate uncertainties
    mDict = measure_FDF_parms(FDF=dirtyFDF,
                              phiArr=phiArr_radm2,
                              fwhmRMSF=fwhmRMSF,
                              dFDF=dFDFth_Jybm,
                              lamSqArr_m2=lambdaSqArr_m2,
                              lam0Sq=lam0Sq_m2)
    mDict["Ifreq0_mJybm"] = toscalar(Ifreq0_mJybm)
    mDict["polyCoeffs"] = ",".join([str(x) for x in fitDict["p"]])
    mDict["IfitStat"] = fitDict["fitStatus"]
    mDict["IfitChiSqRed"] = fitDict["chiSqRed"]
    mDict["lam0Sq_m2"] = toscalar(lam0Sq_m2)
    mDict["freq0_Hz"] = toscalar(freq0_Hz)
    mDict["fwhmRMSF"] = toscalar(fwhmRMSF)
    mDict["dQU_Jybm"] = toscalar(nanmedian(dQUArr_Jy))
    mDict["dFDFth_Jybm"] = toscalar(dFDFth_Jybm)

    # Measure the complexity of the q and u spectra
    mDict["fracPol"] = mDict["ampPeakPIfit_Jybm"] / (Ifreq0_mJybm / 1e3)
    mD, pD = measure_qu_complexity(freqArr_Hz=freqArr_Hz,
                                   qArr=qArr,
                                   uArr=uArr,
                                   dqArr=dqArr,
                                   duArr=duArr,
                                   fracPol=mDict["fracPol"],
                                   psi0_deg=mDict["polAngle0Fit_deg"],
                                   RM_radm2=mDict["phiPeakPIfit_rm2"])
    mDict.update(mD)

    # Debugging plots for spectral complexity measure
    if debug:
        tmpFig = plot_complexity_fig(xArr=pD["xArrQ"],
                                     qArr=pD["yArrQ"],
                                     dqArr=pD["dyArrQ"],
                                     sigmaAddqArr=pD["sigmaAddArrQ"],
                                     chiSqRedqArr=pD["chiSqRedArrQ"],
                                     probqArr=pD["probArrQ"],
                                     uArr=pD["yArrU"],
                                     duArr=pD["dyArrU"],
                                     sigmaAdduArr=pD["sigmaAddArrU"],
                                     chiSqReduArr=pD["chiSqRedArrU"],
                                     probuArr=pD["probArrU"],
                                     mDict=mDict)
        tmpFig.show()

    # Save the  dirty FDF, RMSF and weight array to ASCII files
    print "Saving the dirty FDF, RMSF weight arrays to ASCII files."
    outFile = prefixOut + "_FDFdirty.dat"
    print "> %s" % outFile
    np.savetxt(outFile, zip(phiArr_radm2, dirtyFDF.real, dirtyFDF.imag))
    outFile = prefixOut + "_RMSF.dat"
    print "> %s" % outFile
    np.savetxt(outFile, zip(phi2Arr_radm2, RMSFArr.real, RMSFArr.imag))
    outFile = prefixOut + "_weight.dat"
    print "> %s" % outFile
    np.savetxt(outFile, zip(freqArr_Hz, weightArr))

    # Save the measurements to a "key=value" text file
    print "Saving the measurements on the FDF in 'key=val' and JSON formats."
    outFile = prefixOut + "_RMsynth.dat"
    print "> %s" % outFile
    FH = open(outFile, "w")
    for k, v in mDict.iteritems():
        FH.write("%s=%s\n" % (k, v))
    FH.close()
    outFile = prefixOut + "_RMsynth.json"
    print "> %s" % outFile
    json.dump(dict(mDict), open(outFile, "w"))

    # Print the results to the screen
    print
    print '-' * 80
    print 'RESULTS:\n'
    print 'FWHM RMSF = %.4g rad/m^2' % (mDict["fwhmRMSF"])

    print 'Pol Angle = %.4g (+/-%.4g) deg' % (mDict["polAngleFit_deg"],
                                              mDict["dPolAngleFit_deg"])
    print 'Pol Angle 0 = %.4g (+/-%.4g) deg' % (mDict["polAngle0Fit_deg"],
                                                mDict["dPolAngle0Fit_deg"])
    print 'Peak FD = %.4g (+/-%.4g) rad/m^2' % (mDict["phiPeakPIfit_rm2"],
                                                mDict["dPhiPeakPIfit_rm2"])
    print 'freq0_GHz = %.4g ' % (mDict["freq0_Hz"] / 1e9)
    print 'I freq0 = %.4g mJy/beam' % (mDict["Ifreq0_mJybm"])
    print 'Peak PI = %.4g (+/-%.4g) mJy/beam' % (
        mDict["ampPeakPIfit_Jybm"] * 1e3, mDict["dAmpPeakPIfit_Jybm"] * 1e3)
    print 'QU Noise = %.4g mJy/beam' % (mDict["dQU_Jybm"] * 1e3)
    print 'FDF Noise (theory)   = %.4g mJy/beam' % (mDict["dFDFth_Jybm"] * 1e3)
    print 'FDF SNR = %.4g ' % (mDict["snrPIfit"])
    print 'sigma_add(q) = %.4g (+%.4g, -%.4g)' % (
        mDict["sigmaAddQ"], mDict["dSigmaAddPlusQ"], mDict["dSigmaAddMinusQ"])
    print 'sigma_add(u) = %.4g (+%.4g, -%.4g)' % (
        mDict["sigmaAddU"], mDict["dSigmaAddPlusU"], mDict["dSigmaAddMinusU"])
    print
    print '-' * 80

    # Plot the RM Spread Function and dirty FDF
    if showPlots:
        fdfFig = plt.figure(figsize=(12.0, 8))
        plot_rmsf_fdf_fig(phiArr=phiArr_radm2,
                          FDF=dirtyFDF,
                          phi2Arr=phi2Arr_radm2,
                          RMSFArr=RMSFArr,
                          fwhmRMSF=fwhmRMSF,
                          vLine=mDict["phiPeakPIfit_rm2"],
                          fig=fdfFig)

        # Use the custom navigation toolbar
        try:
            fdfFig.canvas.toolbar.pack_forget()
            CustomNavbar(fdfFig.canvas, fdfFig.canvas.toolbar.window)
        except Exception:
            pass

        # Display the figure
        fdfFig.show()

    # Pause if plotting enabled
    if showPlots or debug:
        print "Press <RETURN> to exit ...",
        raw_input()
def run_rmclean(mDict,
                aDict,
                cutoff,
                maxIter=1000,
                gain=0.1,
                nBits=32,
                showPlots=False,
                prefixOut="",
                verbose=False,
                log=print,
                saveFigures=False,
                window=None):
    """Run RM-CLEAN on a complex FDF spectrum given a RMSF.

    Args:
        mDict (dict): Summary of RM synthesis results.
        aDict (dict): Data output by RM synthesis.
        cutoff (float): CLEAN cutoff in flux units (positive)
                        or as multiple of theoretical noise (negative)
                        (i.e. -8 = clean to 8 sigma threshold)

    Kwargs:
        maxIter (int): Maximum number of CLEAN iterations per pixel.
        gain (float): CLEAN loop gain.
        nBits (int): Precision of floating point numbers.
        showPlots (bool): Show plots?
        verbose (bool): Verbosity.
        log (function): Which logging function to use.
        window (float): Threshold for deeper windowed cleaning

    Returns:
        mDict_cl (dict): Summary of RMCLEAN results.
        aDict_cl (dict): Data output by RMCLEAN.

    """
    phiArr_radm2 = aDict["phiArr_radm2"]
    freqArr_Hz = aDict["freqArr_Hz"]
    weightArr = aDict["weightArr"]
    dirtyFDF = aDict["dirtyFDF"]
    phi2Arr_radm2 = aDict["phi2Arr_radm2"]
    RMSFArr = aDict["RMSFArr"]

    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)

    # If the cutoff is negative, assume it is a sigma level
    if verbose: log("Expected RMS noise = %.4g flux units" % (mDict["dFDFth"]))
    if cutoff < 0:
        if verbose: log("Using a sigma cutoff of %.1f." % (-1 * cutoff))
        cutoff = -1 * mDict["dFDFth"] * cutoff
        if verbose: log("Absolute value = %.3g" % cutoff)
    else:
        if verbose:
            log("Using an absolute cutoff of %.3g (%.1f x expected RMS)." %
                (cutoff, cutoff / mDict["dFDFth"]))

    if window is None:
        window = np.nan
    else:
        if window < 0:
            if verbose:
                log("Using a window sigma cutoff of %.1f." % (-1 * window))
            window = -1 * mDict["dFDFth"] * window
            if verbose: log("Absolute value = %.3g" % window)
        else:
            if verbose:
                log("Using an absolute window cutoff of %.3g (%.1f x expected RMS)."
                    % (window, window / mDict["dFDFth"]))

    startTime = time.time()
    # Perform RM-clean on the spectrum
    cleanFDF, ccArr, iterCountArr, residFDF = \
              do_rmclean_hogbom(dirtyFDF        = dirtyFDF,
                                phiArr_radm2    = phiArr_radm2,
                                RMSFArr         = RMSFArr,
                                phi2Arr_radm2   = phi2Arr_radm2,
                                fwhmRMSFArr     = np.array(mDict["fwhmRMSF"]),
                                cutoff          = cutoff,
                                maxIter         = maxIter,
                                gain            = gain,
                                verbose         = verbose,
                                doPlots         = showPlots,
                                window          = window
                                )

    # ALTERNATIVE RM_CLEAN CODE ----------------------------------------------#
    '''
    cleanFDF, ccArr, fwhmRMSF, iterCount = \
              do_rmclean(dirtyFDF     = dirtyFDF,
                         phiArr       = phiArr_radm2,
                         lamSqArr     = lamSqArr_m2,
                         cutoff       = cutoff,
                         maxIter      = maxIter,
                         gain         = gain,
                         weight       = weightArr,
                         RMSFArr      = RMSFArr,
                         RMSFphiArr   = phi2Arr_radm2,
                         fwhmRMSF     = mDict["fwhmRMSF"],
                         doPlots      = True)
    '''
    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    if verbose: log("> RM-CLEAN completed in %.4f seconds." % cputime)

    # Measure the parameters of the deconvolved FDF
    mDict_cl = measure_FDF_parms(FDF=cleanFDF,
                                 phiArr=phiArr_radm2,
                                 fwhmRMSF=mDict["fwhmRMSF"],
                                 dFDF=mDict["dFDFth"],
                                 lamSqArr_m2=lambdaSqArr_m2,
                                 lam0Sq=mDict["lam0Sq_m2"])
    mDict_cl["cleanCutoff"] = cutoff
    mDict_cl["nIter"] = int(iterCountArr)

    # Measure the complexity of the clean component spectrum
    mDict_cl["mom2CCFDF"] = measure_fdf_complexity(phiArr=phiArr_radm2,
                                                   FDF=ccArr)

    #Calculating observed errors (based on dFDFcorMAD)
    mDict_cl["dPhiObserved_rm2"] = mDict_cl["dPhiPeakPIfit_rm2"] * mDict_cl[
        "dFDFcorMAD"] / mDict["dFDFth"]
    mDict_cl["dAmpObserved"] = mDict_cl["dFDFcorMAD"]
    mDict_cl["dPolAngleFitObserved_deg"] = mDict_cl[
        "dPolAngleFit_deg"] * mDict_cl["dFDFcorMAD"] / mDict["dFDFth"]
    mDict_cl["dPolAngleFit0Observed_deg"] = mDict_cl[
        "dPolAngle0Fit_deg"] * mDict_cl["dFDFcorMAD"] / mDict["dFDFth"]

    if verbose:
        # Print the results to the screen
        log()
        log('-' * 80)
        log('RESULTS:\n')
        log('FWHM RMSF = %.4g rad/m^2' % (mDict["fwhmRMSF"]))
        log('Pol Angle = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
            (mDict_cl["polAngleFit_deg"], mDict_cl["dPolAngleFitObserved_deg"],
             mDict_cl["dPolAngleFit_deg"]))
        log('Pol Angle 0 = %.4g (+/-%.4g observed, +- %.4g theoretical) deg' %
            (mDict_cl["polAngle0Fit_deg"],
             mDict_cl["dPolAngleFit0Observed_deg"],
             mDict_cl["dPolAngle0Fit_deg"]))
        log('Peak FD = %.4g (+/-%.4g observed, +- %.4g theoretical) rad/m^2' %
            (mDict_cl["phiPeakPIfit_rm2"], mDict_cl["dPhiObserved_rm2"],
             mDict_cl["dPhiPeakPIfit_rm2"]))
        log('freq0_GHz = %.4g ' % (mDict["freq0_Hz"] / 1e9))
        log('I freq0 = %.4g %s' % (mDict["Ifreq0"], mDict["units"]))
        log('Peak PI = %.4g (+/-%.4g observed, +- %.4g theoretical) %s' %
            (mDict_cl["ampPeakPIfit"], mDict_cl["dAmpObserved"],
             mDict_cl["dAmpPeakPIfit"], mDict["units"]))
        log('QU Noise = %.4g %s' % (mDict["dQU"], mDict["units"]))
        log('FDF Noise (theory)   = %.4g %s' %
            (mDict["dFDFth"], mDict["units"]))
        log('FDF Noise (Corrected MAD) = %.4g %s' %
            (mDict_cl["dFDFcorMAD"], mDict["units"]))
        log('FDF Noise (rms)   = %.4g %s' %
            (mDict_cl["dFDFrms"], mDict["units"]))

        log('FDF SNR = %.4g ' % (mDict_cl["snrPIfit"]))
        log()
        log('-' * 80)

    # Pause to display the figure
    if showPlots or saveFigures:
        fdfFig = plot_clean_spec(phiArr_radm2, dirtyFDF, cleanFDF, ccArr,
                                 residFDF, cutoff, window, mDict["units"])
    # Pause if plotting enabled
    if showPlots:
        plt.show()
    if saveFigures:
        if verbose: print("Saving CLEAN FDF plot:")
        outFilePlot = prefixOut + "_cleanFDF-plots.pdf"
        if verbose: print("> " + outFilePlot)
        fdfFig.savefig(outFilePlot, bbox_inches='tight')
        # print("Press <RETURN> to exit ...", end=' ')
        # input()

    #add array dictionary
    aDict_cl = dict()
    aDict_cl["phiArr_radm2"] = phiArr_radm2
    aDict_cl["freqArr_Hz"] = freqArr_Hz
    aDict_cl["cleanFDF"] = cleanFDF
    aDict_cl["ccArr"] = ccArr
    aDict_cl["iterCountArr"] = iterCountArr
    aDict_cl["residFDF"] = residFDF

    return mDict_cl, aDict_cl
Exemple #7
0
def run_rmsynth(data,
                polyOrd=3,
                phiMax_radm2=None,
                dPhi_radm2=None,
                nSamples=10.0,
                weightType="variance",
                fitRMSF=False,
                noStokesI=False,
                phiNoise_radm2=1e6,
                nBits=32,
                showPlots=False,
                debug=False,
                verbose=False,
                log=print):
    """
    Read the I, Q & U data and run RM-synthesis.
    """

    # Default data types
    dtFloat = "float" + str(nBits)
    dtComplex = "complex" + str(2 * nBits)

    # freq_Hz, I_Jy, Q_Jy, U_Jy, dI_Jy, dQ_Jy, dU_Jy
    try:
        if verbose:
            log("> Trying [freq_Hz, I_Jy, Q_Jy, U_Jy, dI_Jy, dQ_Jy, dU_Jy]",
                end=' ')
        (freqArr_Hz, IArr_Jy, QArr_Jy, UArr_Jy, dIArr_Jy, dQArr_Jy,
         dUArr_Jy) = data
        if verbose: log("... success.")
    except Exception:
        if verbose: log("...failed.")
        # freq_Hz, q_Jy, u_Jy, dq_Jy, du_Jy
        try:
            if verbose:
                log("> Trying [freq_Hz, q_Jy, u_Jy,  dq_Jy, du_Jy]", end=' ')
            (freqArr_Hz, QArr_Jy, UArr_Jy, dQArr_Jy, dUArr_Jy) = data
            if verbose: log("... success.")
            noStokesI = True
        except Exception:
            if verbose: log("...failed.")
            if debug:
                log(traceback.format_exc())
            sys.exit()
    if verbose: log("Successfully read in the Stokes spectra.")

    # If no Stokes I present, create a dummy spectrum = unity
    if noStokesI:
        log("Warn: no Stokes I data in use.")
        IArr_Jy = np.ones_like(QArr_Jy)
        dIArr_Jy = np.zeros_like(QArr_Jy)

    # Convert to GHz and mJy for convenience
    freqArr_GHz = freqArr_Hz / 1e9
    IArr_mJy = IArr_Jy * 1e3
    QArr_mJy = QArr_Jy * 1e3
    UArr_mJy = UArr_Jy * 1e3
    dIArr_mJy = dIArr_Jy * 1e3
    dQArr_mJy = dQArr_Jy * 1e3
    dUArr_mJy = dUArr_Jy * 1e3
    dQUArr_mJy = (dQArr_mJy + dUArr_mJy) / 2.0
    dQUArr_Jy = dQUArr_mJy / 1e3

    # Fit the Stokes I spectrum and create the fractional spectra
    IModArr, qArr, uArr, dqArr, duArr, fitDict = \
             create_frac_spectra(freqArr  = freqArr_GHz,
                                 IArr     = IArr_mJy,
                                 QArr     = QArr_mJy,
                                 UArr     = UArr_mJy,
                                 dIArr    = dIArr_mJy,
                                 dQArr    = dQArr_mJy,
                                 dUArr    = dUArr_mJy,
                                 polyOrd  = polyOrd,
                                 verbose  = True,
                                 debug    = debug)

    # Plot the data and the Stokes I model fit
    if showPlots:
        if verbose: log("Plotting the input data and spectral index fit.")
        freqHirArr_Hz = np.linspace(freqArr_Hz[0], freqArr_Hz[-1], 10000)
        IModHirArr_mJy = poly5(fitDict["p"])(freqHirArr_Hz / 1e9)
        specFig = plt.figure(figsize=(12.0, 8))
        plot_Ipqu_spectra_fig(freqArr_Hz=freqArr_Hz,
                              IArr_mJy=IArr_mJy,
                              qArr=qArr,
                              uArr=uArr,
                              dIArr_mJy=dIArr_mJy,
                              dqArr=dqArr,
                              duArr=duArr,
                              freqHirArr_Hz=freqHirArr_Hz,
                              IModArr_mJy=IModHirArr_mJy,
                              fig=specFig)

        # Use the custom navigation toolbar (does not work on Mac OS X)
        #        try:
        #            specFig.canvas.toolbar.pack_forget()
        #            CustomNavbar(specFig.canvas, specFig.canvas.toolbar.window)
        #        except Exception:
        #            pass

        # Display the figure
        #        if not plt.isinteractive():
        #            specFig.show()

        # DEBUG (plot the Q, U and average RMS spectrum)
        if debug:
            rmsFig = plt.figure(figsize=(12.0, 8))
            ax = rmsFig.add_subplot(111)
            ax.plot(freqArr_Hz / 1e9,
                    dQUArr_mJy,
                    marker='o',
                    color='k',
                    lw=0.5,
                    label='rms <QU>')
            ax.plot(freqArr_Hz / 1e9,
                    dQArr_mJy,
                    marker='o',
                    color='b',
                    lw=0.5,
                    label='rms Q')
            ax.plot(freqArr_Hz / 1e9,
                    dUArr_mJy,
                    marker='o',
                    color='r',
                    lw=0.5,
                    label='rms U')
            xRange = (np.nanmax(freqArr_Hz) - np.nanmin(freqArr_Hz)) / 1e9
            ax.set_xlim(
                np.min(freqArr_Hz) / 1e9 - xRange * 0.05,
                np.max(freqArr_Hz) / 1e9 + xRange * 0.05)
            ax.set_xlabel('$\\nu$ (GHz)')
            ax.set_ylabel('RMS (mJy bm$^{-1}$)')
            ax.set_title("RMS noise in Stokes Q, U and <Q,U> spectra")
#            rmsFig.show()

#-------------------------------------------------------------------------#

# Calculate some wavelength parameters
    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)
    dFreq_Hz = np.nanmin(np.abs(np.diff(freqArr_Hz)))
    lambdaSqRange_m2 = (np.nanmax(lambdaSqArr_m2) - np.nanmin(lambdaSqArr_m2))
    dLambdaSqMin_m2 = np.nanmin(np.abs(np.diff(lambdaSqArr_m2)))
    dLambdaSqMax_m2 = np.nanmax(np.abs(np.diff(lambdaSqArr_m2)))

    # Set the Faraday depth range
    fwhmRMSF_radm2 = 2.0 * m.sqrt(3.0) / lambdaSqRange_m2
    if dPhi_radm2 is None:
        dPhi_radm2 = fwhmRMSF_radm2 / nSamples
    if phiMax_radm2 is None:
        phiMax_radm2 = m.sqrt(3.0) / dLambdaSqMax_m2
        phiMax_radm2 = max(phiMax_radm2, 600.0)  # Force the minimum phiMax

    # Faraday depth sampling. Zero always centred on middle channel
    nChanRM = int(round(abs((phiMax_radm2 - 0.0) / dPhi_radm2)) * 2.0 + 1.0)
    startPhi_radm2 = -(nChanRM - 1.0) * dPhi_radm2 / 2.0
    stopPhi_radm2 = +(nChanRM - 1.0) * dPhi_radm2 / 2.0
    phiArr_radm2 = np.linspace(startPhi_radm2, stopPhi_radm2, nChanRM)
    phiArr_radm2 = phiArr_radm2.astype(dtFloat)
    if verbose:
        log("PhiArr = %.2f to %.2f by %.2f (%d chans)." %
            (phiArr_radm2[0], phiArr_radm2[-1], float(dPhi_radm2), nChanRM))

    # Calculate the weighting as 1/sigma^2 or all 1s (uniform)
    if weightType == "variance":
        weightArr = 1.0 / np.power(dQUArr_mJy, 2.0)
    else:
        weightType = "uniform"
        weightArr = np.ones(freqArr_Hz.shape, dtype=dtFloat)
    if verbose: log("Weight type is '%s'." % weightType)

    startTime = time.time()

    # Perform RM-synthesis on the spectrum
    dirtyFDF, lam0Sq_m2 = do_rmsynth_planes(dataQ=qArr,
                                            dataU=uArr,
                                            lambdaSqArr_m2=lambdaSqArr_m2,
                                            phiArr_radm2=phiArr_radm2,
                                            weightArr=weightArr,
                                            nBits=nBits,
                                            verbose=True,
                                            log=log)

    # Calculate the Rotation Measure Spread Function
    RMSFArr, phi2Arr_radm2, fwhmRMSFArr, fitStatArr = \
        get_rmsf_planes(lambdaSqArr_m2  = lambdaSqArr_m2,
                        phiArr_radm2    = phiArr_radm2,
                        weightArr       = weightArr,
                        mskArr          = ~np.isfinite(qArr),
                        lam0Sq_m2       = lam0Sq_m2,
                        double          = True,
                        fitRMSF         = fitRMSF,
                        fitRMSFreal     = False,
                        nBits           = nBits,
                        verbose         = True,
                        log             = log)
    fwhmRMSF = float(fwhmRMSFArr)

    # ALTERNATE RM-SYNTHESIS CODE --------------------------------------------#

    #dirtyFDF, [phi2Arr_radm2, RMSFArr], lam0Sq_m2, fwhmRMSF = \
    #          do_rmsynth(qArr, uArr, lambdaSqArr_m2, phiArr_radm2, weightArr)

    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    if verbose: log("> RM-synthesis completed in %.2f seconds." % cputime)

    # Determine the Stokes I value at lam0Sq_m2 from the Stokes I model
    # Multiply the dirty FDF by Ifreq0 to recover the PI in Jy
    freq0_Hz = C / m.sqrt(lam0Sq_m2)
    Ifreq0_mJybm = poly5(fitDict["p"])(freq0_Hz / 1e9)
    dirtyFDF *= (Ifreq0_mJybm / 1e3)  # FDF is in Jy

    # Calculate the theoretical noise in the FDF !!Old formula only works for wariance weights!
    #dFDFth_Jybm = np.sqrt(1./np.sum(1./dQUArr_Jy**2.))
    dFDFth_Jybm = np.sqrt(
        np.sum(weightArr**2 * dQUArr_Jy**2) / (np.sum(weightArr))**2)

    # Measure the parameters of the dirty FDF
    # Use the theoretical noise to calculate uncertainties
    mDict = measure_FDF_parms(FDF=dirtyFDF,
                              phiArr=phiArr_radm2,
                              fwhmRMSF=fwhmRMSF,
                              dFDF=dFDFth_Jybm,
                              lamSqArr_m2=lambdaSqArr_m2,
                              lam0Sq=lam0Sq_m2)
    mDict["Ifreq0_mJybm"] = toscalar(Ifreq0_mJybm)
    mDict["polyCoeffs"] = ",".join([str(x) for x in fitDict["p"]])
    mDict["IfitStat"] = fitDict["fitStatus"]
    mDict["IfitChiSqRed"] = fitDict["chiSqRed"]
    mDict["lam0Sq_m2"] = toscalar(lam0Sq_m2)
    mDict["freq0_Hz"] = toscalar(freq0_Hz)
    mDict["fwhmRMSF"] = toscalar(fwhmRMSF)
    mDict["dQU_Jybm"] = toscalar(nanmedian(dQUArr_Jy))
    mDict["dFDFth_Jybm"] = toscalar(dFDFth_Jybm)
    if mDict['phiPeakPIfit_rm2'] == None:
        log('Peak is at edge of RM spectrum! Peak fitting failed!\n')
        log('Rerunning with Phi_max twice as large.')
        #The following code re-runs everything with higher phiMax,
        #Then overwrite the appropriate variables so as to continue on without
        #interuption.
        mDict, aDict = run_rmsynth(data=data,
                                   polyOrd=polyOrd,
                                   phiMax_radm2=phiMax_radm2 * 2,
                                   dPhi_radm2=dPhi_radm2,
                                   nSamples=nSamples,
                                   weightType=weightType,
                                   fitRMSF=fitRMSF,
                                   noStokesI=noStokesI,
                                   nBits=nBits,
                                   showPlots=False,
                                   debug=debug,
                                   verbose=verbose)
        phiArr_radm2 = aDict["phiArr_radm2"]
        phi2Arr_radm2 = aDict["phi2Arr_radm2"]
        RMSFArr = aDict["RMSFArr"]
        freqArr_Hz = aDict["freqArr_Hz"]
        weightArr = aDict["weightArr"]
        dirtyFDF = aDict["dirtyFDF"]

    # Measure the complexity of the q and u spectra
    mDict["fracPol"] = mDict["ampPeakPIfit_Jybm"] / (Ifreq0_mJybm / 1e3)
    mD, pD = measure_qu_complexity(freqArr_Hz=freqArr_Hz,
                                   qArr=qArr,
                                   uArr=uArr,
                                   dqArr=dqArr,
                                   duArr=duArr,
                                   fracPol=mDict["fracPol"],
                                   psi0_deg=mDict["polAngle0Fit_deg"],
                                   RM_radm2=mDict["phiPeakPIfit_rm2"])
    mDict.update(mD)

    # Debugging plots for spectral complexity measure
    if debug:
        tmpFig = plot_complexity_fig(xArr=pD["xArrQ"],
                                     qArr=pD["yArrQ"],
                                     dqArr=pD["dyArrQ"],
                                     sigmaAddqArr=pD["sigmaAddArrQ"],
                                     chiSqRedqArr=pD["chiSqRedArrQ"],
                                     probqArr=pD["probArrQ"],
                                     uArr=pD["yArrU"],
                                     duArr=pD["dyArrU"],
                                     sigmaAdduArr=pD["sigmaAddArrU"],
                                     chiSqReduArr=pD["chiSqRedArrU"],
                                     probuArr=pD["probArrU"],
                                     mDict=mDict)
        tmpFig.show()

    #add array dictionary
    aDict = dict()
    aDict["phiArr_radm2"] = phiArr_radm2
    aDict["phi2Arr_radm2"] = phi2Arr_radm2
    aDict["RMSFArr"] = RMSFArr
    aDict["freqArr_Hz"] = freqArr_Hz
    aDict["weightArr"] = weightArr
    aDict["dirtyFDF"] = dirtyFDF

    if verbose:
        # Print the results to the screen
        log()
        log('-' * 80)
        log('RESULTS:\n')
        log('FWHM RMSF = %.4g rad/m^2' % (mDict["fwhmRMSF"]))

        log('Pol Angle = %.4g (+/-%.4g) deg' %
            (mDict["polAngleFit_deg"], mDict["dPolAngleFit_deg"]))
        log('Pol Angle 0 = %.4g (+/-%.4g) deg' %
            (mDict["polAngle0Fit_deg"], mDict["dPolAngle0Fit_deg"]))
        log('Peak FD = %.4g (+/-%.4g) rad/m^2' %
            (mDict["phiPeakPIfit_rm2"], mDict["dPhiPeakPIfit_rm2"]))
        log('freq0_GHz = %.4g ' % (mDict["freq0_Hz"] / 1e9))
        log('I freq0 = %.4g mJy/beam' % (mDict["Ifreq0_mJybm"]))
        log('Peak PI = %.4g (+/-%.4g) mJy/beam' %
            (mDict["ampPeakPIfit_Jybm"] * 1e3,
             mDict["dAmpPeakPIfit_Jybm"] * 1e3))
        log('QU Noise = %.4g mJy/beam' % (mDict["dQU_Jybm"] * 1e3))
        log('FDF Noise (theory)   = %.4g mJy/beam' %
            (mDict["dFDFth_Jybm"] * 1e3))
        log('FDF Noise (Corrected MAD) = %.4g mJy/beam' %
            (mDict["dFDFcorMAD_Jybm"] * 1e3))
        log('FDF Noise (rms)   = %.4g mJy/beam' %
            (mDict["dFDFrms_Jybm"] * 1e3))
        log('FDF SNR = %.4g ' % (mDict["snrPIfit"]))
        log('sigma_add(q) = %.4g (+%.4g, -%.4g)' %
            (mDict["sigmaAddQ"], mDict["dSigmaAddPlusQ"],
             mDict["dSigmaAddMinusQ"]))
        log('sigma_add(u) = %.4g (+%.4g, -%.4g)' %
            (mDict["sigmaAddU"], mDict["dSigmaAddPlusU"],
             mDict["dSigmaAddMinusU"]))
        log()
        log('-' * 80)

    # Plot the RM Spread Function and dirty FDF
    if showPlots:
        fdfFig = plt.figure(figsize=(12.0, 8))
        plot_rmsf_fdf_fig(phiArr=phiArr_radm2,
                          FDF=dirtyFDF,
                          phi2Arr=phi2Arr_radm2,
                          RMSFArr=RMSFArr,
                          fwhmRMSF=fwhmRMSF,
                          vLine=mDict["phiPeakPIfit_rm2"],
                          fig=fdfFig)

        # Use the custom navigation toolbar
#        try:
#            fdfFig.canvas.toolbar.pack_forget()
#            CustomNavbar(fdfFig.canvas, fdfFig.canvas.toolbar.window)
#        except Exception:
#            pass

# Display the figure
#        fdfFig.show()

# Pause if plotting enabled
    if showPlots or debug:
        plt.show()
        #        #if verbose: print "Press <RETURN> to exit ...",


#        input()

    return mDict, aDict
Exemple #8
0
def RM_synth(stokes,
             weight=True, # weighting needs more testing. Fails on certain events
             upchan=False, 
             RM_lim=None, 
             nSamples=None,
             normed=False,
             noise_type='theory',
             diagnostic_plots=False,
             cutoff=None):
    
    """ Performs rotation measure synthesis to extract Faraday Dispersion Function and RM measurement
    
    Parameters
    __________
    
    stokes : list (freq,I,Q,U,V,dI,dQ,dU,dV)
        Stokes params.
    weight : Bool. 
        Freq. channel std., used as to create array of weights for FDF
    band_lo, band_hi : float
        bottom and top freq. band limits of burst. 
    upchan: Bool
        If true, sets nSamples=3 
    RM_lim: float (optional)
        limits in Phi space to calculate the FDF
    nSamples: int (optional)
        sampling density in Phi space
    diagnostic_plots: Bool.
        outputs diagnostic plots of FDF
            
    Returns
    _______
    FDF params, (phi, FDF_arr) : list, array_like
        (RM,RM_err,PA,PA_err), FDF_arr
    """

    freqArr=stokes[0].copy()
    IArr=stokes[1].copy()
    QArr=stokes[2].copy()
    UArr=stokes[3].copy()
    VArr=stokes[4].copy()
    dIArr=stokes[5].copy()
    dQArr=stokes[6].copy()
    dUArr=stokes[7].copy()
    dVArr=stokes[8].copy()


    freqArr_Hz=freqArr*1e6
    lamArr_m=phys_const.speed_of_light/freqArr_Hz # convert to wavelength in m
    lambdaSqArr_m2=lamArr_m**2
    
    if normed is True:
        dQArr=IArr*np.sqrt((dQArr/QArr)**2+(dIArr/IArr)**2)
        dUArr=IArr*np.sqrt((dUArr/UArr)**2+(dIArr/IArr)**2)
        QArr/=IArr
        UArr/=IArr


    dQUArr = (dQArr + dUArr)/2.0
    if weight is True:
        weightArr = 1.0 / np.power(dQUArr, 2.0)
    else:
        weightArr = np.ones(freqArr_Hz.shape, dtype=float)
    dFDFth = np.sqrt( np.sum(weightArr**2 * dQUArr**2) / (np.sum(weightArr))**2 ) # check this equation!!!
    
    if nSamples is None:
        if upchan:
            nSamples=3 # sampling resolution of the FDF. 
        else:
            nSamples=10

    lambdaSqRange_m2 = (np.nanmax(lambdaSqArr_m2) - np.nanmin(lambdaSqArr_m2) )  
    fwhmRMSF_radm2 = 2.0 * np.sqrt(3.0) / lambdaSqRange_m2

#     dLambdaSqMin_m2 = np.nanmin(np.abs(np.diff(lambdaSqArr_m2)))
#     dLambdaSqMax_m2 = np.nanmax(np.abs(np.diff(lambdaSqArr_m2)))
    dLambdaSqMed_m2 = np.nanmedian(np.abs(np.diff(lambdaSqArr_m2)))

    dPhi_radm2 = fwhmRMSF_radm2 / nSamples

    #     phiMax_radm2 = np.sqrt(3.0) / dLambdaSqMax_m2
    phiMax_radm2 = np.sqrt(3.0) / dLambdaSqMed_m2 # sets the RM limit that can be probed based on intrachannel depolarization
    phiMax_radm2 = max(phiMax_radm2,600)    # Force the minimum phiMax

    if RM_lim  is None:
        # Faraday depth sampling. Zero always centred on middle channel
        nChanRM = int(round(abs((phiMax_radm2 - 0.0) / dPhi_radm2)) * 2.0 + 1.0)
        startPhi_radm2 = - (nChanRM-1.0) * dPhi_radm2 / 2.0
        stopPhi_radm2 = + (nChanRM-1.0) * dPhi_radm2 / 2.0
        phiArr_radm2 = np.linspace(startPhi_radm2, stopPhi_radm2, nChanRM)

    else:
        startPhi_radm2 = RM_lim[0]
        stopPhi_radm2 = RM_lim[1]
        nChanRM = int(round(abs((((stopPhi_radm2-startPhi_radm2)//2) - 0.0) / dPhi_radm2)) * 2.0 + 1.0)
        phiArr_radm2 = np.linspace(startPhi_radm2, stopPhi_radm2, nChanRM)
    
    phiArr_radm2 = phiArr_radm2.astype(np.float)
  
    ### constructing FDF ###

    dirtyFDF, lam0Sq_m2 = do_rmsynth_planes(
    QArr, 
    UArr, 
    lambdaSqArr_m2, 
    phiArr_radm2)

    RMSFArr, phi2Arr_radm2, fwhmRMSFArr, fitStatArr = get_rmsf_planes(
    lambdaSqArr_m2 = lambdaSqArr_m2,
    phiArr_radm2 = phiArr_radm2,
    weightArr=weightArr, 
    mskArr=None,
    lam0Sq_m2=lam0Sq_m2, 
    double = True) # routine needed for RM-cleaning 

    FDF, lam0Sq_m2 = do_rmsynth_planes(
    QArr, 
    UArr, 
    lambdaSqArr_m2, 
    phiArr_radm2,                   
    weightArr=weightArr, 
    lam0Sq_m2=None,
    nBits=32, 
    verbose=False)

    FDF_max=np.argmax(abs(FDF))
    FDF_med=np.median(abs(FDF))

    dFDFobs=np.median(abs(abs(FDF)-FDF_med)) / np.sqrt(np.pi/2) #MADFM definition of noise
#   dFDF_obs=np.nanstd(abs(FDF)) #std. definition of noise

    if noise_type is 'observed':
        FDF_snr=abs((abs(FDF)-FDF_med)/dFDFobs)/2
    if noise_type is 'theory':
        FDF_snr=abs((abs(FDF)-FDF_med)/dFDFth)/2

    mDict = measure_FDF_parms(FDF         = dirtyFDF,
                              phiArr      = phiArr_radm2,
                              fwhmRMSF    = fwhmRMSF_radm2,
                              dFDF        = dFDFth, #FDF_noise
                              lamSqArr_m2 = lambdaSqArr_m2,
                              lam0Sq      = lam0Sq_m2)

    RM_radm2_fit=mDict["phiPeakPIfit_rm2"]
    dRM_radm2_fit=mDict["dPhiPeakPIfit_rm2"]

    RM_radm2=phiArr_radm2[FDF_max]
    dRM_radm2=fwhmRMSF_radm2/(2*FDF_snr.max())

    polAngle0Fit_deg=mDict["polAngle0Fit_deg"]
    dPolAngle0Fit_deg=mDict["dPolAngle0Fit_deg"] * np.sqrt(freqArr.size) # np.sqrt(freqArr.size) term corrects for band-average noise 

    
    if cutoff is None:
    
        if diagnostic_plots:
            fig, ax = plt.subplots(2,1, figsize=(20,10))
            plt.subplots_adjust(left=0.1, bottom=0.1, right=0.99, top=0.95, wspace=0)
            ax[0].set_title('Faraday Dispersion Function')    
            ax[0].plot(phiArr_radm2,FDF_snr)
            ax[0].set_xlim([phiArr_radm2.min(), phiArr_radm2.max()])
            ax[1].plot(phiArr_radm2,FDF_snr)
            #     ax[1].axvline(RM_radm2, color='k', ls=':', label=r'RM=%.2f $\pm$ %0.2f rad/m$^2$' %(RM_radm2,dRM_radm2))
            ax[1].set_xlim(phiArr_radm2[FDF_max]-300,phiArr_radm2[FDF_max]+300)
            ax[1].set_xlabel('$\phi$ [rad/m$^2$]')
            fig.text(0.03, 0.5, 'Polarized Intensity [S/N]', va='center', rotation='vertical')
            #     plt.legend(fontsize=20)
            #     plt.tight_layout()

            if isinstance(diagnostic_plots, bool):
                plt.show()
            else:
                plot_name = "FDF.png"
                plt.savefig(os.path.join(diagnostic_plots, plot_name))
                plt.close("all")
            
        return (RM_radm2_fit,RM_radm2,dRM_radm2_fit,dRM_radm2,polAngle0Fit_deg,dPolAngle0Fit_deg),(phiArr_radm2,FDF_snr)

    else:

        if noise_type is 'observed':
            cutoff_abs = dFDFobs*cutoff
        if noise_type is 'theory':
            cutoff_abs = dFDFth*cutoff

        cleanFDF, ccArr, iterCountArr, residFDF = do_rmclean_hogbom(dirtyFDF = FDF,
                                phiArr_radm2    = phiArr_radm2,
                                RMSFArr         = RMSFArr,
                                phi2Arr_radm2   = phi2Arr_radm2,
                                fwhmRMSFArr     = fwhmRMSF_radm2,
                                cutoff          = cutoff_abs,
    #                                 maxIter         = maxIter,
    #                                 gain            = gain,
    #                                 verbose         = verbose,
                                doPlots         = True)


        FDF_max=np.argmax(abs(cleanFDF))
        FDF_med=np.median(abs(cleanFDF))

        dFDFobs=np.median(abs(abs(cleanFDF)-FDF_med)) / np.sqrt(np.pi/2) #MADFM definition of noise                                                      #   dFDF_obs=np.nanstd(abs(FDF)) #std. definition of noise                                                                                      
        if noise_type is 'observed':
            FDF_snr_clean=abs((abs(cleanFDF)-FDF_med)/dFDFobs)/2
            ccArr_snr=(abs(ccArr)/dFDFobs)/2
        if noise_type is 'theory':
            FDF_snr_clean=abs((abs(cleanFDF)-FDF_med)/dFDFth)/2
            ccArr_snr=(abs(ccArr)/dFDFth)/2

        mDict = measure_FDF_parms(FDF     = cleanFDF,
                              phiArr      = phiArr_radm2,
                              fwhmRMSF    = fwhmRMSF_radm2,
                              dFDF        = dFDFth, #FDF_noise
                              lamSqArr_m2 = lambdaSqArr_m2,
                              lam0Sq      = lam0Sq_m2)

        RM_radm2_fit=mDict["phiPeakPIfit_rm2"]
        dRM_radm2_fit=mDict["dPhiPeakPIfit_rm2"]

        RM_radm2=phiArr_radm2[FDF_max]
        dRM_radm2=fwhmRMSF_radm2/(2*FDF_snr_clean.max())

        polAngle0Fit_deg=mDict["polAngle0Fit_deg"]
        dPolAngle0Fit_deg=mDict["dPolAngle0Fit_deg"] * np.sqrt(freqArr.size) # np.sqrt(freqArr.size) term corrects for band-average noise 


        if diagnostic_plots:
            fig, ax = plt.subplots(2,1, figsize=(20,10))
            plt.subplots_adjust(left=0.1, bottom=0.1, right=0.99, top=0.95, wspace=0)
            ax[0].set_title('Faraday Dispersion Function')    
            ax[0].plot(phiArr_radm2,FDF_snr_clean, label='clean FDF')
            ax[0].plot(phiArr_radm2,FDF_snr, label='dirty FDF')
            ax[0].axhline(cutoff, ls='--', color='k', label='clean cutoff')
            ax[0].legend()
            ax[0].set_xlim([phiArr_radm2.min(), phiArr_radm2.max()])
            ax[1].plot(phiArr_radm2,FDF_snr_clean, label='clean FDF')
            ax[1].plot(phiArr_radm2,FDF_snr, label='dirty FDF')
            ax[1].axhline(cutoff, ls='--', color='k',label='clean cutoff')
            ax[1].legend()
            ax[1].set_xlim(phiArr_radm2[FDF_max]-300,phiArr_radm2[FDF_max]+300)
            ax[1].set_xlabel('$\phi$ [rad/m$^2$]')
            fig.text(0.03, 0.5, 'Polarized Intensity [S/N]', va='center', rotation='vertical')

            if isinstance(diagnostic_plots, bool):
                plt.show()
            else:
                plot_name = "FDF.png"
                plt.savefig(os.path.join(diagnostic_plots, plot_name))
                plt.close("all")
                            
            fig, ax = plt.subplots(2,1,figsize=(20,10))
            plt.subplots_adjust(left=0.1, bottom=0.1, right=0.99, top=0.95, wspace=0, hspace=0.0)
            ax[0].set_title('Faraday Dispersion Function')    
            ax[0].plot(phi2Arr_radm2,RMSFArr, label='RMTF')
            ax[0].set_xlim([-300,300])
            ax[0].xaxis.set_ticklabels([])
            ax[0].legend()
            ax[1].plot(phiArr_radm2,FDF_snr_clean, label='clean FDF')
            ax[1].bar(phiArr_radm2,ccArr_snr, color='g', label='clean components')
            ax[1].legend()
            ax[1].set_xlim(phiArr_radm2[FDF_max]-300,phiArr_radm2[FDF_max]+300)
            ax[1].set_xlabel('$\phi$ [rad/m$^2$]')
            fig.text(0.03, 0.5, 'Polarized Intensity [S/N]', va='center', rotation='vertical')
            #     plt.legend(fontsize=20)
            #     plt.tight_layout()

            if isinstance(diagnostic_plots, bool): 
                plt.show()
            else:
                plot_name = "FDF_clean.png"
                plt.savefig(os.path.join(diagnostic_plots, plot_name))
                plt.close("all")

        return (RM_radm2_fit,RM_radm2,dRM_radm2_fit,dRM_radm2,polAngle0Fit_deg,dPolAngle0Fit_deg),(phiArr_radm2,FDF_snr_clean)
Exemple #9
0
def run_rmclean(fdfFile,
                rmsfFile,
                weightFile,
                rmSynthFile,
                cutoff,
                maxIter=1000,
                gain=0.1,
                prefixOut="",
                outDir="",
                nBits=32,
                showPlots=False,
                doAnimate=False):
    """
    Run RM-CLEAN on a complex FDF spectrum given a RMSF.
    """

    # Default data types
    dtFloat = "float" + str(nBits)
    dtComplex = "complex" + str(2 * nBits)

    # Read the frequency vector for the lambda^2 array
    freqArr_Hz, weightArr = np.loadtxt(weightFile, unpack=True, dtype=dtFloat)
    lambdaSqArr_m2 = np.power(C / freqArr_Hz, 2.0)

    # Read the FDF from the ASCII file
    phiArr_radm2, FDFreal, FDFimag = np.loadtxt(fdfFile,
                                                unpack=True,
                                                dtype=dtFloat)
    dirtyFDF = FDFreal + 1j * FDFimag

    # Read the RMSF from the ASCII file
    phi2Arr_radm2, RMSFreal, RMSFimag = np.loadtxt(rmsfFile,
                                                   unpack=True,
                                                   dtype=dtFloat)
    RMSFArr = RMSFreal + 1j * RMSFimag

    # Read the RM-synthesis parameters from the JSON file
    mDictS = json.load(open(rmSynthFile, "r"))

    # If the cutoff is negative, assume it is a sigma level
    print "Expected RMS noise = %.4g mJy/beam/rmsf"  % \
        (mDictS["dFDFth_Jybm"]*1e3)
    if cutoff < 0:
        print "Using a sigma cutoff of %.1f." % (-1 * cutoff),
        cutoff = -1 * mDictS["dFDFth_Jybm"] * cutoff
        print "Absolute value = %.3g" % cutoff
    else:
        print "Using an absolute cutoff of %.3g (%.1f x expected RMS)." % \
            (cutoff, cutoff/mDictS["dFDFth_Jybm"])

    startTime = time.time()

    # Perform RM-clean on the spectrum
    cleanFDF, ccArr, iterCountArr = \
              do_rmclean_hogbom(dirtyFDF        = dirtyFDF,
                                phiArr_radm2    = phiArr_radm2,
                                RMSFArr         = RMSFArr,
                                phi2Arr_radm2   = phi2Arr_radm2,
                                fwhmRMSFArr     = np.array(mDictS["fwhmRMSF"]),
                                cutoff          = cutoff,
                                maxIter         = maxIter,
                                gain            = gain,
                                verbose         = False,
                                doPlots         = showPlots,
                                doAnimate       = doAnimate)
    cleanFDF  #/= 1e3
    ccArr  #/= 1e3

    # ALTERNATIVE RM_CLEAN CODE ----------------------------------------------#
    '''
    cleanFDF, ccArr, fwhmRMSF, iterCount = \
              do_rmclean(dirtyFDF     = dirtyFDF,
                         phiArr       = phiArr_radm2,
                         lamSqArr     = lamSqArr_m2,
                         cutoff       = cutoff,
                         maxIter      = maxIter,
                         gain         = gain,
                         weight       = weightArr,
                         RMSFArr      = RMSFArr,
                         RMSFphiArr   = phi2Arr_radm2,
                         fwhmRMSF     = mDictS["fwhmRMSF"],
                         doPlots      = True)
    '''
    #-------------------------------------------------------------------------#

    endTime = time.time()
    cputime = (endTime - startTime)
    print "> RM-CLEAN completed in %.4f seconds." % cputime

    # Measure the parameters of the deconvolved FDF
    mDict = measure_FDF_parms(
        FDF=cleanFDF,
        phiArr=phiArr_radm2,
        fwhmRMSF=mDictS["fwhmRMSF"],
        #dFDF        = mDictS["dFDFth_Jybm"],
        lamSqArr_m2=lambdaSqArr_m2,
        lam0Sq=mDictS["lam0Sq_m2"])
    mDict["cleanCutoff"] = cutoff
    mDict["nIter"] = int(iterCountArr)

    # Measure the complexity of the clean component spectrum
    mDict["mom2CCFDF"] = measure_fdf_complexity(phiArr=phiArr_radm2, FDF=ccArr)

    # Save the deconvolved FDF and CC model to ASCII files
    print "Saving the clean FDF and component model to ASCII files."
    outFile = prefixOut + "_FDFclean.dat"
    print "> %s" % outFile
    np.savetxt(outFile, zip(phiArr_radm2, cleanFDF.real, cleanFDF.imag))
    outFile = prefixOut + "_FDFmodel.dat"
    print "> %s" % outFile
    np.savetxt(outFile, zip(phiArr_radm2, ccArr))

    # Save the RM-clean measurements to a "key=value" text file
    print "Saving the measurements on the FDF in 'key=val' and JSON formats."
    outFile = prefixOut + "_RMclean.dat"
    print "> %s" % outFile
    FH = open(outFile, "w")
    for k, v in mDict.iteritems():
        FH.write("%s=%s\n" % (k, v))
    FH.close()
    outFile = prefixOut + "_RMclean.json"
    print "> %s" % outFile
    json.dump(mDict, open(outFile, "w"))

    # Print the results to the screen
    print
    print '-' * 80
    print 'RESULTS:\n'
    print 'FWHM RMSF = %.4g rad/m^2' % (mDictS["fwhmRMSF"])

    print 'Pol Angle = %.4g (+/-%.4g) deg' % (mDict["polAngleFit_deg"],
                                              mDict["dPolAngleFit_deg"])
    print 'Pol Angle 0 = %.4g (+/-%.4g) deg' % (mDict["polAngle0Fit_deg"],
                                                mDict["dPolAngle0Fit_deg"])
    print 'Peak FD = %.4g (+/-%.4g) rad/m^2' % (mDict["phiPeakPIfit_rm2"],
                                                mDict["dPhiPeakPIfit_rm2"])
    print 'freq0_GHz = %.4g ' % (mDictS["freq0_Hz"] / 1e9)
    print 'I freq0 = %.4g mJy/beam' % (mDictS["Ifreq0_mJybm"])
    print 'Peak PI = %.4g (+/-%.4g) mJy/beam' % (
        mDict["ampPeakPIfit_Jybm"] * 1e3, mDict["dAmpPeakPIfit_Jybm"] * 1e3)
    print 'QU Noise = %.4g mJy/beam' % (mDictS["dQU_Jybm"] * 1e3)
    print 'FDF Noise (measure) = %.4g mJy/beam' % (mDict["dFDFms_Jybm"] * 1e3)
    print 'FDF SNR = %.4g ' % (mDict["snrPIfit"])
    print
    print '-' * 80

    # Pause to display the figure
    if showPlots or doAnimate:
        print "Press <RETURN> to exit ...",
        raw_input()