예제 #1
0
class Shell:
    '''
    A shell consists of two segments, one in the front and one behind. The base location
    of the front segment has a smaller z value than that of the back segement. Thus, rays
    coming from sources in the negative z range will enter the front end first.
    '''

    def __init__(self,
                 base = [0,0,0],
                 seglen = 30.0,
                 ang = 0.00643866102405, # focal length of 2m
                 r = 5.151
                 ):
        '''
        Constructor
        
        Parameters:
            base:    the center point of the wide end of the segment
            seglen:  the axial length of each segment
            ang:     angle between the shell axis and the side of the front segment
            r:       radius of the shell where the two segments meet
        '''
        self.front = Segment(base=base, seglen=seglen, ang=ang, r1=r)
        backBase = [base[0], base[1], base[2]+seglen]
        self.back = Segment(base=backBase, seglen=seglen, ang=3*ang, r0=r)
        
    def getSurfaces(self):
        '''
        Returns a list of surfaces
        '''
        return [self.front,self.back]
        
    def plot2D(self, axes, color = 'b'):
        '''
        Plots a 2d cross section of the shell 
        '''
        self.front.plot2D(axes, color)
        self.back.plot2D(axes, color)
 
        # plot rays
        ang = self.front.ang
        r = self.back.r1
        z = self.back.base[2]
        d = r*tan((pi/2)-4*ang)
        axes.plot((2*z,2*z+d),(r,0),'y:')
        axes.plot((2*z,2*z+d),(-r,0),'y:')   
    
    def plot3D(self, axes, color = 'b'):
        '''
        Generates a 3d plot of the shell in the given figure
        '''
        self.front.plot3D(axes,color)
        self.back.plot3D(axes,color)
        
    def targetFront(self,a,b):
        '''
        Takes two list arguments of equal size, the elements of which range from 0 to 1.
        Returns an array of points that exist on the circle defined by the wide end of 
        the shell. 
        '''
        return self.front.targetFront(a,b)
    
    def targetBack(self,a,b):
        '''
        Takes two list arguments of equal size, the elements of which range from 0 to 1.
        Returns an array of points that exist on the circle defined by the small end of 
        the shell. 
        '''
        return self.back.targetBack(a,b)
     
        
예제 #2
0
class Module:
    '''
    A complete foxsi module. By default, it consists of seven nested shells.
    '''
    
    def __init__(self,
                 base = [0,0,0],
                 seglen = 30.0,
                 focal = 200.0,
                 radii = [5.151,4.9,4.659,4.429,4.21,4.0,3.799],
                 angles = None
                 ):
        '''
        Constructor  
        
        Parameters:
            base:    the center point of the wide end of the segment
            seglen:  the axial length of each segment
            focal:   the focal length, measured from the center of the module
            radii:   a list of radii, one for each shell from biggest to smallest
            angles:  optional parameter to overwrite the shell angles computed by constructor
        '''
        if angles is None:
            angles = calcShellAngle(radii,focal) 
        elif len(radii) != len(angles):
            raise ValueError('number of radii and number of angles do not match')
        
        self.shells = []
        for i,r in enumerate(radii):
            self.shells.append(Shell(base=base, seglen=seglen, ang=angles[i], r=r))
        
        # inner core (blocks rays going through center of module)
        r0 = self.shells[-1].back.r0
        r1 = r0 - seglen * tan(4*angles[-1])
        ang = atan((r0-r1)/(2*seglen))
        self.core = Segment(base=base, seglen=2*seglen, ang=ang, r0=r0)
        self.coreFaces = [Circle(center=base,normal=[0,0,1],radius=r0),
                          Circle(center=[base[0],base[1],base[2]+2*seglen],normal=[0,0,-1],radius=r1)]
    
    def getDims(self):
        '''
        Returns the module's dimensions:
        [radius at wide end, radius at small end, length]
        '''
        front = self.shells[0].front
        back = self.shells[0].back
        return [front.r0, back.r1, front.seglen+back.seglen]
    
    def getSurfaces(self):
        '''
        Returns a list of surfaces
        '''
        surfaces = []
        for shell in self.shells:
            surfaces.extend(shell.getSurfaces())
        surfaces.append(self.core)
        surfaces.extend(self.coreFaces)
        return(surfaces)
    
    def passRays(self, rays, robust = False):
        '''
        Takes an array of rays and passes them through the front end of 
        the module. 
        '''
        #print 'Module: passing ',len(rays),' rays'
        
        # get all module surfaces
        allSurfaces = self.getSurfaces()
        allSurfaces.remove(self.coreFaces[0]) # we'll test these seperately
        allSurfaces.remove(self.coreFaces[1])
        
        # create regions consisting of adjacent shells
        regions = [None for shell in self.shells]
        for i,shell in enumerate(self.shells):
            # innermost shell
            if i == len(self.shells)-1:
                regions[i] = shell.getSurfaces()
                regions[i].append(self.core)
            else:
                regions[i] = shell.getSurfaces() # outer shell (reflective side facing region)
                regions[i].extend(self.shells[i+1].getSurfaces()) # nested shell (non reflective)
        
        for ray in rays:
            
            # skip rays that hit a core face
            if ray.pos[2] < self.coreFaces[0].center[2]:
                sol = self.coreFaces[0].rayIntersect(ray)
                if sol is not None: 
                    ray.pos = ray.getPoint(sol[2])
                    ray.bounces += 1
                    ray.dead = True
                    continue
                else: ray.moveToZ(self.coreFaces[0].center[2])
            elif ray.pos[2] > self.coreFaces[1].center[2]:
                sol = self.coreFaces[1].rayIntersect(ray)
                if sol is not None:
                    ray.pos = ray.getPoint(sol[2])
                    ray.bounces += 1
                    ray.dead = True 
                    continue
                else: ray.moveToZ(self.coreFaces[1].center[2])
            
            # reset surfaces
            surfaces = [s for s in allSurfaces]
            firstBounce = True # used for optimization
                
            # while ray is inside module
            while True:
                
                # find nearest ray intersection
                bestDist = None
                bestSol = None
                bestSurf = None
                for surface in surfaces:
                    
                    sol = surface.rayIntersect(ray)
                    if sol is not None:
                        dist = norm(ray.getPoint(sol[2])-ray.pos)
                        if bestDist is None or dist < bestDist:
                            bestDist = dist
                            bestSol = sol
                            bestSurf = surface
                
                # if a closest intersection was found
                if bestSol is not None:
                
                    # update ray
                    ray.pos = ray.getPoint(bestSol[2])
                    ray.bounces += 1
                    x = reflect(ray.ori,bestSurf.getNormal(bestSol[0],bestSol[1]))
                    # if reflected
                    if x is not None:
                        ray.ori = x / norm(x) # update ori to unit vector reflection
                    # otherwise, no reflection means ray is dead
                    else:
                        ray.dead = True 
                        break 
                    
                    # knowing the surface it has just hit, we can 
                    # narrow down the number of surface to test
                    
                    # remove shells the ray cannot even 'see'
                    if firstBounce:
                        firstBounce = False
                        for region in regions:
                            if bestSurf is region[0] or bestSurf is region[1]:
                                surfaces = [s for s in region]
                                break
                        
                    # assuming each segment can be hit no more than once
                    # eliminate the surface from our list
                    if not robust: surfaces.remove(bestSurf)
                    
                # if no intersection, ray can exit module
                else: break

    def plot2D(self, axes, color = 'b'):
        '''
        Plots a 2d cross section of the module 
        '''
        for shell in self.shells:
            shell.plot2D(axes, color)
        
        # plot core
        self.core.plot2D(axes, color)
        base = self.core.base
        r0 = self.core.r0
        r1 = self.core.r1
        seglen = self.core.seglen
        axes.plot((base[2],base[2]),(r0,-r0),'-'+color)
        axes.plot((base[2]+seglen,base[2]+seglen),(r1,-r1),'-'+color)
    
    def plot3D(self, axes, color = 'b'):
        '''
        Generates a 3d plot of the module in the given figure
        '''
        for shell in self.shells:
            shell.plot3D(axes,color)
        
    def targetFront(self,a,b):
        '''
        Takes two list arguments of equal size, the elements of which range from 0 to 1.
        Returns an array of points that exist on the circle defined by the wide end of 
        the module. 
        '''
        #must modify 'a' so that we dont return points from the core
        r0 = self.shells[0].front.r0
        r1 = self.core.r0
        a0 = (r1/r0)**2 # the 'a' value that gives r1=sqrt(a)*r0
        adiff = 1 - a0
        for i in range(len(a)):
            a[i] = a[i]*adiff + a0
        return self.shells[0].targetFront(a,b)
    
    def targetBack(self,a,b):
        '''
        Takes two list arguments of equal size, the elements of which range from 0 to 1.
        Returns an array of points that exist on the circle defined by the small end of 
        the module. 
        '''
        #must modify 'a' so that we dont return points from the core
        r0 = self.shells[0].back.r1
        r1 = self.core.r1
        a0 = (r1/r0)**2 # the 'a' value that gives r1=sqrt(a)*r0
        adiff = 1 - a0
        for i in range(len(a)):
            a[i] = a[i]*adiff + a0
        return self.shells[0].targetBack(a,b)