'''
MLine (:mod:`skrf.media.MLine`)
========================================
.. autosummary::
:toctree: generated/
MLine
'''
import numpy as npy
from numpy import log, tanh, sqrt, exp, real, imag, cosh, \
ones, zeros, arctan
from scipy.constants import epsilon_0, mu_0, c, pi
from .media import Media
from ..tlineFunctions import skin_depth, surface_resistivity
from ..constants import NumberLike
from typing import Union, TYPE_CHECKING
if TYPE_CHECKING:
from .. frequency import Frequency
[docs]class MLine(Media):
'''
Microstripline class
This class was made from the technical documentation [#]_ provided
by the qucs project [#]_ .
The variables and properties of this class are coincident with
their derivations.
In addition, Djordjevic [#]_ /Svensson [#]_ wideband debye dielectric
model is considered to provide more realistic modelling of broadband
microstrip as well as causal time domain response.
* Quasi-static characteristic impedance models:
+ Kirschning and Jansen
+ Hammerstad and Jensen
+ None
* Quasi-static effective dielectric constant models:
+ Kirschning and Jansen
+ Hammerstad and Jensen
+ Yamashita
+ Kobayashi
+ None
* Strip thickness correction model:
+ Hammerstad and Jensen, add a certain amount to W if T > 0.
In the case another model is used for effective dielectric constant,
Hammerstad and Jensen method is used for the impedance.
Parameters
----------
frequency : :class:`~skrf.frequency.Frequency` object
frequency band of the media
z0 : number, array-like, or None
the port impedance for media. Only needed if its different
from the characteristic impedance of the transmission
w : number, or array-like
width of conductor, in m.
h : number, or array-like
height of substrate between ground plane and conductor, in m.
t : number, or array-like or None, optional
conductor thickness, in m. Default is None.
ep_r : number, or array-like
relative permittivity of dielectric at frequency f_epr_tand
mu_r : number, or array-like
relative permeability of dielectric (assumed frequency invariant)
diel : str
dielectric dispersion model: 'djordjevicsvensson' or 'frequencyinvariant'.
rho: number, or array-like, optional
resistivity of conductor (None)
tand : number, or array-like
dielectric loss factor at frequency f_epr_tand
rough : number, or array-like
RMS roughness of conductor in m.
disp : str
microstripline dispersion model in:
* 'kirschningjansen'
* 'hammerstadjensen'
* 'yamashita'
* 'kobayashi'
* None
f_low : number, or array-like
lower frequency for wideband Debye Djordjevic/Svensson dielectric model
f_high : number, or array-like
higher frequency for wideband Debye Djordjevic/Svensson dielectric model
f_epr_tand : number, or array-like
measurement frequency for ep_r and tand of dielectric
References
----------
.. [#] http://qucs.sourceforge.net/docs/technical.pdf
.. [#] http://www.qucs.sourceforge.net/
.. [#] C. Svensson, G.E. Dermer,
Time domain modeling of lossy interconnects,
IEEE Trans. on Advanced Packaging, May 2001, N2, Vol. 24, pp.191-196.
.. [#] Djordjevic, R.M. Biljic, V.D. Likar-Smiljanic, T.K. Sarkar,
Wideband frequency-domain characterization of FR-4 and time-domain causality,
IEEE Trans. on EMC, vol. 43, N4, 2001, p. 662-667.
'''
[docs] def __init__(self, frequency: Union['Frequency', None] = None,
z0: Union[NumberLike, None] = None,
w: NumberLike = 3, h: NumberLike = 1.6,
t: Union[NumberLike, None] = None,
ep_r: NumberLike = 4.5, mu_r: NumberLike = 1,
diel: str = 'djordjevicsvensson',
rho: NumberLike = 1.68e-8, tand: NumberLike = 0,
rough: NumberLike = 0.15e-6, disp: str = 'kirschningjansen',
f_low: NumberLike = 1e3, f_high: NumberLike = 1e12,
f_epr_tand: NumberLike = 1e9,
*args, **kwargs):
Media.__init__(self, frequency=frequency,z0=z0)
self.w, self.h, self.t = w, h, t
self.ep_r, self.mu_r, self.diel = ep_r, mu_r, diel
self.rho, self.tand, self.rough, self.disp = rho, tand, rough, disp
self.f_low, self.f_high, self.f_epr_tand = f_low, f_high, f_epr_tand
def __str__(self) -> str:
f=self.frequency
output = \
'Microstripline Media. %i-%i %s. %i points'%\
(f.f_scaled[0],f.f_scaled[-1],f.unit, f.npoints) + \
'\n W= %.2em, H= %.2em'% \
(self.w,self.h)
return output
def __repr__(self) -> str:
return self.__str__()
@property
def delta_w1(self) -> NumberLike:
'''
Intermediary parameter. see qucs docs on microstrip lines.
'''
w, h, t = self.w, self.h, self.t
if t > 0.:
# Qucs formula 11.22 is wrong, normalized w has to be used instead (see Hammerstad and Jensen Article)
return t/pi * log(1. + 4*exp(1.)*tanh(sqrt(6.517*w/h))**2/t) * h
else:
return 0.
@property
def delta_wr(self) -> NumberLike:
'''
Intermediary parameter. see qucs docs on microstrip lines.
'''
delta_w1, ep_r = self.delta_w1, real(self.ep_r_f)
if self.t > 0.:
return delta_w1/2 * (1+1/cosh(sqrt(ep_r-1)))
else:
return 0.
@property
def ep_r_f(self) -> NumberLike:
'''
Frequency dependent relative permittivity of dielectric.
'''
ep_r, tand = self.ep_r, self.tand
f_low, f_high, f_epr_tand = self.f_low, self.f_high, self.f_epr_tand
f = self.frequency.f
if self.diel == 'djordjevicsvensson':
# compute the slope for a log frequency scale, tanD dependent.
m = (ep_r*tand) * (pi/(2*log(10)))
# value for frequency above f_high
ep_inf = (ep_r - 1j*ep_r*tand - m*log((f_high + 1j*f_epr_tand)/(f_low + 1j*f_epr_tand)))
return ep_inf + m*log((f_high + 1j*f)/(f_low + 1j*f))
elif self.diel == 'frequencyinvariant':
return ones(self.frequency.f.shape) * (ep_r - 1j*ep_r*tand)
else:
raise ValueError('Unknown dielectric dispersion model')
@property
def tand_f(self) -> NumberLike:
'''
Frequency dependent dielectric loss tangent.
'''
ep_r = self.ep_r_f
return -imag(ep_r) / real(ep_r)
@property
def ep_reff(self) -> NumberLike:
'''
Quasistatic effective relative permittivity of dielectric,
accounting for the filling factor between air and substrate.
'''
h, ep_r = self.h, self.ep_r_f
w1 = self.w + self.delta_w1
wr = self.w + self.delta_wr
return ep_re(wr, h, ep_r) * (ZL1(w1, h)/ZL1(wr, h))**2
@property
def G(self) -> NumberLike:
'''
intermediary parameter. see qucs docs on microstrip lines.
'''
ep_r, ep_reff, ZL = self.ep_r_f, self.ep_reff, self.Z0
ZF0 = npy.sqrt(mu_0/epsilon_0)
return (pi**2)/12 * (ep_r-1)/ep_reff * sqrt(2*pi*ZL/ZF0)
@property
def ep_reff_f(self) -> NumberLike:
'''
Frequency dependent effective relative permittivity of dielectric,
accounting for microstripline dispersion.
'''
ep_r, ep_reff = self.ep_r_f, self.ep_reff
w, h = self.w + self.delta_wr, self.h
f = self.frequency.f
if self.disp == 'hammerstadjensen':
ZL, G = self.Z0, self.G
fp = ZL/(2*mu_0*h)
return ep_r - (ep_r - ep_reff) / (1+G*(f/fp)**2)
elif self.disp == 'kirschningjansen':
fn = self.frequency.f * h * 1e-6
P1 = 0.27488+(0.6315+0.525/(1+0.0157*fn)**20)*w/h -0.065683*exp(-8.7513*w/h)
P2 = 0.33622*(1-exp(-0.03442*ep_r))
P3 = 0.0363*exp(-4.6*w/h)*(1-exp(-(fn/38.7)**4.97))
P4 = 1+2.751*(1-exp(-(ep_r/15.916)**8))
Pf = P1*P2*((0.1844+P3*P4)*fn)**1.5763
return ep_r - (ep_r-ep_reff)/(1+Pf)
elif self.disp == 'yamashita':
k = sqrt(ep_r/ep_reff)
F = 4*h*f*sqrt(ep_r-1)/c * (0.5+(1+2*log(1+w/h))**2)
return ep_reff * ((1+1/4*k*F**1.5)/(1+1/4*F**1.5))**2
elif self.disp == 'kobayashi':
f50 = c/(2*pi*h*(0.75+(0.75-0.332/(ep_r**1.73)*w/h))) \
* arctan(ep_r*sqrt((ep_reff-1)/(ep_r-ep_reff)))/ \
sqrt(ep_r-ep_reff)
m0 = 1+1/(1+sqrt(w/h))+0.32*(1/(1+sqrt(w/h)))**3
mc = 1+1.4/(1+w/h)*(0.15-0.235*exp(-0.45*f/f50))
if(w/h >= 0.7):
mc = 1
m = m0*mc
return ep_r - (ep_r-ep_reff)/(1+f/f50)**m
elif self.disp == 'none':
return ones(self.frequency.f.shape)*self.ep_reff
else:
raise ValueError('Unknown microstripline dispersion model')
@property
def Z0(self) -> NumberLike:
'''
Quasistatic characteristic impedance.
'''
h, ep_reff = self.h, real(self.ep_reff)
wr = self.w + self.delta_wr
return ZL1(wr, h)/sqrt(ep_reff)
@property
def Z0_f(self) -> NumberLike:
'''
Frequency dependent characteristic impedance.
'''
ZL, ep_reff, ep_reff_f = self.Z0, real(self. ep_reff), real(self.ep_reff_f)
wr, h = self.w + self.delta_wr, self.h
if self.disp == 'hammerstadjensen':
return ZL * sqrt(ep_reff/ ep_reff_f) * (ep_reff_f-1)/(ep_reff-1)
elif self.disp == 'kirschningjansen':
u = wr/h
fn = self.frequency.f * self.h * 1e-6
ep_r = real(self.ep_r_f)
R1 = 0.03891*ep_r**1.4
R2 = 0.267*u**7
R3 = 4.766*exp(-3.228*u**0.641)
R4 = 0.016+(0.0514*ep_r)**4.524
R5 = (fn/28.843)**12
R6 = 22.20*u**1.92
R7 = 1.206-0.3144*exp(-R1)*(1-exp(-R2))
R8 = 1+1.275*(1-exp(-0.004625*R3*ep_r**1.674)*(fn/18.365)**2.745)
R9 = 5.086*R4*R5/(0.3838+0.386*R4)*exp(-R6)/(1+1.2992*R5)*(ep_r-1)**6/(1+10*(ep_r-1)**6)
R10 = 0.00044*ep_r**2.136+0.0184
R11 = (fn/19.47)**6/(1+0.0962*(fn/19.47)**6)
R12 = 1/(1+0.00245*u**2)
R13 = 0.9408*ep_reff_f**R8-0.9603
R14 = (0.9408-R9)*ep_reff**R8-0.9603
R15 = 0.707*R10*(fn/12.3)**1.097
R16 = 1+0.0503*ep_r**2*R11*(1-exp(-(u/15)**6))
R17 = R7 * (1-1.1241*R12/R16*exp(-0.026*fn**1.15656-R15))
return ZL * (R13/R14)**R17
elif self.disp == 'yamashita':
return ZL * npy.sqrt(ep_reff/ ep_reff_f) * (ep_reff_f-1)/(ep_reff-1)
elif self.disp == 'kobayashi':
return ZL * npy.sqrt(ep_reff/ ep_reff_f) * (ep_reff_f-1)/(ep_reff-1)
elif self.disp == 'none':
return npy.ones(self.frequency.f.shape)*self.Z0
else:
raise ValueError('Unknown microstripline dispersion model')
@property
def alpha_conductor(self) -> NumberLike:
'''
Losses due to conductor resistivity.
Returns
-------
alpha_conductor : array-like
lossyness due to conductor losses
See Also
--------
surface_resistivity : calculates surface resistivity
'''
if self.rho is None or self.t is None:
raise(AttributeError('must provide values conductivity to calculate this. see initializer help'))
else:
f = self.frequency.f
ZF0 = npy.sqrt(mu_0/epsilon_0)
ZL, rho, mu_r = real(self.Z0_f), self.rho, self.mu_r
w = self.w + self.delta_wr
rough = self.rough
Kr = 1 + 2/pi*arctan(1.4*(rough/skin_depth(f, rho, mu_r))**2)
Ki = exp(-1.2*(ZL/ZF0)**0.7)
Rs = surface_resistivity(f=f, rho=rho, mu_r=1)
return Rs*Kr*Ki/(ZL*w)
@property
def alpha_dielectric(self) -> NumberLike:
'''
Losses due to dielectric.
'''
ep_r, ep_reff, tand = real(self.ep_r_f), real(self.ep_reff), self.tand_f
f = self.frequency.f
return pi*f/c * ep_r/sqrt(ep_reff) * (ep_reff-1)/(ep_r-1) * tand
@property
def beta_phase(self):
'''
Phase parameter.
'''
ep_reff, f = real(self.ep_reff_f), self.frequency.f
return 2*pi*f*sqrt(ep_reff)/c
@property
def gamma(self):
'''
Propagation constant.
See Also
--------
alpha_conductor : calculates losses to conductor
alpha_dielectric: calculates losses to dielectric
beta : calculates phase parameter
'''
f = self.frequency.f
alpha = zeros(len(f))
beta = zeros(len(f))
beta = self.beta_phase
alpha = self.alpha_dielectric
if self.rho is not None:
alpha += self.alpha_conductor
return alpha + 1j*beta
def a(u: NumberLike) -> NumberLike:
'''
Intermediary parameter. see qucs docs on microstrip lines.
'''
return 1. + 1./49.*log((u**4+(u/52)**2)/(u**4+0.432)) + 1./18.7*log(1+(u/18.1)**3)
def b(ep_r: NumberLike) -> NumberLike:
'''
intermediary parameter. see qucs docs on microstrip lines.
'''
return 0.564 * ((ep_r-0.9)/(ep_r+3.))**0.053
def f(u: NumberLike) -> NumberLike:
'''
intermediary parameter. see qucs docs on microstrip lines.
'''
return 6 + (2*pi-6)*exp(-(30.666/u)**0.7528)
def ZL1(w: NumberLike, h: NumberLike) -> NumberLike:
'''
intermediary parameter. see qucs docs on microstrip lines.
'''
u = w/h
ZF0 = sqrt(mu_0/epsilon_0)
return ZF0/(2*pi)*log(f(u)/u + sqrt(1.+(2./u)**2))
def ep_re(w: NumberLike, h: NumberLike, ep_r: NumberLike) -> NumberLike:
'''
intermediary parameter. see qucs docs on microstrip lines.
'''
u = w/h
return (ep_r+1)/2 + (ep_r-1)/2 * (1.+10./u)**(-a(u)*b(ep_r))