Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
0
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
Beispiel #10
0
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
Beispiel #11
0
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
Beispiel #12
0
    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 = []
Beispiel #13
0
    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 = []
Beispiel #14
0
    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
Beispiel #15
0
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
Beispiel #16
0
    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)
Beispiel #17
0
    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 = []
Beispiel #18
0
    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)
Beispiel #19
0
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
Beispiel #20
0
    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
Beispiel #21
0
    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]