Tutorial 3: S-Parameter Extraction

Time: 30 minutes
Difficulty: Intermediate
Prerequisites: Tutorials 1-2

Learning Objectives

  • Set up two-port measurements

  • Extract S11 (reflection) and S21 (transmission)

  • Calculate insertion loss and return loss

  • Perform frequency sweeps

The Device: Directional Coupler

We’ll analyze a 2×2 directional coupler and extract its full S-matrix.

Quick Implementation

import numpy as np
from prismo import Simulation
from prismo.modes.solver import ModeSolver
from prismo.monitors.mode_monitor import ModeExpansionMonitor
from prismo.sources.mode import ModeSource

# Setup (simplified for clarity)
wavelength = 1.55e-6
frequencies = np.linspace(185e12, 200e12, 16)  # Frequency sweep

# 1. Solve for waveguide modes
mode_solver = ModeSolver(wavelength, x, y, epsilon)
modes = mode_solver.solve(num_modes=1, mode_type='TE')

# 2. Create mode source at port 1
source = ModeSource(
    center=(0.0, 0.0, port1_z),
    size=(4e-6, 4e-6, 0.0),
    mode=modes[0],
    direction='+z',
    waveform=pulse,
)

# 3. Add mode monitors at all ports
port1_monitor = ModeExpansionMonitor(
    center=(0.0, 0.0, port1_z),
    size=(4e-6, 4e-6, 0.0),
    modes=modes,
    direction='z',
    frequencies=frequencies.tolist(),
    name='port1'
)

port2_monitor = ModeExpansionMonitor(
    center=(0.0, 0.0, port2_z),
    size=(4e-6, 4e-6, 0.0),
    modes=modes,
    direction='z',
    frequencies=frequencies.tolist(),
    name='port2'
)

# Add monitors for ports 3 and 4...

sim.add_source(source)
sim.add_monitor(port1_monitor)
sim.add_monitor(port2_monitor)

# 4. Run simulation
sim.run(200e-15)

# 5. Extract S-parameters
s11 = port1_monitor.compute_s_parameters(source_mode_index=0)['S_11']
s21 = port2_monitor.compute_s_parameters(source_mode_index=0)['S_11']

# 6. Plot results
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 1, figsize=(10, 8))

# Magnitude
axes[0].plot(frequencies/1e12, 20*np.log10(np.abs(s11)), label='S11 (Reflection)')
axes[0].plot(frequencies/1e12, 20*np.log10(np.abs(s21)), label='S21 (Transmission)')
axes[0].set_ylabel('Magnitude (dB)')
axes[0].set_title('S-Parameters vs Frequency')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Phase
axes[1].plot(frequencies/1e12, np.angle(s11, deg=True), label='S11 Phase')
axes[1].plot(frequencies/1e12, np.angle(s21, deg=True), label='S21 Phase')
axes[1].set_xlabel('Frequency (THz)')
axes[1].set_ylabel('Phase (degrees)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('tutorial3_sparameters.png', dpi=150)
plt.show()

# 7. Calculate metrics
insertion_loss = -20 * np.log10(np.abs(s21))  # dB
return_loss = -20 * np.log10(np.abs(s11))  # dB

print(f"At center frequency:")
print(f"  Insertion Loss: {insertion_loss[len(frequencies)//2]:.2f} dB")
print(f"  Return Loss: {return_loss[len(frequencies)//2]:.2f} dB")

Understanding S-Parameters

For a 2-port device:

  • S11: Reflection at port 1 (how much reflects back)

  • S21: Transmission from port 1 to port 2

  • S12: Transmission from port 2 to port 1 (= S21 for reciprocal devices)

  • S22: Reflection at port 2

     Port 1              Device              Port 2
       →  ────────────────────────────────  →
     S11 ←                                S21 →

Key Metrics

def analyze_sparameters(s11, s21):
    """Extract key metrics from S-parameters."""

    # Insertion Loss (IL)
    IL = -20 * np.log10(np.abs(s21))

    # Return Loss (RL)
    RL = -20 * np.log10(np.abs(s11))

    # Power transmission
    T = np.abs(s21)**2

    # Power reflection
    R = np.abs(s11)**2

    # Check energy conservation (for lossless device)
    conservation = T + R

    print(f"Insertion Loss: {np.mean(IL):.2f} ± {np.std(IL):.2f} dB")
    print(f"Return Loss: {np.mean(RL):.2f} ± {np.std(RL):.2f} dB")
    print(f"Power Conservation: {np.mean(conservation):.4f} (should be ~1.0)")

    return {'IL': IL, 'RL': RL, 'T': T, 'R': R}

metrics = analyze_sparameters(s11, s21)

Validation Checks

# 1. Reciprocity (S12 should equal S21)
s12 = port1_monitor_reversed.compute_s_parameters(source_mode_index=0)['S_11']
reciprocity_error = np.abs(s12 - s21) / np.abs(s21)
print(f"Reciprocity error: {np.max(reciprocity_error)*100:.2f}%")

# 2. Energy conservation
energy_balance = np.abs(s11)**2 + np.abs(s21)**2
print(f"Energy balance: {np.mean(energy_balance):.4f} (should be ~1.0 for lossless)")

# 3. Passivity (|S| ≤ 1 for passive devices)
max_s11 = np.max(np.abs(s11))
max_s21 = np.max(np.abs(s21))
print(f"Max |S11|: {max_s11:.4f} (should be ≤ 1.0)")
print(f"Max |S21|: {max_s21:.4f} (should be ≤ 1.0)")

Touchstone File Export

Export S-parameters in standard format:

def export_touchstone(frequencies, s_params, filename='device.s2p'):
    """Export 2-port S-parameters to Touchstone format."""

    with open(filename, 'w') as f:
        # Header
        f.write('! 2-port S-parameters from Prismo\n')
        f.write('# HZ S MA R 50\n')  # Hz, S-params, Magnitude-Angle, 50Ω

        # Data
        for i, freq in enumerate(frequencies):
            s11 = s_params['S11'][i]
            s12 = s_params['S12'][i]
            s21 = s_params['S21'][i]
            s22 = s_params['S22'][i]

            # Format: freq S11_mag S11_ang S21_mag S21_ang S12_mag S12_ang S22_mag S22_ang
            f.write(f"{freq:.6e} ")
            f.write(f"{np.abs(s11):.6e} {np.angle(s11, deg=True):.6e} ")
            f.write(f"{np.abs(s21):.6e} {np.angle(s21, deg=True):.6e} ")
            f.write(f"{np.abs(s12):.6e} {np.angle(s12, deg=True):.6e} ")
            f.write(f"{np.abs(s22):.6e} {np.angle(s22, deg=True):.6e}\n")

    print(f"✓ Exported to {filename}")

# Export
s_params_dict = {
    'S11': s11, 'S12': s12,
    'S21': s21, 'S22': s22,
}
export_touchstone(frequencies, s_params_dict, 'coupler.s2p')

Exercises

  1. Simulate a simple straight waveguide and verify S21 ≈ 1, S11 ≈ 0

  2. Add losses (lossy material) and observe increased insertion loss

  3. Create a Bragg grating and observe reflection peaks in S11

  4. Measure 3 dB bandwidth of a resonator

Summary

Key Points:

  • Mode monitors enable S-parameter extraction

  • Always check reciprocity and energy conservation

  • Frequency sweeps reveal bandwidth and resonances

  • Touchstone format enables integration with circuit simulators

Next Steps