def test_ion_to_name(): # Normal ionnm = ions.ion_name((6,2)) assert ionnm == 'CII' # Latex ionnm = ions.ion_name((6,2),flg=1) assert ionnm == '{\\rm C}^{+}'
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_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_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 mk_json_ions(dlas, prefix, outfil): """ Generate a JSON table of the Ion database Parameters ---------- dlas : DLASurvey prefix : str outfil : str Output JSON file """ # Sort ra = dlas.coord.ra.degree[0] srt = np.argsort(np.array(ra)) all_ions = {} # Loop on DLA for jj, isrt in enumerate(srt): idla = dlas._abs_sys[isrt] # Astropy Table ion_tab = idla._ionN # Convert key to standard names new_dict = {} for row in ion_tab: Zion = (row['Z'], row['ion']) # Skip HI if Zion == (1, 1): continue # Get name new_key = ltai.ion_name(Zion) # Fine structure? if row['Ej'] > 0.: new_key = new_key + '*' new_dict[new_key] = dict(zip(row.dtype.names, row)) # Write to all_ions name = survey_name(prefix, idla) all_ions[name] = new_dict # Write print('Writing {:s}'.format(outfil)) with io.open(outfil, 'w', encoding='utf-8') as f: f.write( unicode( json.dumps(all_ions, sort_keys=True, indent=4, separators=(',', ': ')))) # Return return all_ions
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_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 mk_json_ions(dlas, prefix, outfil): """ Generate a JSON table of the Ion database Parameters ---------- dlas : DLASurvey prefix : str outfil : str Output JSON file """ # Sort ra = dlas.coord.ra.degree[0] srt = np.argsort(np.array(ra)) all_ions = {} # Loop on DLA for jj, isrt in enumerate(srt): idla = dlas._abs_sys[isrt] # Astropy Table ion_tab = idla._ionN # Convert key to standard names new_dict = {} for row in ion_tab: Zion = (row['Z'], row['ion']) # Skip HI if Zion == (1,1): continue # Get name new_key = ltai.ion_name(Zion) # Fine structure? if row['Ej'] > 0.: new_key = new_key+'*' new_dict[new_key] = dict(zip(row.dtype.names, row)) # Write to all_ions name = survey_name(prefix, idla) all_ions[name] = new_dict # Write print('Writing {:s}'.format(outfil)) with io.open(outfil, 'w', encoding='utf-8') as f: f.write(unicode(json.dumps(all_ions, sort_keys=True, indent=4, separators=(',', ': ')))) # Return return all_ions
def test_ion_to_name(): # Normal ionnm = ions.ion_name((6, 2)) assert ionnm == 'CII' # Latex ionnm = ions.ion_name((6, 2), flg=1) assert ionnm == '{\\rm C}^{+}' # as dict ion = dict(ion=2, Z=6) ionnm = ions.ion_name(ion) assert ionnm == 'CII' # latex not ready yet with pytest.raises(ValueError): ionnm = ions.ion_name((6, 0), flg=1) for ii in [1, 2, 3, 4]: ionnm = ions.ion_name((6, ii), flg=1) # bad flag with pytest.raises(ValueError): ionnm = ions.ion_name((6, 2), flg=99)
def test_ion_to_name(): # Normal ionnm = ions.ion_name((6,2)) assert ionnm == 'CII' # Latex ionnm = ions.ion_name((6,2), flg=1) assert ionnm == '{\\rm C}^{+}' # as dict ion = dict(ion=2, Z=6) ionnm = ions.ion_name(ion) assert ionnm == 'CII' # latex not ready yet with pytest.raises(ValueError): ionnm = ions.ion_name((6,0), flg=1) for ii in [1,2,3,4]: ionnm = ions.ion_name((6,ii), flg=1) # bad flag with pytest.raises(ValueError): ionnm = ions.ion_name((6,2), flg=99)
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_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 __init__(self, radec, Zion, zcomp, vlim, Ej=0./u.cm, A=None, Ntup=None, comment='', name=None, stars=None): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI 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_N) flag_N : Flag describing N measurement logN : log10 N column density sig_logN : Error in log10 N 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. comment : str, optional A comment, default is `` """ # Required if isinstance(radec, (tuple)): self.coord = SkyCoord(ra=radec[0], dec=radec[1]) elif isinstance(radec, SkyCoord): self.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: iname = ions.ion_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) else: self.name = name # Other self._abslines = []
def add_abslines_from_linelist(self, llist='ISM', wvlim=None, min_Wr=None, **kwargs): """ It adds associated AbsLines satisfying some conditions (see parameters below). Parameters ---------- llist : str 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')`. wvlims : 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. """ # get the transitions from LineList llist = LineList(llist) name = ions.ion_name(self.Zion, nspace=0) transitions = llist.all_transitions(name) # unify output to be always QTable if isinstance(transitions, dict): transitions = llist.from_dict_to_qtable(transitions) # check wvlims if wvlim is not None: cond = (transitions['wrest']*(1+self.zcomp) >= wvlim[0]) & \ (transitions['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) 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 logN = self.logN if logN == 0: warnings.warn("AbsComponent does not have logN defined. Appending AbsLines " "regardless of min_Wr.") else: N = 10**logN / (u.cm*u.cm) 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): """ 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. 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_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 # Potential for attributes self.attrib = dict() # Other 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_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 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 wvlims : 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. """ # 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_name(self.Zion, nspace=0) transitions = llist.all_transitions(init_name) # unify output to be always QTable if isinstance(transitions, dict): transitions = llist.from_dict_to_qtable(transitions) # check wvlims if wvlim is not None: cond = (transitions['wrest']*(1+self.zcomp) >= wvlim[0]) & \ (transitions['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 logN = self.logN if logN == 0: warnings.warn( "AbsComponent does not have logN defined. Appending AbsLines " "regardless of min_Wr.") else: N = 10**logN / (u.cm * u.cm) 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): """ 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 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. 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_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 # Potential for attributes self.attrib = dict() # Other self._abslines = []