"""Mach-Zehnder interferometer composite layouts.""" from typing import Any, Optional import nazca as nd import numpy as np # from mxpic.passive.unit import PS_3xg from ..geometry import * from ..routing import Route from ..primitives.pic import * from ..electronics import Vias import pandas as pd from ..primitives.passive import waveguide, PS_2st, PS_2st_Straight from ..basic import __cell_arg__ def __BS_generate__(BS,xs,func_name): """Resolve a beam splitter object into a Nazca cell. Parameters ---------- BS : Any Beam splitter cell, object with a ``cell`` attribute, or ``None``. xs : str Waveguide cross-section name used when a default directional coupler must be generated. func_name : str Name of the calling composite, used in warning messages. Returns ------- nazca.Cell Beam splitter cell used by the composite layout. """ if (BS==None): BS_cell = DC(xs=xs,w_cp=0.45,w_wg=0.45,L_cp=9.27,angle=10,R0=15,Rmax=15,Rmin=15).cell elif (isinstance(BS,nd.Cell)): BS_cell = BS elif (hasattr(BS,'cell')): BS_cell = BS.cell else : print ("WARNING:: In <"+func_name+">, BS not recognizable, Standard DC genereated") BS_cell = DC(xs=xs,w_cp=0.45,w_wg=0.45,L_cp=9.27,angle=10,R0=15,Rmax=15,Rmin=15).cell return BS_cell class MZI: """Mach-Zehnder interferometer with optional heaters and isolation. 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. R_bend : int, optional Bend radius used by internal routing. D_arm : int, optional Vertical distance between interferometer arms. D_port : Any, optional Output port pitch. If omitted, the beam splitter port pitch is used. w_arm : float, optional Internal arm waveguide width. xs_heater : str, optional Heater cross-section name. w_heater : 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. Attributes ---------- cell : nazca.Cell Generated MZI layout cell. """ def __init__(self, name: Optional[str]=None, xs_wg: str='strip', w_wg: float=0.45, dL_Amzi: float=0, L_arm: int=150, R_bend: int=10, D_arm: int=75, D_port: Any=None, w_arm: float = 1.0, xs_heater: str = 'heater', w_heater: float=0, Ltp: int = 15, xs_metal: str = 'metal', w_metal: float=10, via_h2m: Any = None, isl: Any = None, outer_isl: bool = True, dual_ht: bool=True, L_patch: float = 0.5, BS: Any=None, BS2: Any=None, sharp_patch: bool=True, show_pins: bool=False) -> None: """Initialize the MZI composite. See the class docstring for parameter descriptions. """ self.name = name if (self.name==None): self.instantiate = False else : self.instantiate = True self.xs_wg = xs_wg self.w_wg = w_wg self.dL_Amzi = dL_Amzi self.L_arm = L_arm self.R_bend = R_bend self.D_arm = D_arm self.D_port = D_port self.w_arm = w_arm self.xs_heater = xs_heater self.w_heater = w_heater self.xs_metal = xs_metal self.via_h2m = via_h2m self.isl = isl self.w_metal = w_metal self.xs_metal = xs_metal self.L_patch = L_patch self.Ltp = Ltp with nd.Cell(instantiate=self.instantiate,name=self.name) as C: if (BS2==None): BS2 = BS ## revised in 2022.12.28, for general BS function cell_BS = __BS_generate__(BS=BS,xs=xs_wg,func_name="mxpic::functional::MZI") cell_BS2 = __BS_generate__(BS=BS2,xs=xs_wg,func_name="mxpic::functional::MZI") ## revised in 2026.06.07 by Qin Yue # legacy: dY1 = np.abs(cell_BS.pin['b1'].y - cell_BS.pin['b2'].y) dY1 = np.abs(cell_BS.pin['opt_b1'].y - cell_BS.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: dY2 = np.abs(cell_BS2.pin['b1'].y - cell_BS2.pin['b2'].y) dY2 = np.abs(cell_BS2.pin['opt_b1'].y - cell_BS2.pin['opt_b2'].y) # dX = np.abs(cell_BS.pin['a1'].x - cell_BS.pin['b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = cell_BS.put('b1',-L_arm/2-R_bend*2-2.5,dY1/2,180) BS1 = cell_BS.put('opt_b1',-L_arm/2-R_bend*2-2.5,dY1/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = cell_BS2.put('b2', L_arm/2+R_bend*2+2.5,dY2/2,0,flip=0) BS2 = cell_BS2.put('opt_b2', L_arm/2+R_bend*2+2.5,dY2/2,0,flip=0) pic = Route(width=w_wg,radius=R_bend,xs=xs_wg,adapt_width=True,adapt_xs=True) """ Placing Input and Output ports """ pin_in_name = [] for str,Pin in cell_BS.ic_pins(): pin_in_name = pin_in_name+[str] # print("DEBUG - A") ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_in_name): if ('opt_a2' in pin_in_name): if (D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a2']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=self.xs_wg).put(BS1.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a1']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=self.xs_wg).put(BS1.pin['opt_a1']) elif(isinstance(D_port,int) or isinstance(D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() pin_out_name = [] for str,Pin in cell_BS2.ic_pins(): pin_out_name = pin_out_name+[str] ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_out_name): if ('opt_a2' in pin_out_name): if (D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a2']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=self.xs_wg).put(BS2.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a1']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=self.xs_wg).put(BS2.pin['opt_a1']) elif(isinstance(D_port,int) or isinstance(D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() w_ht_u = w_heater if (dual_ht): w_ht_d = w_heater ## single heater selection else : w_ht_d = 0 """ Function of bend heater MZI are abondonded, please use MZI_NS_Ubend as replacement """ """ 2023.03.22 """ # """ Placing Heaters """ # if (bend_heater==False): arm_upper = waveguide(xs_wg=xs_wg,L_wg=L_arm,w_wg=w_arm,w_port=w_wg, shape='strip',LOWER_ISL=outer_isl, xs_heater=xs_heater,w_heater=w_ht_u,L_heater=L_arm, xs_metal=xs_metal,w_metal=w_metal, via_h2m=via_h2m, isl=isl, Ltp=Ltp, # ).cell.put(0,D_arm/2+dL,0) ### Revised in 2022.10.08 after finding the FSR of testing device is 2 time than design ).cell.put(0,D_arm/2+dL_Amzi/2,0) arm_down = waveguide(xs_wg=xs_wg,L_wg=L_arm,w_wg=w_arm,w_port=w_wg, shape='strip',UPPER_ISL=outer_isl, xs_heater=xs_heater,w_heater=w_ht_d,L_heater=L_arm, xs_metal=xs_metal,w_metal=w_metal, via_h2m=via_h2m, isl=isl, Ltp=self.Ltp ).cell.put(0,-D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=BS1.pin['b1'],pin2=arm_upper.pin['a1'],arrow=False,original_function=not sharp_patch).put() pic.sbend_p2p(pin1=BS1.pin['opt_b1'],pin2=arm_upper.pin['opt_a1'],arrow=False,original_function=not sharp_patch).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=BS1.pin['b2'],pin2=arm_down.pin['a1'],arrow=False,original_function=not sharp_patch).put() pic.sbend_p2p(pin1=BS1.pin['opt_b2'],pin2=arm_down.pin['opt_a1'],arrow=False,original_function=not sharp_patch).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=BS2.pin['b2'],pin2=arm_upper.pin['b1'],arrow=False,original_function=not sharp_patch).put() pic.sbend_p2p(pin1=BS2.pin['opt_b2'],pin2=arm_upper.pin['opt_b1'],arrow=False,original_function=not sharp_patch).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=BS2.pin['b1'],pin2=arm_down.pin['b1'],arrow=False,original_function=not sharp_patch).put() pic.sbend_p2p(pin1=BS2.pin['opt_b1'],pin2=arm_down.pin['opt_b1'],arrow=False,original_function=not sharp_patch).put() for str,Pin in arm_upper.ic_pins(): if (str=='ep1') : nd.Pin(name='ep1',pin=Pin).put() if (str=='en1') : nd.Pin(name='en1',pin=Pin).put() for str,Pin in arm_down.ic_pins(): if (str=='ep1') : nd.Pin(name='ep2',pin=Pin).put() if (str=='en1') : nd.Pin(name='en2',pin=Pin).put() ## revise 2022.08.18 if (show_pins): nd.put_stub() self.cell = C ## revised in 2026.06.07 by Qin Yue # legacy: self.L = np.abs(self.cell.pin['a1'].x-self.cell.pin['b1'].x) self.L = np.abs(self.cell.pin['opt_a1'].x-self.cell.pin['opt_b1'].x) class MZI_NS: """Nested-straight MZI composite with taperable arm widths. Parameters ---------- name : str Nazca cell name. BS : Any Beam splitter cell or object used at both splitter positions. 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. Ln : Any Length of the nominal arm section. Ls : Any Length of the shifted arm section. Ltp : Any Taper length between ``w1`` and ``w2``. R_bend : Any Bend radius used by the arm routing. w_wg : float Optical waveguide width used for ports and routing. L_patch : Any Straight patch length at routing interfaces. D_arm : int, optional Vertical distance between interferometer arms. L12 : Any, optional Optional length between the first and second beam splitter regions. w_heater : float, optional Heater width. Use 0 to disable heater drawing. L_ht : Any, 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. Attributes ---------- cell : nazca.Cell Generated nested-straight MZI layout cell. """ def __init__(self, name: str, BS: Any, xs_wg: str, w1: float, w2: float, L0: Any, Ln: Any, Ls: Any, Ltp: Any, R_bend: Any, w_wg: float, L_patch: Any, D_arm: int = 40, L12: Any=None, w_heater: float = 0, L_ht: Any = None, via_h2m: Any = None, isl: Any = None, show_pins: bool=False, D_port: Any = None, sharp_patch: bool =True, dual_ht: bool = False, ) -> None: if (name==None): instantiate = False else : instantiate = True if (L12 == None): L12 = Ltp BS = __cell_arg__(arg=BS,arg_name="BS",func_name="mxpic::functional::MZI_NS_ubend") with nd.Cell(name=name,instantiate=instantiate) as C: pic_strip = Route(radius=R_bend,width=w_wg,xs=xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: _L_ = abs(BS.pin['a1'].x-BS.pin['b1'].x) _L_ = abs(BS.pin['opt_a1'].x-BS.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: _W_ = abs(BS.pin['b1'].y-BS.pin['b2'].y) _W_ = abs(BS.pin['opt_b1'].y-BS.pin['opt_b2'].y) PS = PS_2st_Straight(xs_wg=xs_wg,w_wg=w_wg,w1=w1,w2=w2,L1=Ln,L2=Ls,L_wg=0.25, L12=L12,L_tp=Ltp,w_heater=w_heater, via_h2m=via_h2m, isl=isl, L_ht=L_ht) ## revised in 2026.06.07 by Qin Yue # legacy: PS_U = PS.cell.put('a1',-PS.L_arm/2,D_arm/2,0) PS_U = PS.cell.put('opt_a1',-PS.L_arm/2,D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = BS.put('b1',-R_bend*2-L_patch-PS.L_arm/2,_W_/2,180) BS1 = BS.put('opt_b1',-R_bend*2-L_patch-PS.L_arm/2,_W_/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = BS.put('b2', R_bend*2+L_patch+PS.L_arm/2,_W_/2,0,flip=0) BS2 = BS.put('opt_b2', R_bend*2+L_patch+PS.L_arm/2,_W_/2,0,flip=0) pic = Route(width=w_wg,radius=R_bend,xs=xs_wg,adapt_width=True,adapt_xs=True) """ Plcaing b2 pin """ pin_in_name = [] for str,Pin in BS.ic_pins(): pin_in_name = pin_in_name+[str] # pin_in_name.append(str) ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_in_name): if ('opt_a2' in pin_in_name): if (D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() elif(isinstance(D_port,int) or isinstance(D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() # print("SEGEMENT") ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=PS_U.pin['a1'],pin2=BS1.pin['b1'],Lstart=L_patch/4).put() pic_strip.sbend_p2p(pin1=PS_U.pin['opt_a1'],pin2=BS1.pin['opt_b1'],Lstart=L_patch/4).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=PS_U.pin['b1'],pin2=BS2.pin['b2'],Lstart=L_patch/4).put() pic_strip.sbend_p2p(pin1=PS_U.pin['opt_b1'],pin2=BS2.pin['opt_b2'],Lstart=L_patch/4).put() if (dual_ht==False): w_ht_D = 0 PS = PS_2st_Straight(xs_wg=xs_wg,w_wg=w_wg,w1=w2,w2=w1,L1=Ln,L2=Ls,L_wg=0.25, L12=L12,L_tp=Ltp,w_heater=w_ht_D, via_h2m=via_h2m, isl=isl, L_ht=L_ht) ## revised in 2026.06.07 by Qin Yue # legacy: PS_D = PS.cell.put('a1',-PS.L_arm/2,-D_arm/2,0) PS_D = PS.cell.put('opt_a1',-PS.L_arm/2,-D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=PS_D.pin['a1'],pin2=BS1.pin['b2'],Lstart=L_patch/4).put() pic_strip.sbend_p2p(pin1=PS_D.pin['opt_a1'],pin2=BS1.pin['opt_b2'],Lstart=L_patch/4).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=PS_D.pin['b1'],pin2=BS2.pin['b1'],Lstart=L_patch/4).put() pic_strip.sbend_p2p(pin1=PS_D.pin['opt_b1'],pin2=BS2.pin['opt_b1'],Lstart=L_patch/4).put() if (w_heater>0): nd.Pin(name='ep1',pin=PS_U.pin['ep1']).put() nd.Pin(name='en1',pin=PS_U.pin['en1']).put() if (dual_ht==True): nd.Pin(name='ep2',pin=PS_D.pin['ep1']).put() nd.Pin(name='en2',pin=PS_D.pin['en1']).put() if (show_pins): nd.put_stub() self.cell = C class MZI_NS_ubend: """Nested-straight MZI composite using U-bend arm routing. Parameters ---------- name : str Nazca cell name. BS : Any Beam splitter cell or object used at both splitter positions. 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. Ln : Any Length of the nominal arm section. Ls : Any Length of the shifted arm section. Ltp : Any Taper length between ``w1`` and ``w2``. R_bend : Any Bend radius used by the U-bends. w_wg : float Optical waveguide width used for ports and routing. L_patch : Any Straight patch length at routing interfaces. L12 : Any, optional Optional length between splitter regions. 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. Attributes ---------- cell : nazca.Cell Generated U-bend MZI layout cell. """ def __init__(self, name: str, BS: Any, xs_wg: str, w1: float, w2: float, L0: Any, Ln: Any, Ls: Any, Ltp: Any, R_bend: Any, w_wg: float, L_patch: Any, L12: Any=None, w_ht: float = 0, L_ht: int = 0, via_h2m: Any = None, isl: Any = None, show_pins: bool=False, D_port: Any=None, sharp_patch: bool=True, dual_ht: bool=False, ) -> None: if (name==None): instantiate = False else : instantiate = True if (L12 == None): L12 = Ltp BS = __cell_arg__(arg=BS,arg_name="BS",func_name="mxpic::functional::MZI_NS_ubend") with nd.Cell(name=name,instantiate=instantiate) as C: pic = Route(radius=R_bend,width=w_wg,xs=xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: _L_ = abs(BS.pin['a1'].x-BS.pin['b1'].x) _L_ = abs(BS.pin['opt_a1'].x-BS.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: _W_ = abs(BS.pin['b1'].y-BS.pin['b2'].y) _W_ = abs(BS.pin['opt_b1'].y-BS.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = BS.put('b1',-R_bend*2-L_patch/2,_W_/2,180) BS1 = BS.put('opt_b1',-R_bend*2-L_patch/2,_W_/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = BS.put('b2', R_bend*2+L_patch/2,_W_/2,0) BS2 = BS.put('opt_b2', R_bend*2+L_patch/2,_W_/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() """ Plcaing b2 pin """ pin_in_name = [] for str,Pin in BS.ic_pins(): pin_in_name = pin_in_name+[str] # pin_in_name.append(str) ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_in_name): if ('opt_a2' in pin_in_name): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a2'],type="optical:").put() if (D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=xs_wg).put(BS2.pin['a2']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=xs_wg).put(BS2.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=xs_wg).put(BS2.pin['a1']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=xs_wg).put(BS2.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=xs_wg).put(BS1.pin['a2']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=xs_wg).put(BS1.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=xs_wg).put(BS1.pin['a1']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=xs_wg).put(BS1.pin['opt_a1']) elif(isinstance(D_port,int) or isinstance(D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS2.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS2.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a1'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a1'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a1'].y)), original_function=not sharp_patch).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['a2'].y)), temp = pic.sbend_route(radius=R_bend,width=w_wg,pin=BS1.pin['opt_a2'],length1=L_patch/4,length2=L_patch/4,offset=abs(D_port/2-abs(BS1.pin['opt_a2'].y)), original_function=not sharp_patch).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") PS_L1 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w1,w2=w2,L1=Ln/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, L12=L12,L_tp=Ltp,w_heater=w_ht, via_h2m=via_h2m, isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L_ht=L_ht).cell.put('a1',BS1.pin['b1'].x+L_patch/4+R_bend,BS1.pin['b1'].y+L_patch/4+R_bend,90) L_ht=L_ht).cell.put('opt_a1',BS1.pin['opt_b1'].x+L_patch/4+R_bend,BS1.pin['opt_b1'].y+L_patch/4+R_bend,90) ## revised in 2026.06.07 by Qin Yue # legacy: pic.bend_route_p2p(pin1=PS_L1.pin['a1'],pin2=BS1.pin['b1']).put() pic.bend_route_p2p(pin1=PS_L1.pin['opt_a1'],pin2=BS1.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.bend_route_p2p(pin1=PS_L1.pin['b1'],pin2=BS2.pin['b2']).put() pic.bend_route_p2p(pin1=PS_L1.pin['opt_b1'],pin2=BS2.pin['opt_b2']).put() if (dual_ht): w_ht_d=w_ht else: w_ht_d = 0 PS_L2 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w2,w2=w1,L1=Ls/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, L12=L12,L_tp=Ltp,w_heater=w_ht_d, via_h2m=via_h2m, isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L_ht=L_ht).cell.put('a1',BS1.pin['b1'].x+L_patch/4+R_bend,BS1.pin['b2'].y-L_patch/4-R_bend,-90,flip=1) L_ht=L_ht).cell.put('opt_a1',BS1.pin['opt_b1'].x+L_patch/4+R_bend,BS1.pin['opt_b2'].y-L_patch/4-R_bend,-90,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic.bend_route_p2p(pin1=PS_L2.pin['a1'],pin2=BS1.pin['b2']).put() pic.bend_route_p2p(pin1=PS_L2.pin['opt_a1'],pin2=BS1.pin['opt_b2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.bend_route_p2p(pin1=PS_L2.pin['b1'],pin2=BS2.pin['b1']).put() pic.bend_route_p2p(pin1=PS_L2.pin['opt_b1'],pin2=BS2.pin['opt_b1']).put() if (w_ht>0): nd.Pin(name='ep1',pin=PS_L1.pin['ep1']).put() nd.Pin(name='en1',pin=PS_L1.pin['en1']).put() if (dual_ht): nd.Pin(name='ep2',pin=PS_L2.pin['ep1']).put() nd.Pin(name='en2',pin=PS_L2.pin['en1']).put() if (show_pins): nd.put_stub() self.cell = C class MZI_2st_ubend: """Two-stage U-bend MZI composite with three beam splitters. Parameters ---------- name : str Nazca cell name. BS1, BS2, 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, Ls1 : Any Nominal and shifted arm lengths for the first stage. Ln2, 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. Attributes ---------- cell : nazca.Cell Generated two-stage MZI layout cell. """ def __init__(self, name: str, BS1: Any, BS2: Any, BS3: Any, xs_wg: str, w1: float, w2: float, L0: Any, Ln1: Any, Ls1: Any, Ln2: Any, Ls2: Any, Ltp: Any, R_bend: Any, w_wg: float, L_patch: Any, via_h2m: Any = None, isl: Any = None, L12: Any=None) -> None: if (name==None): instantiate = False else : instantiate = True with nd.Cell(name=name,instantiate=instantiate) as C: BS1 = __cell_arg__(arg=BS1,arg_name="BS1",func_name="mxpic::functional::MZI_2st_ubend") BS2 = __cell_arg__(arg=BS2,arg_name="BS1",func_name="mxpic::functional::MZI_2st_ubend") BS3 = __cell_arg__(arg=BS3,arg_name="BS1",func_name="mxpic::functional::MZI_2st_ubend") pic_strip = Route(radius=R_bend,width=w_wg,xs=xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: _L1_ = abs(BS1.pin['a1'].x-BS1.pin['b1'].x) _L1_ = abs(BS1.pin['opt_a1'].x-BS1.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: _L2_ = abs(BS2.pin['a1'].x-BS2.pin['b1'].x) _L2_ = abs(BS2.pin['opt_a1'].x-BS2.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: _L3_ = abs(BS3.pin['a1'].x-BS3.pin['b1'].x) _L3_ = abs(BS3.pin['opt_a1'].x-BS3.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: _W1_ = abs(BS1.pin['b1'].y-BS1.pin['b2'].y) _W1_ = abs(BS1.pin['opt_b1'].y-BS1.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: _W2_ = abs(BS2.pin['b1'].y-BS2.pin['b2'].y) _W2_ = abs(BS2.pin['opt_b1'].y-BS2.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: _W3_ = abs(BS3.pin['b1'].y-BS3.pin['b2'].y) _W3_ = abs(BS3.pin['opt_b1'].y-BS3.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: BSM = BS2.put('a1',-_L2_/2,_W2_/2,0) BSM = BS2.put('opt_a1',-_L2_/2,_W2_/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = BS1.put('b1',-_L2_/2-R_bend*4-L_patch,_W2_/2,180) BS1 = BS1.put('opt_b1',-_L2_/2-R_bend*4-L_patch,_W2_/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = BS3.put('a1', _L2_/2+R_bend*4+L_patch,_W2_/2,0) BS2 = BS3.put('opt_a1', _L2_/2+R_bend*4+L_patch,_W2_/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['b1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_b1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['b2']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_b2'],type="optical:").put() PS_L1 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w1,w2=w2,L1=Ln1/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, via_h2m=via_h2m,isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L12=L12,L_tp=Ltp,w_heater=0).cell.put('a1',BS1.pin['b1'].x+L_patch/4+R_bend,BS1.pin['b1'].y+L_patch/4+R_bend,90) L12=L12,L_tp=Ltp,w_heater=0).cell.put('opt_a1',BS1.pin['opt_b1'].x+L_patch/4+R_bend,BS1.pin['opt_b1'].y+L_patch/4+R_bend,90) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_L1.pin['a1'],pin2=BS1.pin['b1']).put() pic_strip.bend_route_p2p(pin1=PS_L1.pin['opt_a1'],pin2=BS1.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_L1.pin['b1'],pin2=BSM.pin['a1']).put() pic_strip.bend_route_p2p(pin1=PS_L1.pin['opt_b1'],pin2=BSM.pin['opt_a1']).put() PS_L2 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w2,w2=w1,L1=Ls1/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, via_h2m=via_h2m,isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L12=L12,L_tp=Ltp,w_heater=0).cell.put('a1',BS1.pin['b1'].x+L_patch/4+R_bend,BS1.pin['b2'].y-L_patch/4-R_bend,-90,flip=1) L12=L12,L_tp=Ltp,w_heater=0).cell.put('opt_a1',BS1.pin['opt_b1'].x+L_patch/4+R_bend,BS1.pin['opt_b2'].y-L_patch/4-R_bend,-90,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_L2.pin['a1'],pin2=BS1.pin['b2']).put() pic_strip.bend_route_p2p(pin1=PS_L2.pin['opt_a1'],pin2=BS1.pin['opt_b2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_L2.pin['b1'],pin2=BSM.pin['a2']).put() pic_strip.bend_route_p2p(pin1=PS_L2.pin['opt_b1'],pin2=BSM.pin['opt_a2']).put() PS_R1 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w2,w2=w1,L1=Ln2/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, via_h2m=via_h2m,isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L12=L12,L_tp=Ltp,w_heater=0).cell.put('a1',BSM.pin['b1'].x+L_patch/4+R_bend,BSM.pin['b1'].y+L_patch/4+R_bend,90) L12=L12,L_tp=Ltp,w_heater=0).cell.put('opt_a1',BSM.pin['opt_b1'].x+L_patch/4+R_bend,BSM.pin['opt_b1'].y+L_patch/4+R_bend,90) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_R1.pin['b1'],pin2=BS2.pin['a1']).put() pic_strip.bend_route_p2p(pin1=PS_R1.pin['opt_b1'],pin2=BS2.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_R1.pin['a1'],pin2=BSM.pin['b1']).put() pic_strip.bend_route_p2p(pin1=PS_R1.pin['opt_a1'],pin2=BSM.pin['opt_b1']).put() PS_R2 = PS_2st(xs_wg=xs_wg,w_wg=w_wg,w1=w1,w2=w2,L1=Ls2/2+L0,L2=L0,R_bend=R_bend,L_wg=0.25, via_h2m=via_h2m,isl=isl, ## revised in 2026.06.07 by Qin Yue # legacy: L12=L12,L_tp=Ltp,w_heater=0).cell.put('a1',BSM.pin['b1'].x+L_patch/4+R_bend,BSM.pin['b2'].y-L_patch/4-R_bend,-90,flip=1) L12=L12,L_tp=Ltp,w_heater=0).cell.put('opt_a1',BSM.pin['opt_b1'].x+L_patch/4+R_bend,BSM.pin['opt_b2'].y-L_patch/4-R_bend,-90,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_R2.pin['b1'],pin2=BS2.pin['a2']).put() pic_strip.bend_route_p2p(pin1=PS_R2.pin['opt_b1'],pin2=BS2.pin['opt_a2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route_p2p(pin1=PS_R2.pin['a1'],pin2=BSM.pin['b2']).put() pic_strip.bend_route_p2p(pin1=PS_R2.pin['opt_a1'],pin2=BSM.pin['opt_b2']).put() self.cell = C class MZI_Eubend: """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. Attributes ---------- cell : nazca.Cell Generated Euler U-bend MZI layout cell. """ def __init__(self, name: str, BS: Any, w_arm: float, w_wg: float, L_arm: Any, dL_Amzi: float, L_patch: Any, xs_wg: str, Rmax: int = 30, Rmin: int = 10, dL: float = 0.1, w_arm_min: Optional[float] = None, show_pins: bool=False, sharp_patch: bool=True) -> None: if (name==None): instantiate = False else : instantiate = True if (w_arm_min==None): w_arm_min = w_arm BS = __cell_arg__(arg=BS,arg_name="BS1",func_name="mxpic::functional::MZI_2st_ubend") with nd.Cell(name=name,instantiate=instantiate) as C: EUB_90 = Clothoid(R=[Rmax,Rmin,Rmax],w=[w_wg,w_arm],A=[0,45,90],dL_wg=dL,end_patch=False) EUB_180 = Clothoid(R=[Rmax,Rmin,Rmax],w=[w_arm,w_arm_min,w_arm],A=[0,90,180],dL_wg=dL,end_patch=False) R90_eff = EUB_90.sz[0] R180_eff = EUB_180.sz[1] ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = BS.put('b1',-R90_eff-R180_eff/2-L_patch/2,BS.pin['b1'].y,180) BS1 = BS.put('opt_b1',-R90_eff-R180_eff/2-L_patch/2,BS.pin['opt_b1'].y,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = BS.put('b1', R90_eff+R180_eff/2+L_patch/2,BS.pin['b1'].y,0,flip=1) BS2 = BS.put('opt_b1', R90_eff+R180_eff/2+L_patch/2,BS.pin['opt_b1'].y,0,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['b1'].width).put(BS1.pin['b1']) tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['opt_b1'].width).put(BS1.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: EUB_90.cell.put('a1',tl.pin['b0'],flip=0) EUB_90.cell.put('opt_a1',tl.pin['b0'],flip=0) nd.strt(length=L_arm/2+dL_Amzi/2,xs=xs_wg,width=w_arm).put() tl = EUB_180.cell.put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: tr = nd.strt(length=abs(tl.pin['b1'].x-BS2.pin['b1'].x)-R90_eff,xs=xs_wg,width=BS2.pin['b1'].width).put(BS2.pin['b1']) tr = nd.strt(length=abs(tl.pin['opt_b1'].x-BS2.pin['opt_b1'].x)-R90_eff,xs=xs_wg,width=BS2.pin['opt_b1'].width).put(BS2.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: tr = EUB_90.cell.put('a1',tr.pin['b0'],flip=1) tr = EUB_90.cell.put('opt_a1',tr.pin['b0'],flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=abs(tr.pin['b1'].y - tl.pin['b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['b1']) nd.strt(length=abs(tr.pin['opt_b1'].y - tl.pin['opt_b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['b2'].width).put(BS1.pin['b2']) tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['opt_b2'].width).put(BS1.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: EUB_90.cell.put('a1',tl.pin['b0'],flip=1) EUB_90.cell.put('opt_a1',tl.pin['b0'],flip=1) nd.strt(length=L_arm/2,xs=xs_wg,width=w_arm).put() tl = EUB_180.cell.put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: tr = nd.strt(length=abs(tl.pin['b1'].x-BS2.pin['b2'].x)-R90_eff,xs=xs_wg,width=BS2.pin['b2'].width).put(BS2.pin['b2']) tr = nd.strt(length=abs(tl.pin['opt_b1'].x-BS2.pin['opt_b2'].x)-R90_eff,xs=xs_wg,width=BS2.pin['opt_b2'].width).put(BS2.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: tr = EUB_90.cell.put('a1',tr.pin['b0'],flip=0) tr = EUB_90.cell.put('opt_a1',tr.pin['b0'],flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=abs(tr.pin['b1'].y - tl.pin['b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['b1']) nd.strt(length=abs(tr.pin['opt_b1'].y - tl.pin['opt_b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() """ Plcaing b2 pin """ pin_in_name = [] for str,Pin in BS.ic_pins(): pin_in_name = pin_in_name+[str] # pin_in_name.append(str) ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_in_name): if ('opt_a2' in pin_in_name): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a2'],type="optical:").put() self.cell = C class MZI_Ubend(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. Attributes ---------- cell : nazca.Cell Generated U-bend MZI layout cell. """ def __init__(self, name: str, BS: Any, L_arm: Any, xs_wg: str='strip', w_arm: float=1.0, L_tp: int=10, R_bend: int=10, w_wg: float=0.45, L_patch: float=0.5, w_ht: float=0, L_ht: int=0, via_h2m: Any=None, isl: Any=None, show_pins: bool=False, D_port: Any=None, sharp_patch: bool=True,dual_ht: bool=False) -> None: super().__init__(name=name, BS=BS, xs_wg=xs_wg, w1=w_arm, w2=w_arm, L0=(L_arm-L_tp*2)/3, Ln=(L_arm-L_tp*2)/3, Ls=(L_arm-L_tp*2)/3, Ltp=L_tp, R_bend=R_bend, w_wg=w_wg, L_patch=L_patch, L12=0, w_ht=w_ht, L_ht=L_ht, via_h2m=via_h2m, isl=isl, show_pins=show_pins, D_port=D_port, sharp_patch=sharp_patch,dual_ht=dual_ht) class MZI_Butterfly: """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. Attributes ---------- cell : nazca.Cell Generated butterfly MZI layout cell. """ def __init__(self, name: Optional[str]=None, xs_wg: str='strip', w_wg: float=0.45, dL_AMZI: float=0, L_arm: int=150, L_inner: int = 10, R_bend: int=10, D_port: Any=None, w_arm: float = 1.0, xs_ht: str = 'heater', w_ht: float=0, Ltp: int = 15, xs_metal: str = 'metal', w_metal: float=10, via_h2m: Any = None, isl: Any = None, outer_isl: bool = True, dual_ht: bool=True, L_patch: float = 0.5, BS: Any=None, BS2: Any=None, sharp_patch: bool=True, show_pins: bool=False) -> None: self.name = name if (name!=None): self.instantiate = True else: self.instantiate = False self.xs_wg = xs_wg self.w_wg = w_wg self.dL_Amzi = dL_AMZI self.R_bend = R_bend self.L_arm = L_arm self.D_port = D_port self.w_arm = w_arm self.xs_heater = xs_ht self.w_heater = w_ht self.Ltp = Ltp self.xs_metal = xs_metal self.w_metal = w_metal self.L_patch = L_patch self.L_inner = L_inner self.dual_ht = dual_ht self.via_h2m = via_h2m self.isl = isl self.sharp_patch = sharp_patch ## revised in 2022.12.28, for general BS function if (BS!=None and BS2==None): BS2 = BS cell_BS = __BS_generate__(BS=BS,xs=xs_wg,func_name="mxpic::functional::MZI_Butterfly") cell_BS2 = __BS_generate__(BS=BS2,xs=xs_wg,func_name="mxpic::functional::MZI_Butterfly") self.BS = cell_BS self.BS2 = cell_BS2 self.cell = self.generate_gds(self) def generate_gds(self,show_pin=False): with nd.Cell(name=self.name,instantiate=self.instantiate) as C: pic_strip = Route(radius=self.R_bend,width=self.w_wg,width2_mm=self.w_arm,sharp_patch=self.sharp_patch, xs=self.xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: dY_BS1 = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y) dY_BS1 = abs(self.BS.pin['opt_b1'].y - self.BS.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: BS1 = self.BS.put('b1',-self.R_bend-self.L_inner/2,dY_BS1/2,180) BS1 = self.BS.put('opt_b1',-self.R_bend-self.L_inner/2,dY_BS1/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: dY_BS2 = abs(self.BS2.pin['b1'].y - self.BS2.pin['b2'].y) dY_BS2 = abs(self.BS2.pin['opt_b1'].y - self.BS2.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: BS2 = self.BS2.put('b2',self.R_bend+self.L_inner/2,dY_BS2/2,0) BS2 = self.BS2.put('opt_b2',self.R_bend+self.L_inner/2,dY_BS2/2,0) # print("BS1=%.3f BS2=%.3f" %(dY_BS1,dY_BS2)) LU = self.L_arm + self.dL_Amzi/2 HT_U = waveguide(w_heater=self.w_heater,L_wg=LU,L_heater=LU, w_wg=self.w_arm,Ltp=self.Ltp,w_port=self.w_wg, isl=self.isl,w_metal=self.w_metal, ## revised in 2026.06.07 by Qin Yue # legacy: via_h2m=self.via_h2m).cell.put('a1',-LU/2,dY_BS1/2+self.R_bend*4+self.L_patch,0) via_h2m=self.via_h2m).cell.put('opt_a1',-LU/2,dY_BS1/2+self.R_bend*4+self.L_patch,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_route(pin=BS1.pin['b1'],length=self.L_patch/2,offset=self.R_bend*2).put() pic_strip.ubend_route(pin=BS1.pin['opt_b1'],length=self.L_patch/2,offset=self.R_bend*2).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_p2p(pin2=HT_U.pin['a1'],length=self.L_patch/2).put() pic_strip.ubend_p2p(pin2=HT_U.pin['opt_a1'],length=self.L_patch/2).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_route(pin=BS2.pin['b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1) pic_strip.ubend_route(pin=BS2.pin['opt_b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_p2p(pin2=HT_U.pin['b1'],length=self.L_patch/2).put() pic_strip.ubend_p2p(pin2=HT_U.pin['opt_b1'],length=self.L_patch/2).put() if (self.w_heater>0): nd.Pin(name="ep1",pin=HT_U.pin['ep1']).put() nd.Pin(name="en1",pin=HT_U.pin['en1']).put() LD = self.L_arm if(self.dual_ht): w_ht_d = self.w_heater else: w_ht_d = 0 HT_D = waveguide(w_heater=w_ht_d,L_wg=LD,L_heater=LD, w_wg=self.w_arm,Ltp=self.Ltp,w_port=self.w_wg, isl=self.isl, ## revised in 2026.06.07 by Qin Yue # legacy: via_h2m=self.via_h2m).cell.put('a1',-LD/2,-dY_BS1/2-self.R_bend*4-self.L_patch,0) via_h2m=self.via_h2m).cell.put('opt_a1',-LD/2,-dY_BS1/2-self.R_bend*4-self.L_patch,0) if (w_ht_d>0 and self.dual_ht): nd.Pin(name="ep2",pin=HT_D.pin['ep1']).put() nd.Pin(name="en2",pin=HT_D.pin['en1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_route(pin=BS1.pin['b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1) pic_strip.ubend_route(pin=BS1.pin['opt_b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_p2p(pin2=HT_D.pin['a1'],length=self.L_patch/2).put() pic_strip.ubend_p2p(pin2=HT_D.pin['opt_a1'],length=self.L_patch/2).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_route(pin=BS2.pin['b1'],length=self.L_patch/2,offset=self.R_bend*2).put() pic_strip.ubend_route(pin=BS2.pin['opt_b1'],length=self.L_patch/2,offset=self.R_bend*2).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.ubend_p2p(pin2=HT_D.pin['b1'],length=self.L_patch/2).put() pic_strip.ubend_p2p(pin2=HT_D.pin['opt_b1'],length=self.L_patch/2).put() """ Placing Input and Output ports """ pin_in_name = [] for str,Pin in self.BS.ic_pins(): pin_in_name = pin_in_name+[str] ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_in_name): if ('opt_a2' in pin_in_name): if (self.D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=BS1.pin['a2']).put() nd.Pin(name='opt_a2',pin=BS1.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a2']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=self.xs_wg).put(BS1.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a1']) nd.strt(length=0.005,width=BS1.pin['opt_a2'].width,xs=self.xs_wg).put(BS1.pin['opt_a1']) elif(isinstance(self.D_port,int) or isinstance(self.D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic_strip.sbend_route(pin=BS1.pin['a1'],length1=self.L_patch/4,length2=self.L_patch/4, temp = pic_strip.sbend_route(pin=BS1.pin['opt_a1'],length1=self.L_patch/4,length2=self.L_patch/4, ## revised in 2026.06.07 by Qin Yue # legacy: offset=abs(self.D_port/2-abs(BS1.pin['a1'].y))).put(flip=1) offset=abs(self.D_port/2-abs(BS1.pin['opt_a1'].y))).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic_strip.sbend_route(pin=BS1.pin['a2'],length1=self.L_patch/4,length2=self.L_patch/4, temp = pic_strip.sbend_route(pin=BS1.pin['opt_a2'],length1=self.L_patch/4,length2=self.L_patch/4, ## revised in 2026.06.07 by Qin Yue # legacy: offset=abs(self.D_port/2-abs(BS1.pin['a2'].y))).put(flip=0) offset=abs(self.D_port/2-abs(BS1.pin['opt_a2'].y))).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=BS1.pin['a1']).put() nd.Pin(name='opt_a1',pin=BS1.pin['opt_a1'],type="optical:").put() pin_out_name = [] for str,Pin in self.BS2.ic_pins(): pin_out_name = pin_out_name+[str] ## revised in 2026.06.07 by Qin Yue # legacy: if ('a2' in pin_out_name): if ('opt_a2' in pin_out_name): if (self.D_port==None): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b2',pin=BS2.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a2']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=self.xs_wg).put(BS2.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a2']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a2'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a1']) nd.strt(length=0.005,width=BS2.pin['opt_a2'].width,xs=self.xs_wg).put(BS2.pin['opt_a1']) elif(isinstance(self.D_port,int) or isinstance(self.D_port,float)): ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic_strip.sbend_route(pin=BS2.pin['a2'],length1=self.L_patch/4,length2=self.L_patch/4, temp = pic_strip.sbend_route(pin=BS2.pin['opt_a2'],length1=self.L_patch/4,length2=self.L_patch/4, ## revised in 2026.06.07 by Qin Yue # legacy: offset=abs(self.D_port/2-abs(BS2.pin['a1'].y))).put(flip=0) offset=abs(self.D_port/2-abs(BS2.pin['opt_a1'].y))).put(flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b1',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = pic_strip.sbend_route(pin=BS2.pin['a1'],length1=self.L_patch/4,length2=self.L_patch/4, temp = pic_strip.sbend_route(pin=BS2.pin['opt_a1'],length1=self.L_patch/4,length2=self.L_patch/4, ## revised in 2026.06.07 by Qin Yue # legacy: offset=abs(self.D_port/2-abs(BS2.pin['a2'].y))).put(flip=1) offset=abs(self.D_port/2-abs(BS2.pin['opt_a2'].y))).put(flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b2',pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else: raise Exception("ERROR:: In , type error") else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=BS2.pin['a1']).put() nd.Pin(name='opt_b1',pin=BS2.pin['opt_a1'],type="optical:").put() return C def generate_test_gds(self,dXgc2gc,dYgc2gc,gc,w_wg=0.5,R_bend=10,name=None): if (isinstance(gc,nd.Cell)) : gc_cell = gc elif (hasattr(gc,"cell")): gc_cell = gc.cell gds_name = None if (name is None):instantiate = False else:instantiate = True with nd.Cell(name=name,instantiate=instantiate) as C: if ("g1" in gc_cell.pin): pin_gc = "g1" elif ("a0" in gc_cell.pin): pin_gc = "a0" GC1Instr = gc_cell.put(pin_gc,0,0,180) GC2Instr = gc_cell.put(pin_gc,dXgc2gc,0,0) """ Placing GC inputs """ ## revised in 2026.06.07 by Qin Yue # legacy: if ("a2" in self.cell.pin): if ("opt_a2" in self.cell.pin): GC3Instr = gc_cell.put(pin_gc,0,dYgc2gc,180) ## revised in 2026.06.07 by Qin Yue # legacy: if ("b2" in self.cell.pin): if ("opt_b2" in self.cell.pin): GC4Instr = gc_cell.put(pin_gc,dXgc2gc,dYgc2gc,0) ## revised in 2026.06.07 by Qin Yue # legacy: dYcell = abs(self.cell.pin['b1'].y - self.cell.pin['b2'].y) dYcell = abs(self.cell.pin['opt_b1'].y - self.cell.pin['opt_b2'].y) dYoffset = -(dYcell - dYgc2gc)/2 else: dYoffset = 0 ## revised in 2026.06.07 by Qin Yue # legacy: dXcell = abs(self.cell.pin['a1'].x - self.cell.pin['b1'].x) dXcell = abs(self.cell.pin['opt_a1'].x - self.cell.pin['opt_b1'].x) dXoffset = -(dXcell - dXgc2gc)/2 ## revised in 2026.06.07 by Qin Yue # legacy: cellInstr = self.cell.put('a1',dXoffset,dYoffset,0,flip=1) cellInstr = self.cell.put('opt_a1',dXoffset,dYoffset,0,flip=1) pic = Route(xs=self.xs_wg,width=w_wg,radius=R_bend,sharp_patch=self.sharp_patch) ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['a1'],pin2=GC1Instr.pin[pin_gc]).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_a1'],pin2=GC1Instr.pin[pin_gc]).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['b1'],pin2=GC2Instr.pin[pin_gc]).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_b1'],pin2=GC2Instr.pin[pin_gc]).put() ## revised in 2026.06.07 by Qin Yue # legacy: if ("a2" in cellInstr.pin): if ("opt_a2" in cellInstr.pin): ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['a2'],pin2=GC3Instr.pin[pin_gc]).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_a2'],pin2=GC3Instr.pin[pin_gc]).put() ## revised in 2026.06.07 by Qin Yue # legacy: if ("b2" in cellInstr.pin): if ("opt_b2" in cellInstr.pin): ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['b2'],pin2=GC4Instr.pin[pin_gc]).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_b2'],pin2=GC4Instr.pin[pin_gc]).put() return C