Beispiel #1
0
def traceSector(Rin,Rout,F,N,span=pi/12,d=.605,t=.775,gap=25.,\
                inc=1.5*pi/180,l=95.):
    """Trace Arcus sector
    """
    #Trace parameters
    R = np.arange(Rin, Rout, t)  #Vector of shell radii
    tg = .25 * np.arctan((R + d / 2) / F)  #Primary graze angles
    L = d / tan(tg)  #Primary length
    #L = 4*F*d/R #Vector of mirror lengths
    M = np.size(R)  #Number of shells

    #Trace rays through SPO modules
    traceSPO(R, L, F, N, M, span=span, d=d, t=t)

    #Determine outermost radius of grating array
    PT.transform(0, 0, -(L.max() + gap + 95.), 0, 0, 0)
    PT.flat()
    outerrad = np.max(sqrt(PT.x**2 + PT.y**2))
    hubdist = sqrt(outerrad**2 + (1e4 - (L.max() + gap + 95.))**2)
    angle = np.arctan(outerrad / (1e4 - (L.max() + gap + 95.)))

    #Trace grating array
    gratArray(outerrad, hubdist, angle, inc, l=l)

    return
Beispiel #2
0
def CDAbeam(num, div, pitch, roll, cda):
    ##    PT.transform(*cda)
    PT.pointsource(div, num)
    PT.transform(0, 0, 0, pitch, 0, 0)
    PT.transform(0, 0, 0, 0, 0, roll)
    PT.itransform(*cda)
    return
Beispiel #3
0
def traceCyl(align):
    """Traces a cylindrical approximation to Wolter I geometry
    Assumes 1 m radius of curvature in accordance with OAB samples
    align is a 12 element array giving the transformations to be
    applied to each mirror
    Uses identical set of rays each run defined upon module import
    Adds a restraint such that any vignetting over 25% results in
    a huge merit function
    """

    #Set up source
    np.random.seed(5)
    a, p, d, e = con.woltparam(1000., 10000.)
    r0 = con.primrad(10025., 1000., 10000.)
    r1 = con.primrad(10125., 1000., 10000.)
    dphi = 100. / 1000.
    PT.subannulus(r0, r1, dphi, 10**4)
    PT.transform(0, 0, 0, np.pi, 0, 0)
    PT.transform(0, 0, -10500., 0, 0, 0)

    #Trace primary cylinder: go to tangent point at center
    #of mirror and then rotate to cone angle, then move
    #to new cylinder axis and tracecyl
    rt = con.primrad(10075., 1000., 10000.)
    PT.transform(rt, 0, 0, 0, a, 0)
    PT.transform(*align[:6])
    PT.transform(0, 0, 0, np.pi / 2, 0, 0)
    PT.transform(-1000., 0, 0, 0, 0, 0)
    PT.cyl(1000.)

    #Vignette rays missing physical surface
    ind = np.logical_and(abs(PT.z) < 50., abs(PT.y) < 50.)
    PT.vignette(ind=ind)

    #Reflect and reverse transformations
    PT.reflect()
    PT.transform(1000., 0, 0, 0, 0, 0)
    PT.itransform(0, 0, 0, np.pi / 2, 0, 0)
    PT.itransform(*align[:6])
    PT.itransform(rt, 0, 0, 0, a, 0)

    #Trace secondary cylinder: same principle as before
    rt = con.secrad(9925., 1000., 10000.)
    PT.transform(0, 0, -150., 0, 0, 0)
    PT.transform(rt, 0, 0, 0, 3 * a, 0)
    PT.transform(*align[6:])
    PT.transform(0, 0, 0, np.pi / 2, 0, 0)
    PT.transform(-1000., 0, 0, 0, 0, 0)
    PT.cyl(1000.)

    #Vignette rays missing physical surface
    ind = np.logical_and(abs(PT.z) < 50., abs(PT.y) < 50.)
    PT.vignette(ind=ind)

    #Reflect and reverse transformations
    PT.reflect()
    PT.transform(1000., 0, 0, 0, 0, 0)
    PT.itransform(0, 0, 0, np.pi / 2, 0, 0)
    PT.itransform(*align[6:])
    PT.itransform(rt, 0, 0, 0, 3 * a, 0)

    #Go down to nominal focus
    PT.transform(0, 0, -9925., 0, 0, 0)
    PT.flat()

    #Compute merit function
    nom = PT.rmsCentroid() / 10**4 * 180. / np.pi * 60**2
    nom = nom + max((9500. - np.size(PT.x)), 0.)

    return nom
Beispiel #4
0
def traceWSShell(num,theta,r0,z0,phigh,plow,shigh,slow,\
                 energy,rough,chaseFocus=False,bestFocus=False):
    """Trace a WS mirror pair with 10 m focal length and
    mirror axial cutoffs defined by phigh,plow
    """
    #Define annulus of source rays
    a,p,d,e = con.woltparam(r0,z0)
    r1 = PT.wsPrimRad(plow,1.,r0,z0)#np.tan(a/2.)*(plow-10000.) + r0
    r2 = PT.wsPrimRad(phigh,1.,r0,z0)#np.tan(a/2.)*(phigh-10000.) + r0
    rays = PT.annulus(r1,r2,num)
    PT.transform(rays,0,0,0,np.pi,0,0)
    PT.transform(rays,0,0,z0,0,0,0)

    #Trace to primary
    PT.wsPrimary(rays,r0,z0,1.)
    #Handle vignetting
    ind = np.logical_and(rays[3]<phigh,rays[3]>plow)
    rays = PT.vignette(rays,ind=ind)
    #Vignette rays hitting backside of mirror
    dot = rays[4]*rays[7]+rays[5]*rays[8]+rays[6]*rays[9]
    ind = dot < 0.
    rays = PT.vignette(rays,ind=ind)
    #If all rays are vignetted, return
    if np.size(rays[1]) < 1:
        return 0.,0.,0.
    #Apply pointing error
    rays = [rays[0],rays[1],rays[2],rays[3],\
            rays[4]+np.sin(theta),rays[5],-np.sqrt(1-np.sin(theta)**2),\
            rays[7],rays[8],rays[9]]
##    PT.l = PT.l + np.sin(theta)
##    PT.n = -np.sqrt(1 - PT.l**2)
    #Reflect
    PT.reflect()
    #Compute mean incidence angle for reflectivity
    ang = np.abs(np.mean(np.arcsin(dot))) #radians
    refl1 = CXCreflIr(ang,energy,rough)
    #Total rays entering primary aperture
    N1 = np.size(rays[1])

    #Trace to secondary
    PT.wsSecondary(r0,z0,1.)
    #Vignette anything outside the physical range of the mirror
    ind = np.logical_and(rays[3]>slow,rays[3]<shigh)
    rays = PT.vignette(rays,ind=ind)
    #Vignette anything hitting the backside
    dot = rays[4]*rays[7]+rays[5]*rays[8]+rays[6]*rays[9]
    ind = dot < 0.
    rays = PT.vignette(rays,ind=ind)
    if np.size(rays[1]) < 1:
        return 0.,0.,0.
    PT.reflect()
    #Compute mean incidence angle for reflectivity
    ang = np.abs(np.mean(np.arcsin(dot))) #radians
    refl2 = CXCreflIr(ang,energy,rough)

    #Trace to focal plane
    rays = PT.flat(rays)

##    #Find Chase focus
##    delta = 0.
##    if chaseFocus or bestFocus:
##        cx,cy = PT.centroid()
##        r = np.sqrt(cx**2+cy**2)
##        delta = .0625*(1.+1)*(r**2*(phigh-plow)/10000.**2)\
##                *(1/np.tan(a))**2
##        PT.transform(0,0,delta,0,0,0)
##        PT.flat()
##
##    #Find best focus
##    delta2 = 0.
##    delta3 = 0.
##    if bestFocus:
##        try:
##            tran.focusI(rays,weights=
##        except:
##            pdb.set_trace()
##        PT.flat()

    return refl1*refl2,rays
Beispiel #5
0
def sphericalNodes(rin,z0,fov,Nshells,N):
    """This function will iteratively scan node positions
    about a sphere around the focus. Node will start in obvious
    vignetting position. Extreme rays will be traced including
    FoV. Node will be nudged outward until vignetting no longer
    occurs. Node will then be moved by the designated mechanical
    gap. Then the next node is traced in the same fashion.
    Assumptions: 50 mm symmetric gap
    """
    #Bookkeeping parameters
    f = np.sqrt(rin**2+z0**2)
    fov = fov/60.*np.pi/180. #fov to radians
    zlist = []
    rlist = []
    for i in range(Nshells):
        #Starting radius for next shell node
        rstart = PT.wsPrimRad(z0+225.,1.,rin,z0)
        #Reduce rstart until vignetting is reached
        flag = 0
        while flag==0:
            zstart = np.sqrt(f**2-rstart**2)
            #Set up rays
            r1 = PT.wsPrimRad(zstart+25.,1.,rstart,zstart)
            r2 = PT.wsPrimRad(zstart+225.,1.,rstart,zstart)
            PT.pointsource(0.,N)
            PT.z = np.repeat(10500.,N)
            PT.x = np.linspace(r1,r2,N)
            PT.n = np.repeat(-1.,N)
            #Perform trace and add FoV deflections to rays
            PT.wsPrimary(rstart,zstart,1.)
            PT.l = np.repeat(np.sin(fov),N)
            PT.n = -np.sqrt(1 - PT.l**2)
            #Verify that rays do not hit prior primary
            PT.wsPrimary(rin,z0,1.)
            if np.sum(PT.z<z0+225.) != 0:
                #Ray has hit
                print 'Ray hits prior primary!'
                flag = 1
            #Verify that rays do not hit prior secondary
            PT.wsPrimary(rstart,zstart,1.)
            PT.reflect()
            PT.wsSecondary(rstart,zstart,1.)
            PT.reflect()
            PT.wsSecondary(rin,z0,1.)
            if np.sum(PT.z > z0-225.) != 0:
                print 'Ray hits prior secondary!'
                flag = 1
            #Look at other deflection
            PT.pointsource(0.,N)
            PT.z = np.repeat(10500.,N)
            PT.x = np.linspace(r1,r2,N)
            PT.n = np.repeat(-1.,N)
            #Perform trace and add FoV deflections to rays
            PT.wsPrimary(rstart,zstart,1.)
            PT.l = np.repeat(-np.sin(fov),N)
            PT.n = -np.sqrt(1 - PT.l**2)
            #Verify that rays do not hit prior primary
            PT.wsPrimary(rin,z0,1.)
            if np.sum(PT.z<z0+225.) != 0:
                #Ray has hit
                print 'Ray hits prior primary!'
                flag = 1
            #Verify that rays do not hit prior secondary
            PT.wsPrimary(rstart,zstart,1.)
            PT.reflect()
            PT.wsSecondary(rstart,zstart,1.)
            PT.reflect()
            PT.wsSecondary(rin,z0,1.)
            if np.sum(PT.z > z0-225.) != 0:
                print 'Ray hits prior secondary!'
                flag = 1
            if flag==0:
                rstart = rstart - .01 #Take off 10 microns
##                sys.stdout.write(str(rstart)+'\n')
##                sys.stdout.flush()
        #Vignetting has been reached, append rstart and zstart
        #to list of node positions
        rlist.append(rstart)
        zlist.append(zstart)
        rin = rstart
        z0 = zstart
    return rlist,zlist
Beispiel #6
0
def SXperformance(theta,energy,rough,bestsurface=False,optsurface=False):
    """Go through a SMART-X prescription file and compute
    area weighted performance for a flat focal plane
    """
    #Load in rx data
##    rx = np.transpose(np.genfromtxt('/home/rallured/Dropbox/AXRO/WSTracing/'
##        'mirror-design-260sh-200mmlong-040mmthick'
##        '-3mdiam-10mfl-10arcmin-fov-planarIntersept032713.csv',\
##                       delimiter=','))
    rx = np.transpose(np.genfromtxt('/home/rallured/Dropbox/AXRO/WSTracing/'
                                    '150528_Pauls_Rx.csv',delimiter=','))
    geo = np.transpose(np.genfromtxt('/home/rallured/Dropbox/AXRO/WSTracing/'
                                     'geometric_transmission_102711.txt'))
    therm = np.transpose(np.genfromtxt('/home/rallured/Dropbox/AXRO/'
                                       'WSTracing/thermal_shield_transmission_102711.txt'))
    
    f = np.sqrt(rx[1][-1]**2+10000.**2)
    z = np.sqrt(f**2-rx[1]**2) #spherical
##    z = np.repeat(10000.,np.size(rx[1]))
##    ind = rx[0] > 210.
##    rx = rx[:,ind]
    Ns = np.shape(rx)[1]

    #Loop through and compute a resolution and a weight for each shell
    hpdTelescope = np.zeros(np.size(theta))
    rmsTelescope = np.zeros(np.size(theta))
    delta = np.zeros(np.size(theta))
    cent = np.zeros(np.size(theta))
    platefrac =  np.zeros(np.size(theta))
    #fig = plt.figure()
    for t in theta[:]:
        xi = np.array([])
        yi = np.array([])
        l = np.array([])
        m = np.array([])
        n = np.array([])
        weights = np.array([])
        #plt.clf()
        tstart = time.time()
        plate = np.zeros(Ns)
        for s in np.arange(0,Ns):
            if geo[1][s] > 0.:
                sys.stdout.write('Shell: %03i \r' % s)
                sys.stdout.flush()
                r,rays = traceWSShell(1000,t,rx[1][s],z[s],z[s]+225.,z[s]+25.,\
                                     z[s]-25.,z[s]-225.,energy,rough)
                r = r*geo[1][s]*rx[9][s] #Reflectivity*area*alignmentbars*vign
                #Account for thermal shield in shells 220-321
                if s > 219:
                    r = r * therm[1][np.abs(energy/1000.-therm[0]).argmin()]
                r = np.repeat(r,np.size(rays[1]))
                weights = np.append(weights,r)
                PT.conic(1107.799202,-1.)
                xi = np.append(xi,rays[1])
                yi = np.append(yi,rays[2])
                l = np.append(l,rays[4])
                m = np.append(m,rays[5])
                n = np.append(n,rays[6])
                if s%10==0:
                    plt.plot(rays[1][:100],rays[2][:100],'.')
                plate[s] = anal.centroid(rays,weights=r)[0]
        print time.time()-tstart

        #Have list of photon positions and weights
        #Need to compute centroid and then FoM
        #Normalize weights
        weights = weights/np.sum(weights)
        xi = np.array(xi,order='F')
        yi = np.array(yi,order='F')
        zi = np.zeros(np.size(xi)).astype('float')
        li = np.array(l,order='F')
        mi = np.array(m,order='F')
        ni = np.array(n,order='F')
        uxi = np.zeros(np.size(xi)).astype('float')
        uyi = np.zeros(np.size(xi)).astype('float')
        uzi = np.zeros(np.size(xi)).astype('float')
        rays = [np.zeros(np.size(xi)).astype('float'),\
                xi,yi,zi,\
                li,mi,ni,\
                uxi,uyi,uzi]
        if bestsurface:
            rays = tran.transform(rays,0,0,.25,0,0,0)
            surf.focusI(rays,weights=weights)
        if optsurface:
            PT.conic(1107.799202,-1.) #Emprically found best surface 1128.058314
        
        #Compute FoM
        rmsTelescope[t==theta] = PT.rmsCentroid(weights=weights)/10000.
        hpdTelescope[t==theta] = PT.hpd(weights=weights)/10000.
        cx,cy = PT.centroid()
        cent[t==theta] = cx
        ind = geo[1] > 0.
        platefrac[t==theta] = np.std(plate[ind]/1e4)/rmsTelescope[t==theta]
        
        print hpdTelescope[t==theta],rmsTelescope[t==theta]

    return hpdTelescope,rmsTelescope,delta,cent,plate
Beispiel #7
0
def traceChaseParam(num,
                    psi,
                    theta,
                    alpha,
                    L1,
                    z0,
                    bestFocus=False,
                    chaseFocus=False):
    """Trace a WS mirror pair using the parameters in Eq. 13
    of Chase & VanSpeybroeck
    Return the RMS radius of the focus
    Can specify whether to find the best focus or not
    """
    #Define annulus of source rays
    r0 = np.tan(4 * alpha) * z0
    alphap = 2 * alpha / (1 + 1 / psi)
    r1 = PT.wsPrimRad(z0 + L1, 1., r0, z0)  #np.tan(alphap/2.)*L1 + r0
    PT.annulus(r0, r1, num)
    PT.transform(0, 0, 0, np.pi, 0, 0)
    PT.transform(0, 0, -10000., 0, 0, 0)

    pdb.set_trace()

    #Trace to primary
    PT.wsPrimary(r0, z0, psi)
    #Handle vignetting
    PT.vignette()
    ind = np.logical_and(PT.z < z0 + L1, PT.z > z0)
    PT.vignette(ind=ind)
    ##    pdb.set_trace()
    #Apply pointing error
    PT.l = PT.l + np.sin(theta)
    PT.n = -np.sqrt(1 - PT.l**2)
    #Anything hitting backside goes away
    dot = PT.l * PT.ux + PT.m * PT.uy + PT.n * PT.uz
    ind = dot < 0.
    PT.vignette(ind=ind)
    if np.size(PT.x) < 1:
        return 0., 0., 0.
    #Reflect
    PT.reflect()

    N1 = np.size(PT.x)

    #Trace to secondary
    PT.wsSecondary(r0, z0, psi)
    #Vignette anything that did not converge
    PT.vignette()
    #Vignette anything hitting the backside
    dot = PT.l * PT.ux + PT.m * PT.uy + PT.n * PT.uz
    ind = dot < 0.
    PT.vignette(ind=ind)
    ##    #Vignette anything outside the physical range of the mirror
    ##    ind = np.logical_and(PT.z>z0-L1,PT.z<z0)
    ##    PT.vignette(ind=ind)
    if np.size(PT.x) < 1:
        return 0., 0., 0.
    PT.reflect()

    #Trace to focal plane
    PT.flat()
    cx, cy = PT.centroid()
    r = np.sqrt(cx**2 + cy**2)

    #Find best focus
    delta = 0.
    if chaseFocus or bestFocus:
        delta = .0625 * (psi + 1) * (r**2 * L1 / z0**2) * (1 /
                                                           np.tan(alpha))**2
        PT.transform(0, 0, delta, 0, 0, 0)
        PT.flat()

    delta2 = 0.
    delta3 = 0.
    if bestFocus:
        try:
            delta2 = PT.findimageplane(20., 100)
            PT.transform(0, 0, delta2, 0, 0, 0)
            delta3 = PT.findimageplane(1., 100)
            PT.transform(0, 0, delta3, 0, 0, 0)
        except:
            pdb.set_trace()
        PT.flat()

    return PT.rmsCentroid() / z0, delta + delta2 + delta3, r
Beispiel #8
0
def traceArcus(N,span=20.,d=.605,t=.775,gap=50.,\
                inc=1.5*pi/180,l=95.,bestFocus=bestfoc,order=0,\
                blazeYaw=yaw,wave=1.,marg=marg,findMargin=False,\
                analyzeLine=True,offX=0.,offY=0.,calcCentroid=False,\
               vis=False):
    """Trace Arcus sector
    """

    sys.stdout.write(str(wave) + '\t' + str(order) + '\r')
    sys.stdout.flush()

    if wave is not 'uniform':
        #Compute efficiency and determine whether to proceed
        geff = gratEff(order)
        #Add in grating efficiency and CCD QE
        #geff and ccdQE need to be arrays if wave is array
        g = geff(wave)
        det = ccdQE(wave)
        if g * det == 0.:
            return 0, 0
        g = g.reshape(np.size(g))

    #Assign random wavelength to each ray
    if wave == 'uniform':
        rays,weights,minfoc,lmax,wave,coords = defineSPOaperture(N,wave,\
                                offX=offX,offY=offY,\
                                vis=vis)

    else:
        rays,weights,minfoc,lmax,coords = defineSPOaperture(N,wave,offX=offX,\
                                offY=offY,\
                                vis=vis)

    #Trace rays through SPO modules

##    rays = plotting.pload('/home/rallured/Dropbox/Arcus/'
##                 'Raytrace/Performance/160516_SPORays.pkl')
##    weights = pyfits.getdata('/home/rallured/Dropbox/Arcus/'
##                             'Raytrace/Performance/160516_Weights.fits')
##    minfoc = 11972.53195
##    lmax = 115.97497
##    pdb.set_trace()

#Determine outermost radius of grating array
#outerrad should be fixed
    outerrad = outerradNom  #np.max(sqrt(rays[1]**2+rays[2]**2))
    foc = tran.applyTPos(0, 0, 0, coords, inverse=True)
    hubdist = sqrt(outerrad**2 + foc[2]**2)
    angle = np.arctan(outerrad / (minfoc - (lmax + gap + 95.)))
    thetag = angle - 1.5 * pi / 180.

    ##    print 'Outerrad: %f\nHubdist: %f\nLmax: %f\nOuter Focus: %f\n' % \
    ##          (outerrad,hubdist,L.max(),focVec[-1])

    #Trace grating array - need to add in grating efficiency and
    #CCD quantum efficiency
    if bestFocus is None:
        return gratArray(rays,outerrad,hubdist,angle,inc,l=l,\
                         weights=weights,order=-3,blazeYaw=blazeYaw,\
                         wave=2.4,coords=coords)
##        return traceSector(Rin,Rout,F,N,span=span,d=d,t=t,gap=gap,\
##                inc=inc,l=l,bestFocus=bestFocus,order=order,\
##                blazeYaw=blazeYaw,wave=wave,marg=marg)

    gratArray(rays,outerrad,hubdist,angle,inc,l=l,bestFocus=bestFocus,\
                  weights=weights,order=order,blazeYaw=blazeYaw,wave=wave,\
              offX=offX,vis=vis,coords=coords)

    #Grating vignetting
    weights = weights * (1 - abs(offX) / inc)

    if np.size(wave) == 1:
        #Account for grating efficiency
        weights = weights * g * det

    #Vignette rays with no weight (evanescence)
##    rays = tran.vignette(rays,weights>0.)
##    if len(rays[1])==0:
##        return 0.,0.

#Go to focal plane
    tran.transform(rays, 0, 0, bestFocus, 0, 0, 0, coords=coords)
    surf.flat(rays)

    if vis is True:
        pyfits.writeto('FocalPlanePos.fits',\
                       np.array([rays[1],rays[2],rays[3]]),\
                       clobber=True)
        pyfits.writeto('Wavelengths.fits',\
                       wave,clobber=True)

    if analyzeLine is False:
        return rays, weights

    #Get rid of outliers
    ind = np.abs(rays[2] - np.average(rays[2])) < 10.
    rays = PT.vignette(rays, ind=ind)
    weights = weights[ind]

    if calcCentroid is True:
        cent = anal.centroid(rays, weights=weights)
        if cent[0] < 100 or cent[1] < 100:
            pdb.set_trace()
        return anal.centroid(rays, weights=weights)

    #Get rid of rays that made it through


##    ind = rays[1] > 0
##    rays = PT.vignette(rays,ind=ind)
##    weights = weights[ind]

#If no rays made it through
    if np.size(rays[1]) == 0:
        return 0, 0, 0, 0

    #Look at resolution of this line
    try:
        cy = np.average(rays[2], weights=weights)
    except:
        pdb.set_trace()
    if findMargin is True:
        #Compute FWHM after Gaussian convolution
        fwhms = np.linspace(1, 3, 201) / 60.**2 * pi / 180 * 12e3
        tot = np.array([convolveLSF(rays,.001,m,weights=weights)\
                        for m in fwhms/2.35])
        #Convert to arcsec, return std of margin convolution
        marg = fwhms[np.argmin(
            np.abs(tot / 12e3 * 180 / pi * 60**2 - 3.))] / 2.35
        return marg, rays
    fwhm = convolveLSF(rays, .001, marg, weights=weights)
    resolution = cy / fwhm
    weights = weights**.799 * .83 * .8 * .9  #Grat plates, azimuthal ribs, packing
    area = np.sum(weights)
    #print resolution
    #print area
    #print sqrt((cy/3000)**2 - lsf**2)/F * 180/pi*60**2
    ##    print 'Order: %i, Wave: %.2f\n'%(order,wave)
    return resolution, area, np.nanmean(rays[1]), np.nanmean(rays[2]),fwhm,\
           rays,weights
Beispiel #9
0
def traceSPO(R, L, F, N, M, span=pi / 12, d=.605, t=.775):
    """Trace SPO surfaces sequentially. Collect rays from
    each SPO shell and set them to the PT rays at the end.
    Start at the inner radius, use the wafer and pore thicknesses
    to vignette and compute the next radius, loop while
    radius is less than Rout.
    """
    #Ray bookkeeping arrays
    tx = np.zeros(M * N)
    ty = np.zeros(M * N)
    tz = np.zeros(M * N)
    tl = np.zeros(M * N)
    tm = np.zeros(M * N)
    tn = np.zeros(M * N)
    tux = np.zeros(M * N)
    tuy = np.zeros(M * N)
    tuz = np.zeros(M * N)

    #Loop through shell radii and collect rays
    for i in range(M):
        #Set up source annulus
        PT.subannulus(R[i], R[i] + d, span, N)
        #Transform rays to be above xy plane
        PT.n = -PT.n
        PT.transform(0, 0, -100., 0, 0, 0)
        #Trace to primary
        PT.spoPrimary(R[i], F)
        PT.reflect()
        #Vignette
        ind = np.logical_and(PT.z <= L[i], PT.z >= 0.)
        if np.sum(ind) < N:
            pdb.set_trace()
        PT.vignette(np.logical_and(PT.z <= L[i], PT.z >= 0.))
        #Trace to secondary
        PT.spoSecondary(R[i], F)
        PT.reflect()
        #Vignette
        ind = np.logical_and(PT.z <= 0., PT.z >= -L[i])
        if np.sum(ind) < N:
            pdb.set_trace()
        PT.vignette(ind)
        #Collect rays
        try:
            tx[i * N:(i + 1) * N] = PT.x
            ty[i * N:(i + 1) * N] = PT.y
            tz[i * N:(i + 1) * N] = PT.z
            tl[i * N:(i + 1) * N] = PT.l
            tm[i * N:(i + 1) * N] = PT.m
            tn[i * N:(i + 1) * N] = PT.n
            tux[i * N:(i + 1) * N] = PT.ux
            tuy[i * N:(i + 1) * N] = PT.uy
            tuz[i * N:(i + 1) * N] = PT.uz
        except:
            pdb.set_trace()

    #Set to PT rays
    try:
        PT.x = tx
        PT.y = ty
        PT.z = tz
        PT.l = tl
        PT.m = tm
        PT.n = tn
        PT.ux = tux
        PT.uy = tuy
        PT.uz = tuz
    except:
        pdb.set_trace()

    return
Beispiel #10
0
import traces.PyTrace as PT
import time

tstart = time.time()
PT.circularbeam(200., 512 * 512)
print time.time() - tstart
gpus = PT.transferToGPU()
print time.time() - tstart
PT.radgratC[512, 512](12.e3, 160. / 12.e3, 1, 1., *gpus[:6])
PT.radgratC[512, 512](12.e3, 160. / 12.e3, 1, 1., *gpus[:6])
PT.radgratC[512, 512](12.e3, 160. / 12.e3, 1, 1., *gpus[:6])
PT.radgratC[512, 512](12.e3, 160. / 12.e3, 1, 1., *gpus[:6])
PT.returnFromGPU(*gpus)
print time.time() - tstart

##PT.circularbeam(200.,512*512)
##print time.time()-tstart
##PT.radgrat(12.e3,160./12.e3,1,1.)
##PT.radgrat(12.e3,160./12.e3,1,1.)
##PT.radgrat(12.e3,160./12.e3,1,1.)
##PT.radgrat(12.e3,160./12.e3,1,1.)
##print time.time()-tstart
Beispiel #11
0
def traceFromMask(N,
                  numholes,
                  cda,
                  fold,
                  retro,
                  primary,
                  foldrot=0.,
                  retrorot=0.):
    #Vignette at proper hole
    h = hartmannMask()
    ind = h == N
    PT.vignette(ind=ind)

    #Continue trace up to retro and back to CDA
    PT.transform(0, -123.41, 1156.48 - 651.57 - 134.18, 0, 0, 0)
    PT.flat()
    PT.transform(0, 0, 0, pi, 0, 0)
    PT.transform(*retro)
    PT.zernsurfrot(retrosag, retrofig, 378. / 2, -8.993 * pi / 180 + retrorot)
    PT.itransform(*retro)
    PT.reflect()
    PT.transform(0, 0, 0, -pi, 0, 0)
    PT.transform(0, 123.41, -1156.48 + 651.57 + 134.18, 0, 0, 0)
    PT.flat()
    h = hartmannMask()
    ind = h == N
    PT.vignette(ind=ind)
    PT.transform(0, 0, -134.18, 0, 0, 0)
    rt = conicsolve.primrad(8475., 220., 8400.)
    PT.transform(0, -rt, 75., 0, 0, 0)
    PT.transform(*primary)
    PT.transform(0, rt, -8475., 0, 0, 0)
    PT.wolterprimary(220., 8400.)
    ind = logical_and(PT.z < 8525., PT.z > 8425.)
    PT.vignette(ind=ind)
    PT.reflect()
    PT.transform(0, -rt, 8475., 0, 0, 0)
    PT.itransform(*primary)
    PT.transform(0, rt, -8475., 0, 0, 0)
    PT.transform(0,-85.12,8400.-651.57+85.12\
                 ,0,0,0)
    PT.transform(0, 0, 0, -pi / 4, 0, 0)
    PT.transform(0, 0, 0, 0, 0, pi)
    PT.flat()
    PT.transform(*fold)
    PT.zernsurfrot(foldsag, foldfig, 406. / 2, -174.659 * pi / 180 + foldrot)
    PT.itransform(*fold)
    PT.reflect()
    PT.transform(0, 0, 0, 0, 0, -pi)
    PT.transform(0, 0, 0, 3 * pi / 4, 0, 0)
    PT.transform(0,-85.12,-85.12-(conicsolve.primfocus(220.,8400.)-651.57)\
                 ,0,0,0)
    PT.transform(*cda)
    PT.flat()

    return
Beispiel #12
0
def primMaskTrace(fold, primary, woltVignette=True, foldrot=0.):
    #Get Wolter parameters
    alpha, p, d, e = conicsolve.woltparam(220., 8400.)
    primfoc = conicsolve.primfocus(220., 8400.)

    #Trace to fold mirror
    #translate to center of fold mirror
    PT.transform(0., 85.12, primfoc - 651.57 + 85.12, 0, 0, 0)
    #rotate so surface normal points in correct direction
    PT.transform(0, 0, 0, -3 * pi / 4, 0, 0)
    PT.transform(0, 0, 0, 0, 0, pi)
    #trace to fold flat
    PT.flat()
    #Introduce fold misalignment
    PT.transform(*fold)
    PT.zernsurfrot(foldsag, foldfig, 406. / 2, -174.659 * pi / 180 + foldrot)
    PT.itransform(*fold)
    PT.reflect()
    PT.transform(0, 0, 0, 0, 0, -pi)
    PT.transform(0, 0, 0, pi / 4, 0, 0)

    #Translate to optical axis mid-plane, then down to image of
    #primary focus, place primary mirror and trace
    PT.transform(0, 85.12, 651.57 - 85.12, 0, 0, 0)
    PT.flat()
    ##    pdb.set_trace()
    rt = conicsolve.primrad(8475., 220., 8400.)
    PT.transform(0, -rt, 75., 0, 0, 0)
    PT.transform(*primary)
    PT.transform(0, rt, -8475., 0, 0, 0)
    ##    PT.wolterprimary(220.,8400.)
    PT.primaryLL(220., 8400., 8525., 8425., 30. * np.pi / 180., pcoeff, pax,
                 paz)
    if woltVignette is True:
        ind = logical_and(PT.z < 8525., PT.z > 8425.)
        PT.vignette(ind=ind)
    PT.reflect()
    PT.transform(0, -rt, 8475., 0, 0, 0)
    PT.itransform(*primary)
    PT.transform(0, rt, -8475., 0, 0, 0)

    #Move back up to mask plane and trace flat
    PT.transform(0, 0, 8400. + 134.18, 0, 0, 0)
    PT.flat()
    ##    pdb.set_trace()

    #Rays should now be at Hartmann mask plane

    return
Beispiel #13
0
def fullFromMask(N, cda, fold, retro, prim, sec, foldrot=0., retrorot=0.):
    ##    pdb.set_trace()
    #Vignette at proper hole
    h = hartmannMask()
    ind = h == N
    PT.vignette(ind=ind)

    #Continue trace up to retro and back to CDA
    PT.transform(0, -123.41, 1156.48 - 651.57 - 134.18, 0, 0, 0)
    PT.flat()
    PT.transform(0, 0, 0, pi, 0, 0)
    PT.transform(*retro)
    PT.zernsurfrot(retrosag, retrofig, 378. / 2, -8.993 * pi / 180 + retrorot)
    PT.itransform(*retro)
    PT.reflect()
    PT.transform(0, 0, 0, -pi, 0, 0)

    #Back to mask
    PT.transform(0, 123.41, -1156.48 + 651.57 + 134.18, 0, 0, 0)
    PT.flat()
    h = hartmannMask()
    ind = h == N
    PT.vignette(ind=ind)

    #Place Wolter surfaces
    PT.transform(0, 0, -134.18 - 8400., 0, 0, 0)
    PT.transform(0, -conicsolve.primrad(8425., 220., 8400.), 8425., 0, 0, 0)
    PT.transform(*prim)
    PT.itransform(0, -conicsolve.primrad(8425., 220., 8400.), 8425., 0, 0, 0)
    ##    PT.wolterprimary(220.,8400.)
    PT.primaryLL(220., 8400., 8525., 8425., 30. * np.pi / 180., pcoeff, pax,
                 paz)
    pdb.set_trace()
    ind = logical_and(PT.z < 8525., PT.z > 8425.)
    PT.vignette(ind=ind)
    PT.transform(0, -conicsolve.primrad(8425., 220., 8400.), 8425., 0, 0, 0)
    PT.itransform(*prim)
    PT.itransform(0, -conicsolve.primrad(8425., 220., 8400.), 8425., 0, 0, 0)
    PT.reflect()

    #Wolter secondary
    PT.transform(0, -conicsolve.secrad(8325., 220., 8400.), 8325., 0, 0, 0)
    PT.transform(*sec)
    PT.itransform(0, -conicsolve.secrad(8325., 220., 8400.), 8325., 0, 0, 0)
    PT.woltersecondary(220., 8400.)
    ind = logical_and(PT.z < 8375., PT.z > 8275.)
    PT.vignette(ind=ind)
    PT.reflect()
    PT.transform(0, -conicsolve.secrad(8325., 220., 8400.), 8325., 0, 0, 0)
    PT.itransform(*sec)
    PT.itransform(0, -conicsolve.secrad(8325., 220., 8400.), 8325., 0, 0, 0)

    ##    PT.woltersecondary(220.,8400.)
    ##    ind = logical_and(PT.z<8375.,PT.z>8275.)
    ##    PT.vignette(ind=ind)
    ##    PT.reflect()

    #Back to fold
    PT.transform(0,-85.12,8400.-651.57+85.12\
                 ,0,0,0)
    PT.transform(0, 0, 0, -pi / 4, 0, 0)
    PT.transform(0, 0, 0, 0, 0, pi)
    PT.flat()
    PT.transform(*fold)
    PT.zernsurfrot(foldsag, foldfig, 406. / 2, -174.659 * pi / 180 + foldrot)
    PT.itransform(*fold)
    PT.reflect()
    PT.transform(0, 0, 0, 0, 0, -pi)

    #Back to CDA
    PT.transform(0, 0, 0, 3 * pi / 4, 0, 0)
    PT.transform(0,-85.12,-85.12-8400.+651.57\
                 ,0,0,0)
    PT.transform(*cda)
    PT.flat()

    return
Beispiel #14
0
def arc(inc, yaw, hubdist, wave, order, dpermm):
    """Return x and y positions of diffraction arc
    as a function of wavelength for a given order"""
    #Set up source ray
    PT.circularbeam(0., 1)
    #Transform to grating frame
    PT.transform(0, 0, 0, pi / 2 + inc, 0, 0)
    PT.transform(0, 0, 0, 0, 0, yaw)
    PT.flat()
    #Apply grating
    PT.reflect()
    PT.radgrat(hubdist, dpermm, order, wave)
    #Go to focus
    PT.transform(0, 0, 0, 0, 0, -yaw)
    PT.transform(0, hubdist, 0, 0, 0, 0)
    PT.transform(0, 0, 0, pi / 2, 0, 0)
    PT.flat()
    #Get ray positions
    return PT.x[0], PT.y[0]
Beispiel #15
0
def traceSPO(R,L,focVec,N,M,spanv,wave,d=.605,t=.775,offX=0.,offY=0.,\
             vis=None,ang=None,coords=None,scatter=False):
    """Trace SPO surfaces sequentially. Collect rays from
    each SPO shell and set them to the PT rays at the end.
    Start at the inner radius, use the wafer and pore thicknesses
    to vignette and compute the next radius, loop while
    radius is less than Rout.
    """
    #Ray bookkeeping arrays
    trays = [np.zeros(M * N) for n in range(10)]
    if vis is True:
        xp, yp, zp = [], [], []
        xs, ys, zs = [], [], []

    #Loop through shell radii and collect rays
    ref = np.zeros(M * N)
    for i in range(M):
        #Set up source annulus
        rays = sources.subannulus(R[i], R[i] + d, spanv[i], N, zhat=-1.)
        z, n = rays[3], rays[6]
        ##        #Transform rays to be above xy plane
        ##        tran.transform(rays,0,0,-100.,0,0,0,coords=coords)
        #Apply angular offset
        tran.transform(rays, 0, 0, 0, 0, 0, ang)
        #Get initial positions
        glob = None
        if vis is True:
            #Initial ray positions
            x0, y0, z0 = np.copy(rays[1]), np.copy(rays[2]), np.copy(rays[3])


##
##            #Establish 3d figure
##            fig = plt.figure('vis')
##            ax = fig.add_subplot(111,projection='3d')
#Trace to primary
        surf.spoPrimary(rays, R[i], focVec[i])

        #Export primary ray positions in global reference frame
        if vis is True:
            tran.transform(rays, 0, 0, -focVec[i], 0, 0, 0)
            xp = np.concatenate((xp, rays[1]))
            yp = np.concatenate((yp, rays[2]))
            zp = np.concatenate((zp, rays[3]))
            tran.transform(rays, 0, 0, focVec[i], 0, 0, 0)

        #Add offsets if they apply
        rays = [rays[0],rays[1],rays[2],rays[3],\
                rays[4]+offX,rays[5]+offY,\
                -np.sqrt(rays[6]**2-offX**2-offY**2),\
                rays[7],rays[8],rays[9]]
        tran.reflect(rays)

        #Compute reflectivity
        inc = PT.grazeAngle(rays)  #np.arcsin(l*ux+m*uy+n*uz)
        if np.size(wave) == 1:
            refl = sporef(inc * 180 / np.pi, 1239.8 / wave)
        else:
            refl = np.diag(
                sporef(inc * 180 / np.pi, 1239.8 / wave[i * N:(i + 1) * N]))

        #Trace to secondary
        surf.spoSecondary(rays, R[i], focVec[i])
        tran.reflect(rays)

        #Add scatter
        if scatter is True:
            theta = np.arctan2(rays[2], rays[1])
            inplane = np.random.normal(scale=10. / 2.35 * 5e-6, size=N)
            outplane = np.random.normal(scale=1.5 / 2.35 * 5e-6, size=N)
            rays[4] = rays[4] + inplane * np.cos(theta) + outplane * np.sin(
                theta)
            rays[5] = rays[5] + inplane * np.sin(theta) + outplane * np.cos(
                theta)
            rays[6] = -np.sqrt(1. - rays[5]**2 - rays[4]**2)

        #Compute reflectivity
        inc = PT.grazeAngle(rays)  #inc = np.arcsin(l*ux+m*uy+n*uz)
        if np.size(wave) == 1:
            ref[i*N:(i+1)*N] = refl * sporef(inc*180/np.pi\
                                             ,1239.8/wave)
        else:
            ref[i*N:(i+1)*N] = refl * np.diag(sporef(inc*180/np.pi\
                                             ,1239.8/wave[i*N:(i+1)*N]))

        #Set plane to be at focus
        tran.transform(rays, 0, 0, -focVec[i], 0, 0, 0, coords=coords)
        #Collect rays
        try:
            for t in range(1, 7):
                temp = trays[t]
                temp[i * N:(i + 1) * N] = rays[t]
        except:
            pdb.set_trace()

    #Export secondary ray positions in global coordinate frame
    if vis is True:
        return trays, ref, [xp, yp, zp], [trays[1], trays[2], trays[3]]

    return trays, ref
Beispiel #16
0
def traceBeam(gratAlign,order=1.,wave=2.48,blaze=30.):
    """Traces a beam to grating, applies misalignment,
    then traces to focal plane and returns mean
    x,y position
    x refers to spectral direction
    """
    #Compute yaw to achieve Littrow configuration
    phi0 = arccos(sin(1.5*pi/180)/cos(blaze*pi/180))
    yaw = -tan(sin(blaze*pi/180)/tan(phi0))
##    yaw = yaw - pi/2
##    pdb.set_trace()
    
    #Set up beam
    rays = PT.pointsource(0.,10) #multiple rays to avoid Python complaints

    #Rotate to nominal grating reference frame
    PT.transform(rays,0,0,0,pi/2+1.5*pi/180,0,0)

    #Apply misalignment
    PT.transform(rays,*gratAlign)

    #Trace grating
    PT.flat(rays)
    PT.transform(rays,0,0,0,0,0,yaw) #-1.73211678*pi/180
    PT.radgrat(rays,11500.,160./11500.,order,wave)
    PT.itransform(rays,0,0,0,0,0,yaw)

    #Reverse misalignment
    PT.itransform(rays,*gratAlign)

    #Trace to focal plane
    PT.transform(rays,0,11500.,0,0,0,0)
    PT.transform(rays,0,0,0,pi/2,0,0)
    PT.flat(rays)

    #Return mean positions
    return mean(rays[1]),mean(rays[2])
Beispiel #17
0
def gratArray(outerrad, hubdist, angle, inc, l=95.):
    """Trace rays leaving SPO petal to the fanned grating array.
    Start with outermost radius and rotate grating array about
    the hub. Define outermost grating position by max ray radius
    at desired axial height.
    Rays have been traced to bottom of outermost grating.
    """
    #Put origin at bottom of outermost grating
    PT.transform(outerrad, 0, 0, 0, 0, 0)
    #Go to proper incidence angle of grating
    PT.transform(0, 0, 0, 0, 0, -pi / 2)
    PT.transform(0, 0, 0, -pi / 2 - angle + inc, 0, 0)
    #Go to hub
    PT.transform(0, hubdist, 0, 0, 0, 0)
    #Trace out gratings until no rays hit a grating
    #Flat
    #Indices
    #Reflect
    #Apply Grating
    #Next
    PT.flat()
    ind = np.logical_and(PT.y < -hubdist, PT.y > -l - hubdist)
    ang = l * sin(inc) / hubdist
    i = 0
    while np.sum(ind) > 0:
        i = i + 1
        PT.reflect(ind=ind)
        PT.radgrat(0., 160. / hubdist, 0, 1., ind=ind)
        PT.transform(0, 0, 0, ang, 0, 0)
        PT.flat()
        ind = np.logical_and(PT.y < -hubdist, PT.y > -l - hubdist)
        sys.stdout.write('%i \r' % i)
        sys.stdout.flush()
    pdb.set_trace()
Beispiel #18
0
def traceWSShell(num,theta,r0,z0,phigh,plow,shigh,slow,\
                 chaseFocus=False,bestFocus=False):
    """Trace a WS mirror pair with 10 m focal length and
    mirror axial cutoffs defined by phigh,plow
    """
    #Define annulus of source rays
    a, p, d, e = con.woltparam(r0, z0)
    r1 = PT.wsPrimRad(plow, 1., r0, z0)  #np.tan(a/2.)*(plow-10000.) + r0
    r2 = PT.wsPrimRad(phigh, 1., r0, z0)  #np.tan(a/2.)*(phigh-10000.) + r0
    ##    r2 = np.mean([r1,r2])
    PT.annulus(r1, r2, num)
    PT.transform(0, 0, 0, np.pi, 0, 0)
    PT.transform(0, 0, z0, 0, 0, 0)
    ##    pdb.set_trace()

    #Trace to primary
    PT.wsPrimary(r0, z0, 1.)
    #Handle vignetting
    PT.vignette()
    ind = np.logical_and(PT.z < phigh, PT.z > plow)
    PT.vignette(ind=ind)
    #Vignette rays hitting backside of mirror
    dot = PT.l * PT.ux + PT.m * PT.uy + PT.n * PT.uz
    ind = dot < 0.
    PT.vignette(ind=ind)
    #If all rays are vignetted, return
    if np.size(PT.x) < 1:
        return 0., 0., 0.
    #Apply pointing error
    PT.l = PT.l + np.sin(theta)
    PT.n = -np.sqrt(1 - PT.l**2)
    #Reflect
    PT.reflect()
    #Compute mean incidence angle for reflectivity
    ##    ang = np.abs(np.mean(np.arcsin(dot))) #radians
    ##    refl1 = CXCreflIr(ang,energy,rough)
    #Total rays entering primary aperture
    N1 = np.size(PT.x)

    #Trace to secondary
    PT.wsSecondary(r0, z0, 1.)
    #Vignette anything that did not converge
    PT.vignette()
    #Vignette anything outside the physical range of the mirror
    ind = np.logical_and(PT.z > slow, PT.z < shigh)
    PT.vignette(ind=ind)
    #Vignette anything hitting the backside
    dot = PT.l * PT.ux + PT.m * PT.uy + PT.n * PT.uz
    ind = dot < 0.
    PT.vignette(ind=ind)
    if np.size(PT.x) < 1:
        return 0., 0., 0.
    PT.reflect()
    #Compute mean incidence angle for reflectivity
    ##    ang = np.abs(np.mean(np.arcsin(dot))) #radians
    ##    refl2 = CXCreflIr(ang,energy,rough)

    #Trace to focal plane
    PT.flat()

    #Find Chase focus
    delta = 0.
    if chaseFocus or bestFocus:
        cx, cy = PT.centroid()
        r = np.sqrt(cx**2 + cy**2)
        delta = .0625*(1.+1)*(r**2*(phigh-plow)/10000.**2)\
                *(1/np.tan(a))**2
        PT.transform(0, 0, delta, 0, 0, 0)
        PT.flat()

    #Find best focus
    delta2 = 0.
    delta3 = 0.
    if bestFocus:
        try:
            delta2 = PT.findimageplane(20., 100)
            PT.transform(0, 0, delta2, 0, 0, 0)
            delta3 = PT.findimageplane(1., 100)
            PT.transform(0, 0, delta3, 0, 0, 0)
        except:
            pdb.set_trace()
        PT.flat()

    #return refl1*refl2
    return PT.hpd(), PT.rmsCentroid(), delta
Beispiel #19
0
def setupSource(wave,N,radius=450.,focal=8000.,scatter=50e-6,\
                gwidth=25.,glength=32.,pitch=0.,yaw=0.,roll=0.):
    """Set up converging beam with some scatter.
    Idea is to place a grating at radius and focal length
    with an incidence angle of 1.5 degrees
    """
    #Setup rays over grating surface
    PT.x = np.random.uniform(low=-gwidth / 2, high=gwidth / 2, size=N)
    PT.y = np.random.uniform(low=-glength / 2, high=glength / 2, size=N)
    PT.z = np.repeat(0., N)
    PT.l = np.zeros(N)
    PT.m = np.zeros(N)
    PT.n = np.zeros(N)
    PT.ux = np.zeros(N)
    PT.uy = np.zeros(N)
    PT.uz = np.repeat(1., N)
    #Transform to nominal focus to set ray cosines
    PT.transform(0, 0, 0, -88.5 * np.pi / 180, 0, 0)
    PT.transform(0, radius, -focal, 0, 0, 0)
    rad = np.sqrt(PT.x**2 + PT.y**2 + PT.z**2)
    PT.l = -PT.x / rad
    PT.m = -PT.y / rad
    PT.n = -PT.z / rad
    #Add scatter
    PT.l = PT.l + scatter / 10. * np.random.normal(size=N)
    PT.m = PT.m + scatter * np.random.normal(size=N)
    PT.n = -np.sqrt(1. - PT.l**2 - PT.m**2)
    #Go back to plane of of grating
    PT.transform(0, -radius, focal, 0, 0, 0)
    PT.transform(0, 0, 0, 88.5 * np.pi / 180, 0, np.pi)
    #Place grating and diffract
    hubdist = np.sqrt(radius**2 + focal**2) * np.cos(1.5 * np.pi / 180)
    PT.flat()
    PT.reflect()
    PT.transform(0, 0, 0, pitch, roll, yaw)
    PT.radgrat(hubdist, 160. / hubdist, 1, wave)
    PT.itransform(0, 0, 0, pitch, roll, yaw)
    #Go to focal plane
    PT.transform(0, 0, 0, np.pi / 2, 0, 0)
    PT.transform(0, 0, -hubdist, 0, 0, 0)
    PT.transform(0, 0, 27.27727728 - .04904905, 0, 0, 0)
    PT.flat()

    #plt.plot(PT.x,PT.y,'.')

    return PT.centroid()
Beispiel #20
0
def traceSector(Rin,Rout,F,N,span=20.,d=.605,t=.775,gap=50.,\
                inc=1.5*pi/180,l=95.,bestFocus=None,order=0,\
                blazeYaw=0.,wave=1.,marg=0.,findMargin=False,\
                analyzeLine=True,offX=0.,offY=0.):
    """Trace Arcus sector
    """
    #Trace parameters
    R = np.arange(Rin, Rout, t)  #Vector of shell radii
    tg = .25 * np.arctan((R + d / 2) / F)  #Primary graze angles
    L = d / tan(tg)  #Primary length
    #L = 4*F*d/R #Vector of mirror lengths
    M = np.size(R)  #Number of shells

    #Create focal length vector to implement spherical principle surface
    #This should be changed based on nominal focal lengths of
    #modules from cosine.
    focConst = F**2  #+Rin**2
    focVec = sqrt(focConst - R**2)

    #Weight vector for shell radii
    weights = np.zeros(M * N)
    spanv = np.zeros(M)
    for i in range(M):
        #Full angular span of each shell
        spanv[i] = 2 * np.arcsin(span / 2 / R[i])
        #Geometric area in each shell - 10^4 is unit conversion
        weights[i * N:(i + 1) *
                N] = ((R[i] + d)**2 - R[i]**2) * spanv[i] / 2 / 10000.

    #Assign random wavelength to each ray
    if wave == 'uniform':
        wave = np.random.uniform(3.6, 3.6 * 2, size=M * N)

    #Trace rays through SPO modules
    rays,refl = traceSPO(R,L,focVec,N,M,spanv,wave,d=d,t=t,\
                         offX=offX,offY=offY)
    #Can call traceSPO repeatedly for each individual module
    #Then send rays through grating array

    #Refl needs to be array if wave is array
    weights = weights * refl

    #Determine outermost radius of grating array
    PT.transform(rays, 0, 0, focVec[-1] - (L.max() + gap + 95.), 0, 0, 0)
    PT.flat(rays)
    #outerrad should be fixed
    outerrad = np.max(sqrt(rays[1]**2 + rays[2]**2))
    hubdist = sqrt(outerrad**2 + (focVec[-1] - (L.max() + gap + 95.))**2)
    angle = np.arctan(outerrad / (focVec[-1] - (L.max() + gap + 95.)))
    thetag = angle - 1.5 * pi / 180.
    ##    print 'Outerrad: %f\nHubdist: %f\nLmax: %f\nOuter Focus: %f\n' % \
    ##          (outerrad,hubdist,L.max(),focVec[-1])
    pdb.set_trace()
    #Trace grating array - need to add in grating efficiency and
    #CCD quantum efficiency
    if bestFocus is None:
        return gratArray(rays,outerrad,hubdist,angle,inc,l=l,\
                         weights=weights,order=-3,blazeYaw=blazeYaw,\
                         wave=2.4)
##        return traceSector(Rin,Rout,F,N,span=span,d=d,t=t,gap=gap,\
##                inc=inc,l=l,bestFocus=bestFocus,order=order,\
##                blazeYaw=blazeYaw,wave=wave,marg=marg)

    gratArray(rays,outerrad,hubdist,angle,inc,l=l,bestFocus=bestFocus,\
                  weights=weights,order=order,blazeYaw=blazeYaw,wave=wave)

    #Account for grating efficiency
    geff = gratEff(order)
    #Add in grating efficiency and CCD QE
    #geff and ccdQE need to be arrays if wave is array
    g = geff(wave)
    g = g.reshape(np.size(g))
    weights = weights * g * ccdQE(wave)

    #Go to focal plane
    tran.transform(rays, 0, 0, bestFocus, 0, 0, 0)
    surf.flat(rays)

    if analyzeLine is False:
        return rays, weights, wave

    #Get rid of rays that made it through


##    ind = rays[1] > 0
##    rays = PT.vignette(rays,ind=ind)
##    weights = weights[ind]
#Get rid of outliers
    ind = np.abs(rays[2] - np.average(rays[2])) < 10.
    rays = PT.vignette(rays, ind=ind)
    weights = weights[ind]

    #If no rays made it through
    if np.size(rays[1]) == 0:
        return 0, 0, 0, 0

    #Look at resolution of this line
    try:
        cy = np.average(rays[2], weights=weights)
    except:
        pdb.set_trace()
    if findMargin is True:
        #Compute FWHM after Gaussian convolution
        fwhms = np.linspace(1, 3, 201) / 60.**2 * pi / 180 * 12e3
        tot = np.array([convolveLSF(rays,.001,m,weights=weights)\
                        for m in fwhms/2.35])
        #Convert to arcsec, return std of margin convolution
        marg = fwhms[np.argmin(
            np.abs(tot / 12e3 * 180 / pi * 60**2 - 3.))] / 2.35
        return marg, rays
    fwhm = convolveLSF(rays, .001, marg, weights=weights)
    resolution = cy / fwhm
    area = np.sum(
        weights
    ) * .799 * .83 * .8 * .9  #Grat plates, azimuthal ribs, usable area
    #print resolution
    #print area
    #print sqrt((cy/3000)**2 - lsf**2)/F * 180/pi*60**2
    print 'Done'
    return resolution, area, np.nanmean(rays[1]), np.nanmean(rays[2]),\
           rays,weights