-
I just started using openEMS, and I am trying to simulate the far field of a Yagi antenna that I have simulated before in xnec2c. In case the version matters, I am building openEMS from git. I started by modifying the helix tutorial in python, and this is the output that I get. Notice the warning about the excitation box being unused, and the "energy" value remains zero; I am sure that is not a coincidence... I read here that unused parameters are related to meshing, so any meshing hints that you can give me in this situation would be greatly appreciated. The frequency is much lower than in other tutorial simulations, this has a 2-meter wavelength at 146 MHz. The python script is pasted below. Please notice the comments in the code labeled "HELP" and let me know what the best practices are for those values. Thanks so much!
### 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(), 'Yagi_Ant')
post_proc_only = False
unit = 1e-3 # all length in mm
# 2m band
f0 = 146e6 # center frequency, frequency of interest!
lambda0 = round(C0/f0/unit) # wavelength in mm
fc = 100e6 # 20 dB corner frequency **** HELP: is this right? Do I need to calculate 146e6 less 20dB?
feed_R = 50
### 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
# Yagi dimensions
lengths = [ 1038., 995., 959., 949., 935. ]
spaces = [ 0, 280, 150, 520, 530 ]
space_off = -sum(spaces)/2 # center the antenna in the Z dimension
excitation_length = 5 # mm
# HELP: This is the size of the "wire" in mm. Can it be zero, ie, a line? I had it set to 1mm (ie, 12 AWG)
# but was concerned about mesh size issues so I set it to 5mm for testing.
trace_width = 5
# size of the simulation box
SimBox = np.array([max(lengths)*2, lambda0, sum(spaces)*2])
### 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]/2, SimBox[2]/2])
# 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'))
mesh.SetLines('z', mesh.GetLines('x'))
# HELP: do I really need a new piece of metal (from AddMetal()) for each element?
for i in range(len(lengths)):
space_off += spaces[i]
yagi = CSX.AddMetal('yagi{}'.format(i))
if i == 1: # driver
# HELP: I added +/-1 to excitation_length to make sure the excitation wire
# entered the yagi box, but that didn't seem to change anything. Should it exactly
# abut the yagi element box?
ex_start = [ -excitation_length-1, 0, space_off ]
ex_stop = [ excitation_length+1, 0, space_off ]
yagi_gnd = CSX.AddMetal('gnd')
yagi_gnd.AddBox(priority=10,
start = [ -lengths[i]/2, -trace_width/2, space_off - trace_width/2 ],
stop = [-excitation_length, trace_width/2, space_off + trace_width/2 ],
);
FDTD.AddEdges2Grid(dirs='all', properties=yagi_gnd, metal_edge_res=max_res)
yagi.AddBox(priority=10,
start = [excitation_length, -trace_width/2, space_off - trace_width/2 ],
stop = [ lengths[i]/2, trace_width/2, space_off + trace_width/2 ],
);
else:
yagi.AddBox(priority=10,
start = [-lengths[i]/2, -trace_width/2, space_off - trace_width/2 ],
stop = [ lengths[i]/2 , trace_width/2, space_off + trace_width/2 ],
);
# HELP: Is this the correct way to add meshing to the "yagi" element
FDTD.AddEdges2Grid(dirs='all', properties=yagi, metal_edge_res=max_res)
port = FDTD.AddLumpedPort(1 ,feed_R, ex_start, ex_stop, 'x', 1.0, priority=5)
## Below here is unchanged from the Helix example
# nf2ff calc
nf2ff = FDTD.CreateNF2FFBox(opt_resolution=[lambda0/15]*3)
### Run the simulation
if 1: # debugging only
CSX_file = os.path.join(Sim_Path, 'yagi.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)
### 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() |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 26 replies
-
I did not yet have the time to look at your script in detail, but yes unused objects are usually not good and in this case an unused excitation box indeed results in a simulation without any excitation and thus just iterating over zeros...
Use the AppCSXCAD tool to review your structure and mesh... |
Beta Was this translation helpful? Give feedback.
-
I also like to point out that
is an immediate red flag that shows the existence of the problem. An energy of |
Beta Was this translation helpful? Give feedback.
-
Attached is a rough fixed version of your script. It's not pretty or efficient, but gets some initial results. I think the mesh still needs some improvements and for a first step I went with zero thickness wires... Should be good enough for this low frequencies... |
Beta Was this translation helpful? Give feedback.
-
I'm curious why you want to use openEMS for this task? NEC codes are much more efficient for such wire-only structures. FDTD would be a good fit if you need to include 2D planes or 3D objects. |
Beta Was this translation helpful? Give feedback.
Attached is a rough fixed version of your script. It's not pretty or efficient, but gets some initial results. I think the mesh still needs some improvements and for a first step I went with zero thickness wires... Should be good enough for this low frequencies...
But I think the meshing of the ends of the wires could be better...
yagi.zip