KLayout

KLayout Integration

Wrap laylight devices as KLayout PCells, register a library, and instantiate the result inside a KLayout layout flow.

This guide shows how to take a laylight device, expose it as a KLayout PCell, register it inside a kdb.Library, and instantiate it from a normal KLayout layout flow.

It focuses on the workflow, not the implementation details.

What you can do

With the current integration, you can:

  • wrap an AbstractDevice subclass as a KLayout PCell
  • register one or more wrapped devices in a kdb.Library
  • instantiate those PCells from another layout using KLayout's external-library PCell flow
  • validate the result with repository tests and PNG baselines

Prerequisites

This guide assumes:

  • you are working inside this repository
  • you already know the basics of klayout.db
  • laylight and klayout are available in your Python environment

For the validation steps in this guide:

  • klayout.db is required for PCell and library registration
  • klayout.lay is required for PNG baseline rendering

Step 1: Wrap a laylight device as a PCell

Use register_abstract_device_pcell(...) to expose a laylight device class as a KLayout PCell.

The most common case is a device with mostly simple scalar parameters. For example, WGStraight can be wrapped like this:

import laylight
from klayout import db as kdb

from laylight import PCellParameterSpec, register_abstract_device_pcell
from laylight.structure.wg import WGStraight


layout = laylight.Layout()
layout.dbu = 0.001

pcell_id = register_abstract_device_pcell(
    layout,
    "WGStraightPCell",
    WGStraight,
    parameter_specs={
        "length": PCellParameterSpec(default=20.0, description="Length"),
    },
)

Why does length need PCellParameterSpec here?

Because WGStraight.length defaults to None, and the helper only exposes parameters that have a concrete PCell default value. That means:

  • layout and tech are never exposed as PCell parameters
  • default=None parameters are skipped unless you explicitly provide a default
  • common scalar parameters such as float, int, bool, str, and LayerInfo can be mapped automatically

If you want a single-file smoke test for this step, run:

uv run --package laylight python -m pytest -q --import-mode=importlib packages/core/tests/test_pcell.py

Step 2: Register a KLayout library

Once you can register one PCell into a Layout, the next step is to register a kdb.Library.

This is the minimal pattern:

import laylight
from klayout import db as kdb

from laylight import PCellParameterSpec, register_abstract_device_pcell
from laylight.structure.ring import Ring
from laylight.structure.wg import WGStraight


class DemoLibrary(kdb.Library):
    def __init__(self) -> None:
        super().__init__()

        layout = self.layout()

        register_abstract_device_pcell(
            layout,
            "WGStraightPCell",
            WGStraight,
            parameter_specs={
                "length": PCellParameterSpec(default=40.0),
            },
        )
        register_abstract_device_pcell(
            layout,
            "RingPCell",
            Ring,
            parameter_specs={
                "width": PCellParameterSpec(default=0.5),
            },
        )

        self.description = "Demo laylight PCell library"
        self.register("Laylight Demo Library")


library = DemoLibrary()

After self.register(...) runs, the library is available through KLayout's library registry.

Step 3: Instantiate the PCell from a normal KLayout flow

The wrapped PCell is used through KLayout's external-library PCell API.

This is the flow:

  1. Look up the registered library
  2. Resolve the PCell ID from the library layout
  3. Create a variant in a target layout
  4. Insert that variant into a top cell

Example:

import laylight
from klayout import db as kdb


registered_library = kdb.Library.library_by_name("Laylight Demo Library", "*")
assert registered_library is not None

target_layout = laylight.Layout()
target_layout.dbu = 0.001
top = target_layout.create_cell("TOP")

pcell_id = registered_library.layout().pcell_id("WGStraightPCell")
variant_index = target_layout.add_pcell_variant(
    registered_library,
    pcell_id,
    {
        "length": 60.0,
        "width": 0.8,
        "slab_enable": True,
    },
)

variant = target_layout.cell(variant_index)
top.insert(kdb.DCellInstArray(variant.cell_index(), laylight.Trans()))

target_layout.write("demo_library_usage.gds")

At this point:

  • variant is the external-library proxy cell
  • the underlying laylight geometry is still generated by the wrapped device
  • the resulting layout can be written to GDS or used in a larger assembly flow

Step 4: Run the repository example in KLayout Macro Editor

The repository includes a ready-to-run script for KLayout Macro Editor:

packages/examples/laylight_examples/klayout_library.py

That script:

  • bootstraps packages/core into PYTHONPATH when needed
  • selects a seeded subset of AbstractDevice classes
  • wraps them with register_abstract_device_pcell(...)
  • registers a test library named Laylight Test Library

Try it in Macro Editor

  1. Open KLayout
  2. Open Macro Editor
  3. Open packages/examples/laylight_examples/klayout_library.py
  4. Run the script

If registration succeeds, the script prints the registered library name and the selected PCells.

If you prefer to run it from the repository root first:

uv run --package laylight-examples python packages/examples/laylight_examples/klayout_library.py

Step 5: Verify the geometry

There are two repository-level validation paths for this integration.

Validate the helper itself

This checks parameter exposure and single-PCell variant generation:

uv run --package laylight python -m pytest -q --import-mode=importlib packages/core/tests/test_pcell.py

Validate the library round trip

This checks the larger flow:

  • register a library
  • instantiate external-library PCell variants in a target layout
  • compare the rendered result with a PNG baseline

Run:

uv run --package laylight python -m pytest -q --import-mode=importlib packages/core/tests/test_pcell_library.py

The committed reference image for that test is:

packages/core/tests/baseline/pcell_library_shape.png

If this test passes, the current repository has verified:

  • library registration works
  • external-library PCell instantiation works
  • the generated layout still matches the expected geometry

Complete example

The following script puts the full workflow in one place:

import laylight
from klayout import db as kdb

from laylight import PCellParameterSpec, register_abstract_device_pcell
from laylight.structure.wg import WGStraight


class WaveguideLibrary(kdb.Library):
    def __init__(self) -> None:
        super().__init__()
        self.description = "Waveguide-only laylight library"

        register_abstract_device_pcell(
            self.layout(),
            "WGStraightPCell",
            WGStraight,
            parameter_specs={
                "length": PCellParameterSpec(default=20.0),
            },
        )

        self.register("Waveguide Library")


_library = WaveguideLibrary()

registered_library = kdb.Library.library_by_name("Waveguide Library", "*")
assert registered_library is not None

layout = laylight.Layout()
layout.dbu = 0.001
top = layout.create_cell("TOP")

pcell_id = registered_library.layout().pcell_id("WGStraightPCell")
variant_index = layout.add_pcell_variant(
    registered_library,
    pcell_id,
    {
        "length": 80.0,
        "width": 0.6,
        "slab_enable": False,
    },
)

variant = layout.cell(variant_index)
top.insert(kdb.DCellInstArray(variant.cell_index(), laylight.Trans()))

layout.write("waveguide_library_demo.gds")

Common limits

This integration is intentionally narrow. Keep these limits in mind:

  • parameters with default=None are not exposed automatically
  • object-valued or custom parameters may need an explicit PCellParameterSpec, or may need to stay out of the PCell interface entirely
  • this guide covers Python-driven KLayout flows, not a full KLayout GUI tutorial

If you want a more complex example, start from:

  • packages/examples/laylight_examples/klayout_library.py
  • packages/core/tests/test_pcell.py
  • packages/core/tests/test_pcell_library.py

On this page