Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
 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
Esempio n. 4
0
 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
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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)
Esempio n. 9
0
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)
Esempio n. 10
0
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
Esempio n. 11
0
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