Example #1
0
File: data.py Project: tk575/adapy
    def bin_spectrum(self, binning, bintype='mean'):
        from string import lower
        from scipy import alen, arange, array
        from sys import exit as sysexit
        binning = int(binning)
        self.binning = binning

        ##### temporary solution, saving old stuff
        class Original:
            pass

        Original.d = self.d
        Original.v_arr = self.v_arr
        Original.v_cdelt = self.v_cdelt
        Original.v_cdeltkms = self.v_cdeltkms
        self.Original = Original
        #
        if lower(bintype) == 'resample':
            from congridding import congrid
            # congridding, proper resampling of data
            self.d = congrid(self.d, (alen(self.d) / binning, ),
                             centre=True,
                             method='neighbour')
            self.v_arr = congrid(self.v_arr, (alen(self.v_arr) / binning, ))
            #
            self.v_cdeltkms = self.v_cdeltkms * binning
            self.v_cdelt = self.v_cdelt * binning
        elif lower(bintype) == 'mean':
            if alen(self.d) % binning != 0:
                print 'Bin has to be evenly devide the number of channels: %d' % alen(
                    self.d)
                sysexit()
            #  Old method - simple binning, just average
            indices = arange(0, alen(self.d), binning)
            self.d = array(
                [self.d[x:x + binning].sum(axis=0) / binning for x in indices])
            self.v_arr = array([
                self.v_arr[x:x + binning].sum(axis=0) / binning
                for x in indices
            ])
            #
            self.v_cdeltkms = self.v_cdeltkms * binning
        elif binning == 0 or binning < 0:
            print stylify(
                "\nERROR:\n Variable \"bin\" has to be 1 for no binning, or above 1 \n\
            for the number of channels to bin")
        # print out information about the binning
        print '=' * 40
        print ' ' * 11, "Binning of data\n"
        print "No channels to bin : %d" % self.binning
        print "Velocity step : %f" % self.v_cdeltkms
        if bintype == 'mean':
            print 'Type of binning : Simple mean over selected no. bin channels'
        elif bintype == 'resample':
            print 'Type of binning : Resampling - 1D interpolation'
        # set the "binned" flag to True! (i.e. larger than 0)
        # every time we bin, it increases with the number of the binning parameter
        # hence the number of channels that it has been binned is repr
        # by this parameter
        self.binned += self.binning
Example #2
0
 def bin_spectrum(self, binning, bintype='mean'):
     from string import lower
     from scipy import alen, arange, array
     from sys import exit as sysexit
     binning = int(binning)
     self.binning = binning
     ##### temporary solution, saving old stuff
     class Original: pass
     Original.d = self.d
     Original.v_arr = self.v_arr
     Original.v_cdelt = self.v_cdelt
     Original.v_cdeltkms = self.v_cdeltkms
     self.Original = Original
     #
     if lower(bintype) == 'resample':
         from congridding import congrid
         # congridding, proper resampling of data
         self.d = congrid(self.d,(alen(self.d)/binning,),centre=True, method='neighbour')
         self.v_arr = congrid(self.v_arr,(alen(self.v_arr)/binning,))
         #
         self.v_cdeltkms = self.v_cdeltkms*binning
         self.v_cdelt = self.v_cdelt*binning
     elif lower(bintype) == 'mean':
         if alen(self.d)%binning!=0:
             print 'Bin has to be evenly devide the number of channels: %d' % alen(self.d)
             sysexit()
         #  Old method - simple binning, just average
         indices = arange(0,alen(self.d),binning)
         self.d = array([self.d[x:x+binning].sum(axis=0)/binning for x in indices])
         self.v_arr = array([self.v_arr[x:x+binning].sum(axis=0)/binning for x in indices])
         #
         self.v_cdeltkms = self.v_cdeltkms*binning
     elif binning == 0 or binning <0:
         print stylify("\nERROR:\n Variable \"bin\" has to be 1 for no binning, or above 1 \n\
         for the number of channels to bin")
     # print out information about the binning
     print '='*40
     print ' '*11,"Binning of data\n"
     print "No channels to bin : %d" % self.binning
     print "Velocity step : %f" % self.v_cdeltkms
     if bintype=='mean':
         print 'Type of binning : Simple mean over selected no. bin channels'
     elif bintype=='resample':
         print 'Type of binning : Resampling - 1D interpolation'
     # set the "binned" flag to True! (i.e. larger than 0)
     # every time we bin, it increases with the number of the binning parameter
     # hence the number of channels that it has been binned is repr
     # by this parameter
     self.binned +=self.binning
Example #3
0
File: data.py Project: tk575/adapy
    def __init__(self, Fits, chvals, nsig):
        """
        moment class initialiser

        input :


        DONE : Check if I take enough levels, i.e. that the self.maximum
               really is the maximum of the WHOLE 2D array
                -> Yes it is

        """
        from scipy import sqrt, alen, flipud, arange, array, ones, nan
        # -> never use binned array
        # -> never use velocities from/with the v_sys corrected data
        # get the data from the cube
        # copy header for easy acess to stuff
        self.hdr = Fits.hdr
        self.channels = get_indices(Fits.v_arr, chvals)
        imgs = Fits.d[self.channels]
        ## MOMENT 0
        # calculate the moment 0
        self.zero = imgs.sum(axis=0) * abs(Fits.v_cdeltkms)
        #Isum = imgs.sum(axis=0)*abs(Fits.v_cdeltkms) # make a copy for masking <3sigma values in mom1 map
        ## STATISTICS of MOMENT 0 (sigma, min, max, levels)
        # other statistics
        self.sigma = sqrt(alen(imgs)) * Fits.rms * abs(Fits.v_cdeltkms)
        self.minimum = self.zero.min()
        self.maximum = self.zero.max()
        # calculate levels, start at 1 sigma, jump 1 sigma
        # one for positive and one for negative
        # concatenate before displaying if want certain start & jump
        self.levels_neg = -1 * arange(self.sigma,
                                      abs(self.minimum) + 2 * self.sigma,
                                      self.sigma)
        self.levels_pos = arange(self.sigma, self.maximum + 2 * self.sigma,
                                 self.sigma)
        #levels = arange(nsig*moment0_sigma,moment0_max+moment0_sigma,nsjump*moment0_sigma)
        ## MOMENT 1
        # create array that matches imgs array
        # only the velocities that we want, i.e. Fits.v_arr[self.channels]
        velocities_matrix = array(
            [ones(imgs.shape[1:]) * i for i in Fits.v_arr[self.channels]])
        # removed where(), because a boolean array works fine
        # find out where we calculate the moment 1, i.e. 3 sigma level
        Isum = imgs.sum(axis=0)
        lt_3sigma = (self.zero <
                     (nsig * self.sigma)) * (self.zero >
                                             (-1.0 * nsig * self.sigma))
        Isum[lt_3sigma] = nan
        Ivsum = (imgs * velocities_matrix).sum(axis=0)
        # calculate the denominator, the sum of all images
        # in our velocity interval

        # MOMENT 1
        #
        # M1 = V(x,y)
        # = sum( v_i * I(x,y,v_i)) / sum(I(x,y,v_i))
        #
        self.one = Ivsum / Isum

        # MOMENT 2
        #
        # M2 = sqrt[ sum( I(x,y,v_i) * (v_i - V(x,y))**2 ) / sum(I(x,y,v_i)) ]
        #
        # M2 = sqrt[ sum( I(x,y,v_i) * (v_i - M1)**2 ) / sum(I(x,y,v_i)) ]
        #
        top = imgs * (velocities_matrix - self.one)**2
        division = abs(top.sum(axis=0) / Isum)
        self.two = sqrt(division)
Example #4
0
File: data.py Project: tk575/adapy
    def fit_lines(self, **args):
        """
        TODO : calculate errors for the fit etc, look at Kaper et al (1966)\
                and Condon (1996)
        TODO : calculate the sum (integrated) intensity
                and apply Beam_eff? (for SD, in K kms-1)
        TODO : guess parameters if only one gaussian?
        TODO : interactive?
        TODO : streamline what is printed out, and what is saved in
                the Fit-class

        Intensities:
        from
        http://www.iram.es/IRAMES/otherDocuments/manuals/index.html
        and the document
        http://www.iram.es/IRAMES/otherDocuments/manuals/Report/cali_rep_ddo970205.ps


        T*A temperatures are brightness temperatures of an
        equivalent source which fills the entire 2pi sterradians of
        the forward beam pattern of the telescope. To obtain the
        brightness temperature of and equivalent source just filling
        the main beam (def. as main beam brightness temperature
        Tmb), antenna temperatures have to be multiplied bu the
        ratio of the forward and main beam efficiency Beff.

        Tmb = Feff/Beff * T*A


        """
        from scipy import array, sqrt, log, alen, arange, pi, diff
        from libs.cgsconst import CC
        if 'linefit' not in args:
            # add either interactive fitting or
            # some guessing algorithm
            print 'Need first guesses for the fitting atm.'
            return False
        elif 'linefit' in args:  # first guess parameters supplied!
            # first copy the fit-dictionary
            fitting = args['linefit'].copy()
        # start print msgs
        print '=' * 40
        print ' ' * 11, "Line fitting\n"
        ##### check if error is supplied
        if 'error' not in fitting:
            # if no error is supplied, use the calculated
            # main class RMS
            if hasattr(self, 'rms'):
                fitting['error'] = self.rms
            elif not hasattr(self, 'rms'):
                errmsg = 'No error supplied.\n\
                Either supply with {\'error\': VALUE} or cal\
                calculate with Spect.calc_rms(nvals=[n1,n2,n3,n4])'

                print stylify(errmsg, fg='r')
                raise ParError(fitting)
        elif 'error' in fitting:
            print 'We have error from input, not from Spectrum'
        ##### small useful functions
        fwhmfromsig = 2 * sqrt(2 * log(2))  # the constant
        fwhm = lambda x: fwhmfromsig * x
        sigma = lambda x: x / fwhmfromsig
        ##### fits 1D gaussian(s) to spectral data
        if 'chvals' in args:  # if we supplied limits for the fit
            print args['chvals']
            ch = get_indices(self.v_arr, args['chvals'])
            Fx = self.d[ch]
            X = self.v_arr[ch]
        else:  # else use the eveything
            Fx = self.d
            X = self.v_arr
        #
        p = fitting['params']  # parameters for the fit
        #
        if 'limmin' not in fitting:
            fitting['limmin'] = None
            fitting['minpar'] = None
        if 'limmax' not in fitting:
            fitting['limmax'] = None
            fitting['maxpar'] = None
        if 'fixlist' not in fitting:
            fitting['fixlist'] = None
        if 'tie' not in fitting:
            fitting['tie'] = None
        #
        from time import time
        t1 = time()
        #
        fitting_results = fit_gauss1d((X, Fx),
                                      params=p,
                                      fixlist=fitting['fixlist'],
                                      minbool=fitting['limmin'],
                                      minpar=fitting['minpar'],
                                      maxbool=fitting['limmax'],
                                      maxpar=fitting['maxpar'],
                                      err=fitting['error'],
                                      tie=fitting['tie'],
                                      verbose=0,
                                      full_output=1)

        if fitting_results == None:
            print(stylify('\n\n No fitting done...', f='b', fg='r'))
        elif fitting_results != None:
            ###### subclass fit

            class Fit:
                pass

            Fit.params, Fit.errors, Fit.chi2, Fit.mp = fitting_results
            #~ params, errors, chi2, mp = fitting_results
            print ' Done in %2.3f seconds' % (time() - t1)
            #
            print ' Number of fits : ', alen(Fit.params) / 3
            print(' Fit status : ', Fit.mp.status,
                  '(if 0, it should have halted)')
            print(' Chi2 : {0}, reduced : {1}\n'.format(
                Fit.chi2, Fit.chi2 / float(len(Fx))))
            # now, parse output of fitting and print it out on screen
            Fit.line_widths = []
            Fit.frequencies_fitted = []
            Fit.frequencies_corrected = []
            Fit.vel_shifts = []
            Fit.freq_shifts = []
            Fit.gauss_intensities = []
            Fit.sum_intensities = []
            Fit.sum_intensities2 = []
            Fit.line_names = []
            Fit.peak_intensities = []
            Fit.error_gauss_intensities = []
            #
            Fit.nfits = alen(Fit.params) / 3
            j = 0
            from scipy import sqrt
            for i in arange(0, len(Fit.params), 3):
                # add 1 because channel 1 is in pos 0
                fwhm = Fit.params[i + 2]
                half_fwhm = fwhm / 2.
                ampl = Fit.params[i]
                pos = Fit.params[i + 1]

                # first figure out the extent of the gaussian (the line)
                # jump half a channel down and up so that it finds the correct channels
                lower_half, upper_half = (pos + array([-1, 1]) * half_fwhm)
                lower, upper = (pos + array([-1, 1]) * fwhm)
                lower2, upper2 = (pos + array([-1, 1]) * fwhm * 2)
                #channels = where((velocity>lower)*(velocity<upper))[0]+1
                channels_half = get_indices(self.v_arr,
                                            [lower_half, upper_half])
                channels = get_indices(self.v_arr, [lower, upper])
                channels2 = get_indices(self.v_arr, [lower2, upper2])
                #draw_highlight_box(ax_kms, params[i+1], params[i+2]*3)
                # apply v_sys correction,
                #so that we use v_sys for estimating the correct
                # frequency for the line,
                #especially importat when using line-identification
                frequency_fitted = calc_frequency(pos, self.restfreq / 1e9)
                frequency_corrected = calc_frequency(pos - self.v_sys,
                                                     self.restfreq / 1e9)
                Fit.line_widths.append(fwhm)
                Fit.frequencies_corrected.append(frequency_corrected)
                Fit.frequencies_fitted.append(frequency_fitted)
                Fit.peak_intensities.append(self.d[channels2].max())
                #
                constant = self.teleff
                #gauss_int = (sqrt(2 * pi) *
                #            sigma(Fit.params[i+2]) *
                #            Fit.params[i])*constant
                gauss_int = ampl * sigma(fwhm) * sqrt(2 * pi) * constant
                sum_int = (self.d[channels].sum() *
                           abs(self.v_cdeltkms)) * constant
                sum_int2 = (self.d[channels2].sum() *
                            abs(self.v_cdeltkms)) * constant
                Fit.gauss_intensities.append(gauss_int)
                Fit.sum_intensities.append(sum_int)
                Fit.sum_intensities2.append(sum_int2)
                Fit.error_gauss_intensities.append(
                    1.064 * sqrt((fwhm * Fit.errors[i])**2 +
                                 (ampl * Fit.errors[i + 2])**2))
                if hasattr(self, 'lines'):
                    # frequency shift = rest frequency - measured frequency
                    # velocity shift  =  c (freq_shift/freq._rest)
                    # 'lines' contain the name and frequency of
                    # the identified lines, that is all the
                    # lines we're fitting
                    # TODO : self.lines[1] are strings, change to floats
                    #print self.lines[1][j],type(self.lines[1][j])
                    freq_shift = self.lines[1][j] - frequency_corrected
                    vel_shift = CC * 1e-2 * freq_shift / self.lines[1][
                        j] * 1E-3  # in kms
                    Fit.freq_shifts.append(freq_shift)
                    Fit.vel_shifts.append(vel_shift)
                    Fit.line_names.append(self.lines[0][j])
                print stylify('Fit number : {0}'.format(j + 1), fg='g', bg='k')
                intensity_string = u" Intensity: Fit= {0:2.4f}, Data= {1:2.4f} (\u00b1FWHM), {2:2.4f} (\u00b12*FWHM) {3}".format(
                    gauss_int, sum_int, sum_int2, self.unitint)
                print intensity_string
                parameter_string = u" Ampl= {0:2.3f} (\u00b1{1:2.3f}) {2}, Pos= {3:2.3f} (\u00b1{4:2.3f} {5})".format(
                    Fit.params[i], Fit.errors[i], self.unit, Fit.params[i + 1],
                    Fit.errors[i + 1], KMS)
                print stylify(parameter_string, fg='b')
                print u" Width= {0:2.3f} (\u00b1{1:2.3f}) {2} (FWHM, \u03c3={3:2.3f})".format(
                    Fit.params[i + 2], Fit.errors[i + 2], KMS,
                    sigma(Fit.params[i + 2]))
                # calculate frequency and velocity offset if linelist exists
                if hasattr(self, 'lines'):
                    # print offset (freq. & vel.)
                    print " Id molecule : {0}".format(self.lines[0][j])
                    print u" Frequency shift : {0:2.5} GHz Vel shift : {1:5.5} {2}".format(
                        freq_shift, vel_shift, KMS)
                frequency_string =\
                u' Frequency : {0:3.9f} GHz (v_sys corrected)'.format(frequency_corrected)
                print stylify(frequency_string, fg='b')
                print u' FWHM      : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(
                    channels_half.min(), channels_half.max(),
                    (channels_half.max() - channels_half.min() + 1),
                    lower_half, upper_half, KMS)
                print u' \u00b1FWHM   : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(
                    channels.min(), channels.max(),
                    (channels.max() - channels.min() + 1), lower, upper, KMS)
                print u' \u00b1FWHM   : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(
                    channels.min(), channels.max(),
                    (channels.max() - channels.min() + 1), lower, upper, KMS)
                if self.binned:
                    channels_nobin = get_indices(self.Original.v_arr,
                                                 [lower, upper])
                    channels_half_nobin = get_indices(self.Original.v_arr,
                                                      [lower_half, upper_half])
                    print u'Original channels :\n \t FWHM width  : {0}, {1} (\u00b1 1/2FWHM) (0-based)'.format(
                        channels_half_nobin.min(), channels_half_nobin.max())
                    print u' \t \u00b1FWHM width : {0}, {1} ({2}) (0-based) \n'.format(
                        channels_nobin.min(), channels_nobin.max(),
                        (channels_nobin.max() - channels_nobin.min() + 1))
                j += 1
            #
            Fit.line_widths = array(Fit.line_widths)
            print 20 * '- '
            print u'Mean FWHM : {0:2.1f} \u00b1{1:2.2f} km\u00b7s\u207b\u00b9\n'.format(
                Fit.line_widths.mean(), Fit.line_widths.std())
            Fit.xarr = arange(X[0], X[-1], (diff(X)[0] / 4))
            # lastly get the Fit class into the Spectrum class (self)
            self.Fit = Fit
Example #5
0
def gauss1d(x, params=None, height=None):
    """

    NB: Use this for plotting, the gauss1dfit gaussian function should be
        faster for fitting.

    returns the function vector of input x with parameters p
    just a gaussian function, perhaps to plot a gaussian after the fitting

    fix is the fixed parameters array

    should have subtracted baseline of data before fitting
    i.e. not height is fitted


    gaussian of the form a[0]*exp(-(X-a[1])**2/(2*a[2]**2)*2*sqrt(2*log(2)))
    is fitted. Here a[2] is the FWHM, we multiply with  2*sqrt(2*log(2)) to
    transform it to sigma.
    For an astronomer the FWHM is much more interesting, right?

    TODO : be able to pass height as optional variable

    Changes:
    *using numpy instead of scipy

    """
    from scipy import exp, array, alen, array, log, sqrt, arange
    #
    # Check the input parameters, since this is for plotting, we check them
    #
    # params should always be a list of parameters
    # if it is a series of tuple(s)
    # ie params=((A1,x1,sig1),(A2,x2,sig2))
    if alen(params[0]) == 3:
        # just put them in a long array
        params = array([list(i) for i in params]).flatten()
        # or in their respective arrays directly?
        #a = params[arange(0,len(params),3)]
        #x = params[arange(1,len(params),3)]
        #dx = params[arange(2,len(params),3)]
    elif params == None:
        # add code to
        # calculate moments!
        raise ParError(params)
    #
    no_fits = len(params) / 3
    #
    # check that all parameters are input
    if len(params) % 3 != 0:
        raise ParError(params)
    #
    # 2*sqrt(2*log(2)) to convert to sigma (two is cancled out)
    if height == None:
        gaussian = lambda X, a: (a[0] * exp(-(X - a[1])**2 /
                                            (a[2]**2) * 4 * log(2)))
    elif height != None:
        gaussian = lambda X, a: height + (a[0] * exp(-(X - a[1])**2 /
                                                     (a[2]**2) * 4 * log(2)))
    #
    def func(X, p):
        S = 0
        try:
            for i in arange(0, len(p), 3):
                S += gaussian(X, p[i:i + 3])
        except IndexError:
            raise ParError(p)
        return S

    #
    #
    return func(x, params)
Example #6
0
    pos = params[arange(1, len(params), 3)]
    if (pos < X.min()).any() or (pos > X.max()).any():
        print('You are trying to fit a Gaussian outside of the \
    data range\n Not allowed. Exciting without fitting')
        return None
    widths = params[arange(2, len(params), 3)]
    if (widths < abs(X[1] - X[0])).any():
        raise ParError(
            "Width of a line can not be less than channel width of data (<{0:3.3} kms)."
            .format(abs(X[1] - X[0])))
    # number of gaussians to fit
    no_fits = (len(params)) / 3
    #
    # check the total number of params
    if len(params) % 3 != 0:
        raise ParError(params.reshape((alen(params) / 3, 3)))

    # that was the most important parameters,
    # now on to the other
    # FIXLIST
    if fixlist != None:
        fixlist = array(fixlist).flatten()
        # length of fixlist must equal the number of parameters
        if alen(fixlist) != alen(params):
            raise ParError(fixlist)
    elif fixlist == None:
        fixlist = [False, False, False] * no_fits
    #
    # MINIMUM LIMITS
    #
    # first we define a minimum limit for the width
Example #7
0
def gauss1d(x, params=None, height=None):
    """

    NB: Use this for plotting, the gauss1dfit gaussian function should be
        faster for fitting.

    returns the function vector of input x with parameters p
    just a gaussian function, perhaps to plot a gaussian after the fitting

    fix is the fixed parameters array

    should have subtracted baseline of data before fitting
    i.e. not height is fitted


    gaussian of the form a[0]*exp(-(X-a[1])**2/(2*a[2]**2)*2*sqrt(2*log(2)))
    is fitted. Here a[2] is the FWHM, we multiply with  2*sqrt(2*log(2)) to
    transform it to sigma.
    For an astronomer the FWHM is much more interesting, right?

    TODO : be able to pass height as optional variable

    Changes:
    *using numpy instead of scipy

    """
    from scipy import exp, array, alen, array, log, sqrt, arange

    #
    # Check the input parameters, since this is for plotting, we check them
    #
    # params should always be a list of parameters
    # if it is a series of tuple(s)
    # ie params=((A1,x1,sig1),(A2,x2,sig2))
    if alen(params[0]) == 3:
        # just put them in a long array
        params = array([list(i) for i in params]).flatten()
        # or in their respective arrays directly?
        # a = params[arange(0,len(params),3)]
        # x = params[arange(1,len(params),3)]
        # dx = params[arange(2,len(params),3)]
    elif params == None:
        # add code to
        # calculate moments!
        raise ParError(params)
    #
    no_fits = len(params) / 3
    #
    # check that all parameters are input
    if len(params) % 3 != 0:
        raise ParError(params)
    #
    # 2*sqrt(2*log(2)) to convert to sigma (two is cancled out)
    if height == None:
        gaussian = lambda X, a: (a[0] * exp(-(X - a[1]) ** 2 / (a[2] ** 2) * 4 * log(2)))
    elif height != None:
        gaussian = lambda X, a: height + (a[0] * exp(-(X - a[1]) ** 2 / (a[2] ** 2) * 4 * log(2)))
    #
    def func(X, p):
        S = 0
        try:
            for i in arange(0, len(p), 3):
                S += gaussian(X, p[i : i + 3])
        except IndexError:
            raise ParError(p)
        return S

    #
    #
    return func(x, params)
Example #8
0
        print (
            "You are trying to fit a Gaussian outside of the \
    data range\n Not allowed. Exciting without fitting"
        )
        return None
    widths = params[arange(2, len(params), 3)]
    if (widths < abs(X[1] - X[0])).any():
        raise ParError(
            "Width of a line can not be less than channel width of data (<{0:3.3} kms).".format(abs(X[1] - X[0]))
        )
    # number of gaussians to fit
    no_fits = (len(params)) / 3
    #
    # check the total number of params
    if len(params) % 3 != 0:
        raise ParError(params.reshape((alen(params) / 3, 3)))

    # that was the most important parameters,
    # now on to the other
    # FIXLIST
    if fixlist != None:
        fixlist = array(fixlist).flatten()
        # length of fixlist must equal the number of parameters
        if alen(fixlist) != alen(params):
            raise ParError(fixlist)
    elif fixlist == None:
        fixlist = [False, False, False] * no_fits
    #
    # MINIMUM LIMITS
    #
    # first we define a minimum limit for the width
Example #9
0
    def __init__ (self, Fits, chvals, nsig):
        """
        moment class initialiser

        input :


        DONE : Check if I take enough levels, i.e. that the self.maximum
               really is the maximum of the WHOLE 2D array
                -> Yes it is

        """
        from scipy import sqrt, alen, flipud, arange, array, ones, nan
        # -> never use binned array
        # -> never use velocities from/with the v_sys corrected data
        # get the data from the cube
        # copy header for easy acess to stuff
        self.hdr = Fits.hdr
        self.channels = get_indices(Fits.v_arr, chvals)
        imgs = Fits.d[self.channels]
        ## MOMENT 0
        # calculate the moment 0
        self.zero = imgs.sum(axis=0) * abs(Fits.v_cdeltkms)
        #Isum = imgs.sum(axis=0)*abs(Fits.v_cdeltkms) # make a copy for masking <3sigma values in mom1 map
        ## STATISTICS of MOMENT 0 (sigma, min, max, levels)
        # other statistics
        self.sigma = sqrt(alen(imgs)) * Fits.rms * abs(Fits.v_cdeltkms)
        self.minimum = self.zero.min()
        self.maximum = self.zero.max()
        # calculate levels, start at 1 sigma, jump 1 sigma
        # one for positive and one for negative
        # concatenate before displaying if want certain start & jump
        self.levels_neg = -1 * arange(self.sigma, abs(self.minimum) + 2 * self.sigma, self.sigma)
        self.levels_pos = arange(self.sigma, self.maximum + 2 * self.sigma, self.sigma)
        #levels = arange(nsig*moment0_sigma,moment0_max+moment0_sigma,nsjump*moment0_sigma)
        ## MOMENT 1
        # create array that matches imgs array
        # only the velocities that we want, i.e. Fits.v_arr[self.channels]
        velocities_matrix = array([ones(imgs.shape[1:]) * i for i in Fits.v_arr[self.channels]] )
        # removed where(), because a boolean array works fine
        # find out where we calculate the moment 1, i.e. 3 sigma level
        Isum = imgs.sum(axis=0)
        lt_3sigma = (self.zero < (nsig * self.sigma)) * (self.zero > (-1.0 * nsig * self.sigma))
        Isum[ lt_3sigma ] = nan
        Ivsum = (imgs * velocities_matrix).sum(axis=0)
        # calculate the denominator, the sum of all images
        # in our velocity interval

        # MOMENT 1
        #
        # M1 = V(x,y)
        # = sum( v_i * I(x,y,v_i)) / sum(I(x,y,v_i))
        #
        self.one = Ivsum / Isum

        # MOMENT 2
        #
        # M2 = sqrt[ sum( I(x,y,v_i) * (v_i - V(x,y))**2 ) / sum(I(x,y,v_i)) ]
        #
        # M2 = sqrt[ sum( I(x,y,v_i) * (v_i - M1)**2 ) / sum(I(x,y,v_i)) ]
        #
        top = imgs * (velocities_matrix - self.one)**2
        division = abs(top.sum(axis=0) / Isum)
        self.two = sqrt(division)
Example #10
0
    def fit_lines(self, **args):
        """
        TODO : calculate errors for the fit etc, look at Kaper et al (1966)\
                and Condon (1996)
        TODO : calculate the sum (integrated) intensity
                and apply Beam_eff? (for SD, in K kms-1)
        TODO : guess parameters if only one gaussian?
        TODO : interactive?
        TODO : streamline what is printed out, and what is saved in
                the Fit-class

        Intensities:
        from
        http://www.iram.es/IRAMES/otherDocuments/manuals/index.html
        and the document
        http://www.iram.es/IRAMES/otherDocuments/manuals/Report/cali_rep_ddo970205.ps


        T*A temperatures are brightness temperatures of an
        equivalent source which fills the entire 2pi sterradians of
        the forward beam pattern of the telescope. To obtain the
        brightness temperature of and equivalent source just filling
        the main beam (def. as main beam brightness temperature
        Tmb), antenna temperatures have to be multiplied bu the
        ratio of the forward and main beam efficiency Beff.

        Tmb = Feff/Beff * T*A


        """
        from scipy import array, sqrt, log, alen, arange, pi, diff
        from libs.cgsconst import CC
        if 'linefit' not in args:
            # add either interactive fitting or
            # some guessing algorithm
            print 'Need first guesses for the fitting atm.'
            return False
        elif 'linefit' in args: # first guess parameters supplied!
            # first copy the fit-dictionary
            fitting = args['linefit'].copy()
        # start print msgs
        print '=' * 40
        print ' ' * 11, "Line fitting\n"
        ##### check if error is supplied
        if 'error' not in fitting:
            # if no error is supplied, use the calculated
            # main class RMS
            if hasattr(self, 'rms'):
                fitting['error'] = self.rms
            elif  not hasattr(self, 'rms'):
                errmsg = 'No error supplied.\n\
                Either supply with {\'error\': VALUE} or cal\
                calculate with Spect.calc_rms(nvals=[n1,n2,n3,n4])'
                print stylify(errmsg,fg='r')
                raise ParError(fitting)
        elif 'error' in fitting:
            print 'We have error from input, not from Spectrum'
        ##### small useful functions
        fwhmfromsig = 2*sqrt(2*log(2)) # the constant
        fwhm = lambda x: fwhmfromsig*x
        sigma = lambda x: x/fwhmfromsig
        ##### fits 1D gaussian(s) to spectral data
        if 'chvals' in args: # if we supplied limits for the fit
            print args['chvals']
            ch = get_indices(self.v_arr,args['chvals'])
            Fx = self.d[ch]
            X = self.v_arr[ch]
        else: # else use the eveything
            Fx = self.d
            X = self.v_arr
        #
        p = fitting['params'] # parameters for the fit
        #
        if 'limmin' not in fitting:
            fitting['limmin'] = None
            fitting['minpar'] = None
        if 'limmax' not in fitting:
            fitting['limmax'] = None
            fitting['maxpar'] = None
        if 'fixlist' not in fitting:
            fitting['fixlist'] = None
        if 'tie' not in fitting:
            fitting['tie'] = None
        #
        from time import time
        t1 = time()
        #
        fitting_results = fit_gauss1d((X,Fx), params=p, fixlist=fitting['fixlist'],
                minbool=fitting['limmin'], minpar=fitting['minpar'],
                maxbool=fitting['limmax'], maxpar=fitting['maxpar'],
                err=fitting['error'], tie=fitting['tie'], verbose=0, full_output=1)

        if fitting_results==None:
            print(stylify('\n\n No fitting done...',f='b',fg='r'))
        elif fitting_results!=None:
            ###### subclass fit

            class Fit: pass

            Fit.params, Fit.errors, Fit.chi2, Fit.mp = fitting_results
            #~ params, errors, chi2, mp = fitting_results
            print ' Done in %2.3f seconds' % (time()-t1)
            #
            print ' Number of fits : ', alen(Fit.params)/3
            print(' Fit status : ',
                  Fit.mp.status,
                  '(if 0, it should have halted)')
            print(' Chi2 : {0}, reduced : {1}\n'.format(
                                                    Fit.chi2,
                                                    Fit.chi2/float(len(Fx))))
            # now, parse output of fitting and print it out on screen
            Fit.line_widths = []
            Fit.frequencies_fitted = []
            Fit.frequencies_corrected = []
            Fit.vel_shifts = []
            Fit.freq_shifts = []
            Fit.gauss_intensities = []
            Fit.sum_intensities = []
            Fit.sum_intensities2 = []
            Fit.line_names = []
            Fit.peak_intensities = []
            Fit.error_gauss_intensities = []
            #
            Fit.nfits = alen(Fit.params)/3
            j = 0
            from scipy import sqrt
            for i in arange(0,len(Fit.params),3):
                # add 1 because channel 1 is in pos 0
                fwhm = Fit.params[i+2]
                half_fwhm = fwhm/2.
                ampl = Fit.params[i]
                pos = Fit.params[i+1]

                # first figure out the extent of the gaussian (the line)
                # jump half a channel down and up so that it finds the correct channels
                lower_half, upper_half = (pos + array([-1,1])*half_fwhm)
                lower,upper = (pos + array([-1,1])*fwhm)
                lower2,upper2 = (pos + array([-1,1])*fwhm*2)
                #channels = where((velocity>lower)*(velocity<upper))[0]+1
                channels_half = get_indices(self.v_arr,
                                            [lower_half,upper_half])
                channels = get_indices(self.v_arr,[lower,upper])
                channels2 = get_indices(self.v_arr,[lower2,upper2])
                #draw_highlight_box(ax_kms, params[i+1], params[i+2]*3)
                # apply v_sys correction,
                #so that we use v_sys for estimating the correct
                # frequency for the line,
                #especially importat when using line-identification
                frequency_fitted = calc_frequency(pos, self.restfreq/1e9)
                frequency_corrected = calc_frequency(pos -
                                            self.v_sys, self.restfreq/1e9)
                Fit.line_widths.append(fwhm)
                Fit.frequencies_corrected.append(frequency_corrected)
                Fit.frequencies_fitted.append(frequency_fitted)
                Fit.peak_intensities.append(self.d[channels2].max())
                #
                constant = self.teleff
                #gauss_int = (sqrt(2 * pi) *
                #            sigma(Fit.params[i+2]) *
                #            Fit.params[i])*constant
                gauss_int = ampl*sigma(fwhm)*sqrt(2*pi)*constant
                sum_int = (self.d[channels].sum()*abs(self.v_cdeltkms))*constant
                sum_int2 = (self.d[channels2].sum()*abs(self.v_cdeltkms))*constant
                Fit.gauss_intensities.append(gauss_int)
                Fit.sum_intensities.append(sum_int)
                Fit.sum_intensities2.append(sum_int2)
                Fit.error_gauss_intensities.append(1.064 *
                                sqrt((fwhm*Fit.errors[i])**2 +
                                        (ampl*Fit.errors[i+2])**2))
                if hasattr(self, 'lines'):
                    # frequency shift = rest frequency - measured frequency
                    # velocity shift  =  c (freq_shift/freq._rest)
                    # 'lines' contain the name and frequency of
                    # the identified lines, that is all the
                    # lines we're fitting
                    # TODO : self.lines[1] are strings, change to floats
                    #print self.lines[1][j],type(self.lines[1][j])
                    freq_shift = self.lines[1][j] - frequency_corrected
                    vel_shift = CC*1e-2 * freq_shift/self.lines[1][j] * 1E-3 # in kms
                    Fit.freq_shifts.append(freq_shift)
                    Fit.vel_shifts.append(vel_shift)
                    Fit.line_names.append(self.lines[0][j])
                print  stylify('Fit number : {0}'.format(j+1),fg='g',bg='k')
                intensity_string = u" Intensity: Fit= {0:2.4f}, Data= {1:2.4f} (\u00b1FWHM), {2:2.4f} (\u00b12*FWHM) {3}".format(
                                gauss_int,sum_int,sum_int2,self.unitint)
                print intensity_string
                parameter_string=u" Ampl= {0:2.3f} (\u00b1{1:2.3f}) {2}, Pos= {3:2.3f} (\u00b1{4:2.3f} {5})".format(
                                    Fit.params[i],
                                    Fit.errors[i],
                                    self.unit,
                                    Fit.params[i+1],
                                    Fit.errors[i+1],
                                    KMS)
                print stylify(parameter_string,fg='b')
                print u" Width= {0:2.3f} (\u00b1{1:2.3f}) {2} (FWHM, \u03c3={3:2.3f})".format(Fit.params[i+2],Fit.errors[i+2],KMS,sigma(Fit.params[i+2]))
                # calculate frequency and velocity offset if linelist exists
                if hasattr(self,'lines'):
                    # print offset (freq. & vel.)
                    print " Id molecule : {0}".format(self.lines[0][j])
                    print u" Frequency shift : {0:2.5} GHz Vel shift : {1:5.5} {2}".format(freq_shift,vel_shift,KMS)
                frequency_string =\
                u' Frequency : {0:3.9f} GHz (v_sys corrected)'.format(frequency_corrected)
                print stylify(frequency_string,fg='b')
                print u' FWHM      : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(channels_half.min(), channels_half.max(),(channels_half.max()-channels_half.min()+1), lower_half, upper_half,KMS)
                print u' \u00b1FWHM   : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(channels.min(), channels.max(), (channels.max()-channels.min()+1), lower,upper,KMS)
                print u' \u00b1FWHM   : {0}, {1} ({2}) (0-based) ([{3:.2f}, {4:.2f}] {5})'.format(channels.min(), channels.max(), (channels.max()-channels.min()+1), lower,upper,KMS)
                if self.binned:
                    channels_nobin = get_indices(self.Original.v_arr,[lower,upper])
                    channels_half_nobin = get_indices(self.Original.v_arr,[lower_half,upper_half])
                    print u'Original channels :\n \t FWHM width  : {0}, {1} (\u00b1 1/2FWHM) (0-based)'.format(channels_half_nobin.min(), channels_half_nobin.max())
                    print  u' \t \u00b1FWHM width : {0}, {1} ({2}) (0-based) \n'.format(channels_nobin.min(), channels_nobin.max(),(channels_nobin.max()-channels_nobin.min()+1))
                j+=1
            #
            Fit.line_widths = array(Fit.line_widths)
            print 20*'- '
            print u'Mean FWHM : {0:2.1f} \u00b1{1:2.2f} km\u00b7s\u207b\u00b9\n'.format(Fit.line_widths.mean(),Fit.line_widths.std())
            Fit.xarr = arange(X[0],X[-1],(diff(X)[0]/4))
            # lastly get the Fit class into the Spectrum class (self)
            self.Fit = Fit