Files
mxpic_forge/mxpic/components/primitives/pic/rings.py
T
2026-06-06 16:43:26 +08:00

429 lines
17 KiB
Python

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