New forge coding added

This commit is contained in:
=
2026-06-04 23:21:39 +08:00
parent 518eb06591
commit 8da92ced57
288 changed files with 52017 additions and 1913 deletions
+45
View File
@@ -0,0 +1,45 @@
from typing import Any
import nazca as nd
import numpy as np
import nazca.interconnects as IC
from ..structures import *
from ..routing import *
from ..primitives.pic import *
from ..electronics import Vias
from ..primitives.passive import waveguide
class LoopMirror():
def __init__(self,
xs_wg: str='strip',
w_wg: float=0.45,
Radius: float = 20,
angle: float = 45,
sharp_patch: bool=True,
BS: Any=None) -> None:
with nd.Cell(instantiate=False) as C:
if (BS==None):
BS = DC(w_cp=0.45,gap=0.2,w_wg=w_wg,L_cp=10,R0=10,angle=20,xs=xs_wg,sharp_patch=True)
BS_Inst = BS.cell.put(0,0,0) ## middle
D_port = np.abs(BS_Inst.pin['b1'].y - BS_Inst.pin['b2'].y)
pic = Route(radius=Radius,width=w_wg,xs=xs_wg)
BU = nd.bend(radius=Radius,width=w_wg,xs=xs_wg,angle=angle).put('a0',BS_Inst.pin['b1'],flip=0)
BD = nd.bend(radius=Radius,width=w_wg,xs=xs_wg,angle=angle).put('a0',BS_Inst.pin['b2'],flip=1)
R_loop = (Radius+D_port/2)/np.cos(angle/180*np.pi) - Radius
BC = nd.bend(radius=R_loop,width=w_wg,xs=xs_wg,angle=180+angle*2).put('a0',BU.pin['b0'],flip=1)
if (sharp_patch==True):
for layers,growx,growy,acc in nd.layeriter(xs=xs_wg):
(a1,b1), (a2,b2),c1,c2 = growx
if (layers.find('CLD')!=-1):
### middle patch
nd.strt(length=(R_loop+w_wg*a1+b1)*(1+np.sin((180-angle)/180*np.pi))+(Radius)*np.sin(angle/180*np.pi),width=2*(R_loop+w_wg*a1+b1),layer=layers).put(BS_Inst.pin['b1'].x,0,0)
nd.Pin(name='a1',pin=BS_Inst.pin['a1']).put()
self.cell = C
+892
View File
@@ -0,0 +1,892 @@
from typing import Any, Optional
import nazca as nd
import numpy as np
# from mxpic.passive.unit import PS_3xg
from ..structures 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__
""" Default 50:50 beam splitter generation using standard DC """
def __BS_generate__(BS,xs,func_name):
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:
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:
"""_summary_
Args:
name (_type_, optional): Name of the device, None means no instance cell will be created. Defaults to None.
xs_wg (str, optional): The xsection of the waveguide. Defaults to 'strip'.
w_wg (float, optional): The width of the ouput waveguide. Defaults to 0.45.
dL_Amzi (int, optional): The asysmmetric length of AMZI. Defaults to 0.
L_arm (int, optional): Length of the arms. Defaults to 150.
R_bend (int, optional): Bneding radius of internal attachment. Defaults to 10.
D_arm (int, optional): Distance of arms. Defaults to 75.
D_port (_type_, optional): Distance of ports. If None, distance of BS ports will be used. Defaults to None.
w_arm (float, optional): Width of arms, if you need wide arm to decrease phase errors. Defaults to 1.0.
xs_heater (str, optional): xsection of heaters. Defaults to 'heater'.
w_heater (int, optional): Width of heaters. Defaults to 0.
Ltp (int, optional): Taper length of the wider arm to the thinner waveguide. Defaults to 15.
xs_metal (str, optional): Xsection of metal. Defaults to 'metal'.
w_metal (int, optional): Width of the attached metal. Defaults to 10.
via_h2m (_type_, optional): The class of the vias. Defaults to None.
isl (_type_, optional): The class of the isolation trench. Defaults to None.
outer_isl (bool, optional): Flag of whether to place outside isolation. Defaults to True.
dual_ht (bool, optional): Flag of whether to have two heaters. Defaults to True.
BS (_type_, optional): The cell class for the first beamsplitter. Defaults to None.
BS2 (_type_, optional): The cell class for the first beamsplitter. Defaults to None.
sharp_patch (bool, optional): Flag of whether to add sharp_patch to the device. Defaults to True.
show_pins (bool, optional): Flag of whether to show pins in the waveguide. Defaults to False.
Raises:
Exception: _description_
Exception: _description_
"""
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")
dY1 = np.abs(cell_BS.pin['b1'].y - cell_BS.pin['b2'].y)
dY2 = np.abs(cell_BS2.pin['b1'].y - cell_BS2.pin['b2'].y)
# dX = np.abs(cell_BS.pin['a1'].x - cell_BS.pin['b1'].x)
BS1 = cell_BS.put('b1',-L_arm/2-R_bend*2-2.5,dY1/2,180)
BS2 = cell_BS2.put('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")
if ('a2' in pin_in_name):
if (D_port==None):
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a2'])
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a1'])
elif(isinstance(D_port,int) or isinstance(D_port,float)):
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
else:
raise Exception("ERROR:: In <mxpic::functionalll::MZI>, <D_port> type error")
else :
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
pin_out_name = []
for str,Pin in cell_BS2.ic_pins():
pin_out_name = pin_out_name+[str]
if ('a2' in pin_out_name):
if (D_port==None):
nd.Pin(name='b2',pin=BS2.pin['a1']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a2'])
nd.Pin(name='b1',pin=BS2.pin['a2']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a1'])
elif(isinstance(D_port,int) or isinstance(D_port,float)):
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
else:
raise Exception("ERROR:: In <mxpic::functionalll::MZI>, <D_port> type error")
else :
nd.Pin(name='b1',pin=BS2.pin['a1']).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)
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['b2'],pin2=arm_down.pin['a1'],arrow=False,original_function=not sharp_patch).put()
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['b1'],pin2=arm_down.pin['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
self.L = np.abs(self.cell.pin['a1'].x-self.cell.pin['b1'].x)
class MZI_NS:
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)
_L_ = abs(BS.pin['a1'].x-BS.pin['b1'].x)
_W_ = abs(BS.pin['b1'].y-BS.pin['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)
PS_U = PS.cell.put('a1',-PS.L_arm/2,D_arm/2,0)
BS1 = BS.put('b1',-R_bend*2-L_patch-PS.L_arm/2,_W_/2,180)
BS2 = BS.put('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)
if ('a2' in pin_in_name):
if (D_port==None):
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.Pin(name='b2',pin=BS2.pin['a2']).put()
nd.Pin(name='b1',pin=BS2.pin['a1']).put()
elif(isinstance(D_port,int) or isinstance(D_port,float)):
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
else:
raise Exception("ERROR:: In <mxpic::functionalll::MZI>, <D_port> type error")
else :
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.Pin(name='b1',pin=BS2.pin['a1']).put()
# print("SEGEMENT")
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['b1'],pin2=BS2.pin['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)
PS_D = PS.cell.put('a1',-PS.L_arm/2,-D_arm/2,0)
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['b1'],pin2=BS2.pin['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:
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)
_L_ = abs(BS.pin['a1'].x-BS.pin['b1'].x)
_W_ = abs(BS.pin['b1'].y-BS.pin['b2'].y)
BS1 = BS.put('b1',-R_bend*2-L_patch/2,_W_/2,180)
BS2 = BS.put('b2', R_bend*2+L_patch/2,_W_/2,0)
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.Pin(name='b1',pin=BS2.pin['a1']).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)
if ('a2' in pin_in_name):
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.Pin(name='b2',pin=BS2.pin['a2']).put()
if (D_port==None):
nd.Pin(name='b2',pin=BS2.pin['a1']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=xs_wg).put(BS2.pin['a2'])
nd.Pin(name='b1',pin=BS2.pin['a2']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=xs_wg).put(BS2.pin['a1'])
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=xs_wg).put(BS1.pin['a2'])
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=xs_wg).put(BS1.pin['a1'])
elif(isinstance(D_port,int) or isinstance(D_port,float)):
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=1)
nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
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)),
original_function=not sharp_patch).put(flip=0)
nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).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,
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)
pic.bend_route_p2p(pin1=PS_L1.pin['a1'],pin2=BS1.pin['b1']).put()
pic.bend_route_p2p(pin1=PS_L1.pin['b1'],pin2=BS2.pin['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,
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)
pic.bend_route_p2p(pin1=PS_L2.pin['a1'],pin2=BS1.pin['b2']).put()
pic.bend_route_p2p(pin1=PS_L2.pin['b1'],pin2=BS2.pin['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:
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)
_L1_ = abs(BS1.pin['a1'].x-BS1.pin['b1'].x)
_L2_ = abs(BS2.pin['a1'].x-BS2.pin['b1'].x)
_L3_ = abs(BS3.pin['a1'].x-BS3.pin['b1'].x)
_W1_ = abs(BS1.pin['b1'].y-BS1.pin['b2'].y)
_W2_ = abs(BS2.pin['b1'].y-BS2.pin['b2'].y)
_W3_ = abs(BS3.pin['b1'].y-BS3.pin['b2'].y)
BSM = BS2.put('a1',-_L2_/2,_W2_/2,0)
BS1 = BS1.put('b1',-_L2_/2-R_bend*4-L_patch,_W2_/2,180)
BS2 = BS3.put('a1', _L2_/2+R_bend*4+L_patch,_W2_/2,0)
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.Pin(name='b1',pin=BS2.pin['b1']).put()
nd.Pin(name='b2',pin=BS2.pin['b2']).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,
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)
pic_strip.bend_route_p2p(pin1=PS_L1.pin['a1'],pin2=BS1.pin['b1']).put()
pic_strip.bend_route_p2p(pin1=PS_L1.pin['b1'],pin2=BSM.pin['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,
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)
pic_strip.bend_route_p2p(pin1=PS_L2.pin['a1'],pin2=BS1.pin['b2']).put()
pic_strip.bend_route_p2p(pin1=PS_L2.pin['b1'],pin2=BSM.pin['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,
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)
pic_strip.bend_route_p2p(pin1=PS_R1.pin['b1'],pin2=BS2.pin['a1']).put()
pic_strip.bend_route_p2p(pin1=PS_R1.pin['a1'],pin2=BSM.pin['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,
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)
pic_strip.bend_route_p2p(pin1=PS_R2.pin['b1'],pin2=BS2.pin['a2']).put()
pic_strip.bend_route_p2p(pin1=PS_R2.pin['a1'],pin2=BSM.pin['b2']).put()
self.cell = C
class MZI_Eubend:
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]
BS1 = BS.put('b1',-R90_eff-R180_eff/2-L_patch/2,BS.pin['b1'].y,180)
BS2 = BS.put('b1', R90_eff+R180_eff/2+L_patch/2,BS.pin['b1'].y,0,flip=1)
tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['b1'].width).put(BS1.pin['b1'])
EUB_90.cell.put('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)
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 = EUB_90.cell.put('a1',tr.pin['b0'],flip=1)
nd.strt(length=abs(tr.pin['b1'].y - tl.pin['b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['b1'])
tl = nd.strt(length=L_patch/2,xs=xs_wg,width=BS1.pin['b2'].width).put(BS1.pin['b2'])
EUB_90.cell.put('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)
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 = EUB_90.cell.put('a1',tr.pin['b0'],flip=0)
nd.strt(length=abs(tr.pin['b1'].y - tl.pin['b1'].y),xs=xs_wg,width=w_arm).put(tr.pin['b1'])
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.Pin(name='b1',pin=BS2.pin['a1']).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)
if ('a2' in pin_in_name):
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.Pin(name='b2',pin=BS2.pin['a2']).put()
self.cell = C
class MZI_Ubend(MZI_NS_ubend):
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:
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)
dY_BS1 = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y)
BS1 = self.BS.put('b1',-self.R_bend-self.L_inner/2,dY_BS1/2,180)
dY_BS2 = abs(self.BS2.pin['b1'].y - self.BS2.pin['b2'].y)
BS2 = self.BS2.put('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,
via_h2m=self.via_h2m).cell.put('a1',-LU/2,dY_BS1/2+self.R_bend*4+self.L_patch,0)
pic_strip.ubend_route(pin=BS1.pin['b1'],length=self.L_patch/2,offset=self.R_bend*2).put()
pic_strip.ubend_p2p(pin2=HT_U.pin['a1'],length=self.L_patch/2).put()
pic_strip.ubend_route(pin=BS2.pin['b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1)
pic_strip.ubend_p2p(pin2=HT_U.pin['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,
via_h2m=self.via_h2m).cell.put('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()
pic_strip.ubend_route(pin=BS1.pin['b2'],length=self.L_patch/2,offset=self.R_bend*2).put(flip=1)
pic_strip.ubend_p2p(pin2=HT_D.pin['a1'],length=self.L_patch/2).put()
pic_strip.ubend_route(pin=BS2.pin['b1'],length=self.L_patch/2,offset=self.R_bend*2).put()
pic_strip.ubend_p2p(pin2=HT_D.pin['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]
if ('a2' in pin_in_name):
if (self.D_port==None):
nd.Pin(name='a2',pin=BS1.pin['a2']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a2'])
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
nd.strt(length=0.005,width=BS1.pin['a2'].width,xs=self.xs_wg).put(BS1.pin['a1'])
elif(isinstance(self.D_port,int) or isinstance(self.D_port,float)):
temp = pic_strip.sbend_route(pin=BS1.pin['a1'],length1=self.L_patch/4,length2=self.L_patch/4,
offset=abs(self.D_port/2-abs(BS1.pin['a1'].y))).put(flip=1)
nd.Pin(name='a1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
temp = pic_strip.sbend_route(pin=BS1.pin['a2'],length1=self.L_patch/4,length2=self.L_patch/4,
offset=abs(self.D_port/2-abs(BS1.pin['a2'].y))).put(flip=0)
nd.Pin(name='a2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
else:
raise Exception("ERROR:: In <mxpic::functionalll::MZI>, <D_port> type error")
else :
nd.Pin(name='a1',pin=BS1.pin['a1']).put()
pin_out_name = []
for str,Pin in self.BS2.ic_pins():
pin_out_name = pin_out_name+[str]
if ('a2' in pin_out_name):
if (self.D_port==None):
nd.Pin(name='b2',pin=BS2.pin['a1']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a2'])
nd.Pin(name='b1',pin=BS2.pin['a2']).put()
nd.strt(length=0.005,width=BS2.pin['a2'].width,xs=self.xs_wg).put(BS2.pin['a1'])
elif(isinstance(self.D_port,int) or isinstance(self.D_port,float)):
temp = pic_strip.sbend_route(pin=BS2.pin['a2'],length1=self.L_patch/4,length2=self.L_patch/4,
offset=abs(self.D_port/2-abs(BS2.pin['a1'].y))).put(flip=0)
nd.Pin(name='b1',pin=temp.pin['b0'].move(-0.05,0,0)).put()
temp = pic_strip.sbend_route(pin=BS2.pin['a1'],length1=self.L_patch/4,length2=self.L_patch/4,
offset=abs(self.D_port/2-abs(BS2.pin['a2'].y))).put(flip=1)
nd.Pin(name='b2',pin=temp.pin['b0'].move(-0.05,0,0)).put()
else:
raise Exception("ERROR:: In <mxpic::functionalll::MZI>, <D_port> type error")
else :
nd.Pin(name='b1',pin=BS2.pin['a1']).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 """
if ("a2" in self.cell.pin):
GC3Instr = gc_cell.put(pin_gc,0,dYgc2gc,180)
if ("b2" in self.cell.pin):
GC4Instr = gc_cell.put(pin_gc,dXgc2gc,dYgc2gc,0)
dYcell = abs(self.cell.pin['b1'].y - self.cell.pin['b2'].y)
dYoffset = -(dYcell - dYgc2gc)/2
else:
dYoffset = 0
dXcell = abs(self.cell.pin['a1'].x - self.cell.pin['b1'].x)
dXoffset = -(dXcell - dXgc2gc)/2
cellInstr = self.cell.put('a1',dXoffset,dYoffset,0,flip=1)
pic = Route(xs=self.xs_wg,width=w_wg,radius=R_bend,sharp_patch=self.sharp_patch)
pic.sbend_p2p(pin1=cellInstr.pin['a1'],pin2=GC1Instr.pin[pin_gc]).put()
pic.sbend_p2p(pin1=cellInstr.pin['b1'],pin2=GC2Instr.pin[pin_gc]).put()
if ("a2" in cellInstr.pin):
pic.sbend_p2p(pin1=cellInstr.pin['a2'],pin2=GC3Instr.pin[pin_gc]).put()
if ("b2" in cellInstr.pin):
pic.sbend_p2p(pin1=cellInstr.pin['b2'],pin2=GC4Instr.pin[pin_gc]).put()
return C
+983
View File
@@ -0,0 +1,983 @@
from typing import Any, Optional
import nazca as nd
import numpy as np
from ..primitives.passive import DC
from ..structures 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:
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:
"""Generate a W-shaped waveguide phase shifter section.
Args:
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
w_wg (float, optional): Waveguide width. Defaults to 0.45.
R_bend (int, optional): Bend radius used by the W routing. Defaults to 10.
dL (float, optional): Vertical excursion of each W bend. Defaults to 20.
L_wg (int, optional): Total phase shifter waveguide length. Defaults to 80.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
w_ht (float, optional): Heater width; use 0 to disable heater geometry. Defaults to 2.5.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_metal (float, optional): Metal routing width. Defaults to 6.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Optional isolation helper reserved for isolation layout. Defaults to None.
n_bends (int, optional): Number of W bend periods. Defaults to 3.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
ISL_UPPER (bool, optional): Enable upper isolation placement when used. Defaults to True.
ISL_LOWER (bool, optional): Enable lower isolation placement when used. Defaults to True.
L_patch (float, optional): Short straight patch length at bend interfaces. Defaults to 0.25.
reverse (bool, optional): Swap optical input/output pin naming. Defaults to False.
"""
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):
nd.Pin(name='b1',pin=wg_begin.pin['a0']).put()
nd.Pin(name='a1',pin=wg_end.pin['b0']).put()
else :
nd.Pin(name='a1',pin=wg_begin.pin['a0']).put()
nd.Pin(name='b1',pin=wg_end.pin['b0']).put()
return C
## Standard unit of a mesh with one MZI and a PS
class UMat_2x2_S:
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:
"""Generate a standard 2x2 MZI mesh unit with phase shifter routing.
Args:
name (str, optional): Nazca cell name. Defaults to "unit_mesh_2x2".
BS (Any, optional): Beam splitter cell/object; None generates the default DC. Defaults to None.
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
L_arm (int, optional): Straight arm length between beam splitters. Defaults to 80.
D_arm (int, optional): Vertical spacing between MZI arms. Defaults to 50.
w_wg (float, optional): Input/output waveguide width. Defaults to 0.45.
R_bend (int, optional): Routing bend radius. Defaults to 6.
w_arm (Optional[float], optional): Internal arm width; None uses w_wg. Defaults to None.
Ltp (int, optional): Taper length between w_wg and w_arm. Defaults to 15.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
bend_heaters (bool, optional): Route heaters along bent arms. Defaults to False.
dL_ht (float, optional): Heater routing vertical offset. Defaults to 30.
dL_AMZI (float, optional): Differential length added for AMZI behavior. Defaults to 0.
L_heater (Any, optional): Optional explicit heater length. Defaults to None.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_ht (float, optional): Heater width. Defaults to 2.5.
w_metal (float, optional): Metal width. Defaults to 8.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Isolation helper object or cell. Defaults to None.
ht_same_side (bool, optional): Place both heater contacts on the same side. Defaults to False.
port_align (bool, optional): Align optical ports to a common grid. Defaults to True.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
"""
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 <mxpic::UMat_2x2_S>, 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)
self.dL_BS = np.abs(self.BS.pin['a1'].x - self.BS.pin['b1'].x)
## 2023.1.21 modified
dY_BS = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y)
BS_L = self.BS.put('b1',-2*self.R_bend - self.L_arm/2-1,dY_BS/2,180)
BS_R = self.BS.put('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):
wg_U = wg_oht.put('a1',-self.L_arm/2,self.D_arm/2,0)
wg_D = wg_ht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1)
else:
wg_U = wg_ht.put('a1',-self.L_arm/2,self.D_arm/2,0)
wg_D = wg_oht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1)
pic_strip.sbend_p2p(pin1=BS_L.pin['b1'],pin2=wg_U.pin['a1']).put()
pic_strip.sbend_p2p(pin1=BS_L.pin['b2'],pin2=wg_D.pin['a1']).put()
pic_strip.sbend_p2p(pin1=BS_R.pin['a2'],pin2=wg_U.pin['b1']).put()
pic_strip.sbend_p2p(pin1=BS_R.pin['a1'],pin2=wg_D.pin['b1']).put()
ps_U = wg_oht.put('b1',BS_L.pin['a1'].x-self.R_bend*2-1,self.D_arm/2,180,flip=1)
ps_D = wg_ht.put('b1',BS_L.pin['a1'].x-self.R_bend*2-1,-self.D_arm/2,180,flip=0)
pic_strip.sbend_p2p(pin1=ps_U.pin['b1'],pin2=BS_L.pin['a1']).put()
pic_strip.sbend_p2p(pin1=ps_D.pin['b1'],pin2=BS_L.pin['a2']).put()
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_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)
pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['b2']).put()
pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['b1']).put()
nd.Pin(name='a1',pin=ps_U.pin['a1'].move(-0.05,0,0),width=self.w_wg).put()
nd.Pin(name='a2',pin=ps_D.pin['a1'].move(-0.05,0,0),width=self.w_wg).put()
nd.Pin(name='b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put()
nd.Pin(name='b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).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()
dX_unit = abs(ps_U.pin['a1'].x - patch_U.pin['b0'].x)
dY_unit = abs(ps_U.pin['a1'].y - ps_D.pin['a1'].y)
return MZI_Unit
class MZI_mesh_U:
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:
"""Generate a universal MZI mesh using repeated 2x2 MZI units.
Args:
BS (Any, optional): Beam splitter cell/object; None generates the default DC. Defaults to None.
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
L_arm (int, optional): MZI unit arm length. Defaults to 80.
D_arm (int, optional): MZI unit arm spacing. Defaults to 50.
w_wg (float, optional): Mesh waveguide width. Defaults to 0.45.
n_ports (int, optional): Number of optical input/output ports. Defaults to 8.
R_bend (int, optional): Mesh routing bend radius. Defaults to 6.
L_compensate (int, optional): Straight length inside compensation bends. Defaults to 10.
R_compensate (int, optional): Bend radius inside compensation cells. Defaults to 10.
mesh_type (str, optional): Mesh topology, such as 'triangle' or 'parallelogram'. Defaults to 'triangle'.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
bend_heaters (bool, optional): Route heaters along bent arms. Defaults to True.
dL_ht (float, optional): Heater routing vertical offset. Defaults to 30.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_ht (float, optional): Heater width. Defaults to 2.5.
w_metal (float, optional): Metal width. Defaults to 8.
w_arm (Optional[float], optional): Internal arm width; None uses w_wg. Defaults to None.
Ltp (int, optional): Taper length between w_wg and w_arm. Defaults to 10.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Isolation helper object or cell. Defaults to None.
port_align (bool, optional): Align external optical ports. Defaults to True.
L_heater (Any, optional): Optional explicit heater length for unit cells. Defaults to None.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
"""
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 <mxpic::MZI_mehs>, 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()
nd.Pin(name='a1',width=self.w_wg).put(strip_input.pin['a0'])
nd.Pin(name='b1',width=self.w_wg).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')
dX_unit = abs(self.MZI_unit.pin['a1'].x-self.MZI_unit.pin['b1'].x)
dY_unit = abs(self.MZI_unit.pin['a1'].y-self.MZI_unit.pin['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()
bend_compensate = self.cell_compensate.put('a1',nd.Pin().put(),flip=True)
pic_strip.sbend_p2p(
pin1=bend_compensate.pin['b1'],
pin2=INSTR.pin['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()
bend_compensate = self.cell_compensate.put('a1',nd.Pin().put())
pic_strip.sbend_p2p(
pin1=bend_compensate.pin['b1'],
pin2=INSTR.pin['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()
bend_compensate = self.cell_compensate.put('a1',nd.Pin().put())
pic_strip.sbend_p2p(
pin1=bend_compensate.pin['b1'],
pin2=INSTR.pin['a2']
).put()
# pic_strip.sbend_p2p(pin1=pin_down_pre,pin2=INSTR.pin['a2']).put()
pin_down_pre = INSTR.pin['b2']
pin_up_pre = INSTR.pin['b1']
""" recongizing pins """
if (self.mesh_type=='triangle'):
if (c_idx==0):
pin_a_list.append(INSTR.pin['a1'])
x_list.append(INSTR.pin['a1'].x)
if (c_idx==cols-1):
pin_b_list.append(INSTR.pin['b1'])
x_list.append(INSTR.pin['b1'].x)
if (r_idx==rows):
if (c_idx==0):
pin_a_list.append(INSTR.pin['a2'])
x_list.append(INSTR.pin['a2'].x)
elif (c_idx==cols-1):
pin_b_list.append(INSTR.pin['b2'])
x_list.append(INSTR.pin['b2'].x)
else :
if (np.mod(self.n_ports,2)==1):
if (np.mod(r_idx,2)==1):
if (c_idx==0):
pin_a_list.append(INSTR.pin['a1'])
x_list.append(INSTR.pin['a1'].x)
pin_a_list.append(INSTR.pin['a2'])
x_list.append(INSTR.pin['a2'].x)
if (c_idx==cols-1):
pin_b_list.append(INSTR.pin['b1'])
x_list.append(INSTR.pin['b1'].x)
pin_b_list.append(INSTR.pin['b2'])
x_list.append(INSTR.pin['b2'].x)
elif (r_idx==rows):
if (c_idx==0):
pin_a_list.append(INSTR.pin['a2'])
x_list.append(INSTR.pin['a2'].x)
elif (c_idx==cols-1):
pin_b_list.append(INSTR.pin['b2'])
x_list.append(INSTR.pin['b2'].x)
else :
if (np.mod(r_idx,2)==0 and c_idx==cols-1):
pin_b_list.append(INSTR.pin['b1'])
x_list.append(INSTR.pin['b1'].x)
pin_b_list.append(INSTR.pin['b2'])
x_list.append(INSTR.pin['b2'].x)
if (np.mod(r_idx,2)==1 and c_idx==0):
pin_a_list.append(INSTR.pin['a1'])
x_list.append(INSTR.pin['a1'].x)
pin_a_list.append(INSTR.pin['a2'])
x_list.append(INSTR.pin['a2'].x)
if (r_idx==rows and c_idx==cols-1):
pin_b_list.append(INSTR.pin['b2'])
x_list.append(INSTR.pin['b2'].x)
if (r_idx==1 and c_idx==cols-1):
pin_b_list.append(INSTR.pin['b1'])
x_list.append(INSTR.pin['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(
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),
width=self.w_wg, xs=self.xs_wg
).put()
nd.Pin(name='a'+str(itn+1),pin=nd.Pin().put(),width=0.45).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(
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),
width=self.w_wg, xs=self.xs_wg
).put()
nd.Pin(name='b'+str(itn+1),pin=nd.Pin().put(),width=0.45).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()
nd.Pin(name='a'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0)).put()
else :
nd.Pin(name='a'+str(itn+1) ,width=0.45).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()
nd.Pin(name='b'+str(itn+1) ,width=0.45, pin=temp.pin['b0'].move(-0.05,0,0)).put()
else :
nd.Pin(name='b'+str(itn+1) ,width=0.45).put(pin_b_list[itn])
print("## ===== %s mesh DONE ===== ##" % (self.mesh_type))
if (show_pins):
nd.put_stub()
return mesh
class AMZI_W:
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:
"""Generate an asymmetric MZI with W-shaped phase shifter arms.
Args:
name (str, optional): Nazca cell name. Defaults to "AMZI_W".
BS (Any, optional): Beam splitter cell/object; None generates the default DC. Defaults to None.
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
D_arm (int, optional): Vertical spacing between MZI arms. Defaults to 50.
w_wg (float, optional): Input/output waveguide width. Defaults to 0.45.
R_bend (int, optional): Routing bend radius. Defaults to 6.
n_bend (int, optional): Number of W bends in the phase shifter. Defaults to 3.
w_arm (Optional[float], optional): Internal arm width; None uses w_wg. Defaults to None.
Ltp (int, optional): Taper length between w_wg and w_arm. Defaults to 15.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
dL_ht (float, optional): Heater routing vertical offset. Defaults to 30.
dL_AMZI (float, optional): Differential length between AMZI arms. Defaults to 0.
L_heater (Any, optional): Optional explicit heater length. Defaults to None.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_ht (float, optional): Heater width. Defaults to 2.5.
w_metal (float, optional): Metal width. Defaults to 8.
D_port (Any, optional): Optional external port spacing override. Defaults to None.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Isolation helper object or cell. Defaults to None.
port_align (bool, optional): Align optical ports to a common grid. Defaults to True.
L_patch (float, optional): Short straight patch length at bend interfaces. Defaults to 0.25.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
"""
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 <mxpic::UMat_2x2_S>, 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)
self.dL_BS = np.abs(self.BS.pin['a1'].x - self.BS.pin['b1'].x)
dY_BS = abs(self.BS.pin['b1'].y - self.BS.pin['b2'].y)
dYarm = self.D_arm/2 - self.BS.pin['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
BS_L = self.BS.put('b1',-dLmin - self.L_arm/2-1,dY_BS/2,180)
BS_R = self.BS.put('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
wg_U = wg_ht.put('a1',-self.L_arm/2,self.D_arm/2,0)
wg_D = wg_oht.put('a1',-self.L_arm/2,-self.D_arm/2,0,flip=1)
pic_strip.sbend_p2p(pin1=BS_L.pin['b1'],pin2=wg_U.pin['a1']).put()
pic_strip.sbend_p2p(pin1=BS_L.pin['b2'],pin2=wg_D.pin['a1']).put()
pic_strip.sbend_p2p(pin1=BS_R.pin['a2'],pin2=wg_U.pin['b1']).put()
pic_strip.sbend_p2p(pin1=BS_R.pin['a1'],pin2=wg_D.pin['b1']).put()
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_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)
pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_L.pin['a1']).put()
pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_L.pin['a2']).put()
nd.Pin(name='a1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put()
nd.Pin(name='a2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).put()
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_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)
pic_strip.sbend_p2p(pin1=patch_U.pin['a0'],pin2=BS_R.pin['b2']).put()
pic_strip.sbend_p2p(pin1=patch_D.pin['a0'],pin2=BS_R.pin['b1']).put()
nd.Pin(name='b1',pin=patch_U.pin['b0'].move(-0.05,0,0),width=self.w_wg).put()
nd.Pin(name='b2',pin=patch_D.pin['b0'].move(-0.05,0,0),width=self.w_wg).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)
dYcell = abs(self.cell.pin['a1'].y - self.cell.pin['a2'].y)
dYoffset = (dYcell - dYgc2gc)/2
dXcell = abs(self.cell.pin['a1'].x - self.cell.pin['b1'].x)
dXoffset = -(dXcell - dXgc2gc)/2
cellInstr = self.cell.put('a1',dXoffset,dYoffset,0)
pic = Route(xs=self.xs_wg,width=w_wg,radius=R_bend)
pic.sbend_p2p(pin1=cellInstr.pin['a1'],pin2=GC1Instr.pin['g1']).put()
pic.sbend_p2p(pin1=cellInstr.pin['b1'],pin2=GC2Instr.pin['g1']).put()
pic.sbend_p2p(pin1=cellInstr.pin['a2'],pin2=GC3Instr.pin['g1']).put()
pic.sbend_p2p(pin1=cellInstr.pin['b2'],pin2=GC4Instr.pin['g1']).put()
return C
## Parlogon mesh
class MZI_mesh_Parl(MZI_mesh_U):
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:
"""Generate a parallelogram MZI mesh.
Args:
BS (Any, optional): Beam splitter cell/object; None generates the default DC. Defaults to None.
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
L_arm (int, optional): MZI unit arm length. Defaults to 80.
D_arm (int, optional): MZI unit arm spacing. Defaults to 50.
w_wg (float, optional): Mesh waveguide width. Defaults to 0.45.
n_ports (int, optional): Number of optical input/output ports. Defaults to 8.
R_bend (int, optional): Mesh routing bend radius. Defaults to 6.
L_compensate (int, optional): Straight length inside compensation bends. Defaults to 10.
R_compensate (int, optional): Bend radius inside compensation cells. Defaults to 10.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
bend_heaters (bool, optional): Route heaters along bent arms. Defaults to True.
dL_ht (float, optional): Heater routing vertical offset. Defaults to 0.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_ht (float, optional): Heater width. Defaults to 2.5.
w_metal (float, optional): Metal width. Defaults to 8.
w_ram (float, optional): Internal arm width passed to the unit cell. Defaults to 0.45.
Ltp (int, optional): Taper length between waveguide widths. Defaults to 15.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Isolation helper object or cell. Defaults to None.
L_heater (Any, optional): Optional explicit heater length. Defaults to None.
port_align (bool, optional): Align external optical ports. Defaults to True.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
"""
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):
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:
"""Generate a triangular MZI mesh.
Args:
BS (Any, optional): Beam splitter cell/object; None generates the default DC. Defaults to None.
xs_wg (str, optional): Waveguide xsection name. Defaults to 'strip'.
L_arm (int, optional): MZI unit arm length. Defaults to 80.
D_arm (int, optional): MZI unit arm spacing. Defaults to 50.
w_wg (float, optional): Mesh waveguide width. Defaults to 0.45.
n_ports (int, optional): Number of optical input/output ports. Defaults to 8.
R_bend (int, optional): Mesh routing bend radius. Defaults to 6.
L_compensate (int, optional): Straight length inside compensation bends. Defaults to 10.
R_compensate (int, optional): Bend radius inside compensation cells. Defaults to 10.
xs_heater (str, optional): Heater xsection name. Defaults to 'heater'.
bend_heaters (bool, optional): Route heaters along bent arms. Defaults to True.
dL_ht (float, optional): Heater routing vertical offset. Defaults to 0.
xs_metal (str, optional): Metal xsection name. Defaults to 'metal'.
w_ht (float, optional): Heater width. Defaults to 2.5.
w_metal (float, optional): Metal width. Defaults to 8.
w_ram (float, optional): Internal arm width passed to the unit cell. Defaults to 0.45.
Ltp (int, optional): Taper length between waveguide widths. Defaults to 15.
via_h2m (Any, optional): Heater-to-metal via object or cell. Defaults to None.
isl (Any, optional): Isolation helper object or cell. Defaults to None.
L_heater (Any, optional): Optional explicit heater length. Defaults to None.
port_align (bool, optional): Align external optical ports. Defaults to True.
show_pins (bool, optional): Show Nazca pin stubs. Defaults to False.
"""
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)
+130
View File
@@ -0,0 +1,130 @@
from typing import Any
import nazca as nd
import numpy as np
import nazca.interconnects as IC
from ..structures import *
from ..routing import *
from ..primitives.pic import *
## Class for splitting tree
class SplittingTree():
'''
Class for generating splitting tree.
Paras:
1. ybranch [class] (Default None)
- length (Default: 28) Length of the ybranch
- width (Default: 2) Pitch between two output waveguides
- w_wg (Default: 0.45) Width of output waveguide
- cell (Default: box)
- a1 [Pin] Input waveguide
- b1 [Pin] Output waveguide1
- b2 [Pin] Output waveguide2
2. output_number [-] (Default: 16)
Number of output channels(Need to be 2^N)
3. bend_radius [um] (Default: 10)
Bend radius used to connect the different layer of Y branch
4. output_pitch [um] (Default: None)
Can define the pitch of output channel(If ==None, then pitch=self.width, which is the minimum pitch)
'''
def __init__(self, ybranch: Any=None, output_number: int=16, bend_radius: int=10, output_pitch: Any=None, show_pins: bool=False) -> None:
if ybranch == None:
self._generate_default_ybranch_()
else:
self.yb_cell = ybranch.cell
# self.yb_length = ybranch.length
self.yb_length = np.abs(self.yb_cell.pin['a1'].x - self.yb_cell.pin['b1'].x)
# self.yb_width = ybranch.width
self.yb_width = np.abs(self.yb_cell.pin['b1'].y - self.yb_cell.pin['b2'].y)
self.w_wg = ybranch.w_wg
self.output_number = output_number
self.bend_radius = bend_radius
if output_pitch == None:
self.output_pitch = self.yb_width
else:
self.output_pitch = output_pitch
self.show_pins = show_pins
self.cell = self.generate_gds()
def generate_gds(self):
'''
Generate the gds of splitting tree.
'''
## initialize the parameters of splitting tree
self.level_number = np.log2(self.output_number)
if self.level_number != int(np.log2(self.output_number)):
print("WARNNING:: Please check the output number of your generated splitting tree, which is not 2^N. ")
self.level_number = int(self.level_number)
self.output_number = np.power(2, self.level_number)
## Generate the splitting tree
with nd.Cell(name="SplittingTree_N"+str(self.output_number), instantiate=False) as C:
stripe_class = Route(xs="strip", width=self.w_wg, radius=self.bend_radius)
cell_dic = {}
cell_dic['00'] = self.yb_cell.put('a0', 0, 0)
x_cur = 0
for level_index in range(1, int(self.level_number)):
# Calculate the x location for current level
y_pitch = self.output_pitch * np.power(2, self.level_number-level_index)
if self.bend_radius > (y_pitch/2-self.yb_width/2)/2:
x_cur = x_cur + self.yb_length + 2*np.sqrt(np.power(self.bend_radius,2)-np.power(self.bend_radius-(y_pitch/2)/2, 2)) + 2
else:
x_cur = x_cur + self.yb_length + self.bend_radius*2 + 2
for yb_index in range(np.power(2, level_index)):
y_cur = y_pitch * ((np.power(2, level_index)-1)/2 - yb_index)
# Put the Y-branch
cell_dic[str(level_index)+str(yb_index)] = self.yb_cell.put('a1', x_cur, y_cur)
# Do the routing
stripe_class.sbend_p2p(
pin1=cell_dic[str(level_index-1)+str(yb_index//2)].pin['b'+str(yb_index%2+1)],
pin2=cell_dic[str(level_index)+str(yb_index)].pin['a1'],
Lstart=1,
arrow=False
).put()
## Put pins
nd.Pin(name="a0").put(0, 0, 180)
nd.Pin(name="a1",width=self.w_wg).put(0, 0, 180)
level_index = int(self.level_number-1)
for yb_index in range(np.power(2, level_index)):
nd.Pin(
name="b"+str(2*yb_index+1),
width=self.w_wg
).put(cell_dic[str(level_index)+str(yb_index)].pin['b1'])
nd.Pin(
name="b"+str(2*yb_index+2),
width=self.w_wg
).put(cell_dic[str(level_index)+str(yb_index)].pin['b2'])
nd.Pin(name="b0").put(
cell_dic[str(level_index)+str(yb_index)].pin['b2'].x,
0,
0
)
if self.show_pins:
nd.put_stub()
return C
def _generate_default_ybranch_(self, length=28, width=2, w_wg=0.45):
'''
Generate a ybranch built by box for quick showing.
'''
self.yb_length = length
self.yb_width = width
self.w_wg = w_wg
with nd.Cell(name="Ybranch", instantiate=False) as C:
## Put block to show the size of the device and the location of input&output waveguide
nd.strt(length=self.yb_length, width=self.yb_width+2, layer=(1001, 1)).put(0, 0)
nd.strt(length=0.2, width=self.w_wg, layer=(1001, 2)).put(0, 0)
nd.strt(length=0.2, width=self.w_wg, layer=(1001, 2)).put(self.yb_length, self.yb_width/2, 180)
nd.strt(length=0.2, width=self.w_wg, layer=(1001, 2)).put(self.yb_length, -self.yb_width/2, 180)
## Put pins
nd.Pin(name="a0", width=self.w_wg).put(0, 0, 180)
nd.Pin(name="a1", width=self.w_wg).put(0, 0, 180)
nd.Pin(name="b1", width=self.w_wg).put(self.yb_length, self.yb_width/2, 0)
nd.Pin(name="b2", width=self.w_wg).put(self.yb_length, -self.yb_width/2, 0)
self.yb_cell = C
+11
View File
@@ -0,0 +1,11 @@
from .advance import *
from .MZI import MZI,MZI_NS_ubend,MZI_2st_ubend,MZI_Eubend,MZI_NS,MZI_Ubend,MZI_Butterfly
from .SptTree import SplittingTree
from .LoopMirror import LoopMirror
from .MZI_mesh import MZI_mesh_U, MZI_mesh_Parl, MZI_mesh_Tri, W_waveguide,UMat_2x2_S,AMZI_W
from . import advance
+6
View File
@@ -0,0 +1,6 @@
from ..primitives.passive.rings import AED_ring,MRR_AED,STD_PIC_Rings,MRR_STD_Ring
from ..primitives.passive.crows import CROW_Circular_ring