summaryrefslogtreecommitdiff
path: root/openEMS/python/Tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'openEMS/python/Tutorials')
-rw-r--r--openEMS/python/Tutorials/Bent_Patch_Antenna.py198
-rw-r--r--openEMS/python/Tutorials/CRLH_Extraction.py239
-rw-r--r--openEMS/python/Tutorials/Helical_Antenna.py191
-rw-r--r--openEMS/python/Tutorials/MSL_NotchFilter.py123
-rw-r--r--openEMS/python/Tutorials/RCS_Sphere.py126
-rw-r--r--openEMS/python/Tutorials/Rect_Waveguide.py125
-rw-r--r--openEMS/python/Tutorials/Simple_Patch_Antenna.py151
7 files changed, 1153 insertions, 0 deletions
diff --git a/openEMS/python/Tutorials/Bent_Patch_Antenna.py b/openEMS/python/Tutorials/Bent_Patch_Antenna.py
new file mode 100644
index 0000000..ef2cb0e
--- /dev/null
+++ b/openEMS/python/Tutorials/Bent_Patch_Antenna.py
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+"""
+ Bent Patch Antenna Tutorial
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.33+
+
+ (C) 2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+
+"""
+
+### Import Libraries
+import os, tempfile
+from pylab import *
+from mpl_toolkits.mplot3d import Axes3D
+
+from CSXCAD import CSXCAD
+
+from openEMS.openEMS import openEMS
+from openEMS.physical_constants import *
+
+
+### Setup the simulation
+Sim_Path = os.path.join(tempfile.gettempdir(), 'Bent_Patch')
+
+post_proc_only = False
+
+unit = 1e-3 # all length in mm
+
+f0 = 2.4e9 # center frequency, frequency of interest!
+lambda0 = round(C0/f0/unit) # wavelength in mm
+fc = 0.5e9 # 20 dB corner frequency
+
+# patch width in alpha-direction
+patch_width = 32 # resonant length in alpha-direction
+patch_radius = 50 # radius
+patch_length = 40 # patch length in z-direction
+
+#substrate setup
+substrate_epsR = 3.38
+substrate_kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate_epsR
+substrate_width = 80
+substrate_length = 90
+substrate_thickness = 1.524
+substrate_cells = 4
+
+#setup feeding
+feed_pos = -5.5 #feeding position in x-direction
+feed_width = 2 #feeding port width
+feed_R = 50 #feed resistance
+
+# size of the simulation box
+SimBox_rad = 2*100
+SimBox_height = 1.5*200
+
+### Setup FDTD parameter & excitation function
+FDTD = openEMS(CoordSystem=1) # init a cylindrical FDTD
+f0 = 2e9 # center frequency
+fc = 1e9 # 20 dB corner frequency
+FDTD.SetGaussExcite(f0, fc)
+FDTD.SetBoundaryCond(['MUR', 'MUR', 'MUR', 'MUR', 'MUR', 'MUR']) # boundary conditions
+
+### Setup the Geometry & Mesh
+# init a cylindrical mesh
+CSX = CSXCAD.ContinuousStructure(CoordSystem=1)
+FDTD.SetCSX(CSX)
+mesh = CSX.GetGrid()
+mesh.SetDeltaUnit(unit)
+
+### Setup the geometry using cylindrical coordinates
+# calculate some width as an angle in radiant
+patch_ang_width = patch_width/(patch_radius+substrate_thickness)
+substr_ang_width = substrate_width/patch_radius
+feed_angle = feed_pos/patch_radius
+
+# create patch
+patch = CSX.AddMetal('patch') # create a perfect electric conductor (PEC)
+start = [patch_radius+substrate_thickness, -patch_ang_width/2, -patch_length/2 ]
+stop = [patch_radius+substrate_thickness, patch_ang_width/2, patch_length/2 ]
+CSX.AddBox(patch, priority=10, start=start, stop=stop, edges2grid='all') # add a box-primitive to the metal property 'patch'
+
+# create substrate
+substrate = CSX.AddMaterial('substrate', epsilon=substrate_epsR, kappa=substrate_kappa )
+start = [patch_radius , -substr_ang_width/2, -substrate_length/2]
+stop = [patch_radius+substrate_thickness, substr_ang_width/2, substrate_length/2]
+substrate.AddBox(start=start, stop=stop, edges2grid='all')
+
+# save current density oon the patch
+jt_patch = CSX.AddDump('Jt_patch', dump_type=3, file_type=1)
+start = [patch_radius+substrate_thickness, -substr_ang_width/2, -substrate_length/2]
+stop = [patch_radius+substrate_thickness, +substr_ang_width/2, substrate_length/2]
+jt_patch.AddBox(start=start, stop=stop)
+
+# create ground
+gnd = CSX.AddMetal('gnd') # create a perfect electric conductor (PEC)
+start = [patch_radius, -substr_ang_width/2, -substrate_length/2]
+stop = [patch_radius, +substr_ang_width/2, +substrate_length/2]
+gnd.AddBox(priority=10, start=start, stop=stop, edges2grid='all')
+
+# apply the excitation & resist as a current source
+start = [patch_radius , feed_angle, 0]
+stop = [patch_radius+substrate_thickness, feed_angle, 0]
+port = FDTD.AddLumpedPort(1 ,feed_R, start, stop, 'r', 1.0, priority=50, edges2grid='all')
+
+### Finalize the Mesh
+# add the simulation domain size
+mesh.AddLine('r', patch_radius+np.array([-20, SimBox_rad]))
+mesh.AddLine('a', [-0.75*pi, 0.75*pi])
+mesh.AddLine('z', [-SimBox_height/2, SimBox_height/2])
+
+# add some lines for the substrate
+mesh.AddLine('r', patch_radius+np.linspace(0,substrate_thickness,substrate_cells))
+
+# generate a smooth mesh with max. cell size: lambda_min / 20
+max_res = C0 / (f0+fc) / unit / 20
+max_ang = max_res/(SimBox_rad+patch_radius) # max res in radiant
+mesh.SmoothMeshLines(0, max_res, 1.4)
+mesh.SmoothMeshLines(1, max_ang, 1.4)
+mesh.SmoothMeshLines(2, max_res, 1.4)
+
+## Add the nf2ff recording box
+nf2ff = FDTD.CreateNF2FFBox()
+
+### Run the simulation
+if 0: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'bent_patch.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+### Postprocessing & plotting
+f = np.linspace(max(1e9,f0-fc),f0+fc,401)
+port.CalcPort(Sim_Path, f)
+Zin = port.uf_tot / port.if_tot
+s11 = port.uf_ref/port.uf_inc
+s11_dB = 20.0*np.log10(np.abs(s11))
+
+figure()
+plot(f/1e9, s11_dB)
+grid()
+ylabel('s11 (dB)')
+xlabel('frequency (GHz)')
+
+P_in = 0.5*np.real(port.uf_tot * np.conj(port.if_tot)) # antenna feed power
+
+# plot feed point impedance
+figure()
+plot( f/1e6, real(Zin), 'k-', linewidth=2, label=r'$\Re(Z_{in})$' )
+grid()
+plot( f/1e6, imag(Zin), 'r--', linewidth=2, label=r'$\Im(Z_{in})$' )
+title( 'feed point impedance' )
+xlabel( 'frequency (MHz)' )
+ylabel( 'impedance ($\Omega$)' )
+legend( )
+
+
+idx = np.where((s11_dB<-10) & (s11_dB==np.min(s11_dB)))[0]
+if not len(idx)==1:
+ print('No resonance frequency found for far-field calulation')
+else:
+ f_res = f[idx[0]]
+ theta = np.arange(-180.0, 180.0, 2.0)
+ print("Calculate NF2FF")
+ nf2ff_res_phi0 = nf2ff.CalcNF2FF(Sim_Path, f_res, theta, 0, center=np.array([patch_radius+substrate_thickness, 0, 0])*unit, read_cached=True, outfile='nf2ff_xz.h5')
+
+ figure(figsize=(15, 7))
+ ax = subplot(121, polar=True)
+ E_norm = 20.0*np.log10(nf2ff_res_phi0.E_norm/np.max(nf2ff_res_phi0.E_norm)) + nf2ff_res_phi0.Dmax
+ ax.plot(np.deg2rad(theta), 10**(np.squeeze(E_norm)/20), linewidth=2, label='xz-plane')
+ ax.grid(True)
+ ax.set_xlabel('theta (deg)')
+ ax.set_theta_zero_location('N')
+ ax.set_theta_direction(-1)
+ ax.legend(loc=3)
+
+ phi = theta
+ nf2ff_res_theta90 = nf2ff.CalcNF2FF(Sim_Path, f_res, 90, phi, center=np.array([patch_radius+substrate_thickness, 0, 0])*unit, read_cached=True, outfile='nf2ff_xy.h5')
+
+ ax = subplot(122, polar=True)
+ E_norm = 20.0*np.log10(nf2ff_res_theta90.E_norm/np.max(nf2ff_res_theta90.E_norm)) + nf2ff_res_theta90.Dmax
+ ax.plot(np.deg2rad(phi), 10**(np.squeeze(E_norm)/20), linewidth=2, label='xy-plane')
+ ax.grid(True)
+ ax.set_xlabel('phi (deg)')
+ suptitle('Bent Patch Anteanna Pattern\nFrequency: {} GHz'.format(f_res/1e9), fontsize=14)
+ ax.legend(loc=3)
+
+ print( 'radiated power: Prad = {:.2e} Watt'.format(nf2ff_res_theta90.Prad[0]))
+ print( 'directivity: Dmax = {:.1f} ({:.1f} dBi)'.format(nf2ff_res_theta90.Dmax[0], 10*np.log10(nf2ff_res_theta90.Dmax[0])))
+ print( 'efficiency: nu_rad = {:.1f} %'.format(100*nf2ff_res_theta90.Prad[0]/real(P_in[idx[0]])))
+
+show()
+
diff --git a/openEMS/python/Tutorials/CRLH_Extraction.py b/openEMS/python/Tutorials/CRLH_Extraction.py
new file mode 100644
index 0000000..4e1850e
--- /dev/null
+++ b/openEMS/python/Tutorials/CRLH_Extraction.py
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+"""
+ Tutorials / CRLH_Extraction
+
+ Describtion at:
+ http://openems.de/index.php/Tutorial:_CRLH_Extraction
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.34+
+
+ (C) 2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+"""
+
+
+### Import Libraries
+import os, tempfile
+from pylab import *
+
+from CSXCAD import ContinuousStructure
+from openEMS import openEMS
+from openEMS.physical_constants import *
+
+### Class to represent single CRLH unit cells
+class CRLH_Cells:
+ def __init__(self, LL, LW, Top, Bot, GLT, GLB, SL, SW, VR):
+ self.LL = LL # Line length
+ self.LW = LW # Line width
+ self.Top = Top # top signal height
+ self.Bot = Bot # bottom signal height
+ self.GLT = GLT # gap length top
+ self.GLB = GLB # gap length bottom
+ self.SL = SL # stub length
+ self.SW = SW # stub width
+ self.VR = VR # via radius
+ self.props = dict() # property dictionary
+ self.edge_resolution = None
+
+ def createProperties(self, CSX):
+ for p in ['metal_top', 'metal_bot', 'via']:
+ self.props[p] = CSX.AddMetal(p)
+
+ def setEdgeResolution(self, res):
+ self.edge_resolution = res
+
+ def createCell(self, translate = [0,0,0]):
+ def append_mesh(mesh1, mesh2):
+ for n in range(3):
+ if mesh1[n] is None:
+ mesh1[n] = mesh2[n]
+ elif mesh2[n] is None:
+ continue
+ else:
+ mesh1[n] += mesh2[n]
+ return mesh1
+ translate = array(translate)
+ start = [-self.LL/2 , -self.LW/2, self.Top] + translate
+ stop = [-self.GLT/2, self.LW/2, self.Top] + translate
+ box = self.props['metal_top'].AddBox(start, stop, priority=10)
+ mesh = box.GetGridHint('x', metal_edge_res=self.edge_resolution, down_dir=False)
+ append_mesh(mesh, box.GetGridHint('y', metal_edge_res=self.edge_resolution) )
+
+ start = [+self.LL/2 , -self.LW/2, self.Top] + translate
+ stop = [+self.GLT/2, self.LW/2, self.Top] + translate
+ box = self.props['metal_top'].AddBox(start, stop, priority=10)
+ append_mesh(mesh, box.GetGridHint('x', metal_edge_res=self.edge_resolution, up_dir=False) )
+
+ start = [-(self.LL-self.GLB)/2, -self.LW/2, self.Bot] + translate
+ stop = [+(self.LL-self.GLB)/2, self.LW/2, self.Bot] + translate
+ box = self.props['metal_bot'].AddBox(start, stop, priority=10)
+ append_mesh(mesh, box.GetGridHint('x', metal_edge_res=self.edge_resolution) )
+
+ start = [-self.SW/2, -self.LW/2-self.SL, self.Bot] + translate
+ stop = [+self.SW/2, self.LW/2+self.SL, self.Bot] + translate
+ box = self.props['metal_bot'].AddBox(start, stop, priority=10)
+ append_mesh(mesh, box.GetGridHint('xy', metal_edge_res=self.edge_resolution) )
+
+ start = [0, -self.LW/2-self.SL+self.SW/2, 0 ] + translate
+ stop = [0, -self.LW/2-self.SL+self.SW/2, self.Bot] + translate
+
+ self.props['via'].AddCylinder(start, stop, radius=self.VR, priority=10)
+
+ start[1] *= -1
+ stop [1] *= -1
+ self.props['via'].AddCylinder(start, stop, radius=self.VR, priority=10)
+
+ return mesh
+
+
+if __name__ == '__main__':
+ ### Setup the simulation
+ Sim_Path = os.path.join(tempfile.gettempdir(), 'CRLH_Extraction')
+ post_proc_only = False
+
+ unit = 1e-6 # specify everything in um
+
+ feed_length = 30000
+
+ substrate_thickness = [1524, 101 , 254 ]
+ substrate_epsr = [3.48, 3.48, 3.48]
+
+ CRLH = CRLH_Cells(LL = 14e3, LW = 4e3, GLB = 1950, GLT = 4700, SL = 7800, SW = 1000, VR = 250 , \
+ Top = sum(substrate_thickness), \
+ Bot = sum(substrate_thickness[:-1]))
+
+ # frequency range of interest
+ f_start = 0.8e9
+ f_stop = 6e9
+
+ ### Setup FDTD parameters & excitation function
+ CSX = ContinuousStructure()
+ FDTD = openEMS(EndCriteria=1e-5)
+ FDTD.SetCSX(CSX)
+ mesh = CSX.GetGrid()
+ mesh.SetDeltaUnit(unit)
+
+ CRLH.createProperties(CSX)
+
+ FDTD.SetGaussExcite((f_start+f_stop)/2, (f_stop-f_start)/2 )
+ BC = {'PML_8' 'PML_8' 'MUR' 'MUR' 'PEC' 'PML_8'}
+ FDTD.SetBoundaryCond( ['PML_8', 'PML_8', 'MUR', 'MUR', 'PEC', 'PML_8'] )
+
+ ### Setup a basic mesh and create the CRLH unit cell
+ resolution = C0/(f_stop*sqrt(max(substrate_epsr)))/unit /30 # resolution of lambda/30
+ CRLH.setEdgeResolution(resolution/4)
+
+ mesh.SetLines('x', [-feed_length-CRLH.LL/2, 0, feed_length+CRLH.LL/2])
+ mesh.SetLines('y', [-30000, 0, 30000])
+
+ substratelines = cumsum(substrate_thickness)
+ mesh.SetLines('z', [0, 20000])
+ mesh.AddLine('z', cumsum(substrate_thickness))
+ mesh.AddLine('z', linspace(substratelines[-2],substratelines[-1],4))
+
+ # create the CRLH unit cell (will define additional fixed mesh lines)
+ mesh_hint = CRLH.createCell()
+ mesh.AddLine('x', mesh_hint[0])
+ mesh.AddLine('y', mesh_hint[1])
+
+ # Smooth the given mesh
+ mesh.SmoothMeshLines('all', resolution, 1.2)
+
+ ### Setup the substrate layer
+ substratelines = [0] + substratelines.tolist()
+ start, stop = mesh.GetSimArea()
+
+ for n in range(len(substrate_thickness)):
+ sub = CSX.AddMaterial( 'substrate_{}'.format(n), epsilon=substrate_epsr[n] )
+ start[2] = substratelines[n]
+ stop [2] = substratelines[n+1]
+
+ sub.AddBox( start, stop )
+
+ ### Add the feeding MSL ports
+ pec = CSX.AddMetal( 'PEC' )
+ port = [None, None]
+ x_lines = mesh.GetLines('x')
+ portstart = [ x_lines[0], -CRLH.LW/2, substratelines[-1]]
+ portstop = [ -CRLH.LL/2, CRLH.LW/2, 0]
+ port[0] = FDTD.AddMSLPort( 1, pec, portstart, portstop, 'x', 'z', excite=-1, FeedShift=10*resolution, MeasPlaneShift=feed_length/2, priority=10)
+
+
+ portstart = [ x_lines[-1], -CRLH.LW/2, substratelines[-1]]
+ portstop = [ +CRLH.LL/2 , CRLH.LW/2, 0]
+ port[1] = FDTD.AddMSLPort( 2, pec, portstart, portstop, 'x', 'z', MeasPlaneShift=feed_length/2, priority=10)
+
+ ### Run the simulation
+ if 1: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'CRLH_Extraction.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+ if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+ ### Post-Processing
+ f = linspace( f_start, f_stop, 1601 )
+ for p in port:
+ p.CalcPort( Sim_Path, f, ref_impedance = 50, ref_plane_shift = feed_length)
+
+ # calculate and plot scattering parameter
+ 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}$')
+ plot(f/1e9,20*log10(abs(s21)),'r--', linewidth=2, label='$S_{21}$')
+ grid()
+ legend(loc=3)
+ ylabel('S-Parameter (dB)')
+ xlabel('frequency (GHz)')
+ ylim([-40, 2])
+
+ ### Extract CRLH parameter form ABCD matrix
+ A = ((1+s11)*(1-s11) + s21*s21)/(2*s21)
+ C = ((1-s11)*(1-s11) - s21*s21)/(2*s21) / port[1].Z_ref
+
+ Y = C
+ Z = 2*(A-1)/C
+
+ iZ = imag(Z)
+ iY = imag(Y)
+
+ fse = interp(0, iZ, f)
+ fsh = interp(0, iY, f)
+
+ df = f[1]-f[0]
+ fse_idx = np.where(f>fse)[0][0]
+ fsh_idx = np.where(f>fsh)[0][0]
+
+ LR = 0.5*(iZ[fse_idx]-iZ[fse_idx-1])/(2*pi*df)
+ CL = 1/(2*pi*fse)**2/LR
+
+ CR = 0.5*(iY[fsh_idx]-iY[fsh_idx-1])/(2*pi*df)
+ LL = 1/(2*pi*fsh)**2/CR
+
+ print(' Series tank: CL = {:.2f} pF, LR = {:.2f} nH -> f_se = {:.2f} GHz '.format(CL*1e12, LR*1e9, fse*1e-9))
+ print(' Shunt tank: CR = {:.2f} pF, LL = {:.2f} nH -> f_sh = {:.2f} GHz '.format(CR*1e12, LL*1e9, fsh*1e-9))
+
+ ### Calculate analytical wave-number of an inf-array of cells
+ w = 2*pi*f
+ wse = 2*pi*fse
+ wsh = 2*pi*fsh
+ beta_calc = real(arccos(1-(w**2-wse**2)*(w**2-wsh**2)/(2*w**2/CR/LR)))
+
+ # plot
+ figure()
+ beta = -angle(s21)/CRLH.LL/unit
+ plot(abs(beta)*CRLH.LL*unit/pi,f*1e-9,'k-', linewidth=2, label=r'$\beta_{CRLH,\ 1\ cell}$' )
+ grid()
+ plot(beta_calc/pi,f*1e-9,'c--', linewidth=2, label=r'$\beta_{CRLH,\ \infty\ cells}$')
+ plot(real(port[1].beta)*CRLH.LL*unit/pi,f*1e-9,'g-', linewidth=2, label=r'$\beta_{MSL}$')
+ ylim([1, 6])
+ xlabel(r'$|\beta| p / \pi$')
+ ylabel('frequency (GHz)')
+ legend(loc=2)
+
+ show() \ No newline at end of file
diff --git a/openEMS/python/Tutorials/Helical_Antenna.py b/openEMS/python/Tutorials/Helical_Antenna.py
new file mode 100644
index 0000000..3211ec8
--- /dev/null
+++ b/openEMS/python/Tutorials/Helical_Antenna.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+"""
+ Helical Antenna Tutorial
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.33+
+
+ (C) 2015-2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+
+"""
+
+### Import Libraries
+import os, tempfile
+from pylab import *
+
+from CSXCAD import CSXCAD
+
+from openEMS import openEMS
+from openEMS.physical_constants import *
+
+
+### Setup the simulation
+Sim_Path = os.path.join(tempfile.gettempdir(), 'Helical_Ant')
+post_proc_only = False
+
+unit = 1e-3 # all length in mm
+
+f0 = 2.4e9 # center frequency, frequency of interest!
+lambda0 = round(C0/f0/unit) # wavelength in mm
+fc = 0.5e9 # 20 dB corner frequency
+
+Helix_radius = 20 # --> diameter is ~ lambda/pi
+Helix_turns = 10 # --> expected gain is G ~ 4 * 10 = 40 (16dBi)
+Helix_pitch = 30 # --> pitch is ~ lambda/4
+Helix_mesh_res = 3
+
+gnd_radius = lambda0/2
+
+# feeding
+feed_heigth = 3
+feed_R = 120 #feed impedance
+
+# size of the simulation box
+SimBox = array([1, 1, 1.5])*2.0*lambda0
+
+### Setup FDTD parameter & excitation function
+FDTD = openEMS(EndCriteria=1e-4)
+FDTD.SetGaussExcite( f0, fc )
+FDTD.SetBoundaryCond( ['MUR', 'MUR', 'MUR', 'MUR', 'MUR', 'PML_8'] )
+
+### Setup Geometry & Mesh
+CSX = CSXCAD.ContinuousStructure()
+FDTD.SetCSX(CSX)
+mesh = CSX.GetGrid()
+mesh.SetDeltaUnit(unit)
+
+max_res = floor(C0 / (f0+fc) / unit / 20) # cell size: lambda/20
+
+# create helix mesh
+mesh.AddLine('x', [-Helix_radius, 0, Helix_radius])
+mesh.SmoothMeshLines('x', Helix_mesh_res)
+# add the air-box
+mesh.AddLine('x', [-SimBox[0]/2-gnd_radius, SimBox[0]/2+gnd_radius])
+# create a smooth mesh between specified fixed mesh lines
+mesh.SmoothMeshLines('x', max_res, ratio=1.4)
+
+# copy x-mesh to y-direction
+mesh.SetLines('y', mesh.GetLines('x'))
+
+# create helix mesh in z-direction
+mesh.AddLine('z', [0, feed_heigth, Helix_turns*Helix_pitch+feed_heigth])
+mesh.SmoothMeshLines('z', Helix_mesh_res)
+
+# add the air-box
+mesh.AddLine('z', [-SimBox[2]/2, max(mesh.GetLines('z'))+SimBox[2]/2 ])
+# create a smooth mesh between specified fixed mesh lines
+mesh.SmoothMeshLines('z', max_res, ratio=1.4)
+
+### Create the Geometry
+## * Create the metal helix using the wire primitive.
+## * Create a metal gorund plane as cylinder.
+# create a perfect electric conductor (PEC)
+helix_metal = CSX.AddMetal('helix' )
+
+ang = linspace(0,2*pi,21)
+coil_x = Helix_radius*cos(ang)
+coil_y = Helix_radius*sin(ang)
+coil_z = ang/2/pi*Helix_pitch
+
+Helix_x=np.array([])
+Helix_y=np.array([])
+Helix_z=np.array([])
+zpos = feed_heigth
+for n in range(Helix_turns-1):
+ Helix_x = r_[Helix_x, coil_x]
+ Helix_y = r_[Helix_y, coil_y]
+ Helix_z = r_[Helix_z ,coil_z+zpos]
+ zpos = zpos + Helix_pitch
+
+p = np.array([Helix_x, Helix_y, Helix_z])
+helix_metal.AddCurve(p)
+
+# create ground circular ground
+gnd = CSX.AddMetal( 'gnd' ) # create a perfect electric conductor (PEC)
+
+# add a box using cylindrical coordinates
+start = [0, 0, -0.1]
+stop = [0, 0, 0.1]
+gnd.AddCylinder(start, stop, radius=gnd_radius)
+
+# apply the excitation & resist as a current source
+start = [Helix_radius, 0, 0]
+stop = [Helix_radius, 0, feed_heigth]
+port = FDTD.AddLumpedPort(1 ,feed_R, start, stop, 'z', 1.0, priority=5)
+
+# nf2ff calc
+nf2ff = FDTD.CreateNF2FFBox(opt_resolution=[lambda0/15]*3)
+
+### Run the simulation
+if 0: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'helix.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+### Postprocessing & plotting
+freq = linspace( f0-fc, f0+fc, 501 )
+port.CalcPort(Sim_Path, freq)
+
+Zin = port.uf_tot / port.if_tot
+s11 = port.uf_ref / port.uf_inc
+
+## Plot the feed point impedance
+figure()
+plot( freq/1e6, real(Zin), 'k-', linewidth=2, label=r'$\Re(Z_{in})$' )
+grid()
+plot( freq/1e6, imag(Zin), 'r--', linewidth=2, label=r'$\Im(Z_{in})$' )
+title( 'feed point impedance' )
+xlabel( 'frequency (MHz)' )
+ylabel( 'impedance ($\Omega$)' )
+legend( )
+
+## Plot reflection coefficient S11
+figure()
+plot( freq/1e6, 20*log10(abs(s11)), 'k-', linewidth=2 )
+grid()
+title( 'reflection coefficient $S_{11}$' )
+xlabel( 'frequency (MHz)' )
+ylabel( 'reflection coefficient $|S_{11}|$' )
+
+### Create the NFFF contour
+## * calculate the far field at phi=0 degrees and at phi=90 degrees
+theta = arange(0.,180.,1.)
+phi = arange(-180,180,2)
+disp( 'calculating the 3D far field...' )
+
+nf2ff_res = nf2ff.CalcNF2FF(Sim_Path, f0, theta, phi, read_cached=True, verbose=True )
+
+Dmax_dB = 10*log10(nf2ff_res.Dmax[0])
+E_norm = 20.0*log10(nf2ff_res.E_norm[0]/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
+
+theta_HPBW = theta[ np.where(squeeze(E_norm[:,phi==0])<Dmax_dB-3)[0][0] ]
+
+## * Display power and directivity
+print('radiated power: Prad = {} W'.format(nf2ff_res.Prad[0]))
+print('directivity: Dmax = {} dBi'.format(Dmax_dB))
+print('efficiency: nu_rad = {} %'.format(100*nf2ff_res.Prad[0]/interp(f0, freq, port.P_acc)))
+print('theta_HPBW = {} °'.format(theta_HPBW))
+
+E_norm = 20.0*log10(nf2ff_res.E_norm[0]/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
+E_CPRH = 20.0*log10(np.abs(nf2ff_res.E_cprh[0])/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
+E_CPLH = 20.0*log10(np.abs(nf2ff_res.E_cplh[0])/np.max(nf2ff_res.E_norm[0])) + 10*log10(nf2ff_res.Dmax[0])
+
+## * Plot the pattern
+figure()
+plot(theta, E_norm[:,phi==0],'k-' , linewidth=2, label='$|E|$')
+plot(theta, E_CPRH[:,phi==0],'g--', linewidth=2, label='$|E_{CPRH}|$')
+plot(theta, E_CPLH[:,phi==0],'r-.', linewidth=2, label='$|E_{CPLH}|$')
+grid()
+xlabel('theta (deg)')
+ylabel('directivity (dBi)')
+title('Frequency: {} GHz'.format(nf2ff_res.freq[0]/1e9))
+legend()
+
+show()
+
diff --git a/openEMS/python/Tutorials/MSL_NotchFilter.py b/openEMS/python/Tutorials/MSL_NotchFilter.py
new file mode 100644
index 0000000..f036e0f
--- /dev/null
+++ b/openEMS/python/Tutorials/MSL_NotchFilter.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+"""
+ Microstrip Notch Filter Tutorial
+
+ Describtion at:
+ http://openems.de/doc/openEMS/Tutorials.html#microstrip-notch-filter
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.34+
+
+ (C) 2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+
+"""
+
+### 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 = False
+
+unit = 1e-6 # specify everything in um
+MSL_length = 50000
+MSL_width = 600
+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 0: # 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)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, 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()
diff --git a/openEMS/python/Tutorials/RCS_Sphere.py b/openEMS/python/Tutorials/RCS_Sphere.py
new file mode 100644
index 0000000..df801ce
--- /dev/null
+++ b/openEMS/python/Tutorials/RCS_Sphere.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+"""
+ Tutorials / radar cross section of a metal sphere
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.34+
+
+ (C) 2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+"""
+
+### Import Libraries
+import os, tempfile
+from pylab import *
+
+from CSXCAD import ContinuousStructure
+from openEMS import openEMS
+from openEMS.physical_constants import *
+from openEMS.ports import UI_data
+
+### Setup the simulation
+Sim_Path = os.path.join(tempfile.gettempdir(), 'RCS_Sphere')
+post_proc_only = False
+
+unit = 1e-3 # all length in mm
+
+sphere_rad = 200
+
+inc_angle = 0 #incident angle (to x-axis) in deg
+
+# size of the simulation box
+SimBox = 1200
+PW_Box = 750
+
+### Setup FDTD parameters & excitation function
+FDTD = openEMS(EndCriteria=1e-5)
+
+f_start = 50e6 # start frequency
+f_stop = 1000e6 # stop frequency
+f0 = 500e6
+FDTD.SetGaussExcite( 0.5*(f_start+f_stop), 0.5*(f_stop-f_start) )
+
+FDTD.SetBoundaryCond( ['PML_8', 'PML_8', 'PML_8', 'PML_8', 'PML_8', 'PML_8'] )
+
+### Setup Geometry & Mesh
+CSX = ContinuousStructure()
+FDTD.SetCSX(CSX)
+mesh = CSX.GetGrid()
+mesh.SetDeltaUnit(unit)
+
+#create mesh
+mesh.SetLines('x', [-SimBox/2, 0, SimBox/2])
+mesh.SmoothMeshLines('x', C0 / f_stop / unit / 20) # cell size: lambda/20
+mesh.SetLines('y', mesh.GetLines('x'))
+mesh.SetLines('z', mesh.GetLines('x'))
+
+### Create a metal sphere and plane wave source
+sphere_metal = CSX.AddMetal( 'sphere' ) # create a perfect electric conductor (PEC)
+sphere_metal.AddSphere(priority=10, center=[0, 0, 0], radius=sphere_rad)
+
+# plane wave excitation
+k_dir = [cos(inc_angle), sin(inc_angle), 0] # plane wave direction
+E_dir = [0, 0, 1] # plane wave polarization --> E_z
+
+pw_exc = CSX.AddExcitation('plane_wave', exc_type=10, exc_val=E_dir)
+pw_exc.SetPropagationDir(k_dir)
+pw_exc.SetFrequency(f0)
+
+start = np.array([-PW_Box/2, -PW_Box/2, -PW_Box/2])
+stop = -start
+pw_exc.AddBox(start, stop)
+
+# nf2ff calc
+nf2ff = FDTD.CreateNF2FFBox()
+
+### Run the simulation
+if 0: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'RCS_Sphere.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+### Postprocessing & plotting
+# get Gaussian pulse stength at frequency f0
+ef = UI_data('et', Sim_Path, freq=f0)
+
+Pin = 0.5*norm(E_dir)**2/Z0 * abs(ef.ui_f_val[0])**2
+#
+nf2ff_res = nf2ff.CalcNF2FF(Sim_Path, f0, 90, arange(-180, 180.1, 2))
+RCS = 4*pi/Pin[0]*nf2ff_res.P_rad[0]
+
+fig = figure()
+ax = fig.add_subplot(111, polar=True)
+ax.plot( nf2ff_res.phi, RCS[0], 'k-', linewidth=2 )
+ax.grid(True)
+
+# calculate RCS over frequency
+freq = linspace(f_start,f_stop,100)
+ef = UI_data( 'et', Sim_Path, freq ) # time domain/freq domain voltage
+Pin = 0.5*norm(E_dir)**2/Z0 * abs(np.array(ef.ui_f_val[0]))**2
+
+nf2ff_res = nf2ff.CalcNF2FF(Sim_Path, freq, 90, 180+inc_angle, outfile='back_nf2ff.h5')
+
+back_scat = np.array([4*pi/Pin[fn]*nf2ff_res.P_rad[fn][0][0] for fn in range(len(freq))])
+
+figure()
+plot(freq/1e6,back_scat, linewidth=2)
+grid()
+xlabel('frequency (MHz)')
+ylabel('RCS ($m^2$)')
+title('radar cross section')
+
+figure()
+semilogy(sphere_rad*unit/C0*freq,back_scat/(pi*sphere_rad*unit*sphere_rad*unit), linewidth=2)
+ylim([10^-2, 10^1])
+grid()
+xlabel('sphere radius / wavelength')
+ylabel('RCS / ($\pi a^2$)')
+title('normalized radar cross section')
+
+show() \ No newline at end of file
diff --git a/openEMS/python/Tutorials/Rect_Waveguide.py b/openEMS/python/Tutorials/Rect_Waveguide.py
new file mode 100644
index 0000000..5d38115
--- /dev/null
+++ b/openEMS/python/Tutorials/Rect_Waveguide.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+"""
+ Rectangular Waveguide Tutorial
+
+ Describtion at:
+ http://openems.de/doc/openEMS/Tutorials.html#rectangular-waveguide
+
+ Tested with
+ - python 3.4
+ - openEMS v0.0.34+
+
+ (C) 2015-2016 Thorsten Liebig <thorsten.liebig@gmx.de>
+
+"""
+
+### 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(), 'Rect_WG')
+
+post_proc_only = False
+unit = 1e-6; #drawing unit in um
+
+# waveguide dimensions
+# WR42
+a = 10700; #waveguide width
+b = 4300; #waveguide heigth
+length = 50000;
+
+# frequency range of interest
+f_start = 20e9;
+f_0 = 24e9;
+f_stop = 26e9;
+lambda0 = C0/f_0/unit;
+
+#waveguide TE-mode definition
+TE_mode = 'TE10';
+
+#targeted mesh resolution
+mesh_res = lambda0/30
+
+### Setup FDTD parameter & excitation function
+FDTD = openEMS(NrTS=1e4);
+FDTD.SetGaussExcite(0.5*(f_start+f_stop),0.5*(f_stop-f_start));
+
+# boundary conditions
+FDTD.SetBoundaryCond([0, 0, 0, 0, 3, 3]);
+
+### Setup geometry & mesh
+CSX = ContinuousStructure()
+FDTD.SetCSX(CSX)
+mesh = CSX.GetGrid()
+mesh.SetDeltaUnit(unit)
+
+mesh.AddLine('x', [0, a])
+mesh.AddLine('y', [0, b])
+mesh.AddLine('z', [0, length])
+
+## Apply the waveguide port
+ports = []
+start=[0, 0, 10*mesh_res];
+stop =[a, b, 15*mesh_res];
+mesh.AddLine('z', [start[2], stop[2]])
+ports.append(FDTD.AddRectWaveGuidePort( 0, start, stop, 'z', a*unit, b*unit, TE_mode, 1))
+
+start=[0, 0, length-10*mesh_res];
+stop =[a, b, length-15*mesh_res];
+mesh.AddLine('z', [start[2], stop[2]])
+ports.append(FDTD.AddRectWaveGuidePort( 1, start, stop, 'z', a*unit, b*unit, TE_mode))
+
+mesh.SmoothMeshLines('all', mesh_res, ratio=1.4)
+
+### Define dump box...
+Et = CSX.AddDump('Et', file_type=0, sub_sampling=[2,2,2])
+start = [0, 0, 0];
+stop = [a, b, length];
+Et.AddBox(start, stop);
+
+### Run the simulation
+if 0: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'rect_wg.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+### Postprocessing & plotting
+freq = linspace(f_start,f_stop,201)
+for port in ports:
+ port.CalcPort(Sim_Path, freq)
+
+s11 = ports[0].uf_ref / ports[0].uf_inc
+s21 = ports[1].uf_ref / ports[0].uf_inc
+ZL = ports[0].uf_tot / ports[0].if_tot
+ZL_a = ports[0].ZL # analytic waveguide impedance
+
+## Plot s-parameter
+figure()
+plot(freq*1e-6,20*log10(abs(s11)),'k-',linewidth=2, label='$S_{11}$')
+grid()
+plot(freq*1e-6,20*log10(abs(s21)),'r--',linewidth=2, label='$S_{21}$')
+legend();
+ylabel('S-Parameter (dB)')
+xlabel(r'frequency (MHz) $\rightarrow$')
+
+## Compare analytic and numerical wave-impedance
+figure()
+plot(freq*1e-6,real(ZL), linewidth=2, label='$\Re\{Z_L\}$')
+grid()
+plot(freq*1e-6,imag(ZL),'r--', linewidth=2, label='$\Im\{Z_L\}$')
+plot(freq*1e-6,ZL_a,'g-.',linewidth=2, label='$Z_{L, analytic}$')
+ylabel('ZL $(\Omega)$')
+xlabel(r'frequency (MHz) $\rightarrow$')
+legend()
+
+show()
diff --git a/openEMS/python/Tutorials/Simple_Patch_Antenna.py b/openEMS/python/Tutorials/Simple_Patch_Antenna.py
new file mode 100644
index 0000000..cd80f78
--- /dev/null
+++ b/openEMS/python/Tutorials/Simple_Patch_Antenna.py
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Dec 18 20:56:53 2015
+
+@author: thorsten
+"""
+
+### Import Libraries
+import os, tempfile
+from pylab import *
+
+from CSXCAD import ContinuousStructure
+from openEMS import openEMS
+from openEMS.physical_constants import *
+
+### General parameter setup
+Sim_Path = os.path.join(tempfile.gettempdir(), 'Simp_Patch')
+
+post_proc_only = False
+
+# patch width (resonant length) in x-direction
+patch_width = 32 #
+# patch length in y-direction
+patch_length = 40
+
+#substrate setup
+substrate_epsR = 3.38
+substrate_kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate_epsR
+substrate_width = 60
+substrate_length = 60
+substrate_thickness = 1.524
+substrate_cells = 4
+
+#setup feeding
+feed_pos = -6 #feeding position in x-direction
+feed_R = 50 #feed resistance
+
+# size of the simulation box
+SimBox = np.array([200, 200, 150])
+
+# setup FDTD parameter & excitation function
+f0 = 2e9 # center frequency
+fc = 1e9 # 20 dB corner frequency
+
+### FDTD setup
+## * Limit the simulation to 30k timesteps
+## * Define a reduced end criteria of -40dB
+FDTD = openEMS(NrTS=30000, EndCriteria=1e-4)
+FDTD.SetGaussExcite( f0, fc )
+FDTD.SetBoundaryCond( ['MUR', 'MUR', 'MUR', 'MUR', 'MUR', 'MUR'] )
+
+
+CSX = ContinuousStructure()
+FDTD.SetCSX(CSX)
+mesh = CSX.GetGrid()
+mesh.SetDeltaUnit(1e-3)
+mesh_res = C0/(f0+fc)/1e-3/20
+
+### Generate properties, primitives and mesh-grid
+#initialize the mesh with the "air-box" dimensions
+mesh.AddLine('x', [-SimBox[0]/2, SimBox[0]/2])
+mesh.AddLine('y', [-SimBox[1]/2, SimBox[1]/2] )
+mesh.AddLine('z', [-SimBox[2]/3, SimBox[2]*2/3] )
+
+# create patch
+patch = CSX.AddMetal( 'patch' ) # create a perfect electric conductor (PEC)
+start = [-patch_width/2, -patch_length/2, substrate_thickness]
+stop = [ patch_width/2 , patch_length/2, substrate_thickness]
+patch.AddBox(priority=10, start=start, stop=stop) # add a box-primitive to the metal property 'patch'
+FDTD.AddEdges2Grid(dirs='xy', properties=patch, metal_edge_res=mesh_res/2)
+
+# create substrate
+substrate = CSX.AddMaterial( 'substrate', epsilon=substrate_epsR, kappa=substrate_kappa)
+start = [-substrate_width/2, -substrate_length/2, 0]
+stop = [ substrate_width/2, substrate_length/2, substrate_thickness]
+substrate.AddBox( priority=0, start=start, stop=stop )
+
+# add extra cells to discretize the substrate thickness
+mesh.AddLine('z', linspace(0,substrate_thickness,substrate_cells+1))
+
+# create ground (same size as substrate)
+gnd = CSX.AddMetal( 'gnd' ) # create a perfect electric conductor (PEC)
+start[2]=0
+stop[2] =0
+gnd.AddBox(start, stop, priority=10)
+
+FDTD.AddEdges2Grid(dirs='xy', properties=gnd)
+
+# apply the excitation & resist as a current source
+start = [feed_pos, 0, 0]
+stop = [feed_pos, 0, substrate_thickness]
+port = FDTD.AddLumpedPort(1, feed_R, start, stop, 'z', 1.0, priority=5, edges2grid='xy')
+
+mesh.SmoothMeshLines('all', mesh_res, 1.4)
+
+# Add the nf2ff recording box
+nf2ff = FDTD.CreateNF2FFBox()
+
+### Run the simulation
+if 0: # debugging only
+ CSX_file = os.path.join(Sim_Path, 'simp_patch.xml')
+ if not os.path.exists(Sim_Path):
+ os.mkdir(Sim_Path)
+ CSX.Write2XML(CSX_file)
+ os.system(r'AppCSXCAD "{}"'.format(CSX_file))
+
+if not post_proc_only:
+ FDTD.Run(Sim_Path, verbose=3, cleanup=True)
+
+
+### Post-processing and plotting
+f = np.linspace(max(1e9,f0-fc),f0+fc,401)
+port.CalcPort(Sim_Path, f)
+s11 = port.uf_ref/port.uf_inc
+s11_dB = 20.0*np.log10(np.abs(s11))
+figure()
+plot(f/1e9, s11_dB, 'k-', linewidth=2, label='$S_{11}$')
+grid()
+legend()
+ylabel('S-Parameter (dB)')
+xlabel('Frequency (GHz)')
+
+idx = np.where((s11_dB<-10) & (s11_dB==np.min(s11_dB)))[0]
+if not len(idx)==1:
+ print('No resonance frequency found for far-field calulation')
+else:
+ f_res = f[idx[0]]
+ theta = np.arange(-180.0, 180.0, 2.0)
+ phi = [0., 90.]
+ nf2ff_res = nf2ff.CalcNF2FF(Sim_Path, f_res, theta, phi, center=[0,0,1e-3])
+
+ figure()
+ E_norm = 20.0*np.log10(nf2ff_res.E_norm[0]/np.max(nf2ff_res.E_norm[0])) + nf2ff_res.Dmax[0]
+ plot(theta, np.squeeze(E_norm[:,0]), 'k-', linewidth=2, label='xz-plane')
+ plot(theta, np.squeeze(E_norm[:,1]), 'r--', linewidth=2, label='yz-plane')
+ grid()
+ ylabel('Directivity (dBi)')
+ xlabel('Theta (deg)')
+ title('Frequency: {} GHz'.format(f_res/1e9))
+ legend()
+
+Zin = port.uf_tot/port.if_tot
+figure()
+plot(f/1e9, np.real(Zin), 'k-', linewidth=2, label='$\Re\{Z_{in}\}$')
+plot(f/1e9, np.imag(Zin), 'r--', linewidth=2, label='$\Im\{Z_{in}\}$')
+grid()
+legend()
+ylabel('Zin (Ohm)')
+xlabel('Frequency (GHz)')
+
+show()