def alignTolerances(num,azwidth=60.,axwidth=60.,f=None,catalign=np.zeros(6),\ returnrays=False): """ Set up perfect beam and insert CAT grating Mess with alignment and measure spot shift at focus """ #Set up converging beam rays = sources.convergingbeam2(12e3,-azwidth/2.,azwidth/2.,\ -axwidth/2.,axwidth/2.,num,0.) tran.transform(rays, 0, 0, 0, np.pi, 0, 0) tran.transform(rays, 0, 0, 12e3, 0, 0, 0) #Place CAT grating tran.transform(rays, *catalign) surf.flat(rays) tran.grat(rays, 200., 8, 1.) tran.itransform(rays, *catalign) #Go to focus if f is not None: try: tran.transform(rays, 0, 0, f, 0, 0, 0) surf.flat(rays) except: pdb.set_trace() cx, cy = anal.centroid(rays) if returnrays is True: return rays return cx, cy
def traceThroughPrimary(rays,mask,primalign=np.zeros(6),\ detalign=np.zeros(6),primCoeffs=None,cenSig=0.): """ Trace rays through the primary mirror and then down to a focus. Need to simulate an initial misalignment and then applying an optimization algorithm to align primary to beam. Merit function should include the random error in spot centroiding primCoeffs is a list of coefficients, axial orders, and azimuthal orders Use global coordinate systems to determine sign conventions """ #Move to primary reference frame - rays 200 mm above node tran.transform(rays, 0, 0, -200., 0, 0, 0) glo = [tran.tr.identity_matrix()] * 4 #Move to mirror tangent point and apply misalignment tran.transform(rays, conic.primrad(8450., 220., 8400.), 0, 50, 0, 0, 0, coords=glo) tran.transform(rays, 0, 0, 0, *primalign[3:], coords=glo) tran.itransform(rays, conic.primrad(8450., 220., 8400.), 0, 50, 0, 0, 0, coords=glo) tran.transform(rays, 0, 0, -8400., 0, 0, 0, coords=glo) #Trace to Wolter surface if primCoeffs is None: surf.wolterprimary(rays, 220., 8400.) else: surf.primaryLL(rays,220.,8400.,8500.,8400.,100./220.,\ *primCoeffs) rays = tran.applyT(rays, glo, inverse=True) #Rays are now at primary in global coordinate system #(origin on optical axis and at nominal node height) #Now reflect and trace down to the detector tran.reflect(rays) tran.transform(rays, 0, 0, -conic.primfocus(220., 8400.), 0, 0, 0) #Apply detector misalignment tran.transform(rays, *detalign) surf.flat(rays) #Pick out spot centroids cen = [anal.centroid(rays, weights=mask == i) for i in range(mask[-1] + 1)] cen = np.transpose(np.array(cen)) #Add centroiding error if cenSig > 0: cen = cen + np.random.normal(scale=cenSig, size=np.shape(cen)) return cen
def pairRaytrace(secondaryTilt, despace): """Trace the distorted mirror pair. Assume no gap for now. Vignette rays that land outside active mirror areas.""" #Define ray subannulus r1 = surf.con.primrad(8600., 1000., 8400.) ang = 260. / 1000. #arc length over radius is angular extent rays = sources.subannulus(1000., r1, ang, 10**3) tran.transform(rays, 0, 0, 0, np.pi, 0, 0) #Point in -z tran.transform(rays, 0, 0, -10000, 0, 0, 0) #Converge from above #Trace to primary surf.primaryLL(rays, 1000., 8400., 8600, 8400, ang, res[0], res[2], res[1]) #Vignette rays missing ind = np.logical_and(rays[3] < 8600., rays[3] > 8400.) rays = tran.vignette(rays, ind) numin = float(len(rays[1])) #Reflect tran.reflect(rays) #Bring to midplane of despaced secondary #Apply secondary misalignment tran.transform(rays, surf.con.secrad(8300., 1000., 8400.), 0, 8300, 0, 0, 0) tran.transform(rays, 0, 0, despace, 0, secondaryTilt, 0) tran.itransform(rays, surf.con.secrad(8300., 1000., 8400.), 0, 8300, 0, 0, 0) #Trace to secondary surf.secondaryLL(rays, 1000., 8400., 8400., 8200., ang, res2[0], res2[2], res2[1]) #Vignette rays missing ind = np.logical_and(rays[3] < 8400., rays[3] > 8200.) rays = tran.vignette(rays, ind) numout = float(len(rays[1])) #Reflect tran.reflect(rays) #Reverse secondary misalignment tran.transform(rays, surf.con.secrad(8300., 1000., 8400.), 0, 8300, 0, 0, 0) tran.itransform(rays, 0, 0, despace, 0, secondaryTilt, 0) tran.itransform(rays, surf.con.secrad(8300., 1000., 8400.), 0, 8300, 0, 0, 0) #Go to focus surf.focusI(rays) #Get centroid cx, cy = anal.centroid(rays) #Return merit function return anal.rmsCentroid( rays) / 8400. * 180 / np.pi * 60**2, numout / numin, cx, cy
def singleOptic2(n,misalign=np.zeros(6),srcdist=89.61e3+1.5e3,az=100.,\ returnRays=False,f=None,\ plist=[[0],[0],[0]],\ ax=100.): """Alternative SLF finite source trace""" #Establish subannulus of rays r0 = conic.primrad(8426., 220., 8400.) r1 = conic.primrad(8426. + ax, 220., 8400.) rays = sources.subannulus(r0, r1, az / 220., n, zhat=-1.) #Transform to node position tran.transform(rays, 220, 0, 0, 0, 0, 0) #Set up finite source distance raydist = sqrt(srcdist**2 + rays[1]**2 + rays[2]**2) l = rays[1] / raydist m = rays[2] / raydist n = -sqrt(1. - l**2 - m**2) rays = [ raydist, rays[1], rays[2], rays[3], l, m, n, rays[7], rays[8], rays[9] ] #Align perfectly to beam tran.steerX(rays) #Apply misalignment tran.transform(rays, *misalign) #Place mirror tran.transform(rays, -220., 0, -8400., 0, 0, 0) ## surf.wolterprimarynode(rays,220,8400.) surf.primaryLL(rays, 220., 8400., 8426. + ax, 8426., az / 220., *plist) rays = tran.vignette(rays,ind=np.logical_and(rays[3]<8400.+ax,\ rays[3]>8400.)) tran.itransform(rays, -220., 0., -8400., 0, 0, 0) #Vignette rays not landing in active mirror area ind = np.logical_and(rays[3] > 26., rays[3] < (26. + ax)) ## ind = np.logical_and(np.abs(rays[2])<az/2.,indz) rays = tran.vignette(rays, ind=ind) #Reverse misalignment tran.itransform(rays, *misalign) #Reflect and go to surface tran.reflect(rays) if f is None: f = surf.focusI(rays) else: tran.transform(rays, 0, 0, f, 0, 0, 0) surf.flat(rays) #Get centroid cx, cy = anal.centroid(rays) if returnRays is True: return rays return anal.hpd(rays) / abs(f) * 180 / pi * 60**2, f, cx
def sensitivityPlots(dof, mag): """ Mark the RMS spread and centroid shift of focus as function of DoF """ align = np.repeat(0., 6) misalign = np.linspace(0, mag, 100) cx = [] rms = [] for m in misalign: align[dof] = m rays = distortTrace(*align) cx.append(anal.centroid(rays)[0]) rms.append(anal.rmsCentroid(rays)) return cx, rms
def singleEllipse(n,misalign=np.zeros(6),srcdist=89.61e3+1.5e3,az=100.,\ returnRays=False,f=None,\ plist=[[0],[0],[0]],\ ax=100.,psi=psiE): """Alternative SLF finite source trace""" #Establish subannulus of rays r0 = conic.primrad(8426., 220., 8400.) r1 = conic.primrad(8426. + ax, 220., 8400.) rays = sources.subannulus(r0, r1, az / 220., n, zhat=-1.) tran.pointTo(rays, 0., 0., srcdist, reverse=1.) #Transform to node position tran.transform(rays, 220, 0, 0, 0, 0, 0) #Apply misalignment tran.transform(rays, *misalign) #Place mirror tran.transform(rays, -220., 0, -8400., 0, 0, 0) ## surf.wolterprimarynode(rays,220,8400.) surf.ellipsoidPrimaryLL(rays,220.,8400.,srcdist,psi,8426.+ax,8426.,\ az/220.,*plist) tran.itransform(rays, -220., 0., -8400., 0, 0, 0) #Vignette rays not landing in active mirror area ind = np.logical_and(rays[3] > 26., rays[3] < (26. + ax)) ## ind = np.logical_and(np.abs(rays[2])<az/2.,indz) rays = tran.vignette(rays, ind=ind) #Reverse misalignment tran.itransform(rays, *misalign) #Reflect and go to surface tran.reflect(rays) if f is None: f = surf.focusI(rays) else: tran.transform(rays, 0, 0, f, 0, 0, 0) surf.flat(rays) #Get centroid cx, cy = anal.centroid(rays) if returnRays is True: return rays return anal.hpd(rays) / abs(f) * 180 / pi * 60**2 #,f,cx
def test(N,rin=700.,rout=737.,azwidth=66.,srcdist=89.61e3+1.5e3,\ hubdist=11832.911,yaw=0.,wave=6.,order=1,\ opgalign=[0,0,0,0,0,0],f=None,\ rrays=False,glob=False,rcen=False,\ groll=0.,gyaw=0.,gpitch=0.,\ scatter=False,coordin=None,\ radapprox=False): """ Trace through the SPO module, then place the OPG module at its nominal position, allowing for misalignments about the center of the OPG module. The module tolerances can be investigated by a coordinate transformation around the OPG module placement. """ #Trace through SPO module rays = traceSPO(N,rin=rin,rout=rout,azwidth=azwidth,srcdist=srcdist,\ scatter=scatter) #Find the nominal OPG module location using formalism #from Flanagan's SPIE paper #Go to focus, steer out X and Y, then go up a distance #defined using Flangan formula, this should leave you #at the center of the beam, therefore the center of the #OPG module if coordin is None: coords = [tran.tr.identity_matrix()]*4 tran.transform(rays,0,0,0,0,-np.mean(rays[4]),0,coords=coords) #tran.steerX(rays,coords=coords) #tran.steerY(rays,coords=coords) tran.transform(rays,0,0,0,pi-np.mean(rays[5]),0,0,coords=coords) f0 = surf.focusI(rays,coords=coords) tran.transform(rays,np.mean(rays[1]),np.mean(rays[2]),0,0,0,0,\ coords=coords) tran.transform(rays,0,0,0,0,pi,0,coords=coords) tran.transform(rays,0,0,11832.911*np.cos(1.5*np.pi/180),0,0,0,coords=coords) tran.transform(rays,0,0,0,0,1.5*np.pi/180,0,coords=coords) else: rays = tran.applyT(rays,coordin) coords = np.copy(coordin) surf.flat(rays) #Now at center of central grating, with -z pointing toward hub tran.transform(rays,*opgalign,coords=coords) rays,record = traceOPG(rays,hubdist=hubdist,yaw=yaw,wave=wave,order=order,\ gyaw=gyaw,groll=groll,gpitch=gpitch,\ radapprox=radapprox) tran.itransform(rays,*opgalign,coords=coords) #Should be at same reference frame, with rays now diffracted if np.sum(record)==0: pdb.set_trace() rays = tran.vignette(rays,ind=record>0) record = record[record>0] #Trace to detector and determine LSF rays = tran.applyT(rays,coords,inverse=True) #surf.focusI(rays) if f is not None: try: tran.transform(rays,0,0,-f,0,0,0) surf.flat(rays) except: pdb.set_trace() if rcen is True: return anal.centroid(rays) if rrays is True: if glob is True: tran.transform(rays,0,0,f,0,0,0) return rays,record #Return LSF in arcseconds return anal.hpdY(rays)/12e3*180/pi*60**2
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