PDK¶
pdk
¶
Component-to-circuit design flow (an open process design kit).
- :mod:
.technology: layer stacks as data; :func:generic_soiis a generic 220 nm SOI stack built from open material data only. - :mod:
.cells: parametric components (layout polygons + ports + simulation recipe). - :mod:
.component: :class:Component, GDS output, device problems at the port reference planes, and :class:Assemblyfor hierarchical layouts. - :mod:
.flow: simulate a component (dispatching through :func:lightsmith.solve), fit a compact model of its S-parameters, cache it by parameter hash, and use it in circuit netlists aspdk:<cell>.
Assembly
¶
Place components by connecting their ports; produce a hierarchical :class:Component.
asm.add("mmi_in", mmi) places a first instance; asm.connect("arm", bend, "o1", "mmi_in,o2")
moves bend so that its port o1 meets mmi_in's o2 (facing it) and records the connection;
asm.expose("in", "mmi_in,o1") makes an external port. asm.build(kind, settings) returns the
composite component whose circuit model is the netlist of its parts.
add
¶
add(name: str, component: Component, origin=(0.0, 0.0), rotation_deg: float = 0.0, mirror: bool = False) -> Instance
connect
¶
connect(name: str, component: Component, port: str, to: str, mirror: bool = False) -> Instance
Add component with its port attached to the placed port to ('instance,port').
link
¶
Record a connection between two already placed ports (they must coincide and face each other).
Component
dataclass
¶
Component(kind: str, settings: dict[str, Any], polygons: dict[str, list[ndarray]] = dict(), ports: list[CPort] = list(), instances: list[Instance] = list(), netlist: dict[str, Any] | None = None, model: str = 'simulate', sim: dict[str, Any] = dict())
Layout + ports + parameters of one cell.
kind is the registry name of the parametric cell (mmi1x2, ...) and
settings its validated parameters; together they identify the circuit
model pdk:<kind>. Composite components (from :class:Assembly) also
carry instances and a netlist of their parts.
polygons
class-attribute
instance-attribute
¶
instances
class-attribute
instance-attribute
¶
flat_polygons
¶
All polygons including those of placed instances, in this component's coordinates.
gds_cells
¶
gds_cells(tech: Technology | str | None = None) -> dict[str, dict[str, Any]]
Cells for :func:lightsmith.layout.write_gds: this cell plus every distinct child cell.
write_gds
¶
write_gds(path: str | Path, tech: Technology | str | None = None) -> Path
Write this component (hierarchically) to a GDSII file; the top cell is cell_name.
device_problem
¶
device_problem(tech: Technology | str | None = None, wavelengths: Wavelengths | None = None, *, dimension: int = 2, options: dict[str, dict[str, Any]] | None = None, port_margin_nm: float = 1500.0, padding_nm: float = 1500.0) -> DeviceProblem
A :class:DeviceProblem of this component with ports just outside its reference planes.
dimension=2: plan view (effective-index materials, structures in xy); dimension=3: polygons
extruded with the technology's z ranges. Each port gets a straight input stub reaching
through the absorbing boundary, and a mode window of width + 2 * port_margin_nm (narrowed so
that windows of neighbouring ports on the same plane do not overlap). Default solver options
of the component are merged under options. Solver ports sit PORT_OFFSET_NM outside the
component's ports; :func:lightsmith.pdk.flow.simulate moves S back onto the component's ports.
CPort
dataclass
¶
CPort(name: str, center: tuple[float, float], orientation_deg: float, width_nm: float, layer: str = 'WG')
An optical port of a component: reference plane centre, outward orientation, waveguide width.
Transform
dataclass
¶
Transform(origin: tuple[float, float] = (0.0, 0.0), rotation_deg: float = 0.0, mirror: bool = False)
Mirror about the x axis (optional), then rotate about the origin, then translate (GDS order).
LayerDef
¶
Bases: _Model
One GDS layer of a technology.
gds
class-attribute
instance-attribute
¶
kind
class-attribute
instance-attribute
¶
kind: Literal['material', 'etch', 'drawing'] = Field('material', description='material: polygons are filled with `material` over z_min..z_max; etch: polygons remove the core over z_min..z_max (filled with the cladding); drawing: no optical effect (floorplan, labels).')
sidewall_angle_deg
class-attribute
instance-attribute
¶
Technology
¶
Bases: _Model
A planar photonic technology: patterned layers on a buried oxide, under a cladding.
z = 0 is the top of the buried oxide (bottom of the device layer). Layers are listed in override order: later layers win where polygons overlap (so an etch layer listed after the core removes part of it).
layers
class-attribute
instance-attribute
¶
layers: dict[str, LayerDef] = Field(description='Layer name -> definition, in override order.')
core_layer
class-attribute
instance-attribute
¶
core_layer: str = Field('WG', description='Layer of the waveguide core (sets ports and mode solves).')
cladding
class-attribute
instance-attribute
¶
cladding: Material = Field(description='Material above and around the patterned layers.')
box
class-attribute
instance-attribute
¶
box: Material = Field(description='Buried oxide below z = 0.')
substrate
class-attribute
instance-attribute
¶
substrate: Material | None = Field(None, description='Handle wafer below the buried oxide (None = omit).')
wg_width_nm
class-attribute
instance-attribute
¶
min_feature_nm
class-attribute
instance-attribute
¶
min_feature_nm: float = Field(100.0, gt=0, description='Smallest drawn width or gap the components use.')
min_bend_radius_nm
class-attribute
instance-attribute
¶
polarization
class-attribute
instance-attribute
¶
polarization: Literal['te', 'tm'] = Field('te', description='Mode family: te = E mostly in the wafer plane.')
sources
class-attribute
instance-attribute
¶
sources: list[str] = Field(default_factory=list, description='Where the numbers come from (open data only).')
layer_map
¶
GDS layer -> {material, z_min_nm, z_max_nm, ...} for :mod:lightsmith.layout (3D extrusion).
Etch layers are filled with the cladding, which overrides the core where they overlap.
core_thickness_nm
¶
Thickness of core material left where layer is drawn (an etch layer thins the core).
slab_neff
¶
Effective index of the box / core(thickness) / cladding slab, for this technology's polarization.
eim_material
¶
eim_material(layer: str, wavelengths_nm, step_nm: float = 2.0) -> Material
2D material for regions drawn on layer: the slab effective index, tabulated over the wavelengths.
eim_cladding
¶
eim_cladding(wavelengths_nm) -> Material
2D background material: the cladding (no core drawn).
waveguide_neff
¶
waveguide_neff(wavelengths_nm, width_nm: float | None = None, dimension: int = 2, mode: int = 0, layer: str | None = None) -> ndarray
Effective index of a straight waveguide of width_nm on layer (default: the core, default width).
dimension=2: symmetric slab of the EIM core index in the cladding, E in plane (matches 2D EME/FDFD with polarization 'te'); dimension=3: full-vector FDE of the extruded cross-section.
group_index
¶
group_index(wavelengths_nm, width_nm: float | None = None, dimension: int = 2, layer: str | None = None, dl_nm: float = 1.0) -> ndarray
ng = neff - lambda dneff/dlambda (central difference).
cell
¶
Register a parametric cell fn(params) -> Component under name.
get_component
¶
get_component(name: str, **settings) -> Component
Build a registered component, e.g. get_component("mmi1x2", length_mmi_nm=6000).
generic_soi
¶
generic_soi() -> Technology
Generic 220 nm SOI: Si device layer on 2 um buried oxide with oxide cladding (open data only).
get_technology
¶
get_technology(tech: str | dict | Technology | None = None) -> Technology
A technology by name, JSON dict, file path (.json) or instance; None = generic_soi.
load_technology
¶
load_technology(path: str | Path) -> Technology
Read a technology from a JSON file (the output of tech.model_dump_json()).
register_technology
¶
register_technology(tech: Technology, overwrite: bool = False) -> Technology
Make tech available by name (e.g. in circuit netlists: settings.technology).
flow
¶
Component -> simulation -> fitted compact model -> circuit netlists.
simulate(component) builds the component's :class:DeviceProblem and solves
it with :func:lightsmith.solve (method="auto" walks AUTO_ORDER, so EME,
FDFD or any later device backend is chosen by what the device needs), then
moves FDFD's grid-snapped port planes back onto the component's reference
planes. Straight waveguides use a mode solve instead, and composite components
(rings, the MZI) evaluate the circuit of their parts.
component_model(...) fits the sampled S-parameters (:mod:.fitting) and
caches the result on disk under a hash of everything that determines it
(cell, parameters, technology, wavelengths, solver choice and options).
In a circuit netlist, {"component": "pdk:mmi1x2", "settings": {"length_mmi_nm": 4300}}
instantiates that model; the settings are the cell's parameters plus
:class:FlowSettings (technology, simulated band, solver, fit). Missing models
are simulated on first use.
FlowSettings
¶
Bases: ModelSettings
How a pdk:<cell> circuit model is obtained (the rest of its settings are the cell parameters).
technology
class-attribute
instance-attribute
¶
technology: str | dict[str, Any] = Field('generic_soi', description='Registered technology name or its JSON.')
wl_min_nm
class-attribute
instance-attribute
¶
wl_min_nm: float = Field(1500.0, gt=0, description='Simulated band (the circuit must stay inside it).')
num_points
class-attribute
instance-attribute
¶
method
class-attribute
instance-attribute
¶
method: str = Field('auto', description="Device solver (auto = first applicable in AUTO_ORDER['device']).")
device
class-attribute
instance-attribute
¶
dimension
class-attribute
instance-attribute
¶
dimension: Literal[2, 3] = Field(2, description='2 = plan view with effective indices; 3 = full 3D.')
waveguide_model
class-attribute
instance-attribute
¶
waveguide_model: Literal['mode', 'device'] = Field('mode', description='Straights: S from a mode solve (exact phase, any length), or a device solve with `method` (same discretisation as a direct simulation of a larger layout).')
fit_tol
class-attribute
instance-attribute
¶
fit_tol: float = Field(0.005, gt=0, description='Max |S_fit - S| on held-out samples for a pole-residue fit.')
sim_options
class-attribute
instance-attribute
¶
sim_options: dict[str, dict[str, Any]] = Field(default_factory=dict, description='Per-method solver options.')
cache
class-attribute
instance-attribute
¶
FittedModel
¶
FittedModel(kind: str, settings: dict, technology: str, key: str, ports: list[str], result: SParameterResult, fit: SFit)
A cached compact model: sampled S-parameters, the fit, and where they came from.
neff_poly
property
¶
Polynomial neff(lambda) (degree <= 3) for mode-solved straights, else None.
simulate
¶
simulate(component: Component | str, tech: Technology | str | dict | None = None, wavelengths: Wavelengths | None = None, *, method: str = 'auto', device: str = 'auto', dimension: int = 2, options: dict[str, dict[str, Any]] | None = None, direct: bool = False, **settings) -> SParameterResult
S-parameters of a component at its port reference planes.
component is a :class:Component or a cell name (with settings). By default straights use a mode
solve and composites the circuit of their parts; direct=True solves the whole layout as one device.
method/device/options go to :func:lightsmith.solve (options per method, e.g.
{"fdfd": {"dl_nm": 25}}).
cache_dir
¶
Model cache directory: LIGHTSMITH_PDK_CACHE, else <lightsmith cache>/pdk.
model_key
¶
model_key(kind: str, settings: dict[str, Any], tech: Technology, flow: FlowSettings) -> str
component_model
¶
component_model(cell: str | Component, flow: FlowSettings | dict | None = None, *, refresh: bool = False, **settings) -> FittedModel
The fitted compact model of a cell (simulating it if no cached model matches).
component_model("mmi1x2", length_mmi_nm=4300) or component_model(comp, flow={"num_points": 21}).
netlist_problem
¶
netlist_problem(comp: Component, wavelengths: Wavelengths, flow: FlowSettings | dict | None = None, port_names: dict[str, str] | None = None) -> CircuitProblem
The circuit of a composite component's parts, every pdk: part using flow.
instance
¶
instance(cell: str, flow: FlowSettings | dict | None = None, **settings) -> Instance
A netlist instance pdk:<cell> with validated cell parameters and flow settings.
pdk_model
¶
The circuit :class:CompactModel behind pdk:<cell> (used by circuits.models.get_model).
fsr_estimate
¶
fsr_estimate(tech: Technology, length_nm: float, wavelength_nm: float = 1550.0, width_nm: float | None = None, dimension: int = 2) -> float
lambda^2 / (ng L) with the technology's group index.