308 lines
13 KiB
Python
308 lines
13 KiB
Python
from typing import Optional
|
|
from turtle import shape
|
|
import nazca as nd
|
|
import numpy as np
|
|
import math
|
|
|
|
from .taper import taper_xs2xs
|
|
from ...routing import Route
|
|
|
|
from ...geometry import *
|
|
from ....technologies import *
|
|
|
|
from ...geometry import _my_polygon,Conchoid
|
|
|
|
""" Mono layer MMI """
|
|
class MMI_ML:
|
|
"""
|
|
MMI ML primitive component.
|
|
|
|
This component builds the MMI ML layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
name : Optional[str], optional
|
|
Unique identifier for the device cell. Default is None.
|
|
L_arm : list, optional
|
|
Length parameter in microns. Default is [10].
|
|
w_arm : list, optional
|
|
Width parameter in microns. Default is [0.45, 1.35].
|
|
xs : str, optional
|
|
Layer or cross-section name used by the device. Default is 'strip'.
|
|
arm_sine_width : bool, optional
|
|
Value for the arm_sine_width parameter. Default is False.
|
|
L_mmi : list, optional
|
|
Length parameter in microns. Default is [10].
|
|
w_mmi : list, optional
|
|
Width parameter in microns. Default is [5, 5].
|
|
mmi_sine_width : bool, optional
|
|
Value for the mmi_sine_width parameter. Default is False.
|
|
sharp_patch : bool, optional
|
|
Whether to add geometry patches for sharp corners or cladding continuity. Default is True.
|
|
show_pins : bool, optional
|
|
Whether to draw pin markers in the generated layout. Default is False.
|
|
res : float, optional
|
|
Value for the res parameter. Default is 0.01.
|
|
N_out : int, optional
|
|
Count or repetition parameter. Default is 3.
|
|
N_in : int, optional
|
|
Count or repetition parameter. Default is 1.
|
|
Dp_out : float, optional
|
|
Value for the Dp_out parameter. Default is 1.5.
|
|
Dp_in : float, optional
|
|
Value for the Dp_in parameter. Default is 1.5.
|
|
"""
|
|
def __init__(self,
|
|
name: Optional[str]=None,
|
|
L_arm: list=[10],
|
|
w_arm: list=[0.45,1.35],
|
|
xs: str = 'strip',
|
|
arm_sine_width: bool=False,
|
|
L_mmi: list = [10],
|
|
w_mmi: list = [5,5],
|
|
|
|
mmi_sine_width: bool=False,
|
|
|
|
sharp_patch: bool=True,
|
|
show_pins: bool = False,
|
|
|
|
res: float = 0.01,
|
|
N_out: int = 3,
|
|
N_in: int = 1,
|
|
Dp_out: float = 1.5,
|
|
Dp_in: float = 1.5,
|
|
) -> None:
|
|
|
|
self.name = name
|
|
if (self.name==None):
|
|
self.instantiate = False
|
|
else :
|
|
self.instantiate = True
|
|
|
|
self.L_arm = L_arm
|
|
self.xs = xs
|
|
self.w_arm = w_arm
|
|
self.arm_sine_width = arm_sine_width
|
|
self.L_mmi = L_mmi
|
|
self.w_mmi = w_mmi
|
|
self.res = res
|
|
self.N_out = N_out
|
|
self.N_in = N_in
|
|
self.Dp_out = Dp_out
|
|
self.Dp_in = Dp_in
|
|
self.mmi_sine_width = mmi_sine_width
|
|
|
|
self.cell = self.generate_gds(sharp_patch=sharp_patch,show_pins=show_pins)
|
|
|
|
self.L = np.sum(self.L_arm)*2+np.sum(self.L_mmi)
|
|
|
|
def generate_gds(self,sharp_patch,show_pins):
|
|
|
|
with nd.Cell(instantiate=self.instantiate,name=self.name) as C:
|
|
L = 0
|
|
Lsg = []
|
|
Wsg = []
|
|
for idx in range(0,len(self.L_arm)):
|
|
n_points = round(self.L_arm[idx]/self.res)+1
|
|
L_sect = np.linspace(L,L+self.L_arm[idx],n_points)
|
|
Lsg = np.r_[Lsg,L_sect]
|
|
if (self.arm_sine_width):
|
|
dw = self.w_arm[idx+1]-self.w_arm[idx]
|
|
w_sect = -np.cos(L_sect/self.L_arm[idx]*pi)*dw + (self.w_arm[idx+1]-self.w_arm[idx])/2
|
|
else:
|
|
w_sect = np.linspace(self.w_arm[idx],self.w_arm[idx+1],n_points)
|
|
Wsg = np.r_[Wsg,w_sect]
|
|
|
|
L = L + self.L_arm[idx]
|
|
|
|
with nd.Cell(instantiate=False) as Arm:
|
|
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
vtx_y = np.r_[Wsg*a1+b1, np.flip(Wsg,0)*a2+b2]
|
|
vtx_x = np.r_[Lsg, np.flip(Lsg,0)]
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=Wsg[0]).put(0,0,180)
|
|
nd.Pin(name='opt_a1',width=Wsg[0],type="optical:").put(0,0,180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=Wsg[-1]).put(L,0,0)
|
|
nd.Pin(name='opt_b1',width=Wsg[-1],type="optical:").put(L,0,0)
|
|
|
|
""" For central MMI """
|
|
L_mmi = 0
|
|
Lsg_mmi = []
|
|
Wsg_mmi = []
|
|
for idx in range(0,len(self.L_mmi)):
|
|
n_points = round(self.L_mmi[idx]/self.res)+1
|
|
L_sect = np.linspace(L_mmi,L_mmi+self.L_mmi[idx],n_points)
|
|
Lsg_mmi = np.r_[Lsg_mmi,L_sect]
|
|
if (self.arm_sine_width):
|
|
dw = self.w_mmi[idx+1]-self.w_mmi[idx]
|
|
w_sect = -np.cos(L_sect/self.L_mmi[idx]*pi)*dw + (self.w_mmi[idx+1]-self.w_mmi[idx])/2
|
|
else:
|
|
w_sect = np.linspace(self.w_mmi[idx],self.w_mmi[idx+1],n_points)
|
|
Wsg_mmi = np.r_[Wsg_mmi,w_sect]
|
|
|
|
L_mmi = L_mmi + self.L_mmi[idx]
|
|
|
|
with nd.Cell(instantiate=False) as MMI:
|
|
for layers,growx,growy,acc in nd.layeriter(xs=self.xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
vtx_y = np.r_[Wsg_mmi*a1+b1, np.flip(Wsg_mmi,0)*a2+b2]
|
|
vtx_x = np.r_[Lsg_mmi, np.flip(Lsg_mmi,0)]
|
|
vtx = np.c_[vtx_x,vtx_y]
|
|
if (b1==0 and b2==0):
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
else :
|
|
w = max(Wsg_mmi)+b1*2
|
|
L = max(Lsg_mmi)+b1*2
|
|
nd.strt(length=L,layer=layers,width=w).put(-b1,0,0)
|
|
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a1',width=Wsg_mmi[0]).put(0,0,180)
|
|
nd.Pin(name='opt_a1',width=Wsg_mmi[0],type="optical:").put(0,0,180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b1',width=Wsg_mmi[-1]).put(L_mmi,0,0)
|
|
nd.Pin(name='opt_b1',width=Wsg_mmi[-1],type="optical:").put(L_mmi,0,0)
|
|
|
|
for idx_in in range(0,self.N_in):
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: Arm_inst = Arm.put('b1',0,self.Dp_in*(-idx_in+(self.N_in-1)/2),180)
|
|
Arm_inst = Arm.put('opt_b1',0,self.Dp_in*(-idx_in+(self.N_in-1)/2),180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='a'+str(round(idx_in+1)),pin=Arm_inst.pin['a1']).put()
|
|
nd.Pin(name='opt_a'+str(round(idx_in+1)),pin=Arm_inst.pin['opt_a1'],type="optical:").put()
|
|
|
|
for idx_in in range(0,self.N_out):
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: Arm_inst = Arm.put('b1',L_mmi,self.Dp_out*(-idx_in+(self.N_out-1)/2),0)
|
|
Arm_inst = Arm.put('opt_b1',L_mmi,self.Dp_out*(-idx_in+(self.N_out-1)/2),0)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: nd.Pin(name='b'+str(round(idx_in+1)),pin=Arm_inst.pin['a1']).put()
|
|
nd.Pin(name='opt_b'+str(round(idx_in+1)),pin=Arm_inst.pin['opt_a1'],type="optical:").put()
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: MMI.put('a1',0,0,0)
|
|
MMI.put('opt_a1',0,0,0)
|
|
|
|
if (show_pins):
|
|
nd.put_stub()
|
|
|
|
return C
|
|
|
|
def generate_test_gds(self,gc,dX_gc2gc,dY_gc2gc,R_bend=10,Xout_offset=50):
|
|
if (isinstance(gc,nd.Cell)):
|
|
gc_cell =gc
|
|
elif (hasattr(gc,'cell')):
|
|
gc_cell = gc.cell
|
|
else :
|
|
raise Exception("ERROR: In <mxpic::passive::ADC_STD_2x2::generate_test_gds>, <gc> is not recongized as a cell")
|
|
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
INST = self.cell.put(-self.L/2,0,0)
|
|
pic_strip = Route(width=self.w_arm[0],radius=R_bend,xs=self.xs)
|
|
for idx_in in range(0,self.N_in):
|
|
GC = gc_cell.put('g1',-dX_gc2gc/2,dY_gc2gc*(-idx_in + (self.N_in-1)/2),180)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: pic_strip.sbend_p2p(pin1=GC.pin['g1'],pin2=INST.pin['a'+str(idx_in+1)],Lstart=dX_gc2gc/10).put()
|
|
pic_strip.sbend_p2p(pin1=GC.pin['g1'],pin2=INST.pin['opt_a'+str(idx_in+1)],Lstart=dX_gc2gc/10).put()
|
|
|
|
for idx_in in range(0,self.N_out):
|
|
toggle = np.mod(idx_in,2)-0.5
|
|
GC = gc_cell.put('g1', dX_gc2gc/2+Xout_offset*toggle,dY_gc2gc*(-idx_in + (self.N_out-1)/2),0)
|
|
## revised in 2026.06.07 by Qin Yue
|
|
# legacy: pic_strip.sbend_p2p(pin1=GC.pin['g1'],pin2=INST.pin['b'+str(idx_in+1)],Lstart=dX_gc2gc/10).put()
|
|
pic_strip.sbend_p2p(pin1=GC.pin['g1'],pin2=INST.pin['opt_b'+str(idx_in+1)],Lstart=dX_gc2gc/10).put()
|
|
|
|
return C
|
|
|
|
|
|
class MMI_STD(MMI_ML):
|
|
"""
|
|
MMI STD primitive component.
|
|
|
|
This component builds the MMI STD layout cell.
|
|
|
|
Parameters
|
|
----------
|
|
name : Optional[str], optional
|
|
Unique identifier for the device cell. Default is None.
|
|
N_out : int, optional
|
|
Count or repetition parameter. Default is 3.
|
|
N_in : int, optional
|
|
Count or repetition parameter. Default is 1.
|
|
L_arm : int, optional
|
|
Length parameter in microns. Default is 10.
|
|
w_wg : float, optional
|
|
Width parameter in microns. Default is 0.45.
|
|
w_port : float, optional
|
|
Width parameter in microns. Default is 1.2.
|
|
xs : str, optional
|
|
Layer or cross-section name used by the device. Default is 'strip'.
|
|
L_mmi : int, optional
|
|
Length parameter in microns. Default is 10.
|
|
w_mmi : float, optional
|
|
Width parameter in microns. Default is 5.
|
|
sharp_patch : bool, optional
|
|
Whether to add geometry patches for sharp corners or cladding continuity. Default is True.
|
|
show_pins : bool, optional
|
|
Whether to draw pin markers in the generated layout. Default is False.
|
|
Dp_out : float, optional
|
|
Value for the Dp_out parameter. Default is 1.5.
|
|
Dp_in : float, optional
|
|
Value for the Dp_in parameter. Default is 1.5.
|
|
"""
|
|
def __init__(self,
|
|
name: Optional[str]=None,
|
|
N_out: int=3,
|
|
N_in: int=1,
|
|
L_arm: int=10,
|
|
w_wg: float=0.45,
|
|
w_port: float = 1.2,
|
|
xs: str='strip',
|
|
L_mmi: int=10,
|
|
w_mmi: float=5,
|
|
sharp_patch: bool=True,
|
|
show_pins: bool=False,
|
|
Dp_out: float=1.5,
|
|
Dp_in: float=1.5) -> None:
|
|
""" Standard MMI with multiple input and output
|
|
|
|
Args:
|
|
L_arm (int, optional): _description_. Defaults to 10.
|
|
w_wg (float, optional): _description_. Defaults to 0.45.
|
|
w_port (float, optional): _description_. Defaults to 1.2.
|
|
xs (str, optional): _description_. Defaults to 'strip'.
|
|
arm_sine_width (bool, optional): _description_. Defaults to False.
|
|
L_mmi (int, optional): _description_. Defaults to 10.
|
|
w_mmi (int, optional): _description_. Defaults to 5.
|
|
mmi_sine_width (bool, optional): _description_. Defaults to False.
|
|
sharp_patch (bool, optional): _description_. Defaults to True.
|
|
show_pins (bool, optional): _description_. Defaults to False.
|
|
res (float, optional): _description_. Defaults to 0.01.
|
|
N_out (int, optional): _description_. Defaults to 3.
|
|
N_in (int, optional): _description_. Defaults to 1.
|
|
Dp_out (float, optional): _description_. Defaults to 1.5.
|
|
Dp_in (float, optional): _description_. Defaults to 1.5.
|
|
"""
|
|
super().__init__(name=name,
|
|
L_arm=[L_arm],
|
|
w_arm=[w_wg,w_port],
|
|
xs=xs,
|
|
arm_sine_width=False,
|
|
L_mmi=[L_mmi],
|
|
w_mmi=[w_mmi,w_mmi],
|
|
mmi_sine_width=False,
|
|
sharp_patch=sharp_patch,
|
|
show_pins=show_pins,
|
|
res=min([L_mmi,L_arm]), ## taper resolution
|
|
N_out=N_out,
|
|
N_in=N_in,
|
|
Dp_out=Dp_out,
|
|
Dp_in=Dp_in)
|