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

297 lines
12 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
from scipy.interpolate import CubicSpline
from ...basic import __cell_arg__
class YBranch:
"""
YBranch primitive component.
This component builds the YBranch layout cell.
Parameters
----------
name : str, optional
Unique identifier for the device cell. Default is None.
xs : str, optional
Layer or cross-section name used by the device. Default is 'strip'.
w : list or np.ndarray, optional
Width parameter in microns. Default is [1.2, 1.0, 1.8, 1.2, 1.0, 1.2, 1.2].
L : float, optional
Length parameter in microns. Default is 6.
R_att : float, optional
Radius parameter in microns. Default is 10.
A_att : float, optional
Angle parameter in degrees. Default is 10.
w_port : float, optional
Width parameter in microns. Default is 0.45.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
sharp_patch : bool, optional
Whether to add geometry patches for sharp corners or cladding continuity. Default is True.
res : float, optional
Value for the res parameter. Default is 0.1.
"""
def __init__(self,
name : str = None,
xs : str = 'strip',
w : 'list|np.ndarray' = [1.2,1.0,1.8,1.2,1.0,1.2,1.2],
L : float = 6,
R_att : float = 10,
A_att : float = 10,
w_port : float = 0.45,
show_pins : bool = False,
sharp_patch : bool = True,
res : float = 0.1,
) -> None:
"""_summary_
Args:
name (str, optional): _description_. Defaults to None.
xs (str, optional): _description_. Defaults to 'strip'.
layer (str, optional): _description_. Defaults to None.
w_in (float, optional): input port width. Defaults to 1.2.
w_out (float, optional): output port width, for main body. Defaults to 1.2.
w (list|np.ndarray, optional): _description_. Defaults to [1.2,1.0,1.8,1.2,1.0,1.2].
L (float, optional): _description_. Defaults to 6.
R_att (float, optional): _description_. Defaults to 10.
A_att (float, optional): _description_. Defaults to 10.
w_port (float, optional): _description_. Defaults to 0.45.
show_pins (bool, optional): _description_. Defaults to False.
res (float, optional): _description_. Defaults to 0.1.
"""
self.name = name
if (name!=None):
self.instantiate = True
else :
self.instantiate = False
self.w = w
self.L = L
self.res = res
self.R_att = R_att
self.A_att = A_att
self.w_port = w_port
self.xs = xs
self.cell = self.generate_gds(show_pins=show_pins,sharp_patch=sharp_patch)
def generate_gds(self,show_pins=False,sharp_patch=True):
with nd.Cell(name=self.name,instantiate=self.instantiate) as C:
w = np.r_[self.w]
n_sects = len(self.w)-1
res = self.L/n_sects
n_points = int(self.L/self.res)+1
L = np.linspace(0,self.L,n_sects+1)
L_act = np.linspace(0,self.L,n_points)
f = CubicSpline(L,w) ## cubic spline interpolant
w_act = f(L_act)
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
(a1,b1), (a2,b2),c1,c2 = growx
w_cur = w_act*(a1-a2) + (b1-b2)
if (b1!=0 and b2!=0):
w_cur = max(w_cur)*np.ones(np.shape(w_cur))
vtx_x = np.r_[L_act,np.flip(L_act,0)]
vtx_y = np.r_[w_cur/2,-np.flip(w_cur/2,0)]
vtx = np.c_[vtx_x,vtx_y]
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
temp = circle(xs=self.xs,radius=self.R_att,theta_start=0,theta_stop=self.A_att,width=self.w_port).cell.put('a1',self.L,self.w[-1]/2 - self.w_port/2,0)
temp = circle(xs=self.xs,radius=self.R_att,theta_start=-self.A_att,theta_stop=0,width=self.w_port).cell.put('a1',temp.pin['b1'],flip=1)
nd.Pin(name='b1',width=self.w_port).put(temp.pin['b1'])
temp = circle(xs=self.xs,radius=self.R_att,theta_start=0,theta_stop=self.A_att,width=self.w_port).cell.put('a1',self.L,-self.w[-1]/2 + self.w_port/2,0,flip=1)
temp = circle(xs=self.xs,radius=self.R_att,theta_start=-self.A_att,theta_stop=0,width=self.w_port).cell.put('a1',temp.pin['b1'],flip=0)
nd.Pin(name='b2',width=self.w_port).put(temp.pin['b1'])
nd.Pin(name='a1',width=self.w[0]).put(0,0,180)
if (show_pins):
nd.put_stub()
return C
class Ybranch_3wg:
"""
Ybranch 3wg primitive component.
This component builds the Ybranch 3wg layout cell.
Parameters
----------
name : Optional[str], optional
Unique identifier for the device cell. Default is None.
w0 : float, optional
Width parameter in microns. Default is 0.4.
w1 : float, optional
Width parameter in microns. Default is 0.2.
gap : float, optional
Spacing or gap parameter in microns. Default is 0.18.
Lcp : float, optional
Length parameter in microns. Default is 20.
xs : str, optional
Layer or cross-section name used by the device. Default is 'strip'.
w_wg : float, optional
Width parameter in microns. Default is 0.45.
R0 : float, optional
Radius parameter in microns. Default is 10.
angle : float, optional
Angle parameter in degrees. Default is 20.
L_attach : float, optional
Length parameter in microns. Default is 3.
L_in_tp : float, optional
Length parameter in microns. Default is 3.
sharp_patch : bool, optional
Whether to add geometry patches for sharp corners or cladding continuity. Default is True.
"""
def __init__(self,
name: Optional[str] = None,
w0:float=0.4,
w1:float=0.2,
gap:float=0.18,
Lcp:float=20,
xs:str='strip',
w_wg:float=0.45,
R0:float=10,
angle:float=20,
L_attach:float=3,
L_in_tp:float=3,
sharp_patch:bool=True) -> None:
'''
Initialization of a symmetric tapered coupler for 3dB coupling
Paras :
1. taper part
- w0/w1 [um] (Default: 0.4/0.2um)
Input/Output taper width
- L [um] (Default: 20um)
taper length of the coupling area
- gap [um] (Default : 0.18)
vertical gap between the taper couplers
- xs [str] (Default : 'strip')
xsection of the waveguide
2. attachment part
- w_wg [um] (Default: 0.45um)
Input&ouput waveguide width
- L_attach [um] (Default: 3um)
Input&ouput straight waveguide length
- L_in_taper [um] (Default: 3um)
Input&ouput taper waveguide length for connecting w_wg and w0
- R0 [um] (Default: 10um)
Ouput bending radius
- angle [um] (Default: 10um)
Ouput bending angle
Cell:
- pins
input [a1]
outpuit [b1][b2]
'''
self.name = name
if (self.name==None):
self.instantiate = False
else :
self.instantiate = True
self.w0 = w0
self.w1 = w1
self.gap = gap
self.Lcp = Lcp
self.xs = xs
self.w_wg = w_wg
self.R0 = R0
self.angle = angle
self.L_attach = L_attach
self.L_in_tp = L_in_tp
self.cell = self.generate_gds(sharp_patch=sharp_patch)
self.L = np.abs(self.cell.pin['a1'].x - self.cell.pin['b1'].x)
def generate_gds(self,sharp_patch,err_asy=0):
with nd.Cell(instantiate=self.instantiate,name=self.name) as C:
w0 = self.w0
w1 = self.w1
Lcp = self.Lcp
gap = self.gap
xs = self.xs
w_wg = self.w_wg
L_attach = self.L_attach
L_in_tp = self.L_in_tp
angle = self.angle
R0 = self.R0
t_mid = nd.taper(width1=w0,width2=w1,length=Lcp,xs=xs).put(0,0,0)
t_u = nd.taper(width2=w0,width1=w1,length=Lcp,xs=xs).put(0,w1/2+w0/2+gap,0)
t_d = nd.taper(width2=w0,width1=w1,length=Lcp,xs=xs).put(0,-(w1/2+w0/2+gap),0)
t_in = nd.taper(width1=w_wg,width2=w0,length=L_in_tp,xs=xs).put(-L_in_tp,0,0)
t_in = nd.strt(width=w_wg,length=L_attach,xs=xs).put(t_in.pin['a0'],flip=0)
nd.Pin(name='a1',pin=t_in.pin['b0']).put()
au = nd.bend(radius=R0,angle=angle,xs=xs,width=w0).put(t_u.pin['b0'],flip=0)
au = nd.bend(radius=R0,angle=angle,xs=xs,width=w0).put(au.pin['b0'],flip=1)
au = nd.taper(width1=w0,width2=w_wg,length=L_in_tp,xs=xs).put(au.pin['b0'],flip=0)
au = nd.strt(width=w_wg,length=L_attach,xs=xs).put(au.pin['b0'],flip=0)
nd.Pin(name='b1',pin=au.pin['b0']).put()
ad = nd.bend(radius=R0,angle=angle,xs=xs,width=w0).put(t_d.pin['b0'],flip=1)
ad = nd.bend(radius=R0,angle=angle,xs=xs,width=w0).put(ad.pin['b0'],flip=0)
ad = nd.taper(width1=w0,width2=w_wg,length=L_in_tp,xs=xs).put(ad.pin['b0'],flip=0)
ad = nd.strt(width=w_wg,length=L_attach,xs=xs).put(ad.pin['b0'],flip=0)
nd.Pin(name='b2',pin=ad.pin['b0']).put()
if (sharp_patch==True):
dY = np.abs(ad.pin['b0'].y-au.pin['b0'].y)+w_wg
for layers,growx,growy,acc in nd.layeriter(xs=xs):
(a1,b1), (a2,b2),c1,c2 = growx
if (b1!=0 and b2!=0):
L_patch = dY*(a1-a2)+(b1-b2)
W_patch = dY*(a1-a2)+(b1-b2)
nd.strt(length=W_patch,width=L_patch,layer=layers).put(ad.pin['b0'].x,0,0)
return C
def generate_test_gds(self,gc,dX_gc2gc=400,dY_gc2gc=80,sharp_patch = True,Rbend=15):
with nd.Cell(instantiate=False) as C:
gc_cell = __cell_arg__(arg=gc,arg_name="gc",func_name="mxpic::Ybranch_3wg::generate_test_gds")
inst = self.cell.put('a1',-self.L/2,0,0)
gc_In = gc_cell.put('g1',-dX_gc2gc/2,0,180)
gc_O1 = gc_cell.put('g1',dX_gc2gc/2, dY_gc2gc/2,0)
gc_O2 = gc_cell.put('g1',dX_gc2gc/2,-dY_gc2gc/2,0)
pic_strip = Route(radius=Rbend,width=self.w_wg,xs=self.xs)
pic_strip.taper(pin=gc_O1.pin['g1'],width1=gc_O1.pin['g1'].width,width2=self.w_wg,length=5,arrow=False)
pic_strip.sbend_p2p(original_function=not sharp_patch, pin2=inst.pin['b1'],arrow=False).put()
pic_strip.taper(pin=gc_O2.pin['g1'],width1=gc_O2.pin['g1'].width,width2=self.w_wg,length=5,arrow=False)
pic_strip.sbend_p2p(original_function=not sharp_patch, pin2=inst.pin['b2'],arrow=False).put()
pic_strip.taper_p2p(pin1=gc_In.pin['g1'],pin2=inst.pin['a1'],arrow=False).put()
return C