from typing import Any, Optional import nazca as nd import numpy as np from ...basic import __xs_exist__ from ...geometry import * import nazca.interconnects as IC # class Route(IC.Interconnect): # pass from ...routing import Route from .unit import * from ..pic import * from ...electronics import Vias from ...basic import __xs_exist__ class SOCR(): """ This is the class for normal Strong Over Coupled Ring for phase modulation. Parameters ---------- w_wg : float, optional Width parameter in microns. Default is 0.45. w_cp : float, optional Width parameter in microns. Default is 0.45. w_ring : float, optional Width parameter in microns. Default is 0.45. r_ring : float, optional Radius parameter in microns. Default is 6. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. theta_arc : float, optional Angle parameter in degrees. Default is 100. Ls : float, optional Length parameter in microns. Default is 2. xs_ring : str, optional Layer or cross-section name used by the device. Default is 'strip'. w_heater : float, optional Width parameter in microns. Default is 1.5. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_metal : float, optional Width parameter in microns. Default is 6. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is True. """ def __init__( self, w_wg : float = 0.45, w_cp : float =0.45, w_ring : float =0.45, r_ring : float =6, gap : float =0.2, theta_arc : float =100, Ls : float = 2, xs_ring : str = "strip", w_heater : float =1.5, xs_heater : str = "heater", w_metal : float = 6, xs_metal : str = "metal", via_h2m: Any = None, sharp_patch : bool = True, show_pins : bool = True ) -> None: ''' Initilization of SOCR. Args: - w_wg [um] Width of normal waveguide - w_cp [um] Width of coupling waveguide - w_ring [um] Width of ring resonator - r_ring [um] Radius of ring resonator - gap [um] Gap of coupling region - theta_arc [degree]Angle of coupling region - Ls [um] ''' self.res = 0.1 ## Basic parameters self.w_wg = w_wg self.w_cp = w_cp self.w_ring = w_ring self.r_ring = r_ring self.gap = gap self.theta_arc = theta_arc self.Ls = Ls self.xs_ring = xs_ring self.w_heater = w_heater self.xs_heater = xs_heater self.w_metal = w_metal self.xs_metal = xs_metal self.via_h2m = via_h2m self.xs_via_h2m = self.via_h2m.xs self.sz_via_h2m = self.via_h2m.sz[0] self.sp_via_h2m = self.via_h2m.spacing[0] try: nd.get_xsection(self.xs_via_h2m) except: print("==Passive::rings::SOCR::::",self.xs_via_h2m," not define in the tapeout.") self.xs_via_h2m = None self.sharp_patch = sharp_patch self.show_pins = show_pins self.cell_name = "SOCR_Wcp"+str(np.floor(self.w_cp*1000))+"_Wr"+str(np.floor(self.w_ring*1000))+"_G"+str(self.gap)+"_R"+str(self.r_ring)+"_Arc"+str(self.theta_arc) self.cell = self.generate_gds() def generate_pic_part(self): ''' Generate gds. Return: Cell ''' with nd.Cell(name=self.cell_name+"_pic",instantiate=False) as C: ## Generate the adiabatic ring theta_list = np.linspace(0, 2*np.pi, int(self.r_ring*2*np.pi/self.res)+1) self.r_out = self.r_ring + self.w_ring/2 self.r_in = self.r_ring - self.w_ring/2 curve_r_out = [(self.r_out*np.cos(theta), self.r_out*np.sin(theta)) for theta in theta_list] curve_r_in = [(self.r_in*np.cos(theta), self.r_in*np.sin(theta)) for theta in theta_list] curve_r_in.reverse() poly_resonator = curve_r_out + curve_r_in nd.Polygon(points=poly_resonator, layer=self.xs_ring.upper()+"_COR").put() # Add cladding if cladding layer exists layer_cld_name = nd.get_layer(self.xs_ring.upper()+"_CLD") # if layer_cld_name != "dump": # r_cld_out = self.r_out + 2 # resonator_cld_out = [(r_cld_out*np.cos(theta), r_cld_out*np.sin(theta)) for theta in theta_list] # r_cld_in = self.r_in - 2 # if r_cld_in < 1: # resonator_cld_in = [] # else: # resonator_cld_in = [(r_cld_in*np.cos(theta), r_cld_in*np.sin(theta)) for theta in theta_list] # resonator_cld_in.reverse() # resonator_cld_poly = resonator_cld_out + resonator_cld_in # nd.Polygon(points=resonator_cld_poly, layer=self.xs_ring.upper()+"_CLD").put(0, 0) for layers,growx,growy,acc in nd.layeriter(xs=self.xs_ring): (a1,b1), (a2,b2),c1,c2 = growx if b1!=0 and b2!=0 : r_cld_out = self.r_out + b1 resonator_cld_out = [(r_cld_out*np.cos(theta), r_cld_out*np.sin(theta)) for theta in theta_list] r_cld_in = self.r_in + b2 if r_cld_in < 1: resonator_cld_in = [] else: resonator_cld_in = [(r_cld_in*np.cos(theta), r_cld_in*np.sin(theta)) for theta in theta_list] resonator_cld_in.reverse() resonator_cld_poly = resonator_cld_out + resonator_cld_in nd.Polygon(points=resonator_cld_poly, layer=layers).put(0, 0) ## Add input waveguide wg = Route(width=self.w_cp, radius=5, xs=self.xs_ring) wg.bend(radius=self.r_out+self.gap+self.w_cp/2, angle=self.theta_arc/2, arrow=False).put(0, -(self.r_out+self.gap+self.w_cp/2)) wg.strt(length=self.Ls, arrow=False).put() wg.bend(angle=-self.theta_arc/2, arrow=False).put() strt_out = wg.strt(length=1, arrow=False).put() wg.taper(width1=self.w_cp, width2=self.w_wg, length=4.5, arrow=False).put() wg_out = wg.strt(width=self.w_wg,length=0.5,arrow=False).put() wg.bend(radius=self.r_out+self.gap+self.w_cp/2, angle=-self.theta_arc/2, arrow=False).put(0, -(self.r_out+self.gap+self.w_cp/2), 180) wg.strt(length=self.Ls, arrow=False).put() wg.bend(angle=self.theta_arc/2, arrow=False).put() wg.strt(length=1, arrow=False).put() wg.taper(width1=self.w_cp, width2=self.w_wg, length=4.5, arrow=False).put() wg_in = wg.strt(width=self.w_wg,length=0.5,arrow=False).put() ## Add pins ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="a1", width=self.w_wg).put(wg_in.pin['b0']) nd.Pin(name="opt_a1", width=self.w_wg,type="optical:").put(wg_in.pin['b0']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="b1", width=self.w_wg).put(wg_out.pin['b0']) nd.Pin(name="opt_b1", width=self.w_wg,type="optical:").put(wg_out.pin['b0']) ## Add patch if self.sharp_patch and (layer_cld_name != "dump"): # if hasattr(self.tapeout, "LAYER_STRIP_CLD"): # Vertical patch x_min = self.r_ring + self.w_ring/2 + 2 x_max = self.r_ring - self.w_ring/2 - 2 y_min = 0 y_max = wg_in.pin['a0'].y + self.w_cp/2 + 2 rect_poly = [(x_min,y_min),(x_min,y_max),(x_max,y_max),(x_max,y_min)] nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0) nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0,flip=True) # Horizontal patch x_min = self.r_in - 2 x_max = strt_out.pin['b0'].x y_min = strt_out.pin['b0'].y - self.w_cp/2 - 2 y_max = strt_out.pin['b0'].y + self.w_cp/2 + 2 rect_poly = [(x_min,y_min),(x_min,y_max),(x_max,y_max),(x_max,y_min)] nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0) nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0,flip=True) if self.show_pins: nd.put_stub() return C def generate_eic_part(self): with nd.Cell(name=self.cell_name+"_eic",instantiate=False) as C: # Add heater ring_x = 0 ring_y = 0 ring_radius = self.r_ring ring_width = self.w_heater theta_start = -30 theta_stop = 180+30 n_points = int((theta_stop-theta_start)*2*np.pi/360 * ring_radius / self.res) circle(radius=ring_radius, width=ring_width, theta_start=theta_start, theta_stop=theta_stop, res=self.res, # n_points=n_points, ## revised in 2023.03.20 layer=self.xs_heater.upper()).cell.put(ring_x, ring_y) # Add a broad region to connect heater with metal2 via_region_width = self.w_metal r_below_point = [((ring_radius+ring_width/2) * np.cos(theta_start*np.pi/180)+ring_x, (ring_radius+ring_width/2) * np.sin(theta_start*np.pi/180)+ring_y)] r_up_point = [((ring_radius-ring_width/2) * np.cos(theta_start*np.pi/180)+ring_x, (ring_radius-ring_width/2) * np.sin(theta_start*np.pi/180)+ring_y)] heater_con_poly = r_up_point + r_below_point + [ (r_below_point[0][0], r_below_point[0][1]-2.2), # (r_up_point[0][0]+via_region_width, r_below_point[0][1]-2.2), (r_up_point[0][0], r_below_point[0][1]-2.2) ] nd.Polygon(points=heater_con_poly, layer=self.xs_heater.upper()).put(0, 0) nd.Polygon(points=heater_con_poly, layer=self.xs_heater.upper()).put(0, 0, flip=True) # Add via heater_wide_width = r_up_point[0][0]+via_region_width - r_up_point[0][0] heater_wide_x = (r_up_point[0][0]+via_region_width + r_up_point[0][0]) / 2 heater_wide_y = -heater_wide_width/2 + r_below_point[0][1]-2.2 vias = Vias( xs=self.xs_via_h2m, area=[heater_wide_width, heater_wide_width], sz=[self.sz_via_h2m, self.sz_via_h2m], spacing=[self.sp_via_h2m, self.sp_via_h2m], xs_l1=self.xs_heater, xs_l2=self.xs_metal ) vias.cell.put(heater_wide_x,heater_wide_y) vias.cell.put(-heater_wide_x,heater_wide_y) nd.Pin(name='ep1', width=via_region_width).put(-heater_wide_x, heater_wide_y, -90) nd.Pin(name='en1', width=via_region_width).put( heater_wide_x, heater_wide_y, -90) return C def generate_gds(self): self.cell_pic = self.generate_pic_part() self.cell_eic = self.generate_eic_part() with nd.Cell(name=self.cell_name,instantiate=False) as C: pic = self.cell_pic.put(0, 0) eic = self.cell_eic.put(0, 0) ## Add pins ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="a1", width=self.w_wg).put(pic.pin['a1']) nd.Pin(name="opt_a1", width=self.w_wg,type="optical:").put(pic.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="b1", width=self.w_wg).put(pic.pin['b1']) nd.Pin(name="opt_b1", width=self.w_wg,type="optical:").put(pic.pin['opt_b1']) nd.Pin(name="ep1", width=eic.pin['ep1'].width).put(eic.pin['ep1']) nd.Pin(name="en1", width=eic.pin['en1'].width).put(eic.pin['en1']) if self.show_pins: nd.put_stub() return C def generate_2pi_gds(self, gap=10): with nd.Cell(name=self.cell.cell_name+"_2pi", instantiate=False) as C: ## revised in 2026.06.07 by Qin Yue # legacy: socr_1 = self.cell.put('a1',0,0,0) socr_1 = self.cell.put('opt_a1',0,0,0) ## revised in 2026.06.07 by Qin Yue # legacy: socr_2 = self.cell.put('a1', socr_1.pin['b1'].move(gap,0,0)) socr_2 = self.cell.put('opt_a1', socr_1.pin['opt_b1'].move(gap,0,0)) stripe = Route(xs='strip', width=self.w_wg, radius=5) metal1 = Route(xs='metal', width=6, radius=0) stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=socr_1.pin['b1'].move(-0.1,0,0), pin1=socr_1.pin['opt_b1'].move(-0.1,0,0), ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_2.pin['a1'].move(-0.1,0,0), pin2=socr_2.pin['opt_a1'].move(-0.1,0,0), arrow=False ).put() metal1.strt_p2p( pin1=socr_1.pin['en1'], pin2=socr_2.pin['ep1'], arrow=False ).put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1', width=self.w_wg).put(socr_1.pin['a1']) nd.Pin(name='opt_a1', width=self.w_wg,type="optical:").put(socr_1.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1', width=self.w_wg).put(socr_2.pin['b1']) nd.Pin(name='opt_b1', width=self.w_wg,type="optical:").put(socr_2.pin['opt_b1']) nd.Pin(name='ep1',pin=socr_1.pin['ep1'].move(0,0,-90)).put() nd.Pin(name='en1',pin=socr_2.pin['en1'].move(0,0, 90)).put() return C def generate_test_mzi_gds(self, gc, mmi, num_socr=1, gc2gc_length=250, mid_offset=40, show_pins=False): with nd.Cell(name=self.cell.cell_name+"_test_mzi", instantiate=False) as C: gc_input = gc.cell.put('g1', 0, 0, 180) gc_output_mid = gc.cell.put('g1', gc2gc_length+mid_offset, 0, 0) ## revised in 2026.06.07 by Qin Yue # legacy: mmi_input = mmi.cell.put('a1', 20, 0, 0) mmi_input = mmi.cell.put('opt_a1', 20, 0, 0) stripe = Route(xs='strip', width=gc.w_wg, radius=5) # Connect input gc with first mmi ## revised in 2026.06.07 by Qin Yue # legacy: stripe.strt_p2p(pin1=gc_input.pin['g1'], pin2=mmi_input.pin['a1'], arrow=False).put() stripe.strt_p2p(pin1=gc_input.pin['g1'], pin2=mmi_input.pin['opt_a1'], arrow=False).put() # Connect the upper arm ## revised in 2026.06.07 by Qin Yue # legacy: self.length = self.cell.pin['b1'].x - self.cell.pin['a1'].x self.length = self.cell.pin['opt_b1'].x - self.cell.pin['opt_a1'].x socr_cell = [] for _index_ in range(num_socr): ## revised in 2026.06.07 by Qin Yue # legacy: cell = self.cell.put('a1', mmi_input.pin['b1'].x+30+(self.length+5)*_index_, 40, 0) cell = self.cell.put('opt_a1', mmi_input.pin['opt_b1'].x+30+(self.length+5)*_index_, 40, 0) socr_cell.append(cell) # socr_cell = pd.concat(socr_cell,cell) if _index_>=1: stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=socr_cell[_index_-1].pin['b1'].move(-0.1,0,0), pin1=socr_cell[_index_-1].pin['opt_b1'].move(-0.1,0,0), ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[_index_].pin['a1'].move(-0.1,0,0), pin2=socr_cell[_index_].pin['opt_a1'].move(-0.1,0,0), arrow=False ).put() nd.Pin(name="ep"+str(_index_+1),pin=cell.pin['ep1']).put() nd.Pin(name="en"+str(_index_+1),pin=cell.pin['en1']).put() stripe.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_input.pin['b1'], pin1=mmi_input.pin['opt_b1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[0].pin['a1'].move(-0.1,0,0), pin2=socr_cell[0].pin['opt_a1'].move(-0.1,0,0), Lstart=1, arrow=False).put() ## revised in 2026.06.07 by Qin Yue # legacy: mmi_output_up = mmi.cell.put('a1', socr_cell[-1].pin['b1'].x+30, socr_cell[-1].pin['b1'].y, 0) mmi_output_up = mmi.cell.put('opt_a1', socr_cell[-1].pin['opt_b1'].x+30, socr_cell[-1].pin['opt_b1'].y, 0) stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_up.pin['a1'], pin1=mmi_output_up.pin['opt_a1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[-1].pin['b1'].move(-0.1,0,0), pin2=socr_cell[-1].pin['opt_b1'].move(-0.1,0,0), arrow=False).put() ## revised in 2026.06.07 by Qin Yue # legacy: gc_output_up = gc.cell.put('g1', gc2gc_length, mmi_output_up.pin['b1'].y, 0) gc_output_up = gc.cell.put('g1', gc2gc_length, mmi_output_up.pin['opt_b1'].y, 0) stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_up.pin['b1'], pin1=mmi_output_up.pin['opt_b1'], pin2=gc_output_up.pin['g1'], arrow=False).put() # Connect the below arm ## revised in 2026.06.07 by Qin Yue # legacy: mmi_output_below = mmi.cell.put('a1', socr_cell[-1].pin['b1'].x+30, -socr_cell[-1].pin['b1'].y, 0) mmi_output_below = mmi.cell.put('opt_a1', socr_cell[-1].pin['opt_b1'].x+30, -socr_cell[-1].pin['opt_b1'].y, 0) stripe.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_input.pin['b2'], pin1=mmi_input.pin['opt_b2'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=mmi_output_below.pin['a1'], pin2=mmi_output_below.pin['opt_a1'], Lstart=1, arrow=False).put() ## revised in 2026.06.07 by Qin Yue # legacy: gc_output_below = gc.cell.put('g1', gc2gc_length, mmi_output_below.pin['b2'].y, 0) gc_output_below = gc.cell.put('g1', gc2gc_length, mmi_output_below.pin['opt_b2'].y, 0) stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_below.pin['b2'], pin1=mmi_output_below.pin['opt_b2'], pin2=gc_output_below.pin['g1'], arrow=False).put() # Combine upper and below arm together to do the coherence detection ## revised in 2026.06.07 by Qin Yue # legacy: mmi_output_mid = mmi.cell.put('a1', mmi_output_below.pin['b1'].x+mmi.length+20, gc_output_mid.pin['g1'].y, 180) mmi_output_mid = mmi.cell.put('opt_a1', mmi_output_below.pin['opt_b1'].x+mmi.length+20, gc_output_mid.pin['g1'].y, 180) stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_mid.pin['a1'], pin1=mmi_output_mid.pin['opt_a1'], pin2=gc_output_mid.pin['g1'], arrow=False).put() stripe.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_up.pin['b2'], pin1=mmi_output_up.pin['opt_b2'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=mmi_output_mid.pin['b2'], pin2=mmi_output_mid.pin['opt_b2'], Lstart=1, arrow=False).put() stripe.sbend_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=mmi_output_below.pin['b1'], pin1=mmi_output_below.pin['opt_b1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=mmi_output_mid.pin['b1'], pin2=mmi_output_mid.pin['opt_b1'], Lstart=1, arrow=False).put() if show_pins : nd.put_stub() return C def generate_test_gds(self, gc, num_socr=1, gc2gc_length=250, cell_name=None): if cell_name==None: cell_name = self.cell.cell_name+"_test_N"+str(num_socr) with nd.Cell(name=cell_name, instantiate=False) as C: gc_input = gc.cell.put('g1',0,0,180) gc_output = gc.cell.put('g1',gc2gc_length, 0, 0) stripe = Route(xs='strip', width=gc.w_wg, radius=5) # Add test structure ## revised in 2026.06.07 by Qin Yue # legacy: self.length = self.cell.pin['b1'].x - self.cell.pin['a1'].x self.length = self.cell.pin['opt_b1'].x - self.cell.pin['opt_a1'].x socr_cell = [] for _index_ in range(num_socr): ## revised in 2026.06.07 by Qin Yue # legacy: cell = self.cell.put('a1', gc_input.pin['g1'].x+10+(self.length+5)*_index_, 0, 0) cell = self.cell.put('opt_a1', gc_input.pin['g1'].x+10+(self.length+5)*_index_, 0, 0) socr_cell.append(cell) # socr_cell = pd.concat(socr_cell,[cell]) if _index_>=1: stripe.strt_p2p( ## revised in 2026.06.07 by Qin Yue # legacy: pin1=socr_cell[_index_-1].pin['b1'].move(-0.1,0,0), pin1=socr_cell[_index_-1].pin['opt_b1'].move(-0.1,0,0), ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[_index_].pin['a1'].move(-0.1,0,0), pin2=socr_cell[_index_].pin['opt_a1'].move(-0.1,0,0), arrow=False).put() if num_socr>=1: stripe.strt_p2p( pin1=gc_input.pin['g1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[0].pin['a1'].move(-0.1,0,0), pin2=socr_cell[0].pin['opt_a1'].move(-0.1,0,0), arrow=False).put() stripe.strt_p2p( pin1=gc_output.pin['g1'], ## revised in 2026.06.07 by Qin Yue # legacy: pin2=socr_cell[-1].pin['b1'].move(-0.1,0,0), pin2=socr_cell[-1].pin['opt_b1'].move(-0.1,0,0), arrow=False).put() else: stripe.strt_p2p( pin1=gc_input.pin['g1'], pin2=gc_output.pin['g1'], arrow=False ).put() return C class SOCR_Cband(SOCR): """ This is the class for already designed SOCR with multimode waveguide to suppres the phase noise. Parameters ---------- w_wg : float, optional Width parameter in microns. Default is 0.45. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is True. """ def __init__(self, w_wg : float = 0.45, via_h2m: Any = None, show_pins : bool = True) -> None: super().__init__( w_wg=w_wg, w_cp=0.45, w_ring=0.8, r_ring=5.6, gap=0.18, theta_arc=150, Ls=1, w_heater=1.5, via_h2m=via_h2m, show_pins=show_pins ) class SOCR_Adiabatic(SOCR): """ This is the class for adiabatic Strong Over Coupled Ring(SOCR) for phase modulation. Parameters ---------- w_wg : float, optional Width parameter in microns. Default is 0.45. w_cp : float, optional Width parameter in microns. Default is 0.45. w_in : float, optional Width parameter in microns. Default is 0.45. w_out : float, optional Width parameter in microns. Default is 2. r_out : float, optional Radius parameter in microns. Default is 6. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. theta_arc : float, optional Angle parameter in degrees. Default is 100. Ls : float, optional Length parameter in microns. Default is 2. xs_ring : str, optional Layer or cross-section name used by the device. Default is 'strip'. w_heater : float, optional Width parameter in microns. Default is 1.5. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_metal : float, optional Width parameter in microns. Default is 6. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. sharp_patch : bool, optional Whether to add geometry patches for sharp corners or cladding continuity. Default is True. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is True. """ def __init__( self, w_wg : float = 0.45, w_cp : float = 0.45, w_in : float = 0.45, w_out : float = 2, r_out : float = 6, gap : float = 0.2, theta_arc : float = 100, Ls : float = 2, xs_ring : str = "strip", w_heater : float = 1.5, xs_heater : str = "heater", w_metal : float = 6, xs_metal : str = "metal", via_h2m: Any = None, # sz_via_h2m : float = 0.25, # sp_via_h2m : float = 0.4, # xs_via_h2m : str = "via_h2m", sharp_patch : bool = True, show_pins : bool = True ) -> None: ''' Initilization of SOCR. Args: - w_wg [um] Width of normal waveguide - w_cp [um] Width of coupling waveguide - w_in [um] Width of narrow waveugide - w_out [um] Width of wider waveguide - r_out [um] Outer radius - gap [um] Gap of coupling region - theta_arc [degree]Angle of coupling region - Ls [um] ''' self.res = 0.1 ## Basic parameters self.w_wg = w_wg self.w_cp = w_cp self.w_in = w_in self.w_out = w_out self.r_out = r_out self.gap = gap self.theta_arc = theta_arc self.Ls = Ls self.xs_ring = xs_ring self.w_heater = w_heater self.xs_heater = xs_heater self.w_metal = w_metal self.xs_metal = xs_metal self.via_h2m = via_h2m self.xs_via_h2m = self.via_h2m.xs self.sz_via_h2m = self.via_h2m.sz[0] self.sp_via_h2m = self.via_h2m.spacing[0] try : nd.get_xsection(self.xs_via_h2m) except: print("==Passive::rings::SOCR_Adiabatic::::",self.xs_via_h2m," not define in the tapeout.") self.xs_via_h2m = None self.sharp_patch = sharp_patch self.show_pins = show_pins ## More parametes self.r_in = self.r_out - (self.w_in+self.w_out)/2 self.delta = (self.w_out - self.w_in)/2 ## Generate gds self.cell_name = "SOCR_Adiabatic_Wcp%d_Win%d_Wout%.2f_G%.2f_R%d_Arc%d" %( self.w_cp*1000, self.w_in*1000, self.w_out, self.gap, self.r_out, self.theta_arc ) self.cell = self.generate_gds() def generate_pic_part(self): ''' Generate gds. Return: Cell ''' with nd.Cell(name=self.cell_name+"_pic",instantiate=False) as C: ## Generate the adiabatic ring theta_list = np.linspace(0, 2*np.pi, int(self.r_out*2*np.pi/self.res)+1) resonator_out = [(self.r_out*np.cos(theta), self.r_out*np.sin(theta)) for theta in theta_list] resonator_in = [(self.r_in*np.cos(theta), -self.delta+self.r_in*np.sin(theta)) for theta in theta_list] resonator_in.reverse() poly_resonator = resonator_out + resonator_in nd.Polygon(points=poly_resonator, layer=self.xs_ring.upper()+"_COR").put(0, 0) # Add cladding if cladding layer exists layer_cld_name = nd.get_layer(self.xs_ring.upper()+"_CLD") # if layer_cld_name != "dump": # r_cld_out = self.r_out + 2 # resonator_cld_out = [(r_cld_out*np.cos(theta), r_cld_out*np.sin(theta)) for theta in theta_list] # r_cld_in = self.r_in - 2 # if r_cld_in < 1: # resonator_cld_in = [] # else: # resonator_cld_in = [(r_cld_in*np.cos(theta), -self.delta+r_cld_in*np.sin(theta)) for theta in theta_list] # resonator_cld_in.reverse() # resonator_cld_poly = resonator_cld_out + resonator_cld_in # nd.Polygon(points=resonator_cld_poly, layer=self.xs_ring.upper()+"_CLD").put(0, 0) for layers,growx,growy,acc in nd.layeriter(xs=self.xs_ring): (a1,b1), (a2,b2),c1,c2 = growx if b1!=0 and b2!=0 : r_cld_out = self.r_out + b1 resonator_cld_out = [(r_cld_out*np.cos(theta), r_cld_out*np.sin(theta)) for theta in theta_list] r_cld_in = self.r_in + b2 if r_cld_in < 1: resonator_cld_in = [] else: resonator_cld_in = [(r_cld_in*np.cos(theta), -self.delta+r_cld_in*np.sin(theta)) for theta in theta_list] resonator_cld_in.reverse() resonator_cld_poly = resonator_cld_out + resonator_cld_in nd.Polygon(points=resonator_cld_poly, layer=layers).put(0, 0) ## Add input waveguide wg = Route(width=self.w_cp, radius=5, xs=self.xs_ring) wg.bend(radius=self.r_out+self.gap+self.w_cp/2, angle=self.theta_arc/2, arrow=False).put(0, -(self.r_out+self.gap+self.w_cp/2)) wg.strt(length=self.Ls, arrow=False).put() # wg.strt(length=1, arrow=False).put() wg.bend(angle=-self.theta_arc/2, arrow=False).put() strt_out = wg.strt(length=1, arrow=False).put() wg.taper(width1=self.w_cp, width2=self.w_wg, length=5, arrow=False).put() wg_out = wg.strt(width=self.w_wg,length=0.5,arrow=False).put() wg.bend(radius=self.r_out+self.gap+self.w_cp/2, angle=-self.theta_arc/2, arrow=False).put(0, -(self.r_out+self.gap+self.w_cp/2), 180) wg.strt(length=self.Ls, arrow=False).put() wg.bend(angle=self.theta_arc/2, arrow=False).put() wg.strt(length=1, arrow=False).put() wg.taper(width1=self.w_cp, width2=self.w_wg, length=4.5, arrow=False).put() wg_in = wg.strt(width=self.w_wg,length=0.5,arrow=False).put() ## Add patch if layer_cld_name != "dump": # Vertical patch x_min = r_cld_in x_max = r_cld_out y_min = 0 y_max = wg_in.pin['a0'].y + self.w_cp/2 + 2 rect_poly = [(x_min,y_min),(x_min,y_max),(x_max,y_max),(x_max,y_min)] nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0) nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0,flip=True) # Horizontal patch x_min = strt_out.pin['b0'].x x_max = r_cld_in y_min = strt_out.pin['b0'].y - self.w_cp/2 - 2 y_max = strt_out.pin['b0'].y + self.w_cp/2 + 2 rect_poly = [(x_min,y_min),(x_min,y_max),(x_max,y_max),(x_max,y_min)] nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0) nd.Polygon(points=rect_poly, layer=layer_cld_name).put(0,0,flip=True) ## Add pins ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="a1", width=self.w_wg).put(wg_in.pin['b0']) nd.Pin(name="opt_a1", width=self.w_wg,type="optical:").put(wg_in.pin['b0']) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name="b1", width=self.w_wg).put(wg_out.pin['b0']) nd.Pin(name="opt_b1", width=self.w_wg,type="optical:").put(wg_out.pin['b0']) if self.show_pins: nd.put_stub() self.cell = C return C def generate_eic_part(self): with nd.Cell(name=self.cell_name+"_eic",instantiate=False) as C: # Add heater ring_x = 0 ring_y = -self.delta/2 ring_radius = (self.r_in*2+self.w_in/2+self.w_out/2)/2 ring_width = self.w_heater theta_start = -30 theta_stop = 180+30 # n_points = int((theta_stop-theta_start)*2*np.pi/360 * ring_radius / self.res) circle(radius=ring_radius, width=ring_width, theta_start=theta_start, theta_stop=theta_stop, res=self.res, # n_points=n_points, layer=self.xs_heater.upper()).cell.put(ring_x, ring_y) # Add a broad region to connect heater with metal2 via_region_width = self.w_metal r_below_point = [((ring_radius+ring_width/2) * np.cos(theta_start*np.pi/180)+ring_x, (ring_radius+ring_width/2) * np.sin(theta_start*np.pi/180)+ring_y)] r_up_point = [((ring_radius-ring_width/2) * np.cos(theta_start*np.pi/180)+ring_x, (ring_radius-ring_width/2) * np.sin(theta_start*np.pi/180)+ring_y)] heater_con_poly = r_up_point + r_below_point + [ (r_below_point[0][0], r_below_point[0][1]-2.2), # (r_up_point[0][0]+via_region_width, r_below_point[0][1]-2.2), (r_up_point[0][0], r_below_point[0][1]-2.2) ] nd.Polygon(points=heater_con_poly, layer=self.xs_heater.upper()).put(0, 0) nd.Polygon(points=heater_con_poly, layer=self.xs_heater.upper()).put(0, 0, flip=True) # Add via heater_wide_x = r_up_point[0][0] + via_region_width/2 heater_wide_y = -via_region_width/2 + r_below_point[0][1]-2.2 vias = Vias( xs=self.xs_via_h2m, area=[via_region_width, via_region_width], sz=[self.sz_via_h2m, self.sz_via_h2m], spacing=[self.sp_via_h2m, self.sp_via_h2m], xs_l1=self.xs_heater, xs_l2=self.xs_metal ) vias.cell.put(heater_wide_x,heater_wide_y) vias.cell.put(-heater_wide_x,heater_wide_y) nd.Pin(name='ep1', width=via_region_width).put(-heater_wide_x, heater_wide_y, -90) nd.Pin(name='en1', width=via_region_width).put(heater_wide_x, heater_wide_y, -90) return C def generate_gds(self): return super().generate_gds() class SOCR_Adiabatic_Cband(SOCR_Adiabatic): """ This is already designed SOCR at C-band. Parameters ---------- w_wg : float, optional Width parameter in microns. Default is 0.45. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is True. """ def __init__(self, w_wg : float=0.45, via_h2m: Any = None, show_pins : bool = True) -> None: super().__init__( w_wg=w_wg, w_cp=0.45, w_in=0.51, w_out=2, r_out=9, gap=0.2, theta_arc=120, Ls=1, via_h2m=via_h2m, show_pins=show_pins ) class MRR_AED(AED_ring): """ MRR AED primitive component. This component builds the MRR AED 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.5. 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_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is None. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is None. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. ht_notch_dual : bool, optional Value for the ht_notch_dual parameter. Default is True. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. 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.5, 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_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, Euler_trasition: bool=False, show_pins: bool=False, xs_heater : str = None, w_heater : float = 0, xs_metal : str = None, w_metal : float = 0, via_h2m: Any = None, ## input the class of the via isl: Any = None, ## thermal isolation paramters A_ht : float = 270, ht_notch_dual: bool = True, epin_dX: int = 0, epin_dY: int = 0, res: float = 0.05, res_eic: float = 0.5, ht_rot: bool = False, ) -> None: super().__init__(name=None, ORx=ORx,ORy=ORy,IRx=IRx,IRy=IRy, gap1=gap1,gap2=gap2,w1_bus=w1_bus,w2_bus=w2_bus, R1_cp=R1_cp,R2_cp=R2_cp, A1_cp=A1_cp,A2_cp=A2_cp, A1_att=A1_att,A2_att=A2_att, R1_att=R1_att,R2_att=R2_att, R1_att_min=R1_att_min,R2_att_min=R2_att_min, offset_X=offset_X,offset_Y=offset_Y, w_wg=w_wg,L_tilt=L_tilt,Ltp_bus=Ltp_bus,xs=xs_ring, sharp_patch=sharp_patch, cell_xs_transition = cell_xs_transition, Euler_trasition=Euler_trasition, res=res,show_pins=show_pins,dL_p2p=dL_p2p) self.name = name if (self.name==None): self.instantiate = False else : self.instantiate = True self.w_heater = w_heater self.w_metal = w_metal self.Rx = (ORx+IRx)/2 self.Ry = (ORy+IRy)/2 self.cell_eic=None self.res_eic = res_eic self.xs_heater = xs_heater self.xs_metal = xs_metal self.A_ht = A_ht self.epin_dX = epin_dX self.epin_dY = epin_dY self.isl = isl self.via_h2m = via_h2m self.cell_pic = self.cell self.ht_notch_dual = ht_notch_dual ## building electro part xs_heater = __xs_exist__(xs=xs_heater,para_name="xs_heater",func_name="mxpic::passive::MRR_AED") xs_metal = __xs_exist__(xs=xs_metal,para_name="xs_metal",func_name="mxpic::passive::MRR_AED") if (w_heater>0 and xs_heater!=None and xs_metal!=None): self.cell_eic = self.generate_eic_gds(show_pins=show_pins) else : self.cell_eic = None if (self.cell_eic!=None and self.cell_pic!=None): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: """ 2023.09.20 REVISED for 180 rotation of placing heaters """ if (ht_rot == False): C_eic = self.cell_eic.put(0,0,0) else: C_eic = self.cell_eic.put(0,0,ht_rot) C_pic = self.cell_pic.put(0,0,0) ## transporting pins for str,Pin in C_eic.ic_pins(): nd.Pin(name=str,pin=Pin).put() for str,Pin in C_pic.ic_pins(): nd.Pin(name=str,pin=Pin).put() self.cell = C elif (self.cell_eic==None and self.cell_pic!=None): self.cell = self.cell_pic def generate_eic_gds(self, show_pins=False): with nd.Cell(instantiate=False) as C: w_pad = self.w_metal ## calculating the heater angle if (self.w2_bus>0 and self.ht_notch_dual): A_L = [-self.A_ht/4,self.A_ht/4] else : A_L = [90-self.A_ht/2,90] """ Placing main heaters """ w_heater = self.w_heater Elipse_dual(xs=self.xs_heater,ORy=self.Ry+w_heater/2,ORx=self.Rx+w_heater/2,IRx=self.Rx-w_heater/2,IRy=self.Ry-w_heater/2, theta_start=A_L[0],theta_stop=A_L[1], offset_X=self.offset_X,offset_Y=self.offset_Y,sharp_patch=False,res=self.res_eic, ).cell.put(0,0,0) Elipse_dual(xs=self.xs_heater,ORy=self.Ry+w_heater/2,ORx=self.Rx+w_heater/2,IRx=self.Rx-w_heater/2,IRy=self.Ry-w_heater/2, theta_start=180-A_L[1],theta_stop=180-A_L[0], offset_X=self.offset_X,offset_Y=self.offset_Y,sharp_patch=False,res=self.res_eic, ).cell.put(0,0,0) x_pad = self.Rx*np.cos(A_L[0]/180*np.pi) y_pad = self.Ry*np.sin(A_L[0]/180*np.pi) if (self.via_h2m==None): vias = Vias(xs=None,area=[w_pad,w_pad],sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal).cell ## only putting metal blocks else: if (hasattr(self.via_h2m,"cell")): vias = self.via_h2m.cell else : vias = Vias(xs=self.via_h2m.xs,area=[w_pad,w_pad],sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,xs_l1=self.xs_heater,xs_l2=self.xs_metal).cell ## placing vias if (self.w2_bus >0 and self.ht_notch_dual): nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(-x_pad,-y_pad-w_heater/2,90) att = nd.strt(xs=self.xs_heater,length=self.epin_dY,width=w_heater).put(-x_pad,-y_pad,90) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(att.pin['b0'].x+w_heater/2,att.pin['b0'].y,180) nd.strt(xs=self.xs_heater,length=self.epin_dX,width=w_heater).put(att.pin['b0'].x,att.pin['b0'].y,180) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(x_pad,-y_pad-w_heater/2,90) att = nd.strt(xs=self.xs_heater,length=self.epin_dY,width=w_heater).put(x_pad,-y_pad,90) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(att.pin['b0'].x-w_heater/2,att.pin['b0'].y,0) nd.strt(xs=self.xs_heater,length=self.epin_dX,width=w_heater).put(att.pin['b0'].x,att.pin['b0'].y,0) V1 = vias.put(-x_pad-self.epin_dX,-y_pad+self.epin_dY,90) V2 = vias.put( x_pad+self.epin_dX,-y_pad+self.epin_dY,90,flip=1) dLVia = abs(V1.pin['b0'].x - V2.pin['b0'].x)+V1.pin['b0'].width/2+ V2.pin['b0'].width/2 nd.strt(length=dLVia,width=self.w_metal,xs=self.xs_metal).put(V1.pin['b0'].move(0, V1.pin['b0'].width/2,-90)) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(-x_pad,y_pad-w_heater/2,90) att = nd.strt(xs=self.xs_heater,length=self.epin_dY,width=w_heater).put(-x_pad,y_pad,-90) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(att.pin['b0'].x+w_heater/2,att.pin['b0'].y,180) nd.strt(xs=self.xs_heater,length=self.epin_dX,width=w_heater).put(att.pin['b0'].x,att.pin['b0'].y,180) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(x_pad,y_pad-w_heater/2,90) att = nd.strt(xs=self.xs_heater,length=self.epin_dY,width=w_heater).put(x_pad,y_pad,-90) nd.strt(xs=self.xs_heater,length=w_heater,width=w_heater).put(att.pin['b0'].x-w_heater/2,att.pin['b0'].y,0) nd.strt(xs=self.xs_heater,length=self.epin_dX,width=w_heater).put(att.pin['b0'].x,att.pin['b0'].y,0) if (self.epin_dX>0): VIAL = vias.put(-x_pad-self.epin_dX,y_pad-self.epin_dY,180,flip=1) VIAR = vias.put( x_pad+self.epin_dX,y_pad-self.epin_dY,0) else : VIAL = vias.put(-x_pad-self.epin_dX,y_pad-self.epin_dY,-90,flip=1) VIAR = vias.put( x_pad+self.epin_dX,y_pad-self.epin_dY,-90) nd.Pin(name='ep1',width=w_pad,pin=VIAL.pin['b0']).put() nd.Pin(name='en1',width=w_pad,pin=VIAR.pin['b0']).put() if (self.isl!=None): if (self.w2_bus>0): ## revised in 2026.06.07 by Qin Yue # legacy: L_isl = np.abs(self.cell_pic.pin['a1'].y - self.cell_pic.pin['a2'].y) - self.isl.sp_isl_wg*2 - self.w_wg L_isl = np.abs(self.cell_pic.pin['opt_a1'].y - self.cell_pic.pin['opt_a2'].y) - self.isl.sp_isl_wg*2 - self.w_wg else: ## revised in 2026.06.07 by Qin Yue # legacy: L_isl = np.abs(self.ORy - self.cell_pic.pin['a1'].y) - self.isl.sp_isl_wg - self.w_wg/2 L_isl = np.abs(self.ORy - self.cell_pic.pin['opt_a1'].y) - self.isl.sp_isl_wg - self.w_wg/2 ## revised in 2026.06.07 by Qin Yue # legacy: y_isl = self.cell_pic.pin['a1'].y + self.isl.sp_isl_wg + self.w_wg/2 y_isl = self.cell_pic.pin['opt_a1'].y + self.isl.sp_isl_wg + self.w_wg/2 if (L_isl>self.isl.width): x_isl = self.ORx+self.isl.sp_isl_metal+self.isl.width/2 ISL(xs=self.isl.xs,width=self.isl.width,length=L_isl,spacing=self.isl.spacing,Lmax=self.isl.Lmax).cell.put(-x_isl,y_isl,90) ISL(xs=self.isl.xs,width=self.isl.width,length=L_isl,spacing=self.isl.spacing,Lmax=self.isl.Lmax).cell.put( x_isl,y_isl,90) if (show_pins): nd.put_stub() return C class MRR_STD_Ring(MRR_AED) : """ MRR STD Ring primitive component. This component builds the MRR STD Ring 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 0.45. 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_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. ht_notch_dual : bool, optional Value for the ht_notch_dual parameter. Default is False. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: Optional[str]=None, r_ring: float=10, w_ring: float=0.45, 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_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, Euler_trasition: bool=False, show_pins: bool=False, xs_heater: str = 'heater', w_heater: float = 0, xs_metal: str = 'metal', w_metal: float = 0, via_h2m: Any = None, isl: Any = None, A_ht: float = 270, ht_notch_dual: bool = False, epin_dX: int=0, epin_dY: int=0, res: float = 0.05, res_eic: float = 0.5, ht_rot: bool=False) -> None: """_summary_ Args: name (_type_, optional): Name of the device. Defaults to None. r_ring (int, optional): Central radius of the ring . Defaults to 10. w_ring (float, optional): Width of the ring. Defaults to 0.45. gap1 (float, optional): Gap distnace of the lower bus waveguide and the ring. Defaults to 0.2. gap2 (float, optional): Gap distnace of the upper bus waveguide and the ring. Defaults to 0.2. w1_bus (float, optional): Width of the lower bus waveguide. Defaults to 0.45. w2_bus (int, optional): Width of the upper bus waveguide. Defaults to 0. R1_cp (_type_, optional): Radius of the bus 1, used in asymmetric coupling. Defaults to None. R2_cp (_type_, optional): Radius of the bus 1, used in asymmetric coupling. Defaults to None. A1_cp (int, optional): Coupling angle for lower bus. Defaults to 0. A2_cp (int, optional): Coupling angle for upper bus. Defaults to 0. offset_X (int, optional): Inner and outer circle offset in X. Defaults to 0. offset_Y (int, optional): Inner and outer circle offset in Y. Defaults to 0. w_wg (float, optional): Width of the input and output waveguide. Defaults to 0.45. R1_att (float, optional): Attached bend radius of the IO port, lower waveguide. Defaults to 20. R2_att (float, optional): Attached bend radius of the IO port, lower waveguide. Defaults to 20. R2_att_min (float, optional): Attached minimum bend radius of the IO port, lower waveguide. Defaults to 10. R1_att_min (float, optional): _description_. Defaults to 10. A1_att (float, optional): _description_. Defaults to 30. A2_att (float, optional): _description_. Defaults to 20. Ltp_bus (int, optional): _description_. Defaults to 10. dL_p2p (_type_, optional): _description_. Defaults to None. L_tilt (int, optional): _description_. Defaults to 10. xs_ring (str, optional): _description_. Defaults to 'strip'. sharp_patch (bool, optional): _description_. Defaults to True. Euler_trasition (bool, optional): _description_. Defaults to False. show_pins (bool, optional): _description_. Defaults to False. xs_heater (str, optional): _description_. Defaults to 'heater'. w_heater (float, optional): _description_. Defaults to 0. xs_metal (str, optional): _description_. Defaults to 'metal'. w_metal (float, optional): _description_. Defaults to 0. via_h2m (_type_, optional): _description_. Defaults to None. isl (_type_, optional): _description_. Defaults to None. A_ht (float, optional): _description_. Defaults to 270. ht_notch_dual (bool, optional): _description_. Defaults to True. epin_dX (int, optional): _description_. Defaults to 0. epin_dY (int, optional): _description_. Defaults to 0. res (float, optional): _description_. Defaults to 0.05. res_eic (float, optional): _description_. Defaults to 0.5. """ ORx = r_ring + w_ring/2 ORy = r_ring + w_ring/2 IRx = r_ring - w_ring/2 IRy = r_ring - w_ring/2 self.r_ring = r_ring self.w_ring = w_ring 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_ring, sharp_patch, cell_xs_transition, Euler_trasition, show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m=via_h2m, isl=isl, A_ht=A_ht, ht_notch_dual=ht_notch_dual, epin_dX=epin_dX, epin_dY=epin_dY, res=res,res_eic=res_eic,ht_rot=ht_rot) """ Simplified classed for Standard ring """ class MRR_STD_Allpass(MRR_STD_Ring): """ MRR STD Allpass primitive component. This component builds the MRR STD Allpass layout cell. Parameters ---------- name : str Unique identifier for the device cell. r_ring : float Radius parameter in microns. w_ring : float Width parameter in microns. gap : float Spacing or gap parameter in microns. w_bus : float Width parameter in microns. A_cp : Any Angle parameter in degrees. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. 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_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: str, r_ring: float, w_ring: float, gap: float, w_bus: float, A_cp: Any, w_wg: float=0.45, R_att: float = 20, Ltp_bus: int=10, dL_p2p: Optional[float]=None, L_tilt: int=10, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = 'heater', w_heater: float = 0, xs_metal: str = 'metal', w_metal: float = 0, via_h2m: Any =None, isl: Any =None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float=0.05, res_eic: float=0.5,ht_rot: bool=False) -> None: """_summary_ Args: name (_type_): _description_ r_ring (int, optional): Central radius of the ring . Defaults to 10. w_ring (float, optional): Width of the ring. Defaults to 0.45. gap (float, optional): Gap distnace of the bus waveguide and the ring. Defaults to 0.2. w_bus (float, optional): Width of the bus waveguide. Defaults to 0.45. A_cp (_type_): Angle of coupling, can be 0. w_wg (float, optional): Width of the IO port. Defaults to 0.45. R_att (float, optional): Radius of the anti-direction bend after bend coupling. Defaults to 20. Ltp_bus (int, optional): Length of taper for transision from w_bus to w_wg. Defaults to 10. dL_p2p (_type_, optional): _description_. Defaults to None. L_tilt (int, optional): Length of the tilt transision from bend coupling to anti-direction bend. Defaults to 10. xs_ring (str, optional): xsection of ring. Defaults to 'strip'. sharp_patch (bool, optional): Flag to add sharp angle patch. Defaults to True. show_pins (bool, optional): Flag of displaying pins. Defaults to False. xs_heater (str, optional): Xsection of heaters. Defaults to 'heater'. w_heater (float, optional): Width of the main heater. Defaults to 0. xs_metal (str, optional): Xsection of metal. Defaults to 'metal'. w_metal (float, optional): Width of the output metal width. Defaults to 0. via_h2m (_type_, optional): Class of via from heater to metal. Defaults to None. isl (_type_, optional): Class of isolation trench. Defaults to None. A_ht (float, optional): Angle of heater. Defaults to 270. epin_dX (int, optional): Heater epitaxy in X for connection. Defaults to 0. epin_dY (int, optional): Heater epitaxy in Y for connection. Defaults to 0. res (float, optional): Waveguide resolution. Defaults to 0.05. res_eic (float, optional): Electrical resolution. Defaults to 0.5. """ self.r_ring = r_ring self.w_ring = w_ring super().__init__(name=name, r_ring=r_ring, w_ring=w_ring, gap1=gap, gap2=0, ## coupler 2 not used w1_bus=w_bus, w2_bus=0, ## coupler 2 not used R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=0, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att, R1_att_min=R_att, A1_att=10, A2_att=10, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=L_tilt, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition=cell_xs_transition, Euler_trasition=False, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m = via_h2m, isl = isl, A_ht=A_ht, ht_notch_dual=False, epin_dX=epin_dX, epin_dY=epin_dY, res=res, res_eic=res_eic, ht_rot=ht_rot) class MRR_STD_Adddrop(MRR_STD_Ring): """ MRR STD Adddrop primitive component. This component builds the MRR STD Adddrop layout cell. Parameters ---------- name : str Unique identifier for the device cell. r_ring : float Radius parameter in microns. w_ring : float Width parameter in microns. gap : float Spacing or gap parameter in microns. w_bus : float Width parameter in microns. A_cp : Any Angle parameter in degrees. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. 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_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: str, r_ring: float, w_ring: float, gap: float, w_bus: float, A_cp: Any, w_wg: float=0.45, R_att: float = 20, Ltp_bus: int=10, dL_p2p: Optional[float]=None, L_tilt: int=10, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = 'heater', w_heater: float = 0, xs_metal: str = 'metal', w_metal: float = 0, via_h2m: Any = None, isl: Any = None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float = 0.05, res_eic: float = 0.5, ht_rot: bool=False) -> None: self.r_ring = r_ring self.w_ring = w_ring super().__init__(name=name, r_ring=r_ring, w_ring=w_ring, gap1=gap, gap2=gap, ## coupler 2 not used w1_bus=w_bus, w2_bus=w_bus, ## coupler 2 not used R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=A_cp, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att, R1_att_min=R_att, A1_att=10, A2_att=10, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=L_tilt, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition=cell_xs_transition, Euler_trasition=False, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m = via_h2m, isl = isl, A_ht=A_ht, ht_notch_dual=False, epin_dX=epin_dX, epin_dY=epin_dY, res=res, res_eic=res_eic, ht_rot=ht_rot) """ Multimode ring simplified """ class MRR_MM_Allpass(MRR_STD_Ring): """ MRR MM Allpass primitive component. This component builds the MRR MM Allpass 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 0.45. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. w_bus : float, optional Width parameter in microns. Default is 0.45. A_cp : int, optional Angle parameter in degrees. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. Default is 20. R_att_min : float, optional Radius parameter in microns. Default is 10. A_att : float, optional Angle parameter in degrees. Default is 30. 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. xs_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: Optional[str]=None, r_ring: float=10, w_ring: float=0.45, gap: float=0.2, w_bus: float=0.45, A_cp: int=0, w_wg: float=0.45, R_att: float = 20, R_att_min: float = 10, A_att: float = 30, Ltp_bus: int=10, dL_p2p: Optional[float]=None, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = 'heater', w_heater: float = 0, xs_metal: str = 'metal', w_metal: float = 0, via_h2m: Any=None, isl: Any=None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float = 0.05, res_eic: float = 0.5, ht_rot: bool=False) -> None: """_summary_ Args: R_att (float, optional): The attached radius for the anti-attaching. Defaults to 20. R_att_min (float, optional): The minimum radius for the angti-attaching, due to euler transision. Defaults to 10. A_att (float, optional): The angle of the Euler transision. Defaults to 30. """ self.r_ring = r_ring self.w_ring = w_ring super().__init__(name, r_ring=r_ring, w_ring=w_ring, gap1=gap, gap2=0, w1_bus=w_bus, w2_bus=0, R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=0, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att_min, R1_att_min=R_att_min, A1_att=A_att, A2_att=A_att, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=0, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition = cell_xs_transition, Euler_trasition=True, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m=via_h2m, isl=isl, A_ht=A_ht, ht_notch_dual=False, epin_dX=epin_dX, epin_dY=epin_dY,res=res, res_eic=res_eic, ht_rot=ht_rot) class MRR_MM_Adddrop(MRR_STD_Ring): """ MRR MM Adddrop primitive component. This component builds the MRR MM Adddrop 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 0.45. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. w_bus : float, optional Width parameter in microns. Default is 0.45. A_cp : int, optional Angle parameter in degrees. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. Default is 20. R_att_min : float, optional Radius parameter in microns. Default is 10. A_att : float, optional Angle parameter in degrees. Default is 30. 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. xs_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: Optional[str]=None, r_ring: float=10, w_ring: float=0.45, gap: float=0.2, w_bus: float=0.45, A_cp: int=0, w_wg: float=0.45, R_att: float = 20, R_att_min: float = 10, A_att: float = 30, Ltp_bus: int=10, dL_p2p: Optional[float]=None, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = 'heater', w_heater: float = 0, xs_metal: str = 'metal', w_metal: float = 0, via_h2m: Any=None, isl: Any=None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float = 0.05, res_eic: float = 0.5, ht_rot: bool=False) -> None: self.r_ring = r_ring self.w_ring = w_ring super().__init__(name, r_ring=r_ring, w_ring=w_ring, gap1=gap, gap2=gap, w1_bus=w_bus, w2_bus=w_bus, R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=A_cp, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att_min, R1_att_min=R_att_min, A1_att=A_att, A2_att=A_att, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=0, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition = cell_xs_transition, Euler_trasition=True, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m=via_h2m, isl=isl, A_ht=A_ht, ht_notch_dual=True, epin_dX=epin_dX, epin_dY=epin_dY, res=res, res_eic=res_eic, ht_rot=ht_rot) """ Dual width controled ring with Euler shape coupler """ class MRR_DW_Adddrop(MRR_AED): """ MRR DW Adddrop primitive component. This component builds the MRR DW Adddrop layout cell. Parameters ---------- name : str Unique identifier for the device cell. r_ring : float Radius parameter in microns. w0_ring : float Value for the w0_ring parameter. w1_ring : float Value for the w1_ring parameter. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. w_bus : float, optional Width parameter in microns. Default is 0.45. A_cp : int, optional Angle parameter in degrees. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. Default is 20. R_att_min : float, optional Radius parameter in microns. Default is 10. A_att : float, optional Angle parameter in degrees. Default is 30. 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. xs_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: str, r_ring: float, w0_ring: float, w1_ring: float, gap: float=0.2, w_bus: float=0.45, A_cp: int=0, w_wg: float=0.45, R_att: float = 20, R_att_min: float = 10, A_att: float = 30, Ltp_bus: int=10, dL_p2p: Optional[float]=None, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = "heater", w_heater: float = 0, xs_metal: str = "metal", w_metal: float = 0, via_h2m: Any=None, isl: Any=None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float=0.05, res_eic: float=0.5, ht_rot: bool=False) -> None: ORx = r_ring + w1_ring/2 ORy = r_ring + w0_ring/2 IRx = r_ring - w1_ring/2 IRy = r_ring - w0_ring/2 self.r_ring = r_ring self.w0_ring = w0_ring self.w1_ring = w1_ring super().__init__(name, ORx=ORx, ORy=ORy, IRx=IRx, IRy=IRy, gap1=gap, gap2=gap, w1_bus=w_bus, w2_bus=w_bus, R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=A_cp, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att_min, R1_att_min=R_att_min, A1_att=A_att, A2_att=A_att, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=0, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition = cell_xs_transition, Euler_trasition=True, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m=via_h2m, isl=isl, A_ht=A_ht, ht_notch_dual=False, epin_dX=epin_dX, epin_dY=epin_dY, res=res, res_eic=res_eic,ht_rot=ht_rot) class MRR_DW_Allpass(MRR_AED): """ MRR DW Allpass primitive component. This component builds the MRR DW Allpass layout cell. Parameters ---------- name : str Unique identifier for the device cell. r_ring : float Radius parameter in microns. w0_ring : float Value for the w0_ring parameter. w1_ring : float Value for the w1_ring parameter. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. w_bus : float, optional Width parameter in microns. Default is 0.45. A_cp : int, optional Angle parameter in degrees. Default is 0. w_wg : float, optional Width parameter in microns. Default is 0.45. R_att : float, optional Radius parameter in microns. Default is 20. R_att_min : float, optional Radius parameter in microns. Default is 10. A_att : float, optional Angle parameter in degrees. Default is 30. 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. xs_ring : 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. show_pins : bool, optional Whether to draw pin markers in the generated layout. Default is False. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_heater : float, optional Width parameter in microns. Default is 0. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. w_metal : float, optional Width parameter in microns. Default is 0. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. A_ht : float, optional Angle parameter in degrees. Default is 270. epin_dX : int, optional Value for the epin_dX parameter. Default is 0. epin_dY : int, optional Value for the epin_dY parameter. Default is 0. res : float, optional Value for the res parameter. Default is 0.05. res_eic : float, optional Value for the res_eic parameter. Default is 0.5. ht_rot : bool, optional Value for the ht_rot parameter. Default is False. """ def __init__(self, name: str, r_ring: float, w0_ring: float, w1_ring: float, gap: float=0.2, w_bus: float=0.45, A_cp: int=0, w_wg: float=0.45, R_att: float = 20, R_att_min: float = 10, A_att: float = 30, Ltp_bus: int=10, dL_p2p: Optional[float]=None, xs_ring: str='strip', sharp_patch: bool=True, cell_xs_transition: Any=None, show_pins: bool=False, xs_heater: str = "heater", w_heater: float = 0, xs_metal: str = "metal", w_metal: float = 0, via_h2m: Any=None, isl: Any=None, A_ht: float = 270, epin_dX: int=0, epin_dY: int=0, res: float=0.05, res_eic: float=0.5, ht_rot: bool=False) -> None: ORx = r_ring + w1_ring/2 ORy = r_ring + w0_ring/2 IRx = r_ring - w1_ring/2 IRy = r_ring - w0_ring/2 self.r_ring = r_ring self.w0_ring = w0_ring self.w1_ring = w1_ring super().__init__(name, ORx=ORx, ORy=ORy, IRx=IRx, IRy=IRy, gap1=gap, gap2=0, w1_bus=w_bus, w2_bus=0, R1_cp=None, R2_cp=None, A1_cp=A_cp, A2_cp=A_cp, offset_X=0, offset_Y=0, w_wg=w_wg, R1_att=R_att, R2_att=R_att, R2_att_min=R_att_min, R1_att_min=R_att_min, A1_att=A_att, A2_att=A_att, Ltp_bus=Ltp_bus, dL_p2p=dL_p2p, L_tilt=0, xs_ring=xs_ring, sharp_patch=sharp_patch, cell_xs_transition = cell_xs_transition, Euler_trasition=True, show_pins=show_pins, xs_heater=xs_heater, w_heater=w_heater, xs_metal=xs_metal, w_metal=w_metal, via_h2m=via_h2m, isl=isl, A_ht=A_ht, ht_notch_dual=False, epin_dX=epin_dX, epin_dY=epin_dY, res=res, res_eic=res_eic,ht_rot=ht_rot) """ NEW CLASS: 2023.03.21""" class STD_ring_AMZI_adddrop: """ STD ring AMZI adddrop primitive component. This component builds the STD ring AMZI adddrop 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 30. w_ring : float, optional Width parameter in microns. Default is 0.45. w_wg : float, optional Width parameter in microns. Default is 0.45. xs_wg : str, optional Layer or cross-section name used by the device. Default is 'strip'. w_bus : float, optional Width parameter in microns. Default is 0.45. gap : float, optional Spacing or gap parameter in microns. Default is 0.2. dAc : float, optional Value for the dAc parameter. Default is 10. L_tilt : int, optional Length parameter in microns. Default is 1. dL_arm : float, optional Value for the dL_arm parameter. Default is 10. w_heater : float, optional Width parameter in microns. Default is 2.5. xs_heater : str, optional Layer or cross-section name used by the device. Default is 'heater'. w_metal : float, optional Width parameter in microns. Default is 6. xs_metal : str, optional Layer or cross-section name used by the device. Default is 'metal'. via_h2m : Any, optional Via definition used between heater and metal layers. Default is None. isl : Any, optional Isolation-trench definition used by the electrical layout. Default is None. res : float, optional Value for the res parameter. Default is 0.1. R_bend : int, optional Radius parameter in microns. Default is 10. L_tp : int, optional Length parameter in microns. Default is 5. 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: Optional[str]=None, r_ring: float = 30, w_ring: float = 0.45, w_wg: float = 0.45, xs_wg: str='strip', w_bus: float=0.45, gap: float=0.2, dAc: float=10, L_tilt: int=1, dL_arm: float=10, w_heater: float=2.5, xs_heater: str='heater', w_metal: float=6, xs_metal: str='metal', via_h2m: Any = None, isl: Any = None, res: float = 0.1, R_bend: int=10, L_tp: int = 5, show_pins: bool=False, sharp_patch: bool=True, ) -> None: self.name=name if (name==None): self.instantiate = False else : self.instantiate = True self.r_ring = r_ring self.w_ring = w_ring self.w_wg = w_wg self.xs_wg = xs_wg self.w_bus = w_bus self.gap = gap self.dAc = dAc self.L_tilt = L_tilt self.dL_arm = dL_arm self.w_heater = w_heater self.xs_heater = xs_heater self.w_metal = w_metal self.xs_metal = xs_metal self.via_h2m = via_h2m self.isl = isl self.res = res self.R_bend=R_bend self.L_tp = L_tp self.cell = self.generate_gds(sharp_patch=sharp_patch,show_pins=show_pins) def generate_gds(self,show_pins,sharp_patch): with nd.Cell(name=self.name,instantiate=self.instantiate) as C: R_cp=self.r_ring+self.gap+self.w_bus/2+self.w_ring/2 circle(radius=self.r_ring,width=self.w_ring,theta_start=0,theta_stop=360,res=self.res,xs=self.xs_wg,sharp_patch=sharp_patch).cell.put(0,0,0) wg_cp = ring_bus_wg(xs=self.xs_wg,R_cp=R_cp, w_bus=self.w_bus,bend_DC=True,w_wg=self.w_wg, dAc=self.dAc, euler_anti_bend=False, euler_transistion=False, dL_trans=self.L_tilt,res=self.res, R_max_anti=self.R_bend,R_min_anti=self.R_bend, A_anti=self.dAc/2+45,wg_Ltp=self.L_tp,dL_p2p=None,sharp_patch=sharp_patch) cp_U = wg_cp.cell.put(0,R_cp,0,flip=1) cp_D = wg_cp.cell.put(0,-R_cp,0) cp_L = wg_cp.cell.put(-R_cp,0,-90) cp_R = wg_cp.cell.put( R_cp,0,-90,flip=1) pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs=self.xs_wg) ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a2',pin=cp_D.pin['a1']).put() nd.Pin(name='opt_a2',pin=cp_D.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='a1',pin=cp_L.pin['b1']).put() nd.Pin(name='opt_a1',pin=cp_L.pin['opt_b1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b2',pin=cp_R.pin['a1']).put() nd.Pin(name='opt_b2',pin=cp_R.pin['opt_a1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: nd.Pin(name='b1',pin=cp_U.pin['b1']).put() nd.Pin(name='opt_b1',pin=cp_U.pin['opt_b1'],type="optical:").put() ## revised in 2026.06.07 by Qin Yue # legacy: x_BOX = abs(cp_D.pin['a1'].x - cp_R.pin['a1'].x)*np.sqrt(2) x_BOX = abs(cp_D.pin['opt_a1'].x - cp_R.pin['opt_a1'].x)*np.sqrt(2) ## revised in 2026.06.07 by Qin Yue # legacy: y_BOX = abs(cp_D.pin['a1'].y - cp_R.pin['a1'].y)*np.sqrt(2) y_BOX = abs(cp_D.pin['opt_a1'].y - cp_R.pin['opt_a1'].y)*np.sqrt(2) if (sharp_patch): nd.strt(layer="STRIP_CLD",length=x_BOX/2,width=y_BOX).put(0,0,45) nd.strt(layer="STRIP_CLD",length=x_BOX/2,width=y_BOX).put(0,0,225) """ Generating of EIC parts """ if (self.via_h2m==None): via = Vias(xs=None,area=self.w_metal,sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal).cell ## only putting metal blocks else: if (hasattr(self.via_h2m,"cell")): via = self.via_h2m.cell else : via = Vias(xs=self.via_h2m.xs,area=self.w_metal,sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,xs_l1=self.xs_heater,xs_l2=self.xs_metal).cell ## placing vias eic_dL = 0.5 HT_UR = circle(radius=self.r_ring,width=self.w_heater,xs=self.xs_heater,theta_stop=90,res=eic_dL).cell.put(0,0,0) HT_DL = circle(radius=self.r_ring,width=self.w_heater,xs=self.xs_heater,theta_stop=90,res=eic_dL).cell.put(0,0,180) # via = Vias(xs=self.xs_via_h2m,xs_l1=self.xs_metal,xs_l2=self.xs_heater,sz=self.sz_via_h2m,spacing=self.sp_via_h2m,area=self.w_metal).cell ## revised in 2026.06.07 by Qin Yue # legacy: V1_UR = via.put(HT_UR.pin['a1'].x,HT_UR.pin['a1'].y,45) V1_UR = via.put(HT_UR.pin['opt_a1'].x,HT_UR.pin['opt_a1'].y,45) ## revised in 2026.06.07 by Qin Yue # legacy: V2_UR = via.put(HT_UR.pin['b1'].x,HT_UR.pin['b1'].y,45) V2_UR = via.put(HT_UR.pin['opt_b1'].x,HT_UR.pin['opt_b1'].y,45) ## revised in 2026.06.07 by Qin Yue # legacy: V1_DL = via.put(HT_DL.pin['a1'].x,HT_DL.pin['a1'].y,45) V1_DL = via.put(HT_DL.pin['opt_a1'].x,HT_DL.pin['opt_a1'].y,45) ## revised in 2026.06.07 by Qin Yue # legacy: V2_DL = via.put(HT_DL.pin['b1'].x,HT_DL.pin['b1'].y,45) V2_DL = via.put(HT_DL.pin['opt_b1'].x,HT_DL.pin['opt_b1'].y,45) eic_m = Route(radius=self.w_metal/2,width=self.w_metal,xs=self.xs_metal,PCB=True) eic_m.sbend_p2p(pin1=V2_UR.pin['a0'],pin2=V2_DL.pin['a0'].move(0,0,180),Lstart=abs(V2_UR.pin['a0'].y-V2_DL.pin['a0'].y)/np.sqrt(2)/2 - self.w_metal/2).put() nd.Pin(name='en1',pin=V1_UR.pin['b0']).put() nd.Pin(name='ep1',pin=V1_DL.pin['a0']).put() """ Generating the heaters for MZI part """ ## revised in 2026.06.07 by Qin Yue # legacy: uoffst = np.sqrt(abs(cp_U.pin['a1'].x - cp_L.pin['a1'].x)**2 + abs(cp_U.pin['a1'].y - cp_L.pin['a1'].y)**2) uoffst = np.sqrt(abs(cp_U.pin['opt_a1'].x - cp_L.pin['opt_a1'].x)**2 + abs(cp_U.pin['opt_a1'].y - cp_L.pin['opt_a1'].y)**2) HT = waveguide(w_heater=self.w_heater,xs_heater=self.xs_heater, via_h2m=self.via_h2m, isl = self.isl, shape='ubend', w_metal=self.w_metal,xs_metal=self.xs_metal, R_bend=self.R_bend, ubend_offset=uoffst, L_wg=self.dL_arm+uoffst+(np.pi-2)*self.R_bend, L_heater=uoffst+(np.pi-2)*self.R_bend+self.dL_arm/2) ## revised in 2026.06.07 by Qin Yue # legacy: HT_U = HT.cell.put('a1',cp_L.pin['a1']) HT_U = HT.cell.put('opt_a1',cp_L.pin['opt_a1']) ## revised in 2026.06.07 by Qin Yue # legacy: HT_D = HT.cell.put('a1',cp_R.pin['b1']) HT_D = HT.cell.put('opt_a1',cp_R.pin['opt_b1']) nd.Pin(name='ep2',pin=HT_U.pin['ep1'].move(0,0,90)).put() nd.Pin(name='en2',pin=HT_U.pin['en1'].move(0,0,-90)).put() nd.Pin(name='en3',pin=HT_D.pin['ep1'].move(0,0,90)).put() nd.Pin(name='ep3',pin=HT_D.pin['en1'].move(0,0,-90)).put() if (show_pins): nd.put_stub() return C