Lumicron Documentation
DocumentationPDK authoringAnatomy of a PDK

Anatomy of a PDK

A lumicron PDK is a Python package - by convention, lumicron.pdks.<name> - exposing these namespaces:

Namespace Type Purpose
LAYER LayerTable Named layers + design rules.
STACK LayerStack Physical thicknesses + dispersive indices for simulators.
RP RouteProfiles Cross-section recipes routes can adopt.
(top-level) @pcell factories Component library - EdgeCouplerSi, Pad, NxM_MMI, …
TRANSITIONS TransitionRegistry Optional. Maps (layer_a, layer_b) to a bridge PCell.
META dict Display name, import package name, version, and publisher metadata.

The conventional file layout:

lumicron/pdks/<name>/
    __init__.py        # re-exports layers, stack, profiles, and components
    all.py             # convenience: `import ... .all as pdk`
    layers.py          # LAYER, STACK
    profiles.py        # RP, plus @route_profile defs
    components.py      # @pcell factories
    transitions.py     # TRANSITIONS, registers into default_transitions()
    meta.py            # META: display/import identity + release metadata

Each file is small. The discipline is keeping them small - when a PDK gets big, prefer adding new modules (pcells/, tests/) over fattening the four canonical files.

How an end user sees it

import lumicron as lm
import lumicron.pdks.elyon_demo.all as pdk

pdk.LAYER.SILC          # named layer with design rules
pdk.RP.silc_strip       # route profile
pdk.EdgeCouplerSi(...)  # component factory

The import segment is the PDK’s META["package-name"], not its human display name. Keep both explicit:

# meta.py
META = {
    "name": "TinyPhot Research PDK",  # shown in Lumicron
    "package-name": "tinyphot",       # used in Python imports
    "version": "0.1.0",
}

Display names may contain spaces; package names must be valid Python identifiers. New packages use package-name; the current packer rejects package_name.

The .all re-export gives end users the convention of one short import. Implementing it is a single file:

# lumicron/pdks/<name>/all.py
from .layers import LAYER, STACK
from .profiles import RP
from .transitions import TRANSITIONS
from .components import *  # noqa

The transitions import - even though TRANSITIONS itself is rarely referenced by name - is what registers the transitions into the process-wide default registry. Without it, end users would have to remember to import the module or pass transitions= on every cross-layer route. With it, things just work.

What ships in the package vs the manifest

Two questions that come up:

  • “Should the layer table go in Python or in JSON?” Both. The Python LayerTable is the source of truth - the router reads it, components reference it. The .lumpdk manifest exposes a JSON layer map so the Mac app can render colors / names without importing the Python source. They’re generated from each other; in lumicron, LayerTable.to_manifest() produces the JSON.
  • “Should the layer stack ship?” Yes when you have it. End-user scripts that drive simulators read it. PDKs without a known stack can ship Python-only and add the stack later - components and routing don’t depend on it.

What this guide does not cover

  • DRC rules beyond the per-layer minimums. The full Lumicron DRC system is documented separately in Docs/DRC_DESIGN.md (forthcoming).
  • Verification / LVS. Same - separate doc.
  • Foundry-confidential details. Whatever you ship in your PDK is up to you and your foundry agreement; this guide is process-agnostic.

Search documentation

Type to search all guides and API references.

↑ ↓ to select · Enter to open · Esc to close