def fitFunction(x, y, initial, function, vary): """ Fit a function to a set of x and y values. @param x: The x grid @type x: array @param y: The y grid @type y: array @param initial: initial parameters @type initial: list @param function: The function to be fitted @type function: funclib.function (e.g. funclib.soft_parabola,funclib.gauss) @param vary: Allow initial parameter to be changed in fitting process. Must have same length as initial. @type vary: list[bool] @return: The model after minimization @rtype: funclib.function() (some function) """ #-- fit only soft parabola # 1. setup model #mymodel = funclib.soft_parabola() if function == funclib.gauss and False in vary: mymodel = function(use_jacobian=False) else: mymodel = function() # 2. Initial values: e.g. for SP [int,vlsr,vexp,gamma] mymodel.setup_parameters(values=initial, vary=vary) # 3. minimize and evaluate fit result = fit.minimize(x, y, mymodel) return mymodel
def fitFunction(x, y, initial, function, vary): """ Fit a function to a set of x and y values. @param x: The x grid @type x: array @param y: The y grid @type y: array @param initial: initial parameters @type initial: list @param function: The function to be fitted @type function: funclib.function (e.g. funclib.soft_parabola,funclib.gauss) @param vary: Allow initial parameter to be changed in fitting process. Must have same length as initial. @type vary: list[bool] @return: The model after minimization @rtype: funclib.function() (some function) """ # -- fit only soft parabola # 1. setup model # mymodel = funclib.soft_parabola() if function == funclib.gauss and False in vary: mymodel = function(use_jacobian=False) else: mymodel = function() # 2. Initial values: e.g. for SP [int,vlsr,vexp,gamma] mymodel.setup_parameters(values=initial, vary=vary) # 3. minimize and evaluate fit result = fit.minimize(x, y, mymodel) return mymodel
def fitLP(filename=None,lprof=None,theory=0,show=0,cfg='',convert_ms_kms=0,\ vary_pars=['vexp'],i_vexp=15.0,i_gamma=1.0,do_gauss=0): ''' Fit a line profile with a soft parabola, and a Gaussian component if required. The method automatically checks if a second component is needed (eg an extra absorption component). An estimate of the expansion velocity (width of the profile) and an improved guess of the vlsr are given. A guess for the gas terminal velocity is returned, as well as its error and the fitted profile (sp/gaussian, and if applicable extra gaussian and the full fit). @keyword filename: The filename to the data file of the line profile. If None a line profile object is expected. (default: None) @type filename: string @keyword lprof: A line profile object (LPDataReader or inheriting classes) If None, a filename is expected! If not None, the results are saved in this object as well as returned upon method call (default: None) @type lprof: LPDataReader() @keyword convert_ms_kms: Convert velocity grid from m/s to km/s. (default: 0) @type convert_ms_kms: bool @keyword theory: If theoretical profile, and filename is given, set vlsr to 0 and read two columns. lprof not relevant if True. (default: 0) @type theory: bool @keyword vary_pars: The soft parabola parameters varied (can only be vexp or gamma for now). The initial values for parameters listed here are not used. If 'gamma' is requested, a reasonable guess for i_vexp when calling the method will improve the fitting results. This is done for the first guess only! If a Gaussian absorption is present improving these first guesses won't make much of a difference. However, the first guess value for gamma is still used. Vexp is always varied if absorption is present. (default: ['vexp']) @type vary_pars: list[string] @keyword i_vexp: The initial guess for the expansion velocity. Not relevant if vexp is included in vary_pars. (default: 15.0) @type i_vexp: float @keyword i_gamma: The initial guess for the gamma parameter of soft parab. Not relevant if gamma is included in vary_pars. (default: 1.0) @type i_gamma: float @keyword do_gauss: Force a Gaussian fit regardless of soft parabola fit results. Still does the soft parabola fit first to allow for comparison of parameters. (default: 0) @type do_gauss: bool @keyword show: Show the results of the fit (default: 0) @type show: bool @return: dictionary including [vexp,evexp,gamma,egamma,fitprof,gaussian,\ fullfit,dintint,fgintint] @rtype: dict[float,float,float,float,funclib.Function(),\ funclib.Function(),funclib.Function()] ''' print '*******************************************' if theory and not filename is None: d = DataIO.readCols(filename=filename) vel = d[0] flux = d[1] vlsr = 0.0 else: if filename is None: filename = lprof.filename print '** Fitting line profile in %s.' % filename if lprof is None: lprof = readLineProfile(filename) vel = lprof.getVelocity() flux = lprof.getFlux() vel = vel[-np.isnan(flux)] flux = flux[-np.isnan(flux)] vlsr = lprof.getVlsr() if convert_ms_kms: vel = vel / 1000. #-- Initial values: [peak tmb,vlsr,vexp,gamma] # For the central peak value, get a first guess from the data # Attempt multiple vexp values and return the best fitting case. # The initial values are given, with an arbitrary value for the vexp key i_mid = argmin(np.abs(vel - vlsr)) peak = mean(flux[i_mid - 2:i_mid + 3]) #-- Vary vexp or gamma if requested. If not requested i_vexp or i_gamma are # used. # Multiple values for gamma are tried and the best fitting model # is then chosen based on the relative error of the fitted gamma. if 'gamma' in vary_pars: igammas = array([-0.5, -0.1, 0.1, 0.5, 1.0, 2.0, 4.0]) firstguess = varyInitialFit(vel,flux,[peak,vlsr,i_vexp,0.0],index=3,\ values=igammas,vary_window=1,vary=[1,1,1,1],\ function=funclib.soft_parabola) i_gamma = firstguess.get_parameters()[0][3] #-- varyInitialFit adapts the velocity window itself. No more # assumptions needed for the expansion velocity ivexps = array([50., 40., 30., 25., 20., 15., 10.]) if 'vexp' in vary_pars: firstguess = varyInitialFit(vel,flux,[peak,vlsr,0.,i_gamma],index=2,\ values=ivexps,vary_window=1,vary=[1,1,1,1],\ function=funclib.soft_parabola) vexp = abs(firstguess.get_parameters()[0][2]) window = 2. print 'First guess fit, using a soft parabola:' print firstguess.param2str(accuracy=5) #-- If vexp > 100, replace with 50. This value is unrealistic, and might be # improved with an extra Gaussian. If not, it will be replaced with a # full Gaussian fit anyway if vexp > 100: vexp = 50. #-- Check whether irregularities are present in the profile. # Initial parameters for a gaussian are returned if true. # For this, a broad selection is made of the profile, to allow for a # decent noise determination outside the line profile keep = np.abs(vel - vlsr) <= (2 * window * vexp) velsel, fluxsel = vel[keep], flux[keep] include_gauss = checkLPShape(velsel, fluxsel, vlsr, vexp, window, show=show) #-- Do the fit of the line again, including an extra gaussian if # irregularities are present. if not include_gauss is None: #-- fit soft para model + gaussian # 1. Set up new soft parabola for several guesses of vexp ivexps = list(ivexps) initial = [peak, vlsr, 0., i_gamma] all_init = [[p] * len(ivexps) for i, p in enumerate(initial) if i != 2] all_init.insert(2, ivexps) functions = [funclib.soft_parabola() for i in ivexps] [ ff.setup_parameters(values=initi) for ff, initi in zip(functions, zip(*all_init)) ] # 2. setup gaussian gaussians = [funclib.gauss() for ff in functions] # initial guesses assuming an interstellar absorption line from the # checkLPShape method [ gg.setup_parameters(values=include_gauss, vary=[True, True, True, False]) for gg in gaussians ] # 3. combine soft para + gaussian, and minimize fit mymodels = [ fit.Model(functions=[ff, gg]) for ff, gg in zip(functions, gaussians) ] [fit.minimize(vel[np.abs(vel-vlsr)<=(init[2]*1.5)],\ flux[np.abs(vel-vlsr)<=(init[2]*1.5)],\ mymodel) for mymodel,init in zip(mymodels,zip(*all_init))] # 4. Select the best fitting result based on the error on vexp mymodels = [fg for fg in mymodels if fg.get_parameters()[1][2] != 0.] functions = [ ff for fg, ff in zip(mymodels, functions) if fg.get_parameters()[1][2] != 0. ] gaussians = [ gg for fg, gg in zip(mymodels, gaussians) if fg.get_parameters()[1][2] != 0. ] fitvalues = array([fg.get_parameters()[0][2] for fg in mymodels]) fiterrors = array([fg.get_parameters()[1][2] for fg in mymodels]) mymodel = mymodels[argmin(abs(fiterrors / fitvalues))] finalfit = functions[argmin(abs(fiterrors / fitvalues))] gaussian = gaussians[argmin(abs(fiterrors / fitvalues))] print 'Improved fit, including extra Gaussian:' print mymodel.param2str(accuracy=5) else: #-- if gamma is requested to be varied, allow another iteration on # gamma with the best vexp guess we already have. if 'gamma' in vary_pars: finalfit = varyInitialFit(vel,flux,[peak,vlsr,vexp,0.0],\ index=3,values=igammas,vary_window=1,\ function=funclib.soft_parabola,\ vary=[True,True,True,True]) print 'Final fit with soft parabola, second gamma iteration:' print finalfit.param2str(accuracy=5) #-- firstguess is best we can do at the moment else: finalfit = firstguess #-- If the relative error on vexp is larger than 30%, usually something # funky is going on in the emission line. Try a Gaussian instead. vexp = abs(finalfit.get_parameters()[0][2]) evexp = abs(finalfit.get_parameters()[1][2]) gamma = finalfit.get_parameters()[0][3] egamma = finalfit.get_parameters()[1][3] #-- Gamma has to be positive. If it isnt, dont bother with Gaussian # (double peaked line profile will not be fitted well with a Gaussian!) if (evexp/vexp > 0.40 and gamma > 0) or (evexp/vexp > 0.20 and vexp> 30.) \ or do_gauss: #-- Go back to default window to try a Gaussian fit #keep = np.abs(vel-vlsr)<=(80) #velselg,fluxselg = vel[keep],flux[keep] do_gauss = 1 include_gauss = None #-- FWHM is twice vexp! sigmas = 2 * ivexps / (2. * sqrt(2. * log(2.))) finalfit = varyInitialFit(vel,flux,[peak,vlsr,0.,0.],index=2,\ values=sigmas,function=funclib.gauss,\ vary_window=1,vary=[True,True,True,False]) vexp = abs( finalfit.get_parameters()[0][2]) * (2. * sqrt(2. * log(2.))) / 2. evexp = abs( finalfit.get_parameters()[1][2]) * (2. * sqrt(2. * log(2.))) / 2. gamma, egamma = None, None window = 3. print 'Improved fit, using a gaussian instead of soft parabola:' print finalfit.param2str(accuracy=5) #-- Extract some shared parameters between the gauss and sp. peak = finalfit.get_parameters()[0][0] epeak = finalfit.get_parameters()[1][0] fvlsr = finalfit.get_parameters()[0][1] fevlsr = finalfit.get_parameters()[1][1] #-- Compute numerical integrations. # After fitting, window for integration should be 0.6*window. vexp is # not expected to be too small anymore as in checkLPShape keep = np.abs(vel - vlsr) <= (0.6 * window * vexp) velsel = vel[keep] flux_first = firstguess.evaluate(velsel) flux_final = finalfit.evaluate(velsel) dimb = trapz(y=flux[keep], x=velsel) fi_final = trapz(y=flux_final, x=velsel) print('I_mb (emission line data): %f'\ %dimb) print('I_mb (SP -- initial guess): %f'\ %trapz(y=flux_first,x=velsel)) print('I_mb (SP -- final guess): %f'\ %fi_final) if not include_gauss is None: fitted_flux = mymodel.evaluate(velsel) print('I_mb (SP + Gauss fit): %f'\ %trapz(y=fitted_flux,x=velsel)) print('Final v_exp guess: %.4f +/- %.4f km/s' % (vexp, evexp)) if not gamma is None: print('Final gamma guess: %.4f +/- %.4f' % (gamma, egamma)) print('Final vlsr guess: %.4f +/- %.4f' % (fvlsr, fevlsr)) print('Final peak Tmb guess at v_lsr: %.4f +/- %.4f K' % (peak, epeak)) fwhm = getLPDataFWHM(lprof) print('The FWHM is %.2f km/s.' % (fwhm)) #-- plot if show or cfg: plt.clf() #-- improve velocity window for plotting keep = np.abs(vel - vlsr) <= (1.5 * window * vexp) velsel, fluxsel = vel[keep], flux[keep] vel_highres = np.linspace(velsel[0], velsel[-1], 10000) flux_final_highres = finalfit.evaluate(vel_highres) flux_first_highres = firstguess.evaluate(vel_highres) if not include_gauss is None: flux_full_highres = mymodel.evaluate(vel_highres) if show: plt.step(velsel,fluxsel,'-r',where='mid',lw=3,\ label='Observed profile') plt.plot(vel_highres,flux_first_highres,'b-',lw=3,\ label='First guess') plt.plot(vel_highres,flux_final_highres,'g--',lw=3,\ label='Improved guess') if not include_gauss is None: plt.plot(vel_highres,flux_full_highres,'g-',lw=2,\ label='Full fit (including Gaussian)') leg = plt.legend(loc='best', fancybox=True) leg.get_frame().set_alpha(0.5) plt.show() if cfg: pf = '%s_fitted_%s'%(do_gauss and 'gaussFit' or 'SPFit',\ os.path.split(filename)[1]) keytags = ['Observed profile', 'Improved guess'] line_types = [ '-r', '-b', ] x = [velsel, vel_highres] y = [fluxsel, flux_final_highres] if not include_gauss is None: line_types.append('g--') x.append(vel_highres) y.append(flux_full_highres) keytags.append('Full fit (including Gaussian)') pf = Plotting2.plotCols(x=x,y=y,filename=pf,cfg=cfg,linewidth=5,\ yaxis='$T_\mathrm{mb}\ (\mathrm{K})$',\ xaxis='$v (\mathrm{km}/\mathrm{s})$',\ keytags=keytags,line_types=line_types,\ histoplot=[0]) print 'Your figure can be found at %s .' % pf #-- Collecting all relevant results and returning. results = dict() #-- If a Gaussian was used for the main profile fit results['do_gauss'] = do_gauss #-- Fitted parameters and errors results['vlsr'] = fvlsr results['evlsr'] = fevlsr results['vexp'] = vexp results['evexp'] = evexp results['fwhm'] = fwhm results['peak'] = peak results['epeak'] = epeak #-- Gamma is None if no soft parabola was fitted results['gamma'] = gamma results['egamma'] = egamma #-- Integrated line strengths: main line fit, data themselves, fit window results['fgintint'] = fi_final results['dintint'] = dimb results['intwindow'] = window * 0.6 #-- Saving parameters for later evaluation. Full fit is accessible by # making the functions separately and setting pars, then using fit.Model results['fitprof'] = (do_gauss and 'gauss' or 'soft_parabola',\ list(finalfit.get_parameters()[0])) if not include_gauss is None: results['fitabs'] = ('gauss', list(gaussian.get_parameters()[0])) else: results['fitabs'] = None return results
def fitLP( filename=None, lprof=None, theory=0, show=0, cfg="", convert_ms_kms=0, vary_pars=["vexp"], i_vexp=15.0, i_gamma=1.0, do_gauss=0, ): """ Fit a line profile with a soft parabola, and a Gaussian component if required. The method automatically checks if a second component is needed (eg an extra absorption component). An estimate of the expansion velocity (width of the profile) and an improved guess of the vlsr are given. A guess for the gas terminal velocity is returned, as well as its error and the fitted profile (sp/gaussian, and if applicable extra gaussian and the full fit). @keyword filename: The filename to the data file of the line profile. If None a line profile object is expected. (default: None) @type filename: string @keyword lprof: A line profile object (LPDataReader or inheriting classes) If None, a filename is expected! If not None, the results are saved in this object as well as returned upon method call (default: None) @type lprof: LPDataReader() @keyword convert_ms_kms: Convert velocity grid from m/s to km/s. (default: 0) @type convert_ms_kms: bool @keyword theory: If theoretical profile, and filename is given, set vlsr to 0 and read two columns. lprof not relevant if True. (default: 0) @type theory: bool @keyword vary_pars: The soft parabola parameters varied (can only be vexp or gamma for now). The initial values for parameters listed here are not used. If 'gamma' is requested, a reasonable guess for i_vexp when calling the method will improve the fitting results. This is done for the first guess only! If a Gaussian absorption is present improving these first guesses won't make much of a difference. However, the first guess value for gamma is still used. Vexp is always varied if absorption is present. (default: ['vexp']) @type vary_pars: list[string] @keyword i_vexp: The initial guess for the expansion velocity. Not relevant if vexp is included in vary_pars. (default: 15.0) @type i_vexp: float @keyword i_gamma: The initial guess for the gamma parameter of soft parab. Not relevant if gamma is included in vary_pars. (default: 1.0) @type i_gamma: float @keyword do_gauss: Force a Gaussian fit regardless of soft parabola fit results. Still does the soft parabola fit first to allow for comparison of parameters. (default: 0) @type do_gauss: bool @keyword show: Show the results of the fit (default: 0) @type show: bool @return: dictionary including [vexp,evexp,gamma,egamma,fitprof,gaussian,\ fullfit,dintint,fgintint] @rtype: dict[float,float,float,float,funclib.Function(),\ funclib.Function(),funclib.Function()] """ print "*******************************************" if theory and not filename is None: d = DataIO.readCols(filename=filename) vel = d[0] flux = d[1] vlsr = 0.0 else: if filename is None: filename = lprof.filename print "** Fitting line profile in %s." % filename if lprof is None: lprof = readLineProfile(filename) vel = lprof.getVelocity() flux = lprof.getFlux() vel = vel[-np.isnan(flux)] flux = flux[-np.isnan(flux)] vlsr = lprof.getVlsr() if convert_ms_kms: vel = vel / 1000.0 # -- Initial values: [peak tmb,vlsr,vexp,gamma] # For the central peak value, get a first guess from the data # Attempt multiple vexp values and return the best fitting case. # The initial values are given, with an arbitrary value for the vexp key i_mid = argmin(np.abs(vel - vlsr)) peak = mean(flux[i_mid - 2 : i_mid + 3]) # -- Vary vexp or gamma if requested. If not requested i_vexp or i_gamma are # used. # Multiple values for gamma are tried and the best fitting model # is then chosen based on the relative error of the fitted gamma. if "gamma" in vary_pars: igammas = array([-0.5, -0.1, 0.1, 0.5, 1.0, 2.0, 4.0]) firstguess = varyInitialFit( vel, flux, [peak, vlsr, i_vexp, 0.0], index=3, values=igammas, vary_window=1, vary=[1, 1, 1, 1], function=funclib.soft_parabola, ) i_gamma = firstguess.get_parameters()[0][3] # -- varyInitialFit adapts the velocity window itself. No more # assumptions needed for the expansion velocity ivexps = array([50.0, 40.0, 30.0, 25.0, 20.0, 15.0, 10.0]) if "vexp" in vary_pars: firstguess = varyInitialFit( vel, flux, [peak, vlsr, 0.0, i_gamma], index=2, values=ivexps, vary_window=1, vary=[1, 1, 1, 1], function=funclib.soft_parabola, ) vexp = abs(firstguess.get_parameters()[0][2]) window = 2.0 print "First guess fit, using a soft parabola:" print firstguess.param2str(accuracy=5) # -- If vexp > 100, replace with 50. This value is unrealistic, and might be # improved with an extra Gaussian. If not, it will be replaced with a # full Gaussian fit anyway if vexp > 100: vexp = 50.0 # -- Check whether irregularities are present in the profile. # Initial parameters for a gaussian are returned if true. # For this, a broad selection is made of the profile, to allow for a # decent noise determination outside the line profile keep = np.abs(vel - vlsr) <= (2 * window * vexp) velsel, fluxsel = vel[keep], flux[keep] include_gauss = checkLPShape(velsel, fluxsel, vlsr, vexp, window, show=show) # -- Do the fit of the line again, including an extra gaussian if # irregularities are present. if not include_gauss is None: # -- fit soft para model + gaussian # 1. Set up new soft parabola for several guesses of vexp ivexps = list(ivexps) initial = [peak, vlsr, 0.0, i_gamma] all_init = [[p] * len(ivexps) for i, p in enumerate(initial) if i != 2] all_init.insert(2, ivexps) functions = [funclib.soft_parabola() for i in ivexps] [ff.setup_parameters(values=initi) for ff, initi in zip(functions, zip(*all_init))] # 2. setup gaussian gaussians = [funclib.gauss() for ff in functions] # initial guesses assuming an interstellar absorption line from the # checkLPShape method [gg.setup_parameters(values=include_gauss, vary=[True, True, True, False]) for gg in gaussians] # 3. combine soft para + gaussian, and minimize fit mymodels = [fit.Model(functions=[ff, gg]) for ff, gg in zip(functions, gaussians)] [ fit.minimize( vel[np.abs(vel - vlsr) <= (init[2] * 1.5)], flux[np.abs(vel - vlsr) <= (init[2] * 1.5)], mymodel ) for mymodel, init in zip(mymodels, zip(*all_init)) ] # 4. Select the best fitting result based on the error on vexp mymodels = [fg for fg in mymodels if fg.get_parameters()[1][2] != 0.0] functions = [ff for fg, ff in zip(mymodels, functions) if fg.get_parameters()[1][2] != 0.0] gaussians = [gg for fg, gg in zip(mymodels, gaussians) if fg.get_parameters()[1][2] != 0.0] fitvalues = array([fg.get_parameters()[0][2] for fg in mymodels]) fiterrors = array([fg.get_parameters()[1][2] for fg in mymodels]) mymodel = mymodels[argmin(abs(fiterrors / fitvalues))] finalfit = functions[argmin(abs(fiterrors / fitvalues))] gaussian = gaussians[argmin(abs(fiterrors / fitvalues))] print "Improved fit, including extra Gaussian:" print mymodel.param2str(accuracy=5) else: # -- if gamma is requested to be varied, allow another iteration on # gamma with the best vexp guess we already have. if "gamma" in vary_pars: finalfit = varyInitialFit( vel, flux, [peak, vlsr, vexp, 0.0], index=3, values=igammas, vary_window=1, function=funclib.soft_parabola, vary=[True, True, True, True], ) print "Final fit with soft parabola, second gamma iteration:" print finalfit.param2str(accuracy=5) # -- firstguess is best we can do at the moment else: finalfit = firstguess # -- If the relative error on vexp is larger than 30%, usually something # funky is going on in the emission line. Try a Gaussian instead. vexp = abs(finalfit.get_parameters()[0][2]) evexp = abs(finalfit.get_parameters()[1][2]) gamma = finalfit.get_parameters()[0][3] egamma = finalfit.get_parameters()[1][3] # -- Gamma has to be positive. If it isnt, dont bother with Gaussian # (double peaked line profile will not be fitted well with a Gaussian!) if (evexp / vexp > 0.40 and gamma > 0) or (evexp / vexp > 0.20 and vexp > 30.0) or do_gauss: # -- Go back to default window to try a Gaussian fit # keep = np.abs(vel-vlsr)<=(80) # velselg,fluxselg = vel[keep],flux[keep] do_gauss = 1 include_gauss = None # -- FWHM is twice vexp! sigmas = 2 * ivexps / (2.0 * sqrt(2.0 * log(2.0))) finalfit = varyInitialFit( vel, flux, [peak, vlsr, 0.0, 0.0], index=2, values=sigmas, function=funclib.gauss, vary_window=1, vary=[True, True, True, False], ) vexp = abs(finalfit.get_parameters()[0][2]) * (2.0 * sqrt(2.0 * log(2.0))) / 2.0 evexp = abs(finalfit.get_parameters()[1][2]) * (2.0 * sqrt(2.0 * log(2.0))) / 2.0 gamma, egamma = None, None window = 3.0 print "Improved fit, using a gaussian instead of soft parabola:" print finalfit.param2str(accuracy=5) # -- Extract some shared parameters between the gauss and sp. peak = finalfit.get_parameters()[0][0] epeak = finalfit.get_parameters()[1][0] fvlsr = finalfit.get_parameters()[0][1] fevlsr = finalfit.get_parameters()[1][1] # -- Compute numerical integrations. # After fitting, window for integration should be 0.6*window. vexp is # not expected to be too small anymore as in checkLPShape keep = np.abs(vel - vlsr) <= (0.6 * window * vexp) velsel = vel[keep] flux_first = firstguess.evaluate(velsel) flux_final = finalfit.evaluate(velsel) dimb = trapz(y=flux[keep], x=velsel) fi_final = trapz(y=flux_final, x=velsel) print ("I_mb (emission line data): %f" % dimb) print ("I_mb (SP -- initial guess): %f" % trapz(y=flux_first, x=velsel)) print ("I_mb (SP -- final guess): %f" % fi_final) if not include_gauss is None: fitted_flux = mymodel.evaluate(velsel) print ("I_mb (SP + Gauss fit): %f" % trapz(y=fitted_flux, x=velsel)) print ("Final v_exp guess: %.4f +/- %.4f km/s" % (vexp, evexp)) if not gamma is None: print ("Final gamma guess: %.4f +/- %.4f" % (gamma, egamma)) print ("Final vlsr guess: %.4f +/- %.4f" % (fvlsr, fevlsr)) print ("Final peak Tmb guess at v_lsr: %.4f +/- %.4f K" % (peak, epeak)) fwhm = getLPDataFWHM(lprof) print ("The FWHM is %.2f km/s." % (fwhm)) # -- plot if show or cfg: plt.clf() # -- improve velocity window for plotting keep = np.abs(vel - vlsr) <= (1.5 * window * vexp) velsel, fluxsel = vel[keep], flux[keep] vel_highres = np.linspace(velsel[0], velsel[-1], 10000) flux_final_highres = finalfit.evaluate(vel_highres) flux_first_highres = firstguess.evaluate(vel_highres) if not include_gauss is None: flux_full_highres = mymodel.evaluate(vel_highres) if show: plt.step(velsel, fluxsel, "-r", where="mid", lw=3, label="Observed profile") plt.plot(vel_highres, flux_first_highres, "b-", lw=3, label="First guess") plt.plot(vel_highres, flux_final_highres, "g--", lw=3, label="Improved guess") if not include_gauss is None: plt.plot(vel_highres, flux_full_highres, "g-", lw=2, label="Full fit (including Gaussian)") leg = plt.legend(loc="best", fancybox=True) leg.get_frame().set_alpha(0.5) plt.show() if cfg: pf = "%s_fitted_%s" % (do_gauss and "gaussFit" or "SPFit", os.path.split(filename)[1]) keytags = ["Observed profile", "Improved guess"] line_types = ["-r", "-b"] x = [velsel, vel_highres] y = [fluxsel, flux_final_highres] if not include_gauss is None: line_types.append("g--") x.append(vel_highres) y.append(flux_full_highres) keytags.append("Full fit (including Gaussian)") pf = Plotting2.plotCols( x=x, y=y, filename=pf, cfg=cfg, linewidth=5, yaxis="$T_\mathrm{mb}\ (\mathrm{K})$", xaxis="$v (\mathrm{km}/\mathrm{s})$", keytags=keytags, line_types=line_types, histoplot=[0], ) print "Your figure can be found at %s ." % pf # -- Collecting all relevant results and returning. results = dict() # -- If a Gaussian was used for the main profile fit results["do_gauss"] = do_gauss # -- Fitted parameters and errors results["vlsr"] = fvlsr results["evlsr"] = fevlsr results["vexp"] = vexp results["evexp"] = evexp results["fwhm"] = fwhm results["peak"] = peak results["epeak"] = epeak # -- Gamma is None if no soft parabola was fitted results["gamma"] = gamma results["egamma"] = egamma # -- Integrated line strengths: main line fit, data themselves, fit window results["fgintint"] = fi_final results["dintint"] = dimb results["intwindow"] = window * 0.6 # -- Saving parameters for later evaluation. Full fit is accessible by # making the functions separately and setting pars, then using fit.Model results["fitprof"] = (do_gauss and "gauss" or "soft_parabola", list(finalfit.get_parameters()[0])) if not include_gauss is None: results["fitabs"] = ("gauss", list(gaussian.get_parameters()[0])) else: results["fitabs"] = None return results