Esempio n. 1
0
    def evaluate(
        self,
        in_x,
        scale,
        alpha,
        sil1_amp,
        sil1_center,
        sil1_fwhm,
        sil1_asym,
        sil2_amp,
        sil2_center,
        sil2_fwhm,
        sil2_asym,
        csil_amp,
        csil_center,
        csil_fwhm,
        csil_asym,
    ):
        """
        G21 function

        Parameters
        ----------
        in_x: float
           expects either x in units of wavelengths or frequency
           or assumes wavelengths in wavenumbers [1/micron]

        Returns
        -------
        axav: np array (float)
            A(x)/A(V) extinction curve [mag]

        Raises
        ------
        ValueError
           Input x values outside of defined range
        """
        x = _get_x_in_wavenumbers(in_x)

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, "G21")

        # powerlaw
        axav = scale * ((1.0 / x)**(-1.0 * alpha))

        # silicate feature drudes
        wave = 1 / x
        axav += drude_asym(wave, sil1_amp, sil1_center, sil1_fwhm, sil1_asym)
        axav += drude_asym(wave, sil2_amp, sil2_center, sil2_fwhm, sil2_asym)
        axav += drude_asym(wave, csil_amp, csil_center, csil_fwhm, csil_asym)

        return axav
Esempio n. 2
0
    def evaluate(self, in_x):
        """
        G03_SMCBar_WD01_extension function

        Parameters
        ----------
        in_x: float
           expects either x in units of wavelengths or frequency
           or assumes wavelengths in wavenumbers [1/micron]

           internally wavenumbers are used

        Returns
        -------
        axav: np array (float)
            A(x)/A(V) extinction curve [mag]

        Raises
        ------
        ValueError
           Input x values outside of defined range
        """
        # convert to wavenumbers (1/micron) if x input in units
        # otherwise, assume x in appropriate wavenumber units
        x = _get_x_in_wavenumbers(in_x)

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.__class__.__name__)

        # compute the dust grain model
        dmodel = WD01(modelname="SMCBar")
        dmod = dmodel(in_x)

        # compute the F19 curve for the input Rv over the F19 defined wavelength range
        gvals_g03 = (x > super().x_range[0]) & (x < super().x_range[1])
        fmod = super().evaluate(in_x[gvals_g03])

        # now merge the two smoothly
        outmod = copy.copy(dmod)
        outmod[gvals_g03] = fmod

        merge_range = np.array([1.0 / 0.1675, super().x_range[1]])
        gvals_merge = (x > merge_range[0]) & (x < merge_range[1])
        # have weights be zero at the min merge and 1 at the max merge
        weights = (x[gvals_merge] - merge_range[0]) / (merge_range[1] - merge_range[0])
        outmod[gvals_merge] = (1.0 - weights) * outmod[gvals_merge] + weights * dmod[
            gvals_merge
        ]

        return outmod
Esempio n. 3
0
    def function(self, lamb, Av=1.0, Rv=3.1, Alambda=True, **kwargs):
        """
        Cardelli89 extinction Law

        Parameters
        ----------
        lamb: float or ndarray(dtype=float)
            wavelength [in Angstroms] at which to evaluate the law.

        Av: float
            desired A(V) (default: 1.0)

        Rv: float
            desired R(V) (default: 3.1)

        Alambda: bool
            if set returns +2.5*1./log(10.)*tau, tau otherwise

        Returns
        -------
        r: float or ndarray(dtype=float)
            attenuation as a function of wavelength
            depending on Alambda option +2.5*1./log(10.)*tau,  or tau
        """
        # ensure the units are in angstrom
        _lamb = units.Quantity(lamb, units.angstrom).value

        if isinstance(_lamb, float) or isinstance(_lamb, np.float_):
            _lamb = np.asarray([lamb])
        else:
            _lamb = lamb[:]

        # convert to wavenumbers
        x = 1.0e4 / _lamb

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.name)

        # init variables
        a = np.zeros(np.size(x))
        b = np.zeros(np.size(x))
        # Infrared (Eq 2a,2b)
        ind = np.where((x >= 0.3) & (x < 1.1))
        a[ind] = 0.574 * x[ind]**1.61
        b[ind] = -0.527 * x[ind]**1.61
        # Optical & Near IR
        # Eq 3a, 3b
        ind = np.where((x >= 1.1) & (x < 3.3))
        y = x[ind] - 1.82
        a[ind] = (1.0 + 0.17699 * y - 0.50447 * y**2 - 0.02427 * y**3 +
                  0.72085 * y**4 + 0.01979 * y**5 - 0.77530 * y**6 +
                  0.32999 * y**7)
        b[ind] = (1.41338 * y + 2.28305 * y**2 + 1.07233 * y**3 -
                  5.38434 * y**4 - 0.62251 * y**5 + 5.30260 * y**6 -
                  2.09002 * y**7)
        # UV
        # Eq 4a, 4b
        ind = np.where((x >= 3.3) & (x <= 8.0))
        a[ind] = 1.752 - 0.316 * x[ind] - 0.104 / ((x[ind] - 4.67)**2 + 0.341)
        b[ind] = -3.090 + 1.825 * x[ind] + 1.206 / ((x[ind] - 4.62)**2 + 0.263)

        ind = np.where((x >= 5.9) & (x <= 8.0))
        y = x[ind] - 5.9
        Fa = -0.04473 * y**2 - 0.009779 * y**3
        Fb = 0.21300 * y**2 + 0.120700 * y**3
        a[ind] = a[ind] + Fa
        b[ind] = b[ind] + Fb
        # Far UV
        # Eq 5a, 5b
        ind = np.where((x > 8.0) & (x <= 10.0))
        # Fa = Fb = 0
        y = x[ind] - 8.0
        a[ind] = -1.073 - 0.628 * y + 0.137 * y**2 - 0.070 * y**3
        b[ind] = 13.670 + 4.257 * y - 0.420 * y**2 + 0.374 * y**3

        # Case of -values x out of range [0.289,10.0]
        ind = np.where((x > 10.0) | (x < 0.3))
        a[ind] = 0.0
        b[ind] = 0.0

        # Return Extinction vector
        # Eq 1
        if Alambda:
            return (a + b / Rv) * Av
        else:
            # return( 1./(2.5 * 1. / np.log(10.)) * ( a + b / Rv ) * Av)
            return 0.4 * np.log(10.0) * (a + b / Rv) * Av
Esempio n. 4
0
    def function(self,
                 lamb,
                 Av=1,
                 Rv=2.74,
                 Alambda=True,
                 draine_extend=False,
                 **kwargs):
        """
        Gordon03_SMCBar extinction law

        Parameters
        ----------
        lamb: float or ndarray(dtype=float)
            wavelength [in Angstroms] at which to evaluate the law.

        Av: float
            desired A(V) (default 1.0)

        Rv: float
            desired R(V) (default 2.74)

        Alambda: bool
            if set returns +2.5*1./log(10.)*tau, tau otherwise

        Returns
        -------
        r: float or ndarray(dtype=float)
            attenuation as a function of wavelength
            depending on Alambda option +2.5*1./log(10.)*tau,  or tau
        """
        # ensure the units are in angstrom
        _lamb = units.Quantity(lamb, units.angstrom).value

        if isinstance(_lamb, float) or isinstance(_lamb, np.float_):
            _lamb = np.asarray([lamb])
        else:
            _lamb = lamb[:]

        # convert to wavenumbers
        x = 1.0e4 / _lamb

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.name)

        # set Rv explicitly to the fixed value
        Rv = self.Rv

        c1 = -4.959 / Rv
        c2 = 2.264 / Rv
        c3 = 0.389 / Rv
        c4 = 0.461 / Rv
        x0 = 4.6
        gamma = 1.0

        k = np.zeros(np.size(x))

        # UV part
        xcutuv = 10000.0 / 2700.0
        xspluv = 10000.0 / np.array([2700.0, 2600.0])

        ind = np.where(x >= xcutuv)
        if np.size(ind) > 0:
            k[ind] = (1.0 + c1 + (c2 * x[ind]) + c3 * ((x[ind])**2) /
                      (((x[ind])**2 - (x0**2))**2 + (gamma**2) *
                       ((x[ind])**2)))
            yspluv = (1.0 + c1 + (c2 * xspluv) + c3 * ((xspluv)**2) /
                      (((xspluv)**2 - (x0**2))**2 + (gamma**2) *
                       ((xspluv)**2)))

        # FUV portion
        ind = np.where(x >= 5.9)
        if np.size(ind) > 0:
            if draine_extend:
                dfname = libdir + "SMC_Rv2.74_norm.txt"
                l_draine, k_draine = np.loadtxt(dfname,
                                                usecols=(0, 1),
                                                unpack=True)
                dind = np.where((1.0 / l_draine) >= 5.9)
                k[ind] = interp(x[ind], 1.0 / l_draine[dind][::-1],
                                k_draine[dind][::-1])
            else:
                k[ind] += c4 * (0.5392 * ((x[ind] - 5.9)**2) + 0.05644 *
                                ((x[ind] - 5.9)**3))

        # Opt/NIR part
        ind = np.where(x < xcutuv)
        if np.size(ind) > 0:
            xsplopir = np.zeros(9)
            xsplopir[0] = 0.0
            xsplopir[1:10] = 1.0 / np.array(
                [2.198, 1.65, 1.25, 0.81, 0.65, 0.55, 0.44, 0.37])

            # Values directly from Gordon et al. (2003)
            # ysplopir =  np.array([0.0,0.016,0.169,0.131,0.567,0.801,
            #                      1.00,1.374,1.672])
            # K & J values adjusted to provide a smooth,
            #      non-negative cubic spline interpolation
            ysplopir = np.array(
                [0.0, 0.11, 0.169, 0.25, 0.567, 0.801, 1.00, 1.374, 1.672])

            tck = interpolate.splrep(np.hstack([xsplopir, xspluv]),
                                     np.hstack([ysplopir, yspluv]),
                                     k=3)
            k[ind] = interpolate.splev(x[ind], tck)

        if Alambda:
            return k * Av
        else:
            return k * Av * (np.log(10.0) * 0.4)
Esempio n. 5
0
    def function(self,
                 lamb,
                 Av=1,
                 Rv=3.1,
                 Alambda=True,
                 draine_extend=False,
                 **kwargs):
        """
        Fitzpatrick99 extinction Law

        Parameters
        ----------
        lamb: float or ndarray(dtype=float)
            wavelength [in Angstroms] at which to evaluate the law.

        Av: float
            desired A(V) (default 1.0)

        Rv: float
            desired R(V) (default 3.1)

        Alambda: bool
            if set returns +2.5*1./log(10.)*tau, tau otherwise

        draine_extend: bool
            if set extends the extinction curve to below 912 A

        Returns
        -------
        r: float or ndarray(dtype=float)
            attenuation as a function of wavelength
            depending on Alambda option +2.5*1./log(10.)*tau,  or tau
        """
        # ensure the units are in angstrom
        _lamb = units.Quantity(lamb, units.angstrom).value

        if isinstance(_lamb, float) or isinstance(_lamb, np.float_):
            _lamb = np.asarray([lamb])
        else:
            _lamb = lamb[:]

        # convert to wavenumbers
        x = 1.0e4 / _lamb

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.name)

        # initialize values
        c2 = -0.824 + 4.717 / Rv
        c1 = 2.030 - 3.007 * c2
        c3 = 3.23
        c4 = 0.41
        x0 = 4.596
        gamma = 0.99

        k = np.zeros(np.size(x))

        # compute the UV portion of A(lambda)/E(B-V)
        xcutuv = 10000.0 / 2700.0
        xspluv = 10000.0 / np.array([2700.0, 2600.0])
        ind = np.where(x >= xcutuv)

        if np.size(ind) > 0:
            k[ind] = (c1 + (c2 * x[ind]) + c3 * ((x[ind])**2) /
                      (((x[ind])**2 - (x0**2))**2 + (gamma**2) *
                       ((x[ind])**2)))
            yspluv = (c1 + (c2 * xspluv) + c3 * ((xspluv)**2) /
                      (((xspluv)**2 - (x0**2))**2 + (gamma**2) *
                       ((xspluv)**2)))

            # FUV portion
            if not draine_extend:
                fuvind = np.where(x >= 5.9)
                k[fuvind] += c4 * (0.5392 * ((x[fuvind] - 5.9)**2) + 0.05644 *
                                   ((x[fuvind] - 5.9)**3))

            k[ind] += Rv
            yspluv += Rv

        # Optical/NIR portion

        ind = np.where(x < xcutuv)
        if np.size(ind) > 0:
            xsplopir = np.zeros(7)
            xsplopir[0] = 0.0
            xsplopir[1:7] = 10000.0 / np.array(
                [26500.0, 12200.0, 6000.0, 5470.0, 4670.0, 4110.0])

            ysplopir = np.zeros(7)
            ysplopir[0:3] = np.array([0.0, 0.26469, 0.82925]) * Rv / 3.1

            ysplopir[3:7] = np.array([
                np.poly1d([2.13572e-04, 1.00270, -4.22809e-01])(Rv),
                np.poly1d([-7.35778e-05, 1.00216, -5.13540e-02])(Rv),
                np.poly1d([-3.32598e-05, 1.00184, 7.00127e-01])(Rv),
                np.poly1d([
                    1.19456, 1.01707, -5.46959e-03, 7.97809e-04, -4.45636e-05
                ][::-1])(Rv),
            ])

            tck = interpolate.splrep(np.hstack([xsplopir, xspluv]),
                                     np.hstack([ysplopir, yspluv]),
                                     k=3)
            k[ind] = interpolate.splev(x[ind], tck)

        # convert from A(lambda)/E(B-V) to A(lambda)/A(V)
        k /= Rv

        # FUV portion from Draine curves
        if draine_extend:
            fuvind = np.where(x >= 5.9)
            tmprvs = np.arange(2.0, 6.1, 0.1)
            diffRv = Rv - tmprvs
            if min(abs(diffRv)) < 1e-8:
                dfname = libdir + "MW_Rv%s_ext.txt" % ("{0:.1f}".format(Rv))
                l_draine, k_draine = np.loadtxt(dfname,
                                                usecols=(0, 1),
                                                unpack=True)
            else:
                add, = np.where(diffRv < 0.0)
                Rv1 = tmprvs[add[0] - 1]
                Rv2 = tmprvs[add[0]]
                dfname = libdir + "MW_Rv%s_ext.txt" % ("{0:.1f}".format(Rv1))
                l_draine, k_draine1 = np.loadtxt(dfname,
                                                 usecols=(0, 1),
                                                 unpack=True)
                dfname = libdir + "MW_Rv%s_ext.txt" % ("{0:.1f}".format(Rv2))
                l_draine, k_draine2 = np.loadtxt(dfname,
                                                 usecols=(0, 1),
                                                 unpack=True)
                frac = diffRv[add[0] - 1] / (Rv2 - Rv1)
                k_draine = (1.0 - frac) * k_draine1 + frac * k_draine2

            dind = np.where((1.0 / l_draine) >= 5.9)
            k[fuvind] = interp(x[fuvind], 1.0 / l_draine[dind][::-1],
                               k_draine[dind][::-1])

        # setup the output
        if Alambda:
            return k * Av
        else:
            return k * Av * (np.log(10.0) * 0.4)
Esempio n. 6
0
    def evaluate(self, in_x, Rv):
        """
        F19_D03_extension function

        Parameters
        ----------
        in_x: float
           expects either x in units of wavelengths or frequency
           or assumes wavelengths in wavenumbers [1/micron]

           internally wavenumbers are used

        Returns
        -------
        axav: np array (float)
            A(x)/A(V) extinction curve [mag]

        Raises
        ------
        ValueError
           Input x values outside of defined range
        """
        # convert to wavenumbers (1/micron) if x input in units
        # otherwise, assume x in appropriate wavenumber units
        x = _get_x_in_wavenumbers(in_x)

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.__class__.__name__)

        # just in case someone calls evaluate explicitly
        Rv = np.atleast_1d(Rv)

        # ensure Rv is a single element, not numpy array
        Rv = Rv[0]

        # determine the dust grain models to use for the input Rv
        if Rv < 4.0:
            d1rv = 3.1
            d2rv = 4.0
            d1mod = D03(modelname="MWRV31")
            d2mod = D03(modelname="MWRV40")
        else:
            d1rv = 4.0
            d2rv = 5.5
            d1mod = D03(modelname="MWRV40")
            d2mod = D03(modelname="MWRV55")

        # interpolate to get the model extinction for the input Rv value
        dslope = (d2mod(in_x) - d1mod(in_x)) / (d2rv - d1rv)
        dmod = d1mod(in_x) + dslope * (Rv - d1rv)

        # compute the F19 curve for the input Rv over the F19 defined wavelength range
        gvals_f19 = (x > super().x_range[0]) & (x < super().x_range[1])
        fmod = super().evaluate(in_x[gvals_f19], Rv)

        # now merge the two smoothly
        outmod = copy.copy(dmod)
        outmod[gvals_f19] = fmod

        merge_range = np.array([1.0 / 0.1675, super().x_range[1]])
        gvals_merge = (x > merge_range[0]) & (x < merge_range[1])
        # have weights be zero at the min merge and 1 at the max merge
        weights = (x[gvals_merge] - merge_range[0]) / (merge_range[1] - merge_range[0])
        outmod[gvals_merge] = (1.0 - weights) * outmod[gvals_merge] + weights * dmod[
            gvals_merge
        ]

        return outmod
Esempio n. 7
0
    def function(self, lamb, Av=1, Rv=3.1, Alambda=True, **kwargs):
        """
        Fitzpatrick99 extinction Law

        Parameters
        ----------
        lamb: float or ndarray(dtype=float)
            wavelength [in Angstroms] at which to evaluate the law.

        Av: float
            desired A(V) (default 1.0)

        Rv: float
            desired R(V) (default 3.1)

        Alambda: bool
            if set returns +2.5*1./log(10.)*tau, tau otherwise

        Returns
        -------
        r: float or ndarray(dtype=float)
            attenuation as a function of wavelength
            depending on Alambda option +2.5*1./log(10.)*tau,  or tau
        """
        # ensure the units are in angstrom
        _lamb = units.Quantity(lamb, units.angstrom).value

        if isinstance(_lamb, float) or isinstance(_lamb, np.float_):
            _lamb = np.asarray([lamb])
        else:
            _lamb = lamb[:]

        # convert to wavenumbers
        x = 1.0e4 / _lamb

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, self.name)

        # initialize values
        c2 = -0.824 + 4.717 / Rv
        c1 = 2.030 - 3.007 * c2
        c3 = 3.23
        c4 = 0.41
        x0 = 4.596
        gamma = 0.99

        k = np.zeros(np.size(x))

        # compute the UV portion of A(lambda)/E(B-V)
        xcutuv = 10000.0 / 2700.0
        xspluv = 10000.0 / np.array([2700.0, 2600.0])
        ind = np.where(x >= xcutuv)

        if np.size(ind) > 0:
            k[ind] = (c1 + (c2 * x[ind]) + c3 * ((x[ind])**2) /
                      (((x[ind])**2 - (x0**2))**2 + (gamma**2) *
                       ((x[ind])**2)))
            yspluv = (c1 + (c2 * xspluv) + c3 * ((xspluv)**2) /
                      (((xspluv)**2 - (x0**2))**2 + (gamma**2) *
                       ((xspluv)**2)))

            # FUV portion
            fuvind = np.where(x >= 5.9)
            k[fuvind] += c4 * (0.5392 * ((x[fuvind] - 5.9)**2) + 0.05644 *
                               ((x[fuvind] - 5.9)**3))

            k[ind] += Rv
            yspluv += Rv

        # Optical/NIR portion

        ind = np.where(x < xcutuv)
        if np.size(ind) > 0:
            xsplopir = np.zeros(7)
            xsplopir[0] = 0.0
            xsplopir[1:7] = 10000.0 / np.array(
                [26500.0, 12200.0, 6000.0, 5470.0, 4670.0, 4110.0])

            ysplopir = np.zeros(7)
            ysplopir[0:3] = np.array([0.0, 0.26469, 0.82925]) * Rv / 3.1

            ysplopir[3:7] = np.array([
                np.poly1d([2.13572e-04, 1.00270, -4.22809e-01])(Rv),
                np.poly1d([-7.35778e-05, 1.00216, -5.13540e-02])(Rv),
                np.poly1d([-3.32598e-05, 1.00184, 7.00127e-01])(Rv),
                np.poly1d([
                    1.19456, 1.01707, -5.46959e-03, 7.97809e-04, -4.45636e-05
                ][::-1])(Rv),
            ])

            tck = interpolate.splrep(np.hstack([xsplopir, xspluv]),
                                     np.hstack([ysplopir, yspluv]),
                                     k=3)
            k[ind] = interpolate.splev(x[ind], tck)

        # convert from A(lambda)/E(B-V) to A(lambda)/A(V)
        k /= Rv

        # setup the output
        if Alambda:
            return k * Av
        else:
            return k * Av * (np.log(10.0) * 0.4)
Esempio n. 8
0
    def evaluate(
        self,
        in_x,
        BKG_amp,
        BKG_lambda,
        BKG_width,
        FUV_amp,
        FUV_lambda,
        FUV_b,
        FUV_n,
        NUV_amp,
        NUV_lambda,
        NUV_width,
        SIL1_amp,
        SIL1_lambda,
        SIL1_width,
        SIL2_amp,
        SIL2_lambda,
        SIL2_width,
        FIR_amp,
        FIR_lambda,
        FIR_width,
    ):
        """
        P92 function

        Parameters
        ----------
        in_x: float
           expects either x in units of wavelengths or frequency
           or assumes wavelengths in wavenumbers [1/micron]

           internally wavenumbers are used

        Returns
        -------
        axav: np array (float)
            A(x)/A(V) extinction curve [mag]

        Raises
        ------
        ValueError
           Input x values outside of defined range
        """
        x = _get_x_in_wavenumbers(in_x)

        # check that the wavenumbers are within the defined range
        _test_valid_x_range(x, self.x_range, "P92_mod")

        # compute b from lambda and width
        BKG_b = np.power((BKG_width / BKG_lambda), 2.0) - 2.0
        NUV_b = np.power((NUV_width / NUV_lambda), 2.0) - 2.0
        SIL1_b = np.power((SIL1_width / SIL1_lambda), 2.0) - 2.0
        SIL2_b = np.power((SIL2_width / SIL2_lambda), 2.0) - 2.0
        FIR_b = np.power((FIR_width / FIR_lambda), 2.0) - 2.0

        # calculate the terms
        lam = 1.0 / x
        axav = (
            self._p92_single_term(lam, BKG_amp, BKG_lambda, BKG_b, 2.0) +
            self._p92_single_term(lam, FUV_amp, FUV_lambda, FUV_b, FUV_n) +
            self._p92_single_term(lam, NUV_amp, NUV_lambda, NUV_b, 2.0) +
            self._p92_single_term(lam, SIL1_amp, SIL1_lambda, SIL1_b, 2.0) +
            self._p92_single_term(lam, SIL2_amp, SIL2_lambda, SIL2_b, 2.0) +
            self._p92_single_term(lam, FIR_amp, FIR_lambda, FIR_b, 2.0))

        # return A(x)/A(V)
        return axav