1"""
2This module has all the classes and functions related to waves in optics.
3
4**Contains**
5
6* TWave
7"""
8
9__all__ = ['TWave']
10
11from sympy import (sympify, pi, sin, cos, sqrt, Number, Symbol, S,
12    symbols, Derivative, atan2)
13from sympy.core.expr import Expr
14from sympy.physics.units import speed_of_light, meter, second
15
16
17c = speed_of_light.convert_to(meter/second)
18
19
20class TWave(Expr):
21
22    r"""
23    This is a simple transverse sine wave travelling in a one-dimensional space.
24    Basic properties are required at the time of creation of the object,
25    but they can be changed later with respective methods provided.
26
27    Explanation
28    ===========
29
30    It is represented as :math:`A \times cos(k*x - \omega \times t + \phi )`,
31    where :math:`A` is the amplitude, :math:`\omega` is the angular frequency,
32    :math:`k` is the wavenumber (spatial frequency), :math:`x` is a spatial variable
33    to represent the position on the dimension on which the wave propagates,
34    and :math:`\phi` is the phase angle of the wave.
35
36
37    Arguments
38    =========
39
40    amplitude : Sympifyable
41        Amplitude of the wave.
42    frequency : Sympifyable
43        Frequency of the wave.
44    phase : Sympifyable
45        Phase angle of the wave.
46    time_period : Sympifyable
47        Time period of the wave.
48    n : Sympifyable
49        Refractive index of the medium.
50
51    Raises
52    =======
53
54    ValueError : When neither frequency nor time period is provided
55        or they are not consistent.
56    TypeError : When anything other than TWave objects is added.
57
58
59    Examples
60    ========
61
62    >>> from sympy import symbols
63    >>> from sympy.physics.optics import TWave
64    >>> A1, phi1, A2, phi2, f = symbols('A1, phi1, A2, phi2, f')
65    >>> w1 = TWave(A1, f, phi1)
66    >>> w2 = TWave(A2, f, phi2)
67    >>> w3 = w1 + w2  # Superposition of two waves
68    >>> w3
69    TWave(sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2), f,
70        atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2)))
71    >>> w3.amplitude
72    sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
73    >>> w3.phase
74    atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2))
75    >>> w3.speed
76    299792458*meter/(second*n)
77    >>> w3.angular_velocity
78    2*pi*f
79
80    """
81
82    def __init__(
83            self,
84            amplitude,
85            frequency=None,
86            phase=S.Zero,
87            time_period=None,
88            n=Symbol('n')):
89        frequency = sympify(frequency)
90        amplitude = sympify(amplitude)
91        phase = sympify(phase)
92        time_period = sympify(time_period)
93        n = sympify(n)
94        self._frequency = frequency
95        self._amplitude = amplitude
96        self._phase = phase
97        self._time_period = time_period
98        self._n = n
99        if time_period is not None:
100            self._frequency = 1/self._time_period
101        if frequency is not None:
102            self._time_period = 1/self._frequency
103            if time_period is not None:
104                if frequency != 1/time_period:
105                    raise ValueError("frequency and time_period should be consistent.")
106        if frequency is None and time_period is None:
107            raise ValueError("Either frequency or time period is needed.")
108
109    @property
110    def frequency(self):
111        """
112        Returns the frequency of the wave,
113        in cycles per second.
114
115        Examples
116        ========
117
118        >>> from sympy import symbols
119        >>> from sympy.physics.optics import TWave
120        >>> A, phi, f = symbols('A, phi, f')
121        >>> w = TWave(A, f, phi)
122        >>> w.frequency
123        f
124        """
125        return self._frequency
126
127    @property
128    def time_period(self):
129        """
130        Returns the temporal period of the wave,
131        in seconds per cycle.
132
133        Examples
134        ========
135
136        >>> from sympy import symbols
137        >>> from sympy.physics.optics import TWave
138        >>> A, phi, f = symbols('A, phi, f')
139        >>> w = TWave(A, f, phi)
140        >>> w.time_period
141        1/f
142        """
143        return self._time_period
144
145    @property
146    def wavelength(self):
147        """
148        Returns the wavelength (spatial period) of the wave,
149        in meters per cycle.
150        It depends on the medium of the wave.
151
152        Examples
153        ========
154
155        >>> from sympy import symbols
156        >>> from sympy.physics.optics import TWave
157        >>> A, phi, f = symbols('A, phi, f')
158        >>> w = TWave(A, f, phi)
159        >>> w.wavelength
160        299792458*meter/(second*f*n)
161        """
162        return c/(self._frequency*self._n)
163
164    @property
165    def amplitude(self):
166        """
167        Returns the amplitude of the wave.
168
169        Examples
170        ========
171
172        >>> from sympy import symbols
173        >>> from sympy.physics.optics import TWave
174        >>> A, phi, f = symbols('A, phi, f')
175        >>> w = TWave(A, f, phi)
176        >>> w.amplitude
177        A
178        """
179        return self._amplitude
180
181    @property
182    def phase(self):
183        """
184        Returns the phase angle of the wave,
185        in radians.
186
187        Examples
188        ========
189
190        >>> from sympy import symbols
191        >>> from sympy.physics.optics import TWave
192        >>> A, phi, f = symbols('A, phi, f')
193        >>> w = TWave(A, f, phi)
194        >>> w.phase
195        phi
196        """
197        return self._phase
198
199    @property
200    def speed(self):
201        """
202        Returns the propagation speed of the wave,
203        in meters per second.
204        It is dependent on the propagation medium.
205
206        Examples
207        ========
208
209        >>> from sympy import symbols
210        >>> from sympy.physics.optics import TWave
211        >>> A, phi, f = symbols('A, phi, f')
212        >>> w = TWave(A, f, phi)
213        >>> w.speed
214        299792458*meter/(second*n)
215        """
216        return self.wavelength*self._frequency
217
218    @property
219    def angular_velocity(self):
220        """
221        Returns the angular velocity of the wave,
222        in radians per second.
223
224        Examples
225        ========
226
227        >>> from sympy import symbols
228        >>> from sympy.physics.optics import TWave
229        >>> A, phi, f = symbols('A, phi, f')
230        >>> w = TWave(A, f, phi)
231        >>> w.angular_velocity
232        2*pi*f
233        """
234        return 2*pi*self._frequency
235
236    @property
237    def wavenumber(self):
238        """
239        Returns the wavenumber of the wave,
240        in radians per meter.
241
242        Examples
243        ========
244
245        >>> from sympy import symbols
246        >>> from sympy.physics.optics import TWave
247        >>> A, phi, f = symbols('A, phi, f')
248        >>> w = TWave(A, f, phi)
249        >>> w.wavenumber
250        pi*second*f*n/(149896229*meter)
251        """
252        return 2*pi/self.wavelength
253
254    def __str__(self):
255        """String representation of a TWave."""
256        from sympy.printing import sstr
257        return type(self).__name__ + sstr(self.args)
258
259    __repr__ = __str__
260
261    def __add__(self, other):
262        """
263        Addition of two waves will result in their superposition.
264        The type of interference will depend on their phase angles.
265        """
266        if isinstance(other, TWave):
267            if self._frequency == other._frequency and self.wavelength == other.wavelength:
268                return TWave(sqrt(self._amplitude**2 + other._amplitude**2 + 2 *
269                                  self._amplitude*other._amplitude*cos(
270                                      self._phase - other.phase)),
271                             self._frequency,
272                             atan2(self._amplitude*sin(self._phase)
273                             + other._amplitude*sin(other._phase),
274                             self._amplitude*cos(self._phase)
275                             + other._amplitude*cos(other._phase))
276                             )
277            else:
278                raise NotImplementedError("Interference of waves with different frequencies"
279                    " has not been implemented.")
280        else:
281            raise TypeError(type(other).__name__ + " and TWave objects can't be added.")
282
283    def __mul__(self, other):
284        """
285        Multiplying a wave by a scalar rescales the amplitude of the wave.
286        """
287        other = sympify(other)
288        if isinstance(other, Number):
289            return TWave(self._amplitude*other, *self.args[1:])
290        else:
291            raise TypeError(type(other).__name__ + " and TWave objects can't be multiplied.")
292
293    def __sub__(self, other):
294        return self.__add__(-1*other)
295
296    def __neg__(self):
297        return self.__mul__(-1)
298
299    def __radd__(self, other):
300        return self.__add__(other)
301
302    def __rmul__(self, other):
303        return self.__mul__(other)
304
305    def __rsub__(self, other):
306        return (-self).__radd__(other)
307
308    def _eval_rewrite_as_sin(self, *args, **kwargs):
309        return self._amplitude*sin(self.wavenumber*Symbol('x')
310            - self.angular_velocity*Symbol('t') + self._phase + pi/2, evaluate=False)
311
312    def _eval_rewrite_as_cos(self, *args, **kwargs):
313        return self._amplitude*cos(self.wavenumber*Symbol('x')
314            - self.angular_velocity*Symbol('t') + self._phase)
315
316    def _eval_rewrite_as_pde(self, *args, **kwargs):
317        from sympy import Function
318        mu, epsilon, x, t = symbols('mu, epsilon, x, t')
319        E = Function('E')
320        return Derivative(E(x, t), x, 2) + mu*epsilon*Derivative(E(x, t), t, 2)
321
322    def _eval_rewrite_as_exp(self, *args, **kwargs):
323        from sympy import exp, I
324        return self._amplitude*exp(I*(self.wavenumber*Symbol('x')
325            - self.angular_velocity*Symbol('t') + self._phase))
326