Files

2360 lines
104 KiB
Python

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::<xs_via_h2m>::",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::<xs_via_h2m>::",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