189 lines
6.9 KiB
Python
189 lines
6.9 KiB
Python
from typing import Optional
|
|
import nazca as nd
|
|
import numpy as np
|
|
import math
|
|
from ...routing import Route
|
|
|
|
from ...geometry import *
|
|
from ....technologies import *
|
|
|
|
import pandas as pd
|
|
from ...geometry import _my_polygon
|
|
|
|
class Cross:
|
|
"""
|
|
Cross primitive component.
|
|
|
|
This component builds the Cross layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
name : Optional[str], optional
|
|
Unique identifier for the device cell. Default is None.
|
|
L : list, optional
|
|
Length parameter in microns. Default is [0, 1, 2, 3, 4].
|
|
w : list, optional
|
|
Width parameter in microns. Default is [0.5, 0.45, 0.6, 0.4, 0.5].
|
|
xs : str, optional
|
|
Layer or cross-section name used by the device. Default is 'strip'.
|
|
show_pins : bool, optional
|
|
Whether to draw pin markers in the generated layout. Default is False.
|
|
"""
|
|
def __init__(self,
|
|
name: Optional[str] = None,
|
|
L: list = [0 , 1, 2, 3, 4],
|
|
w: list = [0.5,0.45,0.6,0.4,0.5],
|
|
xs : str = 'strip',
|
|
show_pins: bool=False
|
|
) -> None:
|
|
|
|
self.name = name
|
|
if (self.name==None):
|
|
self.instantiate = False
|
|
else :
|
|
self.instantiate = True
|
|
|
|
self.L = L
|
|
self.w = w
|
|
self.xs =xs
|
|
|
|
self.cell = self.generate_gds(show_pins=show_pins)
|
|
|
|
def generate_gds(self,show_pins):
|
|
with nd.Cell(instantiate=self.instantiate,name=self.name) as C:
|
|
|
|
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
L = np.array(self.L)
|
|
|
|
L_arm = np.max(L)
|
|
self.L_arm = L_arm
|
|
w = np.array(self.w) + b1 - b2
|
|
vtx_x = np.r_[L,np.flip(L,0)]
|
|
vtx_y = np.r_[w/2,-np.flip(w,0)/2]
|
|
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(-L_arm,0,0)
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,-L_arm,90)
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,L_arm,-90)
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(L_arm,0,180)
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=self.w[0]).put(-L_arm,0,180)
|
|
nd.Pin(name='opt_a1',width=self.w[0],type="optical:").put(-L_arm,0,180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a2',width=self.w[0]).put( 0,-L_arm,-90)
|
|
nd.Pin(name='opt_a2',width=self.w[0],type="optical:").put( 0,-L_arm,-90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b2',width=self.w[0]).put( 0, L_arm, 90)
|
|
nd.Pin(name='opt_b2',width=self.w[0],type="optical:").put( 0, L_arm, 90)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=self.w[0]).put( L_arm,0,0)
|
|
nd.Pin(name='opt_b1',width=self.w[0],type="optical:").put( L_arm,0,0)
|
|
|
|
if (show_pins):
|
|
nd.put_stub()
|
|
|
|
return C
|
|
|
|
def generate_test_gds(self,gc,num=5,dX_gc2gc=400,w_end=0.2,L_end=10):
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
if (isinstance(gc,nd.Cell)):
|
|
gc_cell = gc
|
|
elif (hasattr(gc,'cell')):
|
|
gc_cell = gc.cell
|
|
else :
|
|
raise Exception("ERROR: In <mxpiv::passive::cross::generate_test_gds>, <gc> is not recongized")
|
|
|
|
res = self.L_arm*2
|
|
|
|
dX = res*1.5
|
|
|
|
pic_strip = Route(radius=10,width=self.w[0],xs=self.xs)
|
|
|
|
gc_In = gc_cell.put('g1',-dX_gc2gc/2,0,180)
|
|
pin_pre = gc_In.pin['g1']
|
|
for _idx_ in range(0,num):
|
|
inst = self.cell.put('a0',_idx_*dX - (num/2 - 1/2)*dX)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: pic_strip.strt_p2p(pin1=pin_pre,pin2=inst.pin['a1'],arrow=False).put()
|
|
pic_strip.strt_p2p(pin1=pin_pre,pin2=inst.pin['opt_a1'],arrow=False).put()
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: pin_pre = inst.pin['b1']
|
|
pin_pre = inst.pin['opt_b1']
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.taper(length=L_end/2,width1=self.w[0],width2=w_end,xs=self.xs).put(inst.pin['b2'])
|
|
nd.taper(length=L_end/2,width1=self.w[0],width2=w_end,xs=self.xs).put(inst.pin['opt_b2'])
|
|
nd.strt(length=L_end/2,width=w_end,xs=self.xs).put()
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.taper(length=L_end/2,width1=self.w[0],width2=w_end,xs=self.xs).put(inst.pin['a2'])
|
|
nd.taper(length=L_end/2,width1=self.w[0],width2=w_end,xs=self.xs).put(inst.pin['opt_a2'])
|
|
nd.strt(length=L_end/2,width=w_end,xs=self.xs).put()
|
|
|
|
gc_Out = gc_cell.put('g1', dX_gc2gc/2,0,0)
|
|
pic_strip.strt_p2p(pin1=pin_pre,pin2=gc_Out.pin['g1'],arrow=False).put()
|
|
|
|
|
|
|
|
return C
|
|
|
|
class Cross_Sine(Cross):
|
|
"""
|
|
Cross Sine primitive component.
|
|
|
|
This component builds the Cross Sine layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
name : Optional[str], optional
|
|
Unique identifier for the device cell. Default is None.
|
|
res : list, optional
|
|
Value for the res parameter. Default is [1, 1, 1, 1].
|
|
w : list, optional
|
|
Width parameter in microns. Default is [0.5, 0.45, 0.6, 0.4, 0.5].
|
|
xs : str, optional
|
|
Layer or cross-section name used by the device. Default is 'strip'.
|
|
n_points : int, optional
|
|
Value for the n_points parameter. Default is 4.
|
|
show_pins : bool, optional
|
|
Whether to draw pin markers in the generated layout. Default is False.
|
|
"""
|
|
def __init__(self,
|
|
name: Optional[str] = None,
|
|
res: list = [1,1,1,1],
|
|
w: list = [0.5,0.45,0.6,0.4,0.5],
|
|
xs : str = 'strip',
|
|
n_points: int = 4,
|
|
show_pins: bool=False
|
|
) -> None:
|
|
|
|
|
|
L = 0
|
|
for _idx_ in range(0,len(w)-1):
|
|
L_sect = np.linspace(L,L+res[_idx_],n_points)
|
|
L = L+res[_idx_]
|
|
w_sect = -np.cos((L_sect-L)/res[_idx_]*np.pi)*(w[_idx_]-w[_idx_+1])/2 + (w[_idx_]+w[_idx_+1])/2
|
|
|
|
if (_idx_==0):
|
|
L_crs = L_sect
|
|
w_crs = w_sect
|
|
else :
|
|
L_crs = np.r_[L_crs,L_sect]
|
|
w_crs = np.r_[w_crs,w_sect]
|
|
|
|
|
|
L_crs = np.r_[L_crs,L_crs[-1]+w_crs[-1]/2]
|
|
w_crs = np.r_[w_crs,w_crs[-1]]
|
|
|
|
|
|
|
|
|
|
super().__init__(name=name,L=L_crs,w=w_crs,xs=xs,show_pins=show_pins)
|
|
|
|
|
|
|
|
|