def test_name_to_ion(): Zion = ions.name_to_ion('Si II') assert Zion == (14, 2) # bad input with pytest.raises(ValueError): aux = ions.name_to_ion(4) # not a string # Deuterium aux = ions.name_to_ion('DI')
def test_name_to_ion(): Zion = ions.name_to_ion('Si II') assert Zion == (14,2) # bad input with pytest.raises(ValueError): aux = ions.name_to_ion(4) # not a string # Deuterium aux = ions.name_to_ion('DI')
def component_tbl(self, Zion): """ Generate a Table of line measurements for an input ion broken down by component. Parameters ---------- Zion : tuple or str E.g., (8,6) or 'OVI' Returns ------- tbl : astropy.Table """ from linetools.abund.ions import name_to_ion from linetools.isgm import utils as ltiu from astropy.table import Table, vstack, Column if isinstance(Zion, basestring): Zion = name_to_ion(Zion) newcomptab = Table() rhos = [] names = [] for i, isys in enumerate(self.cgm_abs): isys.igm_sys.update_component_vel() comptab = ltiu.table_from_complist(isys.igm_sys._components) comptab = comptab[(comptab['Z'] == Zion[0]) & (comptab['ion'] == Zion[1])] if len(comptab) == 0: continue newcomptab = vstack([newcomptab, comptab]) rhos.extend([isys.rho.value] * len(comptab)) names.extend([isys.name] * len(comptab)) newcomptab.add_column(Column(names, name='cgm_name')) newcomptab.add_column(Column(rhos * u.kpc, name='rho_impact')) return newcomptab
def ion_tbl(self, Zion, fill_ion=True): """ Generate a Table of Ionic column densities for an input ion Parameters ---------- Zion : tuple or str fill_ion : bool, optional Fill each ionN table in the survey (a bit slow) Returns ------- tbl : astropy.Table """ from linetools.abund.ions import name_to_ion if isinstance(Zion, basestring): Zion = name_to_ion(Zion) # Generate dummy IGMSurvey dumb = GenericIGMSurvey() names = [] for cgmabs in self.cgm_abs: if fill_ion: cgmabs.igm_sys.fill_ionN() if cgmabs.igm_sys._ionN is not None: dumb._abs_sys.append(cgmabs.igm_sys) # Names names.append(cgmabs.name) # Run ions tbl = dumb.ions(Zion) # Add CGM name tbl.add_column(Column(names, name='cgm_name')) # Return return tbl
def get_components(obj, ion, zrange=None): """Return list of components from AbsSystem or IGMSightline for given ion Parameters ---------- obj : AbsSystem or IGMSightline Object that has attribute 'Zion' ion : tuple or str, optional Zion tuple or ion name; e.g., (8,6) for OVI zrange : tuple, optional Range in redshift over which to look for components Returns ------- complist : list List of components with given ion """ if isinstance(ion, str): from linetools.abund.ions import name_to_ion Zion = name_to_ion(ion) else: Zion = ion if zrange is None: complist = [comp for comp in obj._components if comp.Zion == Zion] else: complist = [ comp for comp in obj._components if ((comp.Zion == Zion) & (comp.zcomp > zrange[0]) & (comp.zcomp < zrange)) ] return complist
def main(args=None): pargs = parser(options=args) if pargs.inp is None and pargs.all is False: print("No option selected. Use -h for Help") return # Setup from astropy import units as u from astropy.table import Column from linetools.lists.linelist import LineList from linetools.abund import ions as ltai import numpy as np if pargs.llist in ['CO']: cols = ['mol', 'label', 'wrest', 'f'] elif pargs.llist in ['H2']: cols = ['mol', 'name', 'wrest', 'f'] else: cols = ['name', 'wrest', 'f', 'A', 'Ref'] # LineList llist = LineList(pargs.llist) # Redshift? if float(pargs.redshift) != 0.: z = llist._data['wrest']*(1+float(pargs.redshift)) llist._data.add_column(Column(z, name='z')) cols += ['z'] # All? if pargs.all: try: llist._data[cols].pprint(99999) except ValueError: pdb.set_trace() return # Grab line(s) if ustr(pargs.inp[0]).isdecimal(): # Input rest wavelength wrest = float(pargs.inp)*u.AA mtch = np.abs(wrest-llist.wrest) < pargs.toler*u.AA llist._data[cols][mtch].pprint(99999) else: # Either ion or transition istrans = False for jj,char in enumerate(pargs.inp): if char.isdigit(): istrans = True i0 = jj break if istrans: trans = pargs.inp[0:i0]+' '+pargs.inp[i0:] tdict = llist[trans] for key,value in tdict.items(): if key in cols: print('{:s}: {}'.format(key,value)) else: # Ion Zion = ltai.name_to_ion(pargs.inp) mtion = (llist.Z == Zion[0]) & (llist.ion == Zion[1]) llist._data[cols][mtion].pprint(99999)
def dict_to_ions(idict): """ Manipulate dict into an ion astropy Table Will likely be deprecated Parameters ---------- idict : dict Returns ------- table : astropy.Table """ # Could probably use add_row or dict instantiation table = None for ion in idict.keys(): Zion = ltai.name_to_ion(ion) if table is None: tkeys = idict[ion].keys() lst = [[idict[ion][tkey]] for tkey in tkeys] table = Table(lst, names=tkeys) # Extra columns if 'Z' not in tkeys: table.add_column(Column([Zion[0]], name='Z')) table.add_column(Column([Zion[1]], name='ion')) else: tdict = idict[ion] tkeys = idict[ion].keys() if 'Z' not in tkeys: tdict['Z'] = Zion[0] tdict['ion'] = Zion[1] # Add table.add_row(tdict) # Finish try: # Historical keys table.rename_column('clm', 'logN') except: pass else: table.rename_column('sig_clm', 'sig_logN') table.rename_column('flg_clm', 'flag_N') # Return return table
def ion_tbl(self, Zion, fill_ion=True, vrange=None, **kwargs): """ Generate a Table of Ionic column densities for an input ion Parameters ---------- Zion : tuple or str fill_ion : bool, optional Fill each ionN table in the survey (a bit slow) vrange : Quantity, optional Velocity range of components to sum column densities Returns ------- tbl : astropy.Table Returns None if there are no matches to input Zion """ from linetools.abund.ions import name_to_ion if isinstance(Zion, basestring): Zion = name_to_ion(Zion) # Generate dummy IGMSurvey dumb = GenericIGMSurvey() names = [] rhos = [] for cgmabs in self.cgm_abs: if fill_ion: cgmabs.igm_sys.fill_ionN(vrange=vrange, summed_ion=True) if cgmabs.igm_sys._ionN is not None: dumb._abs_sys.append(cgmabs.igm_sys) # Names names.append(cgmabs.name) # Impact parameters rhos.append(cgmabs.rho.to(u.kpc).value) # Run ions tbl = dumb.ions(Zion, skip_null=False, **kwargs) if tbl is None: return None tbl.add_column(Column(names, name='cgm_name')) # Add impact parameter tbl.add_column(Column(rhos * u.kpc, name='rho_impact')) # Return return tbl
def tripp2005(): '''Tripp, T. et al. 2005, ApJ, 2005, 619, 714 PG 1216+069 (LLS in Virgo) HST/STIS, FUSE Metal columns parsed from Tables 2 and 3 Total NHI from damping wings M/H from O/H ''' # Grab ASCII files from ApJ tab_fils = [ pyigm_path + "/data/LLS/tripp2005.tb3.ascii", pyigm_path + "/data/LLS/tripp2005.tb2.ascii" ] urls = [ 'http://iopscience.iop.org/0004-637X/619/2/714/fulltext/60797.tb3.txt', 'http://iopscience.iop.org/0004-637X/619/2/714/fulltext/60797.tb2.txt' ] for jj, tab_fil in enumerate(tab_fils): chk_fil = glob.glob(tab_fil) if len(chk_fil) > 0: tab_fil = chk_fil[0] else: url = urls[jj] print('LLSSurvey: Grabbing table file from {:s}'.format(url)) f = urlopen(url) with open(tab_fil, "wb") as code: code.write(f.read()) # Setup radec = '121920.9320+063838.476' # SIMBAD lls = LLSSystem(name='PG1216+069_z0.006', radec=radec, zem=0.3313, zabs=0.00632, vlim=[-100., 100.] * u.km / u.s, NHI=19.32, ZH=-1.6, sig_NHI=np.array([0.03, 0.03])) # Columns # Start with Table 3 (VPFIT) with open(tab_fils[0], 'r') as f: flines3 = f.readlines() ion_dict = {} for iline in flines3: if (len(iline.strip()) == 0): continue isplit = iline.split('\t') # Ion flg = 2 if (len(isplit[0].strip()) > 0): # & (isplit[0][0] not in ['1','2']): ipos = isplit[0].find('1') ionc = isplit[0][0:ipos - 1].strip() try: Zion = ltai.name_to_ion(ionc) except KeyError: pdb.set_trace() flg = 1 # Column csplit = isplit[3].split(' ') clm = float(csplit[0]) sig = float(csplit[2]) if flg == 1: ion_dict[ionc] = dict(logN=clm, sig_logN=sig, flag_N=1, Z=Zion[0], ion=Zion[1]) else: # Add it in tmp_dict = dict(logN=clm, sig_logN=sig, flag_N=1, Z=Zion[0], ion=Zion[1]) flagN, logN, siglogN = ltaa.sum_logN(ion_dict[ionc], tmp_dict) ion_dict[ionc]['logN'] = logN ion_dict[ionc]['sig_logN'] = siglogN ions = ion_dict.keys() # Now Table 2 for the extras with open(tab_fils[1], 'r') as f: flines2 = f.readlines() # Trim the first 10 lines flines2 = flines2[10:] # Loop for iline in flines2: isplit = iline.split('\t') # ionc = isplit[0].strip() if (len(ionc) == 0) or (ionc in ions): continue # Zion = ltai.name_to_ion(ionc) ion_dict[ionc] = dict(Z=Zion[0], ion=Zion[1], sig_logN=0.) if isplit[4][0] == '<': ion_dict[ionc]['logN'] = float(isplit[4][1:]) ion_dict[ionc]['flag_N'] = 3 else: raise ValueError('Should not get here') # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Tri05') return lls
def complist_from_table(table): """ Returns a list of AbsComponents from an input astropy.Table. Parameters ---------- table : Table Table with component information (each row must correspond to a component). Each column is expecting a unit when appropriate. Returns ------- complist : list List of AbsComponents defined from the input table. Notes ----- Mandatory column names: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' These column are required. Special column names: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN' These columns will fill internal attributes when corresponding. In order to fill in the Ntuple attribute all three 'logN', 'sig_logN', 'flag_logN' must be present. For convenience 'logN' and 'sig_logN' are expected to be floats corresponding to their values in np.log10(1/cm^2). Other columns: 'any_column_name' These will be added as attributes within the AbsComponent.attrib dictionary, with their respective units if given. """ # Convert to QTable to handle units in individual entries more easily table = QTable(table) # mandatory and optional columns min_columns = ['RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax'] special_columns = ['name', 'comment', 'logN', 'sig_logN', 'flag_logN'] for colname in min_columns: if colname not in table.keys(): raise IOError( '{} is a mandatory column. Please make sure your input table has it.' .format(colname)) #loop over rows complist = [] for row in table: # mandatory coord = SkyCoord(row['RA'].to('deg').value, row['DEC'].to('deg').value, unit='deg') # RA y DEC must both come with units Zion = name_to_ion(row['ion_name']) zcomp = row['z_comp'] vlim = [row['vmin'].to('km/s').value, row['vmax'].to('km/s').value ] * u.km / u.s # units are expected here too # special columns try: Ntuple = (row['flag_logN'], row['logN'], row['sig_logN'] ) # no units expected except KeyError: Ntuple = None try: comment = row['comment'] except KeyError: comment = '' try: name = row['name'] except KeyError: name = None # define the component comp = AbsComponent(coord, Zion, zcomp, vlim, Ntup=Ntuple, comment=comment, name=name) # other columns will be filled in comp.attrib dict for colname in table.keys(): if (colname not in special_columns) and (colname not in min_columns): kms_cols = ['b', 'sig_b'] if colname in kms_cols: # check units for parameters expected in velocity units try: val_aux = row[colname].to('km/s').value * u.km / u.s except u.UnitConversionError: raise IOError( 'If `{}` column is present, it must have velocity units.' .format(colname)) comp.attrib[colname] = val_aux # parameters we do not care about units much else: comp.attrib[colname] = row[colname] # append complist += [comp] return complist
def dessauges09(): '''Dessauges-Zavadsky et al. 2009, MNRAS, 396, L96 SLLS with UVES Zn,Fe abundances from Table 1 from astro-ph (LateX) by JXP [AODM] Taken from the Zn/H and Fe/H assuming *no* ionization corrections RA/DEC from the 'other' name ''' # Solar abundances eZn = 4.63 eFe = 7.45 sol = [eFe, eZn] # all_lls = [] # Table 1 tab_fil = pyigm_path + "/data/LLS/Literature/dessauges09.tb1.ascii" with open(tab_fil, 'r') as f: flines1 = f.readlines() # Trim the first few lines flines1 = flines1[3:] for iline in flines1: # Parse isplit = iline.split('&') # QSO if iline[0:2] == 'QS': # QSO, RA/DEC, zem qso = isplit[0][4:].strip() radec = isplit[1].strip()[1:].replace('$', '') zem = float(isplit[3].strip()) # NHI, zabs zabs = float(isplit[4].strip()) is2 = isplit[6].strip() NHI = float(is2[1:6]) sigNHI = np.array([float(is2[10:14])] * 2) # name name = qso + 'z_{:.3f}'.format(zabs) lls = LLSSystem(name=name, radec=radec, vlim=[-500, 500] * u.km / u.s, zem=zem, zabs=zabs, NHI=NHI, sig_NHI=sigNHI) # ADOM Columns ion_dict = {} for kk, ion in enumerate(['Fe II', 'Zn II']): Zion = ltai.name_to_ion(ion) is2 = isplit[7 + kk].strip() if is2[0:2] == '$>': ion_dict[ion] = dict(sig_clm=0., flg_clm=2, Z=Zion[0], ion=Zion[1]) ion_dict[ion]['clm'] = float(is2[2:7]) + NHI - 12 + sol[kk] elif is2[0:2] == '$<': ion_dict[ion] = dict(sig_clm=0., flg_clm=3, Z=Zion[0], ion=Zion[1]) ion_dict[ion]['clm'] = float(is2[2:7]) + NHI - 12 + sol[kk] elif is2[0:2] == '..': pass else: ion_dict[ion] = dict(flg_clm=1, Z=Zion[0], ion=Zion[1]) ion_dict[ion]['clm'] = float(is2[1:6]) + NHI - 12 + sol[kk] ion_dict[ion]['sig_clm'] = float(is2[10:14]) #xdb.set_trace() # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('DZ09') all_lls.append(lls) # Return SLLS only fin_slls = [ills for ills in all_lls if ills.NHI < 20.3] return fin_slls
def tumlinson11(): """Tumlinson, J. et al. 2011, ApJ, 733, 111 J1009+0713 HST/COS Metal columns parsed from Table 1 NHI from LL+Lyman series (uncertain) """ # Grab ASCII file from ApJ tab_fil = pyigm_path + "/data/LLS/Literature/tumlinson11.tb1.ascii" url = 'http://iopscience.iop.org/0004-637X/733/2/111/suppdata/apj388927t1_ascii.txt' chk_fil = glob.glob(tab_fil) if len(chk_fil) > 0: tab_fil = chk_fil[0] else: print('LLSSurvey: Grabbing table file from {:s}'.format(url)) f = urlopen(url) with open(tab_fil, "wb") as code: code.write(f.read()) # Setup radec = '100902.06+071343.8' # From paper lls = LLSSystem(name='J1009+0713_z0.356', radec=radec, zem=0.456, zabs=0.3558, vlim=[-200., 250.] * u.km / u.s, NHI=18.4, sig_NHI=np.array([0.41, 0.41])) # Columns # Start with Table 3 (VPFIT) with open(tab_fil, 'r') as f: flines1 = f.readlines() # Trim flines1 = flines1[18:] # ion_dict = {} line_dict = dict(OI='1302', OVI='1038', MgII='2803^b', SiII='1190', CaII='3934', FeII='2586') ion = None for iline in flines1: isplit = iline.split('\t') if ion == 'FeIII': # Last line break # Ion is2 = isplit[0].split(' ') ion = is2[0] + is2[1] try: gdl = line_dict[ion] except: pass #print('Taking {:s}'.format(isplit[0])) else: if is2[2] != gdl: continue Zion = ltai.name_to_ion(ion) ion_dict[ion] = dict(logN=0., sig_logN=0., flag_N=0, Z=Zion[0], ion=Zion[1]) # Combine components [could replace with SubSystems some day] for iis in isplit[1:-1]: # Upper limit if (iis.strip()[0] == '<') & (ion_dict[ion]['flag_N'] == 0): ion_dict[ion]['flag_N'] = 3 ion_dict[ion]['logN'] = float(iis[1:]) elif (iis.strip()[0] == '>'): # Saturated ion_dict[ion]['flag_N'] = 2 ion_dict[ion]['logN'] = log_sum( [ion_dict[ion]['logN'], float(iis[1:5])]) elif iis.strip()[0] in ['.', '<']: pass else: if ion_dict[ion]['flag_N'] == 2: # Add to saturated ion_dict[ion]['logN'] = log_sum( [ion_dict[ion]['logN'], float(iis[0:4])]) else: ion_dict[ion]['flag_N'] = 1 obj = dict(logN=float(iis[0:4]), sig_logN=float(iis[-4:]), flag_N=1) # Add flag, N, sig = ltaa.sum_logN(ion_dict[ion], obj) ion_dict[ion]['logN'] = N ion_dict[ion]['sig_logN'] = sig # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Tum11') return lls
def meiring09(): '''Meiring et al. 2009, MNRAS, 393, 1513 SLLS with Magellan Abundances from Table 3 from astro-ph (LateX) by JXP [AODM] RA/DEC from Table 1 ''' all_lls = [] # Table 1 tab_fil = pyigm_path + "/data/LLS/Literature/meiring09.tb1.ascii" with open(tab_fil, 'r') as f: flines1 = f.readlines() # Grab RA/DEC qso_dict = {} for iline in flines1: if iline[0:3] in [' QS', '\hl', '$\\c', ' J2', ' ']: continue # Parse isplit = iline.split('&') #xdb.set_trace() if '$' in isplit[3].strip(): isplit[3] = '-' + (isplit[3].strip())[3:] radec = isplit[2].strip() + isplit[3].strip() radec = radec.replace(':', '') # zem zem = float(isplit[5].strip()) # Save qso_dict[isplit[0].strip()] = dict(radec=radec, zem=zem, vlim=[-500, 500.] * u.km / u.s) # Abundances (AODM) # Table 3 tab_fil = pyigm_path + "/data/LLS/Literature/meiring09.tb3.ascii" with open(tab_fil, 'r') as f: flines3 = f.readlines() # for iline in flines3: if iline[0:2] in ['\h', ' ']: continue # Parse isplit = iline.split('&') # Ions if iline[0:2] == 'QS': ioncs = [] Zions = [] for iis in isplit[3:-1]: # Skipping HI # Parse #is2 = iis.split('\\') #ip2 = is2[2].find('}') ionc = iis.strip() # Zion Zion = ltai.name_to_ion(ionc) # Append ioncs.append(ionc) Zions.append(Zion) continue if iline[0] == 'Q': # QSO qso = isplit[0].strip() if qso[-1] in ['A', 'B', 'C']: qso = qso[0:-1] # zabs and name zabs = float(isplit[1].strip()) qso_dict[qso]['name'] = qso + 'z_{:.3f}'.format(zabs) qso_dict[qso]['zabs'] = zabs # NHI is2 = isplit[2].strip() if is2[0] == '$': qso_dict[qso]['NHI'] = 99.99 # THROW OUT Q1436-0051B qso_dict[qso]['sig_NHI'] = np.array([0., 0.]) else: qso_dict[qso]['NHI'] = float(is2[0:5]) qso_dict[qso]['sig_NHI'] = np.array([float(is2[10:])] * 2) #if qso_dict[qso]['NHI'] >= 20.3: # print('Uh oh. DLA') # Generate LLS lls = LLSSystem(**qso_dict[qso]) continue else: # ADOM Columns ion_dict = {} for kk, iis in enumerate(isplit[3:-1]): is2 = iis.strip() if is2[0:3] == '$>$': ion_dict[ioncs[kk]] = dict(sig_clm=0., flg_clm=2, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[3:]) elif is2[0:3] == '$<$': ion_dict[ioncs[kk]] = dict(sig_clm=0., flg_clm=3, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[3:]) elif len(is2) == 0: pass else: ion_dict[ioncs[kk]] = dict(flg_clm=1, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[0:5]) ion_dict[ioncs[kk]]['sig_clm'] = float(is2[10:]) # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Mei09') all_lls.append(lls) # Return SLLS only fin_slls = [ills for ills in all_lls if ills.NHI < 20.3] return fin_slls
def meiring07(): """Meiring et al. 2007, MNRAS, 376, 557 SLLS with Magellan Abundances from Table 11 from astro-ph (LateX) by JXP [AODM] RA/DEC from Table 1 """ all_lls = [] # Table 1 tab_fil = pyigm_path + "/data/LLS/Literature/meiring07.tb1.ascii" with open(tab_fil, 'r') as f: flines1 = f.readlines() # Grab RA/DEC qso_dict = {} for iline in flines1: if iline[0:2] in ['QS', '\h', '$\\', 'J2']: continue # Parse isplit = iline.split('&') if '-' not in isplit[3]: sgn = '+' else: sgn = '' radec = isplit[2].strip() + sgn + isplit[3].strip() radec = radec.replace(':', '') # zem if isplit[0].strip() != 'Q0826-2230': zem = float(isplit[5].strip()) else: zem = 0.911 # Save qso_dict[isplit[0].strip()] = dict(radec=radec, zem=zem, vlim=[-500., 500] * u.km / u.s) # Abundances (AODM) # Table 11 tab_fil = pyigm_path + "/data/LLS/Literature/meiring07.tb11.ascii" with open(tab_fil, 'r') as f: flines11 = f.readlines() # for iline in flines11: if iline[0:2] in ['\h', ' ']: continue # Parse isplit = iline.split('&') # Ions if iline[0:2] == 'QS': ioncs = [] Zions = [] for iis in isplit[3:-1]: # Skipping HI # Parse is2 = iis.split('\\') ip2 = is2[2].find('}') ionc = is2[1][2:].strip() + ' ' + is2[2][0:ip2].strip() # Zion Zion = ltai.name_to_ion(ionc) # Append ioncs.append(ionc) Zions.append(Zion) continue if iline[0] == 'Q': # QSO qso = isplit[0].strip() # zabs and name zabs = float(isplit[1].strip()) qso_dict[qso]['name'] = qso + 'z_{:.3f}'.format(zabs) qso_dict[qso]['zabs'] = zabs # NHI is2 = isplit[2].strip() qso_dict[qso]['NHI'] = float(is2[0:5]) #if qso_dict[qso]['NHI'] >= 20.3: # print('Uh oh. DLA') qso_dict[qso]['sig_NHI'] = np.array([float(is2[10:])] * 2) # Generate LLS lls = LLSSystem(**qso_dict[qso]) continue else: # ADOM Columns ion_dict = {} for kk, iis in enumerate(isplit[3:-1]): is2 = iis.strip() if is2[0:3] == '$>$': ion_dict[ioncs[kk]] = dict(sig_clm=0., flg_clm=2, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[3:]) elif is2[0:3] == '$<$': ion_dict[ioncs[kk]] = dict(sig_clm=0., flg_clm=3, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[3:]) elif len(is2) == 0: pass else: ion_dict[ioncs[kk]] = dict(flg_clm=1, Z=Zions[kk][0], ion=Zions[kk][1]) ion_dict[ioncs[kk]]['clm'] = float(is2[0:5]) ion_dict[ioncs[kk]]['sig_clm'] = float(is2[10:]) # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Mei07') all_lls.append(lls) # Return SLLS only fin_slls = [ills for ills in all_lls if ills.NHI < 20.3] return fin_slls
def jenkins2005(): """Jenkins, E. et al. 2005, ApJ, 2005, 623, 767 PHL 1811 HST/STIS, FUSE Metals parsed from Table 1 OI taken from text Had to input error on columns by hand (JXP) Total NHI from Lyman series. see Fig 3 M/H from O/H """ # Grab ASCII file from ApJ tab_fil = pyigm_path + "/data/LLS/Literature/jenkins2005.tb1.ascii" chk_fil = glob.glob(tab_fil) if len(chk_fil) > 0: tab_fil = chk_fil[0] else: url = 'http://iopscience.iop.org/0004-637X/623/2/767/fulltext/61520.tb1.txt' print('LLSSurvey: Grabbing table file from {:s}'.format(url)) f = urlopen(url) with open(tab_fil, "wb") as code: code.write(f.read()) # Setup radec = '215501.5152-092224.688' # SIMBAD lls = LLSSystem(name='PHL1811_z0.081', radec=radec, zem=0.192, zabs=0.080923, vlim=[-100., 100.] * u.km / u.s, NHI=17.98, ZH=-0.19, sig_NHI=np.array([0.05, 0.05])) lls.lines = [] # Probably not used # AbsLines ism = LineList('ISM') Nsig = { 'C IV': 0.4, 'N II': 0.4, 'Si II': 0.05, 'Si IV': 0.25, 'S II': 0.2, 'Fe II': 0.12, 'H I': 0.05, 'S III': 0.06 } # Parse Table with open(tab_fil, 'r') as f: flines = f.readlines() ion_dict = {} for iline in flines: iline = iline.strip() if (len(iline) == 0): continue # Split on tabs isplit = iline.split('\t') # Offset? ioff = 0 if isplit[0][0] in ['1', '2']: ioff = -1 # Catch bad lines if (isplit[1 + ioff][0:6] in ['1442.0', '1443.7', '1120.9']): # Skip goofy CII line and CII* continue if len(isplit[2 + ioff]) == 0: continue # Ion if (len(isplit[0].strip()) > 0) & (isplit[0][0] not in ['1', '2']): ionc = isplit[0].strip() try: Zion = ltai.name_to_ion(ionc) except (KeyError, RecursionError): pdb.set_trace() # Generate the Line try: newline = AbsLine(float(isplit[2 + ioff]) * u.AA, linelist=ism, closest=True) except ValueError: pdb.set_trace() newline.attrib['z'] = lls.zabs # Spectrum newline.analy['datafile'] = 'STIS' if 'S' in isplit[1] else 'FUSE' # EW try: EWvals = isplit[4 + ioff].split(' ') except IndexError: pdb.set_trace() newline.attrib['EW'] = float(EWvals[0]) * u.AA / 1e3 newline.attrib['sig_EW'] = float(EWvals[2]) * u.AA / 1e3 newline.attrib['flag_EW'] = 1 if len(isplit) < (5 + ioff + 1): continue # Colm? #xdb.set_trace() newline.attrib['sig_logN'] = 0. if (len(isplit[5 + ioff].strip()) > 0) & (isplit[5 + ioff].strip() != '\\ldots'): if isplit[5 + ioff][0] == '\\': ipos = isplit[5 + ioff].find(' ') newline.attrib['logN'] = float(isplit[5 + ioff][ipos + 1:]) newline.attrib['flag_N'] = 2 elif isplit[5 + ioff][0] == '<': ipos = 0 newline.attrib['logN'] = float(isplit[5 + ioff][ipos + 1:]) newline.attrib['flag_N'] = 3 elif isplit[5 + ioff][0] == '1': try: newline.attrib['logN'] = float(isplit[5 + ioff][0:5]) except ValueError: pdb.set_trace() newline.attrib['flag_N'] = 1 try: newline.attrib['sig_logN'] = Nsig[ionc] except KeyError: print('No error for {:s}'.format(ionc)) else: raise ValueError('Bad character') # ion_dict ion_dict[ionc] = dict(clm=newline.attrib['logN'], sig_clm=newline.attrib['sig_logN'], flg_clm=newline.attrib['flag_N'], Z=Zion[0], ion=Zion[1]) # Append lls.lines.append(newline) # Fix NI, OI ion_dict['O I']['clm'] = 14.47 ion_dict['O I']['sig_clm'] = 0.05 ion_dict['N I']['flg_clm'] = 3 lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Jen05') # Return return lls
def complist_from_table(table): """ Returns a list of AbsComponents from an input astropy.Table. Parameters ---------- table : Table Table with component information (each row must correspond to a component). Each column is expecting a unit when appropriate. Returns ------- complist : list List of AbsComponents defined from the input table. Notes ----- Mandatory column names: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' These column are required. Special column names: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN' These columns will fill internal attributes when corresponding. In order to fill in the Ntuple attribute all three 'logN', 'sig_logN', 'flag_logN' must be present. For convenience 'logN' and 'sig_logN' are expected to be floats corresponding to their values in np.log10(1/cm^2). Other columns: 'any_column_name' These will be added as attributes within the AbsComponent.attrib dictionary, with their respective units if given. """ # Convert to QTable to handle units in individual entries more easily table = QTable(table) # mandatory and optional columns min_columns = ['RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax'] special_columns = ['name', 'comment', 'logN', 'sig_logN', 'flag_logN'] for colname in min_columns: if colname not in table.keys(): raise IOError('{} is a mandatory column. Please make sure your input table has it.'.format(colname)) #loop over rows complist = [] for row in table: # mandatory coord = SkyCoord(row['RA'].to('deg').value, row['DEC'].to('deg').value, unit='deg') # RA y DEC must both come with units Zion = name_to_ion(row['ion_name']) zcomp = row['z_comp'] vlim =[row['vmin'].to('km/s').value, row['vmax'].to('km/s').value] * u.km / u.s # units are expected here too # special columns try: Ntuple = (row['flag_logN'], row['logN'], row['sig_logN']) # no units expected except KeyError: Ntuple = None try: comment = row['comment'] except KeyError: comment = '' try: name = row['name'] except KeyError: name = None # define the component comp = AbsComponent(coord, Zion, zcomp, vlim, Ntup=Ntuple, comment=comment, name=name) # other columns will be filled in comp.attrib dict for colname in table.keys(): if (colname not in special_columns) and (colname not in min_columns): kms_cols = ['b', 'sig_b'] if colname in kms_cols: # check units for parameters expected in velocity units try: val_aux = row[colname].to('km/s').value * u.km / u.s except u.UnitConversionError: raise IOError('If `{}` column is present, it must have velocity units.'.format(colname)) comp.attrib[colname] = val_aux # parameters we do not care about units much else: comp.attrib[colname] = row[colname] # append complist += [comp] return complist
def battisti12(): '''Battisti, A. et al. 2012, ApJ, 744, 93 HST/COS QSO info from Table 1 Metal columns parsed from Table 3 NHI from Lya ''' all_lls = [] # Grab ASCII files from ApJ tab_fils = [ pyigm_path + "/data/LLS/Literature/battisti12.tb1.ascii", pyigm_path + "/data/LLS/Literature/battisti12.tb3.ascii" ] urls = [ 'http://iopscience.iop.org/0004-637X/744/2/93/suppdata/apj413924t1_ascii.txt', 'http://iopscience.iop.org/0004-637X/744/2/93/suppdata/apj413924t3_ascii.txt' ] for jj, tab_fil in enumerate(tab_fils): chk_fil = glob.glob(tab_fil) if len(chk_fil) > 0: tab_fil = chk_fil[0] else: url = urls[jj] print('LLSSurvey: Grabbing table file from {:s}'.format(url)) f = urlopen(url) with open(tab_fil, "wb") as code: code.write(f.read()) # QSO info with open(tab_fils[0], 'r') as f: flines1 = f.readlines() # Grab RA/DEC all_idict = [] for iline in flines1: if iline[0:2] != 'SD': continue # Parse isplit = iline.split('\t') name = isplit[0].split(' ')[1] radec = name[1:] zem = float(isplit[1].strip()) zabs = float(isplit[2].strip()) NHI = float(isplit[3].strip()[0:4]) sigNHI = np.array([float(isplit[3].strip()[11:])] * 2) # Save lls = LLSSystem(name=name, radec=radec, zem=zem, zabs=zabs, NHI=NHI, sig_NHI=sigNHI, vlim=[-500, 500] * u.km / u.s) # all_lls.append(lls) all_idict.append({}) # Abundances with open(tab_fils[1], 'r') as f: flines3 = f.readlines() flines3 = flines3[5:] ion = None for iline in flines3: if ion == 'Ni II': break isplit = iline.split('\t') if isplit[0] == 'C II*': # Skipping CII* continue # ion ipos = -1 while (isplit[0][ipos] not in ['I', 'V']): ipos -= 1 ion = isplit[0][0:ipos + 1 + len(isplit[0])] Zion = ltai.name_to_ion(ion) # Loop on systems for kk, iis in enumerate(isplit[1:-1]): if iis.strip()[0] == '.': continue all_idict[kk][ion] = dict(Z=Zion[0], ion=Zion[1], sig_clm=0.) if iis[0] == '>': all_idict[kk][ion]['flg_clm'] = 2 all_idict[kk][ion]['clm'] = float(iis[1:6]) elif iis[0] == '<': all_idict[kk][ion]['flg_clm'] = 3 all_idict[kk][ion]['clm'] = float(iis[1:]) else: all_idict[kk][ion]['flg_clm'] = 1 all_idict[kk][ion]['clm'] = float(iis[0:5]) all_idict[kk][ion]['sig_clm'] = float(iis[-4:]) # Return SLLS only for kk, lls in enumerate(all_lls): try: lls._ionN = pyiau.dict_to_ions(all_idict[kk]) except ValueError: pdb.set_trace() lls.Refs.append('Bat12') fin_slls = [ills for ills in all_lls if ills.NHI < 20.3] return fin_slls
def stack_plot(self, to_plot, pvlim=None, maxtrans=3, return_fig=True, add_missing_lines=False, spec=None, **kwargs): '''Show a stack plot of the CGM absorption system Parameters ---------- to_plot : List of AbsLines, AbsComponents, tuples, or strs If AbsLines, pass list on to linetools.analysis.plots.stack_plot() If not Abslines, will plot up to maxtrans of strongest transitions covered by spectra for components of some species. If tuples or strs, should be Zion value or ion name: (8,6) or 'OVI' pvlim : Quantities, optional Override system vlim for plotting maxtrans : int, optional Maximum number of lines per transition to plot add_missing_lines : bool, optional If True, plot transitions that do not have associated AbsLine objects spec : XSpectrum1D, optional Spectrum to plot in regions of requested lines; required if assoc. AbsLine objects do not have their analy['specfile'] attributes set Returns ------- fig : matplotlib Figure, optional Figure instance containing stack plot with subplots, axes, etc. ''' from linetools.analysis import plots as ltap from linetools.spectralline import AbsLine from linetools.isgm.abscomponent import AbsComponent from linetools.spectra.io import readspec from linetools.lists.linelist import LineList from linetools.abund import ions as ltai from pyigm import utils as pu ilist = LineList('ISM') if not isinstance(to_plot[0], AbsLine): lines2plot = [] # Master list of lines to plot for i, tp in enumerate(to_plot): if isinstance(tp, AbsComponent): comp = to_plot else: # Pick components in system closest to z_cgm thesecomps = pu.get_components(self, tp) if len(thesecomps) == 0: if add_missing_lines is True: # Add components if isinstance(tp, str): tup = ltai.name_to_ion(tp) else: tup = tp comp = AbsComponent(self.coord, tup, zcomp=self.z, vlim=[-100., 100.] * u.km / u.s) comp.add_abslines_from_linelist() if spec is not None: for al in comp._abslines: al.analy['spec'] = spec else: raise ValueError('spec must be provided if ' 'requesting species without' ' existing components.') else: continue else: # Find component with redshift closest to systemic compvels = np.array( [np.median(tc.vlim.value) for tc in thesecomps]) comp = thesecomps[np.argmin(np.abs(compvels))] ### Get strongest transitions covered wmins = [] wmaxs = [] for j, al in enumerate(comp._abslines): # Load spectrum if not already loaded if al.analy['spec'] is None: try: if spec is not None: al.analy['spec'] = spec else: al.analy['spec'] = readspec( al.analy['spec_file']) except: raise LookupError("spec must be defined or " "analy['specfile'] must be " "declared for AbsLines") # Get wavelength limits to know where to look wmins.append(al.analy['spec'].wvmin.value) wmaxs.append(al.analy['spec'].wvmax.value) wlims = (np.min(np.array(wmins)), np.max( np.array(wmaxs))) * u.Angstrom # ID the strong transitions strong = ilist.strongest_transitions(tp, wvlims=wlims / (1. + comp.zcomp), n_max=maxtrans) if strong is None: # No lines covered in the spectra warnings.warn( 'No lines for {} are covered by the spectra' 'provided.'.format(tp)) continue # Grab the AbsLines from this AbsComponent and their names complines = comp._abslines complines = np.array(complines) # For the indexing compnames = np.array([ll.name for ll in complines]) ### Add the lines found to the master list if isinstance(strong, dict): # Only one line covered lines2plot.append( complines[compnames == strong['name']][0]) else: # Multiple lines covered for i, sn in enumerate(strong['name']): # Handle case where relevant components are defined if sn in compnames: tokeep = [complines[compnames == sn][0]] lines2plot.extend(tokeep) # Now case where components were not attached to sys elif add_missing_lines is True: # Add line to the existing comp to preserve z, etc. compcopy = comp.copy( ) #Copy to add dummy lines # Set up observed wavelength range obswave = strong['wrest'][i] * (1. + compcopy.zcomp) wvrange = [obswave - 0.1, obswave + 0.1 ] * u.Angstrom # Add this line to the component compcopy.add_abslines_from_linelist( wvlim=wvrange) al = compcopy._abslines[-1] # Set the spectrum if spec is not None: al.analy['spec'] = spec else: al.analy['spec'] = comp._abslines[0].analy[ 'spec'] # Add the line lines2plot.append(al) else: warnings.warn( '{} covered by spectra but not in' 'components list'.format(sn)) else: lines2plot = to_plot # Deal with velocity limits if pvlim is not None: vlim = pvlim else: vlim = self.vlim ### Make the plot! fig = ltap.stack_plot(lines2plot, vlim=vlim, return_fig=return_fig, zref=self.z, **kwargs) fig.subplots_adjust(bottom=0., left=0.1, right=0.95, hspace=0., wspace=0.35) return fig
def peroux06b(): """Peroux, C. et al. 2006b, A&A, 450, 53 SDSS J1323-0021 Metal rich Metal columns copied by JXP from Table 1 Total NHI from damping wings """ # Setup radec = '132323.78-002155.2' # SDSS Name lls = LLSSystem(name='SDSSJ1323-0021_z0.716', radec=radec, zem=1.390, zabs=0.716, vlim=[-200., 200.] * u.km / u.s, NHI=20.21, sig_NHI=np.array([0.20, 0.20])) # Parse table file tab_fil = pyigm_path + "/data/LLS/Literature/peroux06b.tb1.ascii" with open(tab_fil, 'r') as f: flines = f.readlines() ion_dict = {} for iline in flines: isplit = iline.split('\t') if len(isplit[0]) == 0: # Grab ions and init ions = isplit[3:10] for ion in ions: Zion = ltai.name_to_ion(ion) ion_dict[ion] = dict(clm=0., sig_clm=0., flg_clm=1, Z=Zion[0], ion=Zion[1]) continue # Column or sigma? if isplit[0][0] == 'N': # Column for kk, iis in enumerate(isplit[3:10]): ion = ions[kk] if iis[0] == '>': ion_dict[ion]['flg_clm'] = 2 ion_dict[ion]['clm'] += float(iis[1:]) elif iis[0] == '<': pass elif iis[0] == '.': pass else: ion_dict[ion]['clm'] += float(iis) else: # Sigma for kk, iis in enumerate(isplit[3:10]): ion = ions[kk] if iis[0] == '.': pass else: ion_dict[ion]['sig_clm'] += float(iis)**2 # Convert to log for ion in ions: N = ion_dict[ion]['clm'] sig = np.sqrt(ion_dict[ion]['sig_clm']) # ion_dict[ion]['clm'] = np.log10(N) if ion_dict[ion]['flg_clm'] == 2: ion_dict[ion]['sig_clm'] = 0. else: ion_dict[ion]['sig_clm'] = sig / N / np.log(10) # Finish lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Prx06b') return lls