diff --git a/build_images.py b/build_images.py index d4116ac..2097aa2 100644 --- a/build_images.py +++ b/build_images.py @@ -1,5 +1,8 @@ import importlib +import os from pathlib import Path +import sys +from typing import Any, Optional import matplotlib @@ -7,7 +10,27 @@ matplotlib.use("Agg") import matplotlib.pyplot as plt import nazca as nd -from generate_handbook import DEFAULT_PACKAGE_ROOT, DEFAULT_SRC_ROOT, public_top_level_members +from generate_handbook import DEFAULT_SRC_ROOT + + +_PRIMITIVE_BUILDER_PATH = Path(__file__).resolve().parent / "tests" / "build_all_primitives.py" +_PRIMITIVE_BUILDER_SPEC = importlib.util.spec_from_file_location( + "mxpic_build_all_primitives", + _PRIMITIVE_BUILDER_PATH, +) +if _PRIMITIVE_BUILDER_SPEC is None or _PRIMITIVE_BUILDER_SPEC.loader is None: + raise ImportError(f"Could not load primitive builder: {_PRIMITIVE_BUILDER_PATH}") + +_primitive_builder = importlib.util.module_from_spec(_PRIMITIVE_BUILDER_SPEC) +_PRIMITIVE_BUILDER_SPEC.loader.exec_module(_primitive_builder) + +PRIMITIVES_PACKAGE = _primitive_builder.PRIMITIVES_PACKAGE +bootstrap_technology = _primitive_builder.bootstrap_technology +build_context = _primitive_builder.build_context +build_kwargs = _primitive_builder.build_kwargs +discover_primitive_classes = _primitive_builder.discover_primitive_classes +get_cell = _primitive_builder.get_cell +safe_name = _primitive_builder.safe_name PALETTE = { @@ -23,9 +46,63 @@ PALETTE = { } +USE_COLOR = sys.stdout.isatty() and os.environ.get("NO_COLOR") is None +COLORS = { + "bold": "\033[1m", + "cyan": "\033[36m", + "green": "\033[32m", + "red": "\033[31m", + "yellow": "\033[33m", + "reset": "\033[0m", +} +STATUS_STYLES = { + "info": ("▶", "cyan", "Info"), + "generated": ("✓", "green", "Generated"), + "skipped": ("○", "yellow", "Skipped"), + "failed": ("✗", "red", "Failed"), +} +STATUS_FALLBACK_SYMBOLS = { + "info": ">", + "generated": "+", + "skipped": "-", + "failed": "x", +} + + +def colorize(text: str, color: str) -> str: + if not USE_COLOR: + return text + return f"{COLORS[color]}{text}{COLORS['reset']}" + + +def can_print(text: str) -> bool: + encoding = sys.stdout.encoding or "utf-8" + if not encoding.lower().replace("-", "").startswith("utf"): + return text.isascii() + + try: + text.encode(encoding) + except UnicodeEncodeError: + return False + return True + + +def status_item(status: str, count: Optional[int] = None) -> str: + symbol, color, label = STATUS_STYLES[status] + if not can_print(symbol): + symbol = STATUS_FALLBACK_SYMBOLS[status] + + suffix = f": {count}" if count is not None else "" + return colorize(f"{symbol} {label}{suffix}", color) + + +def print_status(status: str, message: str) -> None: + print(f"{status_item(status)} {message}") + + def apply_mxpic_colors() -> None: """Apply the mxPIC color palette to Nazca when the layers are available.""" - print("Applying mxPIC layer colors...") + print_status("info", "Applying mxPIC layer colors...") for layer_id, color in PALETTE.items(): try: nd.set_layercolor(layer=layer_id, color=color) @@ -47,11 +124,17 @@ def module_name_for(py_file: Path, src_root: Path, package_root: str) -> str: return f"{package_root}.{'.'.join(relative_path.parts)}" -def generate_image_for_class(target_dir: Path, class_name: str, component_class: type) -> str: +def generate_image_for_class( + target_dir: Path, + class_name: str, + component_class: type, + device_name: Optional[str] = None, + context: Optional[dict[str, Any]] = None, +) -> str: """Instantiate one component class and save its cell image.""" - nd.clear_layout() - instance = component_class() - cell = getattr(instance, "cell", None) + kwargs = build_kwargs(component_class, device_name, context) if context else {} + instance = component_class(**kwargs) + cell = get_cell(instance) if context else getattr(instance, "cell", None) if cell is None: return "skipped" @@ -64,68 +147,70 @@ def generate_image_for_class(target_dir: Path, class_name: str, component_class: def generate_component_images( - img_root: Path = Path("images/components"), - src_root: Path = DEFAULT_SRC_ROOT, - package_root: str = DEFAULT_PACKAGE_ROOT, + img_root: Path = Path("docs/source/images"), + src_root: Path = DEFAULT_SRC_ROOT / "primitives", + package_root: str = PRIMITIVES_PACKAGE, ) -> dict[str, int]: - print("Starting mxPIC component image generation...") + print(colorize("mxPIC primitive image generation", "bold")) img_root = Path(img_root) src_root = Path(src_root) counts = {"generated": 0, "skipped": 0, "failed": 0} if not src_root.exists(): - print(f"Source directory not found: {src_root}") + print_status("failed", f"Source directory not found: {src_root}") counts["failed"] += 1 return counts img_root.mkdir(parents=True, exist_ok=True) remove_generated_pngs(img_root) + nd.clear_layout() + bootstrap_technology() apply_mxpic_colors() + context = build_context() - for py_file in sorted(src_root.rglob("*.py")): - if py_file.name == "__init__.py": - continue + for index, component_class in enumerate(discover_primitive_classes(), start=1): + class_name = component_class.__name__ + module_name = component_class.__module__ + module_suffix = module_name.removeprefix(f"{package_root}.") + target_dir = img_root / Path(*module_suffix.split(".")[:-1]) - module_name = module_name_for(py_file, src_root, package_root) try: - module = importlib.import_module(module_name) + device_name = safe_name("IMG", index, class_name) + result = generate_image_for_class( + target_dir, + class_name, + component_class, + device_name=device_name, + context=context, + ) except Exception as error: - print(f"Skipped module import {module_name}: {error}") - counts["skipped"] += 1 + plt.close() + print_status("failed", f"{module_name}.{class_name}: {error}") + counts["failed"] += 1 continue - class_names, _ = public_top_level_members(py_file) - for class_name in class_names: - component_class = getattr(module, class_name, None) - if component_class is None: - print(f"Skipped missing class {module_name}.{class_name}") - counts["skipped"] += 1 - continue - - try: - target_dir = img_root / py_file.relative_to(src_root).parent - result = generate_image_for_class(target_dir, class_name, component_class) - except Exception as error: - plt.close() - print(f"Failed image for {module_name}.{class_name}: {error}") - counts["failed"] += 1 - continue - - counts[result] += 1 - if result == "generated": - print(f"Generated: {target_dir / f'{class_name}.png'}") - else: - print(f"Skipped class without cell: {module_name}.{class_name}") + counts[result] += 1 + if result == "generated": + print_status("generated", str(target_dir / f"{class_name}.png")) + else: + print_status("skipped", f"{module_name}.{class_name} has no cell") print( - "Image generation complete. " - f"Generated: {counts['generated']} | " - f"Skipped: {counts['skipped']} | " - f"Failures: {counts['failed']}" + "\n" + + colorize("Image generation complete.", "bold") + + " " + + status_item("generated", counts["generated"]) + + " | " + + status_item("skipped", counts["skipped"]) + + " | " + + status_item("failed", counts["failed"]) ) + if counts["failed"]: + raise RuntimeError(f"{counts['failed']} primitive image(s) failed to build") + return counts if __name__ == "__main__": - generate_component_images(img_root=Path("docs/source/mxpic/components")) + generate_component_images() diff --git a/build_release.py b/build_release.py index 475d0e6..4f91bd2 100644 --- a/build_release.py +++ b/build_release.py @@ -56,7 +56,7 @@ def build_and_harvest(): # shutil.copy2(file_path, target_dir / file) # (Optional) Copy non-code assets like config files or templates - elif file.endswith((".json", ".yaml", ".yml")): + elif file.endswith((".csv", ".json", ".yaml", ".yml")): shutil.copy2(file_path, target_dir / file) print("✨ Cleanup: Removing intermediate .c files from source...") @@ -66,4 +66,4 @@ def build_and_harvest(): print(f"\n🚀 Success! Your secure package is ready in: {release_dir.parent.resolve()}") if __name__ == "__main__": - build_and_harvest() \ No newline at end of file + build_and_harvest() diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index 9b016fe..c841434 100644 Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/mxpic/components/composites/LoopMirror.doctree b/docs/build/doctrees/mxpic/components/composites/LoopMirror.doctree index 3313025..3c9f7d5 100644 Binary files a/docs/build/doctrees/mxpic/components/composites/LoopMirror.doctree and b/docs/build/doctrees/mxpic/components/composites/LoopMirror.doctree differ diff --git a/docs/build/doctrees/mxpic/components/composites/MZI.doctree b/docs/build/doctrees/mxpic/components/composites/MZI.doctree index 9aa4a13..61a570e 100644 Binary files a/docs/build/doctrees/mxpic/components/composites/MZI.doctree and b/docs/build/doctrees/mxpic/components/composites/MZI.doctree differ diff --git a/docs/build/doctrees/mxpic/components/composites/MZI_mesh.doctree b/docs/build/doctrees/mxpic/components/composites/MZI_mesh.doctree index 798cfd2..5deb0e9 100644 Binary files a/docs/build/doctrees/mxpic/components/composites/MZI_mesh.doctree and b/docs/build/doctrees/mxpic/components/composites/MZI_mesh.doctree differ diff --git a/docs/build/doctrees/mxpic/components/composites/SptTree.doctree b/docs/build/doctrees/mxpic/components/composites/SptTree.doctree index b123e3e..f438b0f 100644 Binary files a/docs/build/doctrees/mxpic/components/composites/SptTree.doctree and b/docs/build/doctrees/mxpic/components/composites/SptTree.doctree differ diff --git a/docs/build/doctrees/mxpic/components/composites/advance.doctree b/docs/build/doctrees/mxpic/components/composites/advance.doctree index 3d07162..902fe03 100644 Binary files a/docs/build/doctrees/mxpic/components/composites/advance.doctree and b/docs/build/doctrees/mxpic/components/composites/advance.doctree differ diff --git a/docs/build/doctrees/mxpic/components/primitives/passive/crows.doctree b/docs/build/doctrees/mxpic/components/primitives/passive/crows.doctree index 027f514..5831245 100644 Binary files a/docs/build/doctrees/mxpic/components/primitives/passive/crows.doctree and b/docs/build/doctrees/mxpic/components/primitives/passive/crows.doctree differ diff --git a/docs/build/doctrees/mxpic/components/primitives/pic/spiral.doctree b/docs/build/doctrees/mxpic/components/primitives/pic/spiral.doctree index cfe522c..a9306b0 100644 Binary files a/docs/build/doctrees/mxpic/components/primitives/pic/spiral.doctree and b/docs/build/doctrees/mxpic/components/primitives/pic/spiral.doctree differ diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html index b9b7a78..af8028c 100644 --- a/docs/build/html/genindex.html +++ b/docs/build/html/genindex.html @@ -364,14 +364,46 @@

C

+ -
  • Cross (class in mxpic.components.primitives.pic.cross)
  • -
    • Cross_Sine (class in mxpic.components.primitives.pic.cross)
    • CROW_AED (class in mxpic.components.primitives.passive.crows) diff --git a/docs/build/html/mxpic/components/composites/LoopMirror.html b/docs/build/html/mxpic/components/composites/LoopMirror.html index ada2900..14988db 100644 --- a/docs/build/html/mxpic/components/composites/LoopMirror.html +++ b/docs/build/html/mxpic/components/composites/LoopMirror.html @@ -375,12 +375,38 @@

      mxpic.components.composites.LoopMirror#

      +

      Loop mirror composite layouts.

      LoopMirror#

      class mxpic.components.composites.LoopMirror.LoopMirror(xs_wg='strip', w_wg=0.45, Radius=20, angle=45, sharp_patch=True, BS=None)#

      Bases: object

      +

      Loop mirror reflector built from a beam splitter and return bend.

      +
      +
      Parameters:
      +
        +
      • xs_wg (str, optional) – Waveguide cross-section name.

      • +
      • w_wg (float, optional) – Optical waveguide width in microns.

      • +
      • Radius (float, optional) – Bend radius used for the loop mirror routing.

      • +
      • angle (float, optional) – Bend angle in degrees for the side bends.

      • +
      • sharp_patch (bool, optional) – Add cladding patch geometry around sharp loop features.

      • +
      • BS (Any, optional) – Beam splitter object or cell. If not provided, a default directional +coupler is generated.

      • +
      +
      +
      +
      +
      +cell#
      +

      Generated loop mirror layout cell.

      +
      +
      Type:
      +

      nazca.Cell

      +
      +
      +
      +
      @@ -434,7 +460,10 @@
      @@ -408,6 +529,43 @@
      class mxpic.components.composites.MZI.MZI_2st_ubend(name, BS1, BS2, BS3, xs_wg, w1, w2, L0, Ln1, Ls1, Ln2, Ls2, Ltp, R_bend, w_wg, L_patch, via_h2m=None, isl=None, L12=None)#

      Bases: object

      +

      Two-stage U-bend MZI composite with three beam splitters.

      +
      +
      Parameters:
      +
        +
      • name (str) – Nazca cell name.

      • +
      • BS1 (Any) – Beam splitter cells or objects for the three splitter stages.

      • +
      • BS2 (Any) – Beam splitter cells or objects for the three splitter stages.

      • +
      • BS3 (Any) – Beam splitter cells or objects for the three splitter stages.

      • +
      • xs_wg (str) – Optical waveguide cross-section name.

      • +
      • w1 (float) – Input/output waveguide width.

      • +
      • w2 (float) – Internal arm waveguide width.

      • +
      • L0 (Any) – Input/output straight section length.

      • +
      • Ln1 (Any) – Nominal and shifted arm lengths for the first stage.

      • +
      • Ls1 (Any) – Nominal and shifted arm lengths for the first stage.

      • +
      • Ln2 (Any) – Nominal and shifted arm lengths for the second stage.

      • +
      • Ls2 (Any) – Nominal and shifted arm lengths for the second stage.

      • +
      • Ltp (Any) – Taper length between w1 and w2.

      • +
      • R_bend (Any) – Bend radius used by U-bend routing.

      • +
      • w_wg (float) – Optical waveguide width used for ports and routing.

      • +
      • L_patch (Any) – Straight patch length at routing interfaces.

      • +
      • via_h2m (Any, optional) – Heater-to-metal via object or cell.

      • +
      • isl (Any, optional) – Isolation trench object or cell.

      • +
      • L12 (Any, optional) – Optional length between beam splitter regions.

      • +
      +
      +
      +
      +
      +cell#
      +

      Generated two-stage MZI layout cell.

      +
      +
      Type:
      +

      nazca.Cell

      +
      +
      +
      +
      @@ -417,6 +575,38 @@
      class mxpic.components.composites.MZI.MZI_Eubend(name, BS, w_arm, w_wg, L_arm, dL_Amzi, L_patch, xs_wg, Rmax=30, Rmin=10, dL=0.1, w_arm_min=None, show_pins=False, sharp_patch=True)#

      Bases: object

      +

      Euler U-bend MZI composite for compact asymmetric routing.

      +
      +
      Parameters:
      +
        +
      • name (str) – Nazca cell name.

      • +
      • BS (Any) – Beam splitter cell or object.

      • +
      • w_arm (float) – Internal arm waveguide width.

      • +
      • w_wg (float) – Input/output waveguide width.

      • +
      • L_arm (Any) – Nominal straight arm length.

      • +
      • dL_Amzi (float) – Differential length for asymmetric MZI operation.

      • +
      • L_patch (Any) – Straight patch length at routing interfaces.

      • +
      • xs_wg (str) – Optical waveguide cross-section name.

      • +
      • Rmax (int, optional) – Maximum Euler bend radius.

      • +
      • Rmin (int, optional) – Minimum Euler bend radius.

      • +
      • dL (float, optional) – Length-search resolution for the Euler compensation.

      • +
      • w_arm_min (float, optional) – Minimum arm width used during tapering.

      • +
      • show_pins (bool, optional) – Show Nazca pin stubs in the generated layout.

      • +
      • sharp_patch (bool, optional) – Add cladding patches around sharp geometry features.

      • +
      +
      +
      +
      +
      +cell#
      +

      Generated Euler U-bend MZI layout cell.

      +
      +
      Type:
      +

      nazca.Cell

      +
      +
      +
      +
      @@ -426,6 +616,41 @@
      class mxpic.components.composites.MZI.MZI_Ubend(name, BS, L_arm, xs_wg='strip', w_arm=1.0, L_tp=10, R_bend=10, w_wg=0.45, L_patch=0.5, w_ht=0, L_ht=0, via_h2m=None, isl=None, show_pins=False, D_port=None, sharp_patch=True, dual_ht=False)#

      Bases: MZI_NS_ubend

      +

      Convenience U-bend MZI wrapper with default width handling.

      +
      +
      Parameters:
      +
        +
      • name (str) – Nazca cell name.

      • +
      • BS (Any) – Beam splitter cell or object.

      • +
      • L_arm (Any) – Nominal arm length.

      • +
      • xs_wg (str, optional) – Optical waveguide cross-section name.

      • +
      • w_arm (float, optional) – Internal arm waveguide width.

      • +
      • L_tp (int, optional) – Taper length between bus and arm widths.

      • +
      • R_bend (int, optional) – Bend radius used by U-bend routing.

      • +
      • w_wg (float, optional) – Input/output waveguide width.

      • +
      • L_patch (float, optional) – Straight patch length at routing interfaces.

      • +
      • w_ht (float, optional) – Heater width. Use 0 to disable heater drawing.

      • +
      • L_ht (int, optional) – Heater length override.

      • +
      • via_h2m (Any, optional) – Heater-to-metal via object or cell.

      • +
      • isl (Any, optional) – Isolation trench object or cell.

      • +
      • show_pins (bool, optional) – Show Nazca pin stubs in the generated layout.

      • +
      • D_port (Any, optional) – Output port pitch override.

      • +
      • sharp_patch (bool, optional) – Add cladding patches around sharp geometry features.

      • +
      • dual_ht (bool, optional) – Add heaters on both arms.

      • +
      +
      +
      +
      +
      +cell#
      +

      Generated U-bend MZI layout cell.

      +
      +
      Type:
      +

      nazca.Cell

      +
      +
      +
      +
      @@ -435,6 +660,47 @@
      class mxpic.components.composites.MZI.MZI_Butterfly(name=None, xs_wg='strip', w_wg=0.45, dL_AMZI=0, L_arm=150, L_inner=10, R_bend=10, D_port=None, w_arm=1.0, xs_ht='heater', w_ht=0, Ltp=15, xs_metal='metal', w_metal=10, via_h2m=None, isl=None, outer_isl=True, dual_ht=True, L_patch=0.5, BS=None, BS2=None, sharp_patch=True, show_pins=False)#

      Bases: object

      +

      Butterfly-style MZI composite with compact folded arms.

      +
      +
      Parameters:
      +
        +
      • name (str, optional) – Nazca cell name. If omitted, the generated cell is not instantiated.

      • +
      • xs_wg (str, optional) – Optical waveguide cross-section name.

      • +
      • w_wg (float, optional) – Input and output waveguide width in microns.

      • +
      • dL_AMZI (float, optional) – Differential arm length for asymmetric MZI operation.

      • +
      • L_arm (int, optional) – Nominal straight arm length.

      • +
      • L_inner (int, optional) – Inner straight section length used by the butterfly fold.

      • +
      • R_bend (int, optional) – Bend radius used by internal routing.

      • +
      • D_port (Any, optional) – Output port pitch. If omitted, beam splitter pitch is used.

      • +
      • w_arm (float, optional) – Internal arm waveguide width.

      • +
      • xs_ht (str, optional) – Heater cross-section name.

      • +
      • w_ht (float, optional) – Heater width. Use 0 to disable heater drawing.

      • +
      • Ltp (int, optional) – Taper length between bus and arm widths.

      • +
      • xs_metal (str, optional) – Metal routing cross-section name.

      • +
      • w_metal (float, optional) – Metal routing width.

      • +
      • via_h2m (Any, optional) – Heater-to-metal via object or cell.

      • +
      • isl (Any, optional) – Isolation trench object or cell.

      • +
      • outer_isl (bool, optional) – Add outer isolation structures.

      • +
      • dual_ht (bool, optional) – Add heaters on both arms.

      • +
      • L_patch (float, optional) – Straight patch length at routing interfaces.

      • +
      • BS (Any, optional) – First beam splitter cell or object. If omitted, a default DC is used.

      • +
      • BS2 (Any, optional) – Second beam splitter cell or object. If omitted, BS is reused.

      • +
      • sharp_patch (bool, optional) – Add cladding patches around sharp geometry features.

      • +
      • show_pins (bool, optional) – Show Nazca pin stubs in the generated layout.

      • +
      +
      +
      +
      +
      +cell#
      +

      Generated butterfly MZI layout cell.

      +
      +
      Type:
      +

      nazca.Cell

      +
      +
      +
      +
      generate_gds(show_pin=False)#
      @@ -498,31 +764,50 @@
      @@ -505,6 +742,7 @@