) Mesh( sim=sim, metal_res=1 / 80, nonmetal_res=1 / 10, min_lines=9, expand_bounds=((0, 0), (0, 0), (10, 40)), ) sim.run(csx=False) return np.abs(sim.ports[0].impedance(freq=ref_freq)) # res = optimize_parameter( # func, # start=0.25 * trace_width, # step=0.25 * trace_width, # tol=0.2, # max_steps=20, # display_progress=True, # ) # print("Minimum ground gap: {}".format(res)) gaps = np.arange(0.5 * trace_width, 3 * trace_width, 0.1 * trace_width) sim_vals = sweep(func, gaps, processes=11) norm_gaps = gaps / trace_width print_table(data=[norm_gaps, sim_vals], col_names=["gap", "z0"], prec=[4, 4])
radius=coax_rad, core_radius=core_rad, shield_thickness=mil_to_mm(5), dielectric=coax_dielectric, propagation_axis=Axis("x", direction=-1), port_number=2, ref_impedance=50, ) mesh = Mesh( sim=sim, metal_res=1 / 120, nonmetal_res=1 / 10, min_lines=5, expand_bounds=((0, 0), (0, 0), (20, 20)), ) box = mesh.sim_box(include_pml=False) field = FieldDump(sim=sim, box=box, dump_type=DumpType.efield_time) sim.run() sim.view_field() s11 = sim.s_param(1, 1) s21 = sim.s_param(2, 1) print_table( data=[sim.freq / 1e9, s11, s21], col_names=["freq", "s11", "s21"], prec=[4, 4, 4], )
dump = FieldDump( sim=sim, box=Box3( Coordinate3(-pcb_len / 2, -pcb_width / 2, 0), Coordinate3(pcb_len / 2, pcb_width / 2, 0), ), ) mesh = Mesh( sim=sim, metal_res=1 / 120, nonmetal_res=1 / 40, smooth=(1.1, 1.5, 1.5), min_lines=25, expand_bounds=((0, 0), (24, 24), (24, 24)), ) if os.getenv("_PYEMS_PYTEST"): sys.exit(0) sim.run() sim.view_field() print_table( data=[sim.freq / 1e9, np.abs(sim.ports[0].impedance()), sim.s_param(1, 1)], col_names=["freq", "z0", "s11"], prec=[4, 4, 4], )
layers=range(3), ) Microstrip( pcb=pcb, position=Coordinate2(0, 0), length=pcb_len, width=trace_width, propagation_axis=Axis("x"), gnd_gap=(gnd_gap, gnd_gap), port_number=1, excite=True, ref_impedance=50, ) Mesh( sim=sim, metal_res=1 / 80, nonmetal_res=1 / 10, min_lines=9, expand_bounds=((0, 0), (0, 0), (10, 40)), ) sim.run() print_table( data=[freq / 1e9, np.abs(sim.ports[0].impedance())], col_names=["freq", "z0"], prec=[2, 4], )
def func(params: List[float]): """ """ cutout_width = params[0] sim = Simulation(freq=freq, unit=unit, sim_dir=None) pcb = PCB( sim=sim, pcb_prop=pcb_prop, length=pcb_len, width=pcb_width, layers=range(3), omit_copper=[0], ) box = Box2( Coordinate2(-pcb_len / 2, -trace_width / 2), Coordinate2(-(cap_dim.length / 2) - (pad_length / 2), trace_width / 2), ) Microstrip( pcb=pcb, position=box.center(), length=box.length(), width=box.width(), propagation_axis=Axis("x"), trace_layer=0, gnd_layer=1, port_number=1, excite=True, feed_shift=0.35, ref_impedance=z0_ref, ) SMDPassive( pcb=pcb, position=Coordinate2(0, 0), axis=Axis("x"), dimensions=cap_dim, pad_width=pad_width, pad_length=pad_length, c=10e-12, pcb_layer=0, gnd_cutout_width=cutout_width, gnd_cutout_length=1, ) box = Box2( Coordinate2(pcb_len / 2, trace_width / 2), Coordinate2((cap_dim.length / 2) + (pad_length / 2), -trace_width / 2), ) Microstrip( pcb=pcb, position=box.center(), length=box.length(), width=box.width(), propagation_axis=Axis("x", direction=-1), trace_layer=0, gnd_layer=1, port_number=2, excite=False, ref_impedance=z0_ref, ) Mesh( sim=sim, metal_res=1 / 120, nonmetal_res=1 / 40, smooth=(1.2, 1.2, 1.2), min_lines=5, expand_bounds=((0, 0), (0, 0), (10, 20)), ) sim.run(csx=False) print_table( data=[sim.freq / 1e9, sim.s_param(1, 1), sim.s_param(2, 1)], col_names=["freq", "s11", "s21"], prec=[4, 4, 4], ) return np.sum(sim.s_param(1, 1))
min_lines=5, expand_bounds=((16, 16), (16, 16), (8, 24)), ) field_dump = FieldDump(sim=sim, box=mesh.sim_box(include_pml=False)) nf2ff = NF2FF(sim=sim) if os.getenv("_PYEMS_PYTEST"): sys.exit(0) sim.run() sim.view_field() s11 = sim.s_param(1, 1) print_table( np.concatenate(([sim.freq / 1e9], [s11])), col_names=["freq", "s11"], prec=[4, 4], ) theta = np.arange(-90, 90, 1) phi = np.arange(0, 360, 1) nf2ff.calc(theta=theta, phi=phi) horn_width = 109.9e-3 horn_height = 80e-3 effective_aperture = horn_height * horn_width print(nf2ff.directivity(effective_aperture)) print("gain: {:.2f} dB".format(nf2ff.gain())) rad_phi0 = nf2ff.radiation_pattern(phi=0)
mesh = Mesh( sim=sim, metal_res=1 / 80, nonmetal_res=1 / 40, smooth=(1.1, 1.5, 1.5), min_lines=5, expand_bounds=((0, 0), (8, 8), (8, 8)), ) # mesh.add_line_manual(0, -cap_dim.length / 2) # mesh.add_line_manual(0, cap_dim.length / 2) FieldDump( sim=sim, box=Box3( Coordinate3(-pcb_len / 2, -pcb_width / 2, 0), Coordinate3(pcb_len / 2, pcb_width / 2, 0), ), dump_type=DumpType.current_density_time, ) sim.run() sim.view_field() print_table( data=[sim.freq / 1e9, sim.s_param(1, 1), sim.s_param(2, 1)], col_names=["freq", "s11", "s21"], prec=[2, 4, 4], )
propagation_axis=Axis("x", direction=-1), trace_layer=0, gnd_layer=1, port_number=2, ref_impedance=50, ) Mesh( sim=sim, metal_res=1 / 120, nonmetal_res=1 / 40, min_lines=5, expand_bounds=((0, 0), (0, 0), (10, 40)), ) # sim.run(csx=False) sim.run() return sim.s_param(1, 1) angles = np.arange(10, 90, 10) angles = [10] res = sweep(sim_func, angles, processes=5) str_angles = [str(angle) for angle in angles] print_table( data=np.concatenate(([freq / 1e9], res)), col_names=["freq"] + str_angles, prec=[2] + [4 for _ in angles], )
def optimize_parameter(func, start, step, tol, max_steps, display_progress=False): """ Compute the lowest-cost value of a parameter that still produces accurate results. """ res1 = func(start) n = len(res1) res_matrix = np.zeros((max_steps, n), dtype=np.clongdouble) res_matrix[0] = np.array(res1) # compute the root mean square differences from the previous results array. rms = np.zeros((max_steps - 1, )) # If the first few RMS values don't show a clear trend, ignore # them, since this may have been the result of poor start value # choice. rms_trend_down = False rms_valid_index = 0 i = 1 orig_start = start start += step while i < max_steps: res_matrix[i] = np.array(func(start)) diff = np.subtract(res_matrix[i], res_matrix[i - 1]) rms[i - 1] = np.sqrt( np.sum(np.real(np.multiply(diff, np.conj(diff)))) / n) if display_progress: num_steps = int(np.round((start - orig_start) / step) + 1) print_table( np.abs(res_matrix[:i + 1]), [ "{:.4f}".format(val) for val in np.linspace(orig_start, start, num=num_steps) ], [4 for _ in range(num_steps)], ) print("parameter: {}".format(start)) print("RMS: {:.10f}".format(rms[i - 1])) if not rms_trend_down and i > 1 and rms[i - 1] > rms[i - 2]: rms_valid_index = i - 1 if i - rms_valid_index > 2: if (not rms_trend_down and rms[i - 1] < rms[i - 2] and rms[i - 2] < rms[i - 3]): rms_trend_down = True valid_start = orig_start + (rms_valid_index * step) num_valid_steps = int( np.round((start - step - valid_start) / step) + 1) try: print( np.linspace(valid_start, start - step, num=num_valid_steps)) print(rms[rms_valid_index:i]) fit = curve_fit( rms_fit, np.linspace(valid_start, start - step, num=num_valid_steps), rms[rms_valid_index:i], ) a = fit[0][0] b = fit[0][1] error_estimate = rms_remaining_sum(a, b, start + 1) if display_progress: print("Error estimate: {:.10f}".format(error_estimate)) if error_estimate < tol: return start except RuntimeError: # If curve fitting fails, ignore the oldest RMS value # and try again on the next iteration. print( "Failed to fit curve to RMS values, ignoring oldest value." ) rms_valid_index += 1 i += 1 start += step raise RuntimeError("Failed to optimize parameter. Consider increasing " "the tolerance or max number of steps.")
shield_thickness=mil_to_mm(5), dielectric=dielectric, propagation_axis=Axis("x"), port_number=1, excite=True, feed_shift=0.3, ref_impedance=50, ) mesh = Mesh( sim=sim, metal_res=1 / 40, nonmetal_res=1 / 10, min_lines=9, expand_bounds=((0, 0), (8, 8), (8, 8)), ) box = mesh.sim_box(include_pml=False) field = FieldDump(sim=sim, box=box, dump_type=DumpType.efield_time) sim.run() sim.view_field() z0 = sim.ports[0].impedance() s11 = sim.s_param(1, 1) print_table( data=[sim.freq / 1e9, np.abs(z0), s11], col_names=["freq", "z0", "s11"], prec=[4, 4, 4], )
Mesh( sim=sim, metal_res=1 / 80, nonmetal_res=1 / 10, min_lines=9, expand_bounds=((0, 0), (0, 0), (10, 40)), ) FieldDump( sim=sim, box=Box3( Coordinate3(-pcb_len / 2, -pcb_width / 2, 0), Coordinate3(pcb_len / 2, pcb_width / 2, 0), ), dump_type=DumpType.current_density_time, ) sim.run(csx=False) return np.abs(sim.ports[0].impedance(freq=ref_freq)) widths = np.arange(0.75 * mid_width, 1.25 * mid_width, 0.05 * mid_width) sim_vals = sweep(func, widths, processes=11) print_table( data=[widths, sim_vals], col_names=["width", "z0"], prec=[4, 4], )
def sim_func(cutout_width: float): """ """ sim = Simulation(freq=freq, unit=unit, reference_frequency=ref_freq) core_rad = (coax_core_diameter( 2 * coax_rad, coax_dielectric.epsr_at_freq(sim.reference_frequency)) / 2) pcb_prop = common_pcbs["oshpark4"] pcb = PCB( sim=sim, pcb_prop=pcb_prop, length=pcb_len, width=pcb_width, layers=range(3), omit_copper=[0], ) Microstrip( pcb=pcb, position=Coordinate2(0, 0), length=pcb_len, width=trace_width, propagation_axis=Axis("x"), trace_layer=0, gnd_layer=1, port_number=1, ref_impedance=50, excite=True, ) # Mueller BU-1420701851 edge mount SMA pad = sim.csx.AddConductingSheet( "pad", conductivity=pcb_prop.metal_conductivity(), thickness=pcb_prop.copper_thickness(0), ) pad.AddBox( priority=priorities["trace"], start=[pcb_len / 2 - sma_lead_len / 2, -sma_lead_width / 2, 0], stop=[pcb_len / 2, sma_lead_width / 2, 0], ) pad_cutout = sim.csx.AddMaterial( "gnd_cutout", epsilon=pcb_prop.substrate.epsr_at_freq(ref_freq), kappa=pcb_prop.substrate.kappa_at_freq(ref_freq), ) pad_cutout.AddBox( priority=priorities["keepout"], start=[ pcb_len / 2 - sma_lead_len / 2, -cutout_width / 2, pcb.copper_layer_elevation(1), ], stop=[pcb_len / 2, cutout_width / 2, pcb.copper_layer_elevation(1)], ) sma_box = sim.csx.AddMetal("sma_box") sma_box.AddBox( priority=priorities["ground"], start=[ pcb_len / 2, -sma_rect_width / 2, -sma_rect_height / 2 + sma_lead_height / 2, ], stop=[ pcb_len / 2 + sma_rect_length, sma_rect_width / 2, sma_rect_height / 2 + sma_lead_height / 2, ], ) sma_keepout = sim.csx.AddMaterial( "sma_keepout", epsilon=coax_dielectric.epsr_at_freq(ref_freq), kappa=coax_dielectric.kappa_at_freq(ref_freq), ) sma_keepout.AddCylinder( priority=priorities["keepout"], start=[pcb_len / 2, 0, sma_lead_height / 2], stop=[pcb_len / 2 + sma_rect_length, 0, sma_lead_height / 2], radius=coax_rad, ) for ypos in [ -sma_rect_width / 2, sma_rect_width / 2 - sma_gnd_prong_width, ]: # sma_box.AddBox( # priority=priorities["ground"], # start=[pcb_len / 2 - sma_gnd_prong_len, ypos, 0], # stop=[ # pcb_len / 2, # ypos + sma_gnd_prong_width, # sma_gnd_prong_height # ], # ) # sma_box.AddBox( # priority=priorities["ground"], # start=[ # pcb_len / 2 - sma_gnd_prong_len, # ypos, # pcb.copper_layer_elevation(1) # ], # stop=[ # pcb_len / 2, # ypos + sma_gnd_prong_width, # pcb.copper_layer_elevation(1) - sma_gnd_prong_height, # ], # ) sma_box.AddBox( priority=priorities["ground"], start=[ pcb_len / 2 - sma_gnd_prong_len, ypos, pcb.copper_layer_elevation(1) - sma_gnd_prong_height, ], stop=[ pcb_len / 2, ypos + sma_gnd_prong_width, sma_gnd_prong_height, ], ) lead = sim.csx.AddMetal("lead") lead.AddBox( priority=priorities["trace"], start=[pcb_len / 2 - sma_lead_len / 2, -sma_lead_width / 2, 0], stop=[ pcb_len / 2 + sma_rect_length, sma_lead_width / 2, sma_lead_height, ], ) # coax port Coax( sim=sim, position=Coordinate3( pcb_len / 2 + sma_rect_length + coax_len / 2, 0, sma_lead_height / 2, ), length=coax_len, radius=coax_rad, core_radius=core_rad, shield_thickness=mil_to_mm(5), dielectric=coax_dielectric, propagation_axis=Axis("x", direction=-1), port_number=2, ref_impedance=50, ) mesh = Mesh( sim=sim, metal_res=1 / 120, nonmetal_res=1 / 10, min_lines=5, expand_bounds=((0, 0), (0, 0), (10, 10)), ) box = mesh.sim_box(include_pml=False) sim.run(csx=False) s11 = sim.s_param(1, 1) s21 = sim.s_param(2, 1) print("cutout width: {}".format(cutout_width)) print_table( data=[sim.freq / 1e9, s11, s21], col_names=["freq", "s11", "s21"], prec=[4, 4, 4], ) return np.sum(s11)