Files

772 lines
36 KiB
Python

from typing import Any, Optional
import nazca as nd
import numpy as np
from ...geometry import *
from ....technologies import *
from ...electronics import Vias
from ...electronics import ISL
import nazca.interconnects as IC
from ...basic import __xs_exist__,__cell_arg__
from ..pic import taper_xs2xs
from ...routing import Route
# class Route(IC.Interconnect):
# pass
class waveguide:
"""
waveguide primitive component.
This component builds the waveguide layout cell.
Parameters
----------
w_heater : float, optional
Width parameter in microns. Default is 2.5.
L_wg : int, optional
Length parameter in microns. Default is 150.
L_heater : int, optional
Length parameter in microns. Default is 150.
w_metal : float, optional
Width parameter in microns. Default is 10.
xs_heater : str, optional
Layer or cross-section name used by the device. Default is 'heater'.
xs_metal : str, optional
Layer or cross-section name used by the device. Default is 'metal'.
xs_wg : 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.
w_port : Optional[float], optional
Width parameter in microns. Default is None.
Ltp : Any, optional
Length parameter in microns. Default is None.
via_h2m : Any, optional
Via definition used between heater and metal layers. Default is None.
isl : Any, optional
Isolation-trench definition used by the electrical layout. Default is None.
euler_bend : bool, optional
Value for the euler_bend parameter. Default is False.
Rmin : int, optional
Radius parameter in microns. Default is 5.
thin_attach : bool, optional
Value for the thin_attach parameter. Default is False.
UPPER_ISL : bool, optional
Value for the UPPER_ISL parameter. Default is True.
LOWER_ISL : bool, optional
Length parameter in microns. Default is True.
shape : str, optional
Value for the shape parameter. Default is 'strip'.
R_bend : int, optional
Radius parameter in microns. Default is 10.
ubend_offset : int, optional
Value for the ubend_offset parameter. Default is 20.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
"""
def __init__(self,
w_heater: float=2.5,
L_wg: int=150,
L_heater: int=150,
w_metal: float=10,
xs_heater: str='heater',
xs_metal: str='metal',
xs_wg: str = 'strip',
w_wg: float = 0.45,
w_port: Optional[float] = None,
Ltp: Any = None,
via_h2m: Any = None,
isl: Any = None,
euler_bend: bool = False,
Rmin: int = 5,
thin_attach: bool = False,
UPPER_ISL: bool = True,
LOWER_ISL: bool = True,
shape: str = 'strip',
R_bend: int=10,
ubend_offset: int=20,
show_pins: bool=False) -> None:
""" Revised in 2022.12.30, to simplify the function logic """
if (w_heater>0 and xs_heater!=None and xs_metal!=None):
xs_heater = __xs_exist__(xs=xs_heater,para_name="xs_heater",func_name="mxpic::passive::waveguide")
xs_metal = __xs_exist__(xs=xs_metal,para_name="xs_metal",func_name="mxpic::passive::waveguide")
if (w_port==None):
w_port = w_wg
if (Ltp==None):
Ltp=0
""" Generating the basic via_h2m """
if (via_h2m==None):
vias = Vias(xs=None,area=w_metal,sz=0,spacing=0,xs_l1=xs_heater,xs_l2=xs_metal) ## only putting metal blocks
else:
if (hasattr(via_h2m,"cell")):
vias = via_h2m
else :
vias = Vias(xs=via_h2m.xs,area=w_metal,sz=via_h2m.sz,spacing=via_h2m.spacing,xs_l1=xs_heater,xs_l2=xs_metal) ## placing vias
if (L_heater==None):
L_heater = L_wg
### Adding the straight waveguide
if (shape=='strip'):
with nd.Cell(instantiate=False) as C:
nd.taper(length=Ltp,width1=w_port,width2=w_wg,xs=xs_wg).put(-L_wg/2,0,0)
nd.strt(length=L_wg-2*Ltp,width=w_wg,xs=xs_wg).put()
nd.taper(length=Ltp,width2=w_port,width1=w_wg,xs=xs_wg).put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',width=w_wg).put(-L_wg/2,0,180)
nd.Pin(name='opt_a1',width=w_wg,type="optical:").put(-L_wg/2,0,180)
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',width=w_wg).put( L_wg/2,0,0)
nd.Pin(name='opt_b1',width=w_wg,type="optical:").put( L_wg/2,0,0)
L_heater = np.min([L_wg,L_heater])
### placing heaters
if (w_heater>0 and xs_heater!=None and xs_metal!=None):
nd.strt(length=L_heater,width=w_heater,xs=xs_heater).put(-L_heater/2,0,0)
if (thin_attach==False):
# VIAL = vias.cell.put(-L_heater/2+w_metal/2,0,180,flip=1)
# VIAR = vias.cell.put( L_heater/2-w_metal/2,0,0)
VIAL = vias.cell.put(-L_heater/2,0,180,flip=1)
VIAR = vias.cell.put( L_heater/2,0,0)
else :
# VIAL = vias.cell.put(-L_heater/2+w_metal/2,0,180,flip=1)
# VIAR = vias.cell.put( L_heater/2-w_metal/2,0,0)
VIAL = vias.cell.put(-L_heater/2,0,180,flip=1)
VIAR = vias.cell.put( L_heater/2,0,0)
nd.Pin(name='ep1',width=w_metal,pin=VIAL.pin['b0']).put()
nd.Pin(name='en1',width=w_metal,pin=VIAR.pin['b0']).put()
if (isl!=None):
if (UPPER_ISL):
ISL(length=L_heater, ## variables
xs=isl.xs,width=isl.width,spacing=isl.spacing,Lmax=isl.Lmax ## default parameters
).cell.put(-L_heater/2,-w_metal/2-isl.width/2-isl.sp_isl_metal,0)
if (LOWER_ISL):
ISL(length=L_heater, ## variables
xs=isl.xs,width=isl.width,spacing=isl.spacing,Lmax=isl.Lmax ## default parameters
).cell.put(-L_heater/2,w_metal/2+isl.width/2+isl.sp_isl_metal,0)
### Adding the bend shape waveguide
elif (shape == 'ubend'):
with nd.Cell(instantiate=False) as C:
if (euler_bend):
bd = Clothoid(R=[R_bend,Rmin,R_bend],w=[w_wg,w_wg],A=[0,90,180],xs=xs_wg,n_points=64)
ubend_offset = bd.sz[1]
R_ht = bd.sz[1]/2
L_wg_mid = 0
L_wg_side = (L_wg-L_wg_mid)/2-R_bend
wg_in = nd.strt(length=L_wg_side,width=w_wg,xs=xs_wg).put(-ubend_offset/2,0,90)
bd.cell.put(flip=1)
wg_out = nd.strt(length=L_wg_side,width=w_wg,xs=xs_wg).put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',width=w_wg,pin=wg_in.pin['a0']).put()
nd.Pin(name='opt_a1',width=w_wg,pin=wg_in.pin['a0'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',width=w_wg,pin=wg_out.pin['b0']).put()
nd.Pin(name='opt_b1',width=w_wg,pin=wg_out.pin['b0'],type="optical:").put()
else:
R_ht = R_bend
L_wg_mid = ubend_offset-R_bend*2
L_wg_side = (L_wg-L_wg_mid-R_bend*np.pi)/2
wg_in = nd.strt(length=L_wg_side,width=w_wg,xs=xs_wg).put(-ubend_offset/2,0,90)
nd.bend(radius=R_bend,width=w_wg,xs=xs_wg).put(flip=1)
nd.strt(length=L_wg_mid,width=w_wg,xs=xs_wg).put()
nd.bend(radius=R_bend,width=w_wg,xs=xs_wg).put(flip=1)
wg_out = nd.strt(length=L_wg_side,width=w_wg,xs=xs_wg).put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',width=w_wg,pin=wg_in.pin['a0']).put()
nd.Pin(name='opt_a1',width=w_wg,pin=wg_in.pin['a0'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',width=w_wg,pin=wg_out.pin['b0']).put()
nd.Pin(name='opt_b1',width=w_wg,pin=wg_out.pin['b0'],type="optical:").put()
L_heater = np.max([L_wg_mid+R_bend+w_metal*2,L_heater])
L_heater = np.min([L_wg,L_heater])
if (w_heater>0):
L_ht_side = (L_heater-L_wg_mid)/2-R_bend
ht_in = nd.strt(length=L_ht_side,width=w_heater,xs=xs_heater).put(-ubend_offset/2,L_wg_side-L_ht_side,90)
nd.bend(radius=R_ht,width=w_heater,xs=xs_heater).put(flip=1)
nd.strt(length=(ubend_offset-R_ht*2),width=w_heater,xs=xs_heater).put()
nd.bend(radius=R_ht,width=w_heater,xs=xs_heater).put(flip=1)
ht_out = nd.strt(length=L_ht_side,width=w_heater,xs=xs_heater).put()
# nd.strt(length=w_metal,width=w_metal,xs=xs_metal).put(-ubend_offset/2,L_wg_side-L_ht_side,90)
# nd.strt(length=w_metal,width=w_metal,xs=xs_heater).put(-ubend_offset/2,L_wg_side-L_ht_side,90)
# nd.strt(length=w_metal,width=w_metal,xs=xs_metal).put( ubend_offset/2,L_wg_side-L_ht_side,90)
# nd.strt(length=w_metal,width=w_metal,xs=xs_heater).put( ubend_offset/2,L_wg_side-L_ht_side,90)
# print(xs_via,sz_via_h2m,sp_via_h2m)
VIAL = vias.cell.put(ubend_offset/2,L_wg_side-L_ht_side+w_metal/2,-90)
VIAR = vias.cell.put(-ubend_offset/2,L_wg_side-L_ht_side+w_metal/2,-90,flip=1)
nd.Pin(name='ep1',width=w_metal,pin=VIAL.pin['b0']).put()
nd.Pin(name='en1',width=w_metal,pin=VIAR.pin['b0']).put()
""" Placing Isolation trench with the parameter pack <isl> """
if (isl!=None):
## placing outer
L_side = L_ht_side + R_bend + w_heater/2 + isl.sp_isl_xs*2 - w_metal
ISL(xs=isl.xs,width=isl.w_idth,length=L_side).cell.put(-ubend_offset/2-w_metal/2-isl.sp_isl_xs,L_wg_side-L_ht_side+w_metal+isl.sp_isl_xs,90)
ISL(xs=isl.xs,width=isl.w_idth,length=L_side).cell.put( ubend_offset/2+w_metal/2+isl.sp_isl_xs,L_wg_side-L_ht_side+w_metal+isl.sp_isl_xs,90)
L_upper = isl.w_idth+2*(ubend_offset/2+w_metal/2+isl.sp_isl_xs)
Y_upper = L_wg_side-L_ht_side+w_metal+isl.sp_isl_xs+L_side-isl.w_idth/2
ISL(xs=isl.xs,width=isl.w_idth,length=L_upper).cell.put( -L_upper/2,Y_upper,0)
if ((ubend_offset/2-w_metal/2-isl.sp_isl_xs)*2-isl.w_idth>5):
L_side = L_ht_side
ISL(xs=isl.xs,width=isl.w_idth,length=L_side).cell.put(-ubend_offset/2+w_metal/2+(isl.sp_isl_xs),L_wg_side-L_ht_side,90)
ISL(xs=isl.xs,width=isl.w_idth,length=L_side).cell.put( ubend_offset/2-w_metal/2-(isl.sp_isl_xs),L_wg_side-L_ht_side,90)
if (show_pins):
nd.put_stub(pinsize=2)
self.cell = C
self.L = L_heater+w_metal*2
""" NEW CLASS:: 2023.03.21, two stage phase shifters, to replace PS_3wg """
class PS_2st:
"""
PS 2st primitive component.
This component builds the PS 2st layout cell.
Parameters
----------
xs_wg : 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.5.
w1 : float, optional
Width parameter in microns. Default is 0.7.
w2 : float, optional
Width parameter in microns. Default is 0.9.
L1 : int, optional
Length parameter in microns. Default is 10.
L2 : int, optional
Length parameter in microns. Default is 55.
L_wg : int, optional
Length parameter in microns. Default is 0.
L_tp : int, optional
Length parameter in microns. Default is 1.
L12 : Any, optional
Length parameter in microns. Default is None.
L_ht : Any, optional
Length parameter in microns. Default is None.
xs_heater : str, optional
Layer or cross-section name used by the device. Default is 'heater'.
xs_metal : str, optional
Layer or cross-section name used by the device. Default is 'metal'.
w_heater : float, optional
Width parameter in microns. Default is 2.5.
w_metal : float, optional
Width parameter in microns. Default is 8.
via_h2m : Any, optional
Via definition used between heater and metal layers. Default is None.
isl : Any, optional
Isolation-trench definition used by the electrical layout. Default is None.
UPPER_ISL : bool, optional
Value for the UPPER_ISL parameter. Default is True.
LOWER_ISL : bool, optional
Length parameter in microns. Default is True.
R_bend : int, optional
Radius parameter in microns. Default is 10.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
"""
def __init__(self,
xs_wg: str = 'strip',
w_wg: float = 0.5,
w1: float = 0.7,
w2: float = 0.9,
L1: int = 10,
L2: int = 55,
L_wg: int = 0,
L_tp: int = 1,
L12: Any = None,
L_ht: Any = None,
xs_heater: str='heater',
xs_metal: str='metal',
w_heater: float=2.5,
w_metal: float = 8,
via_h2m: Any = None,
isl: Any = None,
UPPER_ISL: bool = True,
LOWER_ISL: bool = True,
R_bend: int=10,
show_pins: bool=False) -> None:
self.xs_wg = xs_wg
self.w_wg = w_wg
self.w1 = w1
self.w2 = w2
self.L1 = L1
self.L2 = L2
self.L_wg = L_wg
self.L_tp = L_tp
self.L_ht = L_ht
self.xs_heater = xs_heater
self.xs_metal = xs_metal
self.w_heater = w_heater
self.w_metal = w_metal
self.via_h2m = via_h2m
self.isl = isl
self.R_bend = R_bend
self.show_pins = show_pins
self.R_bend = R_bend
if (L12==None):
L12 = L_tp
self.L12 = L12
self.cell = self.generate_gds()
def generate_gds(self):
with nd.Cell(instantiate=False) as C:
""" Generating the basic via_h2m """
if (self.via_h2m==None):
vias = Vias(xs=None,area=self.w_metal,sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## only putting metal blocks
else:
if (hasattr(self.via_h2m,"cell")):
vias = self.via_h2m
else :
vias = Vias(xs=self.via_h2m.xs,
area=self.w_metal,
sz=self.via_h2m.sz,
spacing=self.via_h2m.spacing,
xs_l1=self.xs_heater,
xs_l2=self.xs_metal) ## placing vias
pic_strip = Route(radius=self.R_bend,width=self.w_wg,xs=self.xs_wg)
start = nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put(0,0,90)
nd.taper(length=self.L_tp,width1=self.w_wg,width2=self.w1,xs=self.xs_wg).put()
nd.strt(length=self.L1,width=self.w1,xs=self.xs_wg).put()
nd.taper(length=self.L12,width1=self.w1,width2=self.w2,xs=self.xs_wg).put()
nd.strt(length=self.L2,width=self.w2,xs=self.xs_wg).put()
nd.taper(length=self.L_tp,width1=self.w2,width2=self.w_wg,xs=self.xs_wg).put()
nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put()
pic_strip.bend_route(angle=180).put(flip=1)
nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put()
nd.taper(length=self.L_tp,width1=self.w_wg,width2=self.w2,xs=self.xs_wg).put()
nd.strt(length=self.L2,width=self.w2,xs=self.xs_wg).put()
nd.taper(length=self.L12,width1=self.w2,width2=self.w1,xs=self.xs_wg).put()
nd.strt(length=self.L1,width=self.w1,xs=self.xs_wg).put()
nd.taper(length=self.L_tp,width1=self.w1,width2=self.w_wg,xs=self.xs_wg).put()
end = nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put()
L_arm = self.L_wg + self.L_tp + self.L1 + self.L12 + self.L2 + self.L_tp + self.L_wg
if (self.L_ht==None):
self.L_ht = (L_arm + self.R_bend*np.pi/2)*2
if (self.w_heater>0) :
# L_wg_side = (L_wg-L_wg_mid)/2-R_bend
# VIA = Vias(xs=self.via_h2m.xs,sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,sp_via_xs=self.via_h2m.sp_via_xs,
# xs_l1=self.xs_heater,xs_l2=self.xs_metal,
# area=self.w_metal)
Ly_ht = (self.L_ht - self.R_bend*np.pi)/2
nd.strt(length=Ly_ht,width=self.w_heater,xs=self.xs_heater).put(0,L_arm-Ly_ht,90)
nd.bend(radius=self.R_bend,angle=180,xs=self.xs_heater,width=self.w_heater).put(flip=1)
nd.strt(length=Ly_ht,width=self.w_heater,xs=self.xs_heater).put()
VIA_L = vias.cell.put(0,L_arm-Ly_ht+self.w_metal/2,-90,flip=1)
VIA_R = vias.cell.put(self.R_bend*2,L_arm-Ly_ht+self.w_metal/2,-90)
# nd.Pin(name='ep1',pin=VIA_L.pin['b0'].move(0,0,-90)).put()
# nd.Pin(name='en1',pin=VIA_R.pin['b0'].move(0,0,90)).put()
nd.Pin(name='ep1',pin=VIA_L.pin['b0']).put()
nd.Pin(name='en1',pin=VIA_R.pin['b0']).put()
""" Placing Isolation trench with the parameter pack <isl> """
if (self.isl!=None):
## placing outer
L_side = abs(L_arm+self.R_bend+self.w_heater/2+self.isl.width/2+self.isl.sp_isl_metal - VIA_L.pin['b0'].y - self.w_metal/2-self.isl.sp_isl_metal)
ISL(xs=self.isl.xs,width=self.isl.width,length=L_side,spacing=self.isl.spacing,Lmax=self.isl.Lmax).cell.put( -self.isl.sp_isl_metal-self.w_heater/2-self.isl.width/2,
VIA_L.pin['b0'].y+self.w_metal/2+self.isl.sp_isl_metal,90)
ISL(xs=self.isl.xs,width=self.isl.width,length=L_side,spacing=self.isl.spacing,Lmax=self.isl.Lmax).cell.put( end.pin['b0'].x+self.isl.sp_isl_metal+self.w_heater/2+self.isl.width/2,
VIA_L.pin['b0'].y+self.w_metal/2+self.isl.sp_isl_metal,90)
L_top = abs(end.pin['b0'].x)+self.isl.width+(self.isl.sp_isl_metal+self.w_heater/2+self.isl.width/2)*2
ISL(xs=self.isl.xs,width=self.isl.width,length=L_top,spacing=self.isl.spacing,Lmax=self.isl.Lmax).cell.put( -self.isl.sp_isl_metal-self.w_heater/2-self.isl.width,
VIA_L.pin['b0'].y+self.w_metal/2+self.isl.sp_isl_metal + L_side,0)
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',pin=start.pin['a0']).put()
nd.Pin(name='opt_a1',pin=start.pin['a0'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',pin=end.pin['b0']).put()
nd.Pin(name='opt_b1',pin=end.pin['b0'],type="optical:").put()
return C
class PS_2st_Straight:
"""
PS 2st Straight primitive component.
This component builds the PS 2st Straight layout cell.
Parameters
----------
xs_wg : 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.5.
w1 : float, optional
Width parameter in microns. Default is 0.7.
w2 : float, optional
Width parameter in microns. Default is 0.9.
L1 : int, optional
Length parameter in microns. Default is 10.
L2 : int, optional
Length parameter in microns. Default is 55.
L_wg : int, optional
Length parameter in microns. Default is 0.
L_tp : int, optional
Length parameter in microns. Default is 1.
L12 : Any, optional
Length parameter in microns. Default is None.
L_ht : Any, optional
Length parameter in microns. Default is None.
xs_heater : str, optional
Layer or cross-section name used by the device. Default is 'heater'.
xs_metal : str, optional
Layer or cross-section name used by the device. Default is 'metal'.
w_heater : float, optional
Width parameter in microns. Default is 2.5.
w_metal : float, optional
Width parameter in microns. Default is 8.
via_h2m : Any, optional
Via definition used between heater and metal layers. Default is None.
isl : Any, optional
Isolation-trench definition used by the electrical layout. Default is None.
UPPER_ISL : bool, optional
Value for the UPPER_ISL parameter. Default is True.
LOWER_ISL : bool, optional
Length parameter in microns. Default is True.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
"""
def __init__(self,
xs_wg: str = 'strip',
w_wg: float = 0.5,
w1: float = 0.7,
w2: float = 0.9,
L1: int = 10,
L2: int = 55,
L_wg: int = 0,
L_tp: int = 1,
L12: Any = None,
L_ht: Any = None,
xs_heater: str='heater',
xs_metal: str='metal',
w_heater: float=2.5,
w_metal: float = 8,
via_h2m: Any = None,
isl: Any = None,
UPPER_ISL: bool = True,
LOWER_ISL: bool = True,
show_pins: bool=False) -> None:
self.xs_wg = xs_wg
self.w_wg = w_wg
self.w1 = w1
self.w2 = w2
self.L1 = L1
self.L2 = L2
self.L_wg = L_wg
self.L_tp = L_tp
self.L_ht = L_ht
self.xs_heater = xs_heater
self.xs_metal = xs_metal
self.w_heater = w_heater
self.w_metal = w_metal
self.via_h2m = via_h2m
self.isl = isl
self.show_pins = show_pins
if (L12==None):
L12 = L_tp
self.L12 = L12
self.cell = self.generate_gds()
def generate_gds(self):
with nd.Cell(instantiate=False) as C:
""" Generating the basic via_h2m """
# if (self.via_h2m==None):
# vias = Vias(xs=None,area=self.w_metal,sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal).cell ## only putting metal blocks
# else:
# if (hasattr(self.via_h2m,"cell")):
# vias = self.via_h2m
# else :
# vias = Vias(xs=self.via_h2m.xs,area=self.w_metal,sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## placing vias
start = nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put(0,0,0)
nd.taper(length=self.L_tp,width1=self.w_wg,width2=self.w1,xs=self.xs_wg).put()
nd.strt(length=self.L1,width=self.w1,xs=self.xs_wg).put()
nd.taper(length=self.L12,width1=self.w1,width2=self.w2,xs=self.xs_wg).put()
nd.strt(length=self.L2,width=self.w2,xs=self.xs_wg).put()
nd.taper(length=self.L_tp,width1=self.w2,width2=self.w_wg,xs=self.xs_wg).put()
end = nd.strt(length=self.L_wg,width=self.w_wg,xs=self.xs_wg).put()
L_arm = self.L_wg + self.L_tp + self.L1 + self.L12 + self.L2 + self.L_tp + self.L_wg
self.L_arm = L_arm
if (self.L_ht==None):
self.L_ht = L_arm
if (self.w_heater>0) :
# L_wg_side = (L_wg-L_wg_mid)/2-R_bend
""" Generating the basic via_h2m """
if (self.via_h2m==None):
vias = Vias(xs=None,area=self.w_metal,sz=0,spacing=0,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## only putting metal blocks
else:
if (hasattr(self.via_h2m,"cell")):
vias = self.via_h2m
else :
vias = Vias(xs=self.via_h2m.xs,area=self.w_metal,sz=self.via_h2m.sz,spacing=self.via_h2m.spacing,xs_l1=self.xs_heater,xs_l2=self.xs_metal) ## placing vias
Ly_ht = (self.L_ht )/2
nd.strt(length=self.L_ht,width=self.w_heater,xs=self.xs_heater).put(self.L_arm/2 - self.L_ht/2,0,0)
VIA_L = vias.cell.put(self.L_arm/2 - self.L_ht/2,0,180)
VIA_R = vias.cell.put(self.L_arm/2 + self.L_ht/2,0,0)
nd.Pin(name='ep1',pin=VIA_L.pin['b0']).put()
nd.Pin(name='en1',pin=VIA_R.pin['b0']).put()
""" Placing Isolation trench with the parameter pack <isl> """
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',pin=start.pin['a0']).put()
nd.Pin(name='opt_a1',pin=start.pin['a0'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',pin=end.pin['b0']).put()
nd.Pin(name='opt_b1',pin=end.pin['b0'],type="optical:").put()
return C
class PSR_1x2:
"""
PSR 1x2 primitive component.
This component builds the PSR 1x2 layout cell.
Parameters
----------
PSR : Any
Polarization splitter-rotator cell or component used by this composite.
MDM : Any
Mode multiplexer/demultiplexer cell or component used by this composite.
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.
L_tp : int, optional
Length parameter in microns. Default is 15.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
"""
def __init__(self,PSR: Any,MDM: Any,xs: str='strip',w_wg: float=0.45,L_tp: int=15,show_pins: bool=False) -> None:
self.w_wg = w_wg
PSR_cell = __cell_arg__(arg=PSR,arg_name="PSR",func_name="mxpicp::functional::PSR_1x2")
MDM_cell = __cell_arg__(arg=MDM,arg_name="MDM",func_name="mxpicp::functional::PSR_1x2")
with nd.Cell(instantiate=False) as C:
## revised in 2026.06.07 by Qin Yue
# legacy: PSR_inst = PSR_cell.put('a1',0,0,0)
PSR_inst = PSR_cell.put('opt_a1',0,0,0)
## revised in 2026.06.07 by Qin Yue
# legacy: MDM_inst = MDM_cell.put('b1',PSR_inst.pin['b1'].x+L_tp,PSR_inst.pin['b1'].y,PSR_inst.pin['b1'].a)
MDM_inst = MDM_cell.put('opt_b1',PSR_inst.pin['opt_b1'].x+L_tp,PSR_inst.pin['opt_b1'].y,PSR_inst.pin['opt_b1'].a)
# taper_xs2xs(xs_1=MDM_inst.pin['b1'].xs,xs_2=PSR_inst.pin['b1'].xs,w_1=MDM_inst.pin['b1'].width,w_2=PSR_inst.pin['b1'].width,L_taper=L_tp).cell.put(MDM_inst.pin['b1'])
## revised in 2026.06.07 by Qin Yue
# legacy: nd.taper(length=L_tp,width1=PSR_inst.pin['b1'].width,width2=MDM_inst.pin['b1'].width,xs=xs).put(PSR_inst.pin['b1'])
nd.taper(length=L_tp,width1=PSR_inst.pin['opt_b1'].width,width2=MDM_inst.pin['opt_b1'].width,xs=xs).put(PSR_inst.pin['opt_b1'])
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',pin=PSR_inst.pin['a1']).put()
nd.Pin(name='opt_a1',pin=PSR_inst.pin['opt_a1'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',pin=MDM_inst.pin['a1']).put()
nd.Pin(name='opt_b1',pin=MDM_inst.pin['opt_a1'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b2',pin=MDM_inst.pin['a2']).put()
nd.Pin(name='opt_b2',pin=MDM_inst.pin['opt_a2'],type="optical:").put()
if (show_pins):
nd.put_stub(pinsize=2)
self.cell = C
## revised in 2026.06.07 by Qin Yue
# legacy: self.L = np.abs(self.cell.pin['a1'].x - np.max([self.cell.pin['b1'].x,self.cell.pin['b2'].x]))
self.L = np.abs(self.cell.pin['opt_a1'].x - np.max([self.cell.pin['opt_b1'].x,self.cell.pin['opt_b2'].x]))
def generate_test_gds(self,gc,gc_IN=None,gc2gc_dX=140,gc2gc_dY=40):
with nd.Cell(name=self.cell.cell_name+"_test", instantiate=False) as C:
gc_cell = __cell_arg__(arg=gc,arg_name="gc",func_name="mxpicp::functional::PSR_1x2::generate_test_gds")
if (gc_IN==None):
gc_IN = gc
else :
gc_IN = __cell_arg__(arg=gc,arg_name="gc_IN",func_name="mxpicp::functional::PSR_1x2::generate_test_gds")
GC_I = gc_IN.put('g1',-gc2gc_dX/2,0,180)
GC_OU = gc.put('g1', gc2gc_dX/2,-gc2gc_dY/2,0)
GC_OD = gc.put('g1', gc2gc_dX/2,gc2gc_dY/2,0)
PSR_test = self.cell.put(-self.L/2,0,0)
stripe=Route(radius=10, width=self.w_wg, xs="strip")
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.taper_p2p(pin1=PSR_test.pin['a1'],pin2=GC_I.pin['g1'],arrow=False).put()
stripe.taper_p2p(pin1=PSR_test.pin['opt_a1'],pin2=GC_I.pin['g1'],arrow=False).put()
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.sbend_p2p(pin1=PSR_test.pin['b1'],pin2=GC_OU.pin['g1'],arrow=False).put()
stripe.sbend_p2p(pin1=PSR_test.pin['opt_b1'],pin2=GC_OU.pin['g1'],arrow=False).put()
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.sbend_p2p(pin1=PSR_test.pin['b2'],pin2=GC_OD.pin['g1'],arrow=False).put()
stripe.sbend_p2p(pin1=PSR_test.pin['opt_b2'],pin2=GC_OD.pin['g1'],arrow=False).put()
return C
class Brag_WDM:
"""
Brag WDM primitive component.
This component builds the Brag WDM layout cell.
Parameters
----------
Brag : Any
Bragg grating cell or component used by this composite.
MDM : Any
Mode multiplexer/demultiplexer cell or component used by this composite.
w_wg : float, optional
Width parameter in microns. Default is 0.45.
L_tp : int, optional
Length parameter in microns. Default is 30.
show_pins : bool, optional
Whether to draw pin markers in the generated layout. Default is False.
"""
def __init__(self,Brag: Any,MDM: Any,w_wg: float=0.45,L_tp: int=30,show_pins: bool=False) -> None:
self.w_wg = w_wg
Brag_cell = __cell_arg__(arg=Brag,arg_name="Brag",func_name="mxpicp::functional::Brag_WDM")
MDM_cell = __cell_arg__(arg=MDM,arg_name="MDM",func_name="mxpicp::functional::Brag_WDM")
with nd.Cell(instantiate=False) as C:
## revised in 2026.06.07 by Qin Yue
# legacy: Brag_inst = Brag_cell.put('a1',0,0,0)
Brag_inst = Brag_cell.put('opt_a1',0,0,0)
## revised in 2026.06.07 by Qin Yue
# legacy: MDM_inst = MDM_cell.put('b1',Brag_inst.pin['a1'].x-L_tp,Brag_inst.pin['a1'].y,Brag_inst.pin['a1'].a)
MDM_inst = MDM_cell.put('opt_b1',Brag_inst.pin['opt_a1'].x-L_tp,Brag_inst.pin['opt_a1'].y,Brag_inst.pin['opt_a1'].a)
## revised in 2026.06.07 by Qin Yue
# legacy: nd.taper(length=L_tp,width1=Brag_inst.pin['b1'].width,width2=MDM_inst.pin['b1'].width,xs='strip').put(Brag_inst.pin['a1'])
nd.taper(length=L_tp,width1=Brag_inst.pin['opt_b1'].width,width2=MDM_inst.pin['opt_b1'].width,xs='strip').put(Brag_inst.pin['opt_a1'])
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b1',pin=Brag_inst.pin['b1']).put()
nd.Pin(name='opt_b1',pin=Brag_inst.pin['opt_b1'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='a1',pin=MDM_inst.pin['a1']).put()
nd.Pin(name='opt_a1',pin=MDM_inst.pin['opt_a1'],type="optical:").put()
## revised in 2026.06.07 by Qin Yue
# legacy: nd.Pin(name='b2',pin=MDM_inst.pin['a2']).put()
nd.Pin(name='opt_b2',pin=MDM_inst.pin['opt_a2'],type="optical:").put()
if (show_pins):
nd.put_stub(pinsize=2)
self.cell = C
## revised in 2026.06.07 by Qin Yue
# legacy: self.L = np.abs(self.cell.pin['a1'].x - np.max([self.cell.pin['b1'].x,self.cell.pin['b2'].x]))
self.L = np.abs(self.cell.pin['opt_a1'].x - np.max([self.cell.pin['opt_b1'].x,self.cell.pin['opt_b2'].x]))
def generate_test_gds(self,gc,gc2gc_dX=140,gc2gc_dY=40,dX_offset=50):
with nd.Cell(name=self.cell.cell_name+"_test", instantiate=False) as C:
gc_cell = __cell_arg__(arg=gc,arg_name="gc",func_name="mxpicp::functional::Brag_WDM::generate_test_gds")
# if (isinstance(gc,nd.Cell)):
# gc_cell = gc
# elif (hasattr(gc,"cell")):
# gc_cell = gc.cell
GC_I = gc_cell.put('g1',-gc2gc_dX/2,0,180)
GC_O1 = gc_cell.put('g1', gc2gc_dX/2,0,0)
GC_O2 = gc_cell.put('g1', gc2gc_dX/2+dX_offset,-gc2gc_dY,0)
## revised in 2026.06.07 by Qin Yue
# legacy: INSTR = self.cell.put('a1',-self.L/2,0,0)
INSTR = self.cell.put('opt_a1',-self.L/2,0,0)
stripe=Route(radius=10, width=self.w_wg, xs="strip")
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.taper_p2p(pin1=INSTR.pin['a1'],pin2=GC_I.pin['g1'],arrow=False).put()
stripe.taper_p2p(pin1=INSTR.pin['opt_a1'],pin2=GC_I.pin['g1'],arrow=False).put()
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.taper(pin=INSTR.pin['b1'],width1=INSTR.pin['b1'].width,width2=GC_O1.pin['g1'].width,length=10,arrow=False).put()
stripe.taper(pin=INSTR.pin['opt_b1'],width1=INSTR.pin['opt_b1'].width,width2=GC_O1.pin['g1'].width,length=10,arrow=False).put()
stripe.sbend_p2p(pin2=GC_O1.pin['g1'],arrow=False).put()
## revised in 2026.06.07 by Qin Yue
# legacy: stripe.ubend_p2p(pin1=INSTR.pin['b2'],pin2=GC_O2.pin['g1'],arrow=False).put()
stripe.ubend_p2p(pin1=INSTR.pin['opt_b2'],pin2=GC_O2.pin['g1'],arrow=False).put()
return C