# Source code for sympy.physics.optics.waves

"""
This module has all the classes and functions related to waves in optics.

**Contains**

* TWave
"""

from __future__ import print_function, division

__all__ = ['TWave']

from sympy import (sympify, pi, sin, cos, sqrt, Symbol, S,
symbols, Derivative, atan2)
from sympy.core.expr import Expr
from sympy.physics.units import speed_of_light, meter, second

c = speed_of_light.convert_to(meter/second)

[docs]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.

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 velocity,
: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*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2)))
>>> w3.amplitude
sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
>>> w3.phase
atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))
>>> w3.speed
299792458*meter/(second*n)
>>> w3.angular_velocity
2*pi*f

"""

def __init__(
self,
amplitude,
frequency=None,
phase=S.Zero,
time_period=None,
n=Symbol('n')):
frequency = sympify(frequency)
amplitude = sympify(amplitude)
phase = sympify(phase)
time_period = sympify(time_period)
n = sympify(n)
self._frequency = frequency
self._amplitude = amplitude
self._phase = phase
self._time_period = time_period
self._n = n
if time_period is not None:
self._frequency = 1/self._time_period
if frequency is not None:
self._time_period = 1/self._frequency
if time_period is not None:
if frequency != 1/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.")

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

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

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

@property
def phase(self):
"""
Returns the phase angle 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.phase
phi
"""
return self._phase

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

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,

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__

"""
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*cos(self._phase)
+other._amplitude*cos(other._phase),
self._amplitude*sin(self._phase)
+other._amplitude*sin(other._phase))
)
else:
raise NotImplementedError("Interference of waves with different frequencies"
" has not been implemented.")
else:
raise TypeError(type(other).__name__ + " and TWave objects can't be added.")

def _eval_rewrite_as_sin(self, *args):
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):
return self._amplitude*cos(self.wavenumber*Symbol('x')
- self.angular_velocity*Symbol('t') + self._phase)

def _eval_rewrite_as_pde(self, *args):
from sympy import Function
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):
from sympy import exp, I
return self._amplitude*exp(I*(self.wavenumber*Symbol('x')
- self.angular_velocity*Symbol('t') + self._phase))