I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -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)

View File

@ -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

View 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)

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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))

View 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))

View 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))