"""
circularWaveguide (:mod:`skrf.media.circularWaveguide`)
================================================================
.. autosummary::
:toctree: generated/
CircularWaveguide
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as npy
from numpy import sqrt, where
from scipy.constants import epsilon_0, mu_0, pi
from scipy.special import jn_zeros, jnp_zeros
from ..constants import NumberLike
from ..data import materials
from .freespace import Freespace
from .media import Media
if TYPE_CHECKING:
from ..frequency import Frequency
[docs]
class CircularWaveguide(Media):
r"""
A single mode of a homogeneously filled Circular Waveguide
Represents a single mode of a homogeneously filled circular
waveguide of cross-section `r^2 pi`. The mode is determined by
`mode-type` (`'te'` or `'tm'`) and mode indices ( `m` and `n` ).
Corrugated circular waveguides, which also support HE modes, are not
supported.
==================================== ============= ===============
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`
==================================== ============= ===============
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.
r : number
radius of the waveguide, in meters.
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 'phi'-direction, the azimuthal index
n : int
mode index in 'r'-direction, the radial index
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`.
\*args, \*\*kwargs : arguments, keyword arguments
passed to :class:`~skrf.media.media.Media`'s constructor
(:func:`~skrf.media.media.Media.__init__`
Examples
--------
In the following example an ideal waveguide of 2.39 mm diameter is
constructed for the high W band, operated in the fundamental TE11 mode.
If no conductivity is provided the walls are treated as perfect
electric conductors.
>>> freq = rf.Frequency(88, 110, 101, 'ghz')
>>> rf.CircularWaveguide(freq, r=0.5 * 2.39e-3)
"""
[docs]
def __init__(self, frequency: Frequency | None = None,
z0_port: NumberLike | None = None,
z0_override: NumberLike | None = None,
z0: NumberLike | None = None,
r: NumberLike = 1,
mode_type: str = 'te', m: int = 1, n: int = 1,
ep_r: NumberLike = 1, mu_r: NumberLike = 1,
rho: NumberLike | str | None = None,
*args, **kwargs):
Media.__init__(self, frequency = frequency,
z0_port = z0_port, z0_override = z0_override, z0 = z0)
if mode_type.lower() not in ['te','tm']:
raise ValueError('mode_type must be either \'te\' or \'tm\'')
self.r = r
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
def __str__(self) -> str:
f=self.frequency
output = \
'Circular Waveguide Media. %i-%i %s. %i points'%\
(f.f_scaled[0], f.f_scaled[-1], f.unit, f.npoints) + \
'\n r= %.2em'% \
(self.r)
return output
def __repr__(self) -> str:
return self.__str__()
[docs]
@classmethod
def from_z0(cls, frequency: Frequency, z0: NumberLike,
f: NumberLike, ep_r: NumberLike = 1, mu_r: NumberLike = 1,
**kwargs):
r"""
Initialize from specified impedance at a given frequency, assuming the
fundamental TE11 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
\*\*kwargs : arguments, keyword arguments
passed to :class:`~skrf.media.media.Media`'s constructor
(:func:`~skrf.media.media.Media.__init__`
"""
mu = mu_0*mu_r
ep = epsilon_0*ep_r
w = 2*pi*f
# if self.mode_type =="te":
u = jnp_zeros(1, 1)[-1]
r =u/(w*mu) * 1./sqrt(1/(z0*1j)**2+ep/mu)
kwargs.update(dict(frequency=frequency, r=r, m=1, n=1, ep_r=ep_r, mu_r=mu_r))
return cls(**kwargs)
@property
def ep(self) -> NumberLike:
"""
The permativity of the filling material.
Returns
-------
ep : number
filling material's relative permittivity
"""
return self.ep_r * epsilon_0
@property
def mu(self) -> NumberLike:
"""
The permeability of the filling material.
Returns
-------
mu : number
filling material's relative permeability
"""
return self.mu_r * mu_0
@property
def k0(self) -> NumberLike:
r"""
Characteristic wave number.
.. math::
k_0 = \omega \sqrt{\varepsilon \mu}
Returns
-------
k0 : number
characteristic wave number
"""
return 2*pi*self.frequency.f*sqrt(self.ep * self.mu)
@property
def kc(self) -> NumberLike:
r"""
Cut-off wave number.
Defined as
.. math::
k_c = \frac{u_{mn}}{R}
where R is the radius of the waveguide, and u_mn is:
* the n-th root of the m-th Bessel function for 'tm' mode
* the n-th root of the Derivative of the m-th Bessel function for 'te' mode.
Returns
-------
kc : number
cut-off wavenumber
"""
if self.mode_type =="te":
u = jnp_zeros(self.m, self.n)[-1]
elif self.mode_type =="tm":
u = jn_zeros(self.m,self.n)[-1]
return u/self.r
@property
def f_cutoff(self) -> NumberLike:
r"""
cutoff frequency for this mode
.. math::
f_c = \frac{v}{2 \pi} \frac{u_{mn}}{R}
where R is the radius of the waveguide, and u_mn is:
* the n-th root of the m-th Bessel function for 'tm' mode
* the n-th root of the Derivative of the m-th Bessel function for 'te' mode.
and v= 1/sqrt(ep*mu) is the bulk velocity inside the filling material.
"""
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
`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) -> NumberLike:
r"""
Guide wavelength.
.. math::
\lambda_g = 2\pi/\beta
the distance in which the phase of the field increases by 2 pi.
"""
return 2*pi/self.beta
@property
def lambda_cutoff(self) -> NumberLike:
r"""
Cutoff wavelength.
.. math::
\lambda_c = v/f_c
where v= 1/sqrt(ep*mu)
"""
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
"""
# This also holds for the circular waveguide
## 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:
"""
Loss due to finite conductivity of the sidewalls for the fundamental mode TE11. Higher order
modes are not implemented, as well as effects due to surface roughness.
In units of Np/m
See property `rho` for setting conductivity.
Effects of finite conductivity are taken from [#]_, but expressed in the same terms as in [#]_.
References
----------
.. [#] Eq. (3.133), Chapter 3.4, Microwave Engineering, Pozar David, 2011
.. [#] Eq. (9.8.1), Chapter 9, Electromagnetic Waves and Antennas by Sophocles J. Orfanidis
http://eceweb1.rutgers.edu/~orfanidi/ewa/
See Also
--------
rho
"""
# TODO: Generalize to higher order modes
if (self.mode_type != "te") or (self.m != 1) or (self.n != 1):
raise NotImplementedError
if self.rho is None:
return 0
r, w, ep, rho, f_n = self.r, self.frequency.w, self.ep, \
self.rho, self.f_norm
u= self.kc*r
return 1./r * sqrt( (w*ep)/(2./rho) ) * ( (1/f_n)**2 + 1/(u**2 - 1) ) \
/sqrt(1-(1/f_n)**2)
@property
def z0_characteristic(self) -> NumberLike:
"""
The characteristic impedance, :math:`z_0`.
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]