Mode Ports
Mode ports are powerful tools for analyzing waveguide devices. They allow you to inject specific waveguide modes and extract mode coefficients to calculate S-parameters.
Overview
A mode port combines:
Mode injection: Launch a specific waveguide mode into your simulation
Mode extraction: Decompose simulation fields into mode amplitudes
S-parameters: Calculate reflection and transmission coefficients
Basic Workflow
1. Solve for Waveguide Modes
First, use the ModeSolver to calculate the waveguide modes:
from prismo.modes.solver import ModeSolver
import numpy as np
# Create waveguide cross-section
nx, ny = 100, 100
x = np.linspace(-2e-6, 2e-6, nx)
y = np.linspace(-2e-6, 2e-6, ny)
# Define permittivity (waveguide structure)
X, Y = np.meshgrid(x, y, indexing='ij')
epsilon = np.ones((nx, ny)) * 1.45**2 # SiO2 cladding
# Add waveguide core
core_width = 0.5e-6
core_mask = (np.abs(X) < core_width/2) & (np.abs(Y) < core_width/2)
epsilon[core_mask] = 3.48**2 # Silicon core
# Solve for modes
wavelength = 1.55e-6
solver = ModeSolver(wavelength, x, y, epsilon)
modes = solver.solve(num_modes=2, mode_type='TE')
print(f"Fundamental mode: neff = {modes[0].neff.real:.4f}")
2. Create Mode Source
Inject a mode into your simulation:
from prismo.sources.mode import ModeSource
from prismo.sources.waveform import GaussianPulse
# Create waveform
waveform = GaussianPulse(
frequency=193.5e12, # 1.55 μm
width=20e-15, # 20 fs pulse
)
# Create mode source
mode_source = ModeSource(
center=(0.0, 0.0, 1e-6), # Position in simulation
size=(4e-6, 4e-6, 0.0), # Source plane size
mode=modes[0], # Fundamental mode
direction='+z', # Propagation direction
waveform=waveform,
amplitude=1.0,
)
sim.add_source(mode_source)
3. Add Mode Monitors
Extract mode coefficients:
from prismo.monitors.mode_monitor import ModeExpansionMonitor
# Input monitor (for reflection S11)
input_monitor = ModeExpansionMonitor(
center=(0.0, 0.0, 0.5e-6),
size=(4e-6, 4e-6, 0.0),
modes=modes,
direction='z',
frequencies=[193.5e12], # Monitor at source frequency
name='input_port'
)
# Output monitor (for transmission S21)
output_monitor = ModeExpansionMonitor(
center=(0.0, 0.0, 10e-6),
size=(4e-6, 4e-6, 0.0),
modes=modes,
direction='z',
frequencies=[193.5e12],
name='output_port'
)
sim.add_monitor(input_monitor)
sim.add_monitor(output_monitor)
4. Run Simulation and Extract S-Parameters
# Run simulation
sim.run(total_time=100e-15)
# Get S-parameters
s11 = input_monitor.compute_s_parameters(source_mode_index=0)['S_11']
s21 = output_monitor.compute_s_parameters(source_mode_index=0)['S_11']
print(f"|S11| = {abs(s11[0]):.4f} (reflection)")
print(f"|S21| = {abs(s21[0]):.4f} (transmission)")
print(f"Loss = {-20*np.log10(abs(s21[0])):.2f} dB")
Advanced Features
Multi-Mode Ports
Handle multiple modes simultaneously:
# Solve for more modes
modes = solver.solve(num_modes=4, mode_type='TE')
# Monitor all modes
monitor = ModeExpansionMonitor(
center=(0.0, 0.0, 5e-6),
size=(4e-6, 4e-6, 0.0),
modes=modes, # All 4 modes
direction='z',
frequencies=[193.5e12],
)
# Extract coefficients for each mode
for mode_idx in range(len(modes)):
coeff = monitor.get_mode_coefficient(mode_idx)
power = monitor.get_mode_power(mode_idx, frequency_index=0)
print(f"Mode {mode_idx}: Power = {power:.6f}")
Forward/Backward Separation
Separate forward and backward propagating modes:
from prismo.utils.mode_matching import separate_forward_backward
# Use two monitors separated by known distance
distance = 5e-6
# Get coefficients at both monitors
coeff_left = monitor1.get_mode_coefficient(0)
coeff_right = monitor2.get_mode_coefficient(0)
# Separate directions
a_fwd, a_bwd = separate_forward_backward(
coeff_left[-1], # Final time step
coeff_right[-1],
modes[0].neff,
distance,
wavelength
)
print(f"Forward amplitude: {abs(a_fwd):.4f}")
print(f"Backward amplitude: {abs(a_bwd):.4f}")
Mode Normalization
Normalize modes to specific power:
from prismo.utils.mode_matching import normalize_mode_to_power
# Normalize mode to 1 mW
mode_normalized = normalize_mode_to_power(
modes[0],
target_power=1e-3, # 1 mW
direction='z',
dx=x[1] - x[0],
dy=y[1] - y[0],
)
# Use normalized mode in source
mode_source = ModeSource(
center=(0.0, 0.0, 1e-6),
size=(4e-6, 4e-6, 0.0),
mode=mode_normalized,
direction='+z',
waveform=waveform,
)
Using ModePort Boundary Condition
The ModePort class provides an integrated boundary condition:
from prismo.boundaries.mode_port import ModePort, ModePortConfig
# Create input port configuration
input_config = ModePortConfig(
center=(0.0, 0.0, 0.5e-6),
size=(4e-6, 4e-6, 0.0),
direction='+z',
modes=modes,
inject=True, # This port injects modes
)
input_port = ModePort(input_config, name='port1')
# Create output port (extraction only)
output_config = ModePortConfig(
center=(0.0, 0.0, 10e-6),
size=(4e-6, 4e-6, 0.0),
direction='+z',
modes=modes,
inject=False, # Only extract, don't inject
)
output_port = ModePort(output_config, name='port2')
# Initialize ports (after creating simulation grid)
input_port.initialize(sim.grid)
output_port.initialize(sim.grid)
# During simulation loop, inject and extract
for time_step in range(num_steps):
# Inject at input port
input_port.inject_fields(fields, time, dt, mode_amplitudes=[1.0])
# Extract at output port
coeffs = output_port.extract_mode_coefficients(fields, time)
Validation and Best Practices
Check Mode Orthogonality
Ensure modes are properly orthogonal:
from prismo.utils.mode_matching import check_mode_orthogonality
orthogonality = check_mode_orthogonality(
modes[0], modes[1],
direction='z',
dx=x[1] - x[0],
dy=y[1] - y[0],
)
print(f"Orthogonality: {orthogonality:.6f}")
# Should be close to 0 for orthogonal modes
Resolution Requirements
Use at least 30 points per wavelength in the mode solver
Mode source plane should cover the entire mode profile (include evanescent tails)
Monitor planes should be placed in regions with uniform waveguide cross-section
Common Pitfalls
Mode mismatch: Ensure mode solver grid matches simulation transverse grid
Insufficient mode decay: Place monitors where evanescent fields have decayed
Reflections: Place monitors away from discontinuities to avoid spurious reflections
Frequency mismatch: Mode effective index varies with frequency
Complete Example
See the complete working example in examples/mode_port_demo.py which demonstrates:
Waveguide design
Mode solving
Mode injection
S-parameter extraction
Visualization
See Also
Waveguide Mode Analysis - Mode solver details
Sources and Monitors - General source and monitor concepts
Mode Solver API - Mode solver API reference
Monitors API - Monitor API reference
Examples - More examples