コード例 #1
0
ファイル: calc.py プロジェクト: mbhatt1/pyoptools
def find_ppp(opsys, opaxis):
    """Function to find the primary principal plane location of a lens or an 
    optical component
    
    Arguments:

    
    opsys
        Optical system or optical component whose principal planes are to be 
        found
    opaxis
        Ray defining the optical axis of the system
    
    For this function to operate, the system should have a rotational symetry
    around the optical axis. 
    
    Note: 
        This function is returns the intersection point of the optical axis and
        the principal plane.
    """

    # Create a system with the component
    if isinstance(opsys, (Component)):
        c = opsys
        opsys = System(complist=[(c, (0, 0, 0), (0, 0, 0))], n=1)

    # To create a ray parallel to the optical axis, find a displacement vector
    # perpendicular to the optical axis, and to the XYZ axes

    d = opaxis.dir
    pv1 = cross(d, (0, 0, 1))
    pv2 = cross(d, (0, 1, 0))
    pv3 = cross(d, (1, 0, 0))

    pv = [pv1, pv2, pv3]

    # Search for the longest pv
    pvn = array((dot(pv1, pv1), dot(pv2, pv2), dot(pv3, pv3)))

    pvm = pv[pvn.argmax()]

    # Create parallel ray

    par_ray = opaxis.copy()
    par_ray.pos = par_ray.pos + pvm * 0.0001

    opsys.clear_ray_list()
    opsys.ray_add([opaxis, par_ray])
    opsys.propagate()
    par_ray_end = par_ray.get_final_rays(inc_zeros=False)

    if len(par_ray_end) != 1:
        raise Exception, "The paraxial ray has more than one final ray"

    pppl = intersection(par_ray, par_ray_end[0])

    # Move the intersection point toward the optical axis

    ppp = pppl[0] - pvm * 0.0001
    return ppp  # , pppl[1])
コード例 #2
0
def intersection(r1, r2):
    ''' 
    Return the point of intersection between the rays r1 and r2.

    
    **Arguments:**
        
        == ===================================
        r1 First Ray to test for intersection
        r2 Second Ray to test for intersection
        == ===================================
    
    **Return Value:**
        
        Tuple (ip,rv) where:
        
        == ============================================================
        ip Intersection point coordinates. If the rays do not intersect
           ip=(nan,nan,nan)
        rv Boolean that indicates if the intersection point represent a
           real image (rv=true) , or a virtual image (rv=false). 
        == ============================================================
        
    '''

    d1 = r1.dir
    d2 = r2.dir
    p1 = r1.pos
    p2 = r2.pos

    d1xd2 = cross(d1, d2)
    # check if the rays are parallel
    #log.info("Vector cross product:"+str(d1xd2))
    if dot(d1xd2, d1xd2) == 0.:
        return array((nan, nan, nan)), False
    p2p1xv2 = cross(p2 - p1, d2)
    p2p1xv1 = cross(p2 - p1, d1)
    a = p2p1xv2 / d1xd2
    b = p2p1xv1 / d1xd2

    # Remove the nan from the list
    keep = ~isnan(a)
    an = a[keep]

    keep = ~isnan(b)
    bn = b[keep]

    ip = array((nan, nan, nan))
    rv = False
    #print an,bn
    if len(an) > 0:
        if alltrue(an == an[0]):
            ip = p1 + an[0] * d1

        # check if all the solutions are equal
        if alltrue(an >= 0) and alltrue(bn >= 0):
            rv = True
    #log.info("Intersection point found at:"+str(ip)+" "+str(rv))
    return ip, rv
コード例 #3
0
ファイル: calc.py プロジェクト: ramezquitao/pyoptools
def intersection(r1,r2):
    ''' 
    Return the point of intersection between the rays r1 and r2.

    
    **Arguments:**
        
        == ===================================
        r1 First Ray to test for intersection
        r2 Second Ray to test for intersection
        == ===================================
    
    **Return Value:**
        
        Tuple (ip,rv) where:
        
        == ============================================================
        ip Intersection point coordinates. If the rays do not intersect
           ip=(nan,nan,nan)
        rv Boolean that indicates if the intersection point represent a
           real image (rv=true) , or a virtual image (rv=false). 
        == ============================================================
        
    '''

    d1=r1.dir
    d2=r2.dir
    p1=r1.pos
    p2=r2.pos

    d1xd2=cross(d1,d2)
    # check if the rays are parallel
    #log.info("Vector cross product:"+str(d1xd2))
    if dot(d1xd2,d1xd2)==0. : 
        return array((nan,nan,nan)),False
    p2p1xv2=cross(p2-p1,d2)
    p2p1xv1=cross(p2-p1,d1)
    a=p2p1xv2/d1xd2
    b=p2p1xv1/d1xd2
    
    # Remove the nan from the list 
    keep=~isnan(a)
    an=a[keep]

    keep=~isnan(b)
    bn=b[keep]
    
    ip=array((nan,nan,nan))
    rv=False
    #print an,bn
    if len(an)>0:
        if alltrue(an==an[0]) :
            ip=p1+an[0]*d1
            
        # check if all the solutions are equal 
        if alltrue(an>=0) and alltrue(bn>=0):
            rv=True
    #log.info("Intersection point found at:"+str(ip)+" "+str(rv))
    return ip,rv
コード例 #4
0
ファイル: calc.py プロジェクト: luzpaz/pyoptools
def intersection(r1, r2):
    ''' 
    Return the point of intersection between the rays r1 and r2.

    
    Parameters
    ----------

    r1,r2 : :class:`~pyoptools.raytrace.ray.Ray` 
        Rays to test for intersection. 
    
    Returns
    -------

    ip : tuple(float, float, float) 
        Intersection point coordinates. If the rays do not intersect 
        ip=(nan,nan,nan)
    rv : bool
        Indicates if the intersection point represent a real image (rv=true),
        or a virtual image (rv=false). In this case virtual has the same meaning
        as in virtual image i.e. intersection point is not in the actual path, 
        or is behind the ray's origin.
    '''

    d1 = r1.dir
    d2 = r2.dir
    p1 = r1.pos
    p2 = r2.pos

    d1xd2 = cross(d1, d2)
    # check if the rays are parallel
    #log.info("Vector cross product:"+str(d1xd2))
    if dot(d1xd2, d1xd2) == 0.:
        return array((nan, nan, nan)), False
    p2p1xv2 = cross(p2 - p1, d2)
    p2p1xv1 = cross(p2 - p1, d1)
    a = p2p1xv2 / d1xd2
    b = p2p1xv1 / d1xd2

    # Remove the nan from the list
    keep = ~isnan(a)
    an = a[keep]

    keep = ~isnan(b)
    bn = b[keep]

    ip = array((nan, nan, nan))
    rv = False
    #print an,bn
    if len(an) > 0:
        if alltrue(an == an[0]):
            ip = p1 + an[0] * d1

        # check if all the solutions are equal
        if alltrue(an >= 0) and alltrue(bn >= 0):
            rv = True
    #log.info("Intersection point found at:"+str(ip)+" "+str(rv))
    return ip, rv
コード例 #5
0
ファイル: ipynbplotutils.py プロジェクト: zengxu/pyoptools
def draw_surf(surf, P, D):
    if isinstance(surf, Surface):
        #points, polylist =surf.shape.polylist(surf.topo)
        points, polylist = surf.polylist()
        glPushMatrix()
        glTranslatef(P[0], P[1], P[2])
        glRotatef(180 * D[2] / pi, 0.0, 0.0, 1.0)
        glRotatef(180 * D[1] / pi, 0.0, 1.0, 0.0)
        glRotatef(180 * D[0] / pi, 1.0, 0.0, 0.0)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     [1., 1., 0, 0.7])
        #glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, [1.,1.,0.,1.])
        #glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, [1.,0.,0.,1.])
        for p in polylist:
            if len(p) == 3:
                p0 = points[p[0]]
                p1 = points[p[1]]
                p2 = points[p[2]]
                v0 = array(p1) - array(p0)
                v1 = array(p2) - array(p0)
                v3 = cross(v0, v1)
                v3 = v3 / sqrt(v3[0]**2 + v3[1]**2 + v3[2]**2)
                #if v3[2]<0: print "**"
                glBegin(GL_TRIANGLES)  #Drawing Using Triangles
                #glColor4f(1,0,0, 1.)
                glNormal3f(v3[0], v3[1], v3[2])
                glVertex3f(p0[0], p0[1], p0[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f(p1[0], p1[1], p1[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f(p2[0], p2[1], p2[2])
                glEnd()
            elif len(p) == 4:
                p0 = points[p[0]]
                p1 = points[p[1]]
                p2 = points[p[2]]
                p3 = points[p[3]]
                v0 = array(p1) - array(p0)
                v1 = array(p2) - array(p0)
                v3 = cross(v0, v1)
                v3 = v3 / sqrt(v3[0]**2 + v3[1]**2 + v3[2]**2)
                #print p0,p1,p2,p3

                glBegin(GL_QUADS)  # Start Drawing The Cube
                #glColor4f(1,0,0, 1.)
                glNormal3f(v3[0], v3[1], v3[2])
                glVertex3f(p0[0], p0[1], p0[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f(p1[0], p1[1], p1[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f(p2[0], p2[1], p2[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f(p3[0], p3[1], p3[2])
                glEnd()
        glPopMatrix()
コード例 #6
0
def draw_surf(surf, P, D):
    if isinstance(surf, Surface):
        #points, polylist =surf.shape.polylist(surf.topo)
        points, polylist =surf.polylist()
        glPushMatrix()
        glTranslatef(P[0],P[1],P[2])    
        glRotatef(180*D[2]/pi,0.0,0.0,1.0)
        glRotatef(180*D[1]/pi,0.0,1.0,0.0)
        glRotatef(180*D[0]/pi,1.0,0.0,0.0)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, [1.,1.,0,0.7])
        #glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, [1.,1.,0.,1.])
        #glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, [1.,0.,0.,1.])
        for p in polylist:
            if len(p)==3:
                p0=points[p[0]]
                p1=points[p[1]]
                p2=points[p[2]]
                v0=array(p1)-array(p0)
                v1=array(p2)-array(p0)
                v3=cross(v0,v1)
                v3=v3/sqrt(v3[0]**2+v3[1]**2+v3[2]**2)
                #if v3[2]<0: print "**"
                glBegin(GL_TRIANGLES) #Drawing Using Triangles
                #glColor4f(1,0,0, 1.)
                glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p0[0], p0[1], p0[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p1[0], p1[1], p1[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p2[0], p2[1], p2[2])
                glEnd()                
            elif len(p)==4:
                p0=points[p[0]]
                p1=points[p[1]]
                p2=points[p[2]]
                p3=points[p[3]]
                v0=array(p1)-array(p0)
                v1=array(p2)-array(p0)
                v3=cross(v0,v1)
                v3=v3/sqrt(v3[0]**2+v3[1]**2+v3[2]**2)
                #print p0,p1,p2,p3
                
                glBegin(GL_QUADS)           # Start Drawing The Cube
                #glColor4f(1,0,0, 1.)
                glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p0[0], p0[1], p0[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p1[0], p1[1], p1[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p2[0], p2[1], p2[2])
                #glNormal3f(v3[0],v3[1],v3[2])
                glVertex3f( p3[0], p3[1], p3[2])
                glEnd()                
        glPopMatrix()
コード例 #7
0
def find_ppp(opsys, opaxis):
    """Function to find the primary principal plane location of a lens or an 
    optical component
    
    Arguments:

    
    opsys
        Optical system or optical component whose principal planes are to be 
        found
    opaxis
        Ray defining the optical axis of the system
    
    For this function to operate, the system should have a rotational symmetry
    around the optical axis. 
    
    Note: 
        This function is returns the intersection point of the optical axis and
        the principal plane.
    """

    # Create a system with the component
    if isinstance(opsys, (Component)):
        c = opsys
        opsys = System(complist=[
            (c, (0, 0, 0), (0, 0, 0)),
        ], n=1)

    # To create a ray parallel to the optical axis, find a displacement vector
    # perpendicular to the optical axis, and to the XYZ axes

    d = opaxis.dir
    pv1 = cross(d, (0, 0, 1))
    pv2 = cross(d, (0, 1, 0))
    pv3 = cross(d, (1, 0, 0))

    pv = [pv1, pv2, pv3]

    # Search for the longest pv
    pvn = array((dot(pv1, pv1), dot(pv2, pv2), dot(pv3, pv3)))

    pvm = pv[pvn.argmax()]

    # Create parallel ray

    par_ray = opaxis.copy()
    par_ray.pos = par_ray.pos + pvm * .0001

    opsys.clear_ray_list()
    opsys.ray_add([opaxis, par_ray])
    opsys.propagate()
    par_ray_end = par_ray.get_final_rays(inc_zeros=False)

    if len(par_ray_end) != 1:
        raise Exception("The paraxial ray has more than one final ray")

    pppl = intersection(par_ray, par_ray_end[0])

    #Move the intersection point toward the optical axis

    ppp = pppl[0] - pvm * .0001
    return ppp  #, pppl[1])
コード例 #8
0
def paraxial_location(opsys, opaxis):
    """Function to find the paraxial image location
    
    This function finds the paraxial image location of a point
    located in the optical axis, and a boolean indicating if the image
    is real or virtual (image_location, real_virtual).
    The origin of the opaxis location is taken as the object location
    
    **ARGUMENTS**

        ====== ===================================
        opsys  Optical system to use.
        opaxis Ray representating the optical axis
        ====== ===================================

    **RETURN VALUE:**

        Tuple (image_location, real) where:

        ============== ==================================================
        image_location Image location coordinates
        real           Boolean that indicates if the intersection point 
                       represent a real image (real=True) , or a virtual 
                       image (real=False).
        ============== ==================================================

    For this function to operate, the system should have a rotational symmetry
    around the optical axis. 
    """

    #log.info("Propagate Optical axis ray")
    opsys.clear_ray_list()
    opsys.reset()
    #opsys.ray_add(cray)
    opsys.ray_add(opaxis)

    opsys.propagate()

    # Calculate vectors perpendicular to the optical axis and to the XYZ axes
    d = opaxis.dir
    pv1 = cross(d, (0, 0, 1))
    pv2 = cross(d, (0, 1, 0))
    pv3 = cross(d, (1, 0, 0))

    pv = [pv1, pv2, pv3]

    # Search for the longest pv
    pvn = array((dot(pv1, pv1), dot(pv2, pv2), dot(pv3, pv3)))

    pvm = pv[pvn.argmax()]

    #log.info("Displacement vector found: "+str(pvm))

    # Create paraxial ray

    par_ray = opaxis.copy()
    par_ray.dir = par_ray.dir + pvm * .001

    opsys.clear_ray_list()
    opsys.reset()
    opsys.ray_add(par_ray)
    opsys.propagate()

    par = par_ray.get_final_rays(inc_zeros=False)
    oax = opaxis.get_final_rays(inc_zeros=False)
    #log.info("par="+str(par))
    #log.info("oax="+str(oax))

    if len(par) != 1 or len(oax) != 1:
        raise Exception("The paraxial ray or the optical axis ray have more"
                        " than one final ray")

    #log.info("Calculating object location")
    expl = intersection(oax[0], par[0])
    return expl
コード例 #9
0
def pupil_location(opsys, ccds, opaxis):
    '''
    Function to find the optical system pupils position
    
    .. note:
        For this function to operate, the system should have a rotational
        symmetry around the optical axis. 
       
    **Parameters:**
        
        opsys   Optical system to use.
        opaxis  Ray representing the optical axis
        ccds    Surface that represents a detector in the aperture plane
    
    **Return Value**
        
        (enpl,expl)
        
        enpl tuple (xen,yen,zen) containing the entrance pupil coordinates
        expl tuple (xex,yex,zex) containing the exit pupil coordinates
        
    
    '''

    #log.info("Propagate Optical axis ray")
    opsys.clear_ray_list()
    opsys.reset()
    #opsys.ray_add(cray)
    opsys.ray_add(opaxis)

    opsys.propagate()

    if (len(ccds.hit_list) == 0):
        raise Exception("The optical axis did not intersect the aperture")
    if (len(ccds.hit_list) > 1):
        raise Exception(
            "The optical axis intersected the aperture more than once")

    aip = ccds.hit_list[0][0]
    air = ccds.hit_list[0][1]

    #log.info("Optical Axis Intersection point= "+str(aip))
    #log.info("Intersection Ray= "+str(air))

    #Getting Intersection point in global coordinates

    if (len(air.childs) != 1):
        raise Exception("The intersected ray can only have one child")

    ip = air.childs[0].pos
    d = air.childs[0].dir
    #log.info("Intersection point in world coordinates= "+str(ip))
    #log.info("Direction of the optical axis at the intersection point"+str(d))

    #Todo: Check if the optical axis and the aperture are perpendicular

    # Calculate vectors perpendicular to the optical axis and to the XYZ axes
    pv1 = cross(d, (0, 0, 1))
    pv2 = cross(d, (0, 1, 0))
    pv3 = cross(d, (1, 0, 0))

    pv = [pv1, pv2, pv3]

    # Search for the longest pv
    pvn = array((dot(pv1, pv1), dot(pv2, pv2), dot(pv3, pv3)))

    pvm = pv[pvn.argmax()]

    #log.info("Displacement vector found: "+str(pvm))

    # Create ray to calculate the exit pupil
    expuray = air.childs[0].copy()
    expuray.dir = expuray.dir + pvm * .0001

    # Create the ray to calculate the entrance pupil
    enpuray = expuray.reverse()

    opsys.clear_ray_list()
    opsys.reset()
    opsys.ray_add(enpuray)
    opsys.ray_add(expuray)

    opsys.propagate()

    enp = enpuray.get_final_rays(inc_zeros=False)
    exp = expuray.get_final_rays(inc_zeros=False)
    oax = opaxis.get_final_rays(inc_zeros=False)
    #log.info("enp="+str(enp))
    #log.info("exp="+str(exp))
    #log.info("oax="+str(oax))
    if len(enp) != 1 or len(exp) != 1 or len(oax) != 1:
        raise Exception("The principal ray or the optical axis ray have more"
                        " than one final ray")
    #log.info("Calculating entrance pupil location")

    # Find the nearest points between the rays.
    # Some times because of numerical errors, or some aberrations in the optical
    # system, the rays do not trully intersect.
    # Use instead the nearest points and issue a warning when the rays do not trully
    # intersect.

    enpl = intersection(opaxis, enp[0])[0]
    if (isnan(enpl)).all():
        p1, p2, d, rv = nearest_points(opaxis, enp[0])
        print(
            "Warning: The optical axis does not intersect the principal ray at the entrance"
        )
        print("pupil. The minimum distance is:", d)
        enpl = (p1 + p2) / 2

    #log.info("Calculating exit pupil location")
    expl = intersection(oax[0], exp[0])[0]
    if (isnan(expl)).all():
        p1, p2, d, rv = nearest_points(oax[0], exp[0])
        print(
            "Warning: The optical axis does not intersect the principal ray at the exit"
        )
        print("pupil. The minimum distance is:", d)
        expl = (p1 + p2) / 2

    return enpl, expl
コード例 #10
0
ファイル: ipywidgets.py プロジェクト: ederag/pyoptools
def surf2mesh(S, P=(0, 0, 0), D=(0, 0, 0), wire=False):

    color = "#ffff00"

    points, polylist = S.polylist()

    #Conversion para quethreejs la entienda

    polylist = list(polylist)

    lpoly = []
    lpoints = []

    for l in points:
        lpoints.append(list(l))

    for l in polylist:
        lpoly.append(list(map(int, l)))

    vertices = lpoints

    faces = lpoly

    # Map the vertex colors into the 'color' slot of the faces
    # Map the normals
    nfaces = []
    for f in faces:
        p0 = points[f[0]]
        p1 = points[f[1]]
        p2 = points[f[2]]
        v0 = array(p1) - array(p0)
        v1 = array(p2) - array(p0)
        v3 = cross(v0, v1)
        v3 = tuple(v3 / sqrt(v3[0]**2 + v3[1]**2 + v3[2]**2))

        nfaces.append(f + [v3, color, None])

    # Create the geometry:

    surfaceGeometry = py3js.Geometry(
        vertices=vertices,
        faces=nfaces,
        #colors=vertexcolors
    )

    #surfaceGeometry = py3js.SphereGeometry(radius=300, widthSegments=32, heightSegments=24)

    if wire:
        surfaceGeometry = py3js.WireframeGeometry(surfaceGeometry)

    # Calculate normals per face, for nice crisp edges:
    surfaceGeometry.exec_three_obj_method('computeFaceNormals')

    surfaceMaterial = py3js.MeshPhongMaterial(color=color,
                                              ambient="#050505",
                                              specular="#ffffff",
                                              shininess=15,
                                              emissive="#000000",
                                              side='DoubleSide',
                                              transparent=True,
                                              opacity=.8)
    #surfaceMaterial = py3js.MeshLambertMaterial(color='red',side='DoubleSide')

    # Create a mesh. Note that the material need to be told to use the vertex colors.
    surfaceMesh = py3js.Mesh(
        geometry=surfaceGeometry,
        material=surfaceMaterial,
    )

    surfaceMesh.rotation = *D, "ZYX"
    surfaceMesh.position = tuple(P)
    return surfaceMesh
コード例 #11
0
ファイル: calc.py プロジェクト: ramezquitao/pyoptools
def paraxial_location(opsys, opaxis):
    """Function to find the paraxial image location
    
    This function finds the paraxial image location of a point
    located in the optical axis, and a boolean indicating if the image
    is real or virtual (image_location, real_virtual).
    The origin of the opaxis location is taken as the object location
    
    Parameters:

    
    *opsys*
        Optical system to use.
    
    *opaxis* 
        Ray representating the optical axis
        
    For this function to operate, the system should have a rotational symmetry
    around the optical axis. 
    """
    
    #log.info("Propagate Optical axis ray")
    opsys.clear_ray_list()
    opsys.reset()
    #opsys.ray_add(cray)
    opsys.ray_add(opaxis)

    opsys.propagate()
    
    # Calculate vectors perpendicular to the optical axis and to the XYZ axes
    d=opaxis.dir
    pv1= cross(d,(0,0,1))
    pv2= cross(d,(0,1,0))
    pv3= cross(d,(1,0,0))
    
    pv=[pv1,pv2,pv3]
    
    # Search for the longest pv
    pvn=array((dot(pv1,pv1),dot(pv2,pv2),dot(pv3,pv3)))
    
    pvm=pv[pvn.argmax()]
    
    #log.info("Displacement vector found: "+str(pvm))

    # Create paraxial ray
    
    par_ray=opaxis.copy()
    par_ray.dir=par_ray.dir+pvm*.001

    
    opsys.clear_ray_list()
    opsys.reset()
    opsys.ray_add(par_ray)
    opsys.propagate()
    
    
    par=par_ray.get_final_rays(inc_zeros = False)
    oax=opaxis.get_final_rays(inc_zeros = False)
    #log.info("par="+str(par))
    #log.info("oax="+str(oax))
    
    if len(par)!=1 or len(oax)!=1: 
        raise Exception("The paraxial ray or the optical axis ray have more"
        " than one final ray")
  
    #log.info("Calculating object location")
    expl=intersection(oax[0],par[0])
    return expl
コード例 #12
0
ファイル: calc.py プロジェクト: ramezquitao/pyoptools
def pupil_location(opsys,ccds,opaxis):
    '''
    Function to find the optical system pupils position
    
    .. note:
        For this function to operate, the system should have a rotational
        symmetry around the optical axis. 
       
    **Parameters:**
        
        opsys   Optical system to use.
        opaxis  Ray representing the optical axis
        ccds    Surface that represents a detector in the aperture plane
    
    **Return Value**
        
        (enpl,expl)
        
        enpl tuple (xen,yen,zen) containing the entrance pupil coordinates
        expl tuple (xex,yex,zex) containing the exit pupil coordinates
        
    
    '''
    
    #log.info("Propagate Optical axis ray")
    opsys.clear_ray_list()
    opsys.reset()
    #opsys.ray_add(cray)
    opsys.ray_add(opaxis)

    opsys.propagate()
    
        
    if (len(ccds.hit_list)==0):
        raise Exception("The optical axis did not intersect the aperture") 
    if(len(ccds.hit_list)>1):
        raise Exception("The optical axis intersected the aperture more than once") 
        
    aip=ccds.hit_list[0][0]
    air=ccds.hit_list[0][1]
    
    #log.info("Optical Axis Intersection point= "+str(aip))
    #log.info("Intersection Ray= "+str(air))
    
    #Getting Intersection point in global coordinates
    
    if(len(air.childs)!=1):
        raise Exception("The intersected ray can only have one child")
    
    ip=air.childs[0].pos
    d=air.childs[0].dir
    #log.info("Intersection point in world coordinates= "+str(ip))
    #log.info("Direction of the optical axis at the intersection point"+str(d))
    
    #Todo: Check if the optical axis and the aperture are perpendicular
    
    # Calculate vectors perpendicular to the optical axis and to the XYZ axes
    pv1= cross(d,(0,0,1))
    pv2= cross(d,(0,1,0))
    pv3= cross(d,(1,0,0))
    
    pv=[pv1,pv2,pv3]
    
    # Search for the longest pv
    pvn=array((dot(pv1,pv1),dot(pv2,pv2),dot(pv3,pv3)))
    
    pvm=pv[pvn.argmax()]
    
    #log.info("Displacement vector found: "+str(pvm))

    # Create ray to calculate the exit pupil
    expuray=air.childs[0].copy()
    expuray.dir=expuray.dir+pvm*.0001
    
    # Create the ray to calculate the entrance pupil
    enpuray=expuray.reverse()
    
    opsys.clear_ray_list()
    opsys.reset()
    opsys.ray_add(enpuray)
    opsys.ray_add(expuray)

    opsys.propagate()
    
    enp=enpuray.get_final_rays(inc_zeros = False)
    exp=expuray.get_final_rays(inc_zeros = False)
    oax=opaxis.get_final_rays(inc_zeros = False)
    #log.info("enp="+str(enp))
    #log.info("exp="+str(exp))
    #log.info("oax="+str(oax))
    if len(enp)!=1 or len(exp)!=1 or len(oax)!=1: 
        raise Exception("The principal ray or the optical axis ray have more"
        " than one final ray")
    #log.info("Calculating entrance pupil location")
    
    # Find the nearest points between the rays. 
    # Some times because of numerical errors, or some aberrations in the optical
    # system, the rays do not trully intersect.
    # Use instead the nearest points and issue a warning when the rays do not trully
    # intersect.
    
    enpl=intersection(opaxis,enp[0])[0]
    if (isnan(enpl)).all():
        p1, p2, d, rv =nearest_points(opaxis,enp[0])
        print("Warning: The optical axis does not intersect the principal ray at the entrance")
        print("pupil. The minimum distance is:", d)
        enpl=(p1+p2)/2
        
    #log.info("Calculating exit pupil location")
    expl=intersection(oax[0],exp[0])[0]
    if (isnan(expl)).all():
        p1, p2, d, rv =nearest_points(oax[0],exp[0])
        print("Warning: The optical axis does not intersect the principal ray at the exit")
        print("pupil. The minimum distance is:", d)
        expl=(p1+p2)/2
    
    return enpl,expl