379 lines
15 KiB
Python
379 lines
15 KiB
Python
import nazca as nd
|
|
import numpy as np
|
|
import math
|
|
from ...routing import Route
|
|
|
|
from ...geometry import *
|
|
from ....technologies import *
|
|
|
|
import pandas as pd
|
|
from ...geometry import _my_polygon
|
|
|
|
""" NEW Classes :: Transistion area of rib to strip, to minize the loss """
|
|
class transition:
|
|
"""
|
|
transition primitive component.
|
|
|
|
This component builds the transition layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
layer_FETCH : str, optional
|
|
Layer or cross-section name used by the device. Default is 'STRIP_TRE'.
|
|
layer_METCH : str, optional
|
|
Layer or cross-section name used by the device. Default is 'RIB_TRE'.
|
|
w_rib : float, optional
|
|
Width parameter in microns. Default is 1.1.
|
|
dw_tolerance : float, optional
|
|
Value for the dw_tolerance parameter. Default is 0.2.
|
|
w_grow_rib : float, optional
|
|
Width parameter in microns. Default is 2.
|
|
w_grow_strip : float, optional
|
|
Width parameter in microns. Default is 2.
|
|
Ltp1 : int, optional
|
|
Length parameter in microns. Default is 15.
|
|
Ltp2 : int, optional
|
|
Length parameter in microns. Default is 10.
|
|
Ltrans : int, optional
|
|
Length parameter in microns. Default is 5.
|
|
L_port : int, optional
|
|
Length parameter in microns. Default is 2.
|
|
show_pins : bool, optional
|
|
Whether to draw pin markers in the generated layout. Default is False.
|
|
"""
|
|
def __init__(self,
|
|
layer_FETCH: str="STRIP_TRE",
|
|
layer_METCH: str="RIB_TRE",
|
|
w_rib: float=1.1,
|
|
dw_tolerance: float = 0.2,
|
|
w_grow_rib: float=2,
|
|
w_grow_strip: float=2,
|
|
Ltp1: int=15,
|
|
Ltp2: int=10,
|
|
Ltrans: int=5,
|
|
L_port: int=2,
|
|
show_pins: bool=False,
|
|
) -> None:
|
|
|
|
with nd.Cell(instantiate=False) as C:
|
|
""" Placing taper for rib etching"""
|
|
dLx = np.array([0,L_port+Ltp1+Ltrans+Ltp2,Ltp2])
|
|
vtx_x = np.cumsum(dLx)
|
|
vtx_x = np.r_[vtx_x,vtx_x[-1],0]
|
|
vtx_y = np.array([w_rib/2,w_rib/2,w_rib/2+dw_tolerance,w_rib/2+w_grow_rib+w_grow_strip,w_rib/2+w_grow_rib+w_grow_strip])
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layer_METCH,vtx=vtx).put(0,0,0)
|
|
|
|
vtx_y = -vtx_y
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layer_METCH,vtx=vtx).put(0,0,0)
|
|
|
|
""" Placing taper for rib etching"""
|
|
dLx = np.array([0,L_port,Ltp1,Ltp2,Ltrans+Ltp2+L_port])
|
|
vtx_x = np.cumsum(dLx)
|
|
vtx_x = np.r_[vtx_x,vtx_x[-1],0]
|
|
vtx_y = np.array([w_rib/2+w_grow_rib,w_rib/2+w_grow_rib,w_rib/2+dw_tolerance,w_rib/2,w_rib/2,w_rib/2+w_grow_rib+w_grow_strip,w_rib/2+w_grow_rib+w_grow_strip])
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layer_FETCH,vtx=vtx).put(0,0,0)
|
|
|
|
vtx_y = -vtx_y
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layer_FETCH,vtx=vtx).put(0,0,0)
|
|
|
|
nd.Pin(name='a0',width=w_rib).put(0,0,180)
|
|
nd.Pin(name='b0',width=w_rib).put(L_port*2+Ltrans+Ltp1+Ltp2*2,0,0)
|
|
|
|
if (show_pins):
|
|
nd.put_stub()
|
|
|
|
self.cell = C
|
|
|
|
class taper_xs2xs:
|
|
"""
|
|
taper xs2xs primitive component.
|
|
|
|
This component builds the taper xs2xs layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
xs_1 : str, optional
|
|
Layer or cross-section name used by the device. Default is 'rib'.
|
|
xs_2 : str, optional
|
|
Layer or cross-section name used by the device. Default is 'strip'.
|
|
L_taper : float, optional
|
|
Length parameter in microns. Default is 10.
|
|
w_1 : float, optional
|
|
Width parameter in microns. Default is 0.45.
|
|
w_2 : float, optional
|
|
Width parameter in microns. Default is 0.45.
|
|
L_port : float, optional
|
|
Length parameter in microns. Default is 0.
|
|
"""
|
|
def __init__ (self,
|
|
xs_1:str='rib',
|
|
xs_2:str='strip',
|
|
L_taper:float=10,
|
|
w_1:float=0.45,
|
|
w_2:float=0.45,
|
|
L_port:float = 0) -> None:
|
|
"""_summary_
|
|
|
|
Args:
|
|
xs_1 (str, optional): fisrt xsection. Defaults to 'rib'.
|
|
xs_2 (str, optional): second xsection. Defaults to 'strip'.
|
|
L_taper (float, optional): length of the xsection taper. Defaults to 10.
|
|
w_1 (float, optional): first xsection width. Defaults to 0.45.
|
|
w_2 (float, optional): second xsection width. Defaults to 0.45.
|
|
L_port (float, optional): length attached to the end. Defaults to 0.
|
|
|
|
Raises:
|
|
Exception: No xsection input found
|
|
"""
|
|
|
|
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
xs_1_layer_list = []
|
|
xs_2_layer_list = []
|
|
growx_1_list = []
|
|
growx_2_list = []
|
|
|
|
## input guard
|
|
try:
|
|
nd.get_xsection(xs_1)
|
|
except:
|
|
raise Exception("ERROR: in <mxpic.passive.pic.taper.taper_xs2xs> No xsection ==",xs_1,"== were found")
|
|
|
|
try:
|
|
nd.get_xsection(xs_2)
|
|
except:
|
|
raise Exception("ERROR: in <mxpic.passive.pic.taper.taper_xs2xs> No xsection ==",xs_2,"== were found")
|
|
|
|
|
|
for layers_1,growx,growy,acc in nd.layeriter(xs=xs_1):
|
|
xs_1_layer_list = xs_1_layer_list + [layers_1]
|
|
growx_1_list = growx_1_list + [growx]
|
|
|
|
|
|
for layers_2,growx,growy,acc in nd.layeriter(xs=xs_2):
|
|
|
|
xs_2_layer_list = xs_2_layer_list + [layers_2]
|
|
growx_2_list = growx_2_list + [growx]
|
|
|
|
|
|
for layers_1 in xs_1_layer_list:
|
|
if (layers_1 in xs_2_layer_list):
|
|
## same layer
|
|
(a1_1,b1_1), (a2_1,b2_1),c1,c2 = growx_1_list[xs_1_layer_list.index(layers_1)]
|
|
(a1_2,b1_2), (a2_2,b2_2),c1,c2 = growx_2_list[xs_2_layer_list.index(layers_1)]
|
|
w1 = w_1*(a1_1-a2_1) + (b1_1-b2_1)
|
|
w2 = w_2*(a1_2-a2_2) + (b1_2-b2_2)
|
|
nd.taper(length=L_taper,width1=w1,width2=w2,layer=layers_1).put(0,0,0)
|
|
else :
|
|
if (layers_1.find('COR')!=-1):
|
|
(a1_1,b1_1), (a2_1,b2_1),c1,c2 = growx_1_list[xs_1_layer_list.index(layers_1)]
|
|
w1 = w_1*(a1_1-a2_1) + (b1_1-b2_1)
|
|
w2 = w_2
|
|
nd.taper(length=L_taper,width1=w1,width2=w2,layer=layers_1).put(0,0,0)
|
|
else :
|
|
(a1_1,b1_1), (a2_1,b2_1),c1,c2 = growx_1_list[xs_1_layer_list.index(layers_1)]
|
|
w1 = w_1*(a1_1-a2_1) + (b1_1-b2_1)
|
|
w2 = w_2*(a1_1-a2_1) + (b1_1-b2_1)
|
|
nd.taper(length=L_taper,width1=w1,width2=w2,layer=layers_1).put(0,0,0)
|
|
|
|
for layers_2 in xs_2_layer_list:
|
|
if (layers_2 not in xs_1_layer_list):
|
|
if (layers_2.find('COR')!=-1):
|
|
(a1_2,b1_2), (a2_2,b2_2),c1,c2 = growx_2_list[xs_2_layer_list.index(layers_2)]
|
|
w1 = w_1
|
|
w2 = w_2*(a1_2-a2_2) + (b1_2-b2_2)
|
|
nd.taper(length=L_taper,width1=w1,width2=w2,layer=layers_2).put(0,0,0)
|
|
|
|
else :
|
|
(a1_1,b1_1), (a2_1,b2_1),c1,c2 = growx_2_list[xs_2_layer_list.index(layers_2)]
|
|
w1 = w_1*(a1_1-a2_1) + (b1_1-b2_1) ## Revised 2022.12.06, HGL
|
|
w2 = w_2*(a1_1-a2_1) + (b1_1-b2_1)
|
|
nd.taper(length=L_taper,width1=w1,width2=w2,layer=layers_2).put(0,0,0)
|
|
|
|
nd.strt(length=L_port,xs=xs_1,width=w_1).put(0,0,180)
|
|
nd.strt(length=L_port,xs=xs_2,width=w_2).put(L_taper,0,0)
|
|
|
|
nd.Pin(name='a0',width=w_1).put(-L_port,0,180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=w_1).put(-L_port,0,180)
|
|
nd.Pin(name='opt_a1',width=w_1,type="optical:").put(-L_port,0,180)
|
|
nd.Pin(name='b0',width=w_2).put(L_taper+L_port,0,0)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=w_2).put(L_taper+L_port,0,0)
|
|
nd.Pin(name='opt_b1',width=w_2,type="optical:").put(L_taper+L_port,0,0)
|
|
|
|
self.cell = C
|
|
|
|
class PSR:
|
|
"""
|
|
Polarization Splitter & rotator.
|
|
|
|
Parameters
|
|
----------
|
|
name : str, optional
|
|
Unique identifier for the device cell. Default is 'PSR_unit'.
|
|
xs : str, optional
|
|
Layer or cross-section name used by the device. Default is 'rib'.
|
|
layer_u : str, optional
|
|
Layer or cross-section name used by the device. Default is None.
|
|
layer_d : str, optional
|
|
Layer or cross-section name used by the device. Default is None.
|
|
w : list, optional
|
|
Width parameter in microns. Default is [0.45, 0.45, 0.55, 1.2, 1.2].
|
|
Lt_rib : list, optional
|
|
Length parameter in microns. Default is [10, 30, 30, 10].
|
|
ws : list, optional
|
|
Value for the ws parameter. Default is [0.45, 2, 1.2].
|
|
Lt_slab : list, optional
|
|
Length parameter in microns. Default is [40, 40].
|
|
shape : str, optional
|
|
Value for the shape parameter. Default is 'sine'.
|
|
L_port : float, optional
|
|
Length parameter in microns. Default is 5.
|
|
res : float, optional
|
|
Value for the res parameter. Default is 0.01.
|
|
"""
|
|
def __init__ (self,
|
|
|
|
name : str="PSR_unit",
|
|
xs :str='rib',
|
|
layer_u: str=None, ## override xs_u
|
|
layer_d: str=None, ## override xs_d
|
|
w: list=[0.45,0.45,0.55,1.2,1.2],
|
|
Lt_rib: list=[10,30,30,10],
|
|
ws: list=[0.45,2,1.2],
|
|
Lt_slab: list=[40,40],
|
|
shape:str='sine',
|
|
L_port:float = 5,
|
|
res:float=0.01) -> None:
|
|
|
|
if (name==None):
|
|
instantiate = False
|
|
else :
|
|
instantiate = True
|
|
|
|
try:
|
|
nd.get_xsection(xs)
|
|
except:
|
|
raise Exception("ERROR: In <mxpic::passive::taper>, xs=",xs," is not defined in the tapeout")
|
|
|
|
|
|
|
|
if (len(w)!=len(Lt_rib)+1):
|
|
print("WARNING: In <mxpic::passive::taper>, rib area width section do not match with taper section")
|
|
return 0
|
|
|
|
if (len(w)!=len(Lt_rib)+1):
|
|
print("WARNING: In <mxpic::passive::taper>, slab area width section do not match with taper section")
|
|
return 0
|
|
|
|
w = np.array(w).tolist()
|
|
ws = np.array(ws).tolist()
|
|
Lt_rib = np.array(Lt_rib).tolist()
|
|
Lt_slab = np.array(Lt_slab).tolist()
|
|
|
|
self.w = w
|
|
self.ws = ws
|
|
self.Lt_rib = Lt_rib
|
|
self.Lt_slab = Lt_slab
|
|
|
|
## defining the shape of the PSR
|
|
if (shape=='sine'):
|
|
wu_final = []
|
|
Lu_final = []
|
|
for _idx_ in range(0,len(Lt_rib)):
|
|
|
|
Lt_seg = np.linspace(0,Lt_rib[_idx_],int(Lt_rib[_idx_]/res)+1)
|
|
wu_temp = (w[_idx_] + w[_idx_+1])/2 + (w[_idx_] - w[_idx_+1])/2*np.cos(Lt_seg/Lt_rib[_idx_]*pi)
|
|
if (_idx_>=0):
|
|
Lt_seg = Lt_seg + np.sum(Lt_rib[0:_idx_])
|
|
|
|
wu_final = np.r_[wu_final,wu_temp]
|
|
Lu_final = np.r_[Lu_final,Lt_seg]
|
|
|
|
wd_final = []
|
|
Ld_final = []
|
|
|
|
for _idx_ in range(0,len(Lt_slab)):
|
|
Lt_seg = np.linspace(0,Lt_slab[_idx_],int(Lt_slab[_idx_]/res)+1)
|
|
wd_temp = (ws[_idx_] + ws[_idx_+1])/2 + (ws[_idx_] - ws[_idx_+1])/2*np.cos(Lt_seg/Lt_slab[_idx_]*pi)
|
|
if (_idx_>0):
|
|
Lt_seg = Lt_seg + np.sum(Lt_slab[0:_idx_])
|
|
|
|
wd_final = np.r_[wd_final,wd_temp]
|
|
Ld_final = np.r_[Ld_final,Lt_seg]
|
|
|
|
elif(shape=='linear'):
|
|
|
|
wd_final = np.array(ws)
|
|
Ld_final = np.array(Lt_slab)
|
|
wu_final = np.array(w)
|
|
Lu_final = np.array(Lt_rib)
|
|
Lu_final = np.r_[0,np.cumsum(Lu_final)]
|
|
Ld_final = np.r_[0,np.cumsum(Ld_final)] ## FATAL ERROR CORRECTED: 2023.1.31
|
|
|
|
# print(wd_final,wu_final)
|
|
# print(Ld_final,Lu_final)
|
|
|
|
else:
|
|
wd_final = np.array(ws)
|
|
Ld_final = np.array(Lt_slab)
|
|
wu_final = np.array(w)
|
|
Lu_final = np.array(Lt_rib)
|
|
Lu_final = np.r_[0,np.cumsum(Lu_final)]
|
|
Ld_final = np.r_[0,np.cumsum(Ld_final)]
|
|
|
|
Lu_vtx = np.r_[Lu_final,np.flip(np.array(Lu_final),0)]
|
|
Ld_vtx = np.r_[Ld_final,np.flip(np.array(Ld_final),0)]
|
|
wu_vtx = np.r_[wu_final/2,np.flip(np.array(-wu_final/2),0)]
|
|
wd_vtx = np.r_[wd_final/2,np.flip(np.array(-wd_final/2),0)]
|
|
|
|
with nd.Cell(instantiate=instantiate,name=name) as C:
|
|
|
|
if (layer_u!=None and layer_d!=None):
|
|
vtx = np.c_[Lu_vtx,wu_vtx]
|
|
_my_polygon(layer_u,vtx).put(0,0,0)
|
|
vtx = np.c_[Ld_vtx,wd_vtx]
|
|
_my_polygon(layer_d,vtx).put(0,0,0)
|
|
|
|
else :
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
if (b1==0 and b2==0 and layers.find("COR")!=-1):
|
|
wu_temp = np.r_[wu_final/2,np.flip(np.array(-wu_final/2),0)]
|
|
vtx = np.c_[Lu_vtx,wu_temp]
|
|
_my_polygon(layers,vtx).put(0,0,0)
|
|
w0=wu_final[0]
|
|
w1=wu_final[-1]
|
|
|
|
elif (layers.find("COR")!=-1):
|
|
wd_temp = np.r_[wd_final/2,np.flip(np.array(-wd_final/2),0)]
|
|
vtx = np.c_[Ld_vtx,wd_temp]
|
|
_my_polygon(layers,vtx).put(0,0,0)
|
|
w0=wd_final[0]
|
|
w1=wd_final[-1]
|
|
else :
|
|
wd_temp = np.r_[(wd_final*(a1-a2)+(b1-b2))/2,np.flip(np.array(-(wd_final*(a1-a2)+(b1-b2))/2),0)]
|
|
vtx = np.c_[Ld_vtx,wd_temp]
|
|
_my_polygon(layers,vtx).put(0,0,0)
|
|
w0=(wd_final[0]*(a1-a2)+(b1-b2))
|
|
w1=(wd_final[-1]*(a1-a2)+(b1-b2))
|
|
|
|
nd.strt(width=w0,length=L_port,layer=layers).put(0,0,180)
|
|
nd.strt(width=w1,length=L_port,layer=layers).put(sum(Lt_rib),0,0)
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=w[0]).put(-L_port,0,180)
|
|
nd.Pin(name='opt_a1',width=w[0],type="optical:").put(-L_port,0,180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=w[-1]).put(sum(Lt_rib)+L_port,0,0)
|
|
nd.Pin(name='opt_b1',width=w[-1],type="optical:").put(sum(Lt_rib)+L_port,0,0)
|
|
self.cell = C
|
|
|
|
|