| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923 | """Gaussian optics.The module implements:- Ray transfer matrices for geometrical and gaussian optics.  See RayTransferMatrix, GeometricRay and BeamParameter- Conjugation relations for geometrical and gaussian optics.  See geometric_conj*, gauss_conj and conjugate_gauss_beamsThe conventions for the distances are as follows:focal distance    positive for convergent lensesobject distance    positive for real objectsimage distance    positive for real images"""__all__ = [    'RayTransferMatrix',    'FreeSpace',    'FlatRefraction',    'CurvedRefraction',    'FlatMirror',    'CurvedMirror',    'ThinLens',    'GeometricRay',    'BeamParameter',    'waist2rayleigh',    'rayleigh2waist',    'geometric_conj_ab',    'geometric_conj_af',    'geometric_conj_bf',    'gaussian_conj',    'conjugate_gauss_beams',]from sympy.core.expr import Exprfrom sympy.core.numbers import (I, pi)from sympy.core.sympify import sympifyfrom sympy.functions.elementary.complexes import (im, re)from sympy.functions.elementary.miscellaneous import sqrtfrom sympy.functions.elementary.trigonometric import atan2from sympy.matrices.dense import Matrix, MutableDenseMatrixfrom sympy.polys.rationaltools import togetherfrom sympy.utilities.misc import filldedent#### A, B, C, D matrices###class RayTransferMatrix(MutableDenseMatrix):    """    Base class for a Ray Transfer Matrix.    It should be used if there is not already a more specific subclass mentioned    in See Also.    Parameters    ==========    parameters :        A, B, C and D or 2x2 matrix (Matrix(2, 2, [A, B, C, D]))    Examples    ========    >>> from sympy.physics.optics import RayTransferMatrix, ThinLens    >>> from sympy import Symbol, Matrix    >>> mat = RayTransferMatrix(1, 2, 3, 4)    >>> mat    Matrix([    [1, 2],    [3, 4]])    >>> RayTransferMatrix(Matrix([[1, 2], [3, 4]]))    Matrix([    [1, 2],    [3, 4]])    >>> mat.A    1    >>> f = Symbol('f')    >>> lens = ThinLens(f)    >>> lens    Matrix([    [   1, 0],    [-1/f, 1]])    >>> lens.C    -1/f    See Also    ========    GeometricRay, BeamParameter,    FreeSpace, FlatRefraction, CurvedRefraction,    FlatMirror, CurvedMirror, ThinLens    References    ==========    .. [1] https://en.wikipedia.org/wiki/Ray_transfer_matrix_analysis    """    def __new__(cls, *args):        if len(args) == 4:            temp = ((args[0], args[1]), (args[2], args[3]))        elif len(args) == 1 \            and isinstance(args[0], Matrix) \                and args[0].shape == (2, 2):            temp = args[0]        else:            raise ValueError(filldedent('''                Expecting 2x2 Matrix or the 4 elements of                the Matrix but got %s''' % str(args)))        return Matrix.__new__(cls, temp)    def __mul__(self, other):        if isinstance(other, RayTransferMatrix):            return RayTransferMatrix(Matrix.__mul__(self, other))        elif isinstance(other, GeometricRay):            return GeometricRay(Matrix.__mul__(self, other))        elif isinstance(other, BeamParameter):            temp = self*Matrix(((other.q,), (1,)))            q = (temp[0]/temp[1]).expand(complex=True)            return BeamParameter(other.wavelen,                                 together(re(q)),                                 z_r=together(im(q)))        else:            return Matrix.__mul__(self, other)    @property    def A(self):        """        The A parameter of the Matrix.        Examples        ========        >>> from sympy.physics.optics import RayTransferMatrix        >>> mat = RayTransferMatrix(1, 2, 3, 4)        >>> mat.A        1        """        return self[0, 0]    @property    def B(self):        """        The B parameter of the Matrix.        Examples        ========        >>> from sympy.physics.optics import RayTransferMatrix        >>> mat = RayTransferMatrix(1, 2, 3, 4)        >>> mat.B        2        """        return self[0, 1]    @property    def C(self):        """        The C parameter of the Matrix.        Examples        ========        >>> from sympy.physics.optics import RayTransferMatrix        >>> mat = RayTransferMatrix(1, 2, 3, 4)        >>> mat.C        3        """        return self[1, 0]    @property    def D(self):        """        The D parameter of the Matrix.        Examples        ========        >>> from sympy.physics.optics import RayTransferMatrix        >>> mat = RayTransferMatrix(1, 2, 3, 4)        >>> mat.D        4        """        return self[1, 1]class FreeSpace(RayTransferMatrix):    """    Ray Transfer Matrix for free space.    Parameters    ==========    distance    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import FreeSpace    >>> from sympy import symbols    >>> d = symbols('d')    >>> FreeSpace(d)    Matrix([    [1, d],    [0, 1]])    """    def __new__(cls, d):        return RayTransferMatrix.__new__(cls, 1, d, 0, 1)class FlatRefraction(RayTransferMatrix):    """    Ray Transfer Matrix for refraction.    Parameters    ==========    n1 :        Refractive index of one medium.    n2 :        Refractive index of other medium.    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import FlatRefraction    >>> from sympy import symbols    >>> n1, n2 = symbols('n1 n2')    >>> FlatRefraction(n1, n2)    Matrix([    [1,     0],    [0, n1/n2]])    """    def __new__(cls, n1, n2):        n1, n2 = map(sympify, (n1, n2))        return RayTransferMatrix.__new__(cls, 1, 0, 0, n1/n2)class CurvedRefraction(RayTransferMatrix):    """    Ray Transfer Matrix for refraction on curved interface.    Parameters    ==========    R :        Radius of curvature (positive for concave).    n1 :        Refractive index of one medium.    n2 :        Refractive index of other medium.    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import CurvedRefraction    >>> from sympy import symbols    >>> R, n1, n2 = symbols('R n1 n2')    >>> CurvedRefraction(R, n1, n2)    Matrix([    [               1,     0],    [(n1 - n2)/(R*n2), n1/n2]])    """    def __new__(cls, R, n1, n2):        R, n1, n2 = map(sympify, (R, n1, n2))        return RayTransferMatrix.__new__(cls, 1, 0, (n1 - n2)/R/n2, n1/n2)class FlatMirror(RayTransferMatrix):    """    Ray Transfer Matrix for reflection.    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import FlatMirror    >>> FlatMirror()    Matrix([    [1, 0],    [0, 1]])    """    def __new__(cls):        return RayTransferMatrix.__new__(cls, 1, 0, 0, 1)class CurvedMirror(RayTransferMatrix):    """    Ray Transfer Matrix for reflection from curved surface.    Parameters    ==========    R : radius of curvature (positive for concave)    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import CurvedMirror    >>> from sympy import symbols    >>> R = symbols('R')    >>> CurvedMirror(R)    Matrix([    [   1, 0],    [-2/R, 1]])    """    def __new__(cls, R):        R = sympify(R)        return RayTransferMatrix.__new__(cls, 1, 0, -2/R, 1)class ThinLens(RayTransferMatrix):    """    Ray Transfer Matrix for a thin lens.    Parameters    ==========    f :        The focal distance.    See Also    ========    RayTransferMatrix    Examples    ========    >>> from sympy.physics.optics import ThinLens    >>> from sympy import symbols    >>> f = symbols('f')    >>> ThinLens(f)    Matrix([    [   1, 0],    [-1/f, 1]])    """    def __new__(cls, f):        f = sympify(f)        return RayTransferMatrix.__new__(cls, 1, 0, -1/f, 1)#### Representation for geometric ray###class GeometricRay(MutableDenseMatrix):    """    Representation for a geometric ray in the Ray Transfer Matrix formalism.    Parameters    ==========    h : height, and    angle : angle, or    matrix : a 2x1 matrix (Matrix(2, 1, [height, angle]))    Examples    ========    >>> from sympy.physics.optics import GeometricRay, FreeSpace    >>> from sympy import symbols, Matrix    >>> d, h, angle = symbols('d, h, angle')    >>> GeometricRay(h, angle)    Matrix([    [    h],    [angle]])    >>> FreeSpace(d)*GeometricRay(h, angle)    Matrix([    [angle*d + h],    [      angle]])    >>> GeometricRay( Matrix( ((h,), (angle,)) ) )    Matrix([    [    h],    [angle]])    See Also    ========    RayTransferMatrix    """    def __new__(cls, *args):        if len(args) == 1 and isinstance(args[0], Matrix) \                and args[0].shape == (2, 1):            temp = args[0]        elif len(args) == 2:            temp = ((args[0],), (args[1],))        else:            raise ValueError(filldedent('''                Expecting 2x1 Matrix or the 2 elements of                the Matrix but got %s''' % str(args)))        return Matrix.__new__(cls, temp)    @property    def height(self):        """        The distance from the optical axis.        Examples        ========        >>> from sympy.physics.optics import GeometricRay        >>> from sympy import symbols        >>> h, angle = symbols('h, angle')        >>> gRay = GeometricRay(h, angle)        >>> gRay.height        h        """        return self[0]    @property    def angle(self):        """        The angle with the optical axis.        Examples        ========        >>> from sympy.physics.optics import GeometricRay        >>> from sympy import symbols        >>> h, angle = symbols('h, angle')        >>> gRay = GeometricRay(h, angle)        >>> gRay.angle        angle        """        return self[1]#### Representation for gauss beam###class BeamParameter(Expr):    """    Representation for a gaussian ray in the Ray Transfer Matrix formalism.    Parameters    ==========    wavelen : the wavelength,    z : the distance to waist, and    w : the waist, or    z_r : the rayleigh range.    n : the refractive index of medium.    Examples    ========    >>> from sympy.physics.optics import BeamParameter    >>> p = BeamParameter(530e-9, 1, w=1e-3)    >>> p.q    1 + 1.88679245283019*I*pi    >>> p.q.n()    1.0 + 5.92753330865999*I    >>> p.w_0.n()    0.00100000000000000    >>> p.z_r.n()    5.92753330865999    >>> from sympy.physics.optics import FreeSpace    >>> fs = FreeSpace(10)    >>> p1 = fs*p    >>> p.w.n()    0.00101413072159615    >>> p1.w.n()    0.00210803120913829    See Also    ========    RayTransferMatrix    References    ==========    .. [1] https://en.wikipedia.org/wiki/Complex_beam_parameter    .. [2] https://en.wikipedia.org/wiki/Gaussian_beam    """    #TODO A class Complex may be implemented. The BeamParameter may    # subclass it. See:    # https://groups.google.com/d/topic/sympy/7XkU07NRBEs/discussion    def __new__(cls, wavelen, z, z_r=None, w=None, n=1):        wavelen = sympify(wavelen)        z = sympify(z)        n = sympify(n)        if z_r is not None and w is None:            z_r = sympify(z_r)        elif w is not None and z_r is None:            z_r = waist2rayleigh(sympify(w), wavelen, n)        elif z_r is None and w is None:            raise ValueError('Must specify one of w and z_r.')        return Expr.__new__(cls, wavelen, z, z_r, n)    @property    def wavelen(self):        return self.args[0]    @property    def z(self):        return self.args[1]    @property    def z_r(self):        return self.args[2]    @property    def n(self):        return self.args[3]    @property    def q(self):        """        The complex parameter representing the beam.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.q        1 + 1.88679245283019*I*pi        """        return self.z + I*self.z_r    @property    def radius(self):        """        The radius of curvature of the phase front.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.radius        1 + 3.55998576005696*pi**2        """        return self.z*(1 + (self.z_r/self.z)**2)    @property    def w(self):        """        The radius of the beam w(z), at any position z along the beam.        The beam radius at `1/e^2` intensity (axial value).        See Also        ========        w_0 :            The minimal radius of beam.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.w        0.001*sqrt(0.2809/pi**2 + 1)        """        return self.w_0*sqrt(1 + (self.z/self.z_r)**2)    @property    def w_0(self):        """         The minimal radius of beam at `1/e^2` intensity (peak value).        See Also        ========        w : the beam radius at `1/e^2` intensity (axial value).        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.w_0        0.00100000000000000        """        return sqrt(self.z_r/(pi*self.n)*self.wavelen)    @property    def divergence(self):        """        Half of the total angular spread.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.divergence        0.00053/pi        """        return self.wavelen/pi/self.w_0    @property    def gouy(self):        """        The Gouy phase.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.gouy        atan(0.53/pi)        """        return atan2(self.z, self.z_r)    @property    def waist_approximation_limit(self):        """        The minimal waist for which the gauss beam approximation is valid.        Explanation        ===========        The gauss beam is a solution to the paraxial equation. For curvatures        that are too great it is not a valid approximation.        Examples        ========        >>> from sympy.physics.optics import BeamParameter        >>> p = BeamParameter(530e-9, 1, w=1e-3)        >>> p.waist_approximation_limit        1.06e-6/pi        """        return 2*self.wavelen/pi#### Utilities###def waist2rayleigh(w, wavelen, n=1):    """    Calculate the rayleigh range from the waist of a gaussian beam.    See Also    ========    rayleigh2waist, BeamParameter    Examples    ========    >>> from sympy.physics.optics import waist2rayleigh    >>> from sympy import symbols    >>> w, wavelen = symbols('w wavelen')    >>> waist2rayleigh(w, wavelen)    pi*w**2/wavelen    """    w, wavelen = map(sympify, (w, wavelen))    return w**2*n*pi/wavelendef rayleigh2waist(z_r, wavelen):    """Calculate the waist from the rayleigh range of a gaussian beam.    See Also    ========    waist2rayleigh, BeamParameter    Examples    ========    >>> from sympy.physics.optics import rayleigh2waist    >>> from sympy import symbols    >>> z_r, wavelen = symbols('z_r wavelen')    >>> rayleigh2waist(z_r, wavelen)    sqrt(wavelen*z_r)/sqrt(pi)    """    z_r, wavelen = map(sympify, (z_r, wavelen))    return sqrt(z_r/pi*wavelen)def geometric_conj_ab(a, b):    """    Conjugation relation for geometrical beams under paraxial conditions.    Explanation    ===========    Takes the distances to the optical element and returns the needed    focal distance.    See Also    ========    geometric_conj_af, geometric_conj_bf    Examples    ========    >>> from sympy.physics.optics import geometric_conj_ab    >>> from sympy import symbols    >>> a, b = symbols('a b')    >>> geometric_conj_ab(a, b)    a*b/(a + b)    """    a, b = map(sympify, (a, b))    if a.is_infinite or b.is_infinite:        return a if b.is_infinite else b    else:        return a*b/(a + b)def geometric_conj_af(a, f):    """    Conjugation relation for geometrical beams under paraxial conditions.    Explanation    ===========    Takes the object distance (for geometric_conj_af) or the image distance    (for geometric_conj_bf) to the optical element and the focal distance.    Then it returns the other distance needed for conjugation.    See Also    ========    geometric_conj_ab    Examples    ========    >>> from sympy.physics.optics.gaussopt import geometric_conj_af, geometric_conj_bf    >>> from sympy import symbols    >>> a, b, f = symbols('a b f')    >>> geometric_conj_af(a, f)    a*f/(a - f)    >>> geometric_conj_bf(b, f)    b*f/(b - f)    """    a, f = map(sympify, (a, f))    return -geometric_conj_ab(a, -f)geometric_conj_bf = geometric_conj_afdef gaussian_conj(s_in, z_r_in, f):    """    Conjugation relation for gaussian beams.    Parameters    ==========    s_in :        The distance to optical element from the waist.    z_r_in :        The rayleigh range of the incident beam.    f :        The focal length of the optical element.    Returns    =======    a tuple containing (s_out, z_r_out, m)    s_out :        The distance between the new waist and the optical element.    z_r_out :        The rayleigh range of the emergent beam.    m :        The ration between the new and the old waists.    Examples    ========    >>> from sympy.physics.optics import gaussian_conj    >>> from sympy import symbols    >>> s_in, z_r_in, f = symbols('s_in z_r_in f')    >>> gaussian_conj(s_in, z_r_in, f)[0]    1/(-1/(s_in + z_r_in**2/(-f + s_in)) + 1/f)    >>> gaussian_conj(s_in, z_r_in, f)[1]    z_r_in/(1 - s_in**2/f**2 + z_r_in**2/f**2)    >>> gaussian_conj(s_in, z_r_in, f)[2]    1/sqrt(1 - s_in**2/f**2 + z_r_in**2/f**2)    """    s_in, z_r_in, f = map(sympify, (s_in, z_r_in, f))    s_out = 1 / ( -1/(s_in + z_r_in**2/(s_in - f)) + 1/f )    m = 1/sqrt((1 - (s_in/f)**2) + (z_r_in/f)**2)    z_r_out = z_r_in / ((1 - (s_in/f)**2) + (z_r_in/f)**2)    return (s_out, z_r_out, m)def conjugate_gauss_beams(wavelen, waist_in, waist_out, **kwargs):    """    Find the optical setup conjugating the object/image waists.    Parameters    ==========    wavelen :        The wavelength of the beam.    waist_in and waist_out :        The waists to be conjugated.    f :        The focal distance of the element used in the conjugation.    Returns    =======    a tuple containing (s_in, s_out, f)    s_in :        The distance before the optical element.    s_out :        The distance after the optical element.    f :        The focal distance of the optical element.    Examples    ========    >>> from sympy.physics.optics import conjugate_gauss_beams    >>> from sympy import symbols, factor    >>> l, w_i, w_o, f = symbols('l w_i w_o f')    >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[0]    f*(1 - sqrt(w_i**2/w_o**2 - pi**2*w_i**4/(f**2*l**2)))    >>> factor(conjugate_gauss_beams(l, w_i, w_o, f=f)[1])    f*w_o**2*(w_i**2/w_o**2 - sqrt(w_i**2/w_o**2 -              pi**2*w_i**4/(f**2*l**2)))/w_i**2    >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[2]    f    """    #TODO add the other possible arguments    wavelen, waist_in, waist_out = map(sympify, (wavelen, waist_in, waist_out))    m = waist_out / waist_in    z = waist2rayleigh(waist_in, wavelen)    if len(kwargs) != 1:        raise ValueError("The function expects only one named argument")    elif 'dist' in kwargs:        raise NotImplementedError(filldedent('''            Currently only focal length is supported as a parameter'''))    elif 'f' in kwargs:        f = sympify(kwargs['f'])        s_in = f * (1 - sqrt(1/m**2 - z**2/f**2))        s_out = gaussian_conj(s_in, z, f)[0]    elif 's_in' in kwargs:        raise NotImplementedError(filldedent('''            Currently only focal length is supported as a parameter'''))    else:        raise ValueError(filldedent('''            The functions expects the focal length as a named argument'''))    return (s_in, s_out, f)#TODO#def plot_beam():#    """Plot the beam radius as it propagates in space."""#    pass#TODO#def plot_beam_conjugation():#    """#    Plot the intersection of two beams.##    Represents the conjugation relation.##    See Also#    ========##    conjugate_gauss_beams#    """#    pass
 |