def repr_vpfit(self, b=10. * u.km / u.s, tie_strs=('', '', ''), fix_strs=('', '', '')): """ String representation for VPFIT (line fitting software) in its fort.26 format Parameters ---------- b : Quantity, optional Doppler parameter of the component. Default is 10*u.km/u.s tie_strs : tuple of strings, optional Strings to be used for tying parameters (z,b,logN), respectively. These are all converted to lower case format, following VPFIT convention. fix_strs : tuple of strings, optional Strings to be used for fixing parameters (z,b,logN), respectively. These are all converted to upper case format, following VPFIT convention. These will take precedence over tie_strs if different than ''. Returns ------- repr_vpfit : str """ # get Doppler parameter to km/s b = b.to('km/s').value # Ion name name = ions.ion_to_name(self.Zion, nspace=1) name = name.replace(' ', '') # Deal with fix and tie parameters # Check format first for i, x_strs in enumerate([tie_strs, fix_strs]): if (not isinstance(x_strs, tuple)) or (not all( isinstance(s, (str, basestring)) for s in x_strs)): if i == 0: raise TypeError('`tie_strs` must be a tuple of strings.') elif i == 1: raise TypeError('`fix_strs` must be a tuple of strings.') if len(x_strs) != 3: raise SyntaxError( '`tie_strs` and `fix_strs` must have len() == 3') # reformat for VPFIT standard fix_strs = np.array([s.upper() for s in fix_strs]) tie_strs = np.array([s.lower() for s in tie_strs]) # preference to fix_strs over tie_strs strs = np.where(fix_strs != '', fix_strs, tie_strs) # create the line string s = '{:s} {:.5f}{:s} {:.5f} {:.2f}{:s} {:.2f} {:.2f}{:s} {:.2f}'.format( name, self.zcomp, strs[0], 0, b, strs[1], 0, self.logN, strs[2], 0) if len(self.comment) > 0: s += '! {:s}'.format(self.comment) s += '\n' return s
def repr_vpfit(self, b=10.*u.km/u.s, tie_strs=('', '', ''), fix_strs=('', '', '')): """ String representation for VPFIT (line fitting software) in its fort.26 format Parameters ---------- b : Quantity, optional Doppler parameter of the component. Default is 10*u.km/u.s tie_strs : tuple of strings, optional Strings to be used for tying parameters (z,b,logN), respectively. These are all converted to lower case format, following VPFIT convention. fix_strs : tuple of strings, optional Strings to be used for fixing parameters (z,b,logN), respectively. These are all converted to upper case format, following VPFIT convention. These will take precedence over tie_strs if different than ''. Returns ------- repr_vpfit : str """ # get Doppler parameter to km/s b = b.to('km/s').value # Ion name name = ions.ion_to_name(self.Zion, nspace=1) name = name.replace(' ', '') # Deal with fix and tie parameters # Check format first for i, x_strs in enumerate([tie_strs, fix_strs]): if (not isinstance(x_strs, tuple)) or (not all(isinstance(s, (str, basestring)) for s in x_strs)): if i == 0: raise TypeError('`tie_strs` must be a tuple of strings.') elif i == 1: raise TypeError('`fix_strs` must be a tuple of strings.') if len(x_strs) != 3: raise SyntaxError('`tie_strs` and `fix_strs` must have len() == 3') # reformat for VPFIT standard fix_strs = np.array([s.upper() for s in fix_strs]) tie_strs = np.array([s.lower() for s in tie_strs]) # preference to fix_strs over tie_strs strs = np.where(fix_strs != '', fix_strs, tie_strs) # create the line string s = '{:s} {:.5f}{:s} {:.5f} {:.2f}{:s} {:.2f} {:.2f}{:s} {:.2f}'.format(name, self.zcomp, strs[0], 0, b, strs[1], 0, self.logN, strs[2], 0) if len(self.comment) > 0: s += '! {:s}'.format(self.comment) s += '\n' return s
def table_from_complist(complist): """ Returns a astropy.Table from an input list of AbsComponents. It only fills in mandatory and special attributes (see notes below). Information stored in dictionary AbsComp.attrib is ignored. Parameters ---------- complist : list of AbsComponents The initial list of AbsComponents to create the QTable from. Returns ------- table : Table Table from the information contained in each component. Notes ----- Mandatory columns: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' Special columns: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN' See also complist_from_table() """ tab = Table() # mandatory columns tab['RA'] = [comp.coord.ra.to('deg').value for comp in complist] * u.deg tab['DEC'] = [comp.coord.dec.to('deg').value for comp in complist] * u.deg ion_names = [] # ion_names for comp in complist: if comp.Zion == (-1, -1): ion_names += ["Molecule"] else: ion_names += [ion_to_name(comp.Zion)] tab['ion_name'] = ion_names tab['z_comp'] = [comp.zcomp for comp in complist] tab['vmin'] = [comp.vlim[0].value for comp in complist] * comp.vlim.unit tab['vmax'] = [comp.vlim[1].value for comp in complist] * comp.vlim.unit # Special columns tab['logN'] = [comp.logN for comp in complist] tab['sig_logN'] = [comp.sig_logN for comp in complist] tab['flag_logN'] = [comp.flag_N for comp in complist] tab['comment'] = [comp.comment for comp in complist] tab['name'] = [comp.name for comp in complist] tab['reliability'] = [comp.reliability for comp in complist] return tab
def table_from_complist(complist): """ Returns a astropy.Table from an input list of AbsComponents. It only fills in mandatory and special attributes (see notes below). Information stored in dictionary AbsComp.attrib is ignored. Parameters ---------- complist : list of AbsComponents The initial list of AbsComponents to create the QTable from. Returns ------- table : Table Table from the information contained in each component. Notes ----- Mandatory columns: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' Special columns: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN' See also complist_from_table() """ tab = Table() # mandatory columns tab['RA'] = [comp.coord.ra.to('deg').value for comp in complist] * u.deg tab['DEC'] = [comp.coord.dec.to('deg').value for comp in complist] * u.deg ion_names = [] # ion_names for comp in complist: if comp.Zion == (-1,-1): ion_names += ["Molecule"] else: ion_names += [ion_to_name(comp.Zion)] tab['ion_name'] = ion_names tab['z_comp'] = [comp.zcomp for comp in complist] tab['vmin'] = [comp.vlim[0].value for comp in complist] * comp.vlim.unit tab['vmax'] = [comp.vlim[1].value for comp in complist] * comp.vlim.unit # Special columns tab['logN'] = [comp.logN for comp in complist] tab['sig_logN'] = [comp.sig_logN for comp in complist] tab['flag_logN'] = [comp.flag_N for comp in complist] tab['comment'] = [comp.comment for comp in complist] tab['name'] = [comp.name for comp in complist] return tab
def read_verner94(): """ Read Verner1994 Table """ # Read verner94 = lt_path + '/data/lines/verner94_tab6.fits' print( 'linetools.lists.parse: Reading linelist --- \n {:s}'.format( verner94)) tbl_6 = Table.read(verner94) # Deal with bad unit tbl_6['lambda'].unit = u.AA tbl_6 = Table(tbl_6) # My table ldict, data = line_data(nrows=len(tbl_6)) # Fill data['wrest'] = tbl_6['lambda'] data['f'] = tbl_6['Fik'] data['gj'] = tbl_6['Gi'] data['gk'] = tbl_6['Gk'] data['Z'] = tbl_6['Z'] data['ion'] = tbl_6['Z'] - tbl_6['N'] + 1 for ii,row in enumerate(tbl_6): data[ii]['name'] = ( row['Species'][0:2].strip() + row['Species'][2:].strip() + ' {:d}'.format(int(row['lambda']))) # name names = [] for row in data: ionnm = ions.ion_to_name((row['Z'], row['ion'])) names.append('{:s} {:d}'.format(ionnm, int(row['wrest']))) data['name'] = names # Finish data['group'] = 1 data['Ref'] = 'Verner1994' data['mol'] = '' # Return return data
def read_verner94(): """ Read Verner1994 Table """ # Read verner94 = lt_path + '/data/lines/verner94_tab6.fits' print('linetools.lists.parse: Reading linelist --- \n {:s}'.format( verner94)) tbl_6 = Table.read(verner94) # Deal with bad unit tbl_6['lambda'].unit = u.AA tbl_6 = Table(tbl_6) # My table ldict, data = line_data(nrows=len(tbl_6)) # Fill data['wrest'] = tbl_6['lambda'] data['f'] = tbl_6['Fik'] data['gj'] = tbl_6['Gi'] data['gk'] = tbl_6['Gk'] data['Z'] = tbl_6['Z'] data['ion'] = tbl_6['Z'] - tbl_6['N'] + 1 for ii, row in enumerate(tbl_6): data[ii]['name'] = (row['Species'][0:2].strip() + row['Species'][2:].strip() + ' {:d}'.format(int(row['lambda']))) # name names = [] for row in data: ionnm = ions.ion_to_name((row['Z'], row['ion'])) names.append('{:s} {:d}'.format(ionnm, int(row['wrest']))) data['name'] = names # Finish data['group'] = 1 data['Ref'] = 'Verner1994' data['mol'] = '' # Return return data
def test_ion_to_name(): # Normal ionnm = ions.ion_to_name((6, 2)) assert ionnm == 'CII' # Latex ionnm = ions.ion_to_name((6, 2), flg=1) assert ionnm == '{\\rm C}^{+}' # as dict ion = dict(ion=2, Z=6) ionnm = ions.ion_to_name(ion) assert ionnm == 'CII' # latex not ready yet with pytest.raises(ValueError): ionnm = ions.ion_to_name((6, 0), flg=1) for ii in [1, 2, 3, 4]: ionnm = ions.ion_to_name((6, ii), flg=1) # bad flag with pytest.raises(ValueError): ionnm = ions.ion_to_name((6, 2), flg=99)
def test_ion_to_name(): # Normal ionnm = ions.ion_to_name((6,2)) assert ionnm == 'CII' # Latex ionnm = ions.ion_to_name((6,2), flg=1) assert ionnm == '{\\rm C}^{+}' # as dict ion = dict(ion=2, Z=6) ionnm = ions.ion_to_name(ion) assert ionnm == 'CII' # latex not ready yet with pytest.raises(ValueError): ionnm = ions.ion_to_name((6,0), flg=1) for ii in [1,2,3,4]: ionnm = ions.ion_to_name((6,ii), flg=1) # bad flag with pytest.raises(ValueError): ionnm = ions.ion_to_name((6,2), flg=99)
def table_from_complist(complist, summed_ion=False, NHI_obj=None, vrange=None, ztbl=None): """ Returns a astropy.Table from an input list of AbsComponents. It only fills in mandatory and special attributes (see notes below). Other information stored in dictionary AbsComp.attrib is ignored. Attributes with units are stored in the Table with units. Parameters ---------- complist : list of AbsComponents The initial list of AbsComponents to create the Table from. NHI_obj : object, optional (with NHI, sig_NHI, flag_NHI attributes) If provided, fill HI with NHI, sig_NHI, flag_NHI vrange : Quantity, optional Velocity range of components to sum column densities ztbl : float, optional Redshift to adopt for the table if summed_ion option is used Returns ------- table : Table Table from the information contained in each component. Notes ----- Mandatory columns: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' Special columns: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN', 'b', 'sig_b', 'specfile' 'reliability' is included if provided See also complist_from_table() """ if summed_ion is False: key_order = ['RA', 'DEC', 'comp_name', 'z_comp', 'sig_z', 'Z', 'ion', 'Ej', 'vmin', 'vmax','ion_name', 'flag_N', 'logN', 'sig_logN', 'b','sig_b', 'vel', 'sig_vel','specfile'] else: key_order = ['RA', 'DEC', 'comp_name', 'z_comp', 'sig_z', 'Z', 'ion', 'Ej', 'vmin', 'vmax', 'ion_name', 'flag_N', 'logN', 'sig_logN', 'vel','specfile'] tab = Table() # Coordinates coords = SkyCoord([icomp.coord for icomp in complist]) # Basics if hasattr(coords, 'ra'): tab['RA'] = coords.ra tab['DEC'] = coords.dec elif hasattr(coords, 'b'): tab['b_gal'] = coords.b tab['l_gal'] = coords.l # Adjust keys key_order.remove('RA') key_order.remove('DEC') key_order = ['b_gal', 'l_gal'] + key_order else: raise IOError("Not ready for this coords frame: {:s}".format(coords.frame.name)) tab['comp_name'] = [comp.name for comp in complist] tab['vmin'] = u.Quantity([icomp.vlim[0] for icomp in complist]) tab['vmax'] = u.Quantity([icomp.vlim[1] for icomp in complist]) tab['Z'] = [icomp.Zion[0] for icomp in complist] tab['ion'] = [icomp.Zion[1] for icomp in complist] # . attributes (required ones) for attrib in ['zcomp', 'Ej']: values = [getattr(icomp,attrib) for icomp in complist] if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Rename tab.rename_column('zcomp', 'z_comp') # Ion names ion_names = [] for comp in complist: if comp.Zion == (-1,-1): ion_names += ["Molecule"] else: ion_names += [ion_to_name(comp.Zion)] if comp.Ej.value > 0.: # Slightly kludgy ion_names[-1] += '*' tab['ion_name'] = ion_names # attrib dict containing logN, b, etc for attrib in ['flag_N', 'logN', 'sig_logN', 'sig_z', 'b', 'sig_b', 'vel', 'sig_vel', 'specfile']: try: values = [icomp.attrib[attrib] for icomp in complist] except KeyError: key_order.pop(key_order.index(attrib)) pass else: # Quantity if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Special columns for key in ['comment', 'reliability']: if hasattr(complist[0], key): tab[key] = [getattr(comp, key) for comp in complist] key_order += [key] #assert len(key_order) == len(tab.keys()) # We allowed keys to be popped! tab = tab[key_order] # May need to add an HI component here as done in the method below if NHI_obj is not None: mt = np.where((tab['Z'] == 1) & (tab['ion'] == 1))[0] # Existing row in Table? if len(mt) == 1: tab[mt[0]]['logN'] = NHI_obj.NHI tab[mt[0]]['sig_logN'] = np.mean(NHI_obj.sig_NHI) # Allow for two values tab[mt[0]]['flag_N'] = NHI_obj.flag_NHI else: # Add a dummy row tab.add_row(tab[0]) tab['logN'][-1] = NHI_obj.NHI tab['sig_logN'][-1] = np.mean(NHI_obj.sig_NHI) # Allow for two values tab['flag_N'][-1] = NHI_obj.flag_NHI tab['Z'][-1] = 1 tab['ion'][-1] = 1 tab['Ej'][-1] = 0. tab['ion_name'][-1] = 'HI' tab['comp_name'][-1] = 'HI_z{:0.5f}'.format(tab['z_comp'][-1]) if summed_ion is False: return tab else: ### Perform logic: ### - find unique ions # Identify unique Zion, Ej (not ready for A) uqions, uiidx = np.unique(tab['ion_name'], return_index=True) ### - select on velocity if vrange is not None: vrange = vrange.to(u.km / u.s).value ### - synthesize column densities # Loop count = 0 for i,ui in enumerate(uqions): # Grab velocities from components compvels = tab['vel'] # Synthesize components with like Zion, Ej, and in vrange if vrange is not None: try: thesecomps = np.where((tab['ion_name'] == ui) & (compvels > vrange[0]) & (compvels < vrange[1]))[0] except: import pdb; pdb.set_trace() else: thesecomps = np.where(tab['ion_name'] == ui)[0] comps = [complist[ii] for ii in thesecomps] # Need a list if len(comps) > 0: synth_comp = synthesize_components(comps, zcomp=ztbl) # Add a row to Table tab.add_row(tab[thesecomps[0]]) tab['logN'][-1] = synth_comp.logN tab['sig_logN'][-1] = synth_comp.sig_logN # Allow for two values tab['flag_N'][-1] = synth_comp.flag_N tab['vmin'][-1] = synth_comp.vlim.value[0] tab['vmax'][-1] = synth_comp.vlim.value[1] count += 1 #tab['comp_name'][-1] = 'HI_z{:0.5f}'.format(tab['z_comp'][-1]) else: print('No components found within velocity range found.') ### - Create new table and return summed_tab = tab[-count:] # We needed component velocities for vrange selection, but # they are meaningless for summed ion info summed_tab.remove_column('vel') return summed_tab
def parse_verner96(orig=False, write=False): """Parse tables from Verner, Verner, & Ferland (1996, Atomic Data and Nuclear Data Tables, Vol. 64, p.1) Parameters ---------- orig : bool, optional Use original code to parse the ASCII file Else, read from a FITS file Returns ------- data : Table Atomic data """ # Look for FITS fitsf = lt_path + '/data/lines/verner96_tab1.fits.gz' verner96_tab1 = glob.glob(fitsf) if (len(verner96_tab1) > 0) & (not orig): print('linetools.lists.parse: Reading linelist --- \n {:s}'.format( verner96_tab1[0])) data = Table(Table.read(verner96_tab1[0]), masked=True) else: # File verner96_tab1 = lt_path + '/data/lines/verner96_tab1.txt' # Read with open(verner96_tab1) as f: lines = f.readlines() # Grab the 'good' ones gdlines = [iline.strip() for iline in lines if len(iline.strip()) > 113] ldict, data = line_data(nrows=len(gdlines)) # Loop for kk, line in enumerate(gdlines): # Z, ion data[kk]['Z'] = ELEMENTS[line[0:2].strip()].number data[kk]['ion'] = int(line[2:4].strip()) # wrest data[kk]['wrest'] = float(line[47:56].strip()) # name ionnm = ions.ion_to_name((data[kk]['Z'], data[kk]['ion'])) data[kk]['name'] = '{:s} {:d}'.format(ionnm, int(data[kk]['wrest'])) # Ej, Ek data[kk]['Ej'] = float(line[59:73].strip()) data[kk]['Ek'] = float(line[73:89].strip()) # gj, gk data[kk]['gj'] = int(line[89:92].strip()) data[kk]['gk'] = int(line[92:95].strip()) # Ak data[kk]['A'] = float(line[95:103].strip()) # f data[kk]['f'] = float(line[104:112].strip()) # Update data['Ref'] = 'Verner1996' # Write if write: outfil = lt_path + '/data/lines/verner96_tab1.fits' data.write(outfil,overwrite=True) print('parse_verner96: Wrote {:s}'.format(outfil)) # Compress and delete print('Now compressing...') with open(outfil) as src: with gzip.open(outfil+'.gz', 'wb') as dst: dst.writelines(src) os.unlink(outfil) # Return return data
def table_from_complist(complist): """ Returns a astropy.Table from an input list of AbsComponents. It only fills in mandatory and special attributes (see notes below). Other information stored in dictionary AbsComp.attrib is ignored. Attributes with units are stored in the Table with units Parameters ---------- complist : list of AbsComponents The initial list of AbsComponents to create the Table from. Returns ------- table : Table Table from the information contained in each component. Notes ----- Mandatory columns: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' Special columns: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN', 'b', 'sig_b', 'specfile' 'reliability' is included if provided See also complist_from_table() """ key_order = ['RA', 'DEC', 'name', 'z_comp', 'sig_z', 'Z', 'ion', 'Ej', 'vmin', 'vmax','ion_name', 'flag_N', 'logN', 'sig_logN', 'b','sig_b', 'specfile'] tab = Table() coords = SkyCoord([icomp.coord for icomp in complist]) # Basics tab['RA'] = coords.ra tab['DEC'] = coords.dec tab['name'] = [comp.name for comp in complist] tab['vmin'] = u.Quantity([icomp.vlim[0] for icomp in complist]) tab['vmax'] = u.Quantity([icomp.vlim[1] for icomp in complist]) tab['Z'] = [icomp.Zion[0] for icomp in complist] tab['ion'] = [icomp.Zion[1] for icomp in complist] # . attributes (required ones) for attrib in ['zcomp', 'Ej', 'flag_N', 'logN', 'sig_logN']: values = [getattr(icomp,attrib) for icomp in complist] if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Rename tab.rename_column('zcomp', 'z_comp') # Ion names ion_names = [] for comp in complist: if comp.Zion == (-1,-1): ion_names += ["Molecule"] else: ion_names += [ion_to_name(comp.Zion)] tab['ion_name'] = ion_names # attrib dict for attrib in ['sig_z', 'b', 'sig_b', 'specfile']: try: values = [icomp.attrib[attrib] for icomp in complist] except KeyError: key_order.pop(key_order.index(attrib)) pass else: # Quantity if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Special columns for key in ['comment', 'reliability']: if hasattr(complist[0], key): tab[key] = [getattr(comp, key) for comp in complist] key_order += [key] assert len(key_order) == len(tab.keys()) tab = tab[key_order] return tab
def __init__(self, radec, Zion, zcomp, vlim, Ej=0./u.cm, A=None, Ntup=None, comment='', name=None, stars=None, reliability='none'): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate.SkyCoord Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI Note: (-1, -1) is special and is meant for moleculer (e.g. H2) This notation will most likely change in the future. zcomp : float Absorption component redshift vlim : Quantity array Velocity limits of the component w/r to `z` e.g. [-300,300]*u.km/u.s A : int, optional Atomic mass -- used to distinguish isotopes Ntup : tuple (int,float,float) (flag_N,logN,sig_logN) flag_N : Flag describing N measurement (0: no info; 1: detection; 2: saturated; 3: non-detection) logN : log10 N column density sig_logN : Error in log10 N # TODO FUTURE IMPLEMENTATION WILL ALLOW FOR 2-element ndarray for sig_logN Ej : Quantity, optional Energy of lower level (1/cm) stars : str, optional asterisks to add to name, e.g. '**' for CI** Required if name=None and Ej>0. reliability : str, optional Reliability of AbsComponent 'a' - reliable 'b' - possible 'c' - uncertain 'none' - not defined (default) comment : str, optional A comment, default is `` """ # Required self.coord = ltu.radec_to_coord(radec) self.Zion = Zion self.zcomp = zcomp self.vlim = vlim # Optional self.A = A self.Ej = Ej self.comment = comment if Ntup is not None: self.flag_N = Ntup[0] self.logN = Ntup[1] self.sig_logN = Ntup[2] _, _ = ltaa.linear_clm(self) # Set linear quantities else: self.flag_N = 0 self.logN = 0. self.sig_logN = 0. # Name if (name is None) and (self.Zion != (-1, -1)): iname = ions.ion_to_name(self.Zion, nspace=0) if self.Ej.value > 0: # Need to put *'s in name try: iname += stars except: raise IOError("Need to provide 'stars' parameter.") self.name = '{:s}_z{:0.5f}'.format(iname, self.zcomp) elif (name is None) and (self.Zion == (-1, -1)): self.name = 'mol_z{:0.5f}'.format(self.zcomp) else: self.name = name # reliability if reliability not in ['a', 'b', 'c', 'none']: raise ValueError("Input reliability `{}` not valid.".format(reliability)) self.reliability = reliability # Potential for attributes self.attrib = dict() # Other self._abslines = []
def __init__(self, radec, Zion, zcomp, vlim, Ej=0. / u.cm, A=None, Ntup=None, comment='', name=None, stars=None, reliability='none'): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate.SkyCoord Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI Note: (-1, -1) is special and is meant for moleculer (e.g. H2) This notation will most likely change in the future. zcomp : float Absorption component redshift vlim : Quantity array Velocity limits of the component w/r to `z` e.g. [-300,300]*u.km/u.s A : int, optional Atomic mass -- used to distinguish isotopes Ntup : tuple (int,float,two-element list,tuple or array) (flag_N, logN, sig_logN) flag_N : Flag describing N measurement (0: no info; 1: detection; 2: saturated; 3: non-detection) logN : log10 N column density sig_logN : Error in log10 N. Two elements are expected but not required Ej : Quantity, optional Energy of lower level (1/cm) stars : str, optional asterisks to add to name, e.g. '**' for CI** Required if name=None and Ej>0. reliability : str, optional Reliability of AbsComponent 'a' - reliable 'b' - possible 'c' - uncertain 'none' - not defined (default) comment : str, optional A comment, default is `` """ # Required self.coord = ltu.radec_to_coord(radec) self.Zion = Zion # Limits zlim = ltu.z_from_dv(vlim, zcomp) self.limits = zLimits(zcomp, zlim.tolist()) # Attributes self.attrib = init_attrib.copy() # Optional self.A = A self.Ej = Ej self.stars = stars self.comment = comment if Ntup is not None: self.attrib['flag_N'] = Ntup[0] self.attrib['logN'] = Ntup[1] if isiterable(Ntup[2]): self.attrib['sig_logN'] = np.array(Ntup[2]) else: self.attrib['sig_logN'] = np.array([Ntup[2]] * 2) _, _ = ltaa.linear_clm(self.attrib) # Set linear quantities # Name if (name is None) and (self.Zion != (-1, -1)): iname = ions.ion_to_name(self.Zion, nspace=0) if self.Ej.value > 0: # Need to put *'s in name if stars is not None: iname += stars else: warnings.warn( "No stars provided. Adding one because Ej > 0.") iname += '*' self.name = '{:s}_z{:0.5f}'.format(iname, self.zcomp) elif (name is None) and (self.Zion == (-1, -1)): self.name = 'mol_z{:0.5f}'.format(self.zcomp) else: self.name = name # reliability if reliability not in ['a', 'b', 'c', 'none']: raise ValueError( "Input reliability `{}` not valid.".format(reliability)) self.reliability = reliability # AbsLines self._abslines = []
def repr_alis(self, T_kin=1e4*u.K, bturb=0.*u.km/u.s, tie_strs=('', '', '', ''), fix_strs=('', '', '', '')): """ String representation for ALIS (line fitting software) Parameters ---------- T_kin : Quantity, optional Kinetic temperature. Default 1e4*u.K bturb : Quantity, optional Turbulent Doppler parameter. Default 0.*u.km/u.s tie_strs : tuple of strings, optional Strings to be used for tying parameters (logN,z,bturb,T_kin), respectively. These are all converted to lower case format, following ALIS convention. fix_strs : tuple of strings, optional Strings to be used for fixing parameters (logN,z,bturb,T_kin), respectively. These are all converted to upper case format, following ALIS convention. These will take precedence over tie_strs if different from ''. Returns ------- repr_alis : str """ # Convert to the units ALIS wants T_kin = T_kin.to('K').value bturb = bturb.to('km/s').value # A patch for nucleons; todo: come up with a better way to do this using ELEMENTS? if self.Zion[0] == 1: nucleons = 1 elif self.Zion[0] > 1: nucleons = 2 * self.Zion[0] # name name = ions.ion_to_name(self.Zion, nspace=1) name = '{}'.format(nucleons)+name.replace(' ', '_') # Deal with fix and tie parameters # Check format first for i, x_strs in enumerate([tie_strs, fix_strs]): if (not isinstance(x_strs, tuple)) or (not all(isinstance(s, (str, basestring)) for s in x_strs)): if i == 0: raise TypeError('`tie_strs` must be a tuple of strings.') elif i == 1: raise TypeError('`fix_strs` must be a tuple of strings.') if len(x_strs) != 4: raise SyntaxError('`tie_strs` and `fix_strs` must have len()== 4') # reformat for ALIS standard fix_strs = np.array([s.upper() for s in fix_strs]) tie_strs = np.array([s.lower() for s in tie_strs]) # preference to fix_strs over tie_strs strs = np.where(fix_strs != '', fix_strs, tie_strs) s = 'voigt ion={:s} {:.2f}{:s} redshift={:.5f}{:s} {:.1f}{:s} {:.1E}{:s}'.format(name, self.logN, strs[0], self.zcomp, strs[1], bturb, strs[2], T_kin, strs[3]) if len(self.comment) > 0: s += '# {:s}'.format(self.comment) s += '\n' return s
def parse_verner96(orig=False, write=False): """Parse tables from Verner, Verner, & Ferland (1996, Atomic Data and Nuclear Data Tables, Vol. 64, p.1) Parameters ---------- orig : bool, optional Use original code to parse the ASCII file Else, read from a FITS file Returns ------- data : Table Atomic data """ # Look for FITS fitsf = lt_path + '/data/lines/verner96_tab1.fits.gz' verner96_tab1 = glob.glob(fitsf) if (len(verner96_tab1) > 0) & (not orig): print('linetools.lists.parse: Reading linelist --- \n {:s}'.format( verner96_tab1[0])) data = Table(Table.read(verner96_tab1[0]), masked=True) else: # File verner96_tab1 = lt_path + '/data/lines/verner96_tab1.txt' # Read with open(verner96_tab1) as f: lines = f.readlines() # Grab the 'good' ones gdlines = [ iline.strip() for iline in lines if len(iline.strip()) > 113 ] ldict, data = line_data(nrows=len(gdlines)) # Loop for kk, line in enumerate(gdlines): # Z, ion data[kk]['Z'] = ELEMENTS[line[0:2].strip()].number data[kk]['ion'] = int(line[2:4].strip()) # wrest data[kk]['wrest'] = float(line[47:56].strip()) # name ionnm = ions.ion_to_name((data[kk]['Z'], data[kk]['ion'])) data[kk]['name'] = '{:s} {:d}'.format(ionnm, int(data[kk]['wrest'])) # Ej, Ek data[kk]['Ej'] = float(line[59:73].strip()) data[kk]['Ek'] = float(line[73:89].strip()) # gj, gk data[kk]['gj'] = int(line[89:92].strip()) data[kk]['gk'] = int(line[92:95].strip()) # Ak data[kk]['A'] = float(line[95:103].strip()) # f data[kk]['f'] = float(line[104:112].strip()) # Update data['Ref'] = 'Verner1996' # Write if write: outfil = lt_path + '/data/lines/verner96_tab1.fits' data.write(outfil, overwrite=True) print('parse_verner96: Wrote {:s}'.format(outfil)) # Compress and delete print('Now compressing...') with open(outfil) as src: with gzip.open(outfil + '.gz', 'wb') as dst: dst.writelines(src) os.unlink(outfil) # Return return data
def add_abslines_from_linelist(self, llist='ISM', init_name=None, wvlim=None, min_Wr=None, **kwargs): """ It adds associated AbsLines satisfying some conditions (see parameters below). Parameters ---------- llist : str, optional Name of the linetools.lists.linelist.LineList object where to look for the transition names. Default is 'ISM', which means the function looks within `list = LineList('ISM')`. init_name : str, optional Name of the initial transition used to define the AbsComponent wvlim : Quantity array, optional Observed wavelength limits for AbsLines to be added. e.g. [1200, 2000]*u.AA. min_Wr : Quantity, optional Minimum rest-frame equivalent with for AbsLines to be added. This is calculated in the very low optical depth regime tau0<<1, where Wr is independent of Doppler parameter or gamma (see eq. 9.15 of Draine 2011). Still, a column density attribute for the AbsComponent is needed. Returns ------- Adds AbsLine objects to the AbsComponent._abslines list. Notes ----- **kwargs are passed to AbsLine.add_absline() method. """ from linetools.lists import utils as ltlu # get the transitions from LineList llist = LineList(llist) if init_name is None: # we have to guess it if (self.Zion) == (-1, -1): # molecules # init_name must be in self.attrib (this is a patch) init_name = self.attrib['init_name'] else: # atoms init_name = ions.ion_to_name(self.Zion, nspace=0) transitions = llist.all_transitions(init_name) # unify output to be a Table if isinstance(transitions, dict): transitions = ltlu.from_dict_to_table(transitions) # check wvlims if wvlim is not None: # Deal with units wrest = transitions['wrest'].data * transitions['wrest'].unit # Logic cond = (wrest*(1+self.zcomp) >= wvlim[0]) & \ (wrest*(1+self.zcomp) <= wvlim[1]) transitions = transitions[cond] # check outputs if len(transitions) == 0: warnings.warn("No transitions satisfying the criteria found. Doing nothing.") return # loop over the transitions when more than one found for transition in transitions: iline = AbsLine(transition['name'], z=self.zcomp, linelist=llist) iline.limits.set(self.vlim) iline.attrib['coord'] = self.coord iline.attrib['logN'] = self.logN iline.attrib['sig_logN'] = self.sig_logN iline.attrib['flag_N'] = self.flag_N iline.attrib['N'] = 10**iline.attrib['logN'] / (u.cm * u.cm) iline.attrib['sig_N'] = 10**iline.attrib['sig_logN'] / (u.cm * u.cm) for key in self.attrib.keys(): iline.attrib[key] = self.attrib[key] if min_Wr is not None: # check logN is defined if self.logN == 0: pass else: N = 10.**self.logN / u.cm**2 Wr_iline = iline.get_Wr_from_N(N=N) # valid for the tau0<<1 regime. if Wr_iline < min_Wr: # do not append continue # add the absline self.add_absline(iline)
def __init__(self, radec, Zion, zcomp, vlim, Ej=0./u.cm, A=None, Ntup=None, comment='', name=None, stars=None, reliability='none'): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate.SkyCoord Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI Note: (-1, -1) is special and is meant for moleculer (e.g. H2) This notation will most likely change in the future. zcomp : float Absorption component redshift vlim : Quantity array Velocity limits of the component w/r to `z` e.g. [-300,300]*u.km/u.s A : int, optional Atomic mass -- used to distinguish isotopes Ntup : tuple (int,float,two-element list,tuple or array) (flag_N, logN, sig_logN) flag_N : Flag describing N measurement (0: no info; 1: detection; 2: saturated; 3: non-detection) logN : log10 N column density sig_logN : Error in log10 N. Two elements are expected but not required Ej : Quantity, optional Energy of lower level (1/cm) stars : str, optional asterisks to add to name, e.g. '**' for CI** Required if name=None and Ej>0. reliability : str, optional Reliability of AbsComponent 'a' - reliable 'b' - possible 'c' - uncertain 'none' - not defined (default) comment : str, optional A comment, default is `` """ # Required self.coord = ltu.radec_to_coord(radec) self.Zion = Zion # Limits zlim = ltu.z_from_dv(vlim, zcomp) self.limits = zLimits(zcomp, zlim.tolist()) # Attributes self.attrib = init_attrib.copy() # Optional self.A = A self.Ej = Ej self.stars = stars self.comment = comment if Ntup is not None: self.attrib['flag_N'] = Ntup[0] self.attrib['logN'] = Ntup[1] if isiterable(Ntup[2]): self.attrib['sig_logN'] = np.array(Ntup[2]) else: self.attrib['sig_logN'] = np.array([Ntup[2]]*2) _, _ = ltaa.linear_clm(self.attrib) # Set linear quantities # Name if (name is None) and (self.Zion != (-1, -1)): iname = ions.ion_to_name(self.Zion, nspace=0) if self.Ej.value > 0: # Need to put *'s in name if stars is not None: iname += stars else: warnings.warn("No stars provided. Adding one because Ej > 0.") iname += '*' self.name = '{:s}_z{:0.5f}'.format(iname, self.zcomp) elif (name is None) and (self.Zion == (-1, -1)): self.name = 'mol_z{:0.5f}'.format(self.zcomp) else: self.name = name # reliability if reliability not in ['a', 'b', 'c', 'none']: raise ValueError("Input reliability `{}` not valid.".format(reliability)) self.reliability = reliability # AbsLines self._abslines = []
def add_abslines_from_linelist(self, llist='ISM', init_name=None, wvlim=None, min_Wr=None, **kwargs): """ It adds associated AbsLines satisfying some conditions (see parameters below). Parameters ---------- llist : str, optional Name of the linetools.lists.linelist.LineList object where to look for the transition names. Default is 'ISM', which means the function looks within `list = LineList('ISM')`. init_name : str, optional Name of the initial transition used to define the AbsComponent wvlim : Quantity array, optional Observed wavelength limits for AbsLines to be added. e.g. [1200, 2000]*u.AA. min_Wr : Quantity, optional Minimum rest-frame equivalent with for AbsLines to be added. This is calculated in the very low optical depth regime tau0<<1, where Wr is independent of Doppler parameter or gamma (see eq. 9.15 of Draine 2011). Still, a column density attribute for the AbsComponent is needed. Returns ------- Adds AbsLine objects to the AbsComponent._abslines list. Notes ----- **kwargs are passed to AbsLine.add_absline() method. """ from linetools.lists import utils as ltlu # get the transitions from LineList llist = LineList(llist) if init_name is None: # we have to guess it if (self.Zion) == (-1, -1): # molecules # init_name must be in self.attrib (this is a patch) init_name = self.attrib['init_name'] else: # atoms init_name = ions.ion_to_name(self.Zion, nspace=0) transitions = llist.all_transitions(init_name) # unify output to be a Table if isinstance(transitions, dict): transitions = ltlu.from_dict_to_table(transitions) # check wvlims if wvlim is not None: # Deal with units wrest = transitions['wrest'].data * transitions['wrest'].unit # Logic cond = (wrest*(1+self.zcomp) >= wvlim[0]) & \ (wrest*(1+self.zcomp) <= wvlim[1]) transitions = transitions[cond] # check outputs if len(transitions) == 0: warnings.warn( "No transitions satisfying the criteria found. Doing nothing.") return # loop over the transitions when more than one found for transition in transitions: iline = AbsLine(transition['name'], z=self.zcomp, linelist=llist) iline.limits.set(self.vlim) iline.attrib['coord'] = self.coord iline.attrib['logN'] = self.logN iline.attrib['sig_logN'] = self.sig_logN iline.attrib['flag_N'] = self.flag_N iline.attrib['N'] = 10**iline.attrib['logN'] / (u.cm * u.cm) iline.attrib['sig_N'] = 10**iline.attrib['sig_logN'] / (u.cm * u.cm) for key in self.attrib.keys(): iline.attrib[key] = self.attrib[key] if min_Wr is not None: # check logN is defined if self.logN == 0: pass else: N = 10.**self.logN / u.cm**2 Wr_iline = iline.get_Wr_from_N( N=N) # valid for the tau0<<1 regime. if Wr_iline < min_Wr: # do not append continue # add the absline self.add_absline(iline)
def table_from_complist(complist, summed_ion=False, NHI_obj=None, vrange=None, ztbl=None): """ Returns a astropy.Table from an input list of AbsComponents. It only fills in mandatory and special attributes (see notes below). Other information stored in dictionary AbsComp.attrib is ignored. Attributes with units are stored in the Table with units. Parameters ---------- complist : list of AbsComponents The initial list of AbsComponents to create the Table from. NHI_obj : object, optional (with NHI, sig_NHI, flag_NHI attributes) If provided, fill HI with NHI, sig_NHI, flag_NHI vrange : Quantity, optional Velocity range of components to sum column densities ztbl : float, optional Redshift to adopt for the table if summed_ion option is used Returns ------- table : Table Table from the information contained in each component. Notes ----- Mandatory columns: 'RA', 'DEC', 'ion_name', 'z_comp', 'vmin', 'vmax' Special columns: 'name', 'comment', 'logN', 'sig_logN', 'flag_logN', 'b', 'sig_b', 'specfile' 'reliability' is included if provided See also complist_from_table() """ if summed_ion is False: key_order = ['RA', 'DEC', 'comp_name', 'z_comp', 'sig_z', 'Z', 'ion', 'Ej', 'vmin', 'vmax','ion_name', 'flag_N', 'logN', 'sig_logN', 'b','sig_b', 'vel', 'sig_vel','specfile'] else: key_order = ['RA', 'DEC', 'comp_name', 'z_comp', 'sig_z', 'Z', 'ion', 'Ej', 'vmin', 'vmax', 'ion_name', 'flag_N', 'logN', 'sig_logN', 'vel','specfile'] tab = Table() # Coordinates coords = SkyCoord([icomp.coord for icomp in complist]) # Basics if hasattr(coords, 'ra'): tab['RA'] = coords.ra tab['DEC'] = coords.dec elif hasattr(coords, 'b'): tab['b_gal'] = coords.b tab['l_gal'] = coords.l # Adjust keys key_order.remove('RA') key_order.remove('DEC') key_order = ['b_gal', 'l_gal'] + key_order else: raise IOError("Not ready for this coords frame: {:s}".format(coords.frame.name)) tab['comp_name'] = [comp.name for comp in complist] tab['vmin'] = u.Quantity([icomp.vlim[0] for icomp in complist]) tab['vmax'] = u.Quantity([icomp.vlim[1] for icomp in complist]) tab['Z'] = [icomp.Zion[0] for icomp in complist] tab['ion'] = [icomp.Zion[1] for icomp in complist] # . attributes (required ones) for attrib in ['zcomp', 'Ej', 'flag_N', 'logN', 'sig_logN', 'b','sig_b','vel','sig_vel']: values = [getattr(icomp,attrib) for icomp in complist] if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Rename tab.rename_column('zcomp', 'z_comp') # Ion names ion_names = [] for comp in complist: if comp.Zion == (-1,-1): ion_names += ["Molecule"] else: ion_names += [ion_to_name(comp.Zion)] if comp.Ej.value > 0.: # Slightly kludgy ion_names[-1] += '*' tab['ion_name'] = ion_names # attrib dict for attrib in ['sig_z', 'b', 'sig_b', 'vel', 'sig_vel', 'specfile']: try: values = [icomp.attrib[attrib] for icomp in complist] except KeyError: key_order.pop(key_order.index(attrib)) pass else: # Quantity if isinstance(values[0], u.Quantity): values = u.Quantity(values) tab[attrib] = values # Special columns for key in ['comment', 'reliability']: if hasattr(complist[0], key): tab[key] = [getattr(comp, key) for comp in complist] key_order += [key] #assert len(key_order) == len(tab.keys()) # We allowed keys to be popped! tab = tab[key_order] # May need to add an HI component here as done in the method below if NHI_obj is not None: mt = np.where((tab['Z'] == 1) & (tab['ion'] == 1))[0] # Existing row in Table? if len(mt) == 1: tab[mt[0]]['logN'] = NHI_obj.NHI tab[mt[0]]['sig_logN'] = np.mean(NHI_obj.sig_NHI) # Allow for two values tab[mt[0]]['flag_N'] = NHI_obj.flag_NHI else: # Add a dummy row tab.add_row(tab[0]) tab['logN'][-1] = NHI_obj.NHI tab['sig_logN'][-1] = np.mean(NHI_obj.sig_NHI) # Allow for two values tab['flag_N'][-1] = NHI_obj.flag_NHI tab['Z'][-1] = 1 tab['ion'][-1] = 1 tab['Ej'][-1] = 0. tab['ion_name'][-1] = 'HI' tab['comp_name'][-1] = 'HI_z{:0.5f}'.format(tab['z_comp'][-1]) if summed_ion is False: return tab else: ### Perform logic: ### - find unique ions # Identify unique Zion, Ej (not ready for A) uqions, uiidx = np.unique(tab['ion_name'], return_index=True) ### - select on velocity if vrange is not None: vrange = vrange.to(u.km / u.s).value ### - synthesize column densities # Loop count = 0 for i,ui in enumerate(uqions): # Grab velocities from components compvels = tab['vel'] # Synthesize components with like Zion, Ej, and in vrange if vrange is not None: try: thesecomps = np.where((tab['ion_name'] == ui) & (compvels > vrange[0]) & (compvels < vrange[1]))[0] except: import pdb; pdb.set_trace() else: thesecomps = np.where(tab['ion_name'] == ui)[0] comps = [complist[ii] for ii in thesecomps] # Need a list if len(comps) > 0: synth_comp = synthesize_components(comps, zcomp=ztbl) # Add a row to Table tab.add_row(tab[thesecomps[0]]) tab['logN'][-1] = synth_comp.logN tab['sig_logN'][-1] = synth_comp.sig_logN # Allow for two values tab['flag_N'][-1] = synth_comp.flag_N tab['vmin'][-1] = synth_comp.vlim.value[0] tab['vmax'][-1] = synth_comp.vlim.value[1] count += 1 #tab['comp_name'][-1] = 'HI_z{:0.5f}'.format(tab['z_comp'][-1]) else: print('No components found within velocity range found.') ### - Create new table and return summed_tab = tab[-count:] # We needed component velocities for vrange selection, but # they are meaningless for summed ion info summed_tab.remove_column('vel') return summed_tab
def repr_alis(self, T_kin=1e4 * u.K, bturb=0. * u.km / u.s, tie_strs=('', '', '', ''), fix_strs=('', '', '', '')): """ String representation for ALIS (line fitting software) Parameters ---------- T_kin : Quantity, optional Kinetic temperature. Default 1e4*u.K bturb : Quantity, optional Turbulent Doppler parameter. Default 0.*u.km/u.s tie_strs : tuple of strings, optional Strings to be used for tying parameters (logN,z,bturb,T_kin), respectively. These are all converted to lower case format, following ALIS convention. fix_strs : tuple of strings, optional Strings to be used for fixing parameters (logN,z,bturb,T_kin), respectively. These are all converted to upper case format, following ALIS convention. These will take precedence over tie_strs if different from ''. Returns ------- repr_alis : str """ # Convert to the units ALIS wants T_kin = T_kin.to('K').value bturb = bturb.to('km/s').value # A patch for nucleons; todo: come up with a better way to do this using ELEMENTS? if self.Zion[0] == 1: nucleons = 1 elif self.Zion[0] > 1: nucleons = 2 * self.Zion[0] # name name = ions.ion_to_name(self.Zion, nspace=1) name = '{}'.format(nucleons) + name.replace(' ', '_') # Deal with fix and tie parameters # Check format first for i, x_strs in enumerate([tie_strs, fix_strs]): if (not isinstance(x_strs, tuple)) or (not all( isinstance(s, (str, basestring)) for s in x_strs)): if i == 0: raise TypeError('`tie_strs` must be a tuple of strings.') elif i == 1: raise TypeError('`fix_strs` must be a tuple of strings.') if len(x_strs) != 4: raise SyntaxError( '`tie_strs` and `fix_strs` must have len()== 4') # reformat for ALIS standard fix_strs = np.array([s.upper() for s in fix_strs]) tie_strs = np.array([s.lower() for s in tie_strs]) # preference to fix_strs over tie_strs strs = np.where(fix_strs != '', fix_strs, tie_strs) s = 'voigt ion={:s} {:.2f}{:s} redshift={:.5f}{:s} {:.1f}{:s} {:.1E}{:s}'.format( name, self.logN, strs[0], self.zcomp, strs[1], bturb, strs[2], T_kin, strs[3]) if len(self.comment) > 0: s += '# {:s}'.format(self.comment) s += '\n' return s
def ions(self, Zion, Ej=0., skip_null=True, pad_with_nulls=False): """ Generate a Table of columns and so on Restrict to those systems where flg_clm > 0 Parameters ---------- Zion : tuple Z, ion e.g. (6,4) for CIV Ej : float [1/cm] Energy of the lower level (0. is resonance) skip_null : boolean (False) Skip systems without an entry, else pad with zeros pad_with_nulls : bool, optional Pad missing/null systems with empty values. A bit risky Returns ------- tbl : MaskedTable of values for the Survey Systems without the ion have rows masked """ from linetools.abund.ions import ion_to_name if self._abs_sys[0]._ionN is None: raise IOError("ionN tables are not set. Use fill_ionN") # Loop me! tbls = [] names = [] for kk, abs_sys in enumerate(self._abs_sys): if len(abs_sys._ionN) == 0: names.append('MASK_ME') tbls.append(None) continue # Parse mt = (abs_sys._ionN['Z'] == Zion[0]) & ( abs_sys._ionN['ion'] == Zion[1]) & (abs_sys._ionN['Ej'] == Ej) if np.any(mt): if np.sum(mt) > 1: # Generally should not get here warnings.warn( "Two components for ion {} for system {}. Taking the first one" .format(Zion, abs_sys)) mt[np.where(mt)[0][1:]] = False tbls.append(abs_sys._ionN[mt]) names.append(abs_sys.name) else: if skip_null is True: # This is probably dangerous continue else: if pad_with_nulls: nulltbl = abs_sys._ionN[:0].copy() datatoadd = [ abs_sys.coord.ra.deg, abs_sys.coord.dec.deg, 'none', Zion[0], Zion[1], Ej, abs_sys.limits.vmin.value, abs_sys.limits.vmax.value, ion_to_name(Zion), 0, 0, 0, '', 'none', abs_sys.zabs ] nulltbl['ion_name'].dtype = '<U6' nulltbl.add_row(datatoadd) tbls.append(nulltbl) names.append(abs_sys.name) else: tbls.append(None) names.append('MASK_ME') # Fill in the bad ones names = np.array(names) idx = np.where(names != 'MASK_ME')[0] if len(idx) == 0: warnings.warn( "There were no entries matching your input Ion={}".format( Zion)) return None bad = np.where(names == 'MASK_ME')[0] for ibad in bad: tbls[ibad] = tbls[idx[0]] # Stack me try: tbl = vstack(tbls) except: pdb.set_trace() tbl['abssys_name'] = names # Mask tbl = Table(tbl, masked=True) mask = names == 'MASK_ME' for key in tbl.keys(): if key == 'flag_N': tbl[key][mask] = 0 else: tbl[key].mask = mask ''' # keys = [u'abssys_name', ] + list(self._abs_sys[kk]._ionN.keys()) t = Table(self._abs_sys[kk]._ionN[0:1]).copy() # Avoids mixin trouble t.add_column(Column(['dum']*len(t), name='name', dtype='<U32')) t = t[keys] if 'Ej' not in keys: warnings.warn("Ej not in your ionN table. Ignoring. Be careful..") # Loop on systems (Masked) for abs_sys in self._abs_sys: # Grab if 'Ej' in keys: mt = ((abs_sys._ionN['Z'] == iZion[0]) & (abs_sys._ionN['ion'] == iZion[1]) & (abs_sys._ionN['Ej'] == Ej)) else: mt = ((abs_sys._ionN['Z'] == iZion[0]) & (abs_sys._ionN['ion'] == iZion[1])) if np.sum(mt) == 1: irow = abs_sys._ionN[mt] # Cut on flg_clm if irow['flag_N'] > 0: row = [abs_sys.name] + [irow[key][0] for key in keys[1:]] t.add_row(row) # This could be slow else: if skip_null is False: row = [abs_sys.name] + [0 for key in keys[1:]] t.add_row(row) elif np.sum(mt) == 0: if skip_null is False: row = [abs_sys.name] + [0 for key in keys[1:]] t.add_row( row ) continue else: pdb.set_trace() raise ValueError("Multple entries") ''' # Reorder all_keys = list(tbl.keys()) all_keys.remove('abssys_name') all_keys = ['abssys_name'] + all_keys # Return return tbl[all_keys]