Generic Structure from Components - simple#
This tutorial tells you how to build your own structure from the primitives in
simpleEMS.components. The structure uses every primitive: it starts as a
microstrip line, turns two corners, passes an open radial stub and a shorted
stub, and ends as a coplanar waveguide.
Note
You must have simpleEMS and openEMS on your computer. See Installation.
Make a file with the name simple_generic_structure.py.
Import the modules#
Import GenericParams, GenericStructure, and setup_simulation from
simpleEMS.
from simpleEMS import GenericParams, GenericStructure, setup_simulation
GenericParams holds the parameters of the board and of the simulation.
See also
GenericStructure gives you one create_* method for each primitive.
setup_simulation makes the CSXCAD geometry and the FDTD solver. It also sets
the frequency range of the simulation.
Set the parameters#
params = GenericParams(
min_freq=1e9,
max_freq=6e9,
target_freq=2.45e9,
substrate_width_mm=40,
substrate_length_mm=60,
substrate_thickness_mm=1.6,
substrate_eps_r=4.4,
substrate_tand=0.001,
charac_imp=50,
)
GenericParams does not design anything for you. You give it the frequency
range, the design frequency, the size of the board, and the properties of the
substrate. target_freq sets the mesh size.
Set up the simulation#
sim = setup_simulation(params)
setup_simulation prepares the FDTD solver and the CSXCAD geometry. It returns
a SimSetup with the CSX, FDTD, and freqs attributes.
Make the board#
structure = GenericStructure(params, sim)
structure.create_substrate()
structure.create_ground(start=[-20, -30, -0.035], stop=[20, 14.5, 0])
create_substrate fills the board size from params. create_ground puts a
copper plane under the board. Here the plane stops at y = 14.5, so the
coplanar waveguide at the end has no copper under it.
Draw the traces#
structure.create_microstrip("feed", position=(-10, -30), width_mm=3, length_mm=10)
structure.create_miter(
position=(-10, -20), width_mm=3, miter_distance_mm=1.8, turn="right"
)
structure.create_microstrip(
"run", position=(-8.5, -18.5), width_mm=3, length_mm=7, rotation=-90
)
structure.create_curved_bend(
position=(-1.5, -18.5), width_mm=3, bend_radius_mm=4, rotation=-90
)
structure.create_microstrip("line", position=(2.5, -14.5), width_mm=3, length_mm=12)
structure.create_radial_stub(
position=(4, -8.5),
inner_radius_mm=0.5,
outer_radius_mm=8,
angle_start=-30,
angle_end=30,
)
structure.create_microstrip(
"short_stub", position=(1, -5), width_mm=1.5, length_mm=16, rotation=90
)
structure.create_via(
"short_via",
position=(-14.4, -5),
via_diameter_mm=0.8,
z_bottom_mm=-0.035,
z_top_mm=1.635,
)
structure.create_taper(position=(2.5, -2.5), width1_mm=3, width2_mm=2, length_mm=3)
structure.create_gcpw(
position=(2.5, 0.5),
trace_width_mm=2,
gap_mm=0.3,
ground_width_mm=4,
length_mm=14,
via_diameter_mm=0.6,
via_pitch_mm=2,
via_z_bottom_mm=-0.035,
via_z_top_mm=1.635,
)
structure.create_cpw(
position=(2.5, 14.5),
trace_width_mm=2,
gap_mm=0.3,
ground_width_mm=4,
length_mm=15.5,
)
Each primitive starts at position, the middle of its input edge, and runs
along +y. rotation turns it counter-clockwise about that point, so the next
primitive starts where the last one stops. Every trace sits on top of the
substrate unless you give it a z_elevation_mm.
Method |
What it draws |
|---|---|
|
A straight trace |
|
A 90° corner with the outer corner cut off |
|
A corner that follows a circular arc |
|
A fan-shaped open stub |
|
A plated hole that joins two copper layers |
|
A trace that changes width along its length |
|
A coplanar waveguide with a ground plane and stitching vias |
|
A coplanar waveguide with no ground plane |
The shorted stub is a microstrip trace with a via at its end. The via connects the trace to the ground plane.
Add the ports and the mesh#
from simpleEMS import GenericParams, GenericStructure, setup_simulation
create_lumped_port drives the microstrip end. Add it before create_mesh, so
the mesher puts lines on its edges.
create_cpw_port terminates the coplanar waveguide end. It measures the field
across both slots at once, so it sees the CPW mode correctly. start is the
reference plane at the board edge, and stop runs 4 mm back into the line. Add
it after create_mesh, because it places its probes on the mesh lines.
Then the commands show the structure in AppCSXCAD. Close the AppCSXCAD window to continue.
Tip
create_cpw_lumped_port is the other CPW port. It puts one lumped port across
each slot and fills the full copper thickness, so it suits thick copper. Add it
before create_mesh.
Run the simulation#
structure.run_simulation(sim)
This command runs the openEMS solver. Wait until the solver stops.
Show the results#
sim_data = structure.compute_sim_data(sim, [port_1, port_2])
structure.plot_s_param(sim_data.freqs, sim_data.s11, sim_data.s21)
structure.plot_smith_chart(sim_data.freqs, sim_data.s11)
structure.plot_impedance(sim_data.freqs, sim_data.z11)
structure.plot_phase(sim_data.freqs, sim_data.s21)
structure.save_plots()
structure.show_plots()
These commands show S11 and S21, the Smith chart, the impedance, and the phase of S21.
Export the results#
You can write the model to different formats. Use these files in other tools, or send them to a PCB manufacturer.
structure.export_touchstone(
sim_data.freqs,
sim_data.s11,
s21=sim_data.s21,
charac_imp=params.charac_imp,
)
structure.export_gerber(sim)
Complete script#
The complete script is below. It builds the structure, runs the simulation, and shows the results.
#!/usr/bin/env python3
"""Simple structure built from the primitives in simpleEMS.components."""
# IMPORTS
from simpleEMS import GenericParams, GenericStructure, setup_simulation
# IMPORTS
# PARAMS
params = GenericParams(
min_freq=1e9,
max_freq=6e9,
target_freq=2.45e9,
substrate_width_mm=40,
substrate_length_mm=60,
substrate_thickness_mm=1.6,
substrate_eps_r=4.4,
substrate_tand=0.001,
charac_imp=50,
)
# PARAMS
# SETUP
sim = setup_simulation(params)
# SETUP
# BOARD
structure = GenericStructure(params, sim)
structure.create_substrate()
structure.create_ground(start=[-20, -30, -0.035], stop=[20, 14.5, 0])
# BOARD
# TRACES
structure.create_microstrip("feed", position=(-10, -30), width_mm=3, length_mm=10)
structure.create_miter(
position=(-10, -20), width_mm=3, miter_distance_mm=1.8, turn="right"
)
structure.create_microstrip(
"run", position=(-8.5, -18.5), width_mm=3, length_mm=7, rotation=-90
)
structure.create_curved_bend(
position=(-1.5, -18.5), width_mm=3, bend_radius_mm=4, rotation=-90
)
structure.create_microstrip("line", position=(2.5, -14.5), width_mm=3, length_mm=12)
structure.create_radial_stub(
position=(4, -8.5),
inner_radius_mm=0.5,
outer_radius_mm=8,
angle_start=-30,
angle_end=30,
)
structure.create_microstrip(
"short_stub", position=(1, -5), width_mm=1.5, length_mm=16, rotation=90
)
structure.create_via(
"short_via",
position=(-14.4, -5),
via_diameter_mm=0.8,
z_bottom_mm=-0.035,
z_top_mm=1.635,
)
structure.create_taper(position=(2.5, -2.5), width1_mm=3, width2_mm=2, length_mm=3)
structure.create_gcpw(
position=(2.5, 0.5),
trace_width_mm=2,
gap_mm=0.3,
ground_width_mm=4,
length_mm=14,
via_diameter_mm=0.6,
via_pitch_mm=2,
via_z_bottom_mm=-0.035,
via_z_top_mm=1.635,
)
structure.create_cpw(
position=(2.5, 14.5),
trace_width_mm=2,
gap_mm=0.3,
ground_width_mm=4,
length_mm=15.5,
)
# TRACES
# PORTS
port_1 = structure.create_lumped_port(
port_nr=1, start=[-11.5, -30, 0], stop=[-8.5, -30, 1.635], excite=1
)
structure.create_mesh()
port_2 = structure.create_cpw_port(
port_nr=2, start=[1.5, 30, 1.6], stop=[3.5, 26, 1.6], gap_mm=0.3
)
structure.write_and_show_structure(sim)
# PORTS
# SIMULATE
structure.run_simulation(sim)
# SIMULATE
# PPROCESS
sim_data = structure.compute_sim_data(sim, [port_1, port_2])
structure.plot_s_param(sim_data.freqs, sim_data.s11, sim_data.s21)
structure.plot_smith_chart(sim_data.freqs, sim_data.s11)
structure.plot_impedance(sim_data.freqs, sim_data.z11)
structure.plot_phase(sim_data.freqs, sim_data.s21)
structure.save_plots()
structure.show_plots()
# PPROCESS
# EXPORT
structure.export_touchstone(
sim_data.freqs,
sim_data.s11,
s21=sim_data.s21,
charac_imp=params.charac_imp,
)
structure.export_gerber(sim)
# EXPORT