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