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.

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

create_microstrip

A straight trace

create_miter

A 90° corner with the outer corner cut off

create_curved_bend

A corner that follows a circular arc

create_radial_stub

A fan-shaped open stub

create_via

A plated hole that joins two copper layers

create_taper

A trace that changes width along its length

create_gcpw

A coplanar waveguide with a ground plane and stitching vias

create_cpw

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