示例#1
0
    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
示例#2
0
def DA( theta, z, cosm=Cosmology(), nz=100 ):
    """
    Calculates the diameter distance [Gpc] for an object of angular size
    theta and redshift z using DA = theta(radians) * DChi / (1+z)
    theta : float : angular size [arcsec]
    z     : float : redshift of object
    cosm  : Cosmology
    nz    : int (100) : number of z-values to use in DChi calculation
    """
    dchi = DChi( z, cosm=cosm, nz=nz )
    return theta * c.arcs2rad() * dchi / (1+z)
示例#3
0
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)
示例#4
0
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
示例#5
0
def path_diff( alpha, x ):
    """
    path_diff( alpha, x )
    -----------------------
    INPUT
    alpha  : scalar : observation angle [arcsec]
    x      : scalar or np.array : position of dust patch (source is at x=0, observer at x=1)
    -----------------------
    OUTPUT
    path difference associated with a particular alpha and x : alpha^2*(1-x)/(2x)
    """
    
    if np.size( alpha ) > 1:
        print 'Error: np.size(alpha) cannot be greater than one.'
        return
    if np.max( x ) > 1.0 or np.min( x ) < 0:
        print 'Error: x must be between 0 and 1'
        return

    alpha_rad = alpha * c.arcs2rad()

    return alpha_rad**2 * (1-x) / (2*x)
示例#6
0
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)
示例#7
0
    def getQs( self, a=1.0, E=1.0, cm=cmi.CmDrude(), getQ='sca', theta=None ):  # Takes single a and E argument

        if np.size(a) > 1:
            print 'Error: Can only specify one value for a'
            return

        indl90 = np.array([])  # Empty arrays indicate that there are no theta values set
        indg90 = np.array([])
          # Do not have to check if theta != None throughout calculation
        s1     = np.array([])
        s2     = np.array([])
        pi     = np.array([])
        pi0    = np.array([])
        pi1    = np.array([])
        tau    = np.array([])
        amu    = np.array([])

        if theta != None:
            if np.size(E) > 1 and np.size(E) != np.size(theta):
                print 'Error: If more than one E value specified, theta must have same size as E'
                return

            if np.size( theta ) == 1:
                theta = np.array( [theta] )
            
            theta_rad = theta * c.arcs2rad()
            amu       = np.abs( np.cos(theta_rad) )

            indl90    = np.where( theta_rad < np.pi/2.0 )
            indg90    = np.where( theta_rad >= np.pi/2.0 )
                        
            nang  = np.size( theta )
            s1    = np.zeros( nang, dtype='complex' )
            s2    = np.zeros( nang, dtype='complex' )
            pi    = np.zeros( nang, dtype='complex' )
            pi0   = np.zeros( nang, dtype='complex' )
            pi1   = np.zeros( nang, dtype='complex' ) + 1.0
            tau   = np.zeros( nang, dtype='complex' )

        refrel = cm.rp(E) + 1j*cm.ip(E)

        x      = ( 2.0 * np.pi * a*c.micron2cm() ) / ( c.kev2lam()/E  )
        y      = x * refrel
        ymod   = np.abs(y)
        nx     = np.size( x )


        # *** Series expansion terminated after NSTOP terms
        # Logarithmic derivatives calculated from NMX on down
        
        xstop  = x + 4.0 * np.power( x, 0.3333 ) + 2.0
        test   = np.append( xstop, ymod )
        nmx    = np.max( test ) + 15
        nmx    = np.int32(nmx)
      
        nstop  = xstop
#        nmxx   = 150000
        
#        if (nmx > nmxx):
#            print 'error: nmx > nmxx=', nmxx, ' for |m|x=', ymod

        # *** Logarithmic derivative D(J) calculated by downward recurrence
        # beginning with initial value (0.,0.) at J=NMX

        d = self.create_d(nx,nmx,y)
        
        # *** Riccati-Bessel functions with real argument X
        # calculated by upward recurrence

        psi0 = np.cos(x)
        psi1 = np.sin(x)
        chi0 = -np.sin(x)
        chi1 = np.cos(x)
        xi1  = psi1 - 1j * chi1

        qsca = 0.0    # scattering efficiency
        gsca = 0.0    # <cos(theta)>

        s1_ext = 0
        s2_ext = 0
        s1_back = 0
        s2_back = 0
        
        pi_ext  = 0
        pi0_ext = 0
        pi1_ext = 1
        tau_ext = 0

        p    = -1.0

        for n in np.arange( np.max(nstop) )+1:  # for n=1, nstop do begin
            en = n
            fn = (2.0*en+1.0)/ (en* (en+1.0))

            # for given N, PSI  = psi_n        CHI  = chi_n
            #              PSI1 = psi_{n-1}    CHI1 = chi_{n-1}
            #              PSI0 = psi_{n-2}    CHI0 = chi_{n-2}
            # Calculate psi_n and chi_n
            # *** Compute AN and BN:

            #*** Store previous values of AN and BN for use
            #    in computation of g=<cos(theta)>
            if n > 1:
                an1 = an
                bn1 = bn

            if nx > 1:
                ig  = nstop >= n
                
                psi    = np.zeros( nx )
                chi    = np.zeros( nx )

                psi[ig] = (2.0*en-1.0) * psi1[ig]/x[ig] - psi0[ig]
                chi[ig] = (2.0*en-1.0) * chi1[ig]/x[ig] - chi0[ig]
                xi      = psi - 1j * chi

                an = np.zeros( nx, dtype='complex' )
                bn = np.zeros( nx, dtype='complex' )
                an[ig], bn[ig] = scattering_utils.an_bn(d[ig,n],refrel[ig],en,x[ig],psi[ig],psi1[ig],xi[ig],xi1[ig])
                # an[ig], bn[ig] = self.an_bn(d[ig,n],refrel[ig],en,x[ig],psi[ig],psi1[ig],xi[ig],xi1[ig])
            else:
                psi = (2.0*en-1.0) * psi1/x - psi0
                chi = (2.0*en-1.0) * chi1/x - chi0
                xi  = psi - 1j * chi

                an = ( d[0,n]/refrel + en/x ) * psi - psi1
                an = an / ( ( d[0,n]/refrel + en/x ) * xi - xi1 )
                bn = ( refrel*d[0,n] + en / x ) * psi - psi1
                bn = bn / ( ( refrel*d[0,n] + en/x ) * xi - xi1 )


            # *** Augment sums for Qsca and g=<cos(theta)>
 
            # NOTE from LIA: In IDL version, bhmie casts double(an)
            # and double(bn).  This disgards the imaginary part.  To
            # avoid type casting errors, I use an.real and bn.real

            # Because animag and bnimag were intended to isolate the
            # real from imaginary parts, I replaced all instances of
            # double( foo * complex(0.d0,-1.d0) ) with foo.imag

            qsca += self.compute_qsca(en,an,bn)
            gsca += self.compute_gsca(en,an,bn)

            if n > 1:
                gsca += self.update_gsca(en,an,an1,bn,bn1)

            # *** Now calculate scattering intensity pattern
            #     First do angles from 0 to 90

                
            # LIA : Altered the two loops below so that only the indices where ang
            # < 90 are used.  Replaced (j) with [indl90]

            # Note also: If theta is specified, and np.size(E) > 1,
            # the number of E values must match the number of theta
            # values.  Cosmological halo functions will utilize this
            # Diff this way.

            pi  = pi1
            tau = en * amu * pi - (en + 1.0) * pi0
            
            if np.size(indl90) != 0:
                antmp = an
                bntmp = bn
                if nx > 1:
                    antmp = an[indl90]
                    bntmp = bn[indl90]  # For case where multiple E and theta are specified
                    
                s1[indl90]  = s1[indl90] + fn* (antmp*pi[indl90]+bntmp*tau[indl90])
                s2[indl90]  = s2[indl90] + fn* (antmp*tau[indl90]+bntmp*pi[indl90])
            #ENDIF

            pi_ext = pi1_ext
            tau_ext = en*1.0*pi_ext - (en+1.0)*pi0_ext
            
            s1_ext = s1_ext + fn* (an*pi_ext+bn*tau_ext)
            s2_ext = s2_ext + fn* (bn*pi_ext+an*tau_ext)

            # *** Now do angles greater than 90 using PI and TAU from
            #     angles less than 90.
            #     P=1 for N=1,3,...; P=-1 for N=2,4,...
            
            p = -p
            
            # LIA : Previous code used tau(j) from the previous loop.  How do I
            # get around this?

            if np.size(indg90) != 0:
                antmp = an
                bntmp = bn
                if nx > 1:
                    antmp = an[indg90]
                    bntmp = bn[indg90]  # For case where multiple E and theta are specified

                s1[indg90]  = s1[indg90] + fn*p* (antmp*pi[indg90]-bntmp*tau[indg90])
                s2[indg90]  = s2[indg90] + fn*p* (bntmp*pi[indg90]-antmp*tau[indg90])
            #ENDIF

            s1_back = s1_back + fn*p* (an*pi_ext-bn*tau_ext)
            s2_back = s2_back + fn*p* (bn*pi_ext-an*tau_ext)
            
            psi0 = psi1
            psi1 = psi
            chi0 = chi1
            chi1 = chi
            xi1  = psi1 - 1j*chi1

            # *** Compute pi_n for next value of n
            #     For each angle J, compute pi_n+1
            #     from PI = pi_n , PI0 = pi_n-1

            pi1  = ( (2.0*en+1.0)*amu*pi- (en+1.0)*pi0 ) / en
            pi0  = pi

            pi1_ext = ( (2.0*en+1.0)*1.0*pi_ext - (en+1.0)*pi0_ext ) / en
            pi0_ext = pi_ext

            # ENDFOR

        # *** Have summed sufficient terms.
        #     Now compute QSCA,QEXT,QBACK,and GSCA
        gsca = 2.0 * gsca / qsca
        qsca = ( 2.0 / np.power(x,2) ) * qsca

        # LIA : Changed qext to use s1(theta=0) instead of s1(1).  Why did the
        # original code use s1(1)?

        qext = ( 4.0 / np.power(x,2) ) * s1_ext.real
        qback = np.power( np.abs(s1_back)/x, 2) / np.pi

        if getQ == 'sca':
            return qsca
        if getQ == 'ext':
            return qext
        if getQ == 'back':
            return qback
        if getQ == 'gsca':
            return gsca
        if getQ == 'diff':
            bad_theta = np.where( theta_rad > np.pi )  #Set to 0 values where theta > !pi
            s1[bad_theta] = 0
            s2[bad_theta] = 0
            return 0.5 * ( np.power( np.abs(s1), 2 ) + np.power( np.abs(s2), 2) ) / (np.pi * x*x)
        else:
            return 0.0
示例#8
0
def UniformISM( halo, NH=1.0e20, d2g=0.009, nx=1000, usepathdiff=False ):
    """
    FUNCTION UniformISM( halo, NH=1.0e20, d2g=0.009, nx=1000, usepathdiff=False )
    MODIFIES halo.htype, halo.dist, halo.taux, halo.intensity
    ----------------------------------------------------------------------------
    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.mp() * 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