Technology and PDK Setup

This notebook builds a custom SiEPIC EBeam technology stack (PDK “technology”) intended for high-speed RF photonics work (modulators, transceivers), then validates/visualizes it by:

  • printing a technology summary (layers / extrusion specs / ports),

  • plotting a representative cross-section,

  • listing the available PDK components,

  • instantiating one example component.

Conventions

  • Geometry dimensions are in µm.

  • Frequencies are in Hz.

The PDK/technology in this project is implemented so that PIC designers can:

  • Understand the process stack quickly (layer thicknesses, materials, and key assumptions).

  • Modify the stack (e.g., oxide thickness, metal thickness/conductivity) without rewriting the whole PDK.

  • Translate the technology definition to other toolchains and formats.

In this notebook, the customization points are intentionally kept in a small set of named parameters (waveguide geometry, oxide stack, and metal stack), so you can reuse the same pattern for other foundry stacks.

46be154ba6d0478fb7ca7d0e03720171

Figure: The technology wrapper exposes a small set of parameters (materials + layer thicknesses) that define the process stack used in layout, visualization, and simulation.

Technology Stack Configuration

In the next cells we define the process stack parameters (waveguide geometry, oxide thicknesses, and metal stack) and the material models used by PhotonForge/Tidy3D.

The goal is to keep all tunable knobs centralized and clearly named so the stack can be reused across projects (or mapped to a foundry PDK).

This section sets up the SiEPIC EBeam technology with a few customizations aimed at high-speed RF photonics applications (modulators, transceivers).

Parameter

Default PDK

This Config

top_oxide_thickness

0.3 µm

1.2 µm

bottom_oxide_thickness

3.017 µm

2.0 µm

metal_si_separation

2.2 µm

0.98 µm

router_thickness

0.6 µm

2.0 µm

router_metal

Au (σ=17)

Al (σ=38)

include_top_opening

False

True

include_substrate

False

True

Key customizations vs ``siepic.ebeam()`` defaults

  • Thicker metal (2 µm Al) for better RF performance and lower loss.

  • Oxide thicknesses chosen to match a realistic process stack.

  • Higher conductivity Al for transmission-line electrodes.

  • Cross-section visualization includes air and substrate so the full stack is visible. This is aligned with the real chip stack where air and substrate are present.

[1]:
# Imports used throughout this notebook:
import tidy3d as td
import photonforge as pf
import numpy as np
import siepic_forge as siepic
from matplotlib import pyplot as plt
[2]:
# Process / geometry parameters (units: µm)

# Waveguide geometry (kept consistent with the default EBeam PDK)
H_CORE = 0.22       # Silicon device/core thickness (µm)
H_SLAB = 0.09       # Silicon slab thickness for rib waveguides (µm)
W_CORE = 0.5        # Nominal waveguide width (µm)

# Oxide thicknesses
TOX_THICKNESS = 1.2  # Top oxide cladding thickness (µm)
BOX_THICKNESS = 2.0  # Buried oxide (BOX) thickness (µm)

# Metal stack (RF routing / heaters)
TL_THICKNESS = 2.0   # Router/heater metal thickness (µm)
METAL_SI_SEP = TOX_THICKNESS - H_CORE  # Vertical gap between top Si and metal (µm)

[3]:
# Define aluminum for both optical and electrical simulations.
# We use Tidy3D's optical material model plus a (frequency-dependent) electrical medium.
al_metal = {
    "optical": td.material_library["Al"]["Rakic1995"],
    "electrical": td.LossyMetalMedium(
        conductivity=38,  # Conductivity in S/µm (≈ 38e6 S/m)
        frequency_range=[0.1e9, 200e9],  # Hz
        fit_param=td.SurfaceImpedanceFitterParam(max_num_poles=16),
    ),
}

Create Custom Technology

Here we instantiate a Technology object by calling siepic.ebeam(...) and overriding the key stack parameters defined above.

  • The returned tech object is used by PhotonForge for layout and for Tidy3D-based visualization/simulation.

  • We also set pf.config.default_technology so subsequent components use this stack by default.

[4]:
# Build the Technology object with our custom stack parameters.
# This object controls layer definitions, vertical extrusions (cross-sections), and port definitions.
tech = siepic.ebeam(
    # Si layer
    si_thickness=H_CORE,
    si_slab_thickness=H_SLAB,

    # Oxide stack
    top_oxide_thickness=TOX_THICKNESS,
    bottom_oxide_thickness=BOX_THICKNESS,

    # Metal stack
    metal_si_separation=METAL_SI_SEP,
    router_thickness=TL_THICKNESS,
    router_metal=al_metal,
    heater_metal=al_metal,

    # Include surrounding media in the cross-section for visualization/simulation context
    include_top_opening=True,
    include_substrate=True,
)

# Revise the "TE_1550_500" port in accordance with W_CORE
tech.ports["TE_1550_500"].path_profiles = [(W_CORE, 0.0, (1, 0))]

# Set this technology as the default for the project
pf.config.default_technology = tech

[5]:
# Display a summary table of layers/ports/extrusions (sanity check)
tech
[5]:
Name: SiEPIC EBeam
Version: 0.4.32
Layers
NameLayerDescriptionColorPattern
Si(1, 0)Waveguides#ff80a818\\
PinRec(1, 10)SiEPIC#00408018/
PinRecM(1, 11)SiEPIC#00408018/
Si_Litho193nm(1, 69)Waveguides#cc80a818\
Waveguide(1, 99)Waveguides#ff80a818\
Si slab(2, 0)Waveguides#80a8ff18/
SiN(4, 0)Waveguides#a6cee318\\
Oxide open (to BOX)(6, 0)Waveguides#ffae0018\
Text(10, 0)#0000ff18\
M1_heater(11, 0)Metal#ebc63418xx
M2_router(12, 0)Metal#90857018xx
M_Open(13, 0)Metal#3471eb18xx
Si N(20, 0)Doping#7000ff18\\
Si N++(24, 0)Doping#0000ff18:
VC(40, 0)Metal#3a027f18xx
DevRec(68, 0)SiEPIC#00408018hollow
FbrTgt(81, 0)SiEPIC#00408018/
FloorPlan(99, 0)Misc#8000ff18hollow
SEM(200, 0)Misc#ff00ff18\
Deep Trench(201, 0)Misc#c0c0c018solid
Keep out(202, 0)Misc#a0a0c018//
Isolation Trench(203, 0)Misc#c0c0c018solid
Dicing(210, 0)Misc#a0a0c018solid
Chip design area(290, 0)Misc#80005718hollow
FDTD(733, 0)SiEPIC#80005718hollow
BlackBox(998, 0)SiEPIC#00408018solid
Errors(999, 0)SiEPIC#00008018/
Extrusion Specs
#MaskLimits (μm)Sidewal (°)Opt. MediumElec. Medium
0()-inf, -20cSi_Li1993_293KSi
1()1.2, inf0Medium(permittivity=1.0)Medium(permittivity=1.0)
2'Oxide open (to BOX)'0, inf0Medium(permittivity=1.0)Medium(permittivity=1.0)
3'Si'0, 0.220cSi_Li1993_293KSi
4'Si slab'0, 0.090cSi_Li1993_293KSi
5'SiN'0, 0.40Si3N4_Luke2015_PMLStableSi3N4
6
'M2_router' +…… 'M1_heater'
1.2, 1.40Al_Rakic1995
LossyMetalMedium……(frequency_range=(100000000.0, 200000000000.0), fit_param={'attrs': {}, 'max_num_poles': 16, 'tolerance_rms': 0.001, 'frequency_sampling_points': 20, 'log_sampling': True, 'type': 'SurfaceImpedanceFitterParam'}, conductivity=38.0)
7'M2_router'1.4, 3.20Al_Rakic1995
LossyMetalMedium……(frequency_range=(100000000.0, 200000000000.0), fit_param={'attrs': {}, 'max_num_poles': 16, 'tolerance_rms': 0.001, 'frequency_sampling_points': 20, 'log_sampling': True, 'type': 'SurfaceImpedanceFitterParam'}, conductivity=38.0)
8'M_Open'3.2, inf0Medium(permittivity=1.0)Medium(permittivity=1.0)
9
'Deep Trench' +…… 'Isolation Trench' + 'Dicing'
-inf, inf0Medium(permittivity=1.0)Medium(permittivity=1.0)
Ports
NameClassificationDescriptionWidth (μm)Limits (μm)Radius (μm)ModesTarget n_effPath profiles (μm)Voltage pathCurrent path
MM_SiN_TE_1550_3000optical
Multimode SiN Strip TE 1550 nm,…… w=3000 nm
8-2.5, 2.9072.1'SiN': 3
MM_TE_1550_2000optical
Multimode Strip TE 1550 nm,…… w=2000 nm
6-2, 2.220123.5'Si': 2
MM_TE_1550_3000optical
Multimode Strip TE 1550 nm,…… w=3000 nm
6-2, 2.220173.5'Si': 3
Rib_TE_1310_350optical
Rib (90 nm slab) TE 1310 nm,…… w=350 nm
2.35-1, 1.22013.5
'Si': 0.35, 'Si…… slab': 3
Rib_TE_1550_500optical
Rib (90 nm slab) TE 1550 nm,…… w=500 nm
2.5-1, 1.22013.5
'Si': 0.5, 'Si…… slab': 3
SiN_TE-TM_1550_1000opticalSiN Strip TM 1550 nm, w=1000 nm3-1.5, 1.9022.1'SiN': 1
SiN_TE_1310_750opticalSiN Strip TE 1310 nm, w=750 nm3-1, 1.4012.1'SiN': 0.75
SiN_TE_1310_800opticalSiN Strip TE 1310 nm, w=800 nm3-1, 1.4012.1'SiN': 0.8
SiN_TE_1550_1000opticalSiN Strip TE 1550 nm, w=1000 nm3-1, 1.4012.1'SiN': 1
SiN_TE_1550_750opticalSiN Strip TE 1550 nm, w=750 nm3-1, 1.4012.1'SiN': 0.75
SiN_TE_1550_800opticalSiN Strip TE 1550 nm, w=800 nm3-1, 1.4012.1'SiN': 0.8
SiN_TE_895_450opticalSiN Strip TE 895 nm, w=450 nm2-1, 1.4012.1'SiN': 0.45
SiN_TM_1310_750opticalSiN Strip TM 1310 nm, w=750 nm3-1.5, 1.901 + 1 (TM)2.1'SiN': 0.75
SiN_TM_1550_1000opticalSiN Strip TM 1550 nm, w=1000 nm3-1.5, 1.901 + 1 (TM)2.1'SiN': 1
Slot_TE_1550_500optical
Slot TE 1550 nm, w=500 nm,…… gap=100nm
2-1, 1.22013.5
'Si': 0.2 (-0.15),…… 'Si': 0.2 (+0.15)
TE-TM_1550_450opticalStrip TE-TM 1550, w=450 nm2-1, 1.22023.5'Si': 0.45
TE_1310_350opticalStrip TE 1310 nm, w=350 nm2-1, 1.22013.5'Si': 0.35
TE_1310_410opticalStrip TE 1310 nm, w=410 nm2-1, 1.22013.5'Si': 0.41
TE_1550_500opticalStrip TE 1550 nm, w=500 nm2-1, 1.22013.5'Si': 0.5
TM_1310_350opticalStrip TM 1310 nm, w=350 nm2-1, 1.2201 + 1 (TM)3.5'Si': 0.35
TM_1550_500opticalStrip TM 1550 nm, w=500 nm2.5-1, 1.2201 + 1 (TM)3.5'Si': 0.5
eskid_TE_1550opticaleskid TE 15503.31-1, 1.22013.5
'Si': 0.35,…… 'Si': 0.06 (+0.265), 'Si': 0.06 (-0.265), 'Si': 0.06 (+0.385), 'Si': 0.06 (-0.385), 'Si': 0.06 (+0.505), 'Si': 0.06 (-0.505), 'Si': 0.06 (+0.625), 'Si': 0.06 (-0.625)
Background medium
  • Optical: SiO2_Palik_Lossless
  • Electrical: SiO2
Connections: []

Plot technology cross-section

Before using the PDK in a larger design, it’s good practice to sanity-check the stack visually.

The helper function below:

  • builds a small “test” component containing shapes on representative layers (Si, SiN, metals, oxide opening, trench),

  • plots a cross-section at y = 0 using pf.tidy3d_plot.

In the plot, x is the lateral position and z is the vertical stack height (both in µm).

[6]:
def plot_technology_cross_section(tech):
    """
    Create a test component and plot its cross-section to visualize
    the technology stack.

    Args:
        tech: PhotonForge Technology object
    """
    # Create test component with various layer types
    c = pf.Component("Technology_Cross_Section", technology=tech)

    # Add test structures on different layers
    c.add(
        # Silicon waveguide (full etch)
        "Si", pf.Rectangle((0, -0.5), (2, 0.5)),

        # Silicon nitride
        "SiN", pf.Rectangle((3, -0.5), (4, 0.5)),

        # Rib waveguide (slab + core)
        "Si slab", pf.Rectangle((5, -1.5), (8, 1.5)),
        "Si", pf.Rectangle((6, -0.25), (7, 0.25)),

        # Heater metal
        "M1_heater", pf.Rectangle((9, -0.5), (11, 0.5)),

        # Router metal with via opening
        "M2_router", pf.Rectangle((12, -1), (15, 1)),
        "M_Open", pf.Rectangle((13, -0.5), (14, 0.5)),

        # Oxide opening (to BOX)
        "Si", pf.Rectangle((16, -0.5), (18, 0.5)),
        "Oxide open (to BOX)", pf.Rectangle((16.5, -0.3), (17.5, 0.3)),

        # Deep trench
        "Deep Trench", pf.Rectangle((19, -0.5), (20, 0.5)),
    )

    # Plot cross-section at y=0
    fig, ax = plt.subplots(figsize=(14, 5))
    pf.tidy3d_plot(c, y=0, ax=ax)
    ax.set_title("Technology Stack Cross-Section (y=0)")
    ax.set_xlabel("x (μm)")
    ax.set_ylabel("z (μm)")

    return c
[7]:
# Generate and display a simple cross-section sanity-check plot
plot_technology_cross_section(tech)
[7]:
../_images/examples_Loading_Technology_13_0.svg
../_images/examples_Loading_Technology_13_1.png

List available components in the PDK

siepic_forge exposes the set of available components via siepic.component_names.

Use this list to discover valid names to instantiate with siepic.component(name).

[8]:
# All component names exposed by this PDK wrapper
siepic.component_names
[8]:
{'ANT_MMI_1x2_te1550_3dB_BB',
 'GC_SiN_TE_1310_8degOxide_BB',
 'GC_SiN_TE_1550_8degOxide_BB',
 'GC_TE_1310_8degOxide_BB',
 'GC_TE_1550_8degOxide_BB',
 'GC_TM_1310_8degOxide_BB',
 'GC_TM_1550_8degOxide_BB',
 'crossing_horizontal',
 'crossing_manhattan',
 'ebeam_BondPad',
 'ebeam_DC_2-1_te895',
 'ebeam_DC_te895',
 'ebeam_MMI_2x2_5050_te1310',
 'ebeam_Polarizer_TM_1550_UQAM',
 'ebeam_YBranch_895',
 'ebeam_YBranch_te1310',
 'ebeam_adiabatic_te1550',
 'ebeam_adiabatic_tm1550',
 'ebeam_bdc_te1550',
 'ebeam_crossing4',
 'ebeam_gc_te1550',
 'ebeam_gc_te895',
 'ebeam_gc_tm1550',
 'ebeam_routing_taper_te1550_w=500nm_to_w=3000nm_L=20um',
 'ebeam_routing_taper_te1550_w=500nm_to_w=3000nm_L=40um',
 'ebeam_splitter_swg_assist_te1310',
 'ebeam_splitter_swg_assist_te1550',
 'ebeam_terminator_SiN_1550',
 'ebeam_terminator_SiN_te895',
 'ebeam_terminator_te1310',
 'ebeam_terminator_te1550',
 'ebeam_terminator_tm1550',
 'ebeam_y_1550',
 'ebeam_y_adiabatic',
 'ebeam_y_adiabatic_500pin',
 'taper_SiN_750_3000',
 'taper_si_simm_1310',
 'taper_si_simm_1550'}

Load an example PDK component

Below we instantiate one component from the library and render it.

This is a quick check that the PDK is installed correctly and that the component’s layers/ports are available.

Tip: replace the component name with any entry from siepic.component_names.

[9]:
# Instantiate one component by name (change this string to explore the library)
siepic.component("ebeam_y_adiabatic_500pin")
[9]:
../_images/examples_Loading_Technology_17_0.svg