Skip to content

Commit 8ee535b

Browse files
Update Tutorials (#202)
Updated all tutorials to use matplotlib.pyplot instead of pylab Restored the formatted labels/captions, with proper escape characters
1 parent e25d547 commit 8ee535b

7 files changed

Lines changed: 236 additions & 179 deletions

File tree

python/Tutorials/Bent_Patch_Antenna.py

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
- openEMS v0.0.35+
88
99
(c) 2016-2023 Thorsten Liebig <thorsten.liebig@gmx.de>
10+
04-Jan-2026: modified to use matplotlib.pyplot instead of pylab
1011
1112
"""
1213

1314
### Import Libraries
1415
import os, tempfile
15-
from pylab import *
16+
import numpy as np
17+
import matplotlib.pyplot as plt # pip install matplotlib
1618
from mpl_toolkits.mplot3d import Axes3D
1719

1820
from CSXCAD import CSXCAD
@@ -39,7 +41,7 @@
3941

4042
#substrate setup
4143
substrate_epsR = 3.38
42-
substrate_kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate_epsR
44+
substrate_kappa = 1e-3 * 2*np.pi*f0 * EPS0*substrate_epsR
4345
substrate_width = 80
4446
substrate_length = 90
4547
substrate_thickness = 1.524
@@ -109,7 +111,7 @@
109111
### Finalize the Mesh
110112
# add the simulation domain size
111113
mesh.AddLine('r', patch_radius+np.array([-20, SimBox_rad]))
112-
mesh.AddLine('a', [-0.75*pi, 0.75*pi])
114+
mesh.AddLine('a', [-0.75*np.pi, 0.75*np.pi])
113115
mesh.AddLine('z', [-SimBox_height/2, SimBox_height/2])
114116

115117
# add some lines for the substrate
@@ -145,23 +147,27 @@
145147
s11 = port.uf_ref/port.uf_inc
146148
s11_dB = 20.0*np.log10(np.abs(s11))
147149

148-
figure()
149-
plot(f/1e9, s11_dB)
150-
grid()
151-
ylabel('s11 (dB)')
152-
xlabel('frequency (GHz)')
150+
fig, axis = plt.subplots(num="S11", tight_layout=True)
151+
axis.plot(f/1e6, s11_dB, 'k-', linewidth=2, label='dB(S11)')
152+
axis.grid()
153+
axis.set_xmargin(0)
154+
axis.set_xlabel('Frequency (MHz)')
155+
axis.set_ylabel('S11 (dB)')
156+
axis.set_title("Input matching")
157+
axis.legend()
153158

154159
P_in = 0.5*np.real(port.uf_tot * np.conj(port.if_tot)) # antenna feed power
155160

156161
# plot feed point impedance
157-
figure()
158-
plot( f/1e6, real(Zin), 'k-', linewidth=2, label=r'$\Re(Z_{in})$' )
159-
grid()
160-
plot( f/1e6, imag(Zin), 'r--', linewidth=2, label=r'$\Im(Z_{in})$' )
161-
title( 'feed point impedance' )
162-
xlabel( 'frequency (MHz)' )
163-
ylabel( 'impedance ($\Omega$)' )
164-
legend( )
162+
fig, axis = plt.subplots(num="Zin", tight_layout=True)
163+
axis.plot(f/1e6, np.real(Zin), 'k-', linewidth=2, label='$\\Re(Z_{in})$' )
164+
axis.plot(f/1e6, np.imag(Zin), 'r--', linewidth=2, label='$\\Im(Z_{in})$')
165+
axis.grid()
166+
axis.set_xmargin(0)
167+
axis.set_xlabel('Frequency (MHz)')
168+
axis.set_ylabel('Impedance ($\\Omega$)')
169+
axis.set_title("Input Impedance")
170+
axis.legend()
165171

166172

167173
idx = np.where((s11_dB<-10) & (s11_dB==np.min(s11_dB)))[0]
@@ -173,30 +179,38 @@
173179
print("Calculate NF2FF")
174180
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')
175181

176-
figure(figsize=(15, 7))
177-
ax = subplot(121, polar=True)
178-
E_norm = 20.0*np.log10(nf2ff_res_phi0.E_norm/np.max(nf2ff_res_phi0.E_norm)) + 10.0*np.log10(nf2ff_res_phi0.Dmax)
179-
ax.plot(np.deg2rad(theta), 10**(np.squeeze(E_norm)/20), linewidth=2, label='xz-plane')
180-
ax.grid(True)
181-
ax.set_xlabel('theta (deg)')
182-
ax.set_theta_zero_location('N')
183-
ax.set_theta_direction(-1)
184-
ax.legend(loc=3)
182+
# --- First subplot (xz-plane) ---
183+
fig = plt.figure(figsize=(15, 7), tight_layout=True)
184+
185+
ax1 = fig.add_subplot(1, 2, 1, polar=True)
186+
E_norm_xz = 20.0*np.log10(nf2ff_res_phi0.E_norm/np.max(nf2ff_res_phi0.E_norm)) + 10.0*np.log10(nf2ff_res_phi0.Dmax)
187+
ax1.plot(np.deg2rad(theta), 10**(np.squeeze(E_norm_xz)/20), linewidth=2, label='xz-plane')
188+
ax1.grid(True)
189+
ax1.set_xlabel('theta (deg)')
190+
ax1.set_theta_zero_location('N')
191+
ax1.set_theta_direction(-1)
192+
ax1.legend(loc=3) # lower left
185193

186194
phi = theta
187195
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')
188196

189-
ax = subplot(122, polar=True)
190-
E_norm = 20.0*np.log10(nf2ff_res_theta90.E_norm/np.max(nf2ff_res_theta90.E_norm)) + 10.0*np.log10(nf2ff_res_theta90.Dmax)
191-
ax.plot(np.deg2rad(phi), 10**(np.squeeze(E_norm)/20), linewidth=2, label='xy-plane')
192-
ax.grid(True)
193-
ax.set_xlabel('phi (deg)')
194-
suptitle('Bent Patch Antenna Pattern\nFrequency: {} GHz'.format(f_res/1e9), fontsize=14)
195-
ax.legend(loc=3)
197+
# --- Second subplot (xy-plane) ---
198+
ax2 = fig.add_subplot(1, 2, 2, polar=True)
199+
E_norm_xy = 20.0*np.log10(nf2ff_res_theta90.E_norm/np.max(nf2ff_res_theta90.E_norm)) + 10.0*np.log10(nf2ff_res_theta90.Dmax)
200+
201+
ax2.plot(np.deg2rad(phi), 10**(np.squeeze(E_norm_xy)/20), linewidth=2, label='xy-plane')
202+
ax2.grid(True)
203+
ax2.set_xlabel('phi (deg)')
204+
ax2.legend(loc=3)
205+
206+
# --- Figure title ---
207+
fig.suptitle('Bent Patch Antenna Pattern\nFrequency: {} GHz'.format(f_res/1e9), fontsize=14)
196208

197209
print( 'radiated power: Prad = {:.2e} Watt'.format(nf2ff_res_theta90.Prad[0]))
198210
print( 'directivity: Dmax = {:.1f} ({:.1f} dBi)'.format(nf2ff_res_theta90.Dmax[0], 10*np.log10(nf2ff_res_theta90.Dmax[0])))
199-
print( 'efficiency: nu_rad = {:.1f} %'.format(100*nf2ff_res_theta90.Prad[0]/real(P_in[idx[0]])))
211+
print( 'efficiency: nu_rad = {:.1f} %'.format(100*nf2ff_res_theta90.Prad[0]/np.real(P_in[idx[0]])))
212+
200213

201-
show()
214+
# show all plots
215+
plt.show()
202216

python/Tutorials/CRLH_Extraction.py

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
- openEMS v0.0.35+
88
99
(c) 2016-2023 Thorsten Liebig <thorsten.liebig@gmx.de>
10-
10+
04-Jan-2026: modified to use matplotlib.pyplot instead of pylab
1111
"""
1212

1313

1414
### Import Libraries
1515
import os, tempfile
16-
from pylab import *
16+
import numpy as np
17+
import matplotlib.pyplot as plt # pip install matplotlib
1718

1819
from CSXCAD import ContinuousStructure
1920
from openEMS import openEMS
@@ -45,7 +46,7 @@ def setEdgeResolution(self, res):
4546
def createCell(self, translate = [0,0,0]):
4647
mesh = [None,None,None]
4748
third_res = self.edge_resolution/3
48-
translate = array(translate)
49+
translate = np.array(translate)
4950
start = [-self.LL/2 , -self.LW/2, self.Top] + translate
5051
stop = [-self.GLT/2, self.LW/2, self.Top] + translate
5152
box = self.props['metal_top'].AddBox(start, stop, priority=10)
@@ -113,16 +114,16 @@ def createCell(self, translate = [0,0,0]):
113114
FDTD.SetBoundaryCond( ['PML_8', 'PML_8', 'MUR', 'MUR', 'PEC', 'PML_8'] )
114115

115116
### Setup a basic mesh and create the CRLH unit cell
116-
resolution = C0/(f_stop*sqrt(max(substrate_epsr)))/unit /30 # resolution of lambda/30
117+
resolution = C0/(f_stop*np.sqrt(max(substrate_epsr)))/unit /30 # resolution of lambda/30
117118
CRLH.setEdgeResolution(resolution/4)
118119

119120
mesh.SetLines('x', [-feed_length-CRLH.LL/2, 0, feed_length+CRLH.LL/2])
120121
mesh.SetLines('y', [-30000, 0, 30000])
121122

122-
substratelines = cumsum(substrate_thickness)
123+
substratelines = np.cumsum(substrate_thickness)
123124
mesh.SetLines('z', [0, 20000])
124-
mesh.AddLine('z', cumsum(substrate_thickness))
125-
mesh.AddLine('z', linspace(substratelines[-2],substratelines[-1],4))
125+
mesh.AddLine('z', np.cumsum(substrate_thickness))
126+
mesh.AddLine('z', np.linspace(substratelines[-2],substratelines[-1],4))
126127

127128
# create the CRLH unit cell (will define additional fixed mesh lines)
128129
mesh_hint = CRLH.createCell()
@@ -169,21 +170,22 @@ def createCell(self, translate = [0,0,0]):
169170
FDTD.Run(Sim_Path, cleanup=True)
170171

171172
### Post-Processing
172-
f = linspace( f_start, f_stop, 1601 )
173+
f = np.linspace( f_start, f_stop, 1601 )
173174
for p in port:
174175
p.CalcPort( Sim_Path, f, ref_impedance = 50, ref_plane_shift = feed_length)
175176

176177
# calculate and plot scattering parameter
177178
s11 = port[0].uf_ref / port[0].uf_inc
178179
s21 = port[1].uf_ref / port[0].uf_inc
179180

180-
plot(f/1e9,20*log10(abs(s11)),'k-' , linewidth=2, label='$S_{11}$')
181-
plot(f/1e9,20*log10(abs(s21)),'r--', linewidth=2, label='$S_{21}$')
182-
grid()
183-
legend(loc=3)
184-
ylabel('S-Parameter (dB)')
185-
xlabel('frequency (GHz)')
186-
ylim([-40, 2])
181+
fig, axis = plt.subplots(num="S11", tight_layout=True)
182+
axis.plot(f/1e9, 20*np.log10(abs(s11)), 'k-', linewidth=2, label='S11')
183+
axis.plot(f/1e9, 20*np.log10(abs(s21)), 'r--', linewidth=2, label='S21')
184+
axis.grid()
185+
axis.set_xmargin(0)
186+
axis.set_xlabel('Frequency (GHz)')
187+
axis.set_ylabel('S-Parameter (dB)')
188+
axis.legend()
187189

188190
### Extract CRLH parameter form ABCD matrix
189191
A = ((1+s11)*(1-s11) + s21*s21)/(2*s21)
@@ -192,41 +194,44 @@ def createCell(self, translate = [0,0,0]):
192194
Y = C
193195
Z = 2*(A-1)/C
194196

195-
iZ = imag(Z)
196-
iY = imag(Y)
197+
iZ = np.imag(Z)
198+
iY = np.imag(Y)
197199

198-
fse = interp(0, iZ, f)
199-
fsh = interp(0, iY, f)
200+
fse = np.interp(0, iZ, f)
201+
fsh = np.interp(0, iY, f)
200202

201203
df = f[1]-f[0]
202204
fse_idx = np.where(f>fse)[0][0]
203205
fsh_idx = np.where(f>fsh)[0][0]
204206

205-
LR = 0.5*(iZ[fse_idx]-iZ[fse_idx-1])/(2*pi*df)
206-
CL = 1/(2*pi*fse)**2/LR
207+
LR = 0.5*(iZ[fse_idx]-iZ[fse_idx-1])/(2*np.pi*df)
208+
CL = 1/(2*np.pi*fse)**2/LR
207209

208-
CR = 0.5*(iY[fsh_idx]-iY[fsh_idx-1])/(2*pi*df)
209-
LL = 1/(2*pi*fsh)**2/CR
210+
CR = 0.5*(iY[fsh_idx]-iY[fsh_idx-1])/(2*np.pi*df)
211+
LL = 1/(2*np.pi*fsh)**2/CR
210212

211213
print(' Series tank: CL = {:.2f} pF, LR = {:.2f} nH -> f_se = {:.2f} GHz '.format(CL*1e12, LR*1e9, fse*1e-9))
212214
print(' Shunt tank: CR = {:.2f} pF, LL = {:.2f} nH -> f_sh = {:.2f} GHz '.format(CR*1e12, LL*1e9, fsh*1e-9))
213215

214216
### Calculate analytical wave-number of an inf-array of cells
215-
w = 2*pi*f
216-
wse = 2*pi*fse
217-
wsh = 2*pi*fsh
218-
beta_calc = real(arccos(1-(w**2-wse**2)*(w**2-wsh**2)/(2*w**2/CR/LR)))
217+
w = 2*np.pi*f
218+
wse = 2*np.pi*fse
219+
wsh = 2*np.pi*fsh
220+
beta_calc = np.real(np.arccos(1-(w**2-wse**2)*(w**2-wsh**2)/(2*w**2/CR/LR)))
219221

220222
# plot
221-
figure()
222-
beta = -angle(s21)/CRLH.LL/unit
223-
plot(abs(beta)*CRLH.LL*unit/pi,f*1e-9,'k-', linewidth=2, label=r'$\beta_{CRLH,\ 1\ cell}$' )
224-
grid()
225-
plot(beta_calc/pi,f*1e-9,'c--', linewidth=2, label=r'$\beta_{CRLH,\ \infty\ cells}$')
226-
plot(real(port[1].beta)*CRLH.LL*unit/pi,f*1e-9,'g-', linewidth=2, label=r'$\beta_{MSL}$')
227-
ylim([1, 6])
228-
xlabel(r'$|\beta| p / \pi$')
229-
ylabel('frequency (GHz)')
230-
legend(loc=2)
231-
232-
show()
223+
beta = -np.angle(s21)/CRLH.LL/unit
224+
fig, axis = plt.subplots(num="beta", tight_layout=True)
225+
axis.plot(np.abs(beta)*CRLH.LL*unit/np.pi,f*1e-9, 'k-', linewidth=2, label=r'$\beta_{CRLH,\ 1\ cell}$')
226+
axis.plot(beta_calc/np.pi,f*1e-9,'c--', linewidth=2, label=r'$\beta_{CRLH,\ \infty\ cells}$')
227+
axis.plot(np.real(port[1].beta)*CRLH.LL*unit/np.pi,f*1e-9,'g-', linewidth=2, label=r'$\beta_{MSL}$')
228+
axis.grid()
229+
# axis.set_xmargin(0)
230+
axis.set_ylim([1, 6])
231+
axis.set_xlabel(r'$|\beta| p / \pi$')
232+
axis.set_ylabel('S-Parameter (dB)')
233+
axis.legend(loc='upper left')
234+
235+
236+
# show all plots
237+
plt.show()

0 commit comments

Comments
 (0)