Example #1
0
    def Qsca( self, E, a=1.0, cm=cmi.CmDrude() ):

        if np.size(a) != 1:
            print 'Error: Must specify only 1 value of a'
            return

        a_cm = a * c.micron2cm()        # cm -- Can only be a single value
        lam  = c.kev2lam() / E          # cm -- Can be many values
        x    = 2.0 * np.pi * a_cm / lam
        mm1  = cm.rp(E) - 1 + 1j * cm.ip(E)
        return 2.0 * np.power( x, 2 ) * np.power( np.abs(mm1), 2 )
Example #2
0
    def __init__( self ):
        self.cmtype = 'Silicate'

        D03vals = c.restore( os.path.join(CMROOT,'CM_D03.pysav'))      # look up file
        
        lamvals = D03vals['Sil_lam']
        revals  = D03vals['Sil_re']
        imvals  = D03vals['Sil_im']
        
        lamEvals = c.kev2lam() / c.micron2cm() / lamvals # keV
        self.rp  = interp1d( lamEvals, revals )
        self.ip  = interp1d( lamEvals, imvals )
Example #3
0
    def __init__( self ):
        self.cmtype = 'Silicate'

        D03file = find_cmfile('CM_D03.pysav')
        D03vals = c.restore(D03file)      # look up file
        
        lamvals = D03vals['Sil_lam']
        revals  = D03vals['Sil_re']
        imvals  = D03vals['Sil_im']
        
        lamEvals = c.kev2lam() / c.micron2cm() / lamvals # keV
        self.rp  = interp1d( lamEvals, revals )
        self.ip  = interp1d( lamEvals, imvals )
Example #4
0
    def Diff( self, theta, E=1.0, a=1.0, cm=cmi.CmDrude() ): # cm^2 ster^-1
        
        if np.size(a) != 1:
            print 'Error: Must specify only 1 value of a'
            return
        if np.logical_and( np.size(E) > 1,                  # If more than 1 energy is specified
                           np.size(E) != np.size(theta) ):  # theta must be of the same size
            print 'Error: If specifying > 1 energy, must have same number of values for theta'
            return

        a_cm  = a * c.micron2cm()      # cm -- Can only be a single value
        lam   = c.kev2lam() / E   # cm
        x     = 2.0 * np.pi * a_cm / lam
        mm1   = cm.rp(E) + 1j * cm.ip(E) - 1
        thdep = 2./9. * np.exp( -np.power( theta/self.Char(a=a, E=E) , 2 ) / 2.0 )
        dsig  = 2.0 * np.power(a_cm,2) * np.power(x,4) * np.power( np.abs(mm1),2 )
        return dsig * thdep
Example #5
0
def sample_extinction(sample, lam, isample, NA=20, d2g=0.009, scatm=ss.makeScatmodel("RG", "Drude")):

    energy = c.kev2lam() / lam  # lam must be in cm to get keV
    logMD = sample_logMD(sample)
    MD = np.power(10.0, logMD)

    result = []
    for i in isample:
        logNH, amax, p = sample[i]
        print "logNH =", logNH, "\tamax =", amax, "\tp =", p
        da = (amax - AMIN) / np.float(NA)
        dist = dust.Dustdist(rad=np.arange(AMIN, amax + da, da), p=p)
        spec = dust.Dustspectrum(rad=dist, md=MD[i])
        kappa = ss.Kappascat(E=energy, dist=spec, scatm=scatm).kappa
        result.append(1.086 * MD[i] * kappa)

    return result
Example #6
0
 def get_Q( self, E, qtype, a ):
     try :
         data = parse_PAH( self.type )
     except :
         print 'ERROR: Cannot find PAH type', self.type
         return
     
     try :
         qvals = np.array( data[a][qtype] )
         wavel = np.array( data[a]['w(micron)'] )
     except :
         print 'ERROR: Cannot get grain size', a, 'for', self.stype
         return
     
     # Wavelengths were listed in reverse order
     q_interp = interp1d( wavel[::-1], qvals[::-1] )
     
     E_um = ( c.kev2lam() / E ) * 1.e4   # cm to um
     return q_interp( E_um )
Example #7
0
    def __init__( self, size='big', orient='perp' ):
        # size : string ('big' or 'small')
        #      : 'big' gives results for 0.1 um sized graphite grains at 20 K [Draine (2003)]
        #      : 'small' gives results for 0.01 um sized grains at 20 K
        # orient : string ('perp' or 'para')
        #        : 'perp' gives results for E-field perpendicular to c-axis
        #        : 'para' gives results for E-field parallel to c-axis
        #
        self.cmtype = 'Graphite'
        self.size   = size
        self.orient = orient
        
        D03file = find_cmfile('CM_D03.pysav') # look up file
        D03vals = c.restore(D03file) # read in index values
        
        if size == 'big':            
            if orient == 'perp':
                lamvals = D03vals['Cpe_010_lam']
                revals  = D03vals['Cpe_010_re']
                imvals  = D03vals['Cpe_010_im']
                
            if orient == 'para':
                lamvals = D03vals['Cpa_010_lam']
                revals  = D03vals['Cpa_010_re']
                imvals  = D03vals['Cpa_010_im']
                    
        if size == 'small':
                        
            if orient == 'perp':
                lamvals = D03vals['Cpe_001_lam']
                revals  = D03vals['Cpe_001_re']
                imvals  = D03vals['Cpe_001_im']

            if orient == 'para':
                lamvals = D03vals['Cpa_001_lam']
                revals  = D03vals['Cpa_001_re']
                imvals  = D03vals['Cpa_001_im']

        lamEvals = c.kev2lam() / c.micron2cm() / lamvals # keV
        self.rp  = interp1d( lamEvals, revals )
        self.ip  = interp1d( lamEvals, imvals )
Example #8
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
Example #9
0
 def rp( self, E ):
     mm1 = self.rho / ( 2.0*c.mp() ) * c.re()/(2.0*np.pi) * np.power( c.kev2lam()/E , 2 )
     return mm1+1