Layout¶
layout
¶
Layout integration: GDS files and gdsfactory components/netlists.
- :func:
resolve_layoutturns theGdsLayoutof aDeviceProbleminto extruded :class:~lightsmith.problems.Prismstructures. It uses a small built-in GDSII reader (no dependencies; flattens SREF/AREF with rotation, magnification and reflection), so every solver can call it. - :func:
from_gdsfactory_componentbuilds aDeviceProblemfrom a gdsfactory component: polygons become prisms with z extents and materials, gdsfactory ports becomePortplanes. - :func:
from_gdsfactory_netlistbuilds aCircuitProblemfrom a gdsfactory netlist, mapping gdsfactory cells to lightsmith compact models.
gdsfactory (MIT) is optional and imported lazily; install it with
pip install gdsfactory (or uv run --with gdsfactory). Note that recent
gdsfactory releases depend on kfactory and KLayout's Python module, which is
GPL-3.0-or-later; lightsmith itself never imports KLayout.
Units: gdsfactory works in um, lightsmith in nm; conversion happens here.
Translator
module-attribute
¶
DEFAULT_GDSFACTORY_MODELS
module-attribute
¶
DEFAULT_GDSFACTORY_MODELS: dict[str, Translator] = {'straight': _straight, 'taper': _straight, 'taper_strip_to_ridge': _straight, 'bend_euler': _bend, 'bend_circular': _bend, 'mmi1x2': _same('mmi1x2'), 'mmi2x2': _same('mmi2x2'), 'coupler': _same('coupler'), 'ring_single': _ring('ring_single'), 'ring_double': _ring('ring_double'), 'grating_coupler_elliptical': _same('grating_coupler'), 'grating_coupler_elliptical_trenches': _same('grating_coupler'), 'grating_coupler_rectangular': _same('grating_coupler')}
gdsfactory cell name -> translator(settings, info) giving an lightsmith instance (lengths um -> nm).
Geometry-dependent optics (coupling vs gap, neff vs width) are not derived: set them through defaults.
LayerSpec
¶
parse_layer
¶
parse_layer(key: Any) -> LayerKey
(layer, datatype) from a tuple, 'L/D' string, int (datatype 0) or gdsfactory layer enum/LayerInfo.
normalize_layer_map
¶
Accept {'1/0': {'material': ..., 'z_min_nm': .., 'z_max_nm': ..}} or LayerSpec values, any key form.
read_gds
¶
read_gds(path: str | Path, cell: str | None = None) -> dict[LayerKey, list[ndarray]]
Flattened polygons per (layer, datatype) in nm from a GDSII file.
cell defaults to the single top cell (one that no other cell references).
write_gds
¶
write_gds(path: str | Path, cells: Mapping[str, Mapping[str, Any]], dbu_nm: float = 1.0, libname: str = 'lightsmith') -> Path
Write a minimal GDSII file.
cells maps cell name -> {"polygons": {(layer, datatype) or 'L/D': [array (N, 2) in nm, ...]},
"refs": [{"cell": name, "origin_nm": (x, y), "angle_deg": 0, "magnification": 1,
"x_reflection": False, "columns": 1, "rows": 1, "spacing_nm": ((dx, 0), (0, dy))}]}.
polygons_to_structures
¶
polygons_to_structures(polygons: Mapping[LayerKey, list[ndarray]], layer_map: Mapping[Any, Any], warn_unmapped: bool = True) -> list[Structure]
Extrude polygons (nm) into Prism structures, in layer_map order (later layers override earlier ones).
layout_structures
¶
Structures for a GdsLayout (path resolved against base_dir when relative).
resolve_layout
¶
resolve_layout(problem: DeviceProblem, base_dir: str | Path | None = None) -> list[Structure]
All structures of a device: explicit structures first, then the extruded GDS layout.
Later structures override earlier ones where they overlap, so the layout
(waveguide cores) wins over explicit background slabs (substrate, cladding).
Solvers call this instead of reading problem.structures directly.
resolved_device
¶
resolved_device(problem: DeviceProblem, base_dir: str | Path | None = None) -> DeviceProblem
A copy of problem with the layout expanded into structures and layout cleared.
component_polygons
¶
component_polygons(component, merge: bool = False) -> dict[LayerKey, list[ndarray]]
Flattened polygons of a gdsfactory component per (layer, datatype), in nm.
gdsfactory_port
¶
gdsfactory_port(port, layer_map: Mapping[LayerKey, LayerSpec], margin_nm: float, num_modes: int, z_range: tuple[float, float]) -> Port
A gdsfactory port as an lightsmith Port plane (normal along x or y).
from_gdsfactory_component
¶
from_gdsfactory_component(component, layer_map: Mapping[Any, Any], *, background, wavelengths: Wavelengths, padding_nm: float = 1000.0, port_margin_nm: float = 1000.0, num_modes: int = 1, port_types: Iterable[str] | None = ('optical',), dimension: int = 3, extra_structures: Iterable[Structure] = (), merge: bool = False, options: dict[str, dict[str, Any]] | None = None) -> DeviceProblem
A DeviceProblem from a gdsfactory component.
layer_map maps layers ((1, 0), "1/0", a gdsfactory LAYER.WG or a PDK
layer name) to {"material": ..., "z_min_nm": ..., "z_max_nm": ...}; its order is the
override order. Each polygon becomes a Prism; ports of the given port_types
become Port planes sized width + 2 * port_margin_nm by thickness + 2 * port_margin_nm,
with direction pointing into the device. The domain is the bounding box of the mapped
geometry plus padding_nm. extra_structures (substrate, cladding) come first.
from_gdsfactory_netlist
¶
from_gdsfactory_netlist(netlist, wavelengths: Wavelengths, *, models: Mapping[str, str | Translator | Instance | dict] | None = None, defaults: Mapping[str, Mapping[str, Any]] | None = None, options: dict[str, dict[str, Any]] | None = None) -> CircuitProblem
A CircuitProblem from a gdsfactory component (its get_netlist()) or netlist dict.
models maps gdsfactory cell names to an lightsmith model name, a fixed
instance (dict/Instance) or a translator f(settings, info) -> instance; it
extends :data:DEFAULT_GDSFACTORY_MODELS (straight/taper -> waveguide using the
netlist's info.length, bends, MMIs, coupler, rings, grating couplers).
defaults adds settings per lightsmith model, e.g. {"waveguide": {"neff": 2.4,
"ng": 4.2}, "bend": {...}} (instance-specific settings win). Nets that touch ports the
target model does not have (e.g. electrical pads) are skipped with a warning.