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 No xsection ==",xs_1,"== were found") try: nd.get_xsection(xs_2) except: raise Exception("ERROR: in 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 , xs=",xs," is not defined in the tapeout") if (len(w)!=len(Lt_rib)+1): print("WARNING: In , rib area width section do not match with taper section") return 0 if (len(w)!=len(Lt_rib)+1): print("WARNING: In , 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