Files
mxpic_forge/runs/build_all_primitives.py

302 lines
8.2 KiB
Python

from __future__ import annotations
import importlib
import inspect
import re
import sys
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
import nazca as nd
import time
import mxpic as mx
RUNS_DIR = ROOT / "runs"
OUTPUT_GDS = RUNS_DIR / "build_all_primitives.gds"
PRIMITIVE_EXPORT_PACKAGES = (
"mxpic.components.primitives.active",
"mxpic.components.primitives.passive",
"mxpic.components.primitives.pic",
)
SKIP_CLASSES = {"Route"}
def bootstrap_technology() -> None:
"""Register broad layer and xsection defaults used by primitive examples."""
mx.technologies.foundry["AMF"]["AMF_Si220_Active"]()
def discover_primitive_classes() -> list[type[Any]]:
classes: list[type[Any]] = []
seen_class_paths: set[str] = set()
for package_name in PRIMITIVE_EXPORT_PACKAGES:
package = importlib.import_module(package_name)
for _, cls in package.__dict__.items():
if not inspect.isclass(cls):
continue
if cls.__name__ in SKIP_CLASSES:
continue
if "__init__" not in cls.__dict__:
continue
if not (
cls.__module__ == package_name
or cls.__module__.startswith(package_name + ".")
):
continue
class_path = f"{cls.__module__}.{cls.__name__}"
if class_path in seen_class_paths:
continue
seen_class_paths.add(class_path)
classes.append(cls)
return classes
def safe_name(prefix: str, index: int, class_name: str) -> str:
cleaned = re.sub(r"[^A-Za-z0-9_]", "_", class_name)
return f"{prefix}{index:03d}_{cleaned}"[:32]
def get_cell(device: Any) -> nd.Cell:
if isinstance(device, nd.Cell):
return device
cell = getattr(device, "cell", None)
if isinstance(cell, nd.Cell):
return cell
raise TypeError(f"{type(device).__name__} did not expose a nazca Cell through .cell")
def build_fiber_coupler_seed() -> nd.Cell:
with nd.Cell(name="_seed_fiber_coupler", instantiate=False) as cell:
straight = nd.strt(length=10, width=0.45, xs="strip").put(0, 0, 0)
nd.Pin(name="g1", pin=straight.pin["a0"]).put()
nd.Pin(name="a0", pin=straight.pin["a0"]).put()
nd.Pin(name="b0", pin=straight.pin["b0"]).put()
return cell
class BuildContext(dict[str, Any]):
def __missing__(self, key: str) -> Any:
try:
builder = CONTEXT_BUILDERS[key]
except KeyError:
raise KeyError(key) from None
value = builder(self)
self[key] = value
return value
def build_context() -> BuildContext:
return BuildContext()
def build_via_i2m(_: BuildContext) -> Any:
from mxpic.components.electronics import Vias
return Vias(
xs="via_s2m",
area=[1.0, 1.0],
sz=[0.25, 0.25],
spacing=[0.35, 0.35],
xs_l1="p",
xs_l2="metal",
)
def build_via_h2m(_: BuildContext) -> Any:
from mxpic.components.electronics import Vias
return Vias(
xs="via_h2m",
area=[1.0, 1.0],
sz=[0.25, 0.25],
spacing=[0.35, 0.35],
xs_l1="heater",
xs_l2="metal",
)
def build_grating_unit(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.gratings import Grating_2D_Hole
return Grating_2D_Hole()
def build_psr(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.taper import PSR
return PSR(name="_seed_psr")
def build_mdm(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.couplers import MDM
return MDM(name="_seed_mdm")
def build_bragg(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.bragg import Bragg
return Bragg()
def build_crow_ring_device(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.rings import AED_ring
return AED_ring(name="_seed_aed_ring")
def build_crow_bus_device(_: BuildContext) -> Any:
from mxpic.components.primitives.pic.couplers import ring_bus_wg
return ring_bus_wg()
CONTEXT_BUILDERS = {
"fiber_coupler": lambda context: build_fiber_coupler_seed(),
"grating_unit": build_grating_unit,
"psr": build_psr,
"mdm": build_mdm,
"bragg": build_bragg,
"_crow_ring_device": build_crow_ring_device,
"crow_ring": lambda context: context["_crow_ring_device"].cell,
"crow_w_ring": lambda context: [0.45, 0.65],
"crow_sz_ring": lambda context: [20, 20],
"_crow_bus_device": build_crow_bus_device,
"crow_bus": lambda context: context["_crow_bus_device"].cell,
"crow_w_bus": lambda context: context["_crow_bus_device"].w,
"crow_sz_bus": lambda context: context["_crow_bus_device"].sz,
"via_h2m": build_via_h2m,
"via_i2m": build_via_i2m,
}
REQUIRED_VALUES: dict[str, Any] = {
"A_cp": 10,
"Brag": lambda context: context["bragg"],
"MDM": lambda context: context["mdm"],
"PSR": lambda context: context["psr"],
"R0": 10,
"R1": 6,
"bus": lambda context: context["crow_bus"],
"dLx": 0,
"dLy": 10,
"fiber_coupler": lambda context: context["fiber_coupler"],
"gap": 0.2,
"grating_unit": lambda context: context["grating_unit"],
"number": 4,
"pitch": 127,
"r0_rck": 6,
"r1_rck": 10,
"r_ring": 10,
"r_rck": 10,
"ring": lambda context: context["crow_ring"],
"sz_bus": lambda context: context["crow_sz_bus"],
"sz_ring": lambda context: context["crow_sz_ring"],
"w0": 0.45,
"w0_ring": 0.35,
"w0_rck": 0.45,
"w1": 0.45,
"w1_ring": 0.55,
"w1_rck": 0.45,
"w_bus": 0.45,
"w_ring": 0.45,
"w_rck": 0.45,
}
def class_overrides(
class_path: str,
_context: dict[str, Any],
) -> dict[str, Any]:
overrides = {
"mxpic.components.primitives.pic.couplers.ADC_STD_2x2": {"Rd1": 20},
}
return dict(overrides.get(class_path, {}))
def required_value(parameter: str, context: dict[str, Any]) -> Any:
value = REQUIRED_VALUES[parameter]
if callable(value):
return value(context)
return value
def build_kwargs(cls: type[Any], device_name: str, context: dict[str, Any]) -> dict[str, Any]:
signature = inspect.signature(cls)
kwargs: dict[str, Any] = {}
class_path = f"{cls.__module__}.{cls.__name__}"
for parameter_name, parameter in signature.parameters.items():
if parameter_name == "self":
continue
if parameter_name == "name":
kwargs[parameter_name] = device_name
continue
if parameter_name == "cell_name":
kwargs[parameter_name] = device_name
continue
if parameter_name == "via_i2m":
kwargs[parameter_name] = context["via_i2m"]
continue
if parameter.default is inspect.Parameter.empty:
kwargs[parameter_name] = required_value(parameter_name, context)
kwargs.update(class_overrides(class_path, context))
return kwargs
def build_top_cell(cell: nd.Cell, top_name: str) -> nd.Cell:
with nd.Cell(name=top_name, instantiate=False) as top_cell:
cell.put(0, 0, 0)
return top_cell
def main() -> None:
bootstrap_technology()
context = build_context()
topcells = []
failures = []
for index, cls in enumerate(discover_primitive_classes(), start=1):
device_name = safe_name("DEV", index, cls.__name__)
top_name = safe_name("TOP", index, cls.__name__)
class_path = f"{cls.__module__}.{cls.__name__}"
try:
kwargs = build_kwargs(cls, device_name, context)
device = cls(**kwargs)
topcells.append(build_top_cell(get_cell(device), top_name))
print(f"built {top_name}: {class_path}")
except Exception as exc:
failures.append((class_path, exc))
print(f"failed {class_path}: {exc}")
if failures:
print("\nPrimitive build failures:")
for class_path, exc in failures:
print(f"- {class_path}: {exc}")
raise RuntimeError(f"{len(failures)} primitive(s) failed to build")
nd.export_gds(topcells=topcells, filename=str(OUTPUT_GDS))
print(f"\nWrote {len(topcells)} top cells to {OUTPUT_GDS}")
if __name__ == "__main__":
main()