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. In short: when KLayout asks for a PCell variant, the device is built in a laylight Layout, compiled to GDSII by the laylight command line, and the result is copied into the variant's cell, without the marker texts laylight writes for ports.
What you can do
With the current integration, you can:
- wrap an
AbstractDevicesubclass 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 the repository tests
Prerequisites
This guide assumes:
- you are working inside this repository
- you already know the basics of
klayout.db laylightwith its KLayout extra is available in your Python environment:pip install "laylight[klayout]"
KLayout objects stay on the KLayout side: the target layout is a kdb.Layout and instances use kdb.DCplxTrans. The laylight.Layout and laylight.Trans types belong to laylight's own layouts.
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:
from klayout import db as kdb
from laylight import PCellParameterSpec, register_abstract_device_pcell
from laylight.structure.wg import WGStraight
layout = kdb.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:
layoutandtechare never exposed as PCell parametersdefault=Noneparameters are skipped unless you explicitly provide a default- common scalar parameters such as
float,int,bool,str, andLayerInfocan 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 pypkg/laylight/tests/test_pcell.pyStep 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:
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:
- Look up the registered library
- Resolve the PCell ID from the library layout
- Create a variant in a target layout
- Insert that variant into a top cell
Example:
from klayout import db as kdb
registered_library = kdb.Library.library_by_name("Laylight Demo Library", "*")
assert registered_library is not None
target_layout = kdb.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(), kdb.DCplxTrans()))
target_layout.write("demo_library_usage.gds")At this point:
variantis 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:
pypkg/laylight-examples/laylight_examples/klayout_library.pyThat script:
- bootstraps
pypkg/laylightintoPYTHONPATHwhen needed - selects a seeded subset of
AbstractDeviceclasses - wraps them with
register_abstract_device_pcell(...) - registers a test library named
Laylight Test Library
Try it in Macro Editor
- Open KLayout
- Open Macro Editor
- Open
pypkg/laylight-examples/laylight_examples/klayout_library.py - 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 command line first:
uv run --package laylight-examples python pypkg/laylight-examples/laylight_examples/klayout_library.pyStep 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 pypkg/laylight/tests/test_pcell.pyValidate the library round trip
This checks the larger flow:
- register a library
- instantiate external-library PCell variants in a target layout
- compare the flattened geometry with a GDS baseline
Run:
uv run --package laylight python -m pytest -q --import-mode=importlib pypkg/laylight/tests/test_pcell_library.pyThe committed reference image for that test is:
pypkg/laylight/tests/baseline/pcell_library_shape.gdsIf 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:
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 = kdb.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(), kdb.DCplxTrans()))
layout.write("waveguide_library_demo.gds")Common limits
This integration is intentionally narrow. Keep these limits in mind:
- parameters with
default=Noneare 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:
pypkg/laylight-examples/laylight_examples/klayout_library.pypypkg/laylight/tests/test_pcell.pypypkg/laylight/tests/test_pcell_library.py