Skip to content

2D Grating Coupler: Wavelength Sweep

A parallel sweep of the design wavelength for the 2D XZ grating-coupler workflow (see meep_2d_xz_gc.ipynb for the single-shot version).

We sweep over 1.54, 1.55, 1.56 µm. At each wavelength both the coupler geometry (grating_coupler_elliptical(wavelength=...) re-tunes the grating pitch/teeth) and the Gaussian-beam source are set to that wavelength. All simulations run on the GDSFactory+ cloud via the non-blocking API, then we overlay the coupling S-parameter for every wavelength in a single plot.

Requirements: GDSFactory+ account for cloud simulation.

Define the sweep

wavelengths_um = [1.54, 1.55, 1.56]

Build a grating coupler + feed straight for a given wavelength

import gdsfactory as gf

gf.gpdk.PDK.activate()


def make_component(wavelength: float) -> gf.Component:
    """Grating coupler tuned to ``wavelength`` plus a 3 µm feed straight."""
    c = gf.Component()
    gc = gf.components.grating_coupler_elliptical(
        wavelength=wavelength, fiber_angle=0.0
    )
    gc_r = c.add_ref(gc)
    s_r = c.add_ref(gf.components.straight(length=3))
    s_r.connect("o1", gc_r.ports["o1"])
    c.add_port("o2", port=s_r.ports["o2"])
    return c


make_component(wavelengths_um[len(wavelengths_um) // 2])  # preview mid-sweep design

png

Configure one XZ 2D simulation per wavelength

Same XZ setup as meep_2d_xz_gc.ipynb, but the coupler geometry and the source_fiber(wavelength=...) are both varied per point. save_animation=False keeps the sweep light since we only need S-params.

from gsim import meep
from gsim.common.stack import get_stack
from gsim.meep.models.api import Material

stack = get_stack()  # auto-detects active PDK

sims = []
for wl in wavelengths_um:
    c = make_component(wl)

    sim = meep.Simulation()

    sim.geometry(component=c, stack=stack)
    sim.materials = {
        "si": Material(refractive_index=3.47),
        "SiO2": Material(refractive_index=1.44),
    }

    sim.solver(resolution=25, mode="2d", y_cut="auto", save_animation=False)
    sim.solver.stop_when_energy_decayed()

    sim.source_fiber(
        x=25.0,
        z=2,
        angle_deg=-6.0,
        waist=5.2,
        wavelength=wl,
        wavelength_span=0.06,
        polarization="TE",
    )

    sim.monitors = ["o2"]
    sim.domain(pml=1.0, margin_x=0.5, margin_y=0.5, margin_z=(1.5, 0))
    sim.num_freqs = 21

    sims.append(sim)

print(f"Configured {len(sims)} simulations for wavelengths {wavelengths_um}")
pyvirtualdisplay not available; continuing without Xvfb


Configured 3 simulations for wavelengths [1.54, 1.55, 1.56]

Upload and start all jobs (non-blocking)

job_ids = [sim.run(wait=False, check_cache=True) for sim in sims]
print(f"Started {len(job_ids)} jobs: {job_ids}")
simulation.py:1625: FutureWarning: Use of domain.margin_z is deprecated and will be removed in a future release; replace it with domain.z_bounds=(-1.5, 4.6).
  result = self.build_config()


Cache hit: reusing job 01a01a1f-a3c6-7c11-8b4d-1d481573fda2


Cache hit: reusing job 01a01a1f-a95c-7693-8032-639b439504ea


Cache hit: reusing job 01a01a1f-af9e-7862-a1a1-293f530beba5
Started 3 jobs: ['01a01a1f-a3c6-7c11-8b4d-1d481573fda2', '01a01a1f-a95c-7693-8032-639b439504ea', '01a01a1f-af9e-7862-a1a1-293f530beba5']

Wait for all jobs to complete

import gsim

results = gsim.wait_for_results(job_ids)
Waiting for 3 jobs...
  meep-761ac8dc                  completed    0m 00s
  meep-5462e6a6                  completed    0m 00s
  meep-c6417d94                  completed    0m 00s


Extracting results.tar.gz...
Downloaded 4 files to sim-data-meep-761ac8dc


Extracting results.tar.gz...
Downloaded 4 files to sim-data-meep-5462e6a6


Extracting results.tar.gz...
Downloaded 4 files to sim-data-meep-c6417d94

Overlay coupling S-parameter vs wavelength

The fiber source produces a single transmission S-parameter (S10) from the beam into the feed waveguide o2. We overlay it in dB for every design wavelength; each sim covers a small band around its center.

import math

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(7, 4))

for wl, sp in zip(wavelengths_um, results, strict=True):
    key = next(iter(sp.s_params))  # single transmission param, e.g. "S10"
    db = [20 * math.log10(abs(v)) if abs(v) > 0 else -100 for v in sp.s_params[key]]
    ax.plot(sp.wavelengths, db, ".-", label=f"{wl:.2f} µm")

ax.set(
    xlabel="Wavelength (µm)",
    ylabel="Coupling |S| (dB)",
    title="Grating-coupler coupling vs design wavelength",
)
ax.legend(title="Design wavelength")
ax.grid(True, alpha=0.3)
fig.tight_layout()

png