def joebvpfit(wave,flux,sig,linepars,flags): xtol=1e-11 gtol=1e-11 # Only feed to the fitter the parameters that go into the model partofit=linepars[:5] parinfo=prepparinfo(partofit,flags) # Save the velocity windows to add back to the parameter array vlim1=linepars[5] ; vlim2=linepars[6] # Get atomic data lam,fosc,gam=atomicdata.setatomicdata(linepars[0]) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam # Set fit regions cfg.fitidx = fitpix(wave, linepars) # Prep parameters for fitter partofit=unfoldpars(partofit) modelvars={'x':wave,'y':flux,'err':sig} # Do the fit and translate the parameters back into the received format m=nmpfit.mpfit(voigterrfunc,partofit,functkw=modelvars,parinfo=parinfo,nprint=1,quiet=0,fastnorm=1,ftol=1e-10,xtol=xtol,gtol=gtol) if m.status <= 0: print 'Fitting error:',m.errmsg fitpars=foldpars(m.params) fiterrors = foldpars(m.perror) # Add velocity windows back to parameter array fitpars.append(vlim1) ; fitpars.append(vlim2) print '\nFit results: \n' for i in range(len(fitpars[0])): print jbg.tabdelimrow([round(fitpars[0][i],2),jbg.decimalplaces(fitpars[3][i],5),jbg.roundto(fitpars[1][i],5),jbg.roundto(fitpars[2][i],5),jbg.roundto(fitpars[4][i],5)])[:-2] print jbg.tabdelimrow([' ',' ',' ',round(fiterrors[1][i],3),round(fiterrors[2][i],3),round(fiterrors[4][i],3)]) return fitpars,fiterrors
def joebvpfit(wave,flux,sig,linepars,flags): xtol=1e-11 gtol=1e-11 # Only feed to the fitter the parameters that go into the model partofit=linepars[:5] parinfo=prepparinfo(partofit,flags) # Save the velocity windows to add back to the parameter array vlim1=linepars[5] ; vlim2=linepars[6] # Get atomic data lam,fosc,gam=atomicdata.setatomicdata(linepars[0]) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam # Set fit regions cfg.fitidx = fitpix(wave, linepars) # Prep parameters for fitter partofit=unfoldpars(partofit) modelvars={'x':wave,'y':flux,'err':sig} # Do the fit and translate the parameters back into the received format m=nmpfit.mpfit(voigterrfunc,partofit,functkw=modelvars,parinfo=parinfo,nprint=1,quiet=0,fastnorm=1,ftol=1e-10,xtol=xtol,gtol=gtol) if m.status <= 0: print('Fitting error:',m.errmsg) fitpars=foldpars(m.params) fiterrors = foldpars(m.perror) # Add velocity windows back to parameter array fitpars.append(vlim1) ; fitpars.append(vlim2) print('\nFit results: \n') for i in range(len(fitpars[0])): print(jbg.tabdelimrow([round(fitpars[0][i],2),jbg.decimalplaces(fitpars[3][i],5),jbg.roundto(fitpars[1][i],5),jbg.roundto(fitpars[2][i],5),jbg.roundto(fitpars[4][i],5)])[:-2]) print(jbg.tabdelimrow([' ',' ',' ',round(fiterrors[1][i],3),round(fiterrors[2][i],3),round(fiterrors[4][i],3)])) return fitpars,fiterrors
def validateWavelength(self): ### Handle approximate wavelength entries by looking up precise values on focus exit if self.lamBox.text()!='': restlam=self.lamBox.text() restlam=float(restlam) try: lam,fosc,gam=atomicdata.setatomicdata([restlam],precise=False) self.lamBox.setText(str(lam[0])) self.ionLabel.setText(atomicdata.lam2ion(lam[0])) except: pass
def modelFromAbsComp(spectrum, abscomp): ''' Generate Voigt profile using Parameters ---------- spectrum: linetools XSpectrum1D Spectrum to provide wavelength input for Voigt profile evaluation abscomp: linetools AbsComponent Component objects whose AbsLines will be used to generate profile Returns ------- spectrum.wavelength: 1D array Wavelength vector corresponding to profile profile: 1D array Model consisting of Voigt profiles across spectrum ''' restwaves = [] zs = [] cols = [] bs = [] vels = [] vlim1s = [] vlim2s = [] for absline in abscomp._abslines: ### Grab parameters from AbsLine object restwaves.append(absline.wrest.value) zs.append(absline.z) cols.append(absline.attrib['logN']) bs.append(absline.attrib['b']) vels.append(np.mean(absline.limits.vlim).value) vlim1s.append(absline.limits.vlim[0].value) vlim2s.append(absline.limits.vlim[1].value) pars = [restwaves, cols, bs, zs, vels, vlim1s, vlim2s] ### Set atomic data so that the right profiles are evaluated! lam, fosc, gam = atomicdata.setatomicdata(restwaves) cfg.lams = lam cfg.fosc = fosc cfg.gam = gam profile = voigtfunc(spectrum.wavelength.value, pars) ### For debugging old feature #nonone = np.where(np.abs(profile - 1.) > 0.01)[0] #print spectrum.wavelength[nonone] return spectrum.wavelength.value, profile
def voigt_and_jac(waves, line, coldens, bval, z, vels): col_to_tau_const = np.sqrt(np.pi) * cfg.echarge**2 / cfg.m_e / cfg.c**2 n_waves = len(waves) n_lines = len(coldens) tautot = np.zeros(n_waves) dg_dcol = np.zeros([n_lines, n_waves]) dg_db = np.zeros([n_lines, n_waves]) dg_dv = np.zeros([n_lines, n_waves]) if len(cfg.lams) == 0: lam, fosc, gam = atomicdata.setatomicdata(line) cfg.lams = lam cfg.fosc = fosc cfg.gam = gam for i, (cd, b, vel) in enumerate(zip(coldens, bval, vels)): #thatfactor=(1.+z[i])*(1.+vels[i]/c) thatfactor = (1. + z[i]) lam0 = cfg.lams[i] gam = cfg.gam[i] fosc = cfg.fosc[i] lam = waves / thatfactor dlam = b * lam0 / c #Doppler param in wavelength x = (lam - lam0 - lam0 * vel / c) / dlam a = gam / (4. * np.pi * (c * 1e13 / lam0**2 * dlam)) dx_dv = -lam0 / (c * dlam) dx_db = -x / b da_db = -a / b K, dK_dx, dK_da = Hfunc_w_jac(x, a) multiplier = col_to_tau_const * (lam0 * 1e-8)**2 * 1e8 * fosc tauval = multiplier * (10**cd) * K / dlam dg_dcol[i, :] = tauval * np.log(10) dg_dv[i, :] = (10**cd * multiplier * dx_dv / dlam) * dK_dx dg_db[i, :] += -tauval / b dg_db[i, :] += (10**cd * multiplier / (dlam) * dx_db * dK_dx + da_db * dK_da) tautot += tauval g = np.exp(-tautot) dg_dcol *= -g dg_dv *= -g dg_db *= -g return g, dg_dcol, dg_dv, dg_db
def modelFromAbsComp(spectrum,abscomp): ''' Generate Voigt profile using Parameters ---------- spectrum: linetools XSpectrum1D Spectrum to provide wavelength input for Voigt profile evaluation abscomp: linetools AbsComponent Component objects whose AbsLines will be used to generate profile Returns ------- spectrum.wavelength: 1D array Wavelength vector corresponding to profile profile: 1D array Model consisting of Voigt profiles across spectrum ''' restwaves = [] zs = [] cols = [] bs = [] vels = [] vlim1s = [] vlim2s = [] for absline in abscomp._abslines: ### Grab parameters from AbsLine object restwaves.append(absline.wrest.value) zs.append(absline.z) cols.append(absline.attrib['logN']) bs.append(absline.attrib['b']) vels.append(np.mean(absline.limits.vlim).value) vlim1s.append(absline.limits.vlim[0].value) vlim2s.append(absline.limits.vlim[1].value) pars = [restwaves,cols,bs,zs,vels,vlim1s,vlim2s] ### Set atomic data so that the right profiles are evaluated! lam,fosc,gam=atomicdata.setatomicdata(restwaves) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam profile = voigtfunc(spectrum.wavelength.value, pars) #nonone = np.where(np.abs(profile - 1.) > 0.01)[0] #print spectrum.wavelength[nonone] return spectrum.wavelength.value,profile
def voigt(waves,line,coldens,bval,z,vels): tautot=np.zeros(len(waves)) if len(cfg.lams)==0: lam,fosc,gam=atomicdata.setatomicdata(line) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam for i in range(len(coldens)): #thatfactor=(1.+z[i])*(1.+vels[i]/c) thatfactor=(1.+z[i]) lam0=cfg.lams[i] gam=cfg.gam[i] fosc=cfg.fosc[i] lam = waves/thatfactor dlam=bval[i]*lam0/c #Doppler param in wavelength x=(lam-lam0-lam0*vels[i]/c)/dlam a=gam/(4.*np.pi*(c*1e13/lam0**2*dlam)) vp=Hfunc(x,a) tauval= np.sqrt(np.pi) * cfg.echarge ** 2 / cfg.m_e / cfg.c ** 2 * (lam0 * 1e-8) ** 2 / dlam * 1e8 * (10 ** coldens[i]) * fosc * vp tautot+=tauval return np.exp(-tautot)
def initlinepars(zs, restwaves, initvals=[], initinfo=[]): ''' Parameters ---------- zs : numpy vector of floats Redshifts of lines (this parameter will be fixed during fitting) restwaves : numpy vector of floats Rest frame wavelengths of lines to be fitted initvals : list of numpy vectors, optional Contains the following (in order): [restwaves,linecol,lineb,zs,linevel,linevlim1,linevlim2] Will default to values set in cfg.py if not set initinfo : list of numpy vectors, optional Contains the flags for fitting (in order): [colflag,bflag,velflag] Parameters with flags = 0 and 1 will freely value and fixed, respectively If 2 or more lines have the same flag value for the same parameter, the parameters will be tied to one another. Returns ------- initpars : list of lists Parameters for fit ready for fitter! parinfo : array of arrays Flags to be used in fit ''' ### Set atomic data for each line lam, fosc, gam = atomicdata.setatomicdata(restwaves) cfg.lams = lam cfg.fosc = fosc cfg.gam = gam initpars = [[], [], [], [], [], [], []] defaultcol = cfg.defaultcol defaultb = cfg.defaultb if initvals == []: for i in range(len(restwaves)): initpars[0].extend([restwaves[i]]) initpars[1].extend([defaultcol]) initpars[2].extend([defaultb]) initpars[3].extend([zs[i]]) initpars[4].extend([0.]) initpars[5].extend([-cfg.defaultvlim]) initpars[6].extend([cfg.defaultvlim]) else: if len(initvals) == 5: for i in range(len(restwaves)): initpars[0].extend([initvals[0][i]]) initpars[1].extend([initvals[1][i]]) initpars[2].extend([initvals[2][i]]) initpars[3].extend([initvals[3][i]]) initpars[4].extend([initvals[4][i]]) initpars[5].extend([-cfg.defaultvlim]) initpars[6].extend([cfg.defaultvlim]) else: initpars = [[], [], [], [], [], [], []] for i in range(len(restwaves)): initpars[0].extend([initvals[0][i]]) initpars[1].extend([initvals[1][i]]) initpars[2].extend([initvals[2][i]]) initpars[3].extend([initvals[3][i]]) initpars[4].extend([initvals[4][i]]) initpars[5].extend([initvals[5][i]]) initpars[6].extend([initvals[6][i]]) #cfg.lowblim alteration was removed from here parinfo = np.zeros([5, len(restwaves)], dtype=int) parinfo[0] = parinfo[0] + 1 parinfo[3] = parinfo[3] + 1 if ((initinfo == []) & (initvals == [])): ### Look for multiplet membership of each line seriesassoc = np.zeros(len(restwaves)) - 99 for i in range(len(restwaves)): for j in range(len(cfg.multiplets)): currmult = np.array(cfg.multiplets[j]) if (abs(restwaves[i] - currmult[jbg.closest(currmult, restwaves[i])]) < 0.01): seriesassoc[i] = j uqions = np.unique(seriesassoc).tolist() if -99 in uqions: uqions.remove(-99) flagctr = 2 for uqion in uqions: ionmatch = np.where(seriesassoc == uqion)[0] uqzs = np.unique(zs[ionmatch]) for uz in uqzs: matchcrit = (zs == uz) & (seriesassoc == uqion) rellines = np.where(matchcrit)[0] uqlams = np.unique(restwaves[rellines]) if len(uqlams) > 1: complist = [] numcomps = [] for ul in uqlams: matchcrit2 = matchcrit & (restwaves == ul) matches = np.where(matchcrit2)[0] complist.append(matches) numcomps.append(len(matches)) numcomps = np.array(numcomps) complist = np.array(complist) compidxsort = sorted(range(len(numcomps)), key=lambda x: numcomps[x], reverse=True) numcompsort = numcomps[compidxsort] complistsort = complist[compidxsort] maxcomps = numcompsort[0] for compidx in range(maxcomps): for li in range(len(complistsort)): if compidx < numcompsort[li]: parinfo[1][complistsort[li][compidx]] = flagctr parinfo[2][complistsort[li][compidx]] = flagctr parinfo[4][complistsort[li][compidx]] = flagctr else: continue flagctr += 1 elif initinfo != []: parinfo[1] = initinfo[0] parinfo[2] = initinfo[1] parinfo[4] = initinfo[2] elif ((initinfo == []) & (initvals != [])): ### Look for multiplet membership of each line seriesassoc = np.zeros(len(restwaves)) - 99 for i in range(len(restwaves)): for j in range(len(cfg.multiplets)): currmult = np.array(cfg.multiplets[j]) if (abs(restwaves[i] - currmult[jbg.closest(currmult, restwaves[i])]) < 0.01): seriesassoc[i] = j ### Fix measurements that are imported for i in range(len(restwaves)): if ((initpars[1][i] != defaultcol) & (initpars[2][i] != defaultb)): parinfo[1][i] = 1 parinfo[2][i] = 1 parinfo[4][i] = 1 uqions = np.unique(seriesassoc).tolist() if -99 in uqions: uqions.remove(-99) flagctr = 2 for uqion in uqions: ionmatch = np.where(seriesassoc == uqion)[0] uqzs = np.unique(zs[ionmatch]) for uz in uqzs: matchcrit = (zs == uz) & (seriesassoc == uqion) rellines = np.where(matchcrit)[0] uqlams = np.unique(restwaves[rellines]) if len(uqlams) > 1: complist = [] numcomps = [] for ul in uqlams: matchcrit2 = matchcrit & (restwaves == ul) matches = np.where(matchcrit2)[0] complist.append(matches.tolist()) numcomps.append(len(matches)) numcomps = np.array(numcomps) complist = np.array(complist) compidxsort = sorted(range(len(numcomps)), key=lambda x: numcomps[x]) complistsort = complist[compidxsort] complistsort = complistsort.tolist() for i in range(len(complistsort) - 1): for idx in complistsort[i]: parinfo[1][idx] = flagctr parinfo[2][idx] = flagctr parinfo[4][idx] = flagctr for j in range(i + 1, len(complistsort)): idxidx = jbg.closest( initvals[4][complistsort[j]], initvals[4][idx]) clidx = complistsort[j][idxidx] parinfo[1][clidx] = flagctr parinfo[2][clidx] = flagctr parinfo[4][clidx] = flagctr complistsort[j].remove(clidx) flagctr += 1 return initpars, parinfo
def initlinepars(zs,restwaves,initvals=[],initinfo=[]): ''' Parameters ---------- zs : numpy vector of floats Redshifts of lines (this parameter will be fixed during fitting) restwaves : numpy vector of floats Rest frame wavelengths of lines to be fitted initvals : list of numpy vectors, optional Contains the following (in order): [restwaves,linecol,lineb,zs,linevel,linevlim1,linevlim2] Will default to values set in cfg.py if not set initinfo : list of numpy vectors, optional Contains the flags for fitting (in order): [colflag,bflag,velflag] Parameters with flags = 0 and 1 will freely value and fixed, respectively If 2 or more lines have the same flag value for the same parameter, the parameters will be tied to one another. Returns ------- initpars : list of lists Parameters for fit ready for fitter! parinfo : array of arrays Flags to be used in fit ''' ### Set atomic data for each line lam,fosc,gam=atomicdata.setatomicdata(restwaves) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam initpars=[[],[],[],[],[],[],[]] defaultcol=cfg.defaultcol defaultb=cfg.defaultb if initvals==[]: for i in range(len(restwaves)): initpars[0].extend([restwaves[i]]) initpars[1].extend([defaultcol]) initpars[2].extend([defaultb]) initpars[3].extend([zs[i]]) initpars[4].extend([0.]) initpars[5].extend([-cfg.defaultvlim]) initpars[6].extend([cfg.defaultvlim]) else: if len(initvals)==5: for i in range(len(restwaves)): initpars[0].extend([initvals[0][i]]) initpars[1].extend([initvals[1][i]]) initpars[2].extend([initvals[2][i]]) initpars[3].extend([initvals[3][i]]) initpars[4].extend([initvals[4][i]]) initpars[5].extend([-cfg.defaultvlim]) initpars[6].extend([cfg.defaultvlim]) else: initpars=[[],[],[],[],[],[],[]] for i in range(len(restwaves)): initpars[0].extend([initvals[0][i]]) initpars[1].extend([initvals[1][i]]) initpars[2].extend([initvals[2][i]]) initpars[3].extend([initvals[3][i]]) initpars[4].extend([initvals[4][i]]) initpars[5].extend([initvals[5][i]]) initpars[6].extend([initvals[6][i]]) ### If hard limits on Doppler b-value are smaller or greater than cfg.lowblim or cfg.upperblim, ### modify those limits maxb=np.max(initpars[2][:]) minb=np.min(initpars[2][:]) if maxb>cfg.upperblim: cfg.upperblim= maxb + 10. if minb<cfg.lowblim: cfg.lowblim= minb - 2. parinfo=np.zeros([5,len(restwaves)],dtype=int) parinfo[0]=parinfo[0]+1 parinfo[3]=parinfo[3]+1 if ((initinfo==[])&(initvals==[])): ### Look for multiplet membership of each line seriesassoc = np.zeros(len(restwaves)) - 99 for i in range(len(restwaves)): for j in range(len(cfg.multiplets)): currmult = np.array(cfg.multiplets[j]) if (abs(restwaves[i] - currmult[jbg.closest(currmult, restwaves[i])]) < 0.01): seriesassoc[i] = j uqions=np.unique(seriesassoc).tolist() if -99 in uqions: uqions.remove(-99) flagctr=2 for uqion in uqions: ionmatch=np.where(seriesassoc == uqion)[0] uqzs=np.unique(zs[ionmatch]) for uz in uqzs: matchcrit=(zs==uz)&(seriesassoc==uqion) rellines=np.where(matchcrit)[0] uqlams=np.unique(restwaves[rellines]) if len(uqlams)>1: complist=[] numcomps=[] for ul in uqlams: matchcrit2=matchcrit&(restwaves==ul) matches=np.where(matchcrit2)[0] complist.append(matches) numcomps.append(len(matches)) numcomps=np.array(numcomps) complist=np.array(complist) compidxsort=sorted(range(len(numcomps)),key = lambda x: numcomps[x],reverse=True) numcompsort=numcomps[compidxsort] complistsort=complist[compidxsort] maxcomps=numcompsort[0] for compidx in range(maxcomps): for li in range(len(complistsort)): if compidx<numcompsort[li]: parinfo[1][complistsort[li][compidx]]=flagctr parinfo[2][complistsort[li][compidx]]=flagctr parinfo[4][complistsort[li][compidx]]=flagctr else: continue flagctr+=1 elif initinfo!=[]: parinfo[1]=initinfo[0] parinfo[2]=initinfo[1] parinfo[4]=initinfo[2] elif ((initinfo==[])&(initvals!=[])): ### Look for multiplet membership of each line seriesassoc = np.zeros(len(restwaves)) - 99 for i in range(len(restwaves)): for j in range(len(cfg.multiplets)): currmult = np.array(cfg.multiplets[j]) if (abs(restwaves[i] - currmult[jbg.closest(currmult, restwaves[i])]) < 0.01): seriesassoc[i] = j ### Fix measurements that are imported for i in range(len(restwaves)): if ((initpars[1][i]!=defaultcol)&(initpars[2][i]!=defaultb)): parinfo[1][i]=1 ; parinfo[2][i]=1 ; parinfo[4][i]=1 uqions=np.unique(seriesassoc).tolist() if -99 in uqions: uqions.remove(-99) flagctr=2 for uqion in uqions: ionmatch=np.where(seriesassoc == uqion)[0] uqzs=np.unique(zs[ionmatch]) for uz in uqzs: matchcrit=(zs==uz)&(seriesassoc==uqion) rellines=np.where(matchcrit)[0] uqlams=np.unique(restwaves[rellines]) if len(uqlams)>1: complist=[] numcomps=[] for ul in uqlams: matchcrit2=matchcrit&(restwaves==ul) matches=np.where(matchcrit2)[0] complist.append(matches.tolist()) numcomps.append(len(matches)) numcomps=np.array(numcomps) complist=np.array(complist) compidxsort=sorted(range(len(numcomps)),key = lambda x: numcomps[x]) complistsort=complist[compidxsort] complistsort=complistsort.tolist() for i in range(len(complistsort)-1): for idx in complistsort[i]: parinfo[1][idx]=flagctr parinfo[2][idx]=flagctr parinfo[4][idx]=flagctr for j in range(i+1,len(complistsort)): idxidx=jbg.closest(initvals[4][complistsort[j]],initvals[4][idx]) clidx=complistsort[j][idxidx] parinfo[1][clidx]=flagctr parinfo[2][clidx]=flagctr parinfo[4][clidx]=flagctr complistsort[j].remove(clidx) flagctr+=1 return initpars,parinfo