"""Mach-Zehnder interferometer mesh composite layouts.""" from typing import Any, Optional import nazca as nd import numpy as np from ..primitives.passive import DC from ..geometry import * from ..routing import Route from ..primitives.pic import * from ..electronics import Vias,ISL import pandas as pd from ..primitives.passive import waveguide from .MZI import __BS_generate__ class W_waveguide: """W-shaped waveguide phase shifter section. Parameters ---------- xs_wg : str, optional Waveguide cross-section name. w_wg : float, optional Optical waveguide width in microns. R_bend : int, optional Bend radius used by W-shaped routing. dL : float, optional Vertical excursion of the W routing. L_wg : int, optional Total phase shifter waveguide length. xs_heater : str, optional Heater cross-section name. w_ht : float, optional Heater width. Use 0 to disable heater geometry. 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 Optional isolation helper. n_bends : int, optional Number of W bend periods. show_pins : bool, optional Show Nazca pin stubs in the generated layout. ISL_UPPER : bool, optional Enable upper isolation placement when used. ISL_LOWER : bool, optional Enable lower isolation placement when used. L_patch : float, optional Straight patch length at bend interfaces. reverse : bool, optional Swap optical input/output pin naming. Attributes ---------- cell : nazca.Cell Generated W-waveguide layout cell. """ def __init__(self, xs_wg: str='strip', w_wg: float = 0.45, R_bend: int=10, dL: float = 20, L_wg: int = 80, xs_heater: str = 'heater', w_ht: float = 2.5, xs_metal: str = 'metal', w_metal: float = 6, via_h2m: Any = None, isl: Any = None, n_bends: int=3, show_pins: bool=False, ISL_UPPER: bool = True, ISL_LOWER: bool = True, L_patch: float = 0.25, reverse: bool=False) -> None: """Initialize the W waveguide composite. See the class docstring for parameter descriptions. """ self.xs_wg = xs_wg self.w_wg = w_wg self.R_bend = R_bend self.xs_heater = xs_heater self.w_ht = w_ht self.dL = dL self.xs_metal = xs_metal self.w_metal = w_metal self.via_h2m = via_h2m self.isl = isl, self.L_wg = L_wg self.reverse = reverse self.n_bends = n_bends self.L_patch = L_patch self.ISL_LOWER = ISL_LOWER self.ISL_UPPER = ISL_UPPER self.cell = self.generate_gds(show_pins) def generate_gds(self,show_pins=False): with nd.Cell(instantiate=False) as C: """ Generating the basic via_h2m """ if (self.via_h2m==None): vias = Vias(xs=None,area=self.w_metal,sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## only putting metal blocks else: if (hasattr(self.via_h2m,"cell")): vias = self.via_h2m else : vias = Vias(xs=self.via_h2m.xs,area=self.w_metal,sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## placing vias pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs=self.xs_wg) wg_begin = nd.strt(length=0,width=self.w_wg,xs=self.xs_wg).put(0,0,0) pin_pre = wg_begin.pin['b0'] for itn in range(0,self.n_bends): wg_cut_U = nd.strt(length=self.L_patch/2,width=self.w_wg,xs=self.xs_wg).put(pin_pre.x+self.R_bend*2+self.L_patch ,self.dL,0) wg_cut_M = nd.strt(length=self.L_patch/2,width=self.w_wg,xs=self.xs_wg).put(wg_cut_U.pin['b0'].x+self.R_bend*2+self.L_patch, 0,0) pic_strip.sbend_p2p(pin1=wg_cut_U.pin['b0'],pin2=wg_cut_M.pin['a0'],Lstart=self.L_patch/2).put() pic_strip.sbend_p2p(pin1=pin_pre,pin2=wg_cut_U.pin['a0'],Lstart=self.L_patch/2).put() pin_pre = wg_cut_M.pin['b0'] wg_end = nd.strt(length=self.L_patch/2,width=self.w_wg,xs=self.xs_wg).put(self.L_wg-self.L_patch/2,0,0) pic_strip.strt_p2p(pin1=wg_end.pin['b0'],pin2=pin_pre,arrow=False).put() if (self.w_ht>0): # vias = Vias(xs=self.xs_via_h2m,spacing=self.sp_via,sz=self.sz_via,xs_l1=self.xs_heater,xs_l2=self.xs_metal, # area=[self.w_metal,self.w_metal]) VIA_L = vias.cell.put(0,0,180,flip=1) ht_strip = Route(radius=self.R_bend,width=self.w_ht,xs=self.xs_heater) ht_begin = nd.strt(length=0,width=self.w_ht,xs=self.xs_heater).put(0,0,0) pin_pre = ht_begin.pin['b0'] for itn in range(0,self.n_bends): ht_cut_U = nd.strt(length=self.L_patch/2,width=self.w_ht,xs=self.xs_heater).put(pin_pre.x+self.R_bend*2+self.L_patch,self.dL,0) ht_cut_M = nd.strt(length=self.L_patch/2,width=self.w_ht,xs=self.xs_heater).put(ht_cut_U.pin['b0'].x+self.R_bend*2+self.L_patch,0,0) ht_strip.sbend_p2p(pin1=ht_cut_U.pin['b0'],pin2=ht_cut_M.pin['a0'],Lstart=self.L_patch/2).put() ht_strip.sbend_p2p(pin1=pin_pre,pin2=ht_cut_U.pin['a0'],Lstart=self.L_patch/2).put() pin_pre = ht_cut_M.pin['b0'] ht_end = nd.strt(length=self.L_patch/2,width=self.w_ht,xs=self.xs_heater).put(self.L_wg-self.L_patch,0,0) ht_strip.strt_p2p(pin1=ht_end.pin['b0'],pin2=pin_pre,arrow=False).put() ## Bug fixed, 2022.12.30, the vias are not connected to heater VIA_R = vias.cell.put(ht_end.pin['b0'].x,0,0,flip=0) nd.Pin(name='ep1',width=self.w_metal,xs=self.xs_metal).put(VIA_L.pin['b0']) nd.Pin(name='en1',width=self.w_metal,xs=self.xs_metal).put(VIA_R.pin['b0']) # if (self.xs_isl!=None): # ## The isolation inside # if (self.ISL_LOWER): # ISL(xs=self.xs_isl,width=self.w_isl,length=self.L_wg).cell.put('a1',0,-self.w_metal/2-self.w_isl/2-self.sp_isl_xs,0) # if (self.ISL_UPPER): # ISL(xs=self.xs_isl,width=self.w_isl,length=self.L_wg).cell.put('a1',0, self.dL+self.w_isl/2+self.sp_isl_xs+self.w_metal/2 ,0) # L_isl_side = self.dL-self.w_metal/2-self.sp_isl_xs # if (L_isl_side > self.w_isl): # ISL(xs=self.xs_isl,width=self.w_isl,length=L_isl_side).cell.put('a1',0,self.w_metal/2+self.sp_isl_xs,90) # ISL(xs=self.xs_isl,width=self.w_isl,length=L_isl_side).cell.put('a1',self.L_wg,self.w_metal/2+self.sp_isl_xs,90) if (self.reverse): ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=wg_begin.pin['a0']).put() nd.Pin(name='opt_b1',pin=wg_begin.pin['a0'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=wg_end.pin['b0']).put() nd.Pin(name='opt_a1',pin=wg_end.pin['b0'],type="optical:").put() else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=wg_begin.pin['a0']).put() nd.Pin(name='opt_a1',pin=wg_begin.pin['a0'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=wg_end.pin['b0']).put() nd.Pin(name='opt_b1',pin=wg_end.pin['b0'],type="optical:").put() return C ## Standard unit of a mesh with one MZI and a PS class UMat_2x2_S: """Standard 2x2 MZI mesh unit with phase shifter routing. Parameters ---------- name : str, optional Nazca cell name. BS : Any, optional Beam splitter cell or object. If omitted, a default DC is generated. xs_wg : str, optional Optical waveguide cross-section name. L_arm : int, optional Straight arm length between beam splitters. D_arm : int, optional Vertical spacing between MZI arms. w_wg : float, optional Input/output waveguide width. R_bend : int, optional Routing bend radius. w_arm : float, optional Internal arm width. If omitted, ``w_wg`` is used. Ltp : int, optional Taper length between bus and arm widths. xs_heater : str, optional Heater cross-section name. bend_heaters : bool, optional Route heaters along bent arms. dL_ht : float, optional Heater routing vertical offset. dL_AMZI : float, optional Differential length added for AMZI behavior. L_heater : Any, optional Optional explicit heater length. xs_metal : str, optional Metal routing cross-section name. w_ht : float, optional Heater width. w_metal : float, optional Metal routing width. via_h2m : Any, optional Heater-to-metal via object or cell. isl : Any, optional Isolation helper object or cell. ht_same_side : bool, optional Place both heater contacts on the same side. port_align : bool, optional Align optical ports to a common grid. show_pins : bool, optional Show Nazca pin stubs in the generated layout. Attributes ---------- cell : nazca.Cell Generated 2x2 unit mesh layout cell. """ def __init__(self, name: str="unit_mesh_2x2", BS: Any=None, xs_wg: str='strip', L_arm: int = 80, D_arm: int = 50, w_wg: float = 0.45, R_bend: int=6, w_arm: Optional[float] = None, Ltp: int = 15, xs_heater: str = 'heater', bend_heaters: bool = False, dL_ht: float = 30, dL_AMZI: float = 0, L_heater: Any = None, xs_metal: str = 'metal', w_ht: float = 2.5, w_metal: float = 8, via_h2m: Any = None, isl: Any = None, ht_same_side: bool= False, port_align: bool = True, show_pins: bool=False) -> None: """Initialize the UMat 2x2 S composite. See the class docstring for parameter descriptions. """ self.BS = __BS_generate__(BS=BS,xs=xs_wg,func_name="mxpic::functioanl::Umat_2x2_S") if (w_arm==None): w_arm = w_wg self.name = name self.L_arm = L_arm self.L_heater = L_heater self.D_arm = D_arm self.R_bend = R_bend self.w_wg = w_wg self.xs_wg = xs_wg self.xs_heater = xs_heater self.xs_metal = xs_metal self.w_ht = w_ht self.w_metal = w_metal self.via_h2m = via_h2m self.isl = isl self.bend_heaters = bend_heaters self.dL_ht = dL_ht self.ht_same_side= ht_same_side self.port_align = port_align self.Ltp = Ltp self.w_arm = w_arm self.cell = self.generate_gds(show_pins=show_pins) def __BS_generate__(self,BS,xs): 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 : raise Exception("ERROR:: In , BS not recognizable") return BS_cell def generate_gds(self,show_pins): if (self.name==None): instantiate = False else : instantiate = True with nd.Cell(name=self.name,instantiate=instantiate) as MZI_Unit: pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs=self.xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: self.dL_BS = np.abs(self.BS.pin['a1'].x - self.BS.pin['b1'].x) self.dL_BS = np.abs(self.BS.pin['opt_a1'].x - self.BS.pin['opt_b1'].x) ## 2023.1.21 modified ## revised in 2026.06.07 by Qin Yue # legacy: dY_BS = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y) dY_BS = abs(self.BS.pin['opt_b1'].y - self.BS.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: BS_L = self.BS.put('b1',-2*self.R_bend - self.L_arm/2-1,dY_BS/2,180) BS_L = self.BS.put('opt_b1',-2*self.R_bend - self.L_arm/2-1,dY_BS/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS_R = self.BS.put('a2', 2*self.R_bend + self.L_arm/2+1,dY_BS/2,0,flip=1) BS_R = self.BS.put('opt_a2', 2*self.R_bend + self.L_arm/2+1,dY_BS/2,0,flip=1) if (self.bend_heaters): wg_ht = W_waveguide(L_wg=self.L_arm,w_wg=self.w_wg,xs_wg=self.xs_wg,dL=self.dL_ht,R_bend=self.R_bend, w_ht=self.w_ht,w_metal=self.w_metal, via_h2m=self.via_h2m, isl = self.isl, xs_metal=self.xs_metal, ).cell wg_oht = W_waveguide(L_wg=self.L_arm,w_wg=self.w_wg,xs_wg=self.xs_wg,dL=self.dL_ht,R_bend=self.R_bend,reverse=True, w_ht=0,w_metal=0, ).cell else : wg_ht = waveguide(L_wg=self.L_arm, w_wg=self.w_arm, xs_wg=self.xs_wg, w_port=self.w_wg, Ltp=self.Ltp, L_heater=self.L_heater, w_heater=self.w_ht,w_metal=self.w_metal, xs_heater=self.xs_heater, via_h2m=self.via_h2m, isl = self.isl, xs_metal=self.xs_metal, ).cell wg_oht = waveguide(L_wg=self.L_arm,w_wg=self.w_arm,xs_wg=self.xs_wg,w_port=self.w_wg,Ltp=self.Ltp, w_heater=0,w_metal=0, xs_heater=self.xs_heater,xs_metal=self.xs_metal).cell if (self.ht_same_side): ## revised in 2026.06.07 by Qin Yue # legacy: wg_U = wg_oht.put('a1',-self.L_arm/2,self.D_arm/2,0) wg_U = wg_oht.put('opt_a1',-self.L_arm/2,self.D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: wg_D = wg_ht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) wg_D = wg_ht.put('opt_a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) else: ## revised in 2026.06.07 by Qin Yue # legacy: wg_U = wg_ht.put('a1',-self.L_arm/2,self.D_arm/2,0) wg_U = wg_ht.put('opt_a1',-self.L_arm/2,self.D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: wg_D = wg_oht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) wg_D = wg_oht.put('opt_a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_L.pin['b1'],pin2=wg_U.pin['a1']).put() pic_strip.sbend_p2p(pin1=BS_L.pin['opt_b1'],pin2=wg_U.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_L.pin['b2'],pin2=wg_D.pin['a1']).put() pic_strip.sbend_p2p(pin1=BS_L.pin['opt_b2'],pin2=wg_D.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_R.pin['a2'],pin2=wg_U.pin['b1']).put() pic_strip.sbend_p2p(pin1=BS_R.pin['opt_a2'],pin2=wg_U.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_R.pin['a1'],pin2=wg_D.pin['b1']).put() pic_strip.sbend_p2p(pin1=BS_R.pin['opt_a1'],pin2=wg_D.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: ps_U = wg_oht.put('b1',BS_L.pin['a1'].x-self.R_bend*2-1,self.D_arm/2,180,flip=1) ps_U = wg_oht.put('opt_b1',BS_L.pin['opt_a1'].x-self.R_bend*2-1,self.D_arm/2,180,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: ps_D = wg_ht.put('b1',BS_L.pin['a1'].x-self.R_bend*2-1,-self.D_arm/2,180,flip=0) ps_D = wg_ht.put('opt_b1',BS_L.pin['opt_a1'].x-self.R_bend*2-1,-self.D_arm/2,180,flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=ps_U.pin['b1'],pin2=BS_L.pin['a1']).put() pic_strip.sbend_p2p(pin1=ps_U.pin['opt_b1'],pin2=BS_L.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=ps_D.pin['b1'],pin2=BS_L.pin['a2']).put() pic_strip.sbend_p2p(pin1=ps_D.pin['opt_b1'],pin2=BS_L.pin['opt_a2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['b2'].x+self.R_bend*2+1, self.D_arm/2,0) patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['opt_b2'].x+self.R_bend*2+1, self.D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['b1'].x+self.R_bend*2+1,-self.D_arm/2,0) patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['opt_b1'].x+self.R_bend*2+1,-self.D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['b2']).put() pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['opt_b2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['b1']).put() pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=ps_U.pin['a1'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_a1',pin=ps_U.pin['opt_a1'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=ps_D.pin['a1'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_a2',pin=ps_D.pin['opt_a1'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() nd.Pin(name='ep1',pin=ps_D.pin['ep1']).put() nd.Pin(name='en1',pin=ps_D.pin['en1']).put() if (self.ht_same_side): nd.Pin(name='ep2',pin=wg_D.pin['ep1']).put() nd.Pin(name='en2',pin=wg_D.pin['en1']).put() else: nd.Pin(name='ep2',pin=wg_U.pin['ep1']).put() nd.Pin(name='en2',pin=wg_U.pin['en1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: dX_unit = abs(ps_U.pin['a1'].x - patch_U.pin['b0'].x) dX_unit = abs(ps_U.pin['opt_a1'].x - patch_U.pin['b0'].x) ## revised in 2026.06.07 by Qin Yue # legacy: dY_unit = abs(ps_U.pin['a1'].y - ps_D.pin['a1'].y) dY_unit = abs(ps_U.pin['opt_a1'].y - ps_D.pin['opt_a1'].y) return MZI_Unit class MZI_mesh_U: """Universal MZI mesh built from repeated 2x2 MZI units. Parameters ---------- BS : Any, optional Beam splitter cell or object used by unit cells. xs_wg : str, optional Optical waveguide cross-section name. L_arm : int, optional Straight arm length in each unit MZI. D_arm : int, optional Vertical spacing between MZI arms. w_wg : float, optional Input/output waveguide width. n_ports : int, optional Number of mesh ports. R_bend : int, optional Routing bend radius. L_compensate : int, optional Length used for compensation routing. R_compensate : int, optional Bend radius used by compensation routing. mesh_type : str, optional Mesh topology name, such as ``"triangle"`` or ``"parallelogram"``. xs_heater : str, optional Heater cross-section name. bend_heaters : bool, optional Route heaters along bent arms. dL_ht : float, optional Heater routing vertical offset. xs_metal : str, optional Metal routing cross-section name. w_ht : float, optional Heater width. w_metal : float, optional Metal routing width. w_arm : float, optional Internal arm width. If omitted, ``w_wg`` is used. Ltp : int, optional Taper length between bus and arm widths. via_h2m : Any, optional Heater-to-metal via object or cell. isl : Any, optional Isolation helper object or cell. port_align : bool, optional Align optical ports to a common grid. L_heater : Any, optional Optional explicit heater length. show_pins : bool, optional Show Nazca pin stubs in the generated layout. Attributes ---------- cell : nazca.Cell Generated universal MZI mesh layout cell. """ def __init__(self, BS: Any=None, xs_wg: str='strip', L_arm: int = 80, D_arm: int = 50, w_wg: float = 0.45, n_ports: int = 8, R_bend: int=6, L_compensate: int = 10, R_compensate: int = 10, mesh_type: str = 'triangle', xs_heater: str = 'heater', bend_heaters: bool = True, dL_ht: float = 30, xs_metal: str = 'metal', w_ht: float = 2.5, w_metal: float = 8, w_arm: Optional[float] = None, Ltp: int = 10, via_h2m: Any = None, isl: Any = None, port_align: bool = True, L_heater: Any = None, show_pins: bool=False) -> None: """Initialize the MZI mesh U composite. See the class docstring for parameter descriptions. """ self.BS = __BS_generate__(BS=BS,xs=xs_wg,func_name="mxpic::functioanl::MZI_mesh_U") self.L_arm = L_arm self.L_heater = L_heater self.D_arm = D_arm self.n_ports = n_ports self.R_bend = R_bend self.w_wg = w_wg self.xs_wg = xs_wg self.xs_heater = xs_heater self.xs_metal = xs_metal self.w_ht = w_ht self.w_metal = w_metal self.bend_heaters = bend_heaters self.dL_ht = dL_ht self.mesh_type = mesh_type self.port_align = port_align MZI_unit = UMat_2x2_S(BS=BS, xs_wg=xs_wg, L_arm=L_arm, L_heater=L_heater, D_arm=D_arm, w_wg=w_wg, R_bend=R_bend, w_arm=w_arm, Ltp=Ltp, xs_heater=xs_heater,xs_metal=xs_metal,dL_ht=dL_ht, w_ht=w_ht,w_metal=w_metal, via_h2m=via_h2m, isl=isl, show_pins=show_pins) self.MZI_unit = MZI_unit.cell self.dL_BS = MZI_unit.dL_BS # self.cell_offset = MZI_unit.cell_offset self.cell_compensate = self.__len_compensate_generate__(L_compensate, R_compensate) self.cell = self.generate_gds(show_pins=show_pins) def __BS_generate__(self,BS,xs): 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 : raise Exception("ERROR:: In , BS not recognizable") return BS_cell def __len_compensate_generate__(self, L_compensate, R_compensate) : with nd.Cell(name='Bend_Len_Compensate', instantiate=False) as ICell : strip = Route(radius=R_compensate,width=self.w_wg,xs='strip') strip_input = strip.strt(length=0.2).put(0,0,0) strip.bend_route(radius=R_compensate,angle=90).put() strip.strt(length=L_compensate).put() strip.bend_route(radius=R_compensate,angle=-180).put() strip.strt(length=L_compensate).put() strip.bend_route(radius=R_compensate,angle=180).put() strip.strt(length=L_compensate).put() strip.bend_route(radius=R_compensate,angle=-180).put() strip.strt(length=L_compensate).put() strip.bend_route(radius=R_compensate,angle=90).put() strip_output = strip.strt(length=0.2).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',width=self.w_wg).put(strip_input.pin['a0']) nd.Pin(name='opt_a1',width=self.w_wg,type="optical:").put(strip_input.pin['a0']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',width=self.w_wg).put(strip_output.pin['b0']) nd.Pin(name='opt_b1',width=self.w_wg,type="optical:").put(strip_output.pin['b0']) nd.Pin(name='a0',width=self.w_wg).put(strip_input.pin['a0']) nd.Pin(name='b0',width=self.w_wg).put(strip_output.pin['b0']) return ICell def generate_gds(self,show_pins): with nd.Cell(name="mesh_"+self.mesh_type, instantiate=True) as mesh: print("## ===== %s mesh generating ===== ##" % (self.mesh_type)) rows = self.n_ports-1 pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs='strip') ## revised in 2026.06.07 by Qin Yue # legacy: dX_unit = abs(self.MZI_unit.pin['a1'].x-self.MZI_unit.pin['b1'].x) dX_unit = abs(self.MZI_unit.pin['opt_a1'].x-self.MZI_unit.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: dY_unit = abs(self.MZI_unit.pin['a1'].y-self.MZI_unit.pin['a2'].y) dY_unit = abs(self.MZI_unit.pin['opt_a1'].y-self.MZI_unit.pin['opt_a2'].y) x_spacing = dX_unit ## incase of overlapping y_spacing = dY_unit ## incase of overlapping x_list = [] pin_a_list = [] pin_b_list = [] epin_label = 0 for r_idx in range(1,rows+1): ## start from the largest row if (self.mesh_type=='triangle'): cols = r_idx ## Triangle type else : cols = int(np.floor((self.n_ports + np.mod(r_idx,2) )/2)) ## (N+1)/2 or (N)/2, rectangle type ## Defining starting points ## if (self.mesh_type=='triangle'): x_init = -(x_spacing)*(cols-1) else: x_init = x_spacing*(1-np.mod(r_idx,2)) _y_ = -(y_spacing)*(r_idx-1) for c_idx in range(0,cols): _x_ = c_idx*x_spacing*2 + x_init INSTR = self.MZI_unit.put(_x_,_y_,0) epin_label = epin_label+1 nd.Pin(name='ep'+str(epin_label),pin=INSTR.pin['ep1']).put() nd.Pin(name='en'+str(epin_label),pin=INSTR.pin['en1']).put() epin_label = epin_label+1 nd.Pin(name='ep'+str(epin_label),pin=INSTR.pin['ep2']).put() nd.Pin(name='en'+str(epin_label),pin=INSTR.pin['en2']).put() #### ============= Connecting the unattached waveguides ================ #### if (c_idx>=1 and r_idx==1): ### This will not happen in Triangle cases # pic_strip.sbend_p2p(pin1=pin_up_pre,pin2=INSTR.pin['a1']).put() pic_strip.strt(pin=pin_up_pre,length=self.L_arm).put() ## revised in 2026.06.07 by Qin Yue # legacy: bend_compensate = self.cell_compensate.put('a1',nd.Pin().put(),flip=True) bend_compensate = self.cell_compensate.put('opt_a1',nd.Pin().put(),flip=True) pic_strip.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=bend_compensate.pin['b1'], pin1=bend_compensate.pin['opt_b1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=INSTR.pin['a1'] pin2=INSTR.pin['opt_a1'] ).put() # self.cell_offset.put(flip=1) # self.cell_offset.put(flip=0) # self.cell_offset.put(flip=1) # self.cell_offset.put(flip=0) # pic_strip.sbend_p2p(pin2=INSTR.pin['a1']).put() if (c_idx>=1 and r_idx==rows): if self.mesh_type=='parallelogram': pic_strip.strt(pin=pin_down_pre,length=self.L_arm).put() ## revised in 2026.06.07 by Qin Yue # legacy: bend_compensate = self.cell_compensate.put('a1',nd.Pin().put()) bend_compensate = self.cell_compensate.put('opt_a1',nd.Pin().put()) pic_strip.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=bend_compensate.pin['b1'], pin1=bend_compensate.pin['opt_b1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=INSTR.pin['a2'] pin2=INSTR.pin['opt_a2'] ).put() # self.cell_offset.put(flip=1) # self.cell_offset.put(flip=0) # # pic_strip.strt(length=self.L_arm+self.dL_BS).put() # self.cell_offset.put(flip=1) # self.cell_offset.put(flip=0) # pic_strip.sbend_p2p(pin2=pin_down_pre).put() elif self.mesh_type=='triangle': pic_strip.strt(pin=pin_down_pre,length=self.L_arm).put() ## revised in 2026.06.07 by Qin Yue # legacy: bend_compensate = self.cell_compensate.put('a1',nd.Pin().put()) bend_compensate = self.cell_compensate.put('opt_a1',nd.Pin().put()) pic_strip.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=bend_compensate.pin['b1'], pin1=bend_compensate.pin['opt_b1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=INSTR.pin['a2'] pin2=INSTR.pin['opt_a2'] ).put() # pic_strip.sbend_p2p(pin1=pin_down_pre,pin2=INSTR.pin['a2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pin_down_pre = INSTR.pin['b2'] pin_down_pre = INSTR.pin['opt_b2'] ## revised in 2026.06.07 by Qin Yue # legacy: pin_up_pre = INSTR.pin['b1'] pin_up_pre = INSTR.pin['opt_b1'] """ recongizing pins """ if (self.mesh_type=='triangle'): if (c_idx==0): ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a1']) pin_a_list.append(INSTR.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a1'].x) x_list.append(INSTR.pin['opt_a1'].x) if (c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b1']) pin_b_list.append(INSTR.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b1'].x) x_list.append(INSTR.pin['opt_b1'].x) if (r_idx==rows): if (c_idx==0): ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a2']) pin_a_list.append(INSTR.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a2'].x) x_list.append(INSTR.pin['opt_a2'].x) elif (c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b2']) pin_b_list.append(INSTR.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b2'].x) x_list.append(INSTR.pin['opt_b2'].x) else : if (np.mod(self.n_ports,2)==1): if (np.mod(r_idx,2)==1): if (c_idx==0): ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a1']) pin_a_list.append(INSTR.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a1'].x) x_list.append(INSTR.pin['opt_a1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a2']) pin_a_list.append(INSTR.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a2'].x) x_list.append(INSTR.pin['opt_a2'].x) if (c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b1']) pin_b_list.append(INSTR.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b1'].x) x_list.append(INSTR.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b2']) pin_b_list.append(INSTR.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b2'].x) x_list.append(INSTR.pin['opt_b2'].x) elif (r_idx==rows): if (c_idx==0): ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a2']) pin_a_list.append(INSTR.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a2'].x) x_list.append(INSTR.pin['opt_a2'].x) elif (c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b2']) pin_b_list.append(INSTR.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b2'].x) x_list.append(INSTR.pin['opt_b2'].x) else : if (np.mod(r_idx,2)==0 and c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b1']) pin_b_list.append(INSTR.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b1'].x) x_list.append(INSTR.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b2']) pin_b_list.append(INSTR.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b2'].x) x_list.append(INSTR.pin['opt_b2'].x) if (np.mod(r_idx,2)==1 and c_idx==0): ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a1']) pin_a_list.append(INSTR.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a1'].x) x_list.append(INSTR.pin['opt_a1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: pin_a_list.append(INSTR.pin['a2']) pin_a_list.append(INSTR.pin['opt_a2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['a2'].x) x_list.append(INSTR.pin['opt_a2'].x) if (r_idx==rows and c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b2']) pin_b_list.append(INSTR.pin['opt_b2']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b2'].x) x_list.append(INSTR.pin['opt_b2'].x) if (r_idx==1 and c_idx==cols-1): ## revised in 2026.06.07 by Qin Yue # legacy: pin_b_list.append(INSTR.pin['b1']) pin_b_list.append(INSTR.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: x_list.append(INSTR.pin['b1'].x) x_list.append(INSTR.pin['opt_b1'].x) xmax = np.max(x_list) xmin = np.min(x_list) if (self.port_align): # print("A:",len(pin_a_list)) # print("B:",len(pin_b_list)) if self.mesh_type == "triangle" : for itn in range(0,len(pin_a_list)): nd.strt(length=0.2,width=self.w_wg,xs=self.xs_wg).put(pin_a_list[itn]) num_compensate_cell = len(pin_a_list)-2-itn if num_compensate_cell < 0 : num_compensate_cell = 0 for _num_ in range(num_compensate_cell) : self.cell_compensate.put() nd.strt( ## revised in 2026.06.07 by Qin Yue # legacy: length=abs(self.MZI_unit.pin['a1'].x-self.MZI_unit.pin['b1'].x)-abs(self.cell_compensate.pin['a1'].x-self.cell_compensate.pin['b1'].x), length=abs(self.MZI_unit.pin['opt_a1'].x-self.MZI_unit.pin['opt_b1'].x)-abs(self.cell_compensate.pin['opt_a1'].x-self.cell_compensate.pin['opt_b1'].x), width=self.w_wg, xs=self.xs_wg ).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a'+str(itn+1),pin=nd.Pin().put(),width=0.45).put() nd.Pin(name='opt_a'+str(itn+1),pin=nd.Pin().put(),width=0.45,type="optical:").put() nd.strt(length=0.2,width=self.w_wg,xs=self.xs_wg).put(pin_b_list[itn]) num_compensate_cell = len(pin_a_list)-2-itn if num_compensate_cell < 0 : num_compensate_cell = 0 for _num_ in range(num_compensate_cell) : self.cell_compensate.put(flip=True) nd.strt( ## revised in 2026.06.07 by Qin Yue # legacy: length=abs(self.MZI_unit.pin['a1'].x-self.MZI_unit.pin['b1'].x)-abs(self.cell_compensate.pin['a1'].x-self.cell_compensate.pin['b1'].x), length=abs(self.MZI_unit.pin['opt_a1'].x-self.MZI_unit.pin['opt_b1'].x)-abs(self.cell_compensate.pin['opt_a1'].x-self.cell_compensate.pin['opt_b1'].x), width=self.w_wg, xs=self.xs_wg ).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b'+str(itn+1),pin=nd.Pin().put(),width=0.45).put() nd.Pin(name='opt_b'+str(itn+1),pin=nd.Pin().put(),width=0.45,type="optical:").put() elif self.mesh_type == "parallelogram" : for itn in range(0,len(pin_a_list)): if np.abs(xmin != pin_a_list[itn].x)>0.001 : nd.strt(length=self.L_arm,width=self.w_wg,xs=self.xs_wg).put(pin_a_list[itn]) temp = self.cell_compensate.put() temp = nd.strt(length=abs(xmin-temp.pin['b0'].x),width=self.w_wg,xs=self.xs_wg).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_a'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a'+str(itn+1) ,width=0.45).put(pin_a_list[itn]) nd.Pin(name='opt_a'+str(itn+1) ,width=0.45,type="optical:").put(pin_a_list[itn]) if np.abs(xmax-pin_b_list[itn].x)>0.001 : nd.strt(length=self.L_arm,width=self.w_wg,xs=self.xs_wg).put(pin_b_list[itn]) temp = self.cell_compensate.put(flip=(itn==0)) temp = nd.strt(length=abs(xmax-temp.pin['b0'].x)+0.001,width=self.w_wg,xs=self.xs_wg).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0)).put() nd.Pin(name='opt_b'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0),type="optical:").put() else : ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b'+str(itn+1) ,width=0.45).put(pin_b_list[itn]) nd.Pin(name='opt_b'+str(itn+1) ,width=0.45,type="optical:").put(pin_b_list[itn]) print("## ===== %s mesh DONE ===== ##" % (self.mesh_type)) if (show_pins): nd.put_stub() return mesh class AMZI_W: """Asymmetric MZI with W-shaped phase shifter arms. Parameters ---------- name : str, optional Nazca cell name. BS : Any, optional Beam splitter cell or object. If omitted, a default DC is generated. xs_wg : str, optional Optical waveguide cross-section name. D_arm : int, optional Vertical spacing between MZI arms. w_wg : float, optional Input/output waveguide width. R_bend : int, optional Routing bend radius. n_bend : int, optional Number of W-shaped bend periods. w_arm : float, optional Internal arm width. If omitted, ``w_wg`` is used. Ltp : int, optional Taper length between bus and arm widths. xs_heater : str, optional Heater cross-section name. dL_ht : float, optional Heater routing vertical offset. dL_AMZI : float, optional Differential length added for AMZI behavior. L_heater : Any, optional Optional explicit heater length. xs_metal : str, optional Metal routing cross-section name. w_ht : float, optional Heater width. w_metal : float, optional Metal routing width. D_port : Any, optional Output port pitch override. via_h2m : Any, optional Heater-to-metal via object or cell. isl : Any, optional Isolation helper object or cell. port_align : bool, optional Align optical ports to a common grid. L_patch : float, optional Straight patch length at routing interfaces. show_pins : bool, optional Show Nazca pin stubs in the generated layout. Attributes ---------- cell : nazca.Cell Generated asymmetric W-MZI layout cell. """ def __init__(self, name: str="AMZI_W", BS: Any=None, xs_wg: str='strip', D_arm: int = 50, w_wg: float = 0.45, R_bend: int=6, n_bend: int=3, w_arm: Optional[float] = None, Ltp: int = 15, xs_heater: str = 'heater', dL_ht: float = 30, dL_AMZI: float = 0, L_heater: Any = None, xs_metal: str = 'metal', w_ht: float = 2.5, w_metal: float = 8, D_port: Any = None, via_h2m: Any = None, isl: Any = None, port_align: bool = True, L_patch: float = 0.25, show_pins: bool=False) -> None: """Initialize the AMZI W composite. See the class docstring for parameter descriptions. """ self.BS = __BS_generate__(BS=BS,xs=xs_wg,func_name="mxpic::functioanl::Umat_2x2_S") if (w_arm==None): w_arm = w_wg self.L_arm = n_bend*R_bend*4+L_patch*4*n_bend+L_patch*2 self.name = name self.L_heater = L_heater self.D_arm = D_arm self.dL_AMZI = dL_AMZI self.R_bend = R_bend self.n_bend = n_bend self.w_wg = w_wg self.xs_wg = xs_wg self.D_port = D_port self.xs_heater = xs_heater self.xs_metal = xs_metal self.w_ht = w_ht self.w_metal = w_metal self.via_h2m = via_h2m self.isl = isl self.dL_ht = dL_ht self.port_align = port_align self.Ltp = Ltp self.w_arm = w_arm self.cell = self.generate_gds(show_pins=show_pins) def __BS_generate__(self,BS,xs): 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 : raise Exception("ERROR:: In , BS not recognizable") return BS_cell def generate_gds(self,show_pins): if (self.name==None): instantiate = False else : instantiate = True self.instantiate = instantiate with nd.Cell(name=self.name,instantiate=instantiate) as MZI_Unit: pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs=self.xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: self.dL_BS = np.abs(self.BS.pin['a1'].x - self.BS.pin['b1'].x) self.dL_BS = np.abs(self.BS.pin['opt_a1'].x - self.BS.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: dY_BS = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y) dY_BS = abs(self.BS.pin['opt_b1'].y - self.BS.pin['opt_b2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: dYarm = self.D_arm/2 - self.BS.pin['b1'].y dYarm = self.D_arm/2 - self.BS.pin['opt_b1'].y dLmin = np.sqrt(abs(self.R_bend**2 - (self.R_bend-dYarm)**2))*2 if (dLmin>2*self.R_bend): dLmin = 2*self.R_bend ## revised in 2026.06.07 by Qin Yue # legacy: BS_L = self.BS.put('b1',-dLmin - self.L_arm/2-1,dY_BS/2,180) BS_L = self.BS.put('opt_b1',-dLmin - self.L_arm/2-1,dY_BS/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: BS_R = self.BS.put('a2', dLmin + self.L_arm/2+1,dY_BS/2,0,flip=1) BS_R = self.BS.put('opt_a2', dLmin + self.L_arm/2+1,dY_BS/2,0,flip=1) wg_ht = W_waveguide(L_wg=self.L_arm,w_wg=self.w_wg,xs_wg=self.xs_wg, dL=self.dL_ht+self.dL_AMZI/2/self.n_bend,R_bend=self.R_bend, w_ht=self.w_ht,w_metal=self.w_metal, n_bends=self.n_bend, via_h2m=self.via_h2m, isl = self.isl, xs_metal=self.xs_metal, ).cell wg_oht = W_waveguide(L_wg=self.L_arm,w_wg=self.w_wg,xs_wg=self.xs_wg, dL=self.dL_ht,R_bend=self.R_bend, w_ht=0,w_metal=self.w_metal, n_bends=self.n_bend, via_h2m=self.via_h2m, isl = self.isl, xs_metal=self.xs_metal, ).cell ## revised in 2026.06.07 by Qin Yue # legacy: wg_U = wg_ht.put('a1',-self.L_arm/2,self.D_arm/2,0) wg_U = wg_ht.put('opt_a1',-self.L_arm/2,self.D_arm/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: wg_D = wg_oht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) wg_D = wg_oht.put('opt_a1',-self.L_arm/2,-self.D_arm/2,0,flip=1) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_L.pin['b1'],pin2=wg_U.pin['a1']).put() pic_strip.sbend_p2p(pin1=BS_L.pin['opt_b1'],pin2=wg_U.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_L.pin['b2'],pin2=wg_D.pin['a1']).put() pic_strip.sbend_p2p(pin1=BS_L.pin['opt_b2'],pin2=wg_D.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_R.pin['a2'],pin2=wg_U.pin['b1']).put() pic_strip.sbend_p2p(pin1=BS_R.pin['opt_a2'],pin2=wg_U.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=BS_R.pin['a1'],pin2=wg_D.pin['b1']).put() pic_strip.sbend_p2p(pin1=BS_R.pin['opt_a1'],pin2=wg_D.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_L.pin['a2'].x-self.R_bend*2-1, self.D_port/2,180) patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_L.pin['opt_a2'].x-self.R_bend*2-1, self.D_port/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_L.pin['a1'].x-self.R_bend*2-1,-self.D_port/2,180) patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_L.pin['opt_a1'].x-self.R_bend*2-1,-self.D_port/2,180) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_L.pin['a1']).put() pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_L.pin['opt_a1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_L.pin['a2']).put() pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_L.pin['opt_a2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_a1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_a2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['b2'].x+self.R_bend*2+1, self.D_port/2,0) patch_U = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['opt_b2'].x+self.R_bend*2+1, self.D_port/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['b1'].x+self.R_bend*2+1,-self.D_port/2,0) patch_D = nd.strt(length=0.5,width=self.w_wg,xs=self.xs_wg).put('a0',BS_R.pin['opt_b1'].x+self.R_bend*2+1,-self.D_port/2,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['b2']).put() pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['opt_b2']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['b1']).put() pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['opt_b1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).put() nd.Pin(name='opt_b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg,type="optical:").put() if (self.w_ht>0): nd.Pin(name='ep1',pin=wg_U.pin['ep1']).put() nd.Pin(name='en1',pin=wg_U.pin['en1']).put() return MZI_Unit def generate_test_gds(self,dXgc2gc,dYgc2gc,gc,w_wg=0.5,R_bend=10): if (isinstance(gc,nd.Cell)) : gc_cell = gc elif (hasattr(gc,"cell")): gc_cell = gc.cell gds_name = None if (self.name is not None): gds_name = self.name + "_test" with nd.Cell(name=gds_name,instantiate=self.instantiate) as C: GC1Instr = gc_cell.put('g1',0,0,180) GC2Instr = gc_cell.put('g1',dXgc2gc,0,0) GC3Instr = gc_cell.put('g1',0,-dYgc2gc,180) GC4Instr = gc_cell.put('g1',dXgc2gc,-dYgc2gc,0) ## revised in 2026.06.07 by Qin Yue # legacy: dYcell = abs(self.cell.pin['a1'].y - self.cell.pin['a2'].y) dYcell = abs(self.cell.pin['opt_a1'].y - self.cell.pin['opt_a2'].y) dYoffset = (dYcell - dYgc2gc)/2 ## 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) cellInstr = self.cell.put('opt_a1',dXoffset,dYoffset,0) pic = Route(xs=self.xs_wg,width=w_wg,radius=R_bend) ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['a1'],pin2=GC1Instr.pin['g1']).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_a1'],pin2=GC1Instr.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['b1'],pin2=GC2Instr.pin['g1']).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_b1'],pin2=GC2Instr.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['a2'],pin2=GC3Instr.pin['g1']).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_a2'],pin2=GC3Instr.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic.sbend_p2p(pin1=cellInstr.pin['b2'],pin2=GC4Instr.pin['g1']).put() pic.sbend_p2p(pin1=cellInstr.pin['opt_b2'],pin2=GC4Instr.pin['g1']).put() return C ## Parlogon mesh class MZI_mesh_Parl(MZI_mesh_U): """Parallelogram MZI mesh topology wrapper. Parameters ---------- BS : Any, optional Beam splitter cell or object used by unit cells. xs_wg : str, optional Optical waveguide cross-section name. L_arm : int, optional Straight arm length in each unit MZI. D_arm : int, optional Vertical spacing between MZI arms. w_wg : float, optional Input/output waveguide width. n_ports : int, optional Number of mesh ports. R_bend : int, optional Routing bend radius. L_compensate : int, optional Length used for compensation routing. R_compensate : int, optional Bend radius used by compensation routing. xs_heater : str, optional Heater cross-section name. bend_heaters : bool, optional Route heaters along bent arms. dL_ht : float, optional Heater routing vertical offset. xs_metal : str, optional Metal routing cross-section name. w_ht : float, optional Heater width. w_metal : float, optional Metal routing width. w_ram : float, optional Internal arm width passed to the unit mesh generator. Ltp : int, optional Taper length between bus and arm widths. via_h2m : Any, optional Heater-to-metal via object or cell. isl : Any, optional Isolation helper object or cell. L_heater : Any, optional Optional explicit heater length. port_align : bool, optional Align optical ports to a common grid. show_pins : bool, optional Show Nazca pin stubs in the generated layout. Attributes ---------- cell : nazca.Cell Generated parallelogram MZI mesh layout cell. """ def __init__(self, BS: Any=None, xs_wg: str='strip', L_arm: int=80, D_arm: int=50, w_wg: float=0.45, n_ports: int=8, R_bend: int=6, L_compensate: int=10, R_compensate: int=10, xs_heater: str='heater', bend_heaters: bool=True, dL_ht: float=0, xs_metal: str='metal', w_ht: float=2.5, w_metal: float=8, w_ram: float = 0.45,Ltp: int=15, via_h2m: Any = None, isl: Any = None, L_heater: Any = None, port_align: bool=True, show_pins: bool=False) -> None: """Initialize the MZI mesh Parl composite. See the class docstring for parameter descriptions. """ super().__init__(BS, xs_wg, L_arm, D_arm, w_wg, n_ports, R_bend, L_compensate, R_compensate, 'parallelogram', xs_heater, bend_heaters, dL_ht, xs_metal, w_ht, w_metal, w_ram,Ltp, via_h2m,isl, port_align, L_heater,show_pins) ## Triangle mesh class MZI_mesh_Tri(MZI_mesh_U): """Triangular MZI mesh topology wrapper. Parameters ---------- BS : Any, optional Beam splitter cell or object used by unit cells. xs_wg : str, optional Optical waveguide cross-section name. L_arm : int, optional Straight arm length in each unit MZI. D_arm : int, optional Vertical spacing between MZI arms. w_wg : float, optional Input/output waveguide width. n_ports : int, optional Number of mesh ports. R_bend : int, optional Routing bend radius. L_compensate : int, optional Length used for compensation routing. R_compensate : int, optional Bend radius used by compensation routing. xs_heater : str, optional Heater cross-section name. bend_heaters : bool, optional Route heaters along bent arms. dL_ht : float, optional Heater routing vertical offset. xs_metal : str, optional Metal routing cross-section name. w_ht : float, optional Heater width. w_metal : float, optional Metal routing width. w_ram : float, optional Internal arm width passed to the unit mesh generator. Ltp : int, optional Taper length between bus and arm widths. via_h2m : Any, optional Heater-to-metal via object or cell. isl : Any, optional Isolation helper object or cell. L_heater : Any, optional Optional explicit heater length. port_align : bool, optional Align optical ports to a common grid. show_pins : bool, optional Show Nazca pin stubs in the generated layout. Attributes ---------- cell : nazca.Cell Generated triangular MZI mesh layout cell. """ def __init__(self, BS: Any=None, xs_wg: str='strip', L_arm: int=80, D_arm: int=50, w_wg: float=0.45, n_ports: int=8, R_bend: int=6, L_compensate: int=10, R_compensate: int=10, xs_heater: str='heater', bend_heaters: bool=True, dL_ht: float=0, xs_metal: str='metal', w_ht: float=2.5, w_metal: float=8, w_ram: float = 0.45,Ltp: int=15, via_h2m: Any = None, isl: Any = None, L_heater: Any = None, port_align: bool=True, show_pins: bool=False) -> None: """Initialize the MZI mesh Tri composite. See the class docstring for parameter descriptions. """ super().__init__(BS, xs_wg, L_arm, D_arm, w_wg, n_ports, R_bend, L_compensate, R_compensate, 'triangle', xs_heater, bend_heaters, dL_ht, xs_metal, w_ht, w_metal, w_ram,Ltp, via_h2m,isl, port_align, L_heater,show_pins)