Folder structure simplfied
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
"""Reusable geometry structures used by MxPIC components.
|
||||
|
||||
Wildcard exports intentionally include the small math/Nazca aliases that
|
||||
older component modules imported from ``structures.py``.
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
from cmath import pi
|
||||
|
||||
import nazca as nd
|
||||
import nazca.clipper as clp
|
||||
import numpy as np
|
||||
|
||||
from .polygons import _my_polygon, strt_round_courner
|
||||
from .rings import circle, mx_bend, Elipse_dual, Elipse, hole
|
||||
from .curves import Conchoid, _line2wg_, _my_poly_spiral, Clothoid
|
||||
from .racetrack import Racetrack
|
||||
|
||||
__all__ = [
|
||||
"Any",
|
||||
"Optional",
|
||||
"pi",
|
||||
"nd",
|
||||
"clp",
|
||||
"np",
|
||||
"_my_polygon",
|
||||
"strt_round_courner",
|
||||
"circle",
|
||||
"mx_bend",
|
||||
"Elipse_dual",
|
||||
"Elipse",
|
||||
"hole",
|
||||
"Conchoid",
|
||||
"_line2wg_",
|
||||
"_my_poly_spiral",
|
||||
"Clothoid",
|
||||
"Racetrack",
|
||||
]
|
||||
@@ -0,0 +1,442 @@
|
||||
"""Curve and spiral 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 Conchoid:
|
||||
def __init__(self,R0: Any,kR: Any,T: Any,w: float,layer: str,w_end: Optional[float]=None,res: float=0.1,final_flat: Any=None,begin_flat: Any=None,xs: Optional[str]=None) -> None:
|
||||
## with half circle to be one cycle
|
||||
|
||||
if (w_end==None):
|
||||
w_end = w
|
||||
|
||||
|
||||
with nd.Cell(instantiate=False)as C:
|
||||
n_sects = int(np.floor(T/np.pi)) ## intersecting into different semi-circle
|
||||
|
||||
L = R0*T + 1/2*kR*T**2 ## The total length of the Conchoid center line
|
||||
n_points = int(np.floor(L/res))+1
|
||||
|
||||
## calculating sections
|
||||
if (np.abs(T-n_sects*np.pi) <0.0001):
|
||||
n_sects = n_sects
|
||||
else:
|
||||
n_sects = n_sects+1
|
||||
|
||||
# res_sect = int(np.floor(res/n_sects))+1
|
||||
|
||||
# res = 0
|
||||
|
||||
if (layer!=None):
|
||||
nd.add_xsection(name='temp')
|
||||
nd.add_layer2xsection(xsection='temp',layer=layer,growx=0,growy=0)
|
||||
xs = 'temp'
|
||||
|
||||
for _n_ in range(0,n_sects):
|
||||
# phi_start = _n_*pi
|
||||
# phi_end = min(T,_n_*pi+pi) ## forward placement
|
||||
|
||||
phi_end = T - _n_*pi
|
||||
phi_start = max(0,T - _n_*pi - pi)
|
||||
|
||||
L_sect = R0*(phi_end-phi_start) + 1/2*kR*(phi_end**2 - phi_start**2)
|
||||
n_points = int(L_sect/res)+1
|
||||
|
||||
if (layer!=None):
|
||||
Theta = np.linspace(phi_start, phi_end ,n_points)
|
||||
R = (Theta*kR+R0) ## conchoid function
|
||||
|
||||
# res = kR/2*T*T + R0*T
|
||||
# res = res + np.sum(R[0:-1]*np.diff(Theta))
|
||||
|
||||
w_cur = w
|
||||
|
||||
if (_n_==0):
|
||||
w_cur = np.linspace(w,w_end,n_points)
|
||||
# print("Loading Taper area")
|
||||
|
||||
vtx_cx = R*np.cos(Theta)
|
||||
vtx_cy = R*np.sin(Theta)
|
||||
vtx_center = np.c_[vtx_cx,vtx_cy]
|
||||
|
||||
e_theta = -1/((R0/kR)+Theta) ## actuall norm towards spiral
|
||||
# e_rou = np.ones(len(e_theta))
|
||||
ey = np.sin(Theta) - np.cos(Theta)*kR/R
|
||||
ex = np.cos(Theta) + np.sin(Theta)*kR/R
|
||||
|
||||
if (final_flat!=None and _n_==0):
|
||||
ey[-1] = np.sin(final_flat/180*np.pi)
|
||||
ex[-1] = np.cos(final_flat/180*np.pi)
|
||||
|
||||
if (begin_flat!=None and _n_==n_sects-1):
|
||||
ey[0] = np.sin(begin_flat/180*np.pi)
|
||||
ex[0] = np.cos(begin_flat/180*np.pi)
|
||||
# if (final_flat!=None and _n_==0):
|
||||
# e_theta[-1] = final_flat
|
||||
# if (begin_flat!=None and _n_==n_sects-1):
|
||||
# e_theta[0] = begin_flat
|
||||
|
||||
if (_n_==0):
|
||||
self.Atilt = np.arctan(ey[0]/ex[0])/np.pi*180
|
||||
# print("Atilt_conchoid = %.5f" % self.Atilt)
|
||||
|
||||
Lnorm = np.sqrt(np.power(ex,2)+np.power(ey,2))
|
||||
|
||||
vtx_x = R*np.cos(Theta)
|
||||
vtx_y = R*np.sin(Theta)
|
||||
|
||||
vtx_out_x = vtx_x + w_cur/2*ex/Lnorm
|
||||
vtx_out_y = vtx_y + w_cur/2*ey/Lnorm
|
||||
|
||||
vtx_in_x = vtx_x - w_cur/2*ex/Lnorm
|
||||
vtx_in_y = vtx_y - w_cur/2*ey/Lnorm
|
||||
|
||||
vtx_in = np.c_[np.flip(vtx_in_x),np.flip(vtx_in_y)]
|
||||
vtx_out = np.c_[vtx_out_x,vtx_out_y]
|
||||
vtx = np.r_[vtx_out,vtx_in]
|
||||
_my_polygon(layer_wg=layer,vtx=vtx).put(0,0,0)
|
||||
|
||||
elif(layer==None and xs!=None):
|
||||
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
||||
(a1,b1), (a2,b2),c1,c2 = growx
|
||||
Theta = np.linspace(phi_start, phi_end ,n_points)
|
||||
R = (Theta*kR+R0)
|
||||
# res = kR/2*T*T + R0*T
|
||||
# res = res + np.sum(R[0:-1]*np.diff(Theta)
|
||||
|
||||
w_cur = w*(a1-a2) + (b1-b2)
|
||||
|
||||
if (_n_==0):
|
||||
w_cur = np.linspace(w,w_end,n_points)
|
||||
w_cur = w_cur*(a1-a2) + (b1-b2)
|
||||
|
||||
vtx_cx = R*np.cos(Theta)
|
||||
vtx_cy = R*np.sin(Theta)
|
||||
vtx_center = np.c_[vtx_cx,vtx_cy]
|
||||
|
||||
# e_theta = -1/((R0/kR)+Theta)
|
||||
# e_rou = np.ones(len(e_theta))
|
||||
|
||||
# if (final_flat!=None and _n_==0):
|
||||
# e_theta[-1] = final_flat
|
||||
# if (begin_flat!=None and _n_==n_sects-1):
|
||||
# e_theta[0] = begin_flat
|
||||
|
||||
|
||||
ey = np.sin(Theta)*R - np.sin(Theta)*kR
|
||||
ex = np.cos(Theta)*R + np.cos(Theta)*kR
|
||||
|
||||
if (final_flat!=None and _n_==0):
|
||||
ey[-1] = np.sin(final_flat/180*np.pi)
|
||||
ex[-1] = np.cos(final_flat/180*np.pi)
|
||||
|
||||
if (begin_flat!=None and _n_==n_sects-1):
|
||||
ey[0] = np.sin(begin_flat/180*np.pi)
|
||||
ex[0] = np.cos(begin_flat/180*np.pi)
|
||||
|
||||
Lnorm = np.sqrt(np.power(ex,2)+np.power(ey,2))
|
||||
vtx_x = R*np.cos(Theta)
|
||||
vtx_y = R*np.sin(Theta)
|
||||
|
||||
vtx_out_x = vtx_x + w_cur/2*ex/Lnorm
|
||||
vtx_out_y = vtx_y + w_cur/2*ey/Lnorm
|
||||
|
||||
vtx_in_x = vtx_x - w_cur/2*ex/Lnorm
|
||||
vtx_in_y = vtx_y - w_cur/2*ey/Lnorm
|
||||
|
||||
vtx_in = np.c_[np.flip(vtx_in_x),np.flip(vtx_in_y)]
|
||||
vtx_out = np.c_[vtx_out_x,vtx_out_y]
|
||||
vtx = np.r_[vtx_out,vtx_in]
|
||||
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
||||
|
||||
Rmax = T*kR+R0
|
||||
nd.Pin(name="a1").put(R0,0,-90)
|
||||
nd.Pin(name="b1").put(Rmax*np.cos(T),Rmax*np.sin(T),(T/np.pi*180+90))
|
||||
|
||||
self.L = L
|
||||
self.cell =C
|
||||
self.vtx_center = vtx_center
|
||||
self.vtx = vtx
|
||||
self.K_end = (np.power(np.max(R),2) + 2*np.power(kR,2)) / np.power((np.power(np.max(R),2) + np.power(kR,2)),1.5)
|
||||
self.R_end = 1/self.K_end
|
||||
|
||||
def _line2wg_(x,y,wu,wd,theta,n_points):
|
||||
""" building waveguide with center line and side expansion
|
||||
|
||||
Args:
|
||||
vtx_line (list[float]): the location of points, [x,y]
|
||||
width (list[float]): the expansion width of points vertical to the pointing vector, [wu,wd]
|
||||
theta (list[float]): the pointing angle, [theta], 0 represent right, 180 represent left
|
||||
"""
|
||||
|
||||
theta = theta*np.pi/180
|
||||
x_u = x+wu*np.cos(theta+pi/2)
|
||||
x_d = x+wd*np.cos(theta-pi/2)
|
||||
|
||||
y_u = y+wu*np.sin(theta+pi/2)
|
||||
y_d = y+wd*np.sin(theta-pi/2)
|
||||
|
||||
### polygon section, reducing resolution
|
||||
sect = np.linspace(start= 0,stop= len(x_u)-1,num= n_points)
|
||||
sect = np.asarray(sect, dtype = int)
|
||||
|
||||
x_u = x_u[sect]
|
||||
x_d = x_d[sect]
|
||||
y_u = y_u[sect]
|
||||
y_d = y_d[sect]
|
||||
|
||||
vtx_u = np.c_[x_u,y_u]
|
||||
vtx_d = np.c_[x_d,y_d]
|
||||
|
||||
|
||||
|
||||
vtx = np.r_[vtx_u,np.flip(vtx_d,0)]
|
||||
|
||||
return vtx
|
||||
|
||||
def _my_poly_spiral(r,theta,order,res,R_max,sz_restrict=None):
|
||||
''' generating a poly spiral curve
|
||||
Args
|
||||
r (2*1 list) :r[0] is the begining
|
||||
theta (2*1 list) :theta[0] is the begining [in degree]
|
||||
Return
|
||||
frame (nazca.cell):
|
||||
'''
|
||||
theta[0] = theta[0]/180*np.pi ## angle format changing
|
||||
theta[1] = theta[1]/180*np.pi ## angle format changing
|
||||
|
||||
K_ends = np.array([1/r[0],1/r[1]]) ## definition of the curvature, r[0] is the beginnin and r[1] is the ending
|
||||
L0 = np.abs(theta[0]-theta[1])/(K_ends[0] + (K_ends[1]-K_ends[0])*order/(order+1))
|
||||
L = np.linspace(0,L0,int(np.floor(L0/res)+1)) ## L = [0:res:L0];
|
||||
K = K_ends[0] + (K_ends[1] - K_ends[0])/np.power(L0,order)*(np.power(L0,order) - np.power(np.abs(L-L0),order))
|
||||
R = 1/K
|
||||
|
||||
dir = np.sign(theta[1] - theta[0])
|
||||
dt = dir*res/R
|
||||
|
||||
theta_temp = np.cumsum(dt) + theta[0]
|
||||
""" 2023.08.01 updated, using array calculation instead of for loop"""
|
||||
|
||||
dx = dir*R[1:]*( np.sin(theta_temp[1:]) - np.sin(theta_temp[0:-1]))
|
||||
dy = -dir*R[1:]*( np.cos(theta_temp[1:]) - np.cos(theta_temp[0:-1]))
|
||||
|
||||
x = np.r_[0,np.cumsum(dx)]
|
||||
y = np.r_[0,np.cumsum(dy)]
|
||||
# x = np.zeros(len(L))
|
||||
# y = np.zeros(len(L))
|
||||
|
||||
# idx = np.linspace(1,len(L)-1,len(L)-1)
|
||||
|
||||
# for _idx_ in idx :
|
||||
# _idx_ = int(_idx_)
|
||||
# x[_idx_] = x[_idx_-1] + dir*R[_idx_]*( np.sin(theta_temp[_idx_]) - np.sin(theta_temp[_idx_-1]))
|
||||
# y[_idx_] = y[_idx_-1] - dir*R[_idx_]*( np.cos(theta_temp[_idx_]) - np.cos(theta_temp[_idx_-1]))
|
||||
|
||||
vector = np.c_[x,y,theta_temp,L]
|
||||
|
||||
return (vector,L0)
|
||||
|
||||
class Clothoid:
|
||||
def __init__(self,
|
||||
name:str=None,
|
||||
R: 'list|np.ndarray'=[10,20],
|
||||
w: 'list|np.ndarray|float'=[0.4,0.5], ## w either has the length as R, or 1 element, or 2 element
|
||||
A: 'list|np.ndarray'=[0,45],
|
||||
width_type: str='sine',
|
||||
spiral_order: float=1,
|
||||
Rmax:float=10000,
|
||||
dL_cal:float=0.001,
|
||||
dL_wg: float = 0.1,
|
||||
# n_points:int=1024,
|
||||
xs:str='strip',
|
||||
layer:str=None,
|
||||
sharp_patch:bool=True,
|
||||
end_patch : bool=True,
|
||||
show_pins:bool=False) -> None:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
R (list|np.ndarray, optional): Curvature radius in each attaching point. Defaults to [10,20].
|
||||
w (list|np.ndarray|float, optional): Width at each attaching point corresponding to R, or it can be set to one or two element. Defaults to [0.4,0.5].
|
||||
A (list|np.ndarray, optional): Angle at each attaching point. Defaults to [0,45].
|
||||
width_type (str, optional): The width function with length or angle 'linear' 'linear2' 'sine' 'sine2'. Defaults to 'sine'.
|
||||
spiral_order (float, optional): The curvature order of spiral. Defaults to 1.
|
||||
Rmax (float, optional): Maxmum radius. Defaults to 10000.
|
||||
res (float, optional): Resolution in calculation. Defaults to 0.001.
|
||||
n_points (int, optional): Resolution in GDS. Defaults to 1024.
|
||||
xs (str, optional): XSection of the devices. Defaults to 'strip'.
|
||||
layer (str, optional): Layer of the devices. Defaults to None.
|
||||
sharp_patch (bool, optional): Either to patch. Defaults to True.
|
||||
show_pins (bool, optional): Either to show pins. Defaults to False.
|
||||
|
||||
Raises:
|
||||
Exception: _description_
|
||||
Exception: _description_
|
||||
"""
|
||||
|
||||
if (isinstance(w,int) or isinstance(w,float)):
|
||||
w= np.array([w,w])
|
||||
|
||||
self.name = name
|
||||
self.R = R
|
||||
self.A = A
|
||||
self.width_type = width_type
|
||||
self.spiral_order = spiral_order
|
||||
self.dL_cal = dL_cal
|
||||
# self.n_points = n_points
|
||||
self.xs =xs
|
||||
self.layer = layer
|
||||
self.dL_wg = dL_wg
|
||||
|
||||
if (len(R) != len(A)):
|
||||
raise Exception("ERROR: In <mxpic::structures::Colthoid>, <A> and <R> are not matched in length, please keep len(A) = len(R)")
|
||||
|
||||
if (isinstance(spiral_order,int) or isinstance(spiral_order,float)):
|
||||
spiral_order = spiral_order*np.ones(len(R)-1)
|
||||
|
||||
elif(isinstance(spiral_order,list)):
|
||||
spiral_order = np.array(spiral_order)
|
||||
|
||||
## center curve routing
|
||||
_idx_act_=0
|
||||
for _idx_ in range(0,len(R)-1):
|
||||
if ( abs( A[_idx_] - A[_idx_+1] )<0.001 ):
|
||||
|
||||
continue
|
||||
vec_cur,L0_cur = _my_poly_spiral([R[_idx_],R[_idx_+1]],[A[_idx_],A[_idx_+1]],spiral_order[_idx_],dL_cal,Rmax)
|
||||
_idx_act_ = _idx_act_+1
|
||||
|
||||
x_cur = vec_cur[:,0]
|
||||
y_cur = vec_cur[:,1]
|
||||
theta_cur = vec_cur[:,2]/np.pi*180 ## pointing vector
|
||||
L_cur = vec_cur[:,3]
|
||||
|
||||
if (_idx_act_==1):
|
||||
L = L_cur
|
||||
x = x_cur
|
||||
y = y_cur
|
||||
theta = theta_cur
|
||||
L0 = L0_cur
|
||||
else :
|
||||
L = np.r_[L,L_cur+L[-1]]
|
||||
x = np.r_[x,x_cur+x[-1]]
|
||||
y = np.r_[y,y_cur+y[-1]]
|
||||
theta = np.r_[theta,theta_cur]
|
||||
L0 = L0 + L0_cur
|
||||
|
||||
if (len(w)>2 and len(w)==len(R)):
|
||||
w_cur = (w[_idx_+1]-w[_idx_])/L0_cur*L_cur + (w[_idx_])
|
||||
if (_idx_act_==1):
|
||||
w_fianl = w_cur
|
||||
else :
|
||||
w_fianl = np.r_[w_fianl,w_cur]
|
||||
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.L = L
|
||||
self.L0 = L0
|
||||
self.theta = theta
|
||||
self.vtx_center = np.c_[x,y]
|
||||
self.end_patch = end_patch
|
||||
self.sz = [np.abs(max(self.x) - min(self.x)),np.abs(max(self.y) - min(self.y))]
|
||||
|
||||
if (dL_wg!=None):
|
||||
self.n_points = int(np.floor(self.L0/self.dL_wg)+1) ## overwrite n_points
|
||||
|
||||
# print("n points",self.n_points)
|
||||
|
||||
if (len(w)==2):
|
||||
## width winding
|
||||
if (width_type=='linear'):
|
||||
w = (w[1]-w[0])/L0*L + w[0]
|
||||
elif (width_type=='dual_linear'):
|
||||
w = (w[1]-w[0])/L0/2*np.abs(L-L0/2) + w[0]
|
||||
elif (width_type=='sine'):
|
||||
w = (w[0]-w[1])*np.cos(theta/180*pi)*np.cos(theta/180*pi) + w[1]
|
||||
elif (width_type=='dual_sine'):
|
||||
w = (w[0]-w[1])*np.cos(theta/2/180*pi)*np.cos(theta/2/180*pi) + w[1]
|
||||
elif (width_type=='crow_customize' or width_type=='pumpkin'):
|
||||
dw = (w[1]-w[0])
|
||||
z = theta/180*np.pi
|
||||
z = np.sqrt(z)*np.sqrt(np.pi/2)
|
||||
z = np.sin(z)**2*np.pi/2
|
||||
|
||||
w = dw*np.sin(z)**2 + w[0]
|
||||
|
||||
|
||||
else :
|
||||
w = (w[1]-w[0])/L0*L + w[0]
|
||||
|
||||
self.w = np.array(w)
|
||||
elif (len(w)==len(R)):
|
||||
self.w = w_fianl
|
||||
else:
|
||||
raise Exception("ERROR, In <mxpic::structures::Clothoid>, <w> is not matched with <R>, please keep len(w)=2 or len(w)=R or w=int")
|
||||
|
||||
self.cell = self.generate_gds(sharp_patch=sharp_patch,show_pins=show_pins)
|
||||
|
||||
|
||||
def generate_gds(self,sharp_patch,show_pins):
|
||||
if (self.name is None):
|
||||
self.instantiate = False
|
||||
else:
|
||||
self.instantiate = True
|
||||
with nd.Cell(name=self.name,instantiate=self.instantiate) as C:
|
||||
|
||||
if (self.layer==None and self.xs!=None): ## if definition is in layers
|
||||
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
||||
(a1,b1), (a2,b2),c1,c2 = growx
|
||||
if (b1!=0 and b2!=0):
|
||||
vtx_wg = _line2wg_(x=self.x,y=self.y,wu=self.w*a1+b1,wd= -self.w*a2-b2,theta=self.theta,n_points=self.n_points)
|
||||
|
||||
dX = np.max(vtx_wg[:,0]) - np.min(vtx_wg[:,0])
|
||||
dY = np.max(vtx_wg[:,1]) - np.min(vtx_wg[:,1])
|
||||
cX = np.max(vtx_wg[:,0])/2 + np.min(vtx_wg[:,0])/2
|
||||
cY = np.max(vtx_wg[:,1])/2 + np.min(vtx_wg[:,1])/2
|
||||
|
||||
if (sharp_patch):
|
||||
if (self.end_patch):
|
||||
nd.strt(length = dX+(b1-b2),width = dY,layer=layers).put(cX-dX/2-b1,cY,0)
|
||||
|
||||
else:
|
||||
nd.strt(length = dX,width = dY,layer=layers).put(cX-dX/2,cY,0)
|
||||
else :
|
||||
|
||||
|
||||
_my_polygon(layers,vtx_wg).put(0,0,0)
|
||||
|
||||
else :
|
||||
vtx_wg = _line2wg_(x=self.x,y=self.y,wu=self.w*a1+b1,wd= -self.w*a2-b2,theta=self.theta,n_points=self.n_points)
|
||||
self.vtx =vtx_wg
|
||||
_my_polygon(layers,vtx_wg).put(0,0,0)
|
||||
|
||||
nd.Pin(name='a0',width=self.w[0],type="Optical:").put(self.x[0],self.y[0],self.A[0]+180)
|
||||
nd.Pin(name='b0',width=self.w[-1],type="Optical:").put(self.x[-1],self.y[-1],self.A[-1])
|
||||
|
||||
nd.Pin(name='a1',width=self.w[0],type="Optical:").put(self.x[0],self.y[0],self.A[0]+180)
|
||||
nd.Pin(name='b1',width=self.w[-1],type="Optical:").put(self.x[-1],self.y[-1],self.A[-1])
|
||||
elif(self.layer!=None) : ## if definition is in xsections
|
||||
|
||||
vtx_wg = _line2wg_(x=self.x,y=self.y,wu=self.w/2,wd= self.w/2,theta=self.theta,n_points=self.n_points)
|
||||
|
||||
_my_polygon(self.layer,vtx_wg).put(0,0,0)
|
||||
|
||||
nd.Pin(name='a0',width=self.w[0],type="Optical:").put(self.x[0],self.y[0],self.A[0]+180)
|
||||
nd.Pin(name='b0',width=self.w[-1],type="Optical:").put(self.x[-1],self.y[-1],self.A[-1])
|
||||
|
||||
nd.Pin(name='a1',width=self.w[0],type="Optical:").put(self.x[0],self.y[0],self.A[0]+180)
|
||||
nd.Pin(name='b1',width=self.w[-1],type="Optical:").put(self.x[-1],self.y[-1],self.A[-1])
|
||||
|
||||
else:
|
||||
raise Exception("ERROR: In <mxpic::structures::Colthoid>, <layer | xs> not defined")
|
||||
|
||||
if (show_pins):
|
||||
nd.put_stub()
|
||||
self.sz_p2p = [np.abs(self.x[-1] - self.x[0]),np.abs(self.y[-1] - self.y[0])]
|
||||
|
||||
return C
|
||||
@@ -0,0 +1,85 @@
|
||||
"""Polygon helpers and simple geometric utility cells."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import nazca as nd
|
||||
import nazca.clipper as clp
|
||||
import numpy as np
|
||||
|
||||
def _my_polygon (layer_wg,vtx,vtx_not=None) :
|
||||
''' establishing a polygon with input vertices
|
||||
Args
|
||||
vtx (2*x list) :
|
||||
Return
|
||||
frame (nazca.cell):
|
||||
'''
|
||||
sz_l = vtx.shape
|
||||
sz_l = sz_l[0]
|
||||
|
||||
idx_seq = np.linspace(1,sz_l-1,sz_l-1)
|
||||
|
||||
_points_ = [(vtx[0,0],vtx[0,1])]
|
||||
for idx in idx_seq:
|
||||
_point_cur_ = [(vtx[int(idx),0],vtx[int(idx),1])]
|
||||
_points_.extend(_point_cur_)
|
||||
|
||||
if (isinstance(vtx_not,np.ndarray) ):
|
||||
_points_cut_ = [(vtx_not[0,0],vtx_not[0,1])]
|
||||
sz_l = vtx_not.shape
|
||||
sz_l = sz_l[0]
|
||||
for idx in range(0,sz_l):
|
||||
_point_cur_ = [(vtx_not[int(idx),0],vtx_not[int(idx),1])]
|
||||
_points_cut_.extend(_point_cur_)
|
||||
|
||||
_points_ = clp.diff_polygons(paths_A=[_points_],paths_B=[_points_cut_])
|
||||
_points_ = _points_[0]
|
||||
# nd.Polygon(layer=layer_wg, points = _points_cut_).put()
|
||||
frame = nd.Polygon(layer=layer_wg, points = _points_)
|
||||
|
||||
return frame
|
||||
|
||||
class strt_round_courner:
|
||||
def __init__(self, width: float=5, length: float = 10, layer: Optional[str]=None, radius: float=1, n_points: int=64) -> None:
|
||||
|
||||
if (radius>width/2):
|
||||
radius = width/2
|
||||
|
||||
with nd.Cell(instantiate=False) as C:
|
||||
|
||||
theta = np.linspace(0,np.pi/2,n_points) ## establish a arc
|
||||
vtx_ru_x = radius*np.cos(theta) + length-radius
|
||||
vtx_ru_y = radius*np.sin(theta) + width/2 - radius
|
||||
|
||||
theta = np.linspace(np.pi/2,np.pi,n_points) ## establish a arc
|
||||
vtx_lu_x = radius*np.cos(theta) + radius
|
||||
vtx_lu_y = radius*np.sin(theta) + width/2 - radius
|
||||
|
||||
|
||||
theta = np.linspace(np.pi,np.pi/2*3,n_points) ## establish a arc
|
||||
vtx_ld_x = radius*np.cos(theta) + radius
|
||||
vtx_ld_y = radius*np.sin(theta) - width/2 + radius
|
||||
|
||||
theta = np.linspace(-np.pi/2,0,n_points) ## establish a arc
|
||||
vtx_rd_x = radius*np.cos(theta) + length-radius
|
||||
vtx_rd_y = radius*np.sin(theta) - width/2 + radius
|
||||
|
||||
vtx_x = np.r_[vtx_ru_x,vtx_lu_x,vtx_ld_x,vtx_rd_x]
|
||||
vtx_y = np.r_[vtx_ru_y,vtx_lu_y,vtx_ld_y,vtx_rd_y]
|
||||
|
||||
|
||||
# vtx_x = vtx_ru_x
|
||||
# vtx_y = vtx_ru_y
|
||||
|
||||
|
||||
vtx = np.c_[vtx_x,vtx_y]
|
||||
# vtx = np.transpose(vtx)
|
||||
# print(np.shape(vtx))
|
||||
|
||||
_my_polygon(layer_wg=layer,vtx=vtx).put()
|
||||
|
||||
nd.Pin(name='a0',width=width).put(0,0,180)
|
||||
nd.Pin(name='b0',width=width).put(length,0,0)
|
||||
|
||||
nd.put_stub()
|
||||
|
||||
self.cell = C
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Racetrack geometry primitive."""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
import nazca as nd
|
||||
import numpy as np
|
||||
|
||||
from .rings import circle
|
||||
|
||||
class Racetrack:
|
||||
def __init__(self,
|
||||
bend_cell: Any=None,
|
||||
xs: str = 'strip',
|
||||
layer: Optional[str] = None,
|
||||
R_bend: int = 10,
|
||||
w: float = 0.5,
|
||||
dLx: float = 100,
|
||||
dLy: float = 100,
|
||||
# n_points = 128,
|
||||
res: float = 0.001,
|
||||
) -> None:
|
||||
|
||||
if (bend_cell==None):
|
||||
bend_cell = circle(xs=xs,theta_start=0,theta_stop=90,res=res,
|
||||
# n_points=n_points,
|
||||
radius=R_bend,width=w).cell
|
||||
|
||||
if (isinstance(bend_cell,nd.Cell)):
|
||||
bend_cell = bend_cell
|
||||
elif (hasattr(bend_cell,'cell')) :
|
||||
bend_cell = bend_cell.cell
|
||||
|
||||
else:
|
||||
raise Exception("ERROR: In <mxpic::structure::Racetrack>, <bend_cell> not a regonizable class, please input [nd.Cell] or class with [nd.Cell]")
|
||||
|
||||
self.dLx = dLx
|
||||
self.R_bend =R_bend
|
||||
self.dLy = dLy
|
||||
self.xs = xs
|
||||
self.layer = layer
|
||||
|
||||
self.bend_cell = bend_cell
|
||||
|
||||
self.A_bend = np.abs(bend_cell.pin['a1'].a - bend_cell.pin['b1'].a)
|
||||
self.w = [bend_cell.pin['b1'].width,bend_cell.pin['a1'].width]
|
||||
|
||||
self.w_crack = 0.002
|
||||
|
||||
self.cell = self.generate_gds()
|
||||
|
||||
|
||||
def generate_gds(self):
|
||||
with nd.Cell(instantiate=False) as C:
|
||||
|
||||
bend_sz = [ abs(self.bend_cell.pin['a0'].x - self.bend_cell.pin['b0'].x)
|
||||
,abs(self.bend_cell.pin['a0'].y - self.bend_cell.pin['b0'].y)]
|
||||
|
||||
if (self.layer==None):
|
||||
if (self.A_bend==90):
|
||||
nd.strt(length=self.dLx+self.w_crack,width=self.w[1],xs=self.xs).put(-self.dLx/2-self.w_crack/2,-self.dLy/2-bend_sz[1],0)
|
||||
self.bend_cell.put(-self.dLx/2,-self.dLy/2-bend_sz[1],0,flip=0)
|
||||
nd.strt(length=self.dLy+self.w_crack,width=self.w[0],xs=self.xs).put( self.dLx/2+bend_sz[0],-self.dLy/2-self.w_crack/2,90)
|
||||
self.bend_cell.put( self.dLx/2,-self.dLy/2-bend_sz[1],180,flip=1)
|
||||
nd.strt(length=self.dLx+self.w_crack,width=self.w[1],xs=self.xs).put(-self.dLx/2-self.w_crack/2, self.dLy/2+bend_sz[1],0)
|
||||
self.bend_cell.put(-self.dLx/2, self.dLy/2+bend_sz[1],0,flip=1)
|
||||
nd.strt(length=self.dLy+self.w_crack,width=self.w[0],xs=self.xs).put(-self.dLx/2-bend_sz[0],-self.dLy/2-self.w_crack/2,90)
|
||||
self.bend_cell.put( self.dLx/2, self.dLy/2+bend_sz[1],180,flip=0)
|
||||
elif (self.A_bend==0 or self.A_bend==360): ## in this case, dy is not used
|
||||
temp = nd.strt(length=self.dLx,width=self.w[0],xs=self.xs).put(-self.dLx/2,-self.dLy/2-bend_sz[1],0)
|
||||
sp_r = self.bend_cell.put()
|
||||
sp_l = self.bend_cell.put(temp.pin['a0'].xya(),flip=1)
|
||||
temp = nd.strt(length=np.abs(sp_r.pin['b0'].x-sp_l.pin['b0'].x),width=sp_r.pin['b0'].width,xs=self.xs).put()
|
||||
else:
|
||||
if (self.A_bend==90):
|
||||
nd.strt(length=self.dLx,width=self.w[0],layer=self.layer).put(-self.dLx/2,-self.dLy/2-bend_sz[1],0)
|
||||
temp = self.bend_cell.put()
|
||||
temp = nd.strt(length=self.dLy,width=self.w[1],layer=self.layer).put()
|
||||
temp = self.bend_cell.put('b0',temp.pin['b0'].xya(),flip=1)
|
||||
temp = nd.strt(length=self.dLx,width=self.w[0],layer=self.layer).put(temp.pin['a0'].xya())
|
||||
temp = self.bend_cell.put()
|
||||
temp = nd.strt(length=self.dLy,width=self.w[1],layer=self.layer).put()
|
||||
temp = self.bend_cell.put('b0',temp.pin['b0'].xya(),flip=1)
|
||||
elif (self.A_bend==0 or self.A_bend==360): ## in this case, dy is not used
|
||||
temp = nd.strt(length=self.dLx,width=self.w[0],layer=self.layer).put(-self.dLx/2,-self.dLy/2-bend_sz[1],0)
|
||||
sp_r = self.bend_cell.put()
|
||||
sp_l = self.bend_cell.put(temp.pin['a0'].xya(),flip=1)
|
||||
temp = nd.strt(length=np.abs(sp_r.pin['b0'].x-sp_l.pin['b0'].x),width=sp_r.pin['b0'].width,layer=self.layer).put()
|
||||
|
||||
|
||||
nd.Pin(name="r1",width=self.w[0]).put(0,-self.dLy/2-bend_sz[1],0)
|
||||
nd.Pin(name="r3",width=self.w[0]).put(0, self.dLy/2+bend_sz[1],180)
|
||||
|
||||
nd.Pin(name="r2",width=self.w[1]).put(-self.dLx/2-bend_sz[0], 0, 90)
|
||||
nd.Pin(name="r4",width=self.w[1]).put( self.dLx/2+bend_sz[0], 0,-90)
|
||||
|
||||
sz = [2*bend_sz[0]+self.dLx,2*bend_sz[1]+self.dLy]
|
||||
self.sz = sz
|
||||
|
||||
return C
|
||||
@@ -0,0 +1,514 @@
|
||||
"""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)
|
||||
|
||||
|
||||
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='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)
|
||||
|
||||
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)
|
||||
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='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)
|
||||
|
||||
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)
|
||||
|
||||
nd.Pin(name='a1').put((Ox[0]+Ix[0])/2,(Oy[0]+Iy[0])/2,theta[0]-90)
|
||||
nd.Pin(name='b1').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
|
||||
|
||||
nd.Pin(name='a1').put(Ox[0]/2+Ix[0]/2,Oy[0]/2+Iy[0]/2,Ain-90)
|
||||
nd.Pin(name='b1').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
|
||||
Reference in New Issue
Block a user