from typing import Any, Optional import nazca as nd import numpy as np from .couplers import ring_bus_wg from .taper import taper_xs2xs from ...geometry import * # import nazca.interconnects as IC from ...routing import Route from ...basic import __cell_arg__ # class Route(IC.Interconnect): # pass # from ...routing import * """ 2023.03.19 REVISED """ """ NOTE: n_points will not be used in any future class, res will be the accurancy parameter """ class AED_ring : """ AED ring primitive component. This component builds the AED ring layout cell. Parameters ---------- name : Optional[str], optional Unique identifier for the device cell. Default is None. ORx : float, optional Value for the ORx parameter. Default is 10. ORy : float, optional Value for the ORy parameter. Default is 10. IRx : float, optional Value for the IRx parameter. Default is 10 - 0.45. IRy : float, optional Value for the IRy parameter. Default is 10 - 0.65. gap1 : float, optional Spacing or gap parameter in microns. Default is 0.2. gap2 : float, optional Spacing or gap parameter in microns. Default is 0.2. w1_bus : float, optional Value for the w1_bus parameter. Default is 0.45. w2_bus : float, optional Value for the w2_bus parameter. Default is 0.45. R1_cp : Any, optional Radius parameter in microns. Default is None. R2_cp : Any, optional Radius parameter in microns. Default is None. A1_cp : int, optional Angle parameter in degrees. Default is 0. A2_cp : int, optional Angle parameter in degrees. Default is 0. offset_X : float, optional Value for the offset_X parameter. Default is 0. offset_Y : float, optional Value for the offset_Y parameter. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R1_att : float, optional Radius parameter in microns. Default is 20. R2_att : float, optional Radius parameter in microns. Default is 20. R2_att_min : float, optional Radius parameter in microns. Default is 10. R1_att_min : float, optional Radius parameter in microns. Default is 10. A1_att : float, optional Angle parameter in degrees. Default is 30. A2_att : float, optional Angle parameter in degrees. Default is 20. Ltp_bus : int, optional Length parameter in microns. Default is 10. dL_p2p : Optional[float], optional Value for the dL_p2p parameter. Default is None. L_tilt : int, optional Length parameter in microns. Default is 10. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. cell_xs_transition : Any, optional Cell or component dependency used by this device. Default is None. Euler_trasition : bool, optional Value for the Euler_trasition parameter. Default is False. res : float, optional Value for the res parameter. Default is 0.01. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. """ def __init__(self, name: Optional[str]=None, ORx: float = 10, ORy: float = 10, IRx: float = 10-0.45, IRy: float = 10-0.65, gap1: float = 0.2, gap2: float = 0.2, w1_bus: float = 0.45, w2_bus: float = 0.45, R1_cp: Any = None, R2_cp: Any = None, A1_cp: int = 0, A2_cp: int = 0, offset_X: float=0, offset_Y: float=0, w_wg: float = 0.45, R1_att: float = 20, R2_att: float = 20, R2_att_min: float= 10, R1_att_min: float = 10, A1_att: float = 30, A2_att: float = 20, Ltp_bus: int = 10, dL_p2p: Optional[float] = None, L_tilt: int = 10, xs: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, Euler_trasition: bool = False, res: float = 0.01, ## default to 1nm accurancy show_pins: bool=False) -> None: self.name = name if (self.name==None): self.instantiate = False else : self.instantiate = True self.ORx = ORx self.ORy = ORy self.IRx = IRx self.IRy = IRy self.gap1 = gap1 self.gap2 = gap2 self.w1_bus = w1_bus self.w2_bus = w2_bus self.R1_cp = R1_cp self.R2_cp = R2_cp self.A1_cp = A1_cp self.A2_cp = A2_cp self.offset_X = offset_X self.offset_Y = offset_Y self.L_tilt = L_tilt self.w_wg = w_wg self.R1_att = R1_att self.R1_att_min = R1_att_min self.A1_att = A1_att self.R2_att = R2_att self.R2_att_min = R2_att_min self.A2_att = A2_att self.Ltp_bus = Ltp_bus self.dL_p2p = dL_p2p self.xs = xs self.sharp_patch = sharp_patch self.show_pins = show_pins self.cell_xs_transition = cell_xs_transition self.Euler_trasition = Euler_trasition # self.n_points = n_points self.res = res self.cell = self.generate_pic_gds(sharp_patch=sharp_patch,show_pins=show_pins) self.pic_cell = self.cell def generate_pic_gds(self,sharp_patch,show_pins): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: Rb = min([self.ORx+self.IRx,self.ORy+self.IRy])/2 Ra = max([self.ORx+self.IRx,self.ORy+self.IRy])/2 _L_perimeter_ = 2*pi*Rb + 4*(Ra-Rb) """ Placing the central ring """ Elipse_dual(xs=self.xs,ORy=self.ORy,ORx=self.ORx,IRx=self.IRx,IRy=self.IRy, offset_X=self.offset_X,offset_Y=self.offset_Y,sharp_patch=sharp_patch, # n_points=self.n_points, res=self.res).cell.put(0,0,0) """ FATAL ERROR: REVISED IN 2022.09.19 """ if (self.R1_cp == None): # self.R1_cp = self.r_ring+self.gap1+self.w1_bus/1+self.w_ring/2 self.R1_cp = self.ORy+self.gap1+self.w1_bus/2 if (self.xs!='strip'): end_patch = True else : end_patch = False bus_class = ring_bus_wg(xs=self.xs, R_cp=self.R1_cp, w_bus=self.w1_bus,bend_DC=True, w_wg=self.w_wg, dAc=self.A1_cp, euler_anti_bend=self.Euler_trasition,euler_transistion=self.Euler_trasition, R_max_trans=self.R1_att,dA_trans=self.A1_att,dL_trans=self.L_tilt, R_max_anti=self.R1_att,R_min_anti=self.R1_att_min, sharp_patch=self.sharp_patch,show_pins=show_pins, wg_Ltp=self.Ltp_bus,w_trans=self.w1_bus, dL_p2p=self.dL_p2p, res=self.res, ## Added in 2023.1.3, to simplfy the points in coupler end_patch=end_patch # ).cell.put(0,-(self.r_ring+self.gap1+self.w1_bus/1+self.w_ring/2)) ) bus = bus_class.cell self.bus = bus_class self.cell_bus = bus bus = bus.put(0,-(self.ORy+self.gap1+self.w1_bus/2)) """ xs transition refreshed in 2023.05.16 """ if (self.cell_xs_transition!=None): if (isinstance(self.cell_xs_transition,nd.Cell)): cell_trans = self.cell_xs_transition elif(hasattr(self.cell_xs_transition,'cell')): cell_trans = self.cell_xs_transition.cell ## revised in 2026.06.07 by Qin Yue # legacy: temp = cell_trans.put(bus.pin['a1']) temp = cell_trans.put(bus.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=temp.pin['b0']).put() nd.Pin(name='opt_a1',pin=temp.pin['b0'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = cell_trans.put(bus.pin['b1']) temp = cell_trans.put(bus.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=temp.pin['b0']).put() nd.Pin(name='opt_b1',pin=temp.pin['b0'],type="optical:").put() else: ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=bus.pin['a1']).put() nd.Pin(name='opt_a1',pin=bus.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=bus.pin['b1']).put() nd.Pin(name='opt_b1',pin=bus.pin['opt_b1'],type="optical:").put() if show_pins: nd.put_stub() ## The upper coupling bus ## if self.w2_bus >0: if (self.R2_cp == None): self.R2_cp = self.ORy+self.gap2+self.w2_bus/2 bus = ring_bus_wg(xs=self.xs, R_cp=self.R2_cp, w_bus=self.w2_bus,bend_DC=True, w_wg=self.w_wg, dAc=self.A2_cp, euler_anti_bend=self.Euler_trasition,euler_transistion=self.Euler_trasition, R_max_trans=self.R2_att,dA_trans=self.A2_att,dL_trans=self.L_tilt, R_max_anti=self.R2_att,R_min_anti=self.R2_att_min, sharp_patch=self.sharp_patch,show_pins=show_pins, wg_Ltp=self.Ltp_bus,w_trans=self.w2_bus, dL_p2p=self.dL_p2p, res=self.res, ## Added in 2023.1.3, to simplfy the points in coupler end_patch=end_patch ).cell.put(0,(self.ORy+self.gap2+self.w2_bus/2),flip=1) if (self.cell_xs_transition!=None): if (isinstance(self.cell_xs_transition,nd.Cell)): cell_trans = self.cell_xs_transition elif(hasattr(self.cell_xs_transition,'cell')): cell_trans = self.cell_xs_transition.cell ## revised in 2026.06.07 by Qin Yue # legacy: temp = cell_trans.put(bus.pin['a1']) temp = cell_trans.put(bus.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=temp.pin['b0']).put() nd.Pin(name='opt_a2',pin=temp.pin['b0'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: temp = cell_trans.put(bus.pin['b1']) temp = cell_trans.put(bus.pin['opt_b1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=temp.pin['b0']).put() nd.Pin(name='opt_b2',pin=temp.pin['b0'],type="optical:").put() else: ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=bus.pin['a1']).put() nd.Pin(name='opt_a2',pin=bus.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=bus.pin['b1']).put() nd.Pin(name='opt_b2',pin=bus.pin['opt_b1'],type="optical:").put() if show_pins: nd.put_stub() return C def generate_test_gds(self,GC,dX_gc2gc,dY_gc2gc,offset=0,w_wg=0.45,w_term=0.18,R_bend=10,xs='strip',cell_attribute=None): if (cell_attribute==None): test_cell = self.cell else : test_cell = getattr(self,cell_attribute) if (self.name!=None): test_c_name = self.name + "_test" else : test_c_name = "AED_ring_test_NO" with nd.Cell(name=test_c_name,instantiate=True) as C: GC = __cell_arg__(arg=GC,arg_name="GC",func_name="AED_rings::generate_test_gds") GC1 = GC.put('g1',0,0,180) GC2 = GC.put('g1',dX_gc2gc,0,0) pic_strip = Route(width=w_wg,radius=R_bend,xs=xs,MM_route=False) if (self.w2_bus>0): ## revised in 2026.06.07 by Qin Yue # legacy: dX_c = abs(test_cell.pin['a1'].y - test_cell.pin['a2'].y) dX_c = abs(test_cell.pin['opt_a1'].y - test_cell.pin['opt_a2'].y) ## revised in 2026.06.07 by Qin Yue # legacy: dY_c = abs(test_cell.pin['a1'].x - test_cell.pin['a2'].x) dY_c = abs(test_cell.pin['opt_a1'].x - test_cell.pin['opt_a2'].x) ## revised in 2026.06.07 by Qin Yue # legacy: INSTR = test_cell.put('a1',GC1.pin['g1'].x + dX_gc2gc/2-dX_c/2-R_bend-offset,GC1.pin['g1'].y-R_bend-1,-90,flip=0) INSTR = test_cell.put('opt_a1',GC1.pin['g1'].x + dX_gc2gc/2-dX_c/2-R_bend-offset,GC1.pin['g1'].y-R_bend-1,-90,flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_p2p(pin1=INSTR.pin['a1'],pin2=GC1.pin['g1']).put() pic_strip.bend_p2p(pin1=INSTR.pin['opt_a1'],pin2=GC1.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_p2p(pin1=INSTR.pin['a2'],pin2=GC2.pin['g1']).put() pic_strip.bend_p2p(pin1=INSTR.pin['opt_a2'],pin2=GC2.pin['g1']).put() GC3 = GC.put('g1',GC2.pin['g1'].x,GC2.pin['g1'].y-dY_gc2gc,0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_p2p(pin1=INSTR.pin['b1'],pin2=GC3.pin['g1']).put() pic_strip.bend_p2p(pin1=INSTR.pin['opt_b1'],pin2=GC3.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.bend_route(pin=INSTR.pin['b2'],length1=0.5,length2=0.5).put() pic_strip.bend_route(pin=INSTR.pin['opt_b2'],length1=0.5,length2=0.5).put() pic_strip.taper(width2=w_term,length=15).put() else: ## revised in 2026.06.07 by Qin Yue # legacy: dX_c = abs(test_cell.pin['a1'].x - test_cell.pin['b1'].x) dX_c = abs(test_cell.pin['opt_a1'].x - test_cell.pin['opt_b1'].x) ## revised in 2026.06.07 by Qin Yue # legacy: INSTR = test_cell.put('a1',GC1.pin['g1'].x + dX_gc2gc/2-dX_c/2-R_bend-offset,GC1.pin['g1'].y,0,flip=0) INSTR = test_cell.put('opt_a1',GC1.pin['g1'].x + dX_gc2gc/2-dX_c/2-R_bend-offset,GC1.pin['g1'].y,0,flip=0) ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.strt_p2p(pin1=INSTR.pin['a1'],pin2=GC1.pin['g1']).put() pic_strip.strt_p2p(pin1=INSTR.pin['opt_a1'],pin2=GC1.pin['g1']).put() ## revised in 2026.06.07 by Qin Yue # legacy: pic_strip.strt_p2p(pin1=INSTR.pin['b1'],pin2=GC2.pin['g1']).put() pic_strip.strt_p2p(pin1=INSTR.pin['opt_b1'],pin2=GC2.pin['g1']).put() INSTR.raise_pins() nd.Pin(name='a0').put(0,0,180) nd.Pin(name='b0').put(0,0,0) return C class STD_PIC_Rings(AED_ring): """ STD PIC Rings primitive component. This component builds the STD PIC Rings layout cell. Parameters ---------- name : Optional[str], optional Unique identifier for the device cell. Default is None. r_ring : float, optional Radius parameter in microns. Default is 10. w_ring : float, optional Width parameter in microns. Default is 10. gap1 : float, optional Spacing or gap parameter in microns. Default is 0.2. gap2 : float, optional Spacing or gap parameter in microns. Default is 0.2. w1_bus : float, optional Value for the w1_bus parameter. Default is 0.45. w2_bus : float, optional Value for the w2_bus parameter. Default is 0. R1_cp : Any, optional Radius parameter in microns. Default is None. R2_cp : Any, optional Radius parameter in microns. Default is None. A1_cp : int, optional Angle parameter in degrees. Default is 0. A2_cp : int, optional Angle parameter in degrees. Default is 0. offset_X : float, optional Value for the offset_X parameter. Default is 0. offset_Y : float, optional Value for the offset_Y parameter. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R1_att : float, optional Radius parameter in microns. Default is 20. R2_att : float, optional Radius parameter in microns. Default is 20. R2_att_min : float, optional Radius parameter in microns. Default is 10. R1_att_min : float, optional Radius parameter in microns. Default is 10. A1_att : float, optional Angle parameter in degrees. Default is 30. A2_att : float, optional Angle parameter in degrees. Default is 20. Ltp_bus : int, optional Length parameter in microns. Default is 10. dL_p2p : Optional[float], optional Value for the dL_p2p parameter. Default is None. L_tilt : int, optional Length parameter in microns. Default is 10. xs : str, optional Layer or cross-section name used by the device. Default is 'strip'. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. cell_xs_transition : Any, optional Cell or component dependency used by this device. Default is None. Euler_trasition : bool, optional Value for the Euler_trasition parameter. Default is False. res : float, optional Value for the res parameter. Default is 0.001. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. """ def __init__(self, name: Optional[str] = None, r_ring: float=10, w_ring: float=10, gap1: float=0.2, gap2: float=0.2, w1_bus: float=0.45, w2_bus: float=0, R1_cp: Any=None, R2_cp: Any=None, A1_cp: int=0, A2_cp: int=0, offset_X: float=0, offset_Y: float=0, w_wg: float=0.45, R1_att: float = 20, R2_att: float = 20, R2_att_min: float = 10, R1_att_min: float = 10, A1_att: float = 30, A2_att: float = 20, Ltp_bus: int=10, dL_p2p: Optional[float]=None, L_tilt: int=10, xs: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, Euler_trasition: bool=False, res: float = 0.001, show_pins: bool=False) -> None: ORx = r_ring+w_ring/2 ORy = r_ring+w_ring/2 IRx = r_ring-w_ring/2 IRy = r_ring-w_ring/2 super().__init__(name, ORx, ORy, IRx, IRy, gap1, gap2, w1_bus, w2_bus, R1_cp, R2_cp, A1_cp, A2_cp, offset_X, offset_Y, w_wg, R1_att, R2_att, R2_att_min, R1_att_min, A1_att, A2_att, Ltp_bus, dL_p2p, L_tilt, xs, sharp_patch, cell_xs_transition, Euler_trasition, res=res, show_pins=show_pins)