Esempio n. 1
0
    def flux_model(self, spec, smooth=0):
        """ Generate a LLS model given an input spectrum

        Parameters
        ----------
        spec :  Spectrum1D
        smooth : int, optional
          Number of pixels to smooth by

        Returns
        -------
        model : XSpectrum1D
          Output model is passed back as a Spectrum 
        """
        from linetools.analysis import voigt as lav

        # Energies in LLS rest-frame
        wv_rest = spec.wavelength / (self.zabs + 1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())

        # Get photo_cross and calculate tau
        tau_LL = (10.**self.NHI / u.cm**2) * ltaa.photo_cross(1, 1, energy)

        # Check for lines
        if 'lls_lines' not in self.__dict__.keys():
            self.fill_lls_lines()

        tau_Lyman = lav.voigt_from_abslines(spec.wavelength,
                                            self.lls_lines,
                                            ret='tau')

        # Combine
        tau_model = tau_LL + tau_Lyman

        # Kludge around the limit
        pix_LL = np.argmin(np.fabs(wv_rest - 911.3 * u.AA))
        pix_kludge = np.where((wv_rest > 911.5 * u.AA)
                              & (wv_rest < 912.8 * u.AA))[0]
        tau_model[pix_kludge] = tau_model[pix_LL]

        # Fill in flux
        model = spec.copy()
        model.flux = np.exp(-1. * tau_model).value

        # Smooth?
        if smooth > 0:
            model.gauss_smooth(smooth)

        # Return
        return model
Esempio n. 2
0
    def __init__(self, radec, zabs, vlim, **kwargs):
        """Standard init
        NHI keyword is required

        Parameters
        ----------
        radec : tuple or coordinate
            RA/Dec of the sightline or astropy.coordinate
        zabs : float
          Absorption redshift
        vlim : Quantity array (2)
          Velocity limits of the system
          Defaulted to +/- 500 km/s if None (see Prochaska et al. 2016 HDLLS)
        NHI= : float, required despite being a keyword
          log10 of HI column density
        **kwargs : keywords
          passed to IGMSystem.__init__
        """
        # NHI
        try:
            NHI = kwargs['NHI']
        except KeyError:
            raise ValueError("NHI must be specified for LLSSystem")
        else:
            kwargs.pop('NHI')
        # vlim
        if vlim is None:
            vlim = [-500., 500.] * u.km / u.s
        # Generate with type
        IGMSystem.__init__(self,
                           radec,
                           zabs,
                           vlim,
                           NHI=NHI,
                           abs_type='LLS',
                           **kwargs)

        # Set tau_LL
        self.tau_LL = (10.**self.NHI) * ltaa.photo_cross(
            1, 1, 1 * u.Ry).to('cm**2').value

        # Other
        self.zpeak = None  # Optical depth weighted redshift
        self.ZH = 0.
        self.metallicity = None  # MetallicityPDF class usually

        # Subsystems
        self.nsub = 0
        self.subsys = {}
Esempio n. 3
0
    def flux_model(self, spec, smooth=0):
        """ Generate a LLS model given an input spectrum

        Parameters
        ----------
        spec :  Spectrum1D
        smooth : int, optional
          Number of pixels to smooth by

        Returns
        -------
        model : XSpectrum1D
          Output model is passed back as a Spectrum 
        """
        from linetools.analysis import voigt as lav

        # Energies in LLS rest-frame
        wv_rest = spec.dispersion / (self.zabs+1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())

        # Get photo_cross and calculate tau
        tau_LL = (10.**self.NHI / u.cm**2) * ltaa.photo_cross(1, 1, energy)

        # Check for lines
        if 'lls_lines' not in self.__dict__.keys():
            self.fill_lls_lines()

        tau_Lyman = lav.voigt_from_abslines(spec.dispersion, self.lls_lines, ret='tau')

        # Combine
        tau_model = tau_LL + tau_Lyman

        # Kludge around the limit
        pix_LL = np.argmin(np.fabs( wv_rest- 911.3*u.AA))
        pix_kludge = np.where((wv_rest > 911.5*u.AA) & (wv_rest < 912.8*u.AA))[0]
        tau_model[pix_kludge] = tau_model[pix_LL]
        
        # Fill in flux
        model = spec.copy()
        model.flux = np.exp(-1. * tau_model).value

        # Smooth?
        if smooth > 0:
            model.gauss_smooth(smooth)

        # Return
        return model
Esempio n. 4
0
def generate_tau(iwave, HIlines, HI_comps, kludge=True):
    """Generate optical depth array given lines and components

    Parameters:
    -----------

    iwave : Quantity array
      Input Spectrum wavelengths
    HIlines : list of AbsLines
    HI_comps : QTable of components
    kludge : bool, optional
      Kludge the opacity

    Returns:
    --------
    tau : ndarray
      Optical depth at subgrid wavelengths.  Will need to rebin back
    """
    # Rebin to subgrid

    wmin = np.min(iwave.to('AA').value)
    wmax = np.max(iwave.to('AA').value)
    nsub = int(np.round((np.log10(wmax) - np.log10(wmin)) / 1.449E-6)) + 1
    wave = 10.**(np.log10(wmin) + np.arange(nsub) * 1.449E-6) * u.AA

    # Voigt for Lyman series
    tau_Lyman = lav.voigt_from_abslines(wave, HIlines, fwhm=0., ret='tau')

    # Continuum opacity
    LL_comps = HI_comps['lgNHI'] > 15.0
    tau_LL = np.zeros(wave.size)
    for row in HI_comps[LL_comps]:
        # Energies in LLS rest-frame
        wv_rest = wave / (row['z'] + 1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())
        # Get photo_cross and calculate tau
        itau_LL = (10.**row['lgNHI'] / u.cm**2) * photo_cross(1, 1, energy)
        # Kludge around the limit
        if kludge:
            pix_LL = np.argmin(np.fabs(wv_rest - 911.3 * u.AA))
            pix_kludge = np.where((wv_rest > 911.5 * u.AA)
                                  & (wv_rest < 912.8 * u.AA))[0]
            itau_LL[pix_kludge] = itau_LL[pix_LL]
        # Sum
        tau_LL += itau_LL.decompose().value
    # Total
    return wave, tau_LL + tau_Lyman
Esempio n. 5
0
def generate_tau(iwave, HIlines, HI_comps, kludge=True):
    """Generate optical depth array given lines and components

    Parameters:
    -----------

    iwave : Quantity array
      Input Spectrum wavelengths
    HIlines : list of AbsLines
    HI_comps : QTable of components
    kludge : bool, optional
      Kludge the opacity

    Returns:
    --------
    tau : ndarray
      Optical depth at subgrid wavelengths.  Will need to rebin back
    """
    # Rebin to subgrid

    wmin = np.min(iwave.to('AA').value)
    wmax = np.max(iwave.to('AA').value)
    nsub = int(np.round( (np.log10(wmax)- np.log10(wmin)) / 1.449E-6)) + 1
    wave = 10.**(np.log10(wmin) + np.arange(nsub)*1.449E-6) * u.AA

    # Voigt for Lyman series
    tau_Lyman = lav.voigt_from_abslines(wave,HIlines,fwhm=0.,ret='tau')

    # Continuum opacity
    LL_comps = HI_comps['lgNHI'] > 15.0
    tau_LL = np.zeros(wave.size)
    for row in HI_comps[LL_comps]:
        # Energies in LLS rest-frame
        wv_rest = wave / (row['z']+1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())
        # Get photo_cross and calculate tau
        itau_LL = (10.**row['lgNHI'] / u.cm**2) * photo_cross(1,1,energy)
        # Kludge around the limit
        if kludge:
            pix_LL = np.argmin(np.fabs(wv_rest- 911.3*u.AA))
            pix_kludge = np.where((wv_rest > 911.5*u.AA) & (wv_rest < 912.8*u.AA))[0]
            itau_LL[pix_kludge] = itau_LL[pix_LL]
        # Sum
        tau_LL += itau_LL
    # Total
    return wave, tau_LL + tau_Lyman
Esempio n. 6
0
def tau_multi_lls(wave, all_lls, **kwargs):
    """Calculate opacities on an input observed wavelength grid

    Parameters
    ----------
    wave : Quantity array
      Wavelengths
    all_lls : list
      List of LLS Class
    **kwargs : dict
      extra keywords go to lav.voigt_from_abslines

    Returns
    -------
    tau : ndarray
      Optical depth values at input wavelengths
    """
    from linetools.analysis import voigt as lav
    #
    all_tau_model = np.zeros(len(wave))
    # Loop on LLS
    for lls in all_lls:
        # LL
        wv_rest = wave / (lls.zabs + 1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())
        # Get photo_cross and calculate tau
        tau_LL = (10.**lls.NHI / u.cm**2) * ltaa.photo_cross(1, 1, energy)

        # Lyman
        tau_Lyman = lav.voigt_from_abslines(wave,
                                            lls.lls_lines,
                                            ret='tau',
                                            **kwargs)
        tau_model = tau_LL + tau_Lyman

        # Kludge around the limit
        pix_LL = np.argmin(np.fabs(wv_rest - 911.3 * u.AA))
        pix_kludge = np.where((wv_rest > 911.5 * u.AA)
                              & (wv_rest < 912.8 * u.AA))[0]
        tau_model[pix_kludge] = tau_model[pix_LL]
        # Add
        all_tau_model += tau_model
    # Return
    return all_tau_model
Esempio n. 7
0
    def __init__(self, radec, zabs, vlim, **kwargs):
        """Standard init

        NHI keyword is required

        Parameters
        ----------
        radec : tuple or coordinate
            RA/Dec of the sightline or astropy.coordinate
        zabs : float
          Absorption redshift
        vlim : Quantity array (2)
          Velocity limits of the system
          Defaulted to +/- 500 km/s if None (see Prochaska et al. 2016 HDLLS)
        NHI= : float, required despite being a keyword
          log10 of HI column density
        **kwargs : keywords
          passed to IGMSystem.__init__
        """
        # NHI
        try:
            NHI = kwargs['NHI']
        except KeyError:
            raise ValueError("NHI must be specified for LLSSystem")
        else:
            kwargs.pop('NHI')
        # vlim
        if vlim is None:
            vlim = [-500.,500.]*u.km/u.s
        # Generate with type
        IGMSystem.__init__(self, 'LLS', radec, zabs, vlim, NHI=NHI, **kwargs)

        # Set tau_LL
        self.tau_LL = (10.**self.NHI)*ltaa.photo_cross(1, 1, 1*u.Ry).to('cm**2').value

        # Other
        self.zpeak = None  # Optical depth weighted redshift
        self.ZH = 0.
        self.metallicity = None  # MetallicityPDF class usually

        # Subsystems
        self.nsub = 0
        self.subsys = {}
Esempio n. 8
0
def tau_multi_lls(wave, all_lls, **kwargs):
    """Calculate opacities on an input observed wavelength grid

    Parameters
    ----------
    wave : Quantity array
      Wavelengths
    all_lls : list
      List of LLS Class
    **kwargs : dict
      extra keywords go to lav.voigt_from_abslines

    Returns
    -------
    tau : ndarray
      Optical depth values at input wavelengths
    """
    from linetools.analysis import voigt as lav
    #
    all_tau_model = np.zeros(len(wave))
    # Loop on LLS
    for lls in all_lls:
        # LL
        wv_rest = wave / (lls.zabs+1)
        energy = wv_rest.to(u.eV, equivalencies=u.spectral())
        # Get photo_cross and calculate tau
        tau_LL = (10.**lls.NHI / u.cm**2) * ltaa.photo_cross(1,1,energy)

        # Lyman
        tau_Lyman = lav.voigt_from_abslines(wave, lls.lls_lines, ret='tau', **kwargs)
        tau_model = tau_LL + tau_Lyman

        # Kludge around the limit
        pix_LL = np.argmin(np.fabs( wv_rest- 911.3*u.AA ))
        pix_kludge = np.where((wv_rest > 911.5*u.AA) & (wv_rest < 912.8*u.AA))[0]
        tau_model[pix_kludge] = tau_model[pix_LL]
        # Add
        all_tau_model += tau_model
    # Return
    return all_tau_model
Esempio n. 9
0
def test_photocross():
    phto = photo_cross(1, 1, 14.*u.eV)
    assert phto.unit == u.cm**2
    np.testing.assert_allclose(phto.value, 5.870146496955153e-18)
Esempio n. 10
0
def hi_model(abssys,
             spec,
             lya_only=False,
             add_lls=False,
             ret_tau=False,
             ignore_abslines=False,
             bval=30 * u.km / u.s,
             **kwargs):
    """ Generate a model of the absorption from the absorption system
    on an input spectrum.

    Parameters
    ----------
    abssys : AbsSystem or list
      If list, must be a list of AbsSystem's
    spec : XSpectrum1D
    lya_only : bool, optional
      Only generate Lya
    ignore_abslines : bool, optional
      Ignore any existing abslines in the object
      NHI tag must be set
    add_lls : bool, optional
      Add Lyman continuum absorption
    bval : Quantity, optional
      Doppler parameter to use if abslines not adopted
    ret_tau : bool, optional
      Return only the optical depth (used for multiple systems)
    kwargs :
      Passed to voigt_from_abslines

    Returns
    -------
    vmodel : XSpectrum1D or ndarray
      Model spectrum with same wavelength as input spectrum
      Assumes a normalized flux
      Or optical depth array [used for multiple systems]
    lyman_lines : list
      List of AbsLine's that contributed to the model

    """
    from astropy.units import Quantity
    from linetools.spectra.xspectrum1d import XSpectrum1D
    from linetools.spectralline import AbsLine
    from linetools.analysis.voigt import voigt_from_abslines
    from linetools.analysis.absline import photo_cross
    # Input
    if isinstance(abssys, list):
        tau = None
        all_lines = []
        for iabssys in abssys:
            itau, ly_lines = hi_model(iabssys,
                                      spec,
                                      lya_only=lya_only,
                                      add_lls=add_lls,
                                      ignore_abslines=ignore_abslines,
                                      bval=bval,
                                      ret_tau=True,
                                      **kwargs)
            all_lines += ly_lines
            if tau is None:
                tau = itau
            else:
                tau += itau
        # Flux
        flux = np.exp(-1 * tau)
        vmodel = XSpectrum1D.from_tuple((spec.wavelength, flux))
        return vmodel, all_lines
    else:
        # Scan abs lines
        if not ignore_abslines:
            alines = []
        else:
            alines = abssys.list_of_abslines()
        lyman_lines = []
        lya_lines = []
        logNHIs = []
        # Scan alines
        for aline in alines:
            # Lya
            if aline.name == 'HI 1215':
                lya_lines.append(aline)
                logNHIs.append(np.log10(aline.attrib['N'].value))
            # Any HI
            if 'HI' in aline.name:
                lyman_lines.append(aline)
        if len(lya_lines) > 0:  # Use the lines
            # Check we have a DLA worth
            if np.log10(np.sum(10**np.array(logNHIs))) < abssys.NHI:
                raise ValueError(
                    "Total NHI of the Lya lines is less than NHI of the system!  Something is wrong.."
                )
        else:  # Generate one
            warnings.warn(
                "Generating the absorption lines from the system info, not abslines"
            )
            if lya_only:
                lya_line = AbsLine('HI 1215', z=abssys.zabs)
                lya_line.attrib['N'] = 10**abssys.NHI / u.cm**2
                lya_line.attrib['b'] = bval
                lyman_lines.append(lya_line)
            else:
                HIlines = LineList('HI')
                wrest = Quantity(HIlines._data['wrest'])
                for iwrest in wrest:
                    # On the spectrum?
                    if iwrest >= spec.wvmin / (1 + abssys.zabs):
                        lyman_line = AbsLine(iwrest,
                                             linelist=HIlines,
                                             z=abssys.zabs)
                        lyman_line.attrib['N'] = 10**abssys.NHI / u.cm**2
                        lyman_line.attrib['b'] = bval
                        lyman_lines.append(lyman_line)
        # tau for abs lines
        if len(lyman_lines) == 0:
            pdb.set_trace()
        tau_Lyman = voigt_from_abslines(spec.wavelength,
                                        lyman_lines,
                                        ret='tau',
                                        **kwargs)
        # LLS?
        if add_lls:
            wv_rest = spec.wavelength / (1 + abssys.zabs)
            energy = wv_rest.to(u.eV, equivalencies=u.spectral())
            # Get photo_cross and calculate tau
            tau_LL = (10.**abssys.NHI / u.cm**2) * photo_cross(1, 1, energy)
            # Kludge
            pix_LL = np.argmin(np.fabs(wv_rest - 911.3 * u.AA))
            pix_kludge = np.where((wv_rest > 911.3 * u.AA)
                                  & (wv_rest < 913.0 * u.AA))[0]
            tau_LL[pix_kludge] = tau_LL[pix_LL]
            tau_Lyman[pix_kludge] = 0.
            # Generate the spectrum
        else:
            tau_LL = 0.
        # Sum
        tau_tot = tau_LL + tau_Lyman
        if ret_tau:
            vmodel = tau_tot
        else:
            flux = np.exp(-1 * tau_tot)
            vmodel = XSpectrum1D.from_tuple((spec.wavelength, flux))
        # Return
        return vmodel, lyman_lines
Esempio n. 11
0
def lyman_limit(fN_model, z912, zem, N_eval=5000, cosmo=None, debug=False):
    """ Calculate teff_LL

    Effective opacity from LL absorption at z912 from zem

    Parameters
    ----------
    fN_model : FNModel
      f(N) model
    z912 : float
      Redshift for evaluation
    zem : float
      Redshift of source
    cosmo : astropy.cosmology, optional
      Cosmological model to adopt (as needed)
    N_eval : int, optional
      Discretization parameter (5000)
    debug : bool, optional

    Returns
    -------
    zval : array
    teff_LL : array
      z values and Effective opacity from LL absorption from z912 to zem
    """
    if not isinstance(fN_model,FNModel):
        raise IOError("Improper model")
    # NHI array
    lgNval = 11.5 + 10.5*np.arange(N_eval)/(N_eval-1.)  #; This is base 10 [Max at 22]
    dlgN = lgNval[1]-lgNval[0]
    Nval = 10.**lgNval

    # z array
    zval = z912 + (zem-z912)*np.arange(N_eval)/(N_eval-1.)
    dz = np.fabs(zval[1]-zval[0])

    teff_LL = np.zeros(N_eval)

    # dXdz
    dXdz = pyigmu.cosm_xz(zval, cosmo=cosmo, flg_return=1)

    # Evaluate f(N,X)
    velo = (zval-zem)/(1+zem) * (const.c.cgs.value/1e5)  # Kludge for eval [km/s]

    log_fnX = fN_model.evaluate(lgNval, zem, cosmo=cosmo, vel_array=velo)
    log_fnz = log_fnX + np.outer(np.ones(N_eval), np.log10(dXdz))

    # Evaluate tau(z,N)
    teff_engy = (const.Ryd.to(u.eV, equivalencies=u.spectral()) /
                 ((1+zval)/(1+zem)))
    sigma_z = ltaa.photo_cross(1, 1, teff_engy)
    tau_zN = np.outer(Nval, sigma_z)

    # Integrand
    intg = 10.**(log_fnz) * (1. - np.exp(-1.*tau_zN))

    # Sum
    sumz_first = False
    if sumz_first is False:
        #; Sum in N first
        N_summed = np.sum(intg * np.outer(Nval, np.ones(N_eval)),  0) * dlgN * np.log(10.)
        # Sum in z
        teff_LL = (np.cumsum(N_summed[::-1]))[::-1] * dz

    # Debug
    if debug is True:
        pdb.set_trace()
    # Return
    return zval, teff_LL
Esempio n. 12
0
def lyman_limit(fN_model, z912, zem, N_eval=5000, cosmo=None, debug=False):
    """ Calculate teff_LL

    Effective opacity from LL absorption at z912 from zem

    Parameters
    ----------
    fN_model : FNModel
      f(N) model
    z912 : float
      Redshift for evaluation
    zem : float
      Redshift of source
    cosmo : astropy.cosmology, optional
      Cosmological model to adopt (as needed)
    N_eval : int, optional
      Discretization parameter (5000)
    debug : bool, optional

    Returns
    -------
    zval : array
    teff_LL : array
      z values and Effective opacity from LL absorption from z912 to zem
    """
    if not isinstance(fN_model,FNModel):
        raise IOError("Improper model")
    # NHI array
    lgNval = 11.5 + 10.5*np.arange(N_eval)/(N_eval-1.)  #; This is base 10 [Max at 22]
    dlgN = lgNval[1]-lgNval[0]
    Nval = 10.**lgNval

    # z array
    zval = z912 + (zem-z912)*np.arange(N_eval)/(N_eval-1.)
    dz = np.fabs(zval[1]-zval[0])

    teff_LL = np.zeros(N_eval)

    # dXdz
    dXdz = pyigmu.cosm_xz(zval, cosmo=cosmo, flg_return=1)

    # Evaluate f(N,X)
    velo = (zval-zem)/(1+zem) * (const.c.cgs.value/1e5)  # Kludge for eval [km/s]

    log_fnX = fN_model.evaluate(lgNval, zem, cosmo=cosmo, vel_array=velo)
    log_fnz = log_fnX + np.outer(np.ones(N_eval), np.log10(dXdz))

    # Evaluate tau(z,N)
    teff_engy = (const.Ryd.to(u.eV, equivalencies=u.spectral()) /
                 ((1+zval)/(1+zem)))
    sigma_z = ltaa.photo_cross(1, 1, teff_engy)
    tau_zN = np.outer(Nval, sigma_z)

    # Integrand
    intg = 10.**(log_fnz) * (1. - np.exp(-1.*tau_zN))

    # Sum
    sumz_first = False
    if sumz_first is False:
        #; Sum in N first
        N_summed = np.sum(intg * np.outer(Nval, np.ones(N_eval)),  0) * dlgN * np.log(10.)
        # Sum in z
        teff_LL = (np.cumsum(N_summed[::-1]))[::-1] * dz

    # Debug
    if debug is True:
        pdb.set_trace()
    # Return
    return zval, teff_LL
Esempio n. 13
0
def test_photocross():
    phto = photo_cross(1, 1, 14. * u.eV)
    assert phto.unit == u.cm**2
    np.testing.assert_allclose(phto.value, 5.870146496955153e-18)