add read me
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
__all__ = [
|
||||
'TWave',
|
||||
|
||||
'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',
|
||||
|
||||
'Medium',
|
||||
|
||||
'refraction_angle', 'deviation', 'fresnel_coefficients', 'brewster_angle',
|
||||
'critical_angle', 'lens_makers_formula', 'mirror_formula', 'lens_formula',
|
||||
'hyperfocal_distance', 'transverse_magnification',
|
||||
|
||||
'jones_vector', 'stokes_vector', 'jones_2_stokes', 'linear_polarizer',
|
||||
'phase_retarder', 'half_wave_retarder', 'quarter_wave_retarder',
|
||||
'transmissive_filter', 'reflective_filter', 'mueller_matrix',
|
||||
'polarizing_beam_splitter',
|
||||
]
|
||||
from .waves import TWave
|
||||
|
||||
from .gaussopt import (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 .medium import Medium
|
||||
|
||||
from .utils import (refraction_angle, deviation, fresnel_coefficients,
|
||||
brewster_angle, critical_angle, lens_makers_formula, mirror_formula,
|
||||
lens_formula, hyperfocal_distance, transverse_magnification)
|
||||
|
||||
from .polarization import (jones_vector, stokes_vector, jones_2_stokes,
|
||||
linear_polarizer, phase_retarder, half_wave_retarder,
|
||||
quarter_wave_retarder, transmissive_filter, reflective_filter,
|
||||
mueller_matrix, polarizing_beam_splitter)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,923 @@
|
||||
"""
|
||||
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 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(self)*Matrix(other))
|
||||
elif isinstance(other, GeometricRay):
|
||||
return GeometricRay(Matrix(self)*Matrix(other))
|
||||
elif isinstance(other, BeamParameter):
|
||||
temp = Matrix(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/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
|
||||
253
venv/lib/python3.12/site-packages/sympy/physics/optics/medium.py
Normal file
253
venv/lib/python3.12/site-packages/sympy/physics/optics/medium.py
Normal file
@@ -0,0 +1,253 @@
|
||||
"""
|
||||
**Contains**
|
||||
|
||||
* Medium
|
||||
"""
|
||||
from sympy.physics.units import second, meter, kilogram, ampere
|
||||
|
||||
__all__ = ['Medium']
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.symbol import Str
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.physics.units import speed_of_light, u0, e0
|
||||
|
||||
|
||||
c = speed_of_light.convert_to(meter/second)
|
||||
_e0mksa = e0.convert_to(ampere**2*second**4/(kilogram*meter**3))
|
||||
_u0mksa = u0.convert_to(meter*kilogram/(ampere**2*second**2))
|
||||
|
||||
|
||||
class Medium(Basic):
|
||||
|
||||
"""
|
||||
This class represents an optical medium. The prime reason to implement this is
|
||||
to facilitate refraction, Fermat's principle, etc.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
An optical medium is a material through which electromagnetic waves propagate.
|
||||
The permittivity and permeability of the medium define how electromagnetic
|
||||
waves propagate in it.
|
||||
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name: string
|
||||
The display name of the Medium.
|
||||
|
||||
permittivity: Sympifyable
|
||||
Electric permittivity of the space.
|
||||
|
||||
permeability: Sympifyable
|
||||
Magnetic permeability of the space.
|
||||
|
||||
n: Sympifyable
|
||||
Index of refraction of the medium.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import epsilon, mu
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m1 = Medium('m1')
|
||||
>>> m2 = Medium('m2', epsilon, mu)
|
||||
>>> m1.intrinsic_impedance
|
||||
149896229*pi*kilogram*meter**2/(1250000*ampere**2*second**3)
|
||||
>>> m2.refractive_index
|
||||
299792458*meter*sqrt(epsilon*mu)/second
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Optical_medium
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, name, permittivity=None, permeability=None, n=None):
|
||||
if not isinstance(name, Str):
|
||||
name = Str(name)
|
||||
|
||||
permittivity = _sympify(permittivity) if permittivity is not None else permittivity
|
||||
permeability = _sympify(permeability) if permeability is not None else permeability
|
||||
n = _sympify(n) if n is not None else n
|
||||
|
||||
if n is not None:
|
||||
if permittivity is not None and permeability is None:
|
||||
permeability = n**2/(c**2*permittivity)
|
||||
return MediumPP(name, permittivity, permeability)
|
||||
elif permeability is not None and permittivity is None:
|
||||
permittivity = n**2/(c**2*permeability)
|
||||
return MediumPP(name, permittivity, permeability)
|
||||
elif permittivity is not None and permittivity is not None:
|
||||
raise ValueError("Specifying all of permittivity, permeability, and n is not allowed")
|
||||
else:
|
||||
return MediumN(name, n)
|
||||
elif permittivity is not None and permeability is not None:
|
||||
return MediumPP(name, permittivity, permeability)
|
||||
elif permittivity is None and permeability is None:
|
||||
return MediumPP(name, _e0mksa, _u0mksa)
|
||||
else:
|
||||
raise ValueError("Arguments are underspecified. Either specify n or any two of permittivity, "
|
||||
"permeability, and n")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def speed(self):
|
||||
"""
|
||||
Returns speed of the electromagnetic wave travelling in the medium.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m')
|
||||
>>> m.speed
|
||||
299792458*meter/second
|
||||
>>> m2 = Medium('m2', n=1)
|
||||
>>> m.speed == m2.speed
|
||||
True
|
||||
|
||||
"""
|
||||
return c / self.n
|
||||
|
||||
@property
|
||||
def refractive_index(self):
|
||||
"""
|
||||
Returns refractive index of the medium.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m')
|
||||
>>> m.refractive_index
|
||||
1
|
||||
|
||||
"""
|
||||
return (c/self.speed)
|
||||
|
||||
|
||||
class MediumN(Medium):
|
||||
|
||||
"""
|
||||
Represents an optical medium for which only the refractive index is known.
|
||||
Useful for simple ray optics.
|
||||
|
||||
This class should never be instantiated directly.
|
||||
Instead it should be instantiated indirectly by instantiating Medium with
|
||||
only n specified.
|
||||
|
||||
Examples
|
||||
========
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m', n=2)
|
||||
>>> m
|
||||
MediumN(Str('m'), 2)
|
||||
"""
|
||||
|
||||
def __new__(cls, name, n):
|
||||
obj = super(Medium, cls).__new__(cls, name, n)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def n(self):
|
||||
return self.args[1]
|
||||
|
||||
|
||||
class MediumPP(Medium):
|
||||
"""
|
||||
Represents an optical medium for which the permittivity and permeability are known.
|
||||
|
||||
This class should never be instantiated directly. Instead it should be
|
||||
instantiated indirectly by instantiating Medium with any two of
|
||||
permittivity, permeability, and n specified, or by not specifying any
|
||||
of permittivity, permeability, or n, in which case default values for
|
||||
permittivity and permeability will be used.
|
||||
|
||||
Examples
|
||||
========
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> from sympy.abc import epsilon, mu
|
||||
>>> m1 = Medium('m1', permittivity=epsilon, permeability=mu)
|
||||
>>> m1
|
||||
MediumPP(Str('m1'), epsilon, mu)
|
||||
>>> m2 = Medium('m2')
|
||||
>>> m2
|
||||
MediumPP(Str('m2'), 625000*ampere**2*second**4/(22468879468420441*pi*kilogram*meter**3), pi*kilogram*meter/(2500000*ampere**2*second**2))
|
||||
"""
|
||||
|
||||
|
||||
def __new__(cls, name, permittivity, permeability):
|
||||
obj = super(Medium, cls).__new__(cls, name, permittivity, permeability)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def intrinsic_impedance(self):
|
||||
"""
|
||||
Returns intrinsic impedance of the medium.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The intrinsic impedance of a medium is the ratio of the
|
||||
transverse components of the electric and magnetic fields
|
||||
of the electromagnetic wave travelling in the medium.
|
||||
In a region with no electrical conductivity it simplifies
|
||||
to the square root of ratio of magnetic permeability to
|
||||
electric permittivity.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m')
|
||||
>>> m.intrinsic_impedance
|
||||
149896229*pi*kilogram*meter**2/(1250000*ampere**2*second**3)
|
||||
|
||||
"""
|
||||
return sqrt(self.permeability / self.permittivity)
|
||||
|
||||
@property
|
||||
def permittivity(self):
|
||||
"""
|
||||
Returns electric permittivity of the medium.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m')
|
||||
>>> m.permittivity
|
||||
625000*ampere**2*second**4/(22468879468420441*pi*kilogram*meter**3)
|
||||
|
||||
"""
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def permeability(self):
|
||||
"""
|
||||
Returns magnetic permeability of the medium.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import Medium
|
||||
>>> m = Medium('m')
|
||||
>>> m.permeability
|
||||
pi*kilogram*meter/(2500000*ampere**2*second**2)
|
||||
|
||||
"""
|
||||
return self.args[2]
|
||||
|
||||
@property
|
||||
def n(self):
|
||||
return c*sqrt(self.permittivity*self.permeability)
|
||||
@@ -0,0 +1,732 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
The module implements routines to model the polarization of optical fields
|
||||
and can be used to calculate the effects of polarization optical elements on
|
||||
the fields.
|
||||
|
||||
- Jones vectors.
|
||||
|
||||
- Stokes vectors.
|
||||
|
||||
- Jones matrices.
|
||||
|
||||
- Mueller matrices.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
We calculate a generic Jones vector:
|
||||
|
||||
>>> from sympy import symbols, pprint, zeros, simplify
|
||||
>>> from sympy.physics.optics.polarization import (jones_vector, stokes_vector,
|
||||
... half_wave_retarder, polarizing_beam_splitter, jones_2_stokes)
|
||||
|
||||
>>> psi, chi, p, I0 = symbols("psi, chi, p, I0", real=True)
|
||||
>>> x0 = jones_vector(psi, chi)
|
||||
>>> pprint(x0, use_unicode=True)
|
||||
⎡-ⅈ⋅sin(χ)⋅sin(ψ) + cos(χ)⋅cos(ψ)⎤
|
||||
⎢ ⎥
|
||||
⎣ⅈ⋅sin(χ)⋅cos(ψ) + sin(ψ)⋅cos(χ) ⎦
|
||||
|
||||
And the more general Stokes vector:
|
||||
|
||||
>>> s0 = stokes_vector(psi, chi, p, I0)
|
||||
>>> pprint(s0, use_unicode=True)
|
||||
⎡ I₀ ⎤
|
||||
⎢ ⎥
|
||||
⎢I₀⋅p⋅cos(2⋅χ)⋅cos(2⋅ψ)⎥
|
||||
⎢ ⎥
|
||||
⎢I₀⋅p⋅sin(2⋅ψ)⋅cos(2⋅χ)⎥
|
||||
⎢ ⎥
|
||||
⎣ I₀⋅p⋅sin(2⋅χ) ⎦
|
||||
|
||||
We calculate how the Jones vector is modified by a half-wave plate:
|
||||
|
||||
>>> alpha = symbols("alpha", real=True)
|
||||
>>> HWP = half_wave_retarder(alpha)
|
||||
>>> x1 = simplify(HWP*x0)
|
||||
|
||||
We calculate the very common operation of passing a beam through a half-wave
|
||||
plate and then through a polarizing beam-splitter. We do this by putting this
|
||||
Jones vector as the first entry of a two-Jones-vector state that is transformed
|
||||
by a 4x4 Jones matrix modelling the polarizing beam-splitter to get the
|
||||
transmitted and reflected Jones vectors:
|
||||
|
||||
>>> PBS = polarizing_beam_splitter()
|
||||
>>> X1 = zeros(4, 1)
|
||||
>>> X1[:2, :] = x1
|
||||
>>> X2 = PBS*X1
|
||||
>>> transmitted_port = X2[:2, :]
|
||||
>>> reflected_port = X2[2:, :]
|
||||
|
||||
This allows us to calculate how the power in both ports depends on the initial
|
||||
polarization:
|
||||
|
||||
>>> transmitted_power = jones_2_stokes(transmitted_port)[0]
|
||||
>>> reflected_power = jones_2_stokes(reflected_port)[0]
|
||||
>>> print(transmitted_power)
|
||||
cos(-2*alpha + chi + psi)**2/2 + cos(2*alpha + chi - psi)**2/2
|
||||
|
||||
|
||||
>>> print(reflected_power)
|
||||
sin(-2*alpha + chi + psi)**2/2 + sin(2*alpha + chi - psi)**2/2
|
||||
|
||||
Please see the description of the individual functions for further
|
||||
details and examples.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Jones_calculus
|
||||
.. [2] https://en.wikipedia.org/wiki/Mueller_calculus
|
||||
.. [3] https://en.wikipedia.org/wiki/Stokes_parameters
|
||||
|
||||
"""
|
||||
|
||||
from sympy.core.numbers import (I, pi)
|
||||
from sympy.functions.elementary.complexes import (Abs, im, re)
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.physics.quantum import TensorProduct
|
||||
|
||||
|
||||
def jones_vector(psi, chi):
|
||||
"""A Jones vector corresponding to a polarization ellipse with `psi` tilt,
|
||||
and `chi` circularity.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
psi : numeric type or SymPy Symbol
|
||||
The tilt of the polarization relative to the `x` axis.
|
||||
|
||||
chi : numeric type or SymPy Symbol
|
||||
The angle adjacent to the mayor axis of the polarization ellipse.
|
||||
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Matrix :
|
||||
A Jones vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The axes on the Poincaré sphere.
|
||||
|
||||
>>> from sympy import pprint, symbols, pi
|
||||
>>> from sympy.physics.optics.polarization import jones_vector
|
||||
>>> psi, chi = symbols("psi, chi", real=True)
|
||||
|
||||
A general Jones vector.
|
||||
|
||||
>>> pprint(jones_vector(psi, chi), use_unicode=True)
|
||||
⎡-ⅈ⋅sin(χ)⋅sin(ψ) + cos(χ)⋅cos(ψ)⎤
|
||||
⎢ ⎥
|
||||
⎣ⅈ⋅sin(χ)⋅cos(ψ) + sin(ψ)⋅cos(χ) ⎦
|
||||
|
||||
Horizontal polarization.
|
||||
|
||||
>>> pprint(jones_vector(0, 0), use_unicode=True)
|
||||
⎡1⎤
|
||||
⎢ ⎥
|
||||
⎣0⎦
|
||||
|
||||
Vertical polarization.
|
||||
|
||||
>>> pprint(jones_vector(pi/2, 0), use_unicode=True)
|
||||
⎡0⎤
|
||||
⎢ ⎥
|
||||
⎣1⎦
|
||||
|
||||
Diagonal polarization.
|
||||
|
||||
>>> pprint(jones_vector(pi/4, 0), use_unicode=True)
|
||||
⎡√2⎤
|
||||
⎢──⎥
|
||||
⎢2 ⎥
|
||||
⎢ ⎥
|
||||
⎢√2⎥
|
||||
⎢──⎥
|
||||
⎣2 ⎦
|
||||
|
||||
Anti-diagonal polarization.
|
||||
|
||||
>>> pprint(jones_vector(-pi/4, 0), use_unicode=True)
|
||||
⎡ √2 ⎤
|
||||
⎢ ── ⎥
|
||||
⎢ 2 ⎥
|
||||
⎢ ⎥
|
||||
⎢-√2 ⎥
|
||||
⎢────⎥
|
||||
⎣ 2 ⎦
|
||||
|
||||
Right-hand circular polarization.
|
||||
|
||||
>>> pprint(jones_vector(0, pi/4), use_unicode=True)
|
||||
⎡ √2 ⎤
|
||||
⎢ ── ⎥
|
||||
⎢ 2 ⎥
|
||||
⎢ ⎥
|
||||
⎢√2⋅ⅈ⎥
|
||||
⎢────⎥
|
||||
⎣ 2 ⎦
|
||||
|
||||
Left-hand circular polarization.
|
||||
|
||||
>>> pprint(jones_vector(0, -pi/4), use_unicode=True)
|
||||
⎡ √2 ⎤
|
||||
⎢ ── ⎥
|
||||
⎢ 2 ⎥
|
||||
⎢ ⎥
|
||||
⎢-√2⋅ⅈ ⎥
|
||||
⎢──────⎥
|
||||
⎣ 2 ⎦
|
||||
|
||||
"""
|
||||
return Matrix([-I*sin(chi)*sin(psi) + cos(chi)*cos(psi),
|
||||
I*sin(chi)*cos(psi) + sin(psi)*cos(chi)])
|
||||
|
||||
|
||||
def stokes_vector(psi, chi, p=1, I=1):
|
||||
"""A Stokes vector corresponding to a polarization ellipse with ``psi``
|
||||
tilt, and ``chi`` circularity.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
psi : numeric type or SymPy Symbol
|
||||
The tilt of the polarization relative to the ``x`` axis.
|
||||
chi : numeric type or SymPy Symbol
|
||||
The angle adjacent to the mayor axis of the polarization ellipse.
|
||||
p : numeric type or SymPy Symbol
|
||||
The degree of polarization.
|
||||
I : numeric type or SymPy Symbol
|
||||
The intensity of the field.
|
||||
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Matrix :
|
||||
A Stokes vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The axes on the Poincaré sphere.
|
||||
|
||||
>>> from sympy import pprint, symbols, pi
|
||||
>>> from sympy.physics.optics.polarization import stokes_vector
|
||||
>>> psi, chi, p, I = symbols("psi, chi, p, I", real=True)
|
||||
>>> pprint(stokes_vector(psi, chi, p, I), use_unicode=True)
|
||||
⎡ I ⎤
|
||||
⎢ ⎥
|
||||
⎢I⋅p⋅cos(2⋅χ)⋅cos(2⋅ψ)⎥
|
||||
⎢ ⎥
|
||||
⎢I⋅p⋅sin(2⋅ψ)⋅cos(2⋅χ)⎥
|
||||
⎢ ⎥
|
||||
⎣ I⋅p⋅sin(2⋅χ) ⎦
|
||||
|
||||
|
||||
Horizontal polarization
|
||||
|
||||
>>> pprint(stokes_vector(0, 0), use_unicode=True)
|
||||
⎡1⎤
|
||||
⎢ ⎥
|
||||
⎢1⎥
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎣0⎦
|
||||
|
||||
Vertical polarization
|
||||
|
||||
>>> pprint(stokes_vector(pi/2, 0), use_unicode=True)
|
||||
⎡1 ⎤
|
||||
⎢ ⎥
|
||||
⎢-1⎥
|
||||
⎢ ⎥
|
||||
⎢0 ⎥
|
||||
⎢ ⎥
|
||||
⎣0 ⎦
|
||||
|
||||
Diagonal polarization
|
||||
|
||||
>>> pprint(stokes_vector(pi/4, 0), use_unicode=True)
|
||||
⎡1⎤
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎢1⎥
|
||||
⎢ ⎥
|
||||
⎣0⎦
|
||||
|
||||
Anti-diagonal polarization
|
||||
|
||||
>>> pprint(stokes_vector(-pi/4, 0), use_unicode=True)
|
||||
⎡1 ⎤
|
||||
⎢ ⎥
|
||||
⎢0 ⎥
|
||||
⎢ ⎥
|
||||
⎢-1⎥
|
||||
⎢ ⎥
|
||||
⎣0 ⎦
|
||||
|
||||
Right-hand circular polarization
|
||||
|
||||
>>> pprint(stokes_vector(0, pi/4), use_unicode=True)
|
||||
⎡1⎤
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎣1⎦
|
||||
|
||||
Left-hand circular polarization
|
||||
|
||||
>>> pprint(stokes_vector(0, -pi/4), use_unicode=True)
|
||||
⎡1 ⎤
|
||||
⎢ ⎥
|
||||
⎢0 ⎥
|
||||
⎢ ⎥
|
||||
⎢0 ⎥
|
||||
⎢ ⎥
|
||||
⎣-1⎦
|
||||
|
||||
Unpolarized light
|
||||
|
||||
>>> pprint(stokes_vector(0, 0, 0), use_unicode=True)
|
||||
⎡1⎤
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎢0⎥
|
||||
⎢ ⎥
|
||||
⎣0⎦
|
||||
|
||||
"""
|
||||
S0 = I
|
||||
S1 = I*p*cos(2*psi)*cos(2*chi)
|
||||
S2 = I*p*sin(2*psi)*cos(2*chi)
|
||||
S3 = I*p*sin(2*chi)
|
||||
return Matrix([S0, S1, S2, S3])
|
||||
|
||||
|
||||
def jones_2_stokes(e):
|
||||
"""Return the Stokes vector for a Jones vector ``e``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
e : SymPy Matrix
|
||||
A Jones vector.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The axes on the Poincaré sphere.
|
||||
|
||||
>>> from sympy import pprint, pi
|
||||
>>> from sympy.physics.optics.polarization import jones_vector
|
||||
>>> from sympy.physics.optics.polarization import jones_2_stokes
|
||||
>>> H = jones_vector(0, 0)
|
||||
>>> V = jones_vector(pi/2, 0)
|
||||
>>> D = jones_vector(pi/4, 0)
|
||||
>>> A = jones_vector(-pi/4, 0)
|
||||
>>> R = jones_vector(0, pi/4)
|
||||
>>> L = jones_vector(0, -pi/4)
|
||||
>>> pprint([jones_2_stokes(e) for e in [H, V, D, A, R, L]],
|
||||
... use_unicode=True)
|
||||
⎡⎡1⎤ ⎡1 ⎤ ⎡1⎤ ⎡1 ⎤ ⎡1⎤ ⎡1 ⎤⎤
|
||||
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
|
||||
⎢⎢1⎥ ⎢-1⎥ ⎢0⎥ ⎢0 ⎥ ⎢0⎥ ⎢0 ⎥⎥
|
||||
⎢⎢ ⎥, ⎢ ⎥, ⎢ ⎥, ⎢ ⎥, ⎢ ⎥, ⎢ ⎥⎥
|
||||
⎢⎢0⎥ ⎢0 ⎥ ⎢1⎥ ⎢-1⎥ ⎢0⎥ ⎢0 ⎥⎥
|
||||
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
|
||||
⎣⎣0⎦ ⎣0 ⎦ ⎣0⎦ ⎣0 ⎦ ⎣1⎦ ⎣-1⎦⎦
|
||||
|
||||
"""
|
||||
ex, ey = e
|
||||
return Matrix([Abs(ex)**2 + Abs(ey)**2,
|
||||
Abs(ex)**2 - Abs(ey)**2,
|
||||
2*re(ex*ey.conjugate()),
|
||||
-2*im(ex*ey.conjugate())])
|
||||
|
||||
|
||||
def linear_polarizer(theta=0):
|
||||
"""A linear polarizer Jones matrix with transmission axis at
|
||||
an angle ``theta``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
theta : numeric type or SymPy Symbol
|
||||
The angle of the transmission axis relative to the horizontal plane.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones matrix representing the polarizer.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic polarizer.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import linear_polarizer
|
||||
>>> theta = symbols("theta", real=True)
|
||||
>>> J = linear_polarizer(theta)
|
||||
>>> pprint(J, use_unicode=True)
|
||||
⎡ 2 ⎤
|
||||
⎢ cos (θ) sin(θ)⋅cos(θ)⎥
|
||||
⎢ ⎥
|
||||
⎢ 2 ⎥
|
||||
⎣sin(θ)⋅cos(θ) sin (θ) ⎦
|
||||
|
||||
|
||||
"""
|
||||
M = Matrix([[cos(theta)**2, sin(theta)*cos(theta)],
|
||||
[sin(theta)*cos(theta), sin(theta)**2]])
|
||||
return M
|
||||
|
||||
|
||||
def phase_retarder(theta=0, delta=0):
|
||||
"""A phase retarder Jones matrix with retardance ``delta`` at angle ``theta``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
theta : numeric type or SymPy Symbol
|
||||
The angle of the fast axis relative to the horizontal plane.
|
||||
delta : numeric type or SymPy Symbol
|
||||
The phase difference between the fast and slow axes of the
|
||||
transmitted light.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix :
|
||||
A Jones matrix representing the retarder.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic retarder.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import phase_retarder
|
||||
>>> theta, delta = symbols("theta, delta", real=True)
|
||||
>>> R = phase_retarder(theta, delta)
|
||||
>>> pprint(R, use_unicode=True)
|
||||
⎡ -ⅈ⋅δ -ⅈ⋅δ ⎤
|
||||
⎢ ───── ───── ⎥
|
||||
⎢⎛ ⅈ⋅δ 2 2 ⎞ 2 ⎛ ⅈ⋅δ⎞ 2 ⎥
|
||||
⎢⎝ℯ ⋅sin (θ) + cos (θ)⎠⋅ℯ ⎝1 - ℯ ⎠⋅ℯ ⋅sin(θ)⋅cos(θ)⎥
|
||||
⎢ ⎥
|
||||
⎢ -ⅈ⋅δ -ⅈ⋅δ ⎥
|
||||
⎢ ───── ─────⎥
|
||||
⎢⎛ ⅈ⋅δ⎞ 2 ⎛ ⅈ⋅δ 2 2 ⎞ 2 ⎥
|
||||
⎣⎝1 - ℯ ⎠⋅ℯ ⋅sin(θ)⋅cos(θ) ⎝ℯ ⋅cos (θ) + sin (θ)⎠⋅ℯ ⎦
|
||||
|
||||
"""
|
||||
R = Matrix([[cos(theta)**2 + exp(I*delta)*sin(theta)**2,
|
||||
(1-exp(I*delta))*cos(theta)*sin(theta)],
|
||||
[(1-exp(I*delta))*cos(theta)*sin(theta),
|
||||
sin(theta)**2 + exp(I*delta)*cos(theta)**2]])
|
||||
return R*exp(-I*delta/2)
|
||||
|
||||
|
||||
def half_wave_retarder(theta):
|
||||
"""A half-wave retarder Jones matrix at angle ``theta``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
theta : numeric type or SymPy Symbol
|
||||
The angle of the fast axis relative to the horizontal plane.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones matrix representing the retarder.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic half-wave plate.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import half_wave_retarder
|
||||
>>> theta= symbols("theta", real=True)
|
||||
>>> HWP = half_wave_retarder(theta)
|
||||
>>> pprint(HWP, use_unicode=True)
|
||||
⎡ ⎛ 2 2 ⎞ ⎤
|
||||
⎢-ⅈ⋅⎝- sin (θ) + cos (θ)⎠ -2⋅ⅈ⋅sin(θ)⋅cos(θ) ⎥
|
||||
⎢ ⎥
|
||||
⎢ ⎛ 2 2 ⎞⎥
|
||||
⎣ -2⋅ⅈ⋅sin(θ)⋅cos(θ) -ⅈ⋅⎝sin (θ) - cos (θ)⎠⎦
|
||||
|
||||
"""
|
||||
return phase_retarder(theta, pi)
|
||||
|
||||
|
||||
def quarter_wave_retarder(theta):
|
||||
"""A quarter-wave retarder Jones matrix at angle ``theta``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
theta : numeric type or SymPy Symbol
|
||||
The angle of the fast axis relative to the horizontal plane.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones matrix representing the retarder.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic quarter-wave plate.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import quarter_wave_retarder
|
||||
>>> theta= symbols("theta", real=True)
|
||||
>>> QWP = quarter_wave_retarder(theta)
|
||||
>>> pprint(QWP, use_unicode=True)
|
||||
⎡ -ⅈ⋅π -ⅈ⋅π ⎤
|
||||
⎢ ───── ───── ⎥
|
||||
⎢⎛ 2 2 ⎞ 4 4 ⎥
|
||||
⎢⎝ⅈ⋅sin (θ) + cos (θ)⎠⋅ℯ (1 - ⅈ)⋅ℯ ⋅sin(θ)⋅cos(θ)⎥
|
||||
⎢ ⎥
|
||||
⎢ -ⅈ⋅π -ⅈ⋅π ⎥
|
||||
⎢ ───── ─────⎥
|
||||
⎢ 4 ⎛ 2 2 ⎞ 4 ⎥
|
||||
⎣(1 - ⅈ)⋅ℯ ⋅sin(θ)⋅cos(θ) ⎝sin (θ) + ⅈ⋅cos (θ)⎠⋅ℯ ⎦
|
||||
|
||||
"""
|
||||
return phase_retarder(theta, pi/2)
|
||||
|
||||
|
||||
def transmissive_filter(T):
|
||||
"""An attenuator Jones matrix with transmittance ``T``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
T : numeric type or SymPy Symbol
|
||||
The transmittance of the attenuator.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones matrix representing the filter.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic filter.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import transmissive_filter
|
||||
>>> T = symbols("T", real=True)
|
||||
>>> NDF = transmissive_filter(T)
|
||||
>>> pprint(NDF, use_unicode=True)
|
||||
⎡√T 0 ⎤
|
||||
⎢ ⎥
|
||||
⎣0 √T⎦
|
||||
|
||||
"""
|
||||
return Matrix([[sqrt(T), 0], [0, sqrt(T)]])
|
||||
|
||||
|
||||
def reflective_filter(R):
|
||||
"""A reflective filter Jones matrix with reflectance ``R``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
R : numeric type or SymPy Symbol
|
||||
The reflectance of the filter.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A Jones matrix representing the filter.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A generic filter.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import reflective_filter
|
||||
>>> R = symbols("R", real=True)
|
||||
>>> pprint(reflective_filter(R), use_unicode=True)
|
||||
⎡√R 0 ⎤
|
||||
⎢ ⎥
|
||||
⎣0 -√R⎦
|
||||
|
||||
"""
|
||||
return Matrix([[sqrt(R), 0], [0, -sqrt(R)]])
|
||||
|
||||
|
||||
def mueller_matrix(J):
|
||||
"""The Mueller matrix corresponding to Jones matrix `J`.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
J : SymPy Matrix
|
||||
A Jones matrix.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
The corresponding Mueller matrix.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Generic optical components.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import (mueller_matrix,
|
||||
... linear_polarizer, half_wave_retarder, quarter_wave_retarder)
|
||||
>>> theta = symbols("theta", real=True)
|
||||
|
||||
A linear_polarizer
|
||||
|
||||
>>> pprint(mueller_matrix(linear_polarizer(theta)), use_unicode=True)
|
||||
⎡ cos(2⋅θ) sin(2⋅θ) ⎤
|
||||
⎢ 1/2 ──────── ──────── 0⎥
|
||||
⎢ 2 2 ⎥
|
||||
⎢ ⎥
|
||||
⎢cos(2⋅θ) cos(4⋅θ) 1 sin(4⋅θ) ⎥
|
||||
⎢──────── ──────── + ─ ──────── 0⎥
|
||||
⎢ 2 4 4 4 ⎥
|
||||
⎢ ⎥
|
||||
⎢sin(2⋅θ) sin(4⋅θ) 1 cos(4⋅θ) ⎥
|
||||
⎢──────── ──────── ─ - ──────── 0⎥
|
||||
⎢ 2 4 4 4 ⎥
|
||||
⎢ ⎥
|
||||
⎣ 0 0 0 0⎦
|
||||
|
||||
A half-wave plate
|
||||
|
||||
>>> pprint(mueller_matrix(half_wave_retarder(theta)), use_unicode=True)
|
||||
⎡1 0 0 0 ⎤
|
||||
⎢ ⎥
|
||||
⎢ 4 2 ⎥
|
||||
⎢0 8⋅sin (θ) - 8⋅sin (θ) + 1 sin(4⋅θ) 0 ⎥
|
||||
⎢ ⎥
|
||||
⎢ 4 2 ⎥
|
||||
⎢0 sin(4⋅θ) - 8⋅sin (θ) + 8⋅sin (θ) - 1 0 ⎥
|
||||
⎢ ⎥
|
||||
⎣0 0 0 -1⎦
|
||||
|
||||
A quarter-wave plate
|
||||
|
||||
>>> pprint(mueller_matrix(quarter_wave_retarder(theta)), use_unicode=True)
|
||||
⎡1 0 0 0 ⎤
|
||||
⎢ ⎥
|
||||
⎢ cos(4⋅θ) 1 sin(4⋅θ) ⎥
|
||||
⎢0 ──────── + ─ ──────── -sin(2⋅θ)⎥
|
||||
⎢ 2 2 2 ⎥
|
||||
⎢ ⎥
|
||||
⎢ sin(4⋅θ) 1 cos(4⋅θ) ⎥
|
||||
⎢0 ──────── ─ - ──────── cos(2⋅θ) ⎥
|
||||
⎢ 2 2 2 ⎥
|
||||
⎢ ⎥
|
||||
⎣0 sin(2⋅θ) -cos(2⋅θ) 0 ⎦
|
||||
|
||||
"""
|
||||
A = Matrix([[1, 0, 0, 1],
|
||||
[1, 0, 0, -1],
|
||||
[0, 1, 1, 0],
|
||||
[0, -I, I, 0]])
|
||||
|
||||
return simplify(A*TensorProduct(J, J.conjugate())*A.inv())
|
||||
|
||||
|
||||
def polarizing_beam_splitter(Tp=1, Rs=1, Ts=0, Rp=0, phia=0, phib=0):
|
||||
r"""A polarizing beam splitter Jones matrix at angle `theta`.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
J : SymPy Matrix
|
||||
A Jones matrix.
|
||||
Tp : numeric type or SymPy Symbol
|
||||
The transmissivity of the P-polarized component.
|
||||
Rs : numeric type or SymPy Symbol
|
||||
The reflectivity of the S-polarized component.
|
||||
Ts : numeric type or SymPy Symbol
|
||||
The transmissivity of the S-polarized component.
|
||||
Rp : numeric type or SymPy Symbol
|
||||
The reflectivity of the P-polarized component.
|
||||
phia : numeric type or SymPy Symbol
|
||||
The phase difference between transmitted and reflected component for
|
||||
output mode a.
|
||||
phib : numeric type or SymPy Symbol
|
||||
The phase difference between transmitted and reflected component for
|
||||
output mode b.
|
||||
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
SymPy Matrix
|
||||
A 4x4 matrix representing the PBS. This matrix acts on a 4x1 vector
|
||||
whose first two entries are the Jones vector on one of the PBS ports,
|
||||
and the last two entries the Jones vector on the other port.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Generic polarizing beam-splitter.
|
||||
|
||||
>>> from sympy import pprint, symbols
|
||||
>>> from sympy.physics.optics.polarization import polarizing_beam_splitter
|
||||
>>> Ts, Rs, Tp, Rp = symbols(r"Ts, Rs, Tp, Rp", positive=True)
|
||||
>>> phia, phib = symbols("phi_a, phi_b", real=True)
|
||||
>>> PBS = polarizing_beam_splitter(Tp, Rs, Ts, Rp, phia, phib)
|
||||
>>> pprint(PBS, use_unicode=False)
|
||||
[ ____ ____ ]
|
||||
[ \/ Tp 0 I*\/ Rp 0 ]
|
||||
[ ]
|
||||
[ ____ ____ I*phi_a]
|
||||
[ 0 \/ Ts 0 -I*\/ Rs *e ]
|
||||
[ ]
|
||||
[ ____ ____ ]
|
||||
[I*\/ Rp 0 \/ Tp 0 ]
|
||||
[ ]
|
||||
[ ____ I*phi_b ____ ]
|
||||
[ 0 -I*\/ Rs *e 0 \/ Ts ]
|
||||
|
||||
"""
|
||||
PBS = Matrix([[sqrt(Tp), 0, I*sqrt(Rp), 0],
|
||||
[0, sqrt(Ts), 0, -I*sqrt(Rs)*exp(I*phia)],
|
||||
[I*sqrt(Rp), 0, sqrt(Tp), 0],
|
||||
[0, -I*sqrt(Rs)*exp(I*phib), 0, sqrt(Ts)]])
|
||||
return PBS
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
from sympy.core.evalf import N
|
||||
from sympy.core.numbers import (Float, I, oo, pi)
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import atan2
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.polys.polytools import factor
|
||||
|
||||
from sympy.physics.optics import (BeamParameter, CurvedMirror,
|
||||
CurvedRefraction, FlatMirror, FlatRefraction, FreeSpace, GeometricRay,
|
||||
RayTransferMatrix, ThinLens, conjugate_gauss_beams,
|
||||
gaussian_conj, geometric_conj_ab, geometric_conj_af, geometric_conj_bf,
|
||||
rayleigh2waist, waist2rayleigh)
|
||||
|
||||
|
||||
def streq(a, b):
|
||||
return str(a) == str(b)
|
||||
|
||||
|
||||
def test_gauss_opt():
|
||||
mat = RayTransferMatrix(1, 2, 3, 4)
|
||||
assert mat == Matrix([[1, 2], [3, 4]])
|
||||
assert mat == RayTransferMatrix( Matrix([[1, 2], [3, 4]]) )
|
||||
assert [mat.A, mat.B, mat.C, mat.D] == [1, 2, 3, 4]
|
||||
|
||||
d, f, h, n1, n2, R = symbols('d f h n1 n2 R')
|
||||
lens = ThinLens(f)
|
||||
assert lens == Matrix([[ 1, 0], [-1/f, 1]])
|
||||
assert lens.C == -1/f
|
||||
assert FreeSpace(d) == Matrix([[ 1, d], [0, 1]])
|
||||
assert FlatRefraction(n1, n2) == Matrix([[1, 0], [0, n1/n2]])
|
||||
assert CurvedRefraction(
|
||||
R, n1, n2) == Matrix([[1, 0], [(n1 - n2)/(R*n2), n1/n2]])
|
||||
assert FlatMirror() == Matrix([[1, 0], [0, 1]])
|
||||
assert CurvedMirror(R) == Matrix([[ 1, 0], [-2/R, 1]])
|
||||
assert ThinLens(f) == Matrix([[ 1, 0], [-1/f, 1]])
|
||||
|
||||
mul = CurvedMirror(R)*FreeSpace(d)
|
||||
mul_mat = Matrix([[ 1, 0], [-2/R, 1]])*Matrix([[ 1, d], [0, 1]])
|
||||
assert mul.A == mul_mat[0, 0]
|
||||
assert mul.B == mul_mat[0, 1]
|
||||
assert mul.C == mul_mat[1, 0]
|
||||
assert mul.D == mul_mat[1, 1]
|
||||
|
||||
angle = symbols('angle')
|
||||
assert GeometricRay(h, angle) == Matrix([[ h], [angle]])
|
||||
assert FreeSpace(
|
||||
d)*GeometricRay(h, angle) == Matrix([[angle*d + h], [angle]])
|
||||
assert GeometricRay( Matrix( ((h,), (angle,)) ) ) == Matrix([[h], [angle]])
|
||||
assert (FreeSpace(d)*GeometricRay(h, angle)).height == angle*d + h
|
||||
assert (FreeSpace(d)*GeometricRay(h, angle)).angle == angle
|
||||
|
||||
p = BeamParameter(530e-9, 1, w=1e-3)
|
||||
assert streq(p.q, 1 + 1.88679245283019*I*pi)
|
||||
assert streq(N(p.q), 1.0 + 5.92753330865999*I)
|
||||
assert streq(N(p.w_0), Float(0.00100000000000000))
|
||||
assert streq(N(p.z_r), Float(5.92753330865999))
|
||||
fs = FreeSpace(10)
|
||||
p1 = fs*p
|
||||
assert streq(N(p.w), Float(0.00101413072159615))
|
||||
assert streq(N(p1.w), Float(0.00210803120913829))
|
||||
|
||||
w, wavelen = symbols('w wavelen')
|
||||
assert waist2rayleigh(w, wavelen) == pi*w**2/wavelen
|
||||
z_r, wavelen = symbols('z_r wavelen')
|
||||
assert rayleigh2waist(z_r, wavelen) == sqrt(wavelen*z_r)/sqrt(pi)
|
||||
|
||||
a, b, f = symbols('a b f')
|
||||
assert geometric_conj_ab(a, b) == a*b/(a + b)
|
||||
assert geometric_conj_af(a, f) == a*f/(a - f)
|
||||
assert geometric_conj_bf(b, f) == b*f/(b - f)
|
||||
assert geometric_conj_ab(oo, b) == b
|
||||
assert geometric_conj_ab(a, oo) == a
|
||||
|
||||
s_in, z_r_in, f = symbols('s_in z_r_in f')
|
||||
assert gaussian_conj(
|
||||
s_in, z_r_in, f)[0] == 1/(-1/(s_in + z_r_in**2/(-f + s_in)) + 1/f)
|
||||
assert gaussian_conj(
|
||||
s_in, z_r_in, f)[1] == z_r_in/(1 - s_in**2/f**2 + z_r_in**2/f**2)
|
||||
assert gaussian_conj(
|
||||
s_in, z_r_in, f)[2] == 1/sqrt(1 - s_in**2/f**2 + z_r_in**2/f**2)
|
||||
|
||||
l, w_i, w_o, f = symbols('l w_i w_o f')
|
||||
assert conjugate_gauss_beams(l, w_i, w_o, f=f)[0] == f*(
|
||||
-sqrt(w_i**2/w_o**2 - pi**2*w_i**4/(f**2*l**2)) + 1)
|
||||
assert 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
|
||||
assert conjugate_gauss_beams(l, w_i, w_o, f=f)[2] == f
|
||||
|
||||
z, l, w_0 = symbols('z l w_0', positive=True)
|
||||
p = BeamParameter(l, z, w=w_0)
|
||||
assert p.radius == z*(pi**2*w_0**4/(l**2*z**2) + 1)
|
||||
assert p.w == w_0*sqrt(l**2*z**2/(pi**2*w_0**4) + 1)
|
||||
assert p.w_0 == w_0
|
||||
assert p.divergence == l/(pi*w_0)
|
||||
assert p.gouy == atan2(z, pi*w_0**2/l)
|
||||
assert p.waist_approximation_limit == 2*l/pi
|
||||
|
||||
p = BeamParameter(530e-9, 1, w=1e-3, n=2)
|
||||
assert streq(p.q, 1 + 3.77358490566038*I*pi)
|
||||
assert streq(N(p.z_r), Float(11.8550666173200))
|
||||
assert streq(N(p.w_0), Float(0.00100000000000000))
|
||||
@@ -0,0 +1,48 @@
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.physics.optics import Medium
|
||||
from sympy.abc import epsilon, mu, n
|
||||
from sympy.physics.units import speed_of_light, u0, e0, m, kg, s, A
|
||||
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
c = speed_of_light.convert_to(m/s)
|
||||
e0 = e0.convert_to(A**2*s**4/(kg*m**3))
|
||||
u0 = u0.convert_to(m*kg/(A**2*s**2))
|
||||
|
||||
|
||||
def test_medium():
|
||||
m1 = Medium('m1')
|
||||
assert m1.intrinsic_impedance == sqrt(u0/e0)
|
||||
assert m1.speed == 1/sqrt(e0*u0)
|
||||
assert m1.refractive_index == c*sqrt(e0*u0)
|
||||
assert m1.permittivity == e0
|
||||
assert m1.permeability == u0
|
||||
m2 = Medium('m2', epsilon, mu)
|
||||
assert m2.intrinsic_impedance == sqrt(mu/epsilon)
|
||||
assert m2.speed == 1/sqrt(epsilon*mu)
|
||||
assert m2.refractive_index == c*sqrt(epsilon*mu)
|
||||
assert m2.permittivity == epsilon
|
||||
assert m2.permeability == mu
|
||||
# Increasing electric permittivity and magnetic permeability
|
||||
# by small amount from its value in vacuum.
|
||||
m3 = Medium('m3', 9.0*10**(-12)*s**4*A**2/(m**3*kg), 1.45*10**(-6)*kg*m/(A**2*s**2))
|
||||
assert m3.refractive_index > m1.refractive_index
|
||||
assert m3 != m1
|
||||
# Decreasing electric permittivity and magnetic permeability
|
||||
# by small amount from its value in vacuum.
|
||||
m4 = Medium('m4', 7.0*10**(-12)*s**4*A**2/(m**3*kg), 1.15*10**(-6)*kg*m/(A**2*s**2))
|
||||
assert m4.refractive_index < m1.refractive_index
|
||||
m5 = Medium('m5', permittivity=710*10**(-12)*s**4*A**2/(m**3*kg), n=1.33)
|
||||
assert abs(m5.intrinsic_impedance - 6.24845417765552*kg*m**2/(A**2*s**3)) \
|
||||
< 1e-12*kg*m**2/(A**2*s**3)
|
||||
assert abs(m5.speed - 225407863.157895*m/s) < 1e-6*m/s
|
||||
assert abs(m5.refractive_index - 1.33000000000000) < 1e-12
|
||||
assert abs(m5.permittivity - 7.1e-10*A**2*s**4/(kg*m**3)) \
|
||||
< 1e-20*A**2*s**4/(kg*m**3)
|
||||
assert abs(m5.permeability - 2.77206575232851e-8*kg*m/(A**2*s**2)) \
|
||||
< 1e-20*kg*m/(A**2*s**2)
|
||||
m6 = Medium('m6', None, mu, n)
|
||||
assert m6.permittivity == n**2/(c**2*mu)
|
||||
# test for equality of refractive indices
|
||||
assert Medium('m7').refractive_index == Medium('m8', e0, u0).refractive_index
|
||||
raises(ValueError, lambda:Medium('m9', e0, u0, 2))
|
||||
@@ -0,0 +1,57 @@
|
||||
from sympy.physics.optics.polarization import (jones_vector, stokes_vector,
|
||||
jones_2_stokes, linear_polarizer, phase_retarder, half_wave_retarder,
|
||||
quarter_wave_retarder, transmissive_filter, reflective_filter,
|
||||
mueller_matrix, polarizing_beam_splitter)
|
||||
from sympy.core.numbers import (I, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.matrices.dense import Matrix
|
||||
|
||||
|
||||
def test_polarization():
|
||||
assert jones_vector(0, 0) == Matrix([1, 0])
|
||||
assert jones_vector(pi/2, 0) == Matrix([0, 1])
|
||||
#################################################################
|
||||
assert stokes_vector(0, 0) == Matrix([1, 1, 0, 0])
|
||||
assert stokes_vector(pi/2, 0) == Matrix([1, -1, 0, 0])
|
||||
#################################################################
|
||||
H = jones_vector(0, 0)
|
||||
V = jones_vector(pi/2, 0)
|
||||
D = jones_vector(pi/4, 0)
|
||||
A = jones_vector(-pi/4, 0)
|
||||
R = jones_vector(0, pi/4)
|
||||
L = jones_vector(0, -pi/4)
|
||||
|
||||
res = [Matrix([1, 1, 0, 0]),
|
||||
Matrix([1, -1, 0, 0]),
|
||||
Matrix([1, 0, 1, 0]),
|
||||
Matrix([1, 0, -1, 0]),
|
||||
Matrix([1, 0, 0, 1]),
|
||||
Matrix([1, 0, 0, -1])]
|
||||
|
||||
assert [jones_2_stokes(e) for e in [H, V, D, A, R, L]] == res
|
||||
#################################################################
|
||||
assert linear_polarizer(0) == Matrix([[1, 0], [0, 0]])
|
||||
#################################################################
|
||||
delta = symbols("delta", real=True)
|
||||
res = Matrix([[exp(-I*delta/2), 0], [0, exp(I*delta/2)]])
|
||||
assert phase_retarder(0, delta) == res
|
||||
#################################################################
|
||||
assert half_wave_retarder(0) == Matrix([[-I, 0], [0, I]])
|
||||
#################################################################
|
||||
res = Matrix([[exp(-I*pi/4), 0], [0, I*exp(-I*pi/4)]])
|
||||
assert quarter_wave_retarder(0) == res
|
||||
#################################################################
|
||||
assert transmissive_filter(1) == Matrix([[1, 0], [0, 1]])
|
||||
#################################################################
|
||||
assert reflective_filter(1) == Matrix([[1, 0], [0, -1]])
|
||||
|
||||
res = Matrix([[S(1)/2, S(1)/2, 0, 0],
|
||||
[S(1)/2, S(1)/2, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]])
|
||||
assert mueller_matrix(linear_polarizer(0)) == res
|
||||
#################################################################
|
||||
res = Matrix([[1, 0, 0, 0], [0, 0, 0, -I], [0, 0, 1, 0], [0, -I, 0, 0]])
|
||||
assert polarizing_beam_splitter() == res
|
||||
@@ -0,0 +1,202 @@
|
||||
from sympy.core.numbers import comp, Rational
|
||||
from sympy.physics.optics.utils import (refraction_angle, fresnel_coefficients,
|
||||
deviation, brewster_angle, critical_angle, lens_makers_formula,
|
||||
mirror_formula, lens_formula, hyperfocal_distance,
|
||||
transverse_magnification)
|
||||
from sympy.physics.optics.medium import Medium
|
||||
from sympy.physics.units import e0
|
||||
|
||||
from sympy.core.numbers import oo
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.geometry.point import Point3D
|
||||
from sympy.geometry.line import Ray3D
|
||||
from sympy.geometry.plane import Plane
|
||||
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
ae = lambda a, b, n: comp(a, b, 10**-n)
|
||||
|
||||
|
||||
def test_refraction_angle():
|
||||
n1, n2 = symbols('n1, n2')
|
||||
m1 = Medium('m1')
|
||||
m2 = Medium('m2')
|
||||
r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
||||
i = Matrix([1, 1, 1])
|
||||
n = Matrix([0, 0, 1])
|
||||
normal_ray = Ray3D(Point3D(0, 0, 0), Point3D(0, 0, 1))
|
||||
P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
||||
assert refraction_angle(r1, 1, 1, n) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle([1, 1, 1], 1, 1, n) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle((1, 1, 1), 1, 1, n) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle(i, 1, 1, [0, 0, 1]) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle(i, 1, 1, (0, 0, 1)) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle(i, 1, 1, normal_ray) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle(i, 1, 1, plane=P) == Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
assert refraction_angle(r1, 1, 1, plane=P) == \
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1))
|
||||
assert refraction_angle(r1, m1, 1.33, plane=P) == \
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(Rational(100, 133), Rational(100, 133), -789378201649271*sqrt(3)/1000000000000000))
|
||||
assert refraction_angle(r1, 1, m2, plane=P) == \
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1))
|
||||
assert refraction_angle(r1, n1, n2, plane=P) == \
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)))
|
||||
assert refraction_angle(r1, 1.33, 1, plane=P) == 0 # TIR
|
||||
assert refraction_angle(r1, 1, 1, normal_ray) == \
|
||||
Ray3D(Point3D(0, 0, 0), direction_ratio=[1, 1, -1])
|
||||
assert ae(refraction_angle(0.5, 1, 2), 0.24207, 5)
|
||||
assert ae(refraction_angle(0.5, 2, 1), 1.28293, 5)
|
||||
raises(ValueError, lambda: refraction_angle(r1, m1, m2, normal_ray, P))
|
||||
raises(TypeError, lambda: refraction_angle(m1, m1, m2)) # can add other values for arg[0]
|
||||
raises(TypeError, lambda: refraction_angle(r1, m1, m2, None, i))
|
||||
raises(TypeError, lambda: refraction_angle(r1, m1, m2, m2))
|
||||
|
||||
|
||||
def test_fresnel_coefficients():
|
||||
assert all(ae(i, j, 5) for i, j in zip(
|
||||
fresnel_coefficients(0.5, 1, 1.33),
|
||||
[0.11163, -0.17138, 0.83581, 0.82862]))
|
||||
assert all(ae(i, j, 5) for i, j in zip(
|
||||
fresnel_coefficients(0.5, 1.33, 1),
|
||||
[-0.07726, 0.20482, 1.22724, 1.20482]))
|
||||
m1 = Medium('m1')
|
||||
m2 = Medium('m2', n=2)
|
||||
assert all(ae(i, j, 5) for i, j in zip(
|
||||
fresnel_coefficients(0.3, m1, m2),
|
||||
[0.31784, -0.34865, 0.65892, 0.65135]))
|
||||
ans = [[-0.23563, -0.97184], [0.81648, -0.57738]]
|
||||
got = fresnel_coefficients(0.6, m2, m1)
|
||||
for i, j in zip(got, ans):
|
||||
for a, b in zip(i.as_real_imag(), j):
|
||||
assert ae(a, b, 5)
|
||||
|
||||
|
||||
def test_deviation():
|
||||
n1, n2 = symbols('n1, n2')
|
||||
r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
||||
n = Matrix([0, 0, 1])
|
||||
i = Matrix([-1, -1, -1])
|
||||
normal_ray = Ray3D(Point3D(0, 0, 0), Point3D(0, 0, 1))
|
||||
P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
||||
assert deviation(r1, 1, 1, normal=n) == 0
|
||||
assert deviation(r1, 1, 1, plane=P) == 0
|
||||
assert deviation(r1, 1, 1.1, plane=P).evalf(3) + 0.119 < 1e-3
|
||||
assert deviation(i, 1, 1.1, normal=normal_ray).evalf(3) + 0.119 < 1e-3
|
||||
assert deviation(r1, 1.33, 1, plane=P) is None # TIR
|
||||
assert deviation(r1, 1, 1, normal=[0, 0, 1]) == 0
|
||||
assert deviation([-1, -1, -1], 1, 1, normal=[0, 0, 1]) == 0
|
||||
assert ae(deviation(0.5, 1, 2), -0.25793, 5)
|
||||
assert ae(deviation(0.5, 2, 1), 0.78293, 5)
|
||||
|
||||
|
||||
def test_brewster_angle():
|
||||
m1 = Medium('m1', n=1)
|
||||
m2 = Medium('m2', n=1.33)
|
||||
assert ae(brewster_angle(m1, m2), 0.93, 2)
|
||||
m1 = Medium('m1', permittivity=e0, n=1)
|
||||
m2 = Medium('m2', permittivity=e0, n=1.33)
|
||||
assert ae(brewster_angle(m1, m2), 0.93, 2)
|
||||
assert ae(brewster_angle(1, 1.33), 0.93, 2)
|
||||
|
||||
|
||||
def test_critical_angle():
|
||||
m1 = Medium('m1', n=1)
|
||||
m2 = Medium('m2', n=1.33)
|
||||
assert ae(critical_angle(m2, m1), 0.85, 2)
|
||||
|
||||
|
||||
def test_lens_makers_formula():
|
||||
n1, n2 = symbols('n1, n2')
|
||||
m1 = Medium('m1', permittivity=e0, n=1)
|
||||
m2 = Medium('m2', permittivity=e0, n=1.33)
|
||||
assert lens_makers_formula(n1, n2, 10, -10) == 5.0*n2/(n1 - n2)
|
||||
assert ae(lens_makers_formula(m1, m2, 10, -10), -20.15, 2)
|
||||
assert ae(lens_makers_formula(1.33, 1, 10, -10), 15.15, 2)
|
||||
|
||||
|
||||
def test_mirror_formula():
|
||||
u, v, f = symbols('u, v, f')
|
||||
assert mirror_formula(focal_length=f, u=u) == f*u/(-f + u)
|
||||
assert mirror_formula(focal_length=f, v=v) == f*v/(-f + v)
|
||||
assert mirror_formula(u=u, v=v) == u*v/(u + v)
|
||||
assert mirror_formula(u=oo, v=v) == v
|
||||
assert mirror_formula(u=oo, v=oo) is oo
|
||||
assert mirror_formula(focal_length=oo, u=u) == -u
|
||||
assert mirror_formula(u=u, v=oo) == u
|
||||
assert mirror_formula(focal_length=oo, v=oo) is oo
|
||||
assert mirror_formula(focal_length=f, v=oo) == f
|
||||
assert mirror_formula(focal_length=oo, v=v) == -v
|
||||
assert mirror_formula(focal_length=oo, u=oo) is oo
|
||||
assert mirror_formula(focal_length=f, u=oo) == f
|
||||
assert mirror_formula(focal_length=oo, u=u) == -u
|
||||
raises(ValueError, lambda: mirror_formula(focal_length=f, u=u, v=v))
|
||||
|
||||
|
||||
def test_lens_formula():
|
||||
u, v, f = symbols('u, v, f')
|
||||
assert lens_formula(focal_length=f, u=u) == f*u/(f + u)
|
||||
assert lens_formula(focal_length=f, v=v) == f*v/(f - v)
|
||||
assert lens_formula(u=u, v=v) == u*v/(u - v)
|
||||
assert lens_formula(u=oo, v=v) == v
|
||||
assert lens_formula(u=oo, v=oo) is oo
|
||||
assert lens_formula(focal_length=oo, u=u) == u
|
||||
assert lens_formula(u=u, v=oo) == -u
|
||||
assert lens_formula(focal_length=oo, v=oo) is -oo
|
||||
assert lens_formula(focal_length=oo, v=v) == v
|
||||
assert lens_formula(focal_length=f, v=oo) == -f
|
||||
assert lens_formula(focal_length=oo, u=oo) is oo
|
||||
assert lens_formula(focal_length=oo, u=u) == u
|
||||
assert lens_formula(focal_length=f, u=oo) == f
|
||||
raises(ValueError, lambda: lens_formula(focal_length=f, u=u, v=v))
|
||||
|
||||
|
||||
def test_hyperfocal_distance():
|
||||
f, N, c = symbols('f, N, c')
|
||||
assert hyperfocal_distance(f=f, N=N, c=c) == f**2/(N*c)
|
||||
assert ae(hyperfocal_distance(f=0.5, N=8, c=0.0033), 9.47, 2)
|
||||
|
||||
|
||||
def test_transverse_magnification():
|
||||
si, so = symbols('si, so')
|
||||
assert transverse_magnification(si, so) == -si/so
|
||||
assert transverse_magnification(30, 15) == -2
|
||||
|
||||
|
||||
def test_lens_makers_formula_thick_lens():
|
||||
n1, n2 = symbols('n1, n2')
|
||||
m1 = Medium('m1', permittivity=e0, n=1)
|
||||
m2 = Medium('m2', permittivity=e0, n=1.33)
|
||||
assert ae(lens_makers_formula(m1, m2, 10, -10, d=1), -19.82, 2)
|
||||
assert lens_makers_formula(n1, n2, 1, -1, d=0.1) == n2/((2.0 - (0.1*n1 - 0.1*n2)/n1)*(n1 - n2))
|
||||
|
||||
|
||||
def test_lens_makers_formula_plano_lens():
|
||||
n1, n2 = symbols('n1, n2')
|
||||
m1 = Medium('m1', permittivity=e0, n=1)
|
||||
m2 = Medium('m2', permittivity=e0, n=1.33)
|
||||
assert ae(lens_makers_formula(m1, m2, 10, oo), -40.30, 2)
|
||||
assert lens_makers_formula(n1, n2, 10, oo) == 10.0*n2/(n1 - n2)
|
||||
@@ -0,0 +1,82 @@
|
||||
from sympy.core.function import (Derivative, Function)
|
||||
from sympy.core.numbers import (I, pi)
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (atan2, cos, sin)
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.abc import epsilon, mu
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.physics.units import speed_of_light, m, s
|
||||
from sympy.physics.optics import TWave
|
||||
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
c = speed_of_light.convert_to(m/s)
|
||||
|
||||
def test_twave():
|
||||
A1, phi1, A2, phi2, f = symbols('A1, phi1, A2, phi2, f')
|
||||
n = Symbol('n') # Refractive index
|
||||
t = Symbol('t') # Time
|
||||
x = Symbol('x') # Spatial variable
|
||||
E = Function('E')
|
||||
w1 = TWave(A1, f, phi1)
|
||||
w2 = TWave(A2, f, phi2)
|
||||
assert w1.amplitude == A1
|
||||
assert w1.frequency == f
|
||||
assert w1.phase == phi1
|
||||
assert w1.wavelength == c/(f*n)
|
||||
assert w1.time_period == 1/f
|
||||
assert w1.angular_velocity == 2*pi*f
|
||||
assert w1.wavenumber == 2*pi*f*n/c
|
||||
assert w1.speed == c/n
|
||||
|
||||
w3 = w1 + w2
|
||||
assert w3.amplitude == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
|
||||
assert w3.frequency == f
|
||||
assert w3.phase == atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2))
|
||||
assert w3.wavelength == c/(f*n)
|
||||
assert w3.time_period == 1/f
|
||||
assert w3.angular_velocity == 2*pi*f
|
||||
assert w3.wavenumber == 2*pi*f*n/c
|
||||
assert w3.speed == c/n
|
||||
assert simplify(w3.rewrite(sin) - w2.rewrite(sin) - w1.rewrite(sin)) == 0
|
||||
assert w3.rewrite('pde') == epsilon*mu*Derivative(E(x, t), t, t) + Derivative(E(x, t), x, x)
|
||||
assert w3.rewrite(cos) == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2)
|
||||
+ A2**2)*cos(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*sin(phi1)
|
||||
+ A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2)))
|
||||
assert w3.rewrite(exp) == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2)
|
||||
+ A2**2)*exp(I*(-2*pi*f*t + atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1)
|
||||
+ A2*cos(phi2)) + pi*s*f*n*x/(149896229*m)))
|
||||
|
||||
w4 = TWave(A1, None, 0, 1/f)
|
||||
assert w4.frequency == f
|
||||
|
||||
w5 = w1 - w2
|
||||
assert w5.amplitude == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2) + A2**2)
|
||||
assert w5.frequency == f
|
||||
assert w5.phase == atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1) - A2*cos(phi2))
|
||||
assert w5.wavelength == c/(f*n)
|
||||
assert w5.time_period == 1/f
|
||||
assert w5.angular_velocity == 2*pi*f
|
||||
assert w5.wavenumber == 2*pi*f*n/c
|
||||
assert w5.speed == c/n
|
||||
assert simplify(w5.rewrite(sin) - w1.rewrite(sin) + w2.rewrite(sin)) == 0
|
||||
assert w5.rewrite('pde') == epsilon*mu*Derivative(E(x, t), t, t) + Derivative(E(x, t), x, x)
|
||||
assert w5.rewrite(cos) == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2)
|
||||
+ A2**2)*cos(-2*pi*f*t + atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1)
|
||||
- A2*cos(phi2)) + pi*s*f*n*x/(149896229*m))
|
||||
assert w5.rewrite(exp) == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2)
|
||||
+ A2**2)*exp(I*(-2*pi*f*t + atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1)
|
||||
- A2*cos(phi2)) + pi*s*f*n*x/(149896229*m)))
|
||||
|
||||
w6 = 2*w1
|
||||
assert w6.amplitude == 2*A1
|
||||
assert w6.frequency == f
|
||||
assert w6.phase == phi1
|
||||
w7 = -w6
|
||||
assert w7.amplitude == -2*A1
|
||||
assert w7.frequency == f
|
||||
assert w7.phase == phi1
|
||||
|
||||
raises(ValueError, lambda:TWave(A1))
|
||||
raises(ValueError, lambda:TWave(A1, f, phi1, t))
|
||||
698
venv/lib/python3.12/site-packages/sympy/physics/optics/utils.py
Normal file
698
venv/lib/python3.12/site-packages/sympy/physics/optics/utils.py
Normal file
@@ -0,0 +1,698 @@
|
||||
"""
|
||||
**Contains**
|
||||
|
||||
* refraction_angle
|
||||
* fresnel_coefficients
|
||||
* deviation
|
||||
* brewster_angle
|
||||
* critical_angle
|
||||
* lens_makers_formula
|
||||
* mirror_formula
|
||||
* lens_formula
|
||||
* hyperfocal_distance
|
||||
* transverse_magnification
|
||||
"""
|
||||
|
||||
__all__ = ['refraction_angle',
|
||||
'deviation',
|
||||
'fresnel_coefficients',
|
||||
'brewster_angle',
|
||||
'critical_angle',
|
||||
'lens_makers_formula',
|
||||
'mirror_formula',
|
||||
'lens_formula',
|
||||
'hyperfocal_distance',
|
||||
'transverse_magnification'
|
||||
]
|
||||
|
||||
from sympy.core.numbers import (Float, I, oo, pi, zoo)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (acos, asin, atan2, cos, sin, tan)
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.polys.polytools import cancel
|
||||
from sympy.series.limits import Limit
|
||||
from sympy.geometry.line import Ray3D
|
||||
from sympy.geometry.util import intersection
|
||||
from sympy.geometry.plane import Plane
|
||||
from sympy.utilities.iterables import is_sequence
|
||||
from .medium import Medium
|
||||
|
||||
|
||||
def refractive_index_of_medium(medium):
|
||||
"""
|
||||
Helper function that returns refractive index, given a medium
|
||||
"""
|
||||
if isinstance(medium, Medium):
|
||||
n = medium.refractive_index
|
||||
else:
|
||||
n = sympify(medium)
|
||||
return n
|
||||
|
||||
|
||||
def refraction_angle(incident, medium1, medium2, normal=None, plane=None):
|
||||
"""
|
||||
This function calculates transmitted vector after refraction at planar
|
||||
surface. ``medium1`` and ``medium2`` can be ``Medium`` or any sympifiable object.
|
||||
If ``incident`` is a number then treated as angle of incidence (in radians)
|
||||
in which case refraction angle is returned.
|
||||
|
||||
If ``incident`` is an object of `Ray3D`, `normal` also has to be an instance
|
||||
of `Ray3D` in order to get the output as a `Ray3D`. Please note that if
|
||||
plane of separation is not provided and normal is an instance of `Ray3D`,
|
||||
``normal`` will be assumed to be intersecting incident ray at the plane of
|
||||
separation. This will not be the case when `normal` is a `Matrix` or
|
||||
any other sequence.
|
||||
If ``incident`` is an instance of `Ray3D` and `plane` has not been provided
|
||||
and ``normal`` is not `Ray3D`, output will be a `Matrix`.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
incident : Matrix, Ray3D, sequence or a number
|
||||
Incident vector or angle of incidence
|
||||
medium1 : sympy.physics.optics.medium.Medium or sympifiable
|
||||
Medium 1 or its refractive index
|
||||
medium2 : sympy.physics.optics.medium.Medium or sympifiable
|
||||
Medium 2 or its refractive index
|
||||
normal : Matrix, Ray3D, or sequence
|
||||
Normal vector
|
||||
plane : Plane
|
||||
Plane of separation of the two media.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Returns an angle of refraction or a refracted ray depending on inputs.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import refraction_angle
|
||||
>>> from sympy.geometry import Point3D, Ray3D, Plane
|
||||
>>> from sympy.matrices import Matrix
|
||||
>>> from sympy import symbols, pi
|
||||
>>> n = Matrix([0, 0, 1])
|
||||
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
||||
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
||||
>>> refraction_angle(r1, 1, 1, n)
|
||||
Matrix([
|
||||
[ 1],
|
||||
[ 1],
|
||||
[-1]])
|
||||
>>> refraction_angle(r1, 1, 1, plane=P)
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1))
|
||||
|
||||
With different index of refraction of the two media
|
||||
|
||||
>>> n1, n2 = symbols('n1, n2')
|
||||
>>> refraction_angle(r1, n1, n2, n)
|
||||
Matrix([
|
||||
[ n1/n2],
|
||||
[ n1/n2],
|
||||
[-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]])
|
||||
>>> refraction_angle(r1, n1, n2, plane=P)
|
||||
Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)))
|
||||
>>> round(refraction_angle(pi/6, 1.2, 1.5), 5)
|
||||
0.41152
|
||||
"""
|
||||
|
||||
n1 = refractive_index_of_medium(medium1)
|
||||
n2 = refractive_index_of_medium(medium2)
|
||||
|
||||
# check if an incidence angle was supplied instead of a ray
|
||||
try:
|
||||
angle_of_incidence = float(incident)
|
||||
except TypeError:
|
||||
angle_of_incidence = None
|
||||
|
||||
try:
|
||||
critical_angle_ = critical_angle(medium1, medium2)
|
||||
except (ValueError, TypeError):
|
||||
critical_angle_ = None
|
||||
|
||||
if angle_of_incidence is not None:
|
||||
if normal is not None or plane is not None:
|
||||
raise ValueError('Normal/plane not allowed if incident is an angle')
|
||||
|
||||
if not 0.0 <= angle_of_incidence < pi*0.5:
|
||||
raise ValueError('Angle of incidence not in range [0:pi/2)')
|
||||
|
||||
if critical_angle_ and angle_of_incidence > critical_angle_:
|
||||
raise ValueError('Ray undergoes total internal reflection')
|
||||
return asin(n1*sin(angle_of_incidence)/n2)
|
||||
|
||||
# Treat the incident as ray below
|
||||
# A flag to check whether to return Ray3D or not
|
||||
return_ray = False
|
||||
|
||||
if plane is not None and normal is not None:
|
||||
raise ValueError("Either plane or normal is acceptable.")
|
||||
|
||||
if not isinstance(incident, Matrix):
|
||||
if is_sequence(incident):
|
||||
_incident = Matrix(incident)
|
||||
elif isinstance(incident, Ray3D):
|
||||
_incident = Matrix(incident.direction_ratio)
|
||||
else:
|
||||
raise TypeError(
|
||||
"incident should be a Matrix, Ray3D, or sequence")
|
||||
else:
|
||||
_incident = incident
|
||||
|
||||
# If plane is provided, get direction ratios of the normal
|
||||
# to the plane from the plane else go with `normal` param.
|
||||
if plane is not None:
|
||||
if not isinstance(plane, Plane):
|
||||
raise TypeError("plane should be an instance of geometry.plane.Plane")
|
||||
# If we have the plane, we can get the intersection
|
||||
# point of incident ray and the plane and thus return
|
||||
# an instance of Ray3D.
|
||||
if isinstance(incident, Ray3D):
|
||||
return_ray = True
|
||||
intersection_pt = plane.intersection(incident)[0]
|
||||
_normal = Matrix(plane.normal_vector)
|
||||
else:
|
||||
if not isinstance(normal, Matrix):
|
||||
if is_sequence(normal):
|
||||
_normal = Matrix(normal)
|
||||
elif isinstance(normal, Ray3D):
|
||||
_normal = Matrix(normal.direction_ratio)
|
||||
if isinstance(incident, Ray3D):
|
||||
intersection_pt = intersection(incident, normal)
|
||||
if len(intersection_pt) == 0:
|
||||
raise ValueError(
|
||||
"Normal isn't concurrent with the incident ray.")
|
||||
else:
|
||||
return_ray = True
|
||||
intersection_pt = intersection_pt[0]
|
||||
else:
|
||||
raise TypeError(
|
||||
"Normal should be a Matrix, Ray3D, or sequence")
|
||||
else:
|
||||
_normal = normal
|
||||
|
||||
eta = n1/n2 # Relative index of refraction
|
||||
# Calculating magnitude of the vectors
|
||||
mag_incident = sqrt(sum(i**2 for i in _incident))
|
||||
mag_normal = sqrt(sum(i**2 for i in _normal))
|
||||
# Converting vectors to unit vectors by dividing
|
||||
# them with their magnitudes
|
||||
_incident /= mag_incident
|
||||
_normal /= mag_normal
|
||||
c1 = -_incident.dot(_normal) # cos(angle_of_incidence)
|
||||
cs2 = 1 - eta**2*(1 - c1**2) # cos(angle_of_refraction)**2
|
||||
if cs2.is_negative: # This is the case of total internal reflection(TIR).
|
||||
return S.Zero
|
||||
drs = eta*_incident + (eta*c1 - sqrt(cs2))*_normal
|
||||
# Multiplying unit vector by its magnitude
|
||||
drs = drs*mag_incident
|
||||
if not return_ray:
|
||||
return drs
|
||||
else:
|
||||
return Ray3D(intersection_pt, direction_ratio=drs)
|
||||
|
||||
|
||||
def fresnel_coefficients(angle_of_incidence, medium1, medium2):
|
||||
"""
|
||||
This function uses Fresnel equations to calculate reflection and
|
||||
transmission coefficients. Those are obtained for both polarisations
|
||||
when the electric field vector is in the plane of incidence (labelled 'p')
|
||||
and when the electric field vector is perpendicular to the plane of
|
||||
incidence (labelled 's'). There are four real coefficients unless the
|
||||
incident ray reflects in total internal in which case there are two complex
|
||||
ones. Angle of incidence is the angle between the incident ray and the
|
||||
surface normal. ``medium1`` and ``medium2`` can be ``Medium`` or any
|
||||
sympifiable object.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
angle_of_incidence : sympifiable
|
||||
|
||||
medium1 : Medium or sympifiable
|
||||
Medium 1 or its refractive index
|
||||
|
||||
medium2 : Medium or sympifiable
|
||||
Medium 2 or its refractive index
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Returns a list with four real Fresnel coefficients:
|
||||
[reflection p (TM), reflection s (TE),
|
||||
transmission p (TM), transmission s (TE)]
|
||||
If the ray is undergoes total internal reflection then returns a
|
||||
list of two complex Fresnel coefficients:
|
||||
[reflection p (TM), reflection s (TE)]
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import fresnel_coefficients
|
||||
>>> fresnel_coefficients(0.3, 1, 2)
|
||||
[0.317843553417859, -0.348645229818821,
|
||||
0.658921776708929, 0.651354770181179]
|
||||
>>> fresnel_coefficients(0.6, 2, 1)
|
||||
[-0.235625382192159 - 0.971843958291041*I,
|
||||
0.816477005968898 - 0.577377951366403*I]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Fresnel_equations
|
||||
"""
|
||||
if not 0 <= 2*angle_of_incidence < pi:
|
||||
raise ValueError('Angle of incidence not in range [0:pi/2)')
|
||||
|
||||
n1 = refractive_index_of_medium(medium1)
|
||||
n2 = refractive_index_of_medium(medium2)
|
||||
|
||||
angle_of_refraction = asin(n1*sin(angle_of_incidence)/n2)
|
||||
try:
|
||||
angle_of_total_internal_reflection_onset = critical_angle(n1, n2)
|
||||
except ValueError:
|
||||
angle_of_total_internal_reflection_onset = None
|
||||
|
||||
if angle_of_total_internal_reflection_onset is None or\
|
||||
angle_of_total_internal_reflection_onset > angle_of_incidence:
|
||||
R_s = -sin(angle_of_incidence - angle_of_refraction)\
|
||||
/sin(angle_of_incidence + angle_of_refraction)
|
||||
R_p = tan(angle_of_incidence - angle_of_refraction)\
|
||||
/tan(angle_of_incidence + angle_of_refraction)
|
||||
T_s = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
|
||||
/sin(angle_of_incidence + angle_of_refraction)
|
||||
T_p = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
|
||||
/(sin(angle_of_incidence + angle_of_refraction)\
|
||||
*cos(angle_of_incidence - angle_of_refraction))
|
||||
return [R_p, R_s, T_p, T_s]
|
||||
else:
|
||||
n = n2/n1
|
||||
R_s = cancel((cos(angle_of_incidence)-\
|
||||
I*sqrt(sin(angle_of_incidence)**2 - n**2))\
|
||||
/(cos(angle_of_incidence)+\
|
||||
I*sqrt(sin(angle_of_incidence)**2 - n**2)))
|
||||
R_p = cancel((n**2*cos(angle_of_incidence)-\
|
||||
I*sqrt(sin(angle_of_incidence)**2 - n**2))\
|
||||
/(n**2*cos(angle_of_incidence)+\
|
||||
I*sqrt(sin(angle_of_incidence)**2 - n**2)))
|
||||
return [R_p, R_s]
|
||||
|
||||
|
||||
def deviation(incident, medium1, medium2, normal=None, plane=None):
|
||||
"""
|
||||
This function calculates the angle of deviation of a ray
|
||||
due to refraction at planar surface.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
incident : Matrix, Ray3D, sequence or float
|
||||
Incident vector or angle of incidence
|
||||
medium1 : sympy.physics.optics.medium.Medium or sympifiable
|
||||
Medium 1 or its refractive index
|
||||
medium2 : sympy.physics.optics.medium.Medium or sympifiable
|
||||
Medium 2 or its refractive index
|
||||
normal : Matrix, Ray3D, or sequence
|
||||
Normal vector
|
||||
plane : Plane
|
||||
Plane of separation of the two media.
|
||||
|
||||
Returns angular deviation between incident and refracted rays
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import deviation
|
||||
>>> from sympy.geometry import Point3D, Ray3D, Plane
|
||||
>>> from sympy.matrices import Matrix
|
||||
>>> from sympy import symbols
|
||||
>>> n1, n2 = symbols('n1, n2')
|
||||
>>> n = Matrix([0, 0, 1])
|
||||
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
||||
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
||||
>>> deviation(r1, 1, 1, n)
|
||||
0
|
||||
>>> deviation(r1, n1, n2, plane=P)
|
||||
-acos(-sqrt(-2*n1**2/(3*n2**2) + 1)) + acos(-sqrt(3)/3)
|
||||
>>> round(deviation(0.1, 1.2, 1.5), 5)
|
||||
-0.02005
|
||||
"""
|
||||
refracted = refraction_angle(incident,
|
||||
medium1,
|
||||
medium2,
|
||||
normal=normal,
|
||||
plane=plane)
|
||||
try:
|
||||
angle_of_incidence = Float(incident)
|
||||
except TypeError:
|
||||
angle_of_incidence = None
|
||||
|
||||
if angle_of_incidence is not None:
|
||||
return float(refracted) - angle_of_incidence
|
||||
|
||||
if refracted != 0:
|
||||
if isinstance(refracted, Ray3D):
|
||||
refracted = Matrix(refracted.direction_ratio)
|
||||
|
||||
if not isinstance(incident, Matrix):
|
||||
if is_sequence(incident):
|
||||
_incident = Matrix(incident)
|
||||
elif isinstance(incident, Ray3D):
|
||||
_incident = Matrix(incident.direction_ratio)
|
||||
else:
|
||||
raise TypeError(
|
||||
"incident should be a Matrix, Ray3D, or sequence")
|
||||
else:
|
||||
_incident = incident
|
||||
|
||||
if plane is None:
|
||||
if not isinstance(normal, Matrix):
|
||||
if is_sequence(normal):
|
||||
_normal = Matrix(normal)
|
||||
elif isinstance(normal, Ray3D):
|
||||
_normal = Matrix(normal.direction_ratio)
|
||||
else:
|
||||
raise TypeError(
|
||||
"normal should be a Matrix, Ray3D, or sequence")
|
||||
else:
|
||||
_normal = normal
|
||||
else:
|
||||
_normal = Matrix(plane.normal_vector)
|
||||
|
||||
mag_incident = sqrt(sum(i**2 for i in _incident))
|
||||
mag_normal = sqrt(sum(i**2 for i in _normal))
|
||||
mag_refracted = sqrt(sum(i**2 for i in refracted))
|
||||
_incident /= mag_incident
|
||||
_normal /= mag_normal
|
||||
refracted /= mag_refracted
|
||||
i = acos(_incident.dot(_normal))
|
||||
r = acos(refracted.dot(_normal))
|
||||
return i - r
|
||||
|
||||
|
||||
def brewster_angle(medium1, medium2):
|
||||
"""
|
||||
This function calculates the Brewster's angle of incidence to Medium 2 from
|
||||
Medium 1 in radians.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
medium 1 : Medium or sympifiable
|
||||
Refractive index of Medium 1
|
||||
medium 2 : Medium or sympifiable
|
||||
Refractive index of Medium 1
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import brewster_angle
|
||||
>>> brewster_angle(1, 1.33)
|
||||
0.926093295503462
|
||||
|
||||
"""
|
||||
|
||||
n1 = refractive_index_of_medium(medium1)
|
||||
n2 = refractive_index_of_medium(medium2)
|
||||
|
||||
return atan2(n2, n1)
|
||||
|
||||
def critical_angle(medium1, medium2):
|
||||
"""
|
||||
This function calculates the critical angle of incidence (marking the onset
|
||||
of total internal) to Medium 2 from Medium 1 in radians.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
medium 1 : Medium or sympifiable
|
||||
Refractive index of Medium 1.
|
||||
medium 2 : Medium or sympifiable
|
||||
Refractive index of Medium 1.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import critical_angle
|
||||
>>> critical_angle(1.33, 1)
|
||||
0.850908514477849
|
||||
|
||||
"""
|
||||
|
||||
n1 = refractive_index_of_medium(medium1)
|
||||
n2 = refractive_index_of_medium(medium2)
|
||||
|
||||
if n2 > n1:
|
||||
raise ValueError('Total internal reflection impossible for n1 < n2')
|
||||
else:
|
||||
return asin(n2/n1)
|
||||
|
||||
|
||||
|
||||
def lens_makers_formula(n_lens, n_surr, r1, r2, d=0):
|
||||
"""
|
||||
This function calculates focal length of a lens.
|
||||
It follows cartesian sign convention.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n_lens : Medium or sympifiable
|
||||
Index of refraction of lens.
|
||||
n_surr : Medium or sympifiable
|
||||
Index of reflection of surrounding.
|
||||
r1 : sympifiable
|
||||
Radius of curvature of first surface.
|
||||
r2 : sympifiable
|
||||
Radius of curvature of second surface.
|
||||
d : sympifiable, optional
|
||||
Thickness of lens, default value is 0.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import lens_makers_formula
|
||||
>>> from sympy import S
|
||||
>>> lens_makers_formula(1.33, 1, 10, -10)
|
||||
15.1515151515151
|
||||
>>> lens_makers_formula(1.2, 1, 10, S.Infinity)
|
||||
50.0000000000000
|
||||
>>> lens_makers_formula(1.33, 1, 10, -10, d=1)
|
||||
15.3418463277618
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(n_lens, Medium):
|
||||
n_lens = n_lens.refractive_index
|
||||
else:
|
||||
n_lens = sympify(n_lens)
|
||||
if isinstance(n_surr, Medium):
|
||||
n_surr = n_surr.refractive_index
|
||||
else:
|
||||
n_surr = sympify(n_surr)
|
||||
d = sympify(d)
|
||||
|
||||
focal_length = 1/((n_lens - n_surr) / n_surr*(1/r1 - 1/r2 + (((n_lens - n_surr) * d) / (n_lens * r1 * r2))))
|
||||
|
||||
if focal_length == zoo:
|
||||
return S.Infinity
|
||||
return focal_length
|
||||
|
||||
|
||||
def mirror_formula(focal_length=None, u=None, v=None):
|
||||
"""
|
||||
This function provides one of the three parameters
|
||||
when two of them are supplied.
|
||||
This is valid only for paraxial rays.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
focal_length : sympifiable
|
||||
Focal length of the mirror.
|
||||
u : sympifiable
|
||||
Distance of object from the pole on
|
||||
the principal axis.
|
||||
v : sympifiable
|
||||
Distance of the image from the pole
|
||||
on the principal axis.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import mirror_formula
|
||||
>>> from sympy.abc import f, u, v
|
||||
>>> mirror_formula(focal_length=f, u=u)
|
||||
f*u/(-f + u)
|
||||
>>> mirror_formula(focal_length=f, v=v)
|
||||
f*v/(-f + v)
|
||||
>>> mirror_formula(u=u, v=v)
|
||||
u*v/(u + v)
|
||||
|
||||
"""
|
||||
if focal_length and u and v:
|
||||
raise ValueError("Please provide only two parameters")
|
||||
|
||||
focal_length = sympify(focal_length)
|
||||
u = sympify(u)
|
||||
v = sympify(v)
|
||||
if u is oo:
|
||||
_u = Symbol('u')
|
||||
if v is oo:
|
||||
_v = Symbol('v')
|
||||
if focal_length is oo:
|
||||
_f = Symbol('f')
|
||||
if focal_length is None:
|
||||
if u is oo and v is oo:
|
||||
return Limit(Limit(_v*_u/(_v + _u), _u, oo), _v, oo).doit()
|
||||
if u is oo:
|
||||
return Limit(v*_u/(v + _u), _u, oo).doit()
|
||||
if v is oo:
|
||||
return Limit(_v*u/(_v + u), _v, oo).doit()
|
||||
return v*u/(v + u)
|
||||
if u is None:
|
||||
if v is oo and focal_length is oo:
|
||||
return Limit(Limit(_v*_f/(_v - _f), _v, oo), _f, oo).doit()
|
||||
if v is oo:
|
||||
return Limit(_v*focal_length/(_v - focal_length), _v, oo).doit()
|
||||
if focal_length is oo:
|
||||
return Limit(v*_f/(v - _f), _f, oo).doit()
|
||||
return v*focal_length/(v - focal_length)
|
||||
if v is None:
|
||||
if u is oo and focal_length is oo:
|
||||
return Limit(Limit(_u*_f/(_u - _f), _u, oo), _f, oo).doit()
|
||||
if u is oo:
|
||||
return Limit(_u*focal_length/(_u - focal_length), _u, oo).doit()
|
||||
if focal_length is oo:
|
||||
return Limit(u*_f/(u - _f), _f, oo).doit()
|
||||
return u*focal_length/(u - focal_length)
|
||||
|
||||
|
||||
def lens_formula(focal_length=None, u=None, v=None):
|
||||
"""
|
||||
This function provides one of the three parameters
|
||||
when two of them are supplied.
|
||||
This is valid only for paraxial rays.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
focal_length : sympifiable
|
||||
Focal length of the mirror.
|
||||
u : sympifiable
|
||||
Distance of object from the optical center on
|
||||
the principal axis.
|
||||
v : sympifiable
|
||||
Distance of the image from the optical center
|
||||
on the principal axis.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.optics import lens_formula
|
||||
>>> from sympy.abc import f, u, v
|
||||
>>> lens_formula(focal_length=f, u=u)
|
||||
f*u/(f + u)
|
||||
>>> lens_formula(focal_length=f, v=v)
|
||||
f*v/(f - v)
|
||||
>>> lens_formula(u=u, v=v)
|
||||
u*v/(u - v)
|
||||
|
||||
"""
|
||||
if focal_length and u and v:
|
||||
raise ValueError("Please provide only two parameters")
|
||||
|
||||
focal_length = sympify(focal_length)
|
||||
u = sympify(u)
|
||||
v = sympify(v)
|
||||
if u is oo:
|
||||
_u = Symbol('u')
|
||||
if v is oo:
|
||||
_v = Symbol('v')
|
||||
if focal_length is oo:
|
||||
_f = Symbol('f')
|
||||
if focal_length is None:
|
||||
if u is oo and v is oo:
|
||||
return Limit(Limit(_v*_u/(_u - _v), _u, oo), _v, oo).doit()
|
||||
if u is oo:
|
||||
return Limit(v*_u/(_u - v), _u, oo).doit()
|
||||
if v is oo:
|
||||
return Limit(_v*u/(u - _v), _v, oo).doit()
|
||||
return v*u/(u - v)
|
||||
if u is None:
|
||||
if v is oo and focal_length is oo:
|
||||
return Limit(Limit(_v*_f/(_f - _v), _v, oo), _f, oo).doit()
|
||||
if v is oo:
|
||||
return Limit(_v*focal_length/(focal_length - _v), _v, oo).doit()
|
||||
if focal_length is oo:
|
||||
return Limit(v*_f/(_f - v), _f, oo).doit()
|
||||
return v*focal_length/(focal_length - v)
|
||||
if v is None:
|
||||
if u is oo and focal_length is oo:
|
||||
return Limit(Limit(_u*_f/(_u + _f), _u, oo), _f, oo).doit()
|
||||
if u is oo:
|
||||
return Limit(_u*focal_length/(_u + focal_length), _u, oo).doit()
|
||||
if focal_length is oo:
|
||||
return Limit(u*_f/(u + _f), _f, oo).doit()
|
||||
return u*focal_length/(u + focal_length)
|
||||
|
||||
def hyperfocal_distance(f, N, c):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
f: sympifiable
|
||||
Focal length of a given lens.
|
||||
|
||||
N: sympifiable
|
||||
F-number of a given lens.
|
||||
|
||||
c: sympifiable
|
||||
Circle of Confusion (CoC) of a given image format.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy.physics.optics import hyperfocal_distance
|
||||
>>> round(hyperfocal_distance(f = 0.5, N = 8, c = 0.0033), 2)
|
||||
9.47
|
||||
"""
|
||||
|
||||
f = sympify(f)
|
||||
N = sympify(N)
|
||||
c = sympify(c)
|
||||
|
||||
return (1/(N * c))*(f**2)
|
||||
|
||||
def transverse_magnification(si, so):
|
||||
"""
|
||||
|
||||
Calculates the transverse magnification upon reflection in a mirror,
|
||||
which is the ratio of the image size to the object size.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
so: sympifiable
|
||||
Lens-object distance.
|
||||
|
||||
si: sympifiable
|
||||
Lens-image distance.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy.physics.optics import transverse_magnification
|
||||
>>> transverse_magnification(30, 15)
|
||||
-2
|
||||
|
||||
"""
|
||||
|
||||
si = sympify(si)
|
||||
so = sympify(so)
|
||||
|
||||
return (-(si/so))
|
||||
340
venv/lib/python3.12/site-packages/sympy/physics/optics/waves.py
Normal file
340
venv/lib/python3.12/site-packages/sympy/physics/optics/waves.py
Normal file
@@ -0,0 +1,340 @@
|
||||
"""
|
||||
This module has all the classes and functions related to waves in optics.
|
||||
|
||||
**Contains**
|
||||
|
||||
* TWave
|
||||
"""
|
||||
|
||||
__all__ = ['TWave']
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.function import Derivative, Function
|
||||
from sympy.core.numbers import (Number, pi, I)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.core.sympify import _sympify, sympify
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (atan2, cos, sin)
|
||||
from sympy.physics.units import speed_of_light, meter, second
|
||||
|
||||
|
||||
c = speed_of_light.convert_to(meter/second)
|
||||
|
||||
|
||||
class TWave(Expr):
|
||||
|
||||
r"""
|
||||
This is a simple transverse sine wave travelling in a one-dimensional space.
|
||||
Basic properties are required at the time of creation of the object,
|
||||
but they can be changed later with respective methods provided.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
It is represented as :math:`A \times cos(k*x - \omega \times t + \phi )`,
|
||||
where :math:`A` is the amplitude, :math:`\omega` is the angular frequency,
|
||||
:math:`k` is the wavenumber (spatial frequency), :math:`x` is a spatial variable
|
||||
to represent the position on the dimension on which the wave propagates,
|
||||
and :math:`\phi` is the phase angle of the wave.
|
||||
|
||||
|
||||
Arguments
|
||||
=========
|
||||
|
||||
amplitude : Sympifyable
|
||||
Amplitude of the wave.
|
||||
frequency : Sympifyable
|
||||
Frequency of the wave.
|
||||
phase : Sympifyable
|
||||
Phase angle of the wave.
|
||||
time_period : Sympifyable
|
||||
Time period of the wave.
|
||||
n : Sympifyable
|
||||
Refractive index of the medium.
|
||||
|
||||
Raises
|
||||
=======
|
||||
|
||||
ValueError : When neither frequency nor time period is provided
|
||||
or they are not consistent.
|
||||
TypeError : When anything other than TWave objects is added.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A1, phi1, A2, phi2, f = symbols('A1, phi1, A2, phi2, f')
|
||||
>>> w1 = TWave(A1, f, phi1)
|
||||
>>> w2 = TWave(A2, f, phi2)
|
||||
>>> w3 = w1 + w2 # Superposition of two waves
|
||||
>>> w3
|
||||
TWave(sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2), f,
|
||||
atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2)), 1/f, n)
|
||||
>>> w3.amplitude
|
||||
sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
|
||||
>>> w3.phase
|
||||
atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2))
|
||||
>>> w3.speed
|
||||
299792458*meter/(second*n)
|
||||
>>> w3.angular_velocity
|
||||
2*pi*f
|
||||
|
||||
"""
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
amplitude,
|
||||
frequency=None,
|
||||
phase=S.Zero,
|
||||
time_period=None,
|
||||
n=Symbol('n')):
|
||||
if time_period is not None:
|
||||
time_period = _sympify(time_period)
|
||||
_frequency = S.One/time_period
|
||||
if frequency is not None:
|
||||
frequency = _sympify(frequency)
|
||||
_time_period = S.One/frequency
|
||||
if time_period is not None:
|
||||
if frequency != S.One/time_period:
|
||||
raise ValueError("frequency and time_period should be consistent.")
|
||||
if frequency is None and time_period is None:
|
||||
raise ValueError("Either frequency or time period is needed.")
|
||||
if frequency is None:
|
||||
frequency = _frequency
|
||||
if time_period is None:
|
||||
time_period = _time_period
|
||||
|
||||
amplitude = _sympify(amplitude)
|
||||
phase = _sympify(phase)
|
||||
n = sympify(n)
|
||||
obj = Basic.__new__(cls, amplitude, frequency, phase, time_period, n)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def amplitude(self):
|
||||
"""
|
||||
Returns the amplitude of the wave.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.amplitude
|
||||
A
|
||||
"""
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def frequency(self):
|
||||
"""
|
||||
Returns the frequency of the wave,
|
||||
in cycles per second.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.frequency
|
||||
f
|
||||
"""
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def phase(self):
|
||||
"""
|
||||
Returns the phase angle of the wave,
|
||||
in radians.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.phase
|
||||
phi
|
||||
"""
|
||||
return self.args[2]
|
||||
|
||||
@property
|
||||
def time_period(self):
|
||||
"""
|
||||
Returns the temporal period of the wave,
|
||||
in seconds per cycle.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.time_period
|
||||
1/f
|
||||
"""
|
||||
return self.args[3]
|
||||
|
||||
@property
|
||||
def n(self):
|
||||
"""
|
||||
Returns the refractive index of the medium
|
||||
"""
|
||||
return self.args[4]
|
||||
|
||||
@property
|
||||
def wavelength(self):
|
||||
"""
|
||||
Returns the wavelength (spatial period) of the wave,
|
||||
in meters per cycle.
|
||||
It depends on the medium of the wave.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.wavelength
|
||||
299792458*meter/(second*f*n)
|
||||
"""
|
||||
return c/(self.frequency*self.n)
|
||||
|
||||
|
||||
@property
|
||||
def speed(self):
|
||||
"""
|
||||
Returns the propagation speed of the wave,
|
||||
in meters per second.
|
||||
It is dependent on the propagation medium.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.speed
|
||||
299792458*meter/(second*n)
|
||||
"""
|
||||
return self.wavelength*self.frequency
|
||||
|
||||
@property
|
||||
def angular_velocity(self):
|
||||
"""
|
||||
Returns the angular velocity of the wave,
|
||||
in radians per second.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.angular_velocity
|
||||
2*pi*f
|
||||
"""
|
||||
return 2*pi*self.frequency
|
||||
|
||||
@property
|
||||
def wavenumber(self):
|
||||
"""
|
||||
Returns the wavenumber of the wave,
|
||||
in radians per meter.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.optics import TWave
|
||||
>>> A, phi, f = symbols('A, phi, f')
|
||||
>>> w = TWave(A, f, phi)
|
||||
>>> w.wavenumber
|
||||
pi*second*f*n/(149896229*meter)
|
||||
"""
|
||||
return 2*pi/self.wavelength
|
||||
|
||||
def __str__(self):
|
||||
"""String representation of a TWave."""
|
||||
from sympy.printing import sstr
|
||||
return type(self).__name__ + sstr(self.args)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Addition of two waves will result in their superposition.
|
||||
The type of interference will depend on their phase angles.
|
||||
"""
|
||||
if isinstance(other, TWave):
|
||||
if self.frequency == other.frequency and self.wavelength == other.wavelength:
|
||||
return TWave(sqrt(self.amplitude**2 + other.amplitude**2 + 2 *
|
||||
self.amplitude*other.amplitude*cos(
|
||||
self.phase - other.phase)),
|
||||
self.frequency,
|
||||
atan2(self.amplitude*sin(self.phase)
|
||||
+ other.amplitude*sin(other.phase),
|
||||
self.amplitude*cos(self.phase)
|
||||
+ other.amplitude*cos(other.phase))
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError("Interference of waves with different frequencies"
|
||||
" has not been implemented.")
|
||||
else:
|
||||
raise TypeError(type(other).__name__ + " and TWave objects cannot be added.")
|
||||
|
||||
def __mul__(self, other):
|
||||
"""
|
||||
Multiplying a wave by a scalar rescales the amplitude of the wave.
|
||||
"""
|
||||
other = sympify(other)
|
||||
if isinstance(other, Number):
|
||||
return TWave(self.amplitude*other, *self.args[1:])
|
||||
else:
|
||||
raise TypeError(type(other).__name__ + " and TWave objects cannot be multiplied.")
|
||||
|
||||
def __sub__(self, other):
|
||||
return self.__add__(-1*other)
|
||||
|
||||
def __neg__(self):
|
||||
return self.__mul__(-1)
|
||||
|
||||
def __radd__(self, other):
|
||||
return self.__add__(other)
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self.__mul__(other)
|
||||
|
||||
def __rsub__(self, other):
|
||||
return (-self).__radd__(other)
|
||||
|
||||
def _eval_rewrite_as_sin(self, *args, **kwargs):
|
||||
return self.amplitude*sin(self.wavenumber*Symbol('x')
|
||||
- self.angular_velocity*Symbol('t') + self.phase + pi/2, evaluate=False)
|
||||
|
||||
def _eval_rewrite_as_cos(self, *args, **kwargs):
|
||||
return self.amplitude*cos(self.wavenumber*Symbol('x')
|
||||
- self.angular_velocity*Symbol('t') + self.phase)
|
||||
|
||||
def _eval_rewrite_as_pde(self, *args, **kwargs):
|
||||
mu, epsilon, x, t = symbols('mu, epsilon, x, t')
|
||||
E = Function('E')
|
||||
return Derivative(E(x, t), x, 2) + mu*epsilon*Derivative(E(x, t), t, 2)
|
||||
|
||||
def _eval_rewrite_as_exp(self, *args, **kwargs):
|
||||
return self.amplitude*exp(I*(self.wavenumber*Symbol('x')
|
||||
- self.angular_velocity*Symbol('t') + self.phase))
|
||||
Reference in New Issue
Block a user