"""
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`
==================================== ============= ===============
.. autosummary::
:toctree: generated/
RectangularWaveguide
"""
from __future__ import annotations
from numbers import Number
from typing import TYPE_CHECKING
import numpy as npy
from numpy import exp, sqrt, where
from scipy.constants import epsilon_0, mu_0, pi
from ..constants import NumberLike
from ..data import materials
from ..tlineFunctions import skin_depth
from .freespace import Freespace
from .media import Media
if TYPE_CHECKING:
from ..frequency import Frequency
[docs]
class RectangularWaveguide(Media):
r"""
A single mode of a homogeneously filled rectangular waveguide.
Parameters
----------
frequency : :class:`~skrf.frequency.Frequency` object
frequency band of this transmission line medium
z0_port : number, array-like, or None
`z0_port` is the port impedance for networks generated by the media.
If `z0_port` is not None, the networks generated by the media are
renormalized (or in other words embedded) from the characteristic
impedance z0 of the media to `z0_port`.
Else if `z0_port` is None, the networks port impedances will be the raw
characteristic impedance z0 of the media.
(Default is None)
z0_override : number, array-like, or None
`z0_override` override the characteristic impedance for the media.
If `z0_override` is not None, the networks generated by the media have
their characteristic impedance `z0` overrided by `z0_override`.
(Default is None)
z0 : number, array-like, or None
deprecated parameter, alias to `z0_override` if `z0_override` is None.
Emmit a deprecation warning.
a : number, optional
width of waveguide, in meters.
Default is 1.
b : number or None, optional
height of waveguide, in meters.
If `None` defaults to a/2.
Default is None
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
:data:`skrf.data.materials`.
roughness : number, or array-like
surface roughness of the conductor walls in units of RMS
deviation from surface
\*args, \*\*kwargs : arguments, keyword 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: Frequency | None = None,
z0_port: NumberLike | None = None,
z0_override: NumberLike | None = None,
z0: NumberLike | None = None,
a: float = 1, b: float | None = None,
mode_type: str = 'te', m: int = 1, n: int = 0,
ep_r: None | NumberLike = 1, mu_r: None | NumberLike = 1,
rho: None | NumberLike = None,
roughness: None | NumberLike = None,
*args, **kwargs):
Media.__init__(self, frequency = frequency,
z0_port = z0_port, z0_override = z0_override, 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.lower()
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 = (
f'Rectangular Waveguide Media. {f.f_scaled[0]}-{f.f_scaled[-1]} {f.unit}. {f.npoints} points'
f'\n a= {self.a:.2e}m, b= {self.b:.2e}m')
return output
def __repr__(self):
return self.__str__()
[docs]
@classmethod
def from_z0(cls, frequency: Frequency, z0: NumberLike, f: Number,
ep_r=1, mu_r=1, **kw) -> Media:
"""
Initialize from specified impedance at a given frequency, assuming
the fundamental TE10 mode.
Parameters
----------
frequency : Frequency Object
z0 : number /array
characteristic impedance to create at `f`
f : number
frequency (in Hz) at which the resultant waveguide has the
characteristic impedance z0
ep_r : number, array-like,
filling material's relative permittivity
mu_r : number, array-like
filling material's relative permeability
"""
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=1, n=0, ep_r=ep_r, mu_r=mu_r))
return cls(**kw)
@property
def ep(self) -> NumberLike:
r"""
The permittivity of the filling material.
.. math:
\varepsilon = \varepsilon_r \varepsilon_0
Returns
-------
ep : number
filling material's permittivity in F/m.
"""
return self.ep_r * epsilon_0
@property
def mu(self) -> NumberLike:
r"""
The permeability of the filling material.
.. math::
\mu = \mu_r \mu_0
Returns
-------
mu : number
filling material's permeability in H/m.
"""
return self.mu_r * mu_0
@property
def k0(self) -> NumberLike:
r"""
Characteristic wave number.
.. math::
k_0 = \frac{\omega}{v} = \omega \sqrt{\varepsilon_r \mu_r}
Returns
-------
k0 : number
characteristic wave number
"""
return 2*pi*self.frequency.f*sqrt(self.ep * self.mu)
@property
def ky(self) -> NumberLike:
r"""
Eigenvalue in the `b` direction.
Defined as
.. math::
k_y = n \frac{\pi}{b}
Returns
-------
ky : number
eigenvalue in `b` direction
"""
return self.n*pi/self.b
@property
def kx(self) -> NumberLike:
r"""
Eigenvalue in the 'a' direction.
Defined as
.. math::
k_x = m \frac{\pi}{a}
Returns
-------
kx : number
eigenvalue in `a` direction
"""
return self.m*pi/self.a
@property
def kc(self) -> NumberLike:
r"""
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) -> NumberLike:
r"""
cutoff frequency for this mode.
.. math::
f_c = \frac{v}{2 \pi} \sqrt {
{m \frac{\pi}{a}}^2 + {n \frac{\pi}{b}}^2}
where :math:`v= 1/\sqrt{\varepsilon \mu}`.
"""
v = 1/sqrt(self.ep*self.mu)
return v* self.kc/(2*npy.pi)
@property
def f_norm(self) -> NumberLike:
"""
Frequency vector normalized to cutoff.
"""
return self.frequency.f/self.f_cutoff
@property
def rho(self) -> NumberLike:
"""
Conductivity 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
:data:`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 is not 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: NumberLike | str):
if isinstance(val, str):
self._rho = materials[val.lower()]['resistivity(ohm*m)']
else:
self._rho=val
@property
def lambda_guide(self) -> NumberLike:
r"""
Guide wavelength.
.. math::
\lambda_g = \frac{2\pi}{\beta}
The distance in which the phase of the field increases by 2 pi.
See Also
--------
k0
"""
return 2*pi/self.beta
@property
def lambda_cutoff(self) -> NumberLike:
r"""
Cutoff wavelength.
.. math::
\lambda_c = v/f_c
where :math:`v= 1/\sqrt{\varepsilon \mu}` and :math:`f_c` the cut-off frequency.
See Also
--------
f_cutoff
"""
v = 1/sqrt(self.ep*self.mu)
return v/self.f_cutoff
@property
def gamma(self) -> NumberLike:
r"""
The propagation constant (aka Longitudinal wave number).
Defined as
.. math::
k_z = \pm j \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) -> NumberLike:
r"""
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_characteristic(self) -> NumberLike:
"""
The characteristic impedance, :math:`z_0`.
The characteristic impedance depends of the mode ('te' or 'tm').
Returns
-------
z0_characteristic : npy.ndarray
Characteristic Impedance in units of ohms
"""
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]