Plotting microstrip impedance vs width using UliEngineering

The following chart provides an easy way of visualizing the relationship between microstrip width and impedance for various substrate heights and thicknesses.

See Prepreg and core thickness for different standard stackups for more information on standard PCB stackup thicknesses and $\epsilon_r$ values.

Basic example

This Python script shows how to calculate the microstrip impedance for a single set of parameters using the UliEngineering library.

example_microstrip_width_calculation.py
#!/usr/bin/env python3
from UliEngineering.Electronics.Microstrip import microstrip_width
from UliEngineering.EngineerIO import format_value
from UliEngineering.EngineerIO.Length import convert_length_to_unit

# Compute the width for a 50 Ω microstrip using string args with units
w = microstrip_width("50 Ω", h="150 um", t="35 um", e_r="4.4")
# Convert result (meters) to mils and print
w_mil = convert_length_to_unit(w, "m", "mil")
print(format_value(w_mil, "mil"))

Output of the example script

example.txt
10.2 mil

Plot source code

This script is used to produce the plot seen above.

plot_microstrip_impedance_vs_width.py
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, AutoMinorLocator

# UliEngineering imports
from UliEngineering.Electronics.Microstrip import microstrip_impedance
from UliEngineering.EngineerIO import format_value
from UliEngineering.EngineerIO.Length import normalize_length

# Constants
e_r = 4.4
thicknesses = ["100um", "150um", "200um", "1550um"]
# Copper thickness (defaulting to 35um as it wasn't specified but is required for calculation)
t_copper = "35um"

# Width axis: 1mil to 10mm linear
w_min = normalize_length("1mil")
w_max = normalize_length("10mm")
# For a log x-axis we need widths spaced logarithmically; use np.logspace
widths = np.logspace(np.log10(w_min), np.log10(w_max), 1000)

# Plotting
plt.style.use("ggplot")
fig, ax = plt.subplots(figsize=(10, 12))

# Generate colors
colors = plt.cm.viridis(np.linspace(0, 1, len(thicknesses)))

for h_str, c in zip(thicknesses, colors):
    # Calculate impedance for each width
    # microstrip_impedance uses math functions so it doesn't support numpy arrays directly
    zs = [microstrip_impedance(w, h=h_str, t=t_copper, e_r=e_r) for w in widths]
    
    ax.plot(widths, zs, color=c, lw=2, label=f"h={h_str}")

# Add a legend
legend = ax.legend(loc='upper right', fontsize='small')

ax.set_xscale("log")
ax.set_xlabel("Width (log scale)")
ax.set_ylabel("Impedance")
ax.set_title(rf"Microstrip Impedance vs Width ($\epsilon_r={e_r}$)")
ax.grid(True, which="both", ls="--")

# Axis formatters using EngineerIO format_value
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, pos: format_value(y, "Ω")))

# Enable minor ticks and add minor tick labels on the Y axis
ax.minorticks_on()
# For linear scale, AutoMinorLocator is usually good, or we can let matplotlib handle it.
# The template used LogLocator, but we are linear.
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_formatter(FuncFormatter(lambda y, pos: format_value(y, "Ω")))

# Style minor tick labels: 30% smaller and 70% gray
maj_ylabels = ax.yaxis.get_ticklabels(which='major')
if len(maj_ylabels) > 0:
    base_size = maj_ylabels[0].get_size()
else:
    base_size = plt.rcParams.get('ytick.labelsize', plt.rcParams.get('font.size', 10))
ax.tick_params(axis='y', which='minor', labelsize=base_size * 0.7, labelcolor='0.7', colors='0.7')

# Also set x-axis minor ticks and style them with smaller rotated labels
from matplotlib.ticker import LogLocator
ax.xaxis.set_minor_locator(LogLocator(base=10.0, subs=(2, 3, 4, 5, 6, 7, 8, 9)))
ax.xaxis.set_minor_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))

maj_xlabels = ax.xaxis.get_ticklabels(which='major')
if len(maj_xlabels) > 0:
    base_x_size = maj_xlabels[0].get_size()
else:
    base_x_size = plt.rcParams.get('xtick.labelsize', plt.rcParams.get('font.size', 10))
ax.tick_params(axis='x', which='minor', labelsize=base_x_size * 0.7, labelcolor='0.7', colors='0.7')

for tl in ax.get_xminorticklabels():
    tl.set_rotation(90)
for tl in ax.get_xmajorticklabels():
    tl.set_rotation(90)

fig.tight_layout()
plt.show()
plt.savefig("Microstrip-Impedance.svg")

Check out similar posts by category: Electronics, RF