Files
mxpic_forge/mxpic/components/composites/MZI.py
T

1585 lines
76 KiB
Python

"""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 <mxpic::functionalll::MZI>, <D_port> 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 <mxpic::functionalll::MZI>, <D_port> 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 <mxpic::functionalll::MZI>, <D_port> 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 <mxpic::functionalll::MZI>, <D_port> 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 <mxpic::functionalll::MZI>, <D_port> 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 <mxpic::functionalll::MZI>, <D_port> 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