# 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 ```{mermaid} graph LR A[Mode Solver] --> B[Mode Port 1
Input] B --> C[Waveguide Device] C --> D[Mode Port 2
Output] D --> E[S-Parameters
S11, S21, S22, S12] ``` ## Basic Workflow ### 1. Solve for Waveguide Modes First, use the `ModeSolver` to calculate the waveguide modes: ```python 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: ```python 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: ```python 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 ```python # 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: ```python # 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: ```python 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: ```python 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: ```python 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: ```python 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 1. **Mode mismatch**: Ensure mode solver grid matches simulation transverse grid 2. **Insufficient mode decay**: Place monitors where evanescent fields have decayed 3. **Reflections**: Place monitors away from discontinuities to avoid spurious reflections 4. **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 - {doc}`mode_analysis` - Mode solver details - {doc}`sources_monitors` - General source and monitor concepts - {doc}`../api/modes` - Mode solver API reference - {doc}`../api/monitors` - Monitor API reference - {doc}`../examples/index` - More examples