Files
mxpic_forge/mxpic/components/primitives/passive/rings.py
T
2026-06-04 23:21:39 +08:00

1549 lines
68 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Any, Optional
import nazca as nd
import numpy as np
from ...basic import __xs_exist__
from ...structures import *
import nazca.interconnects as IC
class Route(IC.Interconnect):
pass
# from ...routing import *
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.
'''
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
nd.Pin(name="a1", width=self.w_wg).put(wg_in.pin['b0'])
nd.Pin(name="b1", width=self.w_wg).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
nd.Pin(name="a1", width=self.w_wg).put(pic.pin['a1'])
nd.Pin(name="b1", width=self.w_wg).put(pic.pin['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:
socr_1 = self.cell.put('a1',0,0,0)
socr_2 = self.cell.put('a1', socr_1.pin['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(
pin1=socr_1.pin['b1'].move(-0.1,0,0),
pin2=socr_2.pin['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()
nd.Pin(name='a1', width=self.w_wg).put(socr_1.pin['a1'])
nd.Pin(name='b1', width=self.w_wg).put(socr_2.pin['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)
mmi_input = mmi.cell.put('a1', 20, 0, 0)
stripe = Route(xs='strip', width=gc.w_wg, radius=5)
# Connect input gc with first mmi
stripe.strt_p2p(pin1=gc_input.pin['g1'], pin2=mmi_input.pin['a1'], arrow=False).put()
# Connect the upper arm
self.length = self.cell.pin['b1'].x - self.cell.pin['a1'].x
socr_cell = []
for _index_ in range(num_socr):
cell = self.cell.put('a1', mmi_input.pin['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(
pin1=socr_cell[_index_-1].pin['b1'].move(-0.1,0,0),
pin2=socr_cell[_index_].pin['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(
pin1=mmi_input.pin['b1'],
pin2=socr_cell[0].pin['a1'].move(-0.1,0,0),
Lstart=1,
arrow=False).put()
mmi_output_up = mmi.cell.put('a1', socr_cell[-1].pin['b1'].x+30, socr_cell[-1].pin['b1'].y, 0)
stripe.strt_p2p(
pin1=mmi_output_up.pin['a1'],
pin2=socr_cell[-1].pin['b1'].move(-0.1,0,0),
arrow=False).put()
gc_output_up = gc.cell.put('g1', gc2gc_length, mmi_output_up.pin['b1'].y, 0)
stripe.strt_p2p(
pin1=mmi_output_up.pin['b1'],
pin2=gc_output_up.pin['g1'],
arrow=False).put()
# Connect the below arm
mmi_output_below = mmi.cell.put('a1', socr_cell[-1].pin['b1'].x+30, -socr_cell[-1].pin['b1'].y, 0)
stripe.sbend_p2p(
pin1=mmi_input.pin['b2'],
pin2=mmi_output_below.pin['a1'],
Lstart=1,
arrow=False).put()
gc_output_below = gc.cell.put('g1', gc2gc_length, mmi_output_below.pin['b2'].y, 0)
stripe.strt_p2p(
pin1=mmi_output_below.pin['b2'],
pin2=gc_output_below.pin['g1'],
arrow=False).put()
# Combine upper and below arm together to do the coherence detection
mmi_output_mid = mmi.cell.put('a1', mmi_output_below.pin['b1'].x+mmi.length+20, gc_output_mid.pin['g1'].y, 180)
stripe.strt_p2p(
pin1=mmi_output_mid.pin['a1'],
pin2=gc_output_mid.pin['g1'],
arrow=False).put()
stripe.sbend_p2p(
pin1=mmi_output_up.pin['b2'],
pin2=mmi_output_mid.pin['b2'],
Lstart=1,
arrow=False).put()
stripe.sbend_p2p(
pin1=mmi_output_below.pin['b1'],
pin2=mmi_output_mid.pin['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
self.length = self.cell.pin['b1'].x - self.cell.pin['a1'].x
socr_cell = []
for _index_ in range(num_socr):
cell = self.cell.put('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(
pin1=socr_cell[_index_-1].pin['b1'].move(-0.1,0,0),
pin2=socr_cell[_index_].pin['a1'].move(-0.1,0,0),
arrow=False).put()
if num_socr>=1:
stripe.strt_p2p(
pin1=gc_input.pin['g1'],
pin2=socr_cell[0].pin['a1'].move(-0.1,0,0),
arrow=False).put()
stripe.strt_p2p(
pin1=gc_output.pin['g1'],
pin2=socr_cell[-1].pin['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.
'''
def __init__(self, w_wg : float = 0.45, 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,
show_pins=show_pins
)
class SOCR_Adiabatic(SOCR):
'''
This is the class for adiabatic Strong Over Coupled Ring(SOCR) for phase modulation.
See detail in:
[1] Liang, G., Huang, H., Mohanty, A. et al. Robust, efficient, micrometre-scale phase modulators at visible wavelengths. Nat. Photon. 15, 908913 (2021).
'''
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
nd.Pin(name="a1", width=self.w_wg).put(wg_in.pin['b0'])
nd.Pin(name="b1", width=self.w_wg).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.
Args:
- Gap: 200nm
- w_wg: 450nm
- w_in: 510nm
- w_out: 2um
- gap: 200nm
- theta_arc: 120degree
- Ls: 1um
'''
def __init__(self, w_wg : float=0.45, 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,
show_pins=show_pins
)
class MRR_AED(AED_ring):
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):
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
else:
L_isl = np.abs(self.ORy - self.cell_pic.pin['a1'].y) - self.isl.sp_isl_wg - self.w_wg/2
y_isl = self.cell_pic.pin['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) :
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):
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):
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):
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):
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):
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):
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:
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)
nd.Pin(name='a2',pin=cp_D.pin['a1']).put()
nd.Pin(name='a1',pin=cp_L.pin['b1']).put()
nd.Pin(name='b2',pin=cp_R.pin['a1']).put()
nd.Pin(name='b1',pin=cp_U.pin['b1']).put()
x_BOX = abs(cp_D.pin['a1'].x - cp_R.pin['a1'].x)*np.sqrt(2)
y_BOX = abs(cp_D.pin['a1'].y - cp_R.pin['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
V1_UR = via.put(HT_UR.pin['a1'].x,HT_UR.pin['a1'].y,45)
V2_UR = via.put(HT_UR.pin['b1'].x,HT_UR.pin['b1'].y,45)
V1_DL = via.put(HT_DL.pin['a1'].x,HT_DL.pin['a1'].y,45)
V2_DL = via.put(HT_DL.pin['b1'].x,HT_DL.pin['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 """
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)
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)
HT_U = HT.cell.put('a1',cp_L.pin['a1'])
HT_D = HT.cell.put('a1',cp_R.pin['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