123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- """
- 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_beams
- The conventions for the distances are as follows:
- focal distance
- positive for convergent lenses
- object distance
- positive for real objects
- image 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 Expr
- from sympy.core.numbers import (I, pi)
- from sympy.core.sympify import sympify
- from sympy.functions.elementary.complexes import (im, re)
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.functions.elementary.trigonometric import atan2
- from sympy.matrices.dense import Matrix, MutableDenseMatrix
- from sympy.polys.rationaltools import together
- from 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 isn't 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.
- 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):
- wavelen = sympify(wavelen)
- z = sympify(z)
- 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)
- else:
- raise ValueError('Constructor expects exactly one named argument.')
- return Expr.__new__(cls, wavelen, z, z_r)
- @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 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 beam radius at `1/e^2` intensity.
- 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 beam waist (minimal radius).
- See Also
- ========
- w : the beam radius at `1/e^2` intensity
- 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.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):
- """
- 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*pi/wavelen
- def 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_af
- def 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
|