def __init__( self, theta=power_angles(), \ scatm=ss.Scatmodel(), \ rad=0.1, ener=1.0, nx=1000 ): # Theta automatically sampled in log space. # If I don't do it this way, linear interpolation easily fails # for small angles (between theta[0] and theta[1]). Since # most of my plots are in log-space, it makes more sense to # sample logarithmically. if np.size(theta) < 2: print('Error: Must give more than one theta value') self.theta = None self.rad = None self.ener = None self.scatm = None self.theta = theta self.rad = rad self.ener = ener self.scatm = scatm dxi = 1.0 / np.float(nx) xi = np.arange( 0, 1.0, dxi ) + dxi itemp = np.array([]) for th in self.theta: thscat = th / xi dsig = ss.Diffscat( theta=thscat, scatm=self.scatm, E=self.ener, a=self.rad ).dsig itemp = np.append( itemp, \ c.intz( xi, dsig/(xi**2) ) ) self.itemp = itemp * c.arcs2rad**2
def DChi( z, zp=0.0, cosm=Cosmology(), nz=100 ): """ Calculates co-moving radial distance [Gpc] from zp to z using dx = cdt/a z : float : redshift zp ; float (0) : starting redshift cosm : Cosmology nz : int (100) : number of z-values to use in calculation """ zvals = zvalues( zs=z, z0=zp, nz=nz ) integrand = c.cperh0 * ( c.h0/cosm.h0 ) / np.sqrt( cosm.m * np.power(1+zvals,3) + cosm.l ) return c.intz( zvals, integrand ) / (1e9 * c.pc2cm ) # Gpc, in comoving coordinates
def ScreenIGM(halo, zs=2.0, zg=1.0, md=1.5e-5, cosm=cosmo.Cosmology()): """ FUNCTION ScreenIGM( halo, zs=2.0, zg=1.0, md=1.5e-5, cosm=cosmo.Cosmology() ) MODIFIES halo.htype, halo.dist, halo.intensity, halo.taux -------------------------------------------------------------------- halo : Halo object zs : float : redshift of source zg : float : redshift of screen md : float : mass density of dust to use in screen [g cm^-2] cosm : cosmo.Cosmology """ if zg >= zs: print "%% STOP: zg must be < zs" E0 = halo.energy alpha = halo.alpha scatm = halo.scatm # Store information about this halo calculation halo.htype = CosmHalo(zs=zs, zg=zg, cosm=cosm, igmtype="Screen") halo.dist = dust.Dustspectrum(rad=halo.rad, md=md) X = cosmo.DChi(zs, zp=zg, cosm=cosm) / cosmo.DChi(zs, cosm=cosm) # Single value thscat = alpha / X # Scattering angle required Eg = E0 * (1 + zg) # Photon energy at the screen ## Single grain size case if type(halo.rad) == dust.Grain: dsig = ss.Diffscat(theta=thscat, a=halo.dist.a, E=Eg, scatm=scatm).dsig intensity = halo.dist.nd / np.power(X, 2) * dsig ## Distribution of grain sizes elif type(halo.rad) == dust.Dustdist: avals = halo.dist.a dsig = np.zeros(shape=(np.size(avals), np.size(thscat))) for i in range(np.size(avals)): dsig[i, :] = ss.Diffscat(theta=thscat, a=avals[i], E=Eg, scatm=scatm).dsig intensity = np.array([]) for j in range(np.size(thscat)): itemp = halo.dist.nd * dsig[:, j] / np.power(X, 2) intensity = np.append(intensity, c.intz(avals, itemp)) else: print "%% Must input type dust.Grain or dust.Dustdist" intensity = np.zeros(np.size(zpvals)) halo.intensity = intensity * np.power(c.arcs2rad(), 2) # arcsec^-2 halo.taux = cosmo.CosmTauScreen(zg, E=halo.energy, dist=halo.dist, scatm=halo.scatm)
def __init__( self, E=1.0, scatm=Scatmodel(), dist=dust.Dustspectrum() ): self.scatm = scatm self.E = E self.dist = dist if scatm.stype == 'RG': print 'Rayleigh-Gans cross-section not currently supported for Kappaext' self.kappa = None return cm = scatm.cmodel scat = scatm.smodel cgeo = np.pi * np.power( dist.a * c.micron2cm(), 2 ) qext = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) qext_pe = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) qext_pa = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) # Test for graphite case if cm.cmtype == 'Graphite': cmGraphitePerp = cmi.CmGraphite(size=cm.size, orient='perp') cmGraphitePara = cmi.CmGraphite(size=cm.size, orient='para') if np.size(dist.a) > 1: for i in range( np.size(dist.a) ): qext_pe[:,i] = scat.Qext( E, a=dist.a[i], cm=cmGraphitePerp ) qext_pa[:,i] = scat.Qext( E, a=dist.a[i], cm=cmGraphitePara ) else: qext_pe = scat.Qext( E, a=dist.a, cm=cmGraphitePerp ) qext_pa = scat.Qext( E, a=dist.a, cm=cmGraphitePara ) qext = ( qext_pa + 2.0 * qext_pe ) / 3.0 else: if np.size(dist.a) > 1: for i in range( np.size(dist.a) ): qext[:,i] = scat.Qext( E, a=dist.a[i], cm=cm ) else: qext = scat.Qext( E, a=dist.a, cm=cm ) if np.size(dist.a) == 1: kappa = dist.nd * qext * cgeo / dist.md else: kappa = np.array([]) for j in range( np.size(E) ): kappa = np.append( kappa, \ c.intz( dist.a, dist.nd * qext[j,:] * cgeo ) / dist.md ) self.kappa = kappa
def CosmTauX( z, E=1.0, dist=dust.Powerlaw(), scatm=ss.Scatmodel(), cosm=Cosmology(), nz=100 ): """ FUNCTION CosmTauX( z, E=1.0, dist=dust.Powerlaw(), scatm=ss.Scatmodel(), cosm=Cosmology(), nz=100 --------------------------------- INPUT z : redshift of source E : scalar or np.array [keV] dist : dust.Powerlaw or dust.Grain scatm : ss.Scatmodel cosm : cosm.Cosmology --------------------------------- OUTPUT tauX : scalar or np.array [optical depth to X-ray scattering] = kappa( dn/da da ) cosmdens (1+z)^2 cdz/hfac """ zvals = zvalues( zs=z, nz=nz ) md = Cosmdens( cosm=cosm ).md spec = dust.Dustspectrum( rad=dist, md=md ) if np.size(E) > 1: result = np.array([]) for ener in E: Evals = ener * (1+zvals) kappa = ss.Kappascat( E=Evals, scatm=scatm, dist=spec ).kappa hfac = np.sqrt( cosm.m * np.power( 1+zvals, 3 ) + cosm.l ) integrand = kappa * md * np.power( 1+zvals, 2 ) * \ c.cperh0 * ( c.h0/cosm.h0 ) / hfac result = np.append( result, c.intz( zvals, integrand ) ) else: Evals = E * (1 + zvals) kappa = ss.Kappascat( E=Evals, scatm=scatm, dist=spec ).kappa hfac = np.sqrt( cosm.m * np.power( 1+zvals, 3 ) + cosm.l ) integrand = kappa * md * np.power( 1+zvals, 2 ) * c.cperh0 * ( c.h0/cosm.h0 ) / hfac result = c.intz( zvals, integrand ) return result
def ecf( self, theta, nth=500 ): NE, NA = len(self.energy), len(self.alpha) result = np.zeros( NE ) # NE x NA taux = self.taux tharray = np.linspace( min(self.alpha), theta, nth ) if self.taux == None: print('ERROR: No taux is specified. Need to run halo calculation') return result for i in range(NE): interpH = interp1d( self.alpha, self.intensity[i,:] ) result[i] = c.intz( tharray, interpH(tharray) * 2.0*np.pi*tharray ) / taux[i] return result
def ecf(self, theta, nth=500): """ Returns the enclosed fraction for the halo surface brightness profile, via integral(theta,2pi*theta*halo)/tau. theta : float : Value for which to compute enclosed fraction (arcseconds) nth : int (500) : Number of angles to use in calculation """ if self.htype == None: print "Error: Halo has not yet beein calculated." return interpH = interp1d(self.alpha, self.intensity) tharray = np.linspace(min(self.alpha), theta, nth) try: return c.intz(tharray, interpH(tharray) * 2.0 * np.pi * tharray) / self.taux except: print "Error: ECF calculation failed. Theta is likely out of bounds." return
def __init__( self, E=1.0, scatm=Scatmodel(), dist=dust.Dustspectrum() ): self.scatm = scatm self.E = E self.dist = dist cm = scatm.cmodel scat = scatm.smodel cgeo = np.pi * np.power( dist.a * c.micron2cm(), 2 ) qsca = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) qsca_pe = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) qsca_pa = np.zeros( shape=( np.size(E),np.size(dist.a) ) ) # Test for graphite case if cm.cmtype == 'Graphite': cmGraphitePerp = cmi.CmGraphite(size=cm.size, orient='perp') cmGraphitePara = cmi.CmGraphite(size=cm.size, orient='para') if np.size(dist.a) > 1: for i in range( np.size(dist.a) ): qsca_pe[:,i] = scat.Qsca( E, a=dist.a[i], cm=cmGraphitePerp ) qsca_pa[:,i] = scat.Qsca( E, a=dist.a[i], cm=cmGraphitePara ) else: qsca_pe = scat.Qsca( E, a=dist.a, cm=cmGraphitePerp ) qsca_pa = scat.Qsca( E, a=dist.a, cm=cmGraphitePara ) qsca = ( qsca_pa + 2.0 * qsca_pe ) / 3.0 else: if np.size(dist.a) > 1: for i in range( np.size(dist.a) ): qsca[:,i] = scat.Qsca( E, a=dist.a[i], cm=cm ) else: qsca = scat.Qsca( E, a=dist.a, cm=cm ) if np.size(dist.a) == 1: kappa = dist.nd * qsca * cgeo / dist.md else: kappa = np.array([]) for j in range( np.size(E) ): kappa = np.append( kappa, \ c.intz( dist.a, dist.nd * qsca[j,:] * cgeo ) / dist.md ) self.kappa = kappa
def DiscreteISM( halo, xg=0.5, NH=1.0e20, d2g=0.009 ): """ Calculate the X-ray scattering intensity for dust in an infinitesimally thin wall somewhere on the line of sight. | **MODIFIES** | halo.htype, halo.dist, halo.taux, halo.intensity | **INPUTS** | halo : Halo object | xg : float : distance FROM source / distance between source and observer | NH : float : column density [cm^-2] | d2g : float : dust-to-gass mass ratio """ E0 = halo.energy alpha = halo.alpha scatm = halo.scatm md = NH * c.m_p * d2g halo.htype = GalHalo( xg=xg, NH=NH, d2g=d2g, ismtype='Screen' ) halo.dist = dust.Dustspectrum( rad=halo.rad, md=md ) thscat = alpha / xg if type(halo.rad) == dust.Grain: dsig = ss.Diffscat( theta=thscat, a=halo.dist.a, E=E0, scatm=scatm ).dsig intensity = np.power( xg, -2.0 ) * dsig * halo.dist.nd elif type(halo.rad) == dust.Dustdist: avals = halo.dist.a intensity = [] for i in range( len(alpha) ): iatemp = np.zeros( shape=( len(avals),len(alpha) ) ) for j in range( len(avals) ): dsig = ss.Diffscat( theta=thscat, a=avals[j], E=E0, scatm=scatm ).dsig iatemp[j,:] = np.power(xg,-2.0) * dsig intensity.append( c.intz( avals, iatemp[:,i] * halo.dist.nd ) ) intensity = np.array( intensity ) else: print('%% Must input type dust.Grain or dust.Dustdist') intensity = np.zeros( np.size(xvals) ) halo.intensity = intensity * np.power( c.arcs2rad, 2 ) # arcsec^-2 halo.taux = ss.Kappascat( E=halo.energy, scatm=halo.scatm, dist=halo.dist ).kappa * md
def DiscreteISM( halo, xg=0.5, NH=1.0e20, d2g=0.009 ): """ FUNCTION DiscreteISM( halo, xg=0.5, NH=1.0e20, d2g=0.009 ) MODIFIES halo.htype, halo.dist, halo.taux, halo.intensity ---------------------------------------------------------------------------- halo : Halo object xg : float : distance FROM source / distance between source and observer NH : float : column density [cm^-2] d2g : float : dust-to-gass mass ratio """ E0 = halo.energy alpha = halo.alpha scatm = halo.scatm md = NH * c.mp() * d2g halo.htype = GalHalo( xg=xg, NH=NH, d2g=d2g, ismtype='Screen' ) halo.dist = dust.Dustspectrum( rad=halo.rad, md=md ) thscat = alpha / xg if type(halo.rad) == dust.Grain: dsig = ss.Diffscat( theta=thscat, a=halo.dist.a, E=E0, scatm=scatm ).dsig intensity = np.power( xg, -2.0 ) * dsig * halo.dist.nd elif type(halo.rad) == dust.Dustdist: avals = halo.dist.a intensity = [] for i in range( len(alpha) ): iatemp = np.zeros( shape=( len(avals),len(alpha) ) ) for j in range( len(avals) ): dsig = ss.Diffscat( theta=thscat, a=avals[j], E=E0, scatm=scatm ).dsig iatemp[j,:] = np.power(xg,-2.0) * dsig intensity.append( c.intz( avals, iatemp[:,i] * halo.dist.nd ) ) intensity = np.array( intensity ) else: print '%% Must input type dust.Grain or dust.Dustdist' intensity = np.zeros( np.size(xvals) ) halo.intensity = intensity * np.power( c.arcs2rad(), 2 ) # arcsec^-2 halo.taux = ss.Kappascat( E=halo.energy, scatm=halo.scatm, dist=halo.dist ).kappa * md
def __call__(self, with_mp=False): cm = self.scatm.cmodel scat = self.scatm.smodel cgeo = np.pi * np.power( self.dist.a * c.micron2cm(), 2 ) # Test for graphite case if cm.cmtype == 'Graphite': if np.size(self.dist.a) > 1: for i in range( np.size(self.dist.a) ): self.qsca_pe[:,i] = scat.Qsca( self.E, a=self.dist.a[i], cm=cmi.CmGraphite(size=cm.size, orient='perp') ) self.qsca_pa[:,i] = scat.Qsca( self.E, a=self.dist.a[i], cm=cmi.CmGraphite(size=cm.size, orient='para') ) else: self.qsca_pe = scat.Qsca( self.E, a=self.dist.a, cm=cmi.CmGraphite(size=cm.size, orient='perp') ) self.qsca_pa = scat.Qsca( self.E, a=self.dist.a, cm=cmi.CmGraphite(size=cm.size, orient='para') ) self.qsca = ( self.qsca_pa + 2.0 * self.qsca_pe ) / 3.0 else: if np.size(self.dist.a) > 1: if with_mp: pool = Pool(processes=2) self.qsca = np.array(pool.map(self._one_scatter,self.dist.a)).T else: for i in range( np.size(self.dist.a) ): self.qsca[:,i] = self._one_scatter(self.dist.a[i]) else: self.qsca = scat.Qsca( self.E, a=self.dist.a, cm=cm ) if np.size(self.dist.a) == 1: kappa = self.dist.nd * self.qsca * cgeo / self.dist.md else: kappa = np.array([]) for j in range( np.size(self.E) ): kappa = np.append( kappa, \ c.intz( self.dist.a, self.dist.nd * self.qsca[j,:] * cgeo ) / self.dist.md ) self.kappa = kappa
def ecf( self, theta, nth=500 ): """ Returns the enclosed fraction for the halo surface brightness profile, as a function of energy via integral(theta,2pi*theta*halo)/total_halo_counts ------------------------------------------------------------------------- theta : float : Value for which to compute enclosed fraction (arcseconds) nth : int (500) : Number of angles to use in calculation """ NE, NA = len(self.energy), len(self.alpha) result = np.zeros( NE ) # NE x NA taux = self.taux tharray = np.linspace( min(self.alpha), theta, nth ) if self.taux == None: print 'ERROR: No taux is specified. Need to run halo calculation' return result for i in range(NE): interpH = interp1d( self.alpha, self.intensity[i,:] ) result[i] = c.intz( tharray, interpH(tharray) * 2.0*np.pi*tharray ) / taux[i] return result
def UniformIGM(halo, zs=4.0, cosm=cosmo.Cosmology(), nz=500): """ FUNCTION UniformIGM( halo, zs=4.0, cosm=cosmo.Cosmology(), nz=500 ) MODIFIES halo.htype, halo.dist, halo.intensity, halo.taux -------------------------------------------------------------------- halo : Halo object zs : float : redshift of source cosm : cosmo.Cosmology nz : int : number of z-values to use in integration """ E0 = halo.energy alpha = halo.alpha scatm = halo.scatm halo.htype = CosmHalo(zs=zs, cosm=cosm, igmtype="Uniform") # Stores information about this halo calc halo.dist = dust.Dustspectrum(rad=halo.rad, md=cosmo.Cosmdens(cosm=cosm).md) Dtot = cosmo.DChi(zs, cosm=cosm, nz=nz) zpvals = cosmo.zvalues(zs=zs - zs / nz, z0=0, nz=nz) DP = np.array([]) for zp in zpvals: DP = np.append(DP, cosmo.DChi(zs, zp=zp, cosm=cosm)) X = DP / Dtot c_H0_cm = c.cperh0() * (c.h0() / cosm.h0) # cm hfac = np.sqrt(cosm.m * np.power(1 + zpvals, 3) + cosm.l) Evals = E0 * (1 + zpvals) ## Single grain case if type(halo.rad) == dust.Grain: intensity = np.array([]) f = 0.0 cnt = 0.0 na = np.size(alpha) for al in alpha: cnt += 1 thscat = al / X # np.size(thscat) = nz dsig = ss.Diffscat(theta=thscat, a=halo.dist.a, E=Evals, scatm=scatm).dsig itemp = c_H0_cm / hfac * np.power((1 + zpvals) / X, 2) * halo.dist.nd * dsig intensity = np.append(intensity, c.intz(zpvals, itemp)) ## Dust distribution case elif type(halo.rad) == dust.Dustdist: avals = halo.dist.a intensity = np.array([]) for al in alpha: thscat = al / X # np.size(thscat) = nz iatemp = np.array([]) for aa in avals: dsig = ss.Diffscat(theta=thscat, a=aa, E=Evals, scatm=scatm).dsig dtmp = c_H0_cm / hfac * np.power((1 + zpvals) / X, 2) * dsig iatemp = np.append(iatemp, c.intz(zpvals, dtmp)) intensity = np.append(intensity, c.intz(avals, halo.dist.nd * iatemp)) else: print "%% Must input type dust.Grain or dust.Dustdist" intensity = np.zeros(np.size(zpvals)) # ----- Finally, set the halo intensity -------- halo.intensity = intensity * np.power(c.arcs2rad(), 2) # arcsec^-2 halo.taux = cosmo.CosmTauX(zs, E=halo.energy, dist=halo.rad, scatm=halo.scatm, cosm=halo.htype.cosm)
def ndens(self, md=MDUST): adep = np.power( self.a, -self.p ) # um^-p dmda = adep * 4./3. * np.pi * self.rho * np.power( self.a*c.micron2cm, 3 ) # g um^-p const = md / c.intz( self.a, dmda ) # cm^-? um^p-1 return const * adep # cm^-? um^-1
def dnda(self, md=1.5e-5): adep = np.power( self.a, -self.p ) # um^-p dmda = adep * 4./3. * np.pi * self.rho * np.power( self.a*c.micron2cm(), 3 ) # g um^-p const = md / c.intz( self.a, dmda ) # cm^-? um^p-1 return const * adep # cm^-? um^-1
def make_WD01_Dustspectrum( R_V=3.1, bc=0.0, rad=DEFAULT_RAD, type='Graphite', gal='MW', verbose=True ): """ make_WD01_Dustspectrum( R_V [float], bc [float], rad [np.array : grain sizes (um)], type [string : 'Graphite' or 'Silicate'] ) gal [string : 'MW', 'LMC', or 'SMC'], ------------------------------------------- Returns a dust.Dustspectrum object containing a (grain sizes), nd (dn/da), and md (total mass density of dust) >>> wd01_sil = make_WD01_Dustspectrum(type='Silicate') >>> (dust._integrate_dust_mass(wd01_sil)/wd01_sil.md) - 1.0 < 0.01 >>> wd01_gra = make_WD01_Dustspectrum(type='Graphite') >>> (dust._integrate_dust_mass(wd01_gra)/wd01_gra.md) - 1.0 < 0.01 """ if type == 'Graphite': rho_d = 2.2 #g cm^-3 elif type == 'Silicate': rho_d = 3.8 else: print('Error: Dust type not recognized') return ANGS2MICRON = 1.e-10 * 1.e6 a = rad # Easier than changing variable names further down a_cm = rad * c.micron2cm NA = np.size( a ) (alpha, beta, a_t, a_c, C) = get_dist_params( R_V=R_V, bc=bc, type=type, gal=gal, verbose=verbose ) if type == 'Graphite': mc = 12. * 1.67e-24 # Mass of carbon atom in grams (12 m_p) rho = 2.24 # g cm^-3 sig = 0.4 a_01 = 3.5*ANGS2MICRON # 3.5 angstroms in units of microns a_01_cm = a_01 * c.micron2cm bc1 = 0.75 * bc * 1.e-5 B_1 = (3.0/(2*np.pi)**1.5) * np.exp(-4.5 * 0.4**2) / (rho*a_01_cm**3 * 0.4) \ * bc1 * mc / (1 + special.erf( 3*0.4/np.sqrt(2) + np.log(a_01/3.5e-4)/(0.4*np.sqrt(2)) ) ) a_02 = 30.0*ANGS2MICRON # 30 angtroms in units of microns a_02_cm = a_02 * c.micron2cm bc2 = 0.25 * bc * 1.e-5 B_2 = (3.0/(2*np.pi)**1.5) * np.exp(-4.5 * 0.4**2) / (rho*a_02_cm**3 * 0.4) \ * bc2 * mc / (1 + special.erf( 3*0.4/np.sqrt(2) + np.log(a_02/3.5e-4)/(0.4*np.sqrt(2)) ) ) D = (B_1/a_cm) * np.exp( -0.5*( np.log(a/a_01)/sig )**2 ) + \ (B_2/a_cm) * np.exp( -0.5*( np.log(a/a_02)/sig )**2 ) Case_vsg = np.where( a < 3.5*ANGS2MICRON ) if np.size(Case_vsg) != 0: D[Case_vsg] = 0.0 Case_g = np.zeros( NA ) case1g = np.where( np.logical_and(a > 3.5*ANGS2MICRON, a < a_t ) ) case2g = np.where( a >= a_t ) if np.size(case1g) != 0: Case_g[case1g] = 1.0 if np.size(case2g) != 0: Case_g[case2g] = np.exp( -( (a[case2g]-a_t) / a_c )**3 ) if beta >= 0: F_g = 1 + beta * a / a_t if beta < 0: F_g = 1.0 / (1 - beta * a / a_t) Dist_WD01 = D + C/a_cm * (a/a_t)**alpha * F_g * Case_g #cm^-4 per n_H if type == 'Silicate': Case_s = np.zeros( NA ) case1s = np.where( np.logical_and( a > 3.5*ANGS2MICRON, a < a_t ) ) case2s = np.where( a >= a_t ) if np.size(case1s) != 0: Case_s[case1s] = 1.0 if np.size(case2s) != 0: Case_s[case2s] = np.exp( -( (a[case2s]-a_t)/a_c )**3 ) F_s = np.zeros( NA ) if beta >= 0: F_s = 1 + beta * a / a_t if beta < 0: F_s = 1. / (1 - beta * a / a_t) Dist_WD01 = C/a_cm * (a/a_t)**alpha * F_s * Case_s #cm^-4 per n_H mg = 4.0/3.0*np.pi*a_cm**3 * rho_d # mass of each dust grain Md = c.intz( a_cm, Dist_WD01 * mg ) result = dust.Dustspectrum() result.a = a result.rho = rho_d result.nd = Dist_WD01 * c.micron2cm # cm^-3 per um per n_H result.md = Md return result
def make_WD01_Dustspectrum( R_V=3.1, bc=0.0, rad=dust.adist(), type='Graphite', gal='MW' ): """ make_WD01_Dustspectrum( R_V [float], bc [float], rad [np.array : grain sizes (um)], type [string : 'Graphite' or 'Silicate'] ) gal [string : 'MW', 'LMC', or 'SMC'], ------------------------------------------- Returns a dust.Dustspectrum object containing a (grain sizes), nd (dn/da), and md (total mass density of dust) """ if type == 'Graphite': rho_d = 2.2 #g cm^-3 elif type == 'Silicate': rho_d = 3.8 else: print 'Error: Dust type not recognized' return dist = dust.Dustdist( rad=rad, rho=rho_d, p=4 ) result = dust.Dustspectrum( rad=dist ) ANGS2MICRON = 1.e-10 * 1.e6 a = dist.a a_cm = dist.a * c.micron2cm() NA = np.size( a ) (alpha, beta, a_t, a_c, C) = get_dist_params( R_V=R_V, bc=bc, type=type, gal=gal ) if type == 'Graphite': mc = 12. * 1.67e-24 # Mass of carbon atom in grams (12 m_p) rho = 2.24 # g cm^-3 sig = 0.4 a_01 = 3.5*ANGS2MICRON # 3.5 angstroms in units of microns a_01_cm = a_01 * c.micron2cm() bc1 = 0.75 * bc * 1.e-5 B_1 = (3.0/(2*np.pi)**1.5) * np.exp(-4.5 * 0.4**2) / (rho*a_01_cm**3 * 0.4) \ * bc1 * mc / (1 + special.erf( 3*0.4/np.sqrt(2) + np.log(a_01/3.5e-4)/(0.4*np.sqrt(2)) ) ) a_02 = 30.0*ANGS2MICRON # 30 angtroms in units of microns a_02_cm = a_02 * c.micron2cm() bc2 = 0.25 * bc * 1.e-5 B_2 = (3.0/(2*np.pi)**1.5) * np.exp(-4.5 * 0.4**2) / (rho*a_02_cm**3 * 0.4) \ * bc2 * mc / (1 + special.erf( 3*0.4/np.sqrt(2) + np.log(a_02/3.5e-4)/(0.4*np.sqrt(2)) ) ) D = (B_1/a_cm) * np.exp( -0.5*( np.log(a/a_01)/sig )**2 ) + \ (B_2/a_cm) * np.exp( -0.5*( np.log(a/a_02)/sig )**2 ) Case_vsg = np.where( a < 3.5*ANGS2MICRON ) if np.size(Case_vsg) != 0: D[Case_vsg] = 0.0 Case_g = np.zeros( NA ) case1g = np.where( np.logical_and(a > 3.5*ANGS2MICRON, a < a_t ) ) case2g = np.where( a >= a_t ) if np.size(case1g) != 0: Case_g[case1g] = 1.0 if np.size(case2g) != 0: Case_g[case2g] = np.exp( -( (a[case2g]-a_t) / a_c )**3 ) if beta >= 0: F_g = 1 + beta * a / a_t if beta < 0: F_g = 1.0 / (1 - beta * a / a_t) Dist_WD01 = D + C/a_cm * (a/a_t)**alpha * F_g * Case_g #cm^-4 per n_H if type == 'Silicate': Case_s = np.zeros( NA ) case1s = np.where( np.logical_and( a > 3.5*ANGS2MICRON, a < a_t ) ) case2s = np.where( a >= a_t ) if np.size(case1s) != 0: Case_s[case1s] = 1.0 if np.size(case2s) != 0: Case_s[case2s] = np.exp( -( (a[case2s]-a_t)/a_c )**3 ) F_s = np.zeros( NA ) if beta >= 0: F_s = 1 + beta * a / a_t if beta < 0: F_s = 1. / (1 - beta * a / a_t) Dist_WD01 = C/a_cm * (a/a_t)**alpha * F_s * Case_s #cm^-4 per n_H ## Modify result Dustspectrum so we get a proper WD01 dist! mg = 4.0/3.0*np.pi*a_cm**3 * rho_d # mass of each dust grain Md = c.intz( a_cm, Dist_WD01 * mg ) result.nd = Dist_WD01 * c.micron2cm() # cm^-3 per um per n_H result.md = Md return result
def UniformISM( halo, NH=1.0e20, d2g=0.009, nx=1000, usepathdiff=False ): """ Calculate the X-ray scattering intensity for dust distributed uniformly along the line of sight | **MODIFIES** | halo.htype, halo.dist, halo.taux, halo.intensity | **INPUTS** | halo : Halo object | NH : float : column density [cm^-2] | d2g : float : dust-to-gass mass ratio | nx : int : number of values to use in integration | usepathdiff : boolean : True = use extinction due to path difference e^(-tau*path_diff) """ E0 = halo.energy alpha = halo.alpha scatm = halo.scatm md = NH * c.m_p * d2g halo.htype = GalHalo( NH=NH, d2g=d2g, ismtype='Uniform' ) halo.dist = dust.Dustspectrum( rad=halo.rad, md=md ) halo.taux = ss.Kappascat( E=halo.energy, scatm=halo.scatm, dist=halo.dist ).kappa * md dx = 1.0 / nx xvals = np.arange( 0.0, 1.0, dx ) + dx #--- Single grain case --- if type( halo.rad ) == dust.Grain: intensity = np.array([]) for al in alpha: thscat = al / xvals # np.size(thscat) = nx dsig = ss.Diffscat( theta=thscat, a=halo.dist.a, E=E0, scatm=scatm ).dsig delta_tau = 0.0 if usepathdiff: print('Using path difference') delta_x = path_diff( al, xvals ) delta_tau = halo.taux * delta_x print(np.max( delta_x )) itemp = np.power( xvals, -2.0 ) * dsig * halo.dist.nd * np.exp( -delta_tau ) intensity = np.append( intensity, c.intz( xvals, itemp ) ) #--- Dust distribution case --- elif type( halo.rad ) == dust.Dustdist: avals = halo.dist.a intensity = np.array([]) for al in alpha: thscat = al / xvals # np.size(thscat) = nx iatemp = np.array([]) for aa in avals: dsig = ss.Diffscat( theta=thscat, a=aa, E=E0, scatm=scatm ).dsig delta_tau = 0.0 if usepathdiff: print('Using path difference') delta_x = path_diff( al, xvals ) delta_tau = halo.taux * delta_x print(max( delta_x )) dtemp = np.power( xvals, -2.0 ) * dsig * np.exp( -delta_tau ) iatemp = np.append( iatemp, c.intz( xvals, dtemp ) ) intensity = np.append( intensity, c.intz( avals, halo.dist.nd * iatemp ) ) else: print('%% Must input type dust.Grain or dust.Dustdist') intensity = np.zeros( np.size(xvals) ) # Set the halo intensity halo.intensity = intensity * np.power( c.arcs2rad, 2 ) # arcsec^-2