531 lines
26 KiB
Python
531 lines
26 KiB
Python
"""Circular, elliptical, and bend geometry primitives."""
|
|
|
|
from typing import Any, Optional
|
|
from cmath import pi
|
|
|
|
import nazca as nd
|
|
import numpy as np
|
|
|
|
from .polygons import _my_polygon
|
|
|
|
class circle :
|
|
'''
|
|
# =================================================================
|
|
# @ File : <mx_frame_lib.py>
|
|
# @ structure: circle ring or disk
|
|
# @ Args : * radius : center radius of the ring
|
|
# : * width : width of the ring
|
|
# : * theta_start : start end of the ring, range [0~360], can be negative
|
|
# : * theta_stop : stop end of the ring, range [0~360], can be negative
|
|
# : * n_points : resolution of the polygon
|
|
# : * xs : placing layer
|
|
# @ located in the center of the ring
|
|
# =================================================================
|
|
'''
|
|
def __init__(self,radius: float = 10, width: float = 0.45, theta_start: float=0, theta_stop: float=360,res: float=0.05,angle: Optional[float]=None,
|
|
# n_points = 64,
|
|
xs: str='strip',layer: Optional[str]=None,sharp_patch: bool=True,
|
|
y_cut: Optional[float] = None) -> None:
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
if (angle!=None):
|
|
theta_start = 0
|
|
theta_stop = angle
|
|
|
|
if (res!=None):
|
|
n_points = int(np.floor(abs(theta_start-theta_stop)*radius*np.pi/180/res)+1)
|
|
|
|
if (layer==None):
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
theta = np.linspace(theta_start,theta_stop,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(radius+width*a1 + b1)
|
|
vtx_outer_y = np.sin(theta)*(radius+width*a1 + b1)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
## used for 360 degree
|
|
if (radius+width*a2+b1>0.0000001 or np.abs(theta_stop-theta_start)<360):
|
|
vtx_inner_x = np.cos(theta)*(radius+width*a2 + b2)
|
|
vtx_inner_y = np.sin(theta)*(radius+width*a2 + b2)
|
|
vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)]
|
|
|
|
vtx = np.r_[vtx_outer,vtx_inner]
|
|
else :
|
|
vtx = vtx_outer
|
|
|
|
""" add in 2023.09.20 """
|
|
vtx_y = vtx[:,1]
|
|
vtx_x = vtx[:,0]
|
|
|
|
vtx_cut = None
|
|
if (y_cut!=None):
|
|
if (y_cut> min(vtx_y)):
|
|
x_cut_max = max(vtx_x)
|
|
x_cut_min = min(vtx_x)
|
|
|
|
y_cut_max = y_cut
|
|
y_cut_min = min(vtx_y)-1
|
|
|
|
vtx_x_cut = np.array([x_cut_max,x_cut_max,x_cut_min,x_cut_min])
|
|
vtx_y_cut = np.array([y_cut_max,y_cut_min,y_cut_min,y_cut_max])
|
|
|
|
vtx_cut = np.c_[vtx_x_cut,vtx_y_cut]
|
|
|
|
|
|
""" """
|
|
|
|
if (sharp_patch==True and b2!=0 and b1!=0):
|
|
L_patch = max([max(vtx_outer_x),max(vtx_inner_x)])-min([min(vtx_outer_x),min(vtx_inner_x)])
|
|
X_patch = 1/2*(max([max(vtx_outer_x),max(vtx_inner_x)])+min([min(vtx_outer_x),min(vtx_inner_x)]))
|
|
W_patch = (max([max(vtx_outer_y),max(vtx_inner_y)])-min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
Y_patch = 1/2*(max([max(vtx_outer_y),max(vtx_inner_y)])+min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
|
|
nd.strt(length=L_patch,width=W_patch,layer=layers).put(X_patch-L_patch/2,Y_patch,0)
|
|
|
|
else:
|
|
_my_polygon(layer_wg=layers,vtx=vtx,vtx_not=vtx_cut).put(0,0,0)
|
|
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=width,xs=xs).put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
nd.Pin(name='opt_a1',width=width,xs=xs,type="optical:").put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=width,xs=xs).put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
nd.Pin(name='opt_b1',width=width,xs=xs,type="optical:").put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
|
|
else:
|
|
theta = np.linspace(theta_start,theta_stop,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(radius+width/2)
|
|
vtx_outer_y = np.sin(theta)*(radius+width/2)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
if (radius-width/2>0.0000001 or np.abs(theta_stop-theta_start)<360):
|
|
vtx_inner_x = np.cos(theta)*(radius-width/2)
|
|
vtx_inner_y = np.sin(theta)*(radius-width/2)
|
|
vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)]
|
|
|
|
vtx = np.r_[vtx_outer,vtx_inner]
|
|
else :
|
|
vtx = vtx_outer
|
|
|
|
""" add in 2023.09.20 """
|
|
vtx_y = vtx[:, 1]
|
|
vtx_x = vtx[:, 0]
|
|
|
|
vtx_cut = None
|
|
if (y_cut!=None):
|
|
if (y_cut> min(vtx_y)):
|
|
x_cut_max = max(vtx_x)
|
|
x_cut_min = min(vtx_x)
|
|
|
|
y_cut_max = y_cut
|
|
y_cut_min = min(vtx_y)-1
|
|
|
|
vtx_x_cut = np.array([x_cut_max,x_cut_max,x_cut_min,x_cut_min])
|
|
vtx_y_cut = np.array([y_cut_max,y_cut_min,y_cut_min,y_cut_max])
|
|
|
|
vtx_cut = np.c_[vtx_x_cut,vtx_y_cut]
|
|
|
|
""" """
|
|
|
|
_my_polygon(layer_wg=layer,vtx=vtx,vtx_not=vtx_cut).put(0,0,0)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=width,layer=layer).put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
nd.Pin(name='opt_a1',width=width,layer=layer,type="optical:").put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=width,layer=layer).put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
nd.Pin(name='opt_b1',width=width,layer=layer,type="optical:").put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
|
|
self.vtx = vtx
|
|
self.sz = [radius*2,radius*2]
|
|
self.w = [width,width]
|
|
self.cell = C
|
|
|
|
class mx_bend :
|
|
def __init__(self,radius: float = 10, width: float = 0.45, theta_start: float=0, theta_stop: float=360,res: float=0.05,angle: Optional[float]=None,
|
|
# n_points = 64,
|
|
xs: str='strip',layer: Optional[str]=None,sharp_patch: bool=True) -> None:
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
if (angle!=None):
|
|
theta_start = 0
|
|
theta_stop = angle
|
|
if (res!=None):
|
|
n_points = int(np.floor(abs(theta_start-theta_stop)*radius/180*np.pi/res)+1)
|
|
|
|
if (layer==None):
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
theta = np.linspace(theta_start,theta_stop,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(radius+width*a1 + b1)
|
|
vtx_outer_y = np.sin(theta)*(radius+width*a1 + b1)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
if (radius+width*a2+b1>0.0000001 or np.abs(theta_stop-theta_start)<360):
|
|
vtx_inner_x = np.cos(theta)*(radius+width*a2 + b2)
|
|
vtx_inner_y = np.sin(theta)*(radius+width*a2 + b2)
|
|
vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)]
|
|
|
|
vtx = np.r_[vtx_outer,vtx_inner]
|
|
else :
|
|
vtx = vtx_outer
|
|
if (sharp_patch==True and b2!=0 and b1!=0):
|
|
L_patch = max([max(vtx_outer_x),max(vtx_inner_x)])-min([min(vtx_outer_x),min(vtx_inner_x)])
|
|
X_patch = 1/2*(max([max(vtx_outer_x),max(vtx_inner_x)])+min([min(vtx_outer_x),min(vtx_inner_x)]))
|
|
W_patch = (max([max(vtx_outer_y),max(vtx_inner_y)])-min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
Y_patch = 1/2*(max([max(vtx_outer_y),max(vtx_inner_y)])+min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
|
|
nd.strt(length=L_patch,width=W_patch,layer=layers).put(X_patch-L_patch/2,Y_patch,0)
|
|
|
|
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
nd.Pin(name='a0',width=width,xs=xs).put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
nd.Pin(name='b0',width=width,xs=xs).put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
|
|
else:
|
|
theta = np.linspace(theta_start,theta_stop,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(radius+width/2)
|
|
vtx_outer_y = np.sin(theta)*(radius+width/2)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
if (radius-width/2>0.0000001 or np.abs(theta_stop-theta_start)<360):
|
|
vtx_inner_x = np.cos(theta)*(radius-width/2)
|
|
vtx_inner_y = np.sin(theta)*(radius-width/2)
|
|
vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)]
|
|
|
|
vtx = np.r_[vtx_outer,vtx_inner]
|
|
else :
|
|
vtx = vtx_outer
|
|
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,0)
|
|
nd.Pin(name='a0',width=width,layer=layer).put(radius*np.cos(theta_start/180*np.pi),radius*np.sin(theta_start/180*np.pi),theta_start-90)
|
|
nd.Pin(name='b0',width=width,layer=layer).put(radius*np.cos(theta_stop/180*np.pi),radius*np.sin(theta_stop/180*np.pi),theta_stop+90)
|
|
self.sz = [radius*2,radius*2]
|
|
self.w = [width,width]
|
|
self.cell = C
|
|
|
|
class Elipse_dual :
|
|
def __init__(self,
|
|
ORx : float ,
|
|
ORy : float ,
|
|
IRx : float ,
|
|
IRy : float ,
|
|
offset_X : float = 0,
|
|
offset_Y : float = 0,
|
|
xs : Optional[str] = None,
|
|
layer : Optional[str] = None,
|
|
theta_start : float = 0,
|
|
theta_stop : float = 360,
|
|
sharp_patch : bool = True,
|
|
# n_points : int = 1024,
|
|
res : float = 0.001,
|
|
y_cut: Optional[float]=None) -> None:
|
|
"""_summary_
|
|
|
|
Args:
|
|
ORx (float): Outer semi X-axis length
|
|
ORy (float): Outer semi Y-axis length
|
|
IRx (float): Inner semi X-axis length
|
|
IRy (float): Inner semi Y-axis length
|
|
offset_X (float, optional): Outer and Inner elipse offset in X. Defaults to 0.
|
|
offset_Y (float, optional): Outer and Inner elipse offset in Y. Defaults to 0.
|
|
xs (str, optional): xsection. Defaults to None.
|
|
layer (str, optional): layer. Defaults to None.
|
|
theta_start (str, optional): X-axis positvive starts at 0, rotation anti-clockwise . Defaults to 0.
|
|
theta_stop (str, optional): X-axis positvive starts at 0, rotation anti-clockwise. Defaults to 360.
|
|
sharp_patch (bool, optional): sharp patch. Defaults to True.
|
|
n_points (int, optional): points of the ring. Defaults to 1024.
|
|
"""
|
|
self.ORx = ORx
|
|
self.ORy = ORy
|
|
self.IRx = IRx
|
|
self.IRy = IRy
|
|
self.offset_X = offset_X
|
|
self.offset_Y = offset_Y
|
|
self.xs = xs
|
|
self.layer = layer
|
|
self.res = res
|
|
# self.n_points = int(n_points) ## Force type fixing
|
|
self.theta_start = theta_start
|
|
self.theta_stop = theta_stop
|
|
self.y_cut = y_cut
|
|
|
|
self.cell = self.generate_gds(sharp_patch=sharp_patch)
|
|
self.wa = ORx-IRx
|
|
self.wb = ORy-IRy
|
|
|
|
def generate_gds(self,sharp_patch):
|
|
with nd.Cell(instantiate=False) as C:
|
|
if (self.layer==None and self.xs!=None):
|
|
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
""" Calculating points inside the ring """
|
|
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)
|
|
n_points = int(_L_perimeter_/self.res)
|
|
|
|
n_points = int(n_points/360*abs(self.theta_start-self.theta_stop)) ## modified the points by the angle of ring
|
|
|
|
theta = np.linspace(self.theta_start,self.theta_stop,n_points)
|
|
|
|
Ox = (self.ORx + b1)*np.cos(theta/180*pi)
|
|
Oy = (self.ORy + b1)*np.sin(theta/180*pi)
|
|
|
|
Ix = (self.IRx + b2)*np.cos(theta/180*pi)+self.offset_X
|
|
Iy = (self.IRy + b2)*np.sin(theta/180*pi)+self.offset_Y
|
|
|
|
dX = np.max([np.max(Ox),np.max(Ix)]) - np.min([np.min(Ox),np.min(Ix)])
|
|
dY = np.max([np.max(Oy),np.max(Iy)]) - np.min([np.min(Oy),np.min(Iy)])
|
|
|
|
X = np.max([np.max(Ox),np.max(Ix)])/2 + np.min([np.min(Ox),np.min(Ix)])/2
|
|
Y = np.max([np.max(Oy),np.max(Iy)])/2 + np.min([np.min(Oy),np.min(Iy)])/2
|
|
|
|
cx = Ox/2+Ix/2
|
|
cy = Oy/2+Iy/2
|
|
|
|
LX = np.max(cx) - np.min(cx)
|
|
LY = np.max(cy) - np.min(cy)
|
|
|
|
self.sz = [LX,LY]
|
|
vtx_out = np.c_[Ox,Oy]
|
|
vtx_In = np.c_[np.flip(Ix),np.flip(Iy)]
|
|
vtx = np.r_[vtx_out,vtx_In]
|
|
|
|
""" add in 2023.09.20 """
|
|
vtx_y = vtx[:,1]
|
|
vtx_x = vtx[:,0]
|
|
|
|
vtx_cut = None
|
|
if (self.y_cut!=None):
|
|
if (self.y_cut> min(vtx_y)):
|
|
x_cut_max = max(vtx_x)
|
|
x_cut_min = min(vtx_x)
|
|
|
|
y_cut_max = self.y_cut
|
|
y_cut_min = min(vtx_y)-1
|
|
|
|
vtx_x_cut = np.array([x_cut_max,x_cut_max,x_cut_min,x_cut_min])
|
|
vtx_y_cut = np.array([y_cut_max,y_cut_min,y_cut_min,y_cut_max])
|
|
|
|
vtx_cut = np.c_[vtx_x_cut,vtx_y_cut]
|
|
""" """
|
|
|
|
if (sharp_patch==True and b1!=0 and b2!=0):
|
|
patch = hole(r_hole=min([self.IRx + b2,self.IRy + b2]),Lx_sq=dX,Ly_sq=dY,layer=layers)
|
|
patch.cell.put(0,Y,0)
|
|
patch.cell.put(0,Y,0,flip=1)
|
|
# nd.strt(length=dX,width=dY,layer=layers).put(X-dX/2,Y,0)
|
|
|
|
else:
|
|
_my_polygon(layer_wg=layers,vtx=vtx,vtx_not=vtx_cut).put(0,0,0)
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1').put((Ox[0]+Ix[0])/2,(Oy[0]+Iy[0])/2,theta[0]-90)
|
|
nd.Pin(name='opt_a1',type="optical:").put((Ox[0]+Ix[0])/2,(Oy[0]+Iy[0])/2,theta[0]-90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1').put((Ox[-1]+Ix[-1])/2,(Oy[-1]+Iy[-1])/2,theta[-1]+90)
|
|
nd.Pin(name='opt_b1',type="optical:").put((Ox[-1]+Ix[-1])/2,(Oy[-1]+Iy[-1])/2,theta[-1]+90)
|
|
|
|
return C
|
|
|
|
class Elipse:
|
|
def __init__(self,La: Any=None,Lb: Any=None,wa: Any=None,wb: Any=None,offset_a: float=0,offset_b: float=0,type: str="center",width_type: str='sine',layer: Optional[str]=None,xs: Optional[str]=None,theta_start: float=0,theta_stop: float=360,
|
|
# n_points=512,
|
|
res: float = 0.001,
|
|
sharp_patch: bool=False,show_pins: bool=False) -> None:
|
|
self.La = La
|
|
self.Lb = Lb
|
|
self.wa = wa
|
|
self.wb = wb
|
|
self.offset_a = offset_a
|
|
self.offset_b = offset_b
|
|
self.type = type
|
|
self.layer = layer
|
|
self.xs = xs
|
|
self.theta_start = theta_start
|
|
self.theta_stop = theta_stop
|
|
# self.n_points = n_points
|
|
|
|
self.res = res
|
|
|
|
self.cell = self.generate_gds(sharp_patch=sharp_patch,show_pins=show_pins)
|
|
|
|
def generate_gds(self,sharp_patch,show_pins):
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
if (self.layer==None and self.xs!=None):
|
|
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
""" calculated number of points """
|
|
Rb = self.La
|
|
Ra = self.Lb
|
|
_L_perimeter_ = 2*pi*Rb + 4*(Ra-Rb)
|
|
n_points = int(_L_perimeter_/self.res)
|
|
|
|
n_points = int(n_points/360*abs(self.theta_start-self.theta_stop)) ## modified the points by the angle of ring
|
|
|
|
theta = np.linspace(self.theta_start,self.theta_stop,n_points)
|
|
|
|
if (self.type=='center'):
|
|
|
|
cx = self.La*np.cos(theta/180*pi)
|
|
cy = self.Lb*np.sin(theta/180*pi)
|
|
w = (self.wa-self.wb)*np.cos(theta/180*pi)*np.cos(theta/180*pi) + self.wb
|
|
offset = (self.offset_a-self.offset_b)*np.cos(theta/180*pi)*np.cos(theta/180*pi) + self.offset_b
|
|
|
|
w = w*(a1-a2) + (b1-b2)
|
|
## norm vector
|
|
nx = 2*cx/self.La/self.La
|
|
ny = 2*cy/self.Lb/self.Lb
|
|
|
|
Ln = np.sqrt(nx*nx + ny*ny)
|
|
|
|
|
|
Ox = cx + nx*(w/2 + offset)/Ln
|
|
Oy = cy + ny*(w/2 + offset)/Ln
|
|
Ix = cx + nx*(-w/2 + offset)/Ln
|
|
Iy = cy + ny*(-w/2 + offset)/Ln
|
|
|
|
|
|
|
|
elif (self.type == 'concentric'):
|
|
Ox = (self.La+(self.wa*a1+b1))*np.cos(theta/180*pi)
|
|
Oy = (self.Lb+(self.wb*a1+b1))*np.sin(theta/180*pi)
|
|
Ix = (self.La+(self.wa*a2+b2))*np.cos(theta/180*pi)
|
|
Iy = (self.Lb+(self.wb*a2+b2))*np.sin(theta/180*pi)
|
|
|
|
cx = Ox/2+Ix/2
|
|
cy = Oy/2+Iy/2
|
|
|
|
else :
|
|
raise Exception("ERROR: In <mxpic::passive::Elipse>, <type> not recongized, please input [center | concentric]")
|
|
|
|
dX = np.max([np.max(Ox),np.max(Ix)]) - np.min([np.min(Ox),np.min(Ix)])
|
|
dY = np.max([np.max(Oy),np.max(Iy)]) - np.min([np.min(Oy),np.min(Iy)])
|
|
|
|
X = np.max([np.max(Ox),np.max(Ix)])/2 + np.min([np.min(Ox),np.min(Ix)])/2
|
|
Y = np.max([np.max(Oy),np.max(Iy)])/2 + np.min([np.min(Oy),np.min(Iy)])/2
|
|
|
|
LX = np.max(cx) - np.min(cx)
|
|
LY = np.max(cy) - np.min(cy)
|
|
|
|
self.sz = [LX,LY]
|
|
vtx_out = np.c_[Ox,Oy]
|
|
vtx_In = np.c_[np.flip(Ix),np.flip(Iy)]
|
|
vtx = np.r_[vtx_out,vtx_In]
|
|
|
|
if (sharp_patch==True and b1!=0 and b2!=0):
|
|
nd.strt(length=dX,width=dY,layer=layers).put(X-dX/2,Y,0)
|
|
else:
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
|
|
Ain = np.angle(nx[0]+1j*ny[0])/pi*180
|
|
Aout = np.angle(nx[-1]+1j*ny[-1])/pi*180
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1').put(Ox[0]/2+Ix[0]/2,Oy[0]/2+Iy[0]/2,Ain-90)
|
|
nd.Pin(name='opt_a1',type="optical:").put(Ox[0]/2+Ix[0]/2,Oy[0]/2+Iy[0]/2,Ain-90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1').put(Ox[-1]/2+Ix[-1]/2,Oy[-1]/2+Iy[-1]/2,Aout+90)
|
|
nd.Pin(name='opt_b1',type="optical:").put(Ox[-1]/2+Ix[-1]/2,Oy[-1]/2+Iy[-1]/2,Aout+90)
|
|
|
|
nd.Pin(name='a0').put(0,0,180)
|
|
nd.Pin(name='b0').put(0,0,0)
|
|
if (show_pins):
|
|
nd.put_stub()
|
|
return C
|
|
|
|
class hole :
|
|
def __init__(self,r_hole: float = 0.3,Dx_hole: float=0.3,Dy_hole: float=0.3,Lx_sq: int = 6,Ly_sq: int=6,offset: float=0,
|
|
# n_points = 1024,
|
|
res: float = 0.05,
|
|
xs: str='strip',layer: Optional[str]=None,sharp_patch: bool=True,hole_shape: str='circle') -> None:
|
|
with nd.Cell(instantiate=False) as C:
|
|
if (r_hole+offset>Lx_sq/2 or -r_hole+offset<-Lx_sq/2):
|
|
raise Exception("ERROR: In <mxpic::structure::hole>, hole outside sqaure area, <r_hole>")
|
|
|
|
if (Dx_hole/2+offset>Lx_sq/2 or -Dx_hole/2+offset<-Lx_sq/2):
|
|
raise Exception("ERROR: In <mxpic::structure::hole>, hole outside sqaure area, <Dx_hole>")
|
|
|
|
if (Dy_hole>Ly_sq):
|
|
raise Exception("ERROR: In <mxpic::structure::hole>, hole outside sqaure area, <Dy_hole>")
|
|
|
|
|
|
n_points = int(np.floor(r_hole*2*np.pi/res)+1)
|
|
|
|
if (layer==None):
|
|
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
if (b1==0 and b2==0):
|
|
if (hole_shape=='circle'):
|
|
theta = np.linspace(0,180,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(r_hole)+offset
|
|
vtx_outer_y = np.sin(theta)*(r_hole)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
vtx_sq_x = np.array([Lx_sq/2, Lx_sq/2,-Lx_sq/2,-Lx_sq/2])
|
|
vtx_sq_y = np.array([ 0, Ly_sq/2, Ly_sq/2, 0])
|
|
vtx_sq = np.c_[vtx_sq_x,vtx_sq_y]
|
|
|
|
vtx = np.r_[vtx_outer,np.flip(vtx_sq,0)]
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,180,flip=1)
|
|
|
|
elif (hole_shape=='rectangle'):
|
|
vtx_outer_x = np.array([Dx_hole/2,Dx_hole/2,-Dx_hole/2,-Dx_hole/2])+offset
|
|
vtx_outer_y = np.array([0,Dy_hole/2, Dy_hole/2,0])
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
vtx_sq_x = np.array([Lx_sq/2, Lx_sq/2,-Lx_sq/2,-Lx_sq/2])
|
|
vtx_sq_y = np.array([ 0, Ly_sq/2, Ly_sq/2, 0])
|
|
vtx_sq = np.c_[vtx_sq_x,vtx_sq_y]
|
|
|
|
vtx = np.r_[vtx_outer,np.flip(vtx_sq,0)]
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,180,flip=1)
|
|
|
|
else :
|
|
_L_ = Lx_sq*(a1-a2)+(b1-b1)
|
|
_w_ = Ly_sq*(a1-a2)+(b1-b1)
|
|
nd.strt(length=_L_,width=_w_,layer=layers).put(-_L_/2,0,0)
|
|
|
|
|
|
else:
|
|
if (hole_shape=='circle'):
|
|
theta = np.linspace(0,180,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.cos(theta)*(r_hole)+offset
|
|
vtx_outer_y = np.sin(theta)*(r_hole)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
vtx_sq_x = np.array([Lx_sq/2, Lx_sq/2,-Lx_sq/2,-Lx_sq/2])
|
|
vtx_sq_y = np.array([ 0, Ly_sq/2, Ly_sq/2, 0])
|
|
vtx_sq = np.c_[vtx_sq_x,vtx_sq_y]
|
|
|
|
vtx = np.r_[vtx_outer,np.flip(vtx_sq,0)]
|
|
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,0)
|
|
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,0,flip=1)
|
|
elif (hole_shape=='rectangle'):
|
|
vtx_outer_x = np.array([Dx_hole/2,Dx_hole/2,-Dx_hole/2,-Dx_hole/2])+offset
|
|
vtx_outer_y = np.array([0,Dy_hole/2, Dy_hole/2,0])
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
vtx_sq_x = np.array([Lx_sq/2, Lx_sq/2,-Lx_sq/2,-Lx_sq/2])
|
|
vtx_sq_y = np.array([ 0, Ly_sq/2, Ly_sq/2, 0])
|
|
vtx_sq = np.c_[vtx_sq_x,vtx_sq_y]
|
|
|
|
vtx = np.r_[vtx_outer,np.flip(vtx_sq,0)]
|
|
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,0)
|
|
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,180,flip=1)
|
|
self.cell = C
|