from typing import Any, Optional from turtle import shape import nazca as nd import numpy as np import math from .taper import taper_xs2xs from ...routing import Route from ...geometry import * from ....technologies import * from ...geometry import _my_polygon,Conchoid,_my_poly_spiral from scipy import optimize class spiral: """ spiral primitive component. This component builds the spiral layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. shape : str, optional Value for the shape parameter. Default is 'circle'. Dmin : float, optional Value for the Dmin parameter. Default is 50. R_bend : float, optional Radius parameter in microns. Default is 10. Rmin_euler : float, optional Radius parameter in microns. Default is 10. Lmin : float, optional Length parameter in microns. Default is 50. width : float, optional Width parameter in microns. Default is 2. w_port : float, optional Width parameter in microns. Default is 0.45. w_bend_center : float, optional Width parameter in microns. Default is 1. Rmin_bend_center : float, optional Radius parameter in microns. Default is 10. gap : float, optional Spacing or gap parameter in microns. Default is 1. cycles : float, optional Count or repetition parameter. Default is 20. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. layer : str, optional Layer or cross-section name used by the device. Default is None. w_bend_port : Optional[float], optional Width parameter in microns. Default is None. Ltp_port : int, optional Length parameter in microns. Default is 10. res : float, optional Value for the res parameter. Default is 0.5. rib2strip : bool, optional Value for the rib2strip parameter. Default is True. port_angle : float, optional Value for the port_angle parameter. Default is 180. Euler_bend : bool, optional Value for the Euler_bend parameter. Default is False. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. """ def __init__(self, name: str = None, shape: str = 'circle', Dmin: float = 50, R_bend: float = 10, Rmin_euler: float = 10, Lmin: float = 50, width: float = 2, w_port: float = 0.45, ## not used at this moment w_bend_center: float = 1, Rmin_bend_center: float = 10, gap: float = 1, cycles: float = 20, xs: str = 'strip', layer: str = None, w_bend_port: Optional[float]=None, Ltp_port: int = 10, # Tres: int = 256,## resolution for one cycle, these two parameters aborted in 2023.1.4 # Bres: int = 64, ## resolution for bends, these two parameters aborted in 2023.1.4 res : float = 0.5, ## added in 2023.1.4, the length resolution rib2strip: bool=True, port_angle: float = 180, Euler_bend: bool = False, show_pins: bool = False, sharp_patch:bool = True ) -> None: """_summary_ Args: name (str, optional): Name of the internal cell. Defaults to None. shape (str, optional): circular or rectangular. Defaults to 'circle'. Dmin (float, optional): Mimimum internal distance. Defaults to 50. R_bend (float, optional): Radius of the bends. Defaults to 10. Rmin_euler (float, optional): _description_. Defaults to 10. Lmin (float, optional): Minimum length of the inner cycle. Defaults to 50. width (float, optional): _description_. Defaults to 2. w_port (float, optional): _description_. Defaults to 0.45. w_bend_center (float, optional): _description_. Defaults to 1. Rmin_bend_center (float, optional): _description_. Defaults to 10. gap (float, optional): _description_. Defaults to 1. cycles (float, optional): _description_. Defaults to 20. xs (str, optional): _description_. Defaults to 'strip'. layer (str, optional): _description_. Defaults to None. Tres (int, optional): _description_. Defaults to 256. port_angle (float, optional): _description_. Defaults to 90. Euler_bend (bool, optional): _description_. Defaults to 'Euler'. show_pins (bool, optional): _description_. Defaults to True. sharp_patch (bool, optional): _description_. Defaults to True. """ self.Dmin = Dmin self.Lmin = Lmin self.R_bend = R_bend self.shape = shape self.cycles = cycles self.width = width self.w_port = w_port self.gap = gap self.xs = xs self.layer = layer self.name=name if (self.name==None): self.instantiate = False else : self.instantiate = True self.port_angle=port_angle self.w_bend_center= w_bend_center self.Rmin_bend_center= Rmin_bend_center self.Euler_bend= Euler_bend self.Rmin_euler= Rmin_euler self.sharp_patch= sharp_patch self.w_bend_port = w_bend_port self.Ltp_port = Ltp_port self.rib2strip = rib2strip self.res = res self.cell = self.generate_gds(show_pins=show_pins) def __strt_with_taper__(self,width1,width2,xs,length,Ltp=15,Lstart=2): with nd.Cell(instantiate=False) as C: L_mm = length-Ltp*2-Lstart*2 if (L_mm>0): instr_a1 = nd.strt(length=Lstart,width=width1,xs=xs).put(0,0,0) nd.taper(length=Ltp,width1=width1,width2=width2,xs=xs).put() nd.strt(length=L_mm,width=width2,xs=xs).put() nd.taper(length=Ltp,width1=width2,width2=width1,xs=xs).put() instr_b1 = nd.strt(length=Lstart,width=width1,xs=xs).put() else : instr_a1 = nd.strt(length=length/2,width=width1,xs=xs).put(0,0,0) instr_b1 = nd.strt(length=length/2,width=width1,xs=xs).put() nd.Pin(name='a0',pin=instr_a1.pin['a0']).put() nd.Pin(name='b0',pin=instr_b1.pin['b0']).put() return C def generate_gds(self,show_pins): if (self.w_port==None): self.w_port = self.width if (self.w_bend_port==None): self.w_bend_port = self.width """ Circular Spiral """ if (self.shape=='circle'): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: if (self.layer==None): pitch = (self.width+self.gap)*2 ## a bi-twsited circle Dmin = self.Dmin R0 = Dmin/2 kR = pitch/(np.pi*2) K_att = (np.power(R0,2)+2*np.power(kR,2))/np.power((np.power(R0,2)+np.power(kR,2)),1.5) R_att = 1/K_att for layers,growx,growy,acc in nd.layeriter(xs=self.xs): (a1,b1), (a2,b2),c1,c2 = growx """ Generating Central Euler bend """ if (self.Euler_bend == True): """ Modified in 2023.07.31, Clothoid simplified into the simple attachment of Clothoid and Conchoid No Angle compensation were build due to no significant improvement """ # spr_bend = Clothoid(xs=self.xs,R=[R_att/1.5,R_att/2.4,R_att], spr_bend = Clothoid(xs=self.xs,R=[R_att,R_att/2.5001,R_att], w=[self.w_bend_center,self.w_bend_port],A=[0,90,180],dL_wg=self.res,width_type='dual_sine') else : """ Genreating Circular bend for center """ spr_bend = Clothoid(xs=self.xs,R=[R0/2,R0/2,R0/2], w=[self.w_bend_port,self.w_bend_port],A=[0,90,180],dL_wg=self.res,width_type='dual_sine') w_cur = self.width*(a1-a2)+(b1-b2) if (w_cur, Dmin too small") D_port = self.Dmin - bend_sz[1]*2 with nd.Cell(instantiate=False) as wg_mid_cell: wg = self.__strt_with_taper__(length=self.Lmin-bend_sz[0]*2,width2=self.width,width1=bend_cell.pin['a0'].width,xs=self.xs,Ltp=self.Ltp_port).put(-self.Lmin/2+bend_sz[0]*2,0,0) bend_cell.put(wg.pin['a0'],flip=1) self.__strt_with_taper__(length=D_port,width2=self.width,width1=bend_cell.pin['a0'].width,xs=self.xs,Ltp=self.Ltp_port).put() bd = bend_cell.put(flip=0) nd.Pin(name='a0',pin=bd.pin['b0']).put() nd.Pin(name='b0',pin=wg.pin['b0']).put() wg_mid = wg_mid_cell.put(-self.Lmin/2,self.Dmin/2,0) pin_U_pre = wg_mid.pin['b0'] pin_D_pre = wg_mid.pin['a0'] bend_U = bend_cell.put(pin_U_pre) wg_U = self.__strt_with_taper__(length=pitch/2+D_port, width2=self.width,width1=bend_U.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_U.pin['b0']) bend_U2 = bend_cell.put(wg_U.pin['b0']) wg_U = self.__strt_with_taper__(length=self.Lmin+pitch/2, width2=self.width,width1=bend_U2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_U2.pin['b0']) pin_U_pre = wg_U.pin['b0'] bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=pitch/2+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D.pin['b0']) bend_D2 = bend_cell.put(wg_D.pin['b0']) wg_D = self.__strt_with_taper__(length=self.Lmin+pitch/2, width2=self.width,width1=bend_D2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D2.pin['b0']) pin_D_pre = wg_D.pin['b0'] L = self.Lmin*3+pitch+L_bend _cycle_ = 1 for _cycle_ in range(1,self.cycles-1): bend_U = bend_cell.put(pin_U_pre) wg_U = self.__strt_with_taper__(length=_cycle_*pitch+pitch/2+D_port, width2=self.width,width1=bend_U.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_U.pin['b0']) bend_U2 = bend_cell.put(wg_U.pin['b0']) wg_U = self.__strt_with_taper__(length=self.Lmin+_cycle_*pitch+pitch/2, width2=self.width,width1=bend_U2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_U2.pin['b0']) pin_U_pre = wg_U.pin['b0'] bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=_cycle_*pitch+pitch/2+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D.pin['b0']) bend_D2 = bend_cell.put(wg_D.pin['b0']) wg_D = self.__strt_with_taper__(length=self.Lmin+_cycle_*pitch+pitch/2, width2=self.width,width1=bend_D2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D2.pin['b0']) pin_D_pre = wg_D.pin['b0'] L = L+(_cycle_*pitch-pitch/2+D_port+self.Lmin+_cycle_*pitch+pitch/2)*2+L_bend*4 """ 2023.03.19 REVISED, the spiral will end at the same Y level with begining """ bend_D = bend_cell.put(pin_D_pre) ## adding bend connection to outside if(self.port_angle==90 or self.port_angle==270 or self.port_angle==-90): wg_D = self.__strt_with_taper__(length=(_cycle_+1)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D.pin['b0']) pin_D_pre = wg_D.pin['b0'] elif(self.port_angle==180) : wg_D = self.__strt_with_taper__(length=(_cycle_+1)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D.pin['b0']) bend_D2 = bend_cell.put(wg_D.pin['b0'],flip=1) pin_D_pre = bend_D2.pin['b0'] elif(self.port_angle==0 or self.port_angle==360) : wg_D = self.__strt_with_taper__(length=(_cycle_+1.5)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp_port).put(bend_D.pin['b0']) bend_D2 = bend_cell.put(wg_D.pin['b0'],flip=0) pin_D_pre = bend_D2.pin['b0'] if (self.rib2strip): taper = taper_xs2xs(xs_1=self.xs,xs_2='strip',w_1=self.w_bend_port,w_2=self.w_bend_port,L_taper=10,L_port=bend_sz[0]).cell.put(pin_D_pre) pin_D_pre = taper.pin['b0'] taper = taper_xs2xs(xs_1=self.xs,xs_2='strip',w_1=self.w_bend_port,w_2=self.w_bend_port,L_taper=10,L_port=bend_sz[0]).cell.put(pin_U_pre) pin_U_pre = taper.pin['b0'] """ 2023.03.19 REVISED, the begining will not be influenced by the rotation angle """ if (self.w_port !=self.width) : if (self.rib2strip): nd.taper(length=self.Ltp_port,width1=self.width,width2=self.w_port,xs='strip').put(pin_D_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',width=self.w_bend_port).put() nd.Pin(name='opt_b1',width=self.w_bend_port,type="optical:").put() nd.taper(length=self.Ltp_port,width1=self.width,width2=self.w_port,xs='strip').put(pin_U_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',width=self.w_bend_port).put() nd.Pin(name='opt_a1',width=self.w_bend_port,type="optical:").put() else : nd.taper(length=self.Ltp_port,width1=self.width,width2=self.w_port,xs=self.xs).put(pin_D_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',width=self.w_bend_port).put() nd.Pin(name='opt_b1',width=self.w_bend_port,type="optical:").put() nd.taper(length=self.Ltp_port,width1=self.width,width2=self.w_port,xs=self.xs).put(pin_U_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',width=self.w_bend_port).put() nd.Pin(name='opt_a1',width=self.w_bend_port,type="optical:").put() else: ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',width=self.w_bend_port).put(pin_D_pre) nd.Pin(name='opt_b1',width=self.w_bend_port,type="optical:").put(pin_D_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',width=self.w_bend_port).put(pin_U_pre) nd.Pin(name='opt_a1',width=self.w_bend_port,type="optical:").put(pin_U_pre) self.L = L ## revise 2022.08.18 if (show_pins): nd.put_stub() return C class spiral_rectangle: """ spiral rectangle primitive component. This component builds the spiral rectangle layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. Dmin : float, optional Value for the Dmin parameter. Default is 50. Rmax_bend : float, optional Radius parameter in microns. Default is 10. Rmin_bend : float, optional Radius parameter in microns. Default is 10. wmin_bend : float, optional Value for the wmin_bend parameter. Default is 10. Lmin : float, optional Length parameter in microns. Default is 50. width : float, optional Width parameter in microns. Default is 2. w_port : float, optional Width parameter in microns. Default is None. gap : float, optional Spacing or gap parameter in microns. Default is 1. cycles : float, optional Count or repetition parameter. Default is 20. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. layer : str, optional Layer or cross-section name used by the device. Default is None. w_bend_port : Optional[float], optional Width parameter in microns. Default is None. Lport : int, optional Length parameter in microns. Default is 10. Ltp : int, optional Length parameter in microns. Default is 10. res : float, optional Value for the res parameter. Default is 0.5. cell_xs_transition : Any, optional Cell or component dependency used by this device. Default is None. port_angle : float, optional Value for the port_angle parameter. Default is 180. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. in_out_align : bool, optional Value for the in_out_align parameter. Default is True. Lpatch : float, optional Length parameter in microns. Default is 0.05. """ def __init__(self, name: str = None, Dmin: float = 50, Rmax_bend: float = 10, Rmin_bend: float = 10, wmin_bend: float = 10, Lmin: float = 50, width: float = 2, w_port: float = None, ## not used at this moment gap: float = 1, cycles: float = 20, xs: str = 'strip', layer: str = None, w_bend_port: Optional[float]=None, Lport: int = 10, Ltp: int = 10, # Tres: int = 256,## resolution for one cycle, these two parameters aborted in 2023.1.4 # Bres: int = 64, ## resolution for bends, these two parameters aborted in 2023.1.4 res : float = 0.5, ## added in 2023.1.4, the length resolution cell_xs_transition: Any=None, port_angle: float = 180, show_pins: bool = False, sharp_patch:bool = True, in_out_align: bool = True, Lpatch: float = 0.05, ) -> None: """_summary_ Args: name (str, optional): Name of the internal cell. Defaults to None. shape (str, optional): circular or rectangular. Defaults to 'circle'. Dmin (float, optional): Mimimum internal distance. Defaults to 50. R_bend (float, optional): Radius of the bends. Defaults to 10. Rmin_euler (float, optional): _description_. Defaults to 10. Lmin (float, optional): Minimum length of the inner cycle. Defaults to 50. width (float, optional): _description_. Defaults to 2. w_port (float, optional): _description_. Defaults to 0.45. w_bend_center (float, optional): _description_. Defaults to 1. Rmin_bend_center (float, optional): _description_. Defaults to 10. gap (float, optional): _description_. Defaults to 1. cycles (float, optional): _description_. Defaults to 20. xs (str, optional): _description_. Defaults to 'strip'. layer (str, optional): _description_. Defaults to None. Tres (int, optional): _description_. Defaults to 256. port_angle (float, optional): _description_. Defaults to 90. Euler_bend (bool, optional): _description_. Defaults to 'Euler'. show_pins (bool, optional): _description_. Defaults to True. sharp_patch (bool, optional): _description_. Defaults to True. """ self.Dmin = Dmin self.Lmin = Lmin self.Rmax_bend = Rmax_bend self.Rmin_bend = Rmin_bend self.wmin_bend = wmin_bend self.cycles = cycles self.width = width self.w_port = w_port self.gap = gap self.xs = xs self.layer = layer self.Ltp = Ltp self.Lpatch = Lpatch self.name=name if (self.name==None): self.instantiate = False else : self.instantiate = True self.port_angle=port_angle self.sharp_patch= sharp_patch self.w_bend_port = w_bend_port self.Lport = Lport if (hasattr(cell_xs_transition,'cell')): self.cell_xs_transition = cell_xs_transition.cell elif (isinstance(cell_xs_transition,nd.Cell)): self.cell_xs_transition = cell_xs_transition else : self.cell_xs_transition = None self.res = res self.in_out_align = in_out_align self.cell = self.generate_gds(show_pins=show_pins) def __strt_with_taper__(self,width1,width2,xs,length,Ltp=15,Lstart=2): with nd.Cell(instantiate=False) as C: L_mm = length-Ltp*2-Lstart*2 if (L_mm>0): instr_a1 = nd.strt(length=Lstart,width=width1,xs=xs).put(0,0,0) nd.taper(length=Ltp,width1=width1,width2=width2,xs=xs).put() nd.strt(length=L_mm,width=width2,xs=xs).put() nd.taper(length=Ltp,width1=width2,width2=width1,xs=xs).put() instr_b1 = nd.strt(length=Lstart,width=width1,xs=xs).put() else : instr_a1 = nd.strt(length=length/2,width=width1,xs=xs).put(0,0,0) instr_b1 = nd.strt(length=length/2,width=width1,xs=xs).put() nd.Pin(name='a0',pin=instr_a1.pin['a0']).put() nd.Pin(name='b0',pin=instr_b1.pin['b0']).put() return C def generate_gds(self,show_pins): if (self.w_port==None): self.w_port = self.width if (self.w_bend_port==None): self.w_bend_port = self.width """ Rectangluar Spiral """ with nd.Cell(instantiate=self.instantiate,name=self.name) as C: pitch = (self.width+self.gap)*2 ## a bi-twsited circle bend_rt = Clothoid(xs=self.xs,R=[self.Rmax_bend,self.Rmin_bend,self.Rmax_bend], w=[self.w_bend_port,self.wmin_bend,self.w_bend_port], A=[0,45,90], dL_wg=self.res, end_patch=False, sharp_patch=self.sharp_patch) bend_rt_anti = Clothoid(xs=self.xs,R=[self.Rmax_bend,self.Rmin_bend,self.Rmax_bend], w=[self.w_bend_port,self.wmin_bend,self.w_bend_port], A=[0,-45,-90], dL_wg=self.res, end_patch=False, sharp_patch=self.sharp_patch) """ Adding small patch to the bending connection """ with nd.Cell(instantiate=False) as bend_cell: inst = bend_rt.cell.put(0,0,0) nd.strt(xs=self.xs,width=self.w_bend_port,length=self.Lpatch).put(inst.pin['a0'].move(-self.Lpatch/2,0,0),flip=1) nd.strt(xs=self.xs,width=self.w_bend_port,length=self.Lpatch).put(inst.pin['b0'].move(-self.Lpatch/2,0,0)) inst.raise_pins() with nd.Cell(instantiate=False) as bend_cell_anti: inst = bend_rt_anti.cell.put(0,0,0) nd.strt(xs=self.xs,width=self.w_bend_port,length=self.Lpatch).put(inst.pin['a0'].move(-self.Lpatch/2,0,0),flip=1) nd.strt(xs=self.xs,width=self.w_bend_port,length=self.Lpatch).put(inst.pin['b0'].move(-self.Lpatch/2,0,0)) inst.raise_pins() # bend_cell = bend_rt.cell # bend_cell_anti = bend_rt_anti.cell bend_sz = bend_rt.sz L_bend = bend_rt.L0 self.bend_cell = bend_cell if (self.Dmin < bend_sz[1]*2): self.Dmin = bend_sz[1]*2 print("WARNING: In , Dmin too small") D_port = self.Dmin - bend_sz[1]*2 with nd.Cell(instantiate=True,name="wg_mid_cell"+self.name) as wg_mid_cell: wg = self.__strt_with_taper__(length=self.Lmin-bend_sz[0]*2-bend_sz[0],width2=self.width,width1=bend_cell.pin['a0']. width,xs=self.xs,Ltp=self.Ltp).put(-self.Lmin/2+bend_sz[0]*2+bend_sz[0],0,0,flip=1) bend_cell_anti.put(wg.pin['a0'],flip=0) self.__strt_with_taper__(length=D_port,width2=self.width,width1=bend_cell.pin['a0'].width,xs=self.xs,Ltp=self.Ltp).put() # bd = bend_cell.put(flip=0) bd = bend_cell.put() bd = self.__strt_with_taper__(length=bend_sz[0], width2=bend_cell.pin['a0'].width, width1=bend_cell.pin['a0'].width,xs=self.xs,Ltp=self.Ltp).put() nd.Pin(name='a0',pin=bd.pin['b0']).put() nd.Pin(name='b0',pin=wg.pin['b0']).put() wg_mid = wg_mid_cell.put(-self.Lmin/2,self.Dmin/2,0) L = self.Lmin-bend_sz[0]*2 + D_port + L_bend*2 pin_U_pre = wg_mid.pin['b0'] pin_D_pre = wg_mid.pin['a0'] bend_U = bend_cell_anti.put(pin_U_pre,flip=1) wg_U = self.__strt_with_taper__(length=pitch/2+D_port, width2=self.width,width1=bend_U.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_U.pin['b0'],flip=1) L = L + pitch/2+D_port + L_bend bend_U2 = bend_cell_anti.put(wg_U.pin['b0'],flip=1) wg_U = self.__strt_with_taper__(length=self.Lmin+pitch/2, width2=self.width,width1=bend_U2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_U2.pin['b0'],flip=1) pin_U_pre = wg_U.pin['b0'] L = L + self.Lmin+pitch/2 + L_bend bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=pitch/2+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D.pin['b0']) L = L + pitch/2+D_port + L_bend bend_D2 = bend_cell.put(wg_D.pin['b0']) wg_D = self.__strt_with_taper__(length=self.Lmin+pitch/2, width2=self.width,width1=bend_D2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D2.pin['b0']) pin_D_pre = wg_D.pin['b0'] L = L + self.Lmin+pitch/2 + L_bend # L = self.Lmin*3+pitch+L_bend _cycle_ = 1 for _cycle_ in range(1,self.cycles-1): bend_U = bend_cell_anti.put(pin_U_pre,flip=1) wg_U = self.__strt_with_taper__(length=_cycle_*pitch+pitch/2+D_port, width2=self.width,width1=bend_U.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_U.pin['b0'],flip=1) L = L+_cycle_*pitch+pitch/2+D_port+L_bend bend_U2 = bend_cell_anti.put(wg_U.pin['b0'],flip=1) wg_U = self.__strt_with_taper__(length=self.Lmin+_cycle_*pitch+pitch/2, width2=self.width,width1=bend_U2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_U2.pin['b0'],flip=1) pin_U_pre = wg_U.pin['b0'] L = L+self.Lmin+_cycle_*pitch+pitch/2+D_port+L_bend bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=_cycle_*pitch+pitch/2+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D.pin['b0']) L = L+_cycle_*pitch+pitch/2+D_port+L_bend bend_D2 = bend_cell.put(wg_D.pin['b0']) wg_D = self.__strt_with_taper__(length=self.Lmin+_cycle_*pitch+pitch/2, width2=self.width,width1=bend_D2.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D2.pin['b0']) pin_D_pre = wg_D.pin['b0'] L = L+self.Lmin+_cycle_*pitch+pitch/2+L_bend """ 2023.03.19 REVISED, the spiral will end at the same Y level with begining """ ## adding bend connection to outside if(self.port_angle==90 or self.port_angle==270 or self.port_angle==-90): bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=(_cycle_+1)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D.pin['b0']) pin_D_pre = wg_D.pin['b0'] L = L+(_cycle_+1)*pitch+D_port+L_bend elif(self.port_angle==180 and self.in_out_align) : bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=(_cycle_+1)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D.pin['b0']) bend_D2 = bend_cell_anti.put(wg_D.pin['b0']) pin_D_pre = bend_D2.pin['b0'] L = L+(_cycle_+1)*pitch+D_port+L_bend*2 elif(self.port_angle==180 and self.in_out_align==False): pass elif(self.port_angle==0 or self.port_angle==360) : bend_D = bend_cell.put(pin_D_pre) wg_D = self.__strt_with_taper__(length=(_cycle_+1.5)*pitch+D_port, width2=self.width,width1=bend_D.pin['b0'].width,xs=self.xs,Ltp=self.Ltp).put(bend_D.pin['b0']) bend_D2 = bend_cell.put(wg_D.pin['b0'],flip=0) pin_D_pre = bend_D2.pin['b0'] L = L+(_cycle_+1.5)*pitch+D_port+L_bend*2 """ 2023.03.19 REVISED, the begining will not be influenced by the rotation angle """ """ 2023.09.18 REVISED, the xsection output can be defined with the transition area """ if (self.w_port !=self.width) : pin_D_pre = nd.taper(length=self.Lport,width1=pin_D_pre.width,width2=self.w_port,xs=self.xs).put(pin_D_pre) pin_U_pre = nd.taper(length=self.Lport,width1=pin_U_pre.width,width2=self.w_port,xs=self.xs).put(pin_U_pre) """ 2023.09.18 REVISED, the transition area are only added at w_port area """ ### Because putting transisiton in multimode area are dengerous if (self.cell_xs_transition != None): taper = self.cell_xs_transition.put(pin_D_pre) pin_D_pre = taper.pin['b0'] taper = self.cell_xs_transition.put(pin_U_pre) pin_U_pre = taper.pin['b0'] ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',width=self.w_port).put(pin_D_pre) nd.Pin(name='opt_b1',width=self.w_port,type="optical:").put(pin_D_pre) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',width=self.w_port).put(pin_U_pre) nd.Pin(name='opt_a1',width=self.w_port,type="optical:").put(pin_U_pre) self.L = L ## revise 2022.08.18 if (show_pins): nd.put_stub() return C class Spiral_Rect_STD(spiral_rectangle): """ Spiral Rect STD primitive component. This component builds the Spiral Rect STD layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. Dmin : float, optional Value for the Dmin parameter. Default is 50. R_bend : float, optional Radius parameter in microns. Default is 10. Lmin : float, optional Length parameter in microns. Default is 50. width : float, optional Width parameter in microns. Default is 2. w_port : float, optional Width parameter in microns. Default is 0.45. gap : float, optional Spacing or gap parameter in microns. Default is 1. cycles : float, optional Count or repetition parameter. Default is 20. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. layer : str, optional Layer or cross-section name used by the device. Default is None. Lport : int, optional Length parameter in microns. Default is 10. in_out_align : bool, optional Value for the in_out_align parameter. Default is True. res : float, optional Value for the res parameter. Default is 0.5. cell_xs_transition : Any, optional Cell or component dependency used by this device. Default is None. port_angle : float, optional Value for the port_angle parameter. Default is 180. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. """ def __init__(self, name: str = None, Dmin: float = 50, R_bend: float = 10, Lmin: float = 50, width: float = 2, w_port: float = 0.45, gap: float = 1, cycles: float = 20, xs: str = 'strip', layer: str = None, Lport: int=10, in_out_align: bool = True, res: float = 0.5, cell_xs_transition: Any=None, port_angle: float = 180, show_pins: bool = False, sharp_patch: bool = True) -> None: super().__init__(name=name, Dmin=Dmin, Rmax_bend=R_bend, Rmin_bend=R_bend, wmin_bend=width, Lmin=Lmin, width=width, w_port=w_port, gap=gap, cycles=cycles, xs=xs, layer=layer, w_bend_port=None, Lport=Lport, res=res, cell_xs_transition=cell_xs_transition, port_angle=port_angle, show_pins=show_pins, sharp_patch=sharp_patch, in_out_align=in_out_align) class spiral_circle: """ spiral circle primitive component. This component builds the spiral circle layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. Dmin : float, optional Value for the Dmin parameter. Default is 50. width : float, optional Width parameter in microns. Default is 2. w_port : float, optional Width parameter in microns. Default is 0.45. w_bend_center : float, optional Width parameter in microns. Default is 1. gap : float, optional Spacing or gap parameter in microns. Default is 1. cycles : float, optional Count or repetition parameter. Default is 20. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. layer : str, optional Layer or cross-section name used by the device. Default is None. Lport : int, optional Length parameter in microns. Default is 10. res : float, optional Value for the res parameter. Default is 0.5. rib2strip : bool, optional Value for the rib2strip parameter. Default is True. port_angle : float, optional Value for the port_angle parameter. Default is 180. Euler_Sbend : bool, optional Value for the Euler_Sbend parameter. Default is False. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. strict_condition : bool, optional Value for the strict_condition parameter. Default is False. R_ratio_mamnual : Any, optional Radius parameter in microns. Default is None. """ def __init__(self, name: str = None, Dmin: float = 50, width: float = 2, w_port: float = 0.45, ## not used at this moment w_bend_center: float = 1, gap: float = 1, cycles: float = 20, xs: str = 'strip', layer: str = None, Lport: int = 10, res : float = 0.5, ## added in 2023.1.4, the length resolution rib2strip: bool=True, port_angle: float = 180, Euler_Sbend: bool = False, show_pins: bool = False, sharp_patch:bool = True, strict_condition: bool = False, R_ratio_mamnual: Any = None, ) -> None: """_summary_ Args: name (str, optional): Name of the internal cell. Defaults to None. Dmin (float, optional): Mimimum internal distance. Defaults to 50. width (float, optional): _description_. Defaults to 2. w_port (float, optional): _description_. Defaults to 0.45. w_bend_center (float, optional): _description_. Defaults to 1. gap (float, optional): _description_. Defaults to 1. cycles (float, optional): _description_. Defaults to 20. xs (str, optional): _description_. Defaults to 'strip'. layer (str, optional): _description_. Defaults to None. Tres (int, optional): _description_. Defaults to 256. port_angle (float, optional): _description_. Defaults to 90. Euler_bend (bool, optional): _description_. Defaults to 'Euler'. show_pins (bool, optional): _description_. Defaults to True. sharp_patch (bool, optional): _description_. Defaults to True. """ self.Dmin = Dmin self.cycles = cycles self.width = width self.w_port = w_port self.gap = gap self.xs = xs self.layer = layer self.name=name if (self.name==None): self.instantiate = False else : self.instantiate = True self.port_angle=port_angle self.w_bend_center= w_bend_center self.Euler_Sbend= Euler_Sbend self.sharp_patch= sharp_patch self.Lport = Lport self.rib2strip = rib2strip self.res = res self.strict_condition = strict_condition self.R_ratio_mamnual = R_ratio_mamnual self.cell = self.generate_gds(show_pins=show_pins) """ Optimizing the bend radius for the minimum and central """ def opt_euler(self,R,R0): (R1,R2) = R R1 = R1 R2 = R2 [A,LA] = _my_poly_spiral(r=[R0/R1,R0/R2],theta=[0,90],res=0.01,R_max=1000,order=1) [B,LA] = _my_poly_spiral(r=[R0/R2,R0],theta=[90,180],res=0.01,R_max=1000,order=1) x_final = A[-1,0]+B[-1,0] y_final = A[-1,1]+B[-1,1] D_final = np.sqrt(x_final**2 + y_final**2) A_final = abs(np.arctan(x_final/y_final))*180/pi # Dmis = abs(D_final-D0) # Amis = abs(abs(A_final)-abs(A0)) return [D_final,A_final] def generate_gds(self,show_pins): if (self.w_port==None): self.w_port = self.width with nd.Cell(instantiate=self.instantiate,name=self.name) as C: if (self.layer==None): pitch = (self.width+self.gap)*2 ## a bi-twsited circle Dmin = self.Dmin R0 = Dmin/2 kR = pitch/(np.pi*2) K_att = (np.power(R0,2)+2*np.power(kR,2))/np.power((np.power(R0,2)+np.power(kR,2)),1.5) R_att = 1/K_att Atilt_con = abs(np.arctan(kR/R0))*180/pi ## the initial tilt norm of the conchoid """ Calculating the spiral with internal Sbend """ if (self.Euler_Sbend): if (self.R_ratio_mamnual==None): n_swp = 201 R1_range = np.linspace(0.9,1.4,n_swp) R2_range = np.linspace(2.4,2.6,n_swp) mis = np.zeros((n_swp,n_swp)) for ix in range(0,len(R1_range)): for iy in range(0,len(R2_range)): temp = self.opt_euler((R1_range[ix],R2_range[iy]),R0=R_att) mis[ix,iy] = abs(temp[0]-R0)+abs(temp[1]-abs(Atilt_con))*10 idx = mis.argmin() ix = idx//n_swp iy = np.mod(idx,n_swp) Rc_ratio = R1_range[ix] Rm_ratio = R2_range[iy] final_mismatch = self.opt_euler((Rc_ratio,Rm_ratio),R_att) print("====================================================") print("Optimized D/A = %.3f -- %.3f" % (final_mismatch[0],final_mismatch[1])) print("Target D/A = %.3f -- %.3f" % (R0,Atilt_con)) print("Optimized para = %.3f -- %.3f" % (Rc_ratio,Rm_ratio)) else : Rc_ratio = self.R_ratio_mamnual[0] Rm_ratio = self.R_ratio_mamnual[1] print("====================================================") print("manual para = %.3f -- %.3f" % (Rc_ratio,Rm_ratio)) for layers,growx,growy,acc in nd.layeriter(xs=self.xs): (a1,b1), (a2,b2),c1,c2 = growx """ Generating Central Euler bend """ if (self.Euler_Sbend == True): spr_bend = Clothoid(xs=self.xs,R=[R_att/Rc_ratio,R_att/Rm_ratio,R_att], w=[self.w_bend_center,self.width],A=[0,90,180],dL_wg=self.res, width_type='dual_sine',dL_cal=0.01) else : """ Genreating Circular bend for center """ spr_bend = Clothoid(xs=self.xs,R=[R0/2,R0/2,R0/2], w=[self.width,self.width],A=[0,90,180],dL_wg=self.res, width_type='dual_sine',dL_cal=0.01) w_cur = self.width*(a1-a2)+(b1-b2) if (w_cur None: super().__init__(name, Dmin, width, w_port, w_bend_center, gap, cycles, xs, layer, Lport, res, rib2strip, port_angle, Euler_Sbend=True, show_pins=show_pins, sharp_patch=sharp_patch, strict_condition=strict_condition) class Spiral_Cicle_STD(spiral_circle): """ Spiral Cicle STD primitive component. This component builds the Spiral Cicle STD layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. Dmin : float, optional Value for the Dmin parameter. Default is 50. width : float, optional Width parameter in microns. Default is 2. w_port : float, optional Width parameter in microns. Default is 0.45. gap : float, optional Spacing or gap parameter in microns. Default is 1. cycles : float, optional Count or repetition parameter. Default is 20. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. layer : str, optional Layer or cross-section name used by the device. Default is None. Lport : int, optional Length parameter in microns. Default is 10. res : float, optional Value for the res parameter. Default is 0.5. rib2strip : bool, optional Value for the rib2strip parameter. Default is True. port_angle : float, optional Value for the port_angle parameter. Default is 180. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. strict_condition : bool, optional Value for the strict_condition parameter. Default is False. """ def __init__(self, name: str = None, Dmin: float = 50, width: float = 2, w_port: float = 0.45, gap: float = 1, cycles: float = 20, xs: str = 'strip', layer: str = None, Lport: int=10, res: float = 0.5, rib2strip: bool=True, port_angle: float = 180, show_pins: bool = False, sharp_patch: bool = True, strict_condition: bool=False) -> None: super().__init__(name, Dmin, width, w_port, w_bend_center=width, gap=gap, cycles=cycles, xs=xs, layer=layer, Lport=Lport, res=res, rib2strip=rib2strip, port_angle=port_angle, Euler_Sbend=False, show_pins=show_pins, sharp_patch=sharp_patch, strict_condition=strict_condition)