def __init__(self, name, fmin=0, fmax=50e9, NrTS=1e6, EndCriteria=1e-6, #BC = {xmin xmax ymin ymax zmin zmax}; boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'], fsteps = 1601 ): self.FDTD = openEMS(NrTS=NrTS, EndCriteria=EndCriteria) self.FDTD.SetGaussExcite((fmin+fmax)/2.0, (fmax-fmin)/2.0) self.FDTD.SetBoundaryCond(boundaries) self.CSX = ContinuousStructure() self.FDTD.SetCSX(self.CSX) self.mesh = self.CSX.GetGrid() self.mesh.SetDeltaUnit(1.0) # specify everything in m self.fmin = fmin self.fmax = fmax self.fsteps = fsteps self.objects = {} self.name = name self.ports = [] self.excitation_port = 0 self.excite_ports = [1] self.via_offset_x = 0.0 self.via_offset_y = 0.0 self.xgrid = None # for plot self.ygrid = None self.legend_location = 2 # upper left self.options = '' self.name_count = 0 self.resolution = 0.0001
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)
class OpenEMS: def __init__(self, name, fmin=0, fmax=50e9, NrTS=1e6, EndCriteria=1e-6, #BC = {xmin xmax ymin ymax zmin zmax}; boundaries = ['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'], fsteps = 1601 ): self.FDTD = openEMS(NrTS=NrTS, EndCriteria=EndCriteria) self.FDTD.SetGaussExcite((fmin+fmax)/2.0, (fmax-fmin)/2.0) self.FDTD.SetBoundaryCond(boundaries) self.CSX = ContinuousStructure() self.FDTD.SetCSX(self.CSX) self.mesh = self.CSX.GetGrid() self.mesh.SetDeltaUnit(1.0) # specify everything in m self.fmin = fmin self.fmax = fmax self.fsteps = fsteps self.objects = {} self.name = name self.ports = [] self.excitation_port = 0 self.excite_ports = [1] self.via_offset_x = 0.0 self.via_offset_y = 0.0 self.xgrid = None # for plot self.ygrid = None self.legend_location = 2 # upper left self.options = '' self.name_count = 0 self.resolution = 0.0001 def AddPort(self, start, stop, direction, z): return Port(self, start, stop, direction, z) def get_name(self, name): if name: return name self.name_count += 1 return "pad_{}".format(self.name_count) def write_kicad(self, fpname, mirror=""): g = kicadfpwriter.Generator(fpname) g.mirror = mirror for object in self.objects: g.drill = 0 self.objects[object].generate_kicad(g) fp = g.finish() with open(self.name+".kicad_mod", "w") as f: f.write(fp) def run_openems(self, options='view solve', z=50, initialize=True): cwd = os.getcwd() basename = cwd + '/' + self.name simpath = r'/tmp/openems_data' if not os.path.exists(simpath): os.mkdir(simpath) for object in self.objects: self.objects[object].generate_octave() import collections if not isinstance(self.resolution, collections.Sequence): self.resolution = np.ones(3) * self.resolution ratio = 1.5 for i in range(3): if self.resolution[i] is not None: self.mesh.SmoothMeshLines('xyz'[i], self.resolution[i], ratio) if 'view' in options: CSX_file = simpath + '/csx.xml' self.CSX.Write2XML(CSX_file) os.system(r'AppCSXCAD "{}"'.format(CSX_file)) if 'solve' in options: self.FDTD.Run(simpath, verbose=3, cleanup=True) f = np.linspace(self.fmin, self.fmax, self.fsteps) for p in self.ports: p.port.CalcPort(simpath, f, ref_impedance = z) nports = len(self.ports) s = [] for p in range(nports): s.append(self.ports[p].port.uf_ref / self.ports[0].port.uf_inc) if nports < 1: return self.frequencies = f fig, ax = matplotlib.pyplot.subplots() s11 = s[0] if nports == 1: save_s1p(f, s11, basename+".s1p", z=z) if nports > 1: s21 = s[1] ax.plot(f/1e9, 20*np.log10(np.abs(s21)), label = 'dB(s21)') save_s2p_symmetric(f, s11, s21, basename+".s2p", z=z) ax.plot(f/1e9, 20*np.log10(np.abs(s11)), label = 'dB(s11)') if nports > 2: s31 = s[2] ax.plot(f/1e9, 20*np.log10(np.abs(s31)), label = 'dB(s31)') if nports > 3: s41 = s[3] ax.plot(f/1e9, 20*np.log10(np.abs(s41)), label = 'dB(s41)') ax.set_xlabel('Frequency (GHz)') ax.set_ylabel('dB') if hasattr(self.xgrid, "__len__"): ax.set_xticks(self.xgrid) if hasattr(self.ygrid, "__len__"): ax.set_yticks(self.ygrid) ax.grid(True) fig.tight_layout() ax.legend(loc=self.legend_location) matplotlib.pyplot.savefig(basename+".png") matplotlib.pyplot.savefig(basename+".svg") matplotlib.pyplot.savefig(basename+".pdf") matplotlib.pyplot.show()
# 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]
# 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)
class OpenEMS: def __init__( self, name, fmin=0, fmax=50e9, NrTS=1e6, EndCriteria=1e-6, #BC = {xmin xmax ymin ymax zmin zmax}; boundaries=['PEC', 'PEC', 'PEC', 'PEC', 'PEC', 'PEC'], fsteps=1601): self.FDTD = openEMS(NrTS=NrTS, EndCriteria=EndCriteria) self.FDTD.SetGaussExcite((fmin + fmax) / 2.0, (fmax - fmin) / 2.0) self.FDTD.SetBoundaryCond(boundaries) self.CSX = ContinuousStructure() self.FDTD.SetCSX(self.CSX) self.mesh = self.CSX.GetGrid() self.mesh.SetDeltaUnit(1.0) # specify everything in m self.fmin = fmin self.fmax = fmax self.fsteps = fsteps self.objects = {} self.name = name self.ports = [] self.excitation_port = 0 self.excite_ports = [1] self.metalloss = False self.via_offset_x = 0.0 self.via_offset_y = 0.0 self.xgrid = None # for plot self.ygrid = None self.legend_location = 2 # upper left self.options = '' self.name_count = 0 self.resolution = 0.0001 def AddPort(self, start, stop, direction, z): return Port(self, start, stop, direction, z) def add_resistor(self, name, origin=np.array([0, 0, 0]), direction='x', value=100.0, invert=False, priority=9, dielectric=None, metal=None, element_down=False, size='0201'): """ currently only supports 'x', 'y' for direction """ element = LumpedElement(self, name, element_type='R', value=value, direction=direction) # resistor end caps start = np.array([-0.15 * mm, -0.3 * mm, 0]) stop = np.array([0.15 * mm, -0.25 * mm / 2, 0.25 * mm]) if size == '0402': start = np.array([-0.25 * mm, -0.5 * mm, 0]) stop = np.array([0.25 * mm, -0.5 * mm / 2, 0.35 * mm]) for m in [np.array([1, -1, 1]), np.array([1, 1, 1])]: metal.AddBox(origin + start * m, origin + stop * m, priority=priority, padname=None) # resistor body start = np.array([-0.15, -0.27, 0.02]) * mm stop = np.array([0.15, 0.27, 0.23]) * mm if size == '0402': start = np.array([-0.25, -0.47, 0.02]) * mm stop = np.array([0.25, 0.47, 0.33]) * mm body = dielectric.AddBox(origin + start, origin + stop, priority=priority + 1, padname=None) # resistor element if element_down: zoff = 0.0 else: zoff = 0.33 if size == '0402' else 0.23 start = np.array([-0.1, -0.25 / 2, 0 + zoff]) * mm stop = np.array([0.1, 0.25 / 2, 0.02 + zoff]) * mm if size == '0402': start = np.array([-0.2, -0.25, 0 + zoff]) * mm stop = np.array([0.2, 0.25, 0.02 + zoff]) * mm start += origin stop += origin element = element.AddBox(start, stop, priority=priority + 1, padname=None) # reposition if invert: cap1.mirror('z') cap2.mirror('z') body.mirror('z') element.mirror('z') if not 'y' in direction: cap1.rotate_ccw_90() cap2.rotate_ccw_90() body.rotate_ccw_90() element.rotate_ccw_90() def get_name(self, name): if name: return name self.name_count += 1 return "pad_{}".format(self.name_count) def write_kicad(self, fpname, mirror=""): g = kicadfpwriter.Generator(fpname) g.mirror = mirror for object in self.objects: g.drill = 0 self.objects[object].generate_kicad(g) fp = g.finish() with open(self.name + ".kicad_mod", "w") as f: f.write(fp) def run_openems(self, options='view solve', z=50): cwd = os.getcwd() basename = cwd + '/' + self.name simpath = r'/tmp/openems_data' if not os.path.exists(simpath): os.mkdir(simpath) for object in self.objects: self.objects[object].generate_octave() import collections if not isinstance(self.resolution, collections.Sequence): self.resolution = np.ones(3) * self.resolution ratio = 1.5 self.mesh.SmoothMeshLines('x', self.resolution[0], ratio) self.mesh.SmoothMeshLines('y', self.resolution[1], ratio) self.mesh.SmoothMeshLines('z', self.resolution[2], ratio) if 'view' in options: CSX_file = simpath + '/csx.xml' self.CSX.Write2XML(CSX_file) os.system(r'AppCSXCAD "{}"'.format(CSX_file)) if 'solve' in options: self.FDTD.Run(simpath, verbose=3, cleanup=True) f = np.linspace(self.fmin, self.fmax, self.fsteps) for p in self.ports: p.port.CalcPort(simpath, f, ref_impedance=z) nports = len(self.ports) s = [] for p in range(nports): s.append(self.ports[p].port.uf_ref / self.ports[0].port.uf_inc) if nports < 1: return self.frequencies = f fig, ax = matplotlib.pyplot.subplots() s11 = s[0] if nports == 1: save_s1p(f, s11, basename + ".s1p", z=z) if nports > 1: s21 = s[1] ax.plot(f / 1e9, 20 * np.log10(np.abs(s21)), label='dB(s21)') save_s2p_symmetric(f, s11, s21, basename + ".s2p", z=z) ax.plot(f / 1e9, 20 * np.log10(np.abs(s11)), label='dB(s11)') if nports > 2: s31 = s[2] ax.plot(f / 1e9, 20 * np.log10(np.abs(s31)), label='dB(s31)') if nports > 3: s41 = s[3] ax.plot(f / 1e9, 20 * np.log10(np.abs(s41)), label='dB(s41)') ax.set_xlabel('Frequency (GHz)') ax.set_ylabel('dB') if hasattr(self.xgrid, "__len__"): ax.set_xticks(self.xgrid) if hasattr(self.ygrid, "__len__"): ax.set_yticks(self.ygrid) ax.grid(True) fig.tight_layout() ax.legend(loc=self.legend_location) matplotlib.pyplot.savefig(basename + ".png") matplotlib.pyplot.savefig(basename + ".svg") matplotlib.pyplot.savefig(basename + ".pdf") matplotlib.pyplot.show() print(len(self.frequencies)) print(s)
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])
#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,