# Generic-PDK grating coupler: fiber to chip

Use GDSFactory's built-in `grating_coupler_elliptical_arbitrary` to simulate coupling from a tilted Gaussian beam into a silicon waveguide. The 16-period grating has a 14 µm taper and a short straight input for guided-mode measurement. The FDTD cell size is 60 nm; 21 wavelength samples keep the run fast.

```python
from math import cos, radians, sin

import gdsfactory as gf

from gsim import fdtd

FIBER_ANGLE_DEG = 15.0
FIBER_HEIGHT_UM = 1.05
BEAM_WAIST_UM = 5.0
BEAM_OFFSET_UM = -0.75
Z_BOUNDS = (-1.0, 1.55)
```

## Build the generic-PDK grating coupler

```python
pdk = gf.gpdk.PDK
pdk.activate()

# Explicit gap and tooth widths make it easy to try an apodized pattern.
# These uniform values preserve the pitch and tooth width of the tuned design.
gaps_um = [0.324] * 16
widths_um = [0.343] * 16

component = gf.Component()
grating = component.add_ref(
    gf.components.grating_coupler_elliptical_arbitrary(
        gaps=gaps_um,
        widths=widths_um,
        fiber_angle=FIBER_ANGLE_DEG,
        taper_length=14.0,
        taper_angle=40.0,
        wavelength=1.510,
    )
)
straight = component.add_ref(gf.components.straight(length=0.5))
straight.connect("o2", grating.ports["o1"])
component.add_port("o1", port=straight.ports["o1"])
component.add_port("o2", port=grating.ports["o2"])
component
```

## Place the fiber beam

The grating's vertical port `o2` locates the tooth array; the straight waveguide provides the guided output port `o1`. Shift the beam 0.75 µm toward the taper to keep the beam centered at the position used for the tuned grating.

```python
fiber_port = component.ports["o2"]
fiber_x_um, fiber_y_um = map(float, fiber_port.center)
beam_x_um = fiber_x_um + BEAM_OFFSET_UM
beam_size_um = (2 * BEAM_WAIST_UM, 2 * BEAM_WAIST_UM, 0.0)
beam_angle_rad = radians(FIBER_ANGLE_DEG)
beam_direction = (-sin(beam_angle_rad), 0.0, -cos(beam_angle_rad))
```

```python
simulation = fdtd.Simulation(pdk=pdk)
simulation.materials(background="sio2", overrides={"si": 3.47, "sio2": 1.443})
simulation.geometry(component, mesh_size_nm=500, geometry_tolerance_nm=20)
simulation.source = fdtd.GaussianBeamSource(
    center_um=(beam_x_um, fiber_y_um, FIBER_HEIGHT_UM),
    size_um=beam_size_um,
    aperture_normal="-z",
    propagation_direction=beam_direction,
    e_polarization=(0.0, 1.0, 0.0),
    focal_point_um=(beam_x_um, fiber_y_um, 0.22),
    waist_radius_um=BEAM_WAIST_UM,
    refractive_index=1.443,
    wavelength_um=1.554,
    wavelength_span_um=0.1,
    num_wavelengths=21,
)
simulation.monitors.add_plane(
    name="src_down",
    center_um=(beam_x_um, fiber_y_um, 0.4),
    size_um=beam_size_um,
    normal="-z",
    flux=True,
)
simulation.domain(padding=0.5, z_bounds=Z_BOUNDS, pml_cells=16)
simulation.solver(cell_size_nm=60, energy_decay_fraction=4e-4)
```

```text
Solver(cell_size_nm=60.0, max_timesteps=None, energy_decay_fraction=0.0004, max_wall_seconds=3600.0)
```

```python
simulation.plot_3d()
```

## Run and inspect coupling

The flux plane below the source measures net downward power, while guided port `o1` measures transmitted waveguide power. Their ratio shows the guided fraction of net downward flux. Reflections cross the plane upward, so this is not incident-normalized coupling efficiency or a guided-port S-parameter.

```python
result = simulation.run(check_cache=True)
```

```text
  fdtd-fb59abde  [####################] 100%  complete  elapsed 0m 00s


Extracting results.tar.gz...
Downloaded 2 files to sim-data-fdtd-fb59abde
```

```python
import plotly.graph_objects as go

guided_power = result.port_outputs["o1"].modal_power
downward_flux = result.monitors["src_down"].flux

# Ignore the low-power spectral tails before dividing the two powers.
usable = result.valid & (downward_flux > 0.01 * downward_flux.max())
guided_fraction_percent = 100 * guided_power[usable] / downward_flux[usable]
figure = go.Figure()
figure.add_scatter(
    x=result.wavelength_um[usable],
    y=guided_fraction_percent,
    mode="lines+markers",
    name="o1",
)
figure.update_layout(
    xaxis_title="Wavelength (µm)",
    yaxis_title="Guided fraction of net downward flux (%)",
)
figure
```
