Files
mxpic_forge/mxpic/components/primitives/pic/cross.py
T

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)