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
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 repository tests and PNG baselines
Prerequisites
This guide assumes:
- you are working inside this repository
- you already know the basics of
klayout.db laylightandklayoutare available in your Python environment
For the validation steps in this guide:
klayout.dbis required for PCell and library registrationklayout.layis 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:
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 packages/core/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:
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:
- 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:
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:
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:
packages/examples/laylight_examples/klayout_library.pyThat script:
- bootstraps
packages/coreintoPYTHONPATHwhen 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
packages/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 repository root first:
uv run --package laylight-examples python packages/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 packages/core/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 rendered result with a PNG baseline
Run:
uv run --package laylight python -m pytest -q --import-mode=importlib packages/core/tests/test_pcell_library.pyThe committed reference image for that test is:
packages/core/tests/baseline/pcell_library_shape.pngIf 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=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:
packages/examples/laylight_examples/klayout_library.pypackages/core/tests/test_pcell.pypackages/core/tests/test_pcell_library.py