Ring Resonator
Simulate an optical ring resonator coupled to a straight waveguide
Ring Resonator Simulation
This example simulates an optical ring resonator coupled to a straight waveguide, demonstrating resonance detection and quality factor analysis.
Application: Ring resonators are fundamental building blocks for optical filters, modulators, sensors, and photonic integrated circuits.
Physics Background
Resonance Condition
Light circulating in the ring interferes constructively at resonance:
$$ m\lambda = n_{eff} \cdot 2\pi R $$
where m is the mode number, n_eff is the effective index, and R is the ring radius.
Free Spectral Range
The spacing between adjacent resonances:
$$ FSR = \frac{\lambda^2}{n_g \cdot 2\pi R} $$
where n_g is the group index.
Quality Factor
The Q factor measures resonance sharpness:
$$ Q = \frac{f_0}{\Delta f} = \frac{2\pi n_{eff}}{\alpha \lambda} $$
Simulation Overview
Define Geometry
Create ring and bus waveguide with specified coupling gap.
Excite with Broadband Source
Launch a Gaussian pulse to probe all frequencies.
Monitor Transmission
Track flux at through and drop ports.
Extract Resonances
Identify dips in transmission spectrum.
Configuration Parameters
| Parameter | Value | Description |
|---|---|---|
resolution | 10 | Grid resolution |
n | 3.4 | Waveguide index (Si) |
w | 1 | Waveguide width |
r | 1 | Ring radius |
g | 0.1 | Coupling gap |
Complete Code
"""
Ring Resonator Simulation with OptixLog Integration
Simulates an optical ring resonator coupled to a straight waveguide.
"""
import os
import meep as mp
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
from optixlog import Optixlog
def main():
api_key = os.getenv("OPTIX_API_KEY", "your_api_key_here")
client = Optixlog(api_key=api_key)
project = client.project(name="MeepExamples", create_if_not_exists=True)
# Parameters
resolution = 10
n = 3.4 # waveguide index (silicon)
w = 1 # waveguide width
r = 1 # ring radius
pad = 4 # padding
dpml = 2
# Ring parameters
sxy = 2 * (r + w + pad + dpml)
# Source parameters
fcen = 0.15
df = 0.1
nfreq = 500
run = project.run(
name="ring_resonator_simulation",
config={
"simulation_type": "ring_resonator",
"resolution": resolution,
"ring_radius": r,
"waveguide_width": w,
"refractive_index": n,
"center_frequency": fcen,
"num_frequencies": nfreq
}
)
run.log(step=0, ring_radius=r, waveguide_width=w, refractive_index=n)
cell = mp.Vector3(sxy, sxy)
pml_layers = [mp.PML(dpml)]
# Geometry: ring + bus waveguide
geometry = [
# Bus waveguide (straight)
mp.Block(
center=mp.Vector3(0, r + w/2 + w/2),
size=mp.Vector3(mp.inf, w, mp.inf),
material=mp.Medium(index=n)
),
# Ring (cylinder with hole)
mp.Cylinder(
radius=r + w,
height=mp.inf,
material=mp.Medium(index=n)
),
mp.Cylinder(
radius=r,
height=mp.inf,
material=mp.Medium(index=1) # Air hole
),
]
# Source in bus waveguide
sources = [
mp.Source(
mp.GaussianSource(fcen, fwidth=df),
component=mp.Ez,
center=mp.Vector3(-r - w - pad/2, r + w/2 + w/2),
size=mp.Vector3(0, w),
)
]
sim = mp.Simulation(
cell_size=cell,
geometry=geometry,
sources=sources,
boundary_layers=pml_layers,
resolution=resolution,
)
# Transmission monitor (through port)
tran_fr = mp.FluxRegion(
center=mp.Vector3(r + w + pad/2, r + w/2 + w/2),
size=mp.Vector3(0, 2*w)
)
tran = sim.add_flux(fcen, df, nfreq, tran_fr)
# Run straight waveguide for normalization
print("⚡ Running straight waveguide reference...")
sim_straight = mp.Simulation(
cell_size=cell,
geometry=[
mp.Block(
center=mp.Vector3(0, r + w/2 + w/2),
size=mp.Vector3(mp.inf, w, mp.inf),
material=mp.Medium(index=n)
)
],
sources=sources,
boundary_layers=pml_layers,
resolution=resolution,
)
tran_straight = sim_straight.add_flux(fcen, df, nfreq, tran_fr)
pt = mp.Vector3(r + w + pad/2, r + w/2 + w/2)
sim_straight.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, pt, 1e-3))
straight_flux = mp.get_fluxes(tran_straight)
flux_freqs = mp.get_flux_freqs(tran_straight)
run.log(step=1, phase="straight_reference_completed")
# Run ring resonator
print("⚡ Running ring resonator simulation...")
sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, pt, 1e-3))
ring_flux = mp.get_fluxes(tran)
# Calculate transmission
wavelengths = [1/f for f in flux_freqs]
transmission = [ring_flux[i] / straight_flux[i] if straight_flux[i] > 0 else 0
for i in range(nfreq)]
run.log(step=2, phase="ring_simulation_completed",
min_transmission=float(min(transmission)),
max_transmission=float(max(transmission)))
# Find resonances (transmission dips)
resonance_indices = []
for i in range(1, len(transmission) - 1):
if transmission[i] < transmission[i-1] and transmission[i] < transmission[i+1]:
if transmission[i] < 0.8: # Significant dip
resonance_indices.append(i)
resonance_wavelengths = [wavelengths[i] for i in resonance_indices]
resonance_depths = [1 - transmission[i] for i in resonance_indices]
# Calculate FSR if multiple resonances found
if len(resonance_wavelengths) >= 2:
fsr = abs(resonance_wavelengths[0] - resonance_wavelengths[1])
else:
fsr = 0
run.log(step=3,
num_resonances=len(resonance_indices),
free_spectral_range=fsr)
# Create plots
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Transmission spectrum
axes[0, 0].plot(wavelengths, transmission, 'b-', linewidth=1.5)
for idx in resonance_indices:
axes[0, 0].axvline(wavelengths[idx], color='r', linestyle='--', alpha=0.5)
axes[0, 0].set_xlabel('Wavelength (μm)')
axes[0, 0].set_ylabel('Transmission')
axes[0, 0].set_title('Ring Resonator Transmission Spectrum')
axes[0, 0].set_ylim(0, 1.1)
axes[0, 0].grid(True, alpha=0.3)
# Frequency spectrum
axes[0, 1].plot(flux_freqs, transmission, 'g-', linewidth=1.5)
axes[0, 1].set_xlabel('Frequency (c/a)')
axes[0, 1].set_ylabel('Transmission')
axes[0, 1].set_title('Transmission vs Frequency')
axes[0, 1].grid(True, alpha=0.3)
# Resonance depths
if resonance_wavelengths:
axes[1, 0].bar(range(len(resonance_depths)), resonance_depths, color='red', alpha=0.7)
axes[1, 0].set_xlabel('Resonance Index')
axes[1, 0].set_ylabel('Extinction (1-T)')
axes[1, 0].set_title('Resonance Extinction Depths')
axes[1, 0].grid(True, alpha=0.3)
# Loss (1-T) on log scale
loss = [1 - t if t < 1 else 1e-6 for t in transmission]
axes[1, 1].semilogy(wavelengths, loss, 'purple', linewidth=1.5)
axes[1, 1].set_xlabel('Wavelength (μm)')
axes[1, 1].set_ylabel('1 - Transmission')
axes[1, 1].set_title('Loss Spectrum (Log Scale)')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
run.log_matplotlib("ring_resonator_analysis", fig)
plt.close(fig)
run.log(step=4, simulation_completed=True)
print(f"📊 Found {len(resonance_indices)} resonances")
if fsr > 0:
print(f"📊 FSR: {fsr:.4f} μm")
print(f"\n✅ Simulation complete!")
if __name__ == "__main__":
main()Key Concepts
Ring Geometry
Create ring using concentric cylinders:
geometry = [
mp.Cylinder(radius=r + w, material=mp.Medium(index=n)),
mp.Cylinder(radius=r, material=mp.Medium(index=1)), # Air core
]Resonance Detection
Find transmission dips:
for i in range(1, len(transmission) - 1):
if transmission[i] < transmission[i-1] and transmission[i] < transmission[i+1]:
if transmission[i] < 0.8:
resonance_indices.append(i)Normalization
Compare to straight waveguide for accurate transmission:
transmission = ring_flux / straight_fluxExpected Results
Transmission Spectrum
- Periodic dips at resonant wavelengths
- Dip spacing = FSR
- Dip depth indicates coupling strength
Typical Parameters
| Ring Radius | FSR (approx) |
|---|---|
| 1 μm | ~100 nm |
| 5 μm | ~20 nm |
| 10 μm | ~10 nm |
OptixLog Integration
Logged Metrics
| Metric | Description |
|---|---|
ring_radius | Ring radius |
num_resonances | Detected resonance count |
free_spectral_range | FSR in μm |
min_transmission | Deepest dip |
Logged Plots
- ring_resonator_analysis — 4-panel comprehensive analysis
Variations
Add-Drop Configuration
Add second bus waveguide for drop port.
Racetrack Resonator
Use straight coupling section for better control.
Coupled Rings
Multiple rings for filter design.