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
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
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
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)
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)
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
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)
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