# Source code for skrf.media.rectangularWaveguide

'''
.. module:: skrf.media.rectangularWaveguide
================================================================
rectangularWaveguide (:mod:skrf.media.rectangularWaveguide)
================================================================

Represents a single mode of a homogeneously filled rectangular
waveguide of cross-section a x b. The mode is determined by
mode-type ('te' or 'tm') and mode indices ( m and n ).

====================================  =============  ===============
Quantity                              Symbol         Variable
====================================  =============  ===============
Characteristic Wave Number            :math:k_0    :attr:k0
Cut-off Wave Number                   :math:k_c    :attr:kc
Longitudinal Wave Number              :math:k_z    :attr:gamma
Transverse Wave Number (a)            :math:k_x    :attr:kx
Transverse Wave Number (b)            :math:k_y    :attr:ky
Characteristic Impedance              :math:z_0    :attr:Z0
====================================  =============  ===============

'''
from scipy.constants import  epsilon_0, mu_0,pi,c
from numpy import sqrt, exp, sinc,where
import numpy as npy
from .media import Media
from ..data import materials
from ..tlineFunctions import skin_depth

from .freespace import Freespace

[docs]class RectangularWaveguide(Media):
'''
A single mode of a homogeneously filled rectangular waveguide

Parameters
----------
frequency : :class:~skrf.frequency.Frequency object
frequency band of this transmission line medium
z0 : number, array-like, or None
the port impedance for media. Only needed if  its different
from the characterisitc impedance of the transmission
line. if z0 is None then will default to Z0
a : number
width of waveguide, in meters.
b : number
height of waveguide, in meters. If None defaults to a/2
mode_type : ['te','tm']
mode type, transverse electric (te) or transverse magnetic
(tm) to-z. where z is direction of propagation
m : int
mode index in 'a'-direction
n : int
mode index in 'b'-direction
ep_r : number, array-like,
filling material's relative permittivity
mu_r : number, array-like
filling material's relative permeability
rho : number, array-like, string
resistivity (ohm-m) of the conductor walls. If array-like
must be same length as frequency. if str, it must be a key in
skrf.data.materials.
roughness : number, or array-like
surface roughness of the conductor walls in units of RMS
deviation from surface

*args,**kwargs : arguments, keywrod arguments
passed to :class:~skrf.media.media.Media's constructor
(:func:~skrf.media.media.Media.__init__

Examples
------------
Most common usage is standard aspect ratio (2:1) dominant
mode, TE10 mode of wr10 waveguide can be constructed by

>>> freq = rf.Frequency(75,110,101,'ghz')
>>> rf.RectangularWaveguide(freq,a= 100*mil)
'''
[docs]    def __init__(self, frequency=None, z0=None, a=1, b=None,
mode_type = 'te', m=1, n=0, ep_r=1, mu_r=1, rho=None,
roughness=None, *args, **kwargs):

Media.__init__(self, frequency=frequency,z0=z0)

if b is None:
b = a/2.
if mode_type.lower() not in ['te','tm']:
raise ValueError('mode_type must be either \'te\' or \'tm\'')

self.a = a
self.b = b
self.mode_type = mode_type
self.m = m
self.n = n
self.ep_r = ep_r
self.mu_r = mu_r
self.rho = rho
self.roughness = roughness

def __str__(self):
f=self.frequency
output =  \
'Rectangular Waveguide Media.  %i-%i %s.  %i points'%\
(f.f_scaled[0],f.f_scaled[-1],f.unit, f.npoints) + \
'\n a= %.2em, b= %.2em'% \
(self.a,self.b)
return output

def __repr__(self):
return self.__str__()

[docs]    @classmethod
def from_Z0(cls,frequency, Z0,f, m=1,n=0,ep_r=1, mu_r=1, **kw):
'''
Initialize from specfied impedance at a given frequency.

Parameters
-------------
frequency : Frequency Object
Z0 : number /array
scharacteristic impedance to create at f
f : number
frequency (in Hz) that the resultant waveguide has Z0=Z0
'''
if n !=0:
raise NotImplemented()

mu = mu_0*mu_r
ep = epsilon_0*ep_r
w = 2*pi*f
a =pi/(w*mu) * 1./sqrt((1/(Z0*1j)**2+ep/mu))

kw.update(dict(frequency=frequency,a=a, m=m, n=n, ep_r=ep_r, mu_r=mu_r))

return cls(**kw)

@property
def ep(self):
'''
The permativity of the filling material

Returns
-------
ep : number
filling material's relative permittivity
'''
return self.ep_r * epsilon_0

@property
def mu(self):
'''
The permeability of the filling material

Returns
-------
mu : number
filling material's relative permeability

'''
return self.mu_r * mu_0

@property
def k0(self):
'''
Characteristic wave number

Returns
-------
k0 : number
characteristic wave number
'''
return 2*pi*self.frequency.f*sqrt(self.ep * self.mu)

@property
def ky(self):
'''
Eigen-value in the b direction.

Defined as

.. math::

k_y = n \\frac{\pi}{b}

Returns
-------
ky : number
eigen-value in b direction
'''
return self.n*pi/self.b

@property
def kx(self):
'''
Eigen value in the 'a' direction

Defined as

.. math::

k_x = m \\frac{\pi}{a}

Returns
-------
kx : number
eigen-value in a direction
'''
return self.m*pi/self.a

@property
def kc(self):
'''
Cut-off wave number

Defined as

.. math::

k_c = \\sqrt {k_x^2 + k_y^2} = \\sqrt {
{m \\frac{\pi}{a}}^2 + {n \\frac{\pi}{b}}^2}

Returns
-------
kc : number
cut-off wavenumber
'''
return sqrt( self.kx**2 + self.ky**2)

@property
def f_cutoff(self):
'''
cutoff frequency for this mode

.. math::

max ( \frac{m \cdot v}{2a} , \frac{n \cdot v}{2b})

where v= sqrt(ep*mu)

'''
v = 1/sqrt(self.ep*self.mu)
if not ( self.m==1 and self.n==0):
print('f_cutoff not verified as correct for this mode ')

if self.m/self.a > self.n/self.b:
return self.m*v/(2*self.a)
else:
return self.n*v/(2*self.b)

@property
def f_norm(self):
'''
frequency vector normalized to cutoff
'''
return self.frequency.f/self.f_cutoff

@property
def rho(self):
'''
conductivty of sidewalls in ohm*m

Parameters
--------------
val : float, array-like or str
the conductivity in ohm*m. If array-like must be same length
as self.frequency. if str, it must be a key in
skrf.data.materials.

Examples
---------
>>> wg.rho = 2.8e-8
>>> wg.rho = 2.8e-8 * ones(len(wg.frequency))
>>> wg.rho = 'al'
>>> wg.rho = 'aluminum'
'''
if self.roughness != None:
delta = skin_depth(self.frequency.f, self._rho, self.mu_r)
k_w = 1. +exp(-(delta/(2*self.roughness))**1.6)
return self._rho*k_w**2

return self._rho

@rho.setter
def rho(self, val):
if isinstance(val, str):
self._rho = materials[val.lower()]['resistivity(ohm*m)']
else:
self._rho=val

@property
def lambda_guide(self):
'''
guide wavelength

the distance in which the phase of the field increases by 2 pi
'''
return 2*pi/self.beta

@property
def lambda_cutoff(self):
'''
cuttoff wavelength

.. math::

f_c * v

where v= sqrt(ep*mu)
'''
v = 1/sqrt(self.ep*self.mu)
return self.f_cutoff*v

@property
def gamma(self):
'''
The propagation constant (aka Longitudinal wave number)

Defined as

.. math::

k_z = j \\pm \\sqrt {k_0^2 - k_c^2}

This is.
* IMAGINARY for propagating modes
* REAL  for non-propagating modes,

Returns
--------
gamma :  number
The propagation constant

'''
## haringtons form
if False:#self.m==1 and self.n==0:
fs = Freespace(frequency=self.frequency,
ep_r=self.ep_r,
mu_r=self.mu_r)

g= where(self.f_norm>1.,
sqrt(1-self.f_norm**(-2))*fs.gamma, # cutton
-1j*sqrt(1-self.f_norm**(2))*fs.gamma)# cutoff

else:
# TODO:  fix this for lossy ep/mu (remove abs?)
k0,kc = self.k0, self.kc
g=  1j*sqrt(abs(k0**2 - kc**2)) * (k0>kc) +\
sqrt(abs(kc**2- k0**2))*(k0<kc) + \
0*(kc==k0)

g = g + self.alpha_c *(self.rho is not None)

return g

@property
def alpha_c(self):
'''
Loss due to finite conductivity and roughness of sidewalls

In units of np/m
See property rho for setting conductivity.

Effects of finite conductivity are taken from [#]_. If
:attr:roughness is not None, then its effects the conductivity
by

.. math::

\\sigma_c = \\frac{\\sigma}{k_w^2}

where

.. math::

k_w = 1 + e^{(-\\delta/2h)^{1.6}}

\\delta = \\mbox{skin depth}

h = \\mbox{surface roughness }

This is taken from Ansoft HFSS help documents.

References
--------------

.. [#] Chapter 9, (eq 9.8.1) of Electromagnetic Waves and Antennas by Sophocles J. Orfanidis
http://eceweb1.rutgers.edu/~orfanidi/ewa/
'''

if self.rho is None:
return 0

a,b,w,ep,rho,f_n = self.a, self.b, self.frequency.w, self.ep, \
self.rho, self.f_norm

return 1./b * sqrt( (w*ep)/(2./rho) ) * (1+2.*b/a*(1/f_n)**2)/\
sqrt(1-(1/f_n)**2)

@property
def Z0(self):
'''
The characteristic impedance
'''
omega = self.frequency.w
impedance_dict = {'te':   1j*omega*self.mu/(self.gamma),
'tm':   -1j*self.gamma/(omega*self.ep),\
}

return impedance_dict[self.mode_type]