Microstrip Notch Filter
A straight MSL line with a open-ended stub to create a simple microwave filter.
Introduction
This tutorial covers:
Setup a mircostrip line (MSL) and MSL port
Apply an inhomogeneous mesh used for improved accuracy and simulation speed
Calculate the S-Parameter of the filter
Python Script
Get the latest version from git.
Import Libraries
import os, tempfile
from pylab import *
from CSXCAD import ContinuousStructure
from openEMS import openEMS
from openEMS.physical_constants import *
Setup the simulation
Sim_Path = os.path.join(tempfile.gettempdir(), 'NotchFilter')
post_proc_only = True
unit = 1e-6 # specify everything in um
MSL_length = 50000
#MSL_width = 600
MSL_width = 50
substrate_thickness = 254
substrate_epr = 3.66
stub_length = 12e3
f_max = 7e9
Setup FDTD parameters & excitation function
FDTD = openEMS()
FDTD.SetGaussExcite( f_max/2, f_max/2 )
FDTD.SetBoundaryCond( ['PML_8', 'PML_8', 'MUR', 'MUR', 'PEC', 'MUR'] )
Setup Geometry & Mesh
CSX = ContinuousStructure()
FDTD.SetCSX(CSX)
mesh = CSX.GetGrid()
mesh.SetDeltaUnit(unit)
resolution = C0/(f_max*sqrt(substrate_epr))/unit/50 # resolution of lambda/50
third_mesh = array([2*resolution/3, -resolution/3])/4
Do manual meshing
mesh.AddLine('x', 0)
mesh.AddLine('x', MSL_width/2+third_mesh)
mesh.AddLine('x', -MSL_width/2-third_mesh)
mesh.SmoothMeshLines('x', resolution/4)
mesh.AddLine('x', [-MSL_length, MSL_length])
mesh.SmoothMeshLines('x', resolution)
mesh.AddLine('y', 0)
mesh.AddLine('y', MSL_width/2+third_mesh)
mesh.AddLine('y', -MSL_width/2-third_mesh)
mesh.SmoothMeshLines('y', resolution/4)
mesh.AddLine('y', [-15*MSL_width, 15*MSL_width+stub_length])
mesh.AddLine('y', (MSL_width/2+stub_length)+third_mesh)
mesh.SmoothMeshLines('y', resolution)
mesh.AddLine('z', linspace(0,substrate_thickness,5))
mesh.AddLine('z', 3000)
mesh.SmoothMeshLines('z', resolution)
Add the substrate
substrate = CSX.AddMaterial( 'RO4350B', epsilon=substrate_epr)
start = [-MSL_length, -15*MSL_width, 0]
stop = [+MSL_length, +15*MSL_width+stub_length, substrate_thickness]
substrate.AddBox(start, stop )
MSL port setup
port = [None, None]
pec = CSX.AddMetal( 'PEC' )
portstart = [ -MSL_length, -MSL_width/2, substrate_thickness]
portstop = [ 0, MSL_width/2, 0]
port[0] = FDTD.AddMSLPort( 1, pec, portstart, portstop, 'x', 'z', excite=-1, FeedShift=10*resolution, MeasPlaneShift=MSL_length/3, priority=10)
portstart = [MSL_length, -MSL_width/2, substrate_thickness]
portstop = [0 , MSL_width/2, 0]
port[1] = FDTD.AddMSLPort( 2, pec, portstart, portstop, 'x', 'z', MeasPlaneShift=MSL_length/3, priority=10 )
Filter-Stub Definition
# start = [-MSL_width/2, MSL_width/2, substrate_thickness]
# stop = [ MSL_width/2, MSL_width/2+stub_length, substrate_thickness]
# pec.AddBox(start, stop, priority=10 )
Run the simulation
if 1: # debugging only
CSX_file = os.path.join(Sim_Path, 'notch.xml')
if not os.path.exists(Sim_Path):
os.mkdir(Sim_Path)
CSX.Write2XML(CSX_file)
from CSXCAD import AppCSXCAD_BIN
os.system(AppCSXCAD_BIN + ' "{}"'.format(CSX_file))
if not post_proc_only:
FDTD.Run(Sim_Path, cleanup=True)
Post-processing and plotting
f = linspace( 1e6, f_max, 1601 )
for p in port:
p.CalcPort( Sim_Path, f, ref_impedance = 50)
s11 = port[0].uf_ref / port[0].uf_inc
s21 = port[1].uf_ref / port[0].uf_inc
plot(f/1e9,20*log10(abs(s11)),'k-',linewidth=2 , label='$S_{11}$')
grid()
plot(f/1e9,20*log10(abs(s21)),'r--',linewidth=2 , label='$S_{21}$')
legend()
ylabel('S-Parameter (dB)')
xlabel('frequency (GHz)')
ylim([-40, 2])
show()
z0 = 50
freq_list = linspace(100e6, f_max, 1601)
for p in port:
p.CalcPort(Sim_Path, freq_list, ref_impedance=z0)
s11_list = port[0].uf_ref / port[0].uf_inc
s21_list = port[1].uf_ref / port[0].uf_inc
# hack: assume symmetry and reciprocity, only correct for passive linear circuit!
s22_list = s11_list
s12_list = s21_list
z11_list = port[0].uf_tot / port[0].if_tot
for idx, z11 in enumerate(z11_list):
print(freq_list[idx], z11, file=sys.stderr)
#from pprint import pprint
#pprint(z11_list)
# write Touchstone s2p
print("# HZ S RI R %f" % z0) # Touchstone metadata, not comment!
for idx, freq in enumerate(freq_list):
s11 = s11_list[idx]
s21 = s21_list[idx]
s12 = s12_list[idx]
s22 = s22_list[idx]
print("%f %f %f %f %f %f %f %f %f" % (freq, s11.real, s11.imag, s21.real, s21.imag, s12.real, s12.imag, s22.real, s22.imag))
Images

S-Parameter over Frequency