Files

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