import nazca as nd import numpy as np import math import pandas as pd from ...routing import Route from ..geometry import _my_polygon,circle,Clothoid,hole from ...basic import __cell_arg__ ''' Class for nanoantenna ''' class Nano_ant(): """ Configure a nano-antenna for optical phased-array grating couplers. This is the class of nanoantenna for optical phased array. GDS cell can be generated using this class. Simulation structure generation and simulation results analysis is going to be added in the future. Parameters ---------- w_wg : float, optional Input waveguide width in microns (default is 0.41). xs_wg : str, optional Nazca cross-section key for the feed waveguide (default is "strip"). define_type : str, optional Antenna definition scheme, either "non-periodic" or "periodic" (default is "non-periodic"). vector : Sequence[float], optional Alternating etched/filled segment lengths (µm) when ``define_type`` is "non-periodic" (default is ``[0.5, 0.5, 0.5, 0.5, 0.5, 0.5]``). taper_length : float, optional Linear taper length preceding the teeth region in microns (default is 3). width : float, optional Maximum aperture width in microns (default is 6). max_theta : float, optional Fan-out opening angle in degrees (default is 110). pitch : float or Sequence[float], optional Tooth pitch (µm) when ``define_type`` is "periodic"; scalar applies to all periods (default is 0.6). duty_cycle : float or Sequence[float], optional Etched fraction per period for periodic antennas; scalar or dual-entry list for dual-etch (default is 0.3). teeth_number : int, optional Number of etched teeth when periodic mode is used (default is 6). etch_depth : Sequence[str], optional List of etch-depth identifiers ("FETCH", "METCH", "SETCH"); length determines single/dual etch (default is ["METCH"]). show_pins : bool, optional Draw Nazca stub markers on exported pins (default is True). """ def __init__( self, w_wg: float = 0.41, xs_wg: str = "strip", define_type: str = "non-periodic", vector: 'float|list' = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5], taper_length: float = 3, width: float = 6, max_theta: float = 110, pitch: 'float|list' = 0.6, duty_cycle: 'float|list' = 0.3, teeth_number: float = 6, etch_depth: 'str|list' = ["METCH"], show_pins: bool = True ): # Init and save the input parameters self.w_wg = w_wg self.xs_wg = xs_wg # Determine the etch type if len(etch_depth)==1: self.etch_type = "single" elif len(etch_depth)==2: self.etch_type = "dual" if define_type=="non-periodic": self.vector = vector self.teeth_number = len(vector)/2 elif define_type=="periodic": # Parameters necessary when the ant is defined by "periodic" way if self.etch_type=="single": self.vector = [pitch*((2*duty_cycle-1)*(index%2)+1-duty_cycle) for index in range(teeth_number*2)] self.pitch = pitch self.duty_cycle = duty_cycle self.teeth_number = teeth_number elif self.etch_type=="dual": self.vector = [ pitch[1]*((2*duty_cycle[1]-1)*(index%2)+1-duty_cycle[1]) for index in range(teeth_number*2) ] self.vector[0] = pitch[0]*(1-duty_cycle[0]) self.vector[1] = pitch[0]*duty_cycle[0] self.pitch = pitch self.duty_cycle = duty_cycle self.teeth_number = teeth_number self.taper_length = taper_length self.ant_length = self.taper_length + sum(self.vector) self.width = width self.max_theta = max_theta self.define_type = define_type # Here, I should change the name-type according to the difinition in the foundry.py self.etch_depth = [] for etch in etch_depth: if etch=="FETCH": self.etch_depth = self.etch_depth+["STRIP"] # self.etch_depth.append("STRIP") elif etch=="METCH": self.etch_depth = self.etch_depth+["RIB"] # self.etch_depth.append("RIB") elif etch=="SETCH": self.etch_depth = self.etch_depth+["SRIB"] # self.etch_depth.append("SRIB") self.show_pins = show_pins self.cell = self.generate_gds() def generate_gds(self, sample_step=0.1, cell_name="Nanoantenna"): with nd.Cell(name=cell_name, instantiate=False) as nano_ant: layer_tre = nd.get_layer("STRIP_TRE") if layer_tre == "STRIP_TRE" : self.generate_gds_positive(sample_step=sample_step) else : ## TO DO self.generate_gds_error() # Add pins nd.Pin(name="a0", width=self.w_wg).put(0, 0, 180) nd.Pin(name="g1", width=self.w_wg).put(0, 0, 180) if self.show_pins: nd.put_stub(pinname="g1") return nano_ant def generate_gds_positive(self, sample_step=0.1): """ Generate a gds cell based on the logic of positive photoresistance. | Positive: Define the etched region using GETCH_TRE layer. """ width_extra_trench = 0.1 theta_rad_max = self.max_theta * math.pi / 180 ## Check if the input is appropriate or not if math.floor(self.teeth_number) != self.teeth_number: print("WARNNING :: Please re-check the vector of your antenna and make sure the length of vector is even.") message = 'Inappropriate Definition of antenna.' nd.text(text=message, height=5, layer=(96, 0), align='cc').put(0, 0) return 0 ## Build the structure # Add input waveguide nd.strt(length=self.taper_length-0.5, width=self.w_wg, xs=self.xs_wg).put(0, 0) # Add the fan polygon region radius_max = self.taper_length + sum(self.vector) if self.width/2 > radius_max: theta_rad = theta_rad_max/2 elif math.asin(self.width/2/radius_max) > theta_rad_max/2: theta_rad = theta_rad_max/2 elif math.asin(self.width/2/radius_max) <= theta_rad_max/2: theta_rad = math.asin(self.width/2/radius_max) theta_list = np.linspace(-theta_rad, theta_rad, math.floor(theta_rad*2*radius_max/sample_step)) fan_polygon = [(radius_max*math.cos(theta), radius_max*math.sin(theta)) for theta in theta_list] # fan_polygon = fan_polygon + [(self.width/2/math.tan(theta_rad_max/2), self.width/2), (0, self.w_wg/2), # (0, -self.w_wg/2), (self.width/2/math.tan(theta_rad_max/2), -self.width/2)] fan_polygon = fan_polygon + [(self.width/2/math.tan(theta_rad_max/2), self.width/2), (0, 0), (self.width/2/math.tan(theta_rad_max/2), -self.width/2)] nd.Polygon(points=fan_polygon, layer="STRIP_COR").put(0, 0) layer_cld = nd.get_layer("STRIP_CLD") if layer_cld == "STRIP_CLD": # Add CLD region if necessary nd.strt(length=self.ant_length+1, width=max(self.width+1, self.w_wg+4), layer=layer_cld).put(0, 0) # Add the teeth radius_cur = self.taper_length for teeth_index in range(0, int(self.teeth_number)): ## Determine the angular region first if teeth_index == 0: radius_ref = radius_cur else: radius_ref = radius_cur + self.vector[teeth_index*2-1] if (self.width+width_extra_trench)/2 > radius_ref: theta_rad = theta_rad_max/2 + width_extra_trench/radius_ref elif math.asin((self.width+width_extra_trench)/2/radius_ref) > theta_rad_max/2: theta_rad = theta_rad_max/2 + width_extra_trench/radius_ref else: theta_rad = math.asin((self.width+width_extra_trench)/2/radius_ref) theta_step = sample_step / radius_ref theta_list = np.linspace(-theta_rad, theta_rad, math.floor(2*theta_rad/theta_step)) ## Construct the inner radius curve if teeth_index == 0: radius_cur = radius_cur else: radius_cur = radius_cur + self.vector[teeth_index*2-1] inner_radius_curve = [(radius_cur*math.cos(theta), radius_cur*math.sin(theta)) for theta in theta_list] ## Construct the outer radius curve radius_cur = radius_cur + self.vector[teeth_index*2] outer_radius_curve = [(radius_cur*math.cos(theta), radius_cur*math.sin(theta)) for theta in theta_list] outer_radius_curve.reverse() ## Add two dummy points to avoid sharp angle offset_length = 0.015 / 2 minimum_etch = 0.2 radius_inner = radius_cur - self.vector[teeth_index*2] radius_outer = radius_inner + self.vector[teeth_index*2] x_1 = radius_inner * math.cos(theta_rad) + offset_length * math.cos(theta_rad) y_1 = radius_inner * math.sin(theta_rad) + offset_length * math.sin(theta_rad) vertical_length = math.sqrt(np.power(minimum_etch, 2) - np.power(offset_length, 2)) dummy1_x = x_1 - vertical_length * math.sin(theta_rad) dummy1_y = y_1 + vertical_length * math.cos(theta_rad) x_2 = radius_outer * math.cos(theta_rad) - offset_length * math.cos(theta_rad) y_2 = radius_outer * math.sin(theta_rad) - offset_length * math.sin(theta_rad) dummy2_x = x_2 - vertical_length * math.sin(theta_rad) dummy2_y = y_2 + vertical_length * math.cos(theta_rad) ## Construct the teeth polygon teeth_polygon = inner_radius_curve+[(dummy1_x,dummy1_y),(dummy2_x,dummy2_y)]+outer_radius_curve+[(dummy2_x, -dummy2_y),(dummy1_x,-dummy1_y)] if self.etch_type == "single": nd.Polygon(points=teeth_polygon, layer=self.etch_depth[0]+"_TRE").put(0, 0) if self.etch_type == "dual": if teeth_index==0: nd.Polygon(points=teeth_polygon, layer=self.etch_depth[0]+"_TRE").put(0, 0) else: nd.Polygon(points=teeth_polygon, layer=self.etch_depth[1]+"_TRE").put(0, 0) def generate_gds_error(self): nd.text(text="This foundry is not compatiable with current device. Please check.", height=10, layer=1001).put(0, 0) ''' Class for 2D antenna array for FMF grating ''' class Taper() : """ Create a stand-alone planar taper cell for 2D antenna feeds. Parameters ---------- width1 : float, optional Input width in microns (default is 4). width2 : float, optional Output width in microns (default is 0.45). length : float, optional Physical taper length in microns (default is 30). type : str, optional Transition profile, "linear" or "parabolic" (default is "linear"). show_pins : bool, optional Draw Nazca stub markers for debugging (default is False). """ def __init__(self, width1=4, width2=0.45, length=30, type="linear", show_pins=False) -> None: self.width1 = width1 self.width2 = width2 self.length = length self.type = type if self.type == "parabolic" : self.order = 2 elif self.type == "linear" : self.order = 1 self.show_pins = show_pins self.cell = self.generate_gds() def generate_gds(self) : with nd.Cell(name="taper", instantiate=False) as ic : if self.order == 1 : strip = Route(radius=10,width=self.width1,xs='strip') linear_taper = strip.taper( length=self.length,width1=self.width1,width2=self.width2,patch=True).put(0,0,0) output_strt = strip.strt(length=0.5,width=self.width2).put() nd.Pin(name="a1",width=self.width1).put(linear_taper.pin['a0']) nd.Pin(name="b1",width=self.width2).put(output_strt.pin['b0']) else : c2 = self.width1/2 c1 = (c2 - self.width2/2) / np.power(self.length, self.order) x_list = np.linspace(0, self.length, int(np.floor(self.length/0.2))) taper_up_poly = [(x, -c1*np.power(x, self.order)+c2) for x in x_list] taper_down_poly = [(x, -(-c1*np.power(x, self.order)+c2)) for x in x_list] taper_down_poly.reverse() taper_poly = taper_up_poly + taper_down_poly nd.Polygon(points=taper_poly, layer='STRIP_COR').put(0,0) c2 = (self.width1+4)/2 c1 = (c2 - (self.width2+4)/2) / np.power(self.length, self.order) x_list = np.linspace(0, self.length, int(np.floor(self.length/0.2))) taper_up_poly = [(x, -c1*np.power(x, self.order)+c2) for x in x_list] taper_down_poly = [(x, -(-c1*np.power(x, self.order)+c2)) for x in x_list] taper_down_poly.reverse() taper_poly = taper_up_poly + taper_down_poly nd.Polygon(points=taper_poly, layer='STRIP_CLD').put(0,0) width_max = np.max(np.array([self.width1, self.width2])) taper_poly = [ (0, width_max/2+2), (0, -width_max/2-2), (self.length, -width_max/2-2), (self.length, width_max/2+2) ] nd.Polygon(points=taper_poly, layer='STRIP_CLD').put(0,0) nd.strt(length=0.5, width=self.width2, xs='strip').put(self.length,0,0) nd.Pin(name='a1',width=self.width1).put(0,0,180) nd.Pin(name="b1",width=self.width2).put(self.length+0.5,0,0) if self.show_pins : nd.put_stub() return ic class Grating_2D_Hole() : """ Define a single 2D hole-array grating (diffraction + reflector + taper). This is a class for 2D Grating in IMEC. Parameters ---------- w_wg : float, optional Feed waveguide width in microns (default is 0.5). w_gt : float, optional Square grating aperture width in microns (default is 5). l_taper : float, optional Taper length from grating to feed in microns (default is 30). type_taper : str, optional Taper profile ("linear" or "parabolic", default is "parabolic"). gt_vector : Sequence[float], optional Pitch list (µm) for the main etched holes (default is ``[0.5, 0.5, 0.5, 0.5, 0.5]``). gt_diameter : float, optional Diameter of the main holes in microns (default is 0.4). gt_layer : str, optional Nazca layer name used for the main holes (default is "STRIP_COR"). polysi_vector : Sequence[float], optional Pitch list (µm) for polysilicon holes (default is ``[0.5, 0.5, 0.5, 0.5, 0.5]``). polysi_diameter : float, optional Diameter of polysilicon holes in microns (default is 0.4). polysi_layer : str, optional Layer name for polysilicon etch (default is "FCW_TRE"). reflector_vector : Sequence[float], optional Alternating reflector spacing/width values in microns (default is ``[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]``). l_field_center : float, optional Offset from the grating edge to the mode-field center in microns (default is 1). """ def __init__( self, w_wg=0.5, w_gt=5, l_taper=30, type_taper="parabolic", gt_vector=[0.5,0.5,0.5,0.5,0.5,], gt_diameter=0.4, gt_layer="STRIP_COR", polysi_vector=[0.5,0.5,0.5,0.5,0.5], polysi_diameter=0.4, polysi_layer="FCW_TRE", reflector_vector=[0.3,0.3,0.3,0.3,0.3,0.3], l_field_center = 1 ) -> None: self.w_wg = w_wg self.w_gt = w_gt self.l_taper = l_taper self.type_taper = type_taper self.gt_vector = gt_vector self.gt_num = len(self.gt_vector) self.gt_diameter = gt_diameter self.gt_layer =gt_layer self.polysi_vector = polysi_vector self.polysi_num = len(self.polysi_vector) self.polysi_diameter = polysi_diameter self.polysi_layer = polysi_layer self.reflector_vector = reflector_vector self.l_field_center = l_field_center self.cell = self.generate_gds() def generate_gds(self) : with nd.Cell(name="2D_Grating", instantiate=False) as ic : '''Generate the diffraction region first.''' strip_cor_poly = [ (self.w_gt/2, self.w_gt/2), (self.w_gt/2, -self.w_gt/2), (-self.w_gt/2, -self.w_gt/2), (-self.w_gt/2, self.w_gt/2) ] nd.Polygon(points=strip_cor_poly, layer='STRIP_COR').put(0,0,0) strip_cld_poly = [ (self.w_gt/2+2, self.w_gt/2+2), (self.w_gt/2+2, -self.w_gt/2-2), (-self.w_gt/2-2, -self.w_gt/2-2), (-self.w_gt/2-2, self.w_gt/2+2) ] nd.Polygon(points=strip_cld_poly, layer='STRIP_CLD').put(0,0,0) '''Generate the reflection region.''' self.reflector_num = int(len(self.reflector_vector)/2) for index in range(self.reflector_num) : loc = self.w_gt/2 + sum(self.reflector_vector[0:2*index+1]) + self.reflector_vector[2*index+1]/2 nd.strt(length=self.w_gt, width=self.reflector_vector[2*index+1], xs='strip').put( -loc, -(self.w_gt)/2, 90 ) nd.strt(length=self.w_gt, width=self.reflector_vector[2*index+1], xs='strip').put( -(self.w_gt)/2, -loc, 0 ) '''Generate the taper output.''' taper = Taper(width1=self.w_gt, width2=self.w_wg, length=self.l_taper, type=self.type_taper) taper_horizontal = taper.cell.put('a1', self.w_gt/2,0,0) taper_vertical = taper.cell.put('a1',0,self.w_gt/2,90) '''Generate the diffraction etched region.''' theta_list = np.linspace(0, 2*np.pi, 32) gt_ring_poly = [ (self.gt_diameter/2*np.cos(theta), self.gt_diameter/2*np.sin(theta)) for theta in theta_list ] polysi_ring_poly = [ (self.polysi_diameter/2*np.cos(theta), self.polysi_diameter/2*np.sin(theta)) for theta in theta_list ] self._generate_hole_array_( polygon=gt_ring_poly, vector=self.gt_vector, layer=self.gt_layer ).put(self.w_gt/2, self.w_gt/2) self._generate_hole_array_( polygon=polysi_ring_poly, vector=self.polysi_vector, layer=self.polysi_layer ).put(self.w_gt/2, self.w_gt/2) '''Put the pin location''' nd.Pin(name='g1').put(taper_horizontal.pin['b1']) nd.Pin(name='g2').put(taper_vertical.pin['b1']) # nd.put_stub() return ic def _generate_hole_array_(self,polygon,vector,layer) : with nd.Cell(name="diffration_"+layer, instantiate=False) as ic : for lateral_index in range(len(vector)) : for vertical_index in range(len(vector)) : nd.Polygon(points=polygon,layer=layer).put( -sum(vector[0:lateral_index+1]), -sum(vector[0:vertical_index+1]) ) return ic """ Renamed for simplification in 2023.04.02 """ class GC_STD_2D: """ General-purpose 2D grating coupler generator with rectangular or arc shapes. Parameters ---------- name : str, optional Nazca cell name (default is None, meaning uninstantiated cell). etch_type : str, optional Etch depth selector: "FETCH", "METCH", or "SETCH" (default is "FETCH"). xs_wg : str, optional Cross-section for the grating slab and output waveguide (default is "grating"). Dx_hole : float or Sequence[float], optional Hole size along x (µm). Scalar applies to all columns (default is 0.3). Dy_hole : float or Sequence[float], optional Hole size along y (µm). Scalar applies to all rows (default is 0.3). hole_shape : str, optional Individual hole shape, "circle" or "rectangle" (default is "circle"). shape : str, optional Overall grating footprint, "circle", "arc", or "rectangle" (default is "circle"). xs_open : str or None, optional Optional open-area cross-section for keep-out regions (default is None). Px : float or Sequence[float], optional Periods along x in microns; scalar broadcasts (default is 0.57). Py : float or Sequence[float], optional Periods along y in microns; scalar broadcasts (default is 0.57). num_x : int, optional Number of periods along x when ``Px`` is scalar (default is 25). num_y : int, optional Number of periods along y when ``Py`` is scalar (default is 25). Lx_taper : float, optional Horizontal taper length to the output port in microns (default is 50). Ly_taper : float, optional Vertical taper length in microns (default is 0). Lx_end : float, optional Extra straight length appended to the positive-x end (default is 1). Ly_end : float, optional Extra straight length appended to the +/-y ends (default is 1). Lx_side : float, optional Lateral margin on the +/−x sides in microns (default is 0.5). Ly_side : float, optional Lateral margin on the +/−y sides in microns (default is 0.5). Lx_port : float, optional Straight-section length after the x-port taper in microns (default is 5). Ly_port : float, optional Straight-section length after the y-port taper in microns (default is 5). w_wg : float, optional Output waveguide width in microns (default is 0.5). show_pins : bool, optional Draw Nazca stub markers (default is False). P_AR : float, optional Anti-reflection pitch in microns (default is 0.6). L_AR : float, optional Anti-reflection taper length in microns (default is 1). Raises ------ Exception Period do not match D_hole. """ def __init__(self, name=None, etch_type :str = 'FETCH', xs_wg:str='grating', Dx_hole:float=0.3, Dy_hole:float=0.3, hole_shape :str= 'circle', shape:str = 'circle', xs_open:str = None, Px:float=0.57, Py:float=0.57, num_x:float=25, num_y:float=25, Lx_taper:float = 50.0, Ly_taper:float = 0.0, Lx_end:float = 1.0, Ly_end:float = 1.0, Lx_side:float = 0.5, Ly_side:float = 0.5, Lx_port:float=5.0, Ly_port:float=5.0, w_wg:float=0.5, show_pins:bool=False, P_AR: float = 0.6, L_AR: float = 1, ): self.name = name if (self.name==None): self.instantiate = False else : self.instantiate = True if (isinstance(Px,int) or isinstance(Px,float)) : Px = Px * np.ones(num_x) if (isinstance(Py,int) or isinstance(Py,float)) : Py = Py * np.ones(num_y) if (isinstance(Dx_hole,int) or isinstance(Dx_hole,float)) : Dx_hole = Dx_hole * np.ones((num_x)) if (isinstance(Dy_hole,int) or isinstance(Dy_hole,float)) : Dy_hole = Dy_hole * np.ones((num_y)) self.num_x = len(Px) self.num_y = len(Py) if (len(Px)!=len(Dx_hole) or len(Py)!=len(Dy_hole)): raise Exception("In Grating define : [Period] length not matching [D_hole] length") self.Lx_taper = Lx_taper self.Ly_taper = Ly_taper self.Lx_end = Lx_end self.Ly_end = Ly_end self.Lx_side = Lx_side self.Ly_side = Ly_side self.Lx_port = Lx_port self.Ly_port = Ly_port self.xs_open = xs_open self.w_wg = w_wg self.xs_wg = xs_wg self.etch_type = etch_type self.shape = shape self.hole_shape = hole_shape self.Dx_hole = Dx_hole self.Dy_hole = Dy_hole self.Px = Px self.Py = Py self.P_AR = P_AR self.L_AR = L_AR self.show_pins = show_pins if (nd.get_layer(layer="STRIP_TRE") == "STRIP_TRE"): self.positive = False if (hole_shape=='circle'): if (etch_type=="FETCH"): layer_etch = "STRIP_HOL" elif (etch_type=="METCH"): layer_etch = "RIB_HOL" elif (etch_type=="SETCH"): layer_etch = "SRIB_HOL" elif (hole_shape=='rectangle'): if (etch_type=="FETCH"): layer_etch = "STRIP_TRE" elif (etch_type=="METCH"): layer_etch = "RIB_TRE" elif (etch_type=="SETCH"): layer_etch = "SRIB_TRE" else : self.positive = True if (etch_type=="FETCH"): layer_etch = None elif (etch_type=="METCH"): layer_etch = "RIB_COR" elif (etch_type=="SETCH"): layer_etch = "SRIB_COR" self.layer_etch = layer_etch if (layer_etch!=None): if (nd.get_layer(layer_etch)!=layer_etch): layer_etch=None print("WARNING: In mxpic::passive::GC_STD_1D, ::",layer_etch," not defined in tapeout") if (self.positive): self.cell = self.generate_positive() else: self.cell = self.generate_negative() def generate_negative(self): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: ## arc shape grating if (self.shape=='circle' or self.shape=='arc'): print("Sorry, this function has not been built up") ## retangular grating elif (self.shape=='rectangle'): Lx = sum(self.Px)+self.Lx_side*2 # if (self.Ly_taper==0): Ly = sum(self.Py)+self.Ly_side*2 # else: # Ly = sum(self.Py) y_offset = sum(self.Py)/2 x_offset = sum(self.Px)/2 nd.strt(length=Lx,width=Ly,xs=self.xs_wg).put(-Lx/2,0,0) if (self.xs_open!=None): circle(radius=max([Lx,Ly])*2/2,width=max([Lx,Ly])*2,xs=self.xs_open, # n_points=32 ).cell.put(0,0,0) for _x_ in range(0,self.num_x): for _y_ in range(0,self.num_y): pos_x = np.sum(self.Px[0:_x_+1])-self.Px[0]/2-x_offset pos_y = np.sum(self.Py[0:_y_+1])-y_offset-self.Py[0]/2 if (self.hole_shape=='circle'): circle(radius=self.Dx_hole[_x_]/4,width=self.Dx_hole[_x_]/2,layer=self.layer_etch, # n_points=32, sharp_patch=False).cell.put(pos_x,pos_y,0) elif (self.hole_shape=='rectangle'): nd.strt(length=self.Dx_hole[_x_],width=self.Dy_hole[_y_],layer=self.layer_etch).put(pos_x-self.Dx_hole[_x_]/2,pos_y,0) else : raise Exception("ERROR: In , is not defined, please input [circle | rectangle]") if (self.Ly_taper!=0): nd.strt(length=self.Ly_end,width=Lx,xs=self.xs_wg).put(0,Ly/2,90) if (self.P_AR>0): _num_AR_ = int(np.floor(self.L_AR/self.P_AR)+1) for _idx_ in range(0,_num_AR_): # nd.strt(xs=self.xs_wg,width=Lx,length=self.P_AR/2).put(0,self.P_AR/2+Ly/2+self.Ly_end+self.P_AR*_idx_,90) nd.strt(xs=self.xs_wg,width=self.P_AR/2,length=Lx).put(-Lx/2,self.P_AR+Ly/2+self.Ly_end+self.P_AR*_idx_,0) # if (self.P_AR >0 and self.L_AR>0): # _num_AR_ = int(np.floor(Ly/self.P_AR)) # for _idx_ in range(0,_num_AR_): # nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(_idx_*self.P_AR - (_num_AR_-1)/2*self.P_AR+Lx/2,Ly/2+self.Ly_end,90) nd.strt(length=self.Ly_end,width=Lx,xs=self.xs_wg).put(0,-Ly/2,-90) y_port = nd.taper(width1=Ly,width2=self.w_wg,xs=self.xs_wg,length=self.Ly_taper).put() y_port = nd.strt(length=self.Ly_port,width=self.w_wg,xs=self.xs_wg).put() nd.Pin(name='g2',pin=y_port.pin['b0']).put() nd.strt(length=self.Lx_end,width=Ly,xs=self.xs_wg).put(Lx/2,0,0) ## adding anti reflection if (self.Lx_taper!=0): if (self.P_AR>0): _num_AR_ = int(np.floor(self.L_AR/self.P_AR)+1) for _idx_ in range(0,_num_AR_): # nd.strt(xs=self.xs_wg,width=Lx,length=self.P_AR/2).put(self.P_AR/2+Lx/2+self.Lx_end+self.P_AR*_idx_,0,0) nd.strt(xs=self.xs_wg,length=Ly,width=self.P_AR/2).put(self.P_AR+Lx/2+self.Lx_end+self.P_AR*_idx_,-Ly/2,90) # _num_AR_ = int(np.floor(Ly/self.P_AR)) # for _idx_ in range(0,_num_AR_): # nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(Lx+self.Lx_end,_idx_*self.P_AR - (_num_AR_-1)/2*self.P_AR,0) nd.strt(length=self.Lx_end,width=Ly,xs=self.xs_wg).put(-Lx/2,0,180) x_port = nd.taper(width1=Ly,width2=self.w_wg,xs=self.xs_wg,length=self.Lx_taper).put() x_port = nd.strt(length=self.Lx_port,width=self.w_wg,xs=self.xs_wg).put() nd.Pin(name='g1',pin=x_port.pin['b0']).put() # print("Sorry, this function has not been built up") else : raise Exception("In Grating define : [shape] not defined") if (self.show_pins): nd.put_stub(pinsize=3) return C def generate_positive(self): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: ## arc shape grating if (self.shape=='circle' or self.shape=='arc'): print("Sorry, this function has not been built up") pass ## retangular grating elif (self.shape=='rectangle'): Lx = sum(self.Px) if (self.Ly_taper==0): Ly = sum(self.Py)+self.Ly_side*2 nd.strt(length=Lx,width=self.Ly_side,xs=self.xs_wg).put(0,sum(self.Py)/2+self.Lx_side/2,0) nd.strt(length=Lx,width=self.Ly_side,xs=self.xs_wg).put(0,-sum(self.Py)/2-self.Lx_side/2,0) else: Ly = sum(self.Py) y_offset = sum(self.Py)/2 if (self.layer_etch!=None): nd.strt(length=Lx,width=Ly,layer=self.layer_etch).put(0,0,0) if (self.xs_open!=None): nd.strt(length=Lx*2,width=Ly*2,xs=self.xs_open).put(-Lx/2,0,0) for _x_ in range(0,self.num_x): for _y_ in range(0,self.num_y): pos_x = np.sum(self.Px[0:_x_+1])-self.Px[0]/2 pos_y = np.sum(self.Py[0:_y_+1])-y_offset-self.Py[0]/2 hole(r_hole=self.Dx_hole[_x_]/2,Lx_sq=self.Px[_x_],Ly_sq=self.Py[_y_], Dx_hole=self.Dx_hole[_x_],Dy_hole=self.Dy_hole[_y_], # n_points=12, xs=self.xs_wg,hole_shape=self.hole_shape).cell.put(pos_x,pos_y,0) if (self.Ly_taper!=0): nd.strt(length=self.Ly_end,width=Lx,xs=self.xs_wg).put(Lx/2,Ly/2,90) _num_AR_ = int(np.floor(Ly/self.P_AR)) for _idx_ in range(0,_num_AR_): nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(_idx_*self.P_AR - (_num_AR_-1)/2*self.P_AR+Lx/2,Ly/2+self.Ly_end,90) nd.strt(length=self.Ly_end,width=Lx,xs=self.xs_wg).put(Lx/2,-Ly/2,-90) y_port = nd.taper(width1=Ly,width2=self.w_wg,xs=self.xs_wg,length=self.Lx_taper).put() y_port = nd.strt(length=self.Ly_port,width=self.w_wg,xs=self.xs_wg).put() nd.Pin(name='g2',pin=y_port.pin['b0']).put() nd.strt(length=self.Lx_end,width=Ly,xs=self.xs_wg).put(Lx,0,0) ## adding anti reflection _num_AR_ = int(np.floor(Ly/self.P_AR)) for _idx_ in range(0,_num_AR_): nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(Lx+self.Lx_end,_idx_*self.P_AR - (_num_AR_-1)/2*self.P_AR,0) nd.strt(length=self.Lx_end,width=Ly,xs=self.xs_wg).put(0,0,180) x_port = nd.taper(width1=Ly,width2=self.w_wg,xs=self.xs_wg).put() x_port = nd.strt(length=self.Lx_port,width=self.w_wg,xs=self.xs_wg).put() nd.Pin(name='g1',pin=x_port.pin['b0']).put() else : raise Exception("In Grating define : [shape] not defined") if (self.show_pins): nd.put_stub(pinsize=3) return C def generate_test_gds(self,dX_gc2gc=300): with nd.Cell(instantiate=False) as C: self.cell.put('g1',-dX_gc2gc/2,0,180) self.cell.put('g1', dX_gc2gc/2,0,0) nd.strt(xs=self.xs_wg,width=self.w_wg,length=dX_gc2gc).put(-dX_gc2gc/2,0,0) return C class GC_STD_1D: """ Versatile 1D grating coupler supporting sector and rectangular layouts. Parameters ---------- name : str, optional Nazca cell name (default is None). xs_wg : str, optional Cross-section key for the slab/taper region (default is "strip"). w_wg : float, optional Input waveguide width in microns (default is 0.5). etch_type : str, optional Etch depth selector: "FETCH", "METCH", or "SETCH" (default is "FETCH"). xs_open : str or None, optional Optional cross-section for keep-out/open regions (default is None). L_taper : float, optional Length of the entrance taper in microns (default is 10). L_end : float, optional Terminal slab length after the grating in microns (default is 2). A_taper : float, optional Fan-out angle in degrees (default is 30). Period : float or Sequence[float], optional Grating periods in microns; scalar broadcasts (default is 0.5). eta_etch : float or Sequence[float], optional Etch duty (between 0 and 1) per period (default is 0.5). num : int, optional Number of periods when ``Period`` and ``eta_etch`` are scalars (default is 20). sector_gc : bool, optional Use sector (True) or rectangular (False) geometry (default is True). show_pins : bool, optional Draw Nazca stub markers (default is False). L_tail : float, optional Extra straight length added before the taper in microns (default is 2). P_AR : float, optional Anti-reflection pitch in microns (default is 1). L_AR : float, optional Anti-reflection taper length in microns (default is 2). """ def __init__ (self, name=None, xs_wg : str = 'strip', w_wg : float = 0.5, etch_type :str = 'FETCH', xs_open :str=None, L_taper :float = 10.0, L_end :float = 2.0, A_taper :float = 30.0, Period :float = 0.5, eta_etch :float = 0.5, num :int = 20, ### note, when Period and eta is defined as list, this is not usefull sector_gc :bool =True, show_pins=False, L_tail = 2, # n_points = 64, P_AR: float = 1.0, ### adding anti reflection pitches L_AR: float = 2.0, ): self.name = name if (self.name==None): self.instantiate = False else : self.instantiate = True if (xs_open!=None): try: nd.get_xsection(xs_open) except: xs_open=None print("WARNING:In , ::",xs_open," not defined in tapeout") self.xs_open=xs_open self.xs_wg=xs_wg self.w_wg=w_wg self.L_taper=L_taper self.L_end=L_end self.A_taper=A_taper self.show_pins = show_pins self.L_tail = L_tail if (isinstance(eta_etch,list) or isinstance(eta_etch,np.ndarray)): num = len(eta_etch) if (isinstance(Period,list) or isinstance(Period,np.ndarray)): num = len(Period) if (isinstance(Period,int) or isinstance(Period,float)): Period = Period*np.ones(num) if (isinstance(eta_etch,int) or isinstance(eta_etch,float)): eta_etch = eta_etch*np.ones(num) """ Generate ERROR """ if (len(Period)!=len(eta_etch)): raise Exception("ERROR: In : [Period] length not matching [eta_etch] length") if (nd.get_layer(layer="STRIP_TRE") == "STRIP_TRE"): self.positive = False if (etch_type=="FETCH"): layer_etch = "STRIP_TRE" elif (etch_type=="METCH"): layer_etch = "RIB_TRE" elif (etch_type=="SETCH"): layer_etch = "SRIB_TRE" else : self.positive = True if (etch_type=="FETCH"): layer_etch = None elif (etch_type=="METCH"): layer_etch = "RIB_COR" elif (etch_type=="SETCH"): layer_etch = ["SRIB_COR","RIB_COR"] self.Period=Period self.eta_etch=eta_etch self.num=len(Period) self.sector_gc=sector_gc # self.n_points = n_points ## revise 2022.08.18 self.L_AR = L_AR self.P_AR = P_AR if (layer_etch!=None): if (isinstance(layer_etch,str)): if (nd.get_layer(layer_etch)!=layer_etch): layer_etch=None print("WARNING: In mxpic::passive::GC_STD_1D, ::",layer_etch," not defined in tapeout") else : for _layer_ in layer_etch: if (nd.get_layer(_layer_)!=_layer_): layer_etch=None print("WARNING: In mxpic::passive::GC_STD_1D, ::",layer_etch," not defined in tapeout") self.layer_etch = layer_etch if (self.positive): self.cell = self.generate_positive() else: self.cell = self.generate_negative() def generate_negative(self): with nd.Cell(instantiate=self.instantiate,name=self.name) as C: ## arc shape grating if (self.sector_gc == True): L_total = np.sum(self.Period) + self.L_taper + self.L_end L_tail = self.L_tail for layers,growx,growy,acc in nd.layeriter(xs=self.xs_wg): (a1,b1), (a2,b2),c1,c2 = growx x_offset = -b1/np.sin(self.A_taper/2*np.pi/180) if (self.P_AR>0 and self.L_AR>0): ## anti reflection r_tap = L_total*1.3 - b1 - x_offset else : r_tap = L_total + b1 - x_offset circle(radius=r_tap/2,width=r_tap, theta_start=-self.A_taper/2,theta_stop=self.A_taper/2, # n_points=self.n_points, layer=layers).cell.put(x_offset,0,0) _L_tail_ = np.abs(x_offset) if _L_tail_ > L_tail: L_tail = _L_tail_ r_grat_inner = self.L_taper for _idx_ in range(0,self.num): d_pitch = self.Period[_idx_]*self.eta_etch[_idx_] circle(radius=r_grat_inner + d_pitch/2, width=d_pitch, theta_start=-self.A_taper/2-5,theta_stop=self.A_taper/2+5, # n_points=self.n_points, layer=self.layer_etch).cell.put(0,0,0) r_grat_inner = r_grat_inner + self.Period[_idx_] L_open = 1.5*(L_total-self.L_taper) W_open = 1.1*(L_total*np.tan(self.A_taper/2*np.pi/180))*2 x_open = (L_total+self.L_taper)/2 - L_open/2 if (self.xs_open!=None): nd.strt(length=L_open,width=W_open,xs=self.xs_open).put(x_open,0,0) nd.strt(length=L_tail,width=self.w_wg,xs=self.xs_wg).put(-L_tail,0,0) nd.Pin(name='g1',width=self.w_wg).put(-L_tail,0,180) nd.strt(length=np.abs(self.w_wg/2/np.tan(self.A_taper/2/180*np.pi)),width=self.w_wg,xs=self.xs_wg).put(0,0,0) ## retangular grating else: L_total = np.sum(self.Period) + self.L_taper + self.L_end L_grat = sum(self.Period)+self.L_end W_grat = self.w_wg + self.L_taper*np.tan(self.A_taper/2*np.pi/180)*2 nd.taper(length=self.L_taper,width1=self.w_wg,width2=W_grat,xs=self.xs_wg).put(0,0,0) nd.strt(length=L_grat,width=W_grat,xs=self.xs_wg).put(self.L_taper,0,0) ### adding Anti-reflection # if (self.P_AR>0 and self.L_AR>0): # _num_AR_ = int(np.floor(W_grat/self.P_AR)) # for _idx_ in range(0,_num_AR_): # nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(self.L_taper+L_grat, (_idx_ - (_num_AR_-1)/2)*self.P_AR,0) x_grat = self.L_taper for _idx_ in range(0,self.num): nd.strt(length=self.Period[_idx_]*self.eta_etch[_idx_],width=W_grat+2,layer=self.layer_etch).put(x_grat,0,0) x_grat = x_grat + self.Period[_idx_] nd.strt(length=10,width=self.w_wg,xs=self.xs_wg).put(-5,0,0) nd.Pin(name='g1',width=self.w_wg).put(-5,0,180) L_open = 1.5*(L_total-self.L_taper) W_open = 1.1*(L_total*np.tan(self.A_taper/2*np.pi/180))*2 x_open = (L_total+self.L_taper)/2 - L_open/2 if (self.xs_open!=None): nd.strt(length=L_open,width=W_open,xs=self.xs_open).put(x_open,0,0) pass if (self.show_pins): nd.put_stub() return C def generate_positive(self): with nd.Cell(instantiate=False) as C: ## arc shape grating if (self.sector_gc==True): L_tail = self.L_tail for layers,growx,growy,acc in nd.layeriter(xs=self.xs_wg): (a1,b1), (a2,b2),c1,c2 = growx x_offset = -b1/np.sin(self.A_taper/2*np.pi/180) r_tap = self.L_taper + b1 - x_offset circle(radius=r_tap/2,width=r_tap, theta_start=-self.A_taper/2,theta_stop=self.A_taper/2, # n_points=self.n_points, layer=layers).cell.put(x_offset,0,0) r_grat_inner = self.L_taper - x_offset _L_tail_ = np.abs(x_offset) if _L_tail_ > L_tail: L_tail = _L_tail_ for _idx_ in range(0,self.num): d_pitch = self.Period[_idx_]*(1-self.eta_etch[_idx_])+b1*2 circle(radius=r_grat_inner + self.Period[_idx_]*self.eta_etch[_idx_] + d_pitch/2, width=d_pitch, theta_start=-self.A_taper/2,theta_stop=self.A_taper/2, # n_points=self.n_points, layer=layers).cell.put(x_offset,0,0) r_grat_inner = r_grat_inner + self.Period[_idx_] d_pitch = self.L_end+b1*2 r_grat_inner = r_grat_inner + self.Period[-1]*self.eta_etch[-1] circle(radius=r_grat_inner + d_pitch/2, width=d_pitch, theta_start=-self.A_taper/2,theta_stop=self.A_taper/2, # n_points=64, layer=layers).cell.put(x_offset,0,0) nd.strt(length=L_tail*2,width=self.w_wg,xs=self.xs_wg).put(-L_tail,0,0) nd.Pin(name='g1',width=self.w_wg).put(-L_tail,0,180) L_total = np.sum(self.Period) + self.L_taper + self.L_end L_open = 1.5*(L_total-self.L_taper) W_open = 1.2*(L_total*np.tan(self.A_taper/2*np.pi/180))*2 x_open = (L_total+self.L_taper)/2 - L_open/2 if (self.layer_etch!=None): if (isinstance(self.layer_etch,str)): nd.strt(length=L_open,width=W_open,layer=self.layer_etch).put(x_open,0,0) elif(isinstance(self.layer_etch,list)): for _layer_ in self.layer_etch: nd.strt(length=L_open,width=W_open,layer=_layer_).put(x_open,0,0) if (self.xs_open!=None): nd.strt(length=L_open,width=W_open,xs=self.xs_open).put(x_open,0,0) ## retangular grating elif (self.shape=='rectangle'): L_grat = sum(self.Period) W_grat = self.w_wg + self.L_taper*np.tan(self.A_taper/2*np.pi/180)*2 nd.taper(length=self.L_taper,width1=self.w_wg,width2=W_grat,xs=self.xs_wg).put(0,0,0) x_grat = self.L_taper for _idx_ in range(0,self.num): nd.strt(length=self.Period[_idx_]*(1-self.eta_etch[_idx_]),width=W_grat,xs=self.xs_wg).put(x_grat+self.Period[_idx_]*self.eta_etch[_idx_],0,0) x_grat = x_grat + self.Period[_idx_] nd.strt(length=10,width=self.w_wg,xs=self.xs_wg).put(-5,0,0) nd.Pin(name='g1',width=self.w_wg).put(-5,0,180) ### adding Anti-reflection if (self.P_AR>0 and self.L_AR>0): _num_AR_ = int(np.floor(W_grat/self.P_AR)) for _idx_ in range(0,_num_AR_): nd.taper(xs=self.xs_wg,width1=self.P_AR-0.2,width2=0.2,length=self.L_AR).put(self.L_taper+L_grat, (_idx_ - (_num_AR_-1)/2)*self.P_AR,0) L_total = np.sum(self.Period) + self.L_taper + self.L_end L_open = 1.5*(L_total-self.L_taper) W_open = 1.1*(L_total*np.tan(self.A_taper/2*np.pi/180))*2 x_open = (L_total+self.L_taper)/2 - L_open/2 if (self.layer_etch!=None): nd.strt(length=L_open,width=W_open,layer=self.layer_etch).put(x_open,0,0) if (self.xs_open!=None): nd.strt(length=L_open,width=W_open,xs=self.xs_open).put(x_open,0,0) pass else : raise Exception("In Grating define : [shape] not defined") if (self.show_pins): nd.put_stub() return C def generate_test_dev(self,dX_gc2gc): with nd.Cell(instantiate=False) as C: self.cell.put('g1',-dX_gc2gc/2,0,180) self.cell.put('g1', dX_gc2gc/2,0,0) nd.strt(xs=self.xs_wg,width=self.w_wg,length=dX_gc2gc).put(-dX_gc2gc/2,0,0) return C class FA: """ Instantiate a fiber-array fanout from repeated grating/fiber couplers. Parameters ---------- fiber_coupler : nd.Cell or object Reference coupler cell or instance exposing ``cell`` and pin ``g1``/``a0``. pitch : float, optional Center-to-center spacing between adjacent couplers in microns. number : int Total number of channels in the array. show_pins : bool, optional Draw Nazca stub markers on exported pins (default is False). """ def __init__(self,fiber_coupler,pitch,number,show_pins=False): # if (isinstance(fiber_coupler,nd.Cell)): # fiber_cell = fiber_coupler # elif (hasattr(fiber_coupler,'cell')): # fiber_cell = fiber_coupler.cell # else: # raise Exception("ERROR: In , not recongized, please input nazca.cell or classes that has nazca.cell") fiber_cell = __cell_arg__(arg=fiber_coupler,arg_name="fiber_coupler",func_name="mxpic::FA") pin_in_name = [] for name,Pin in fiber_cell.ic_pins(): pin_in_name = pin_in_name+[name] # pin_in_name.append(name) if ('g1' in pin_in_name): pin_name = 'g1' else: pin_name = 'a0' print("WARNING: In , dose not contain 'g1' pin, using 'a0' in default") self.pitch = pitch self.number = number with nd.Cell(instantiate=False) as C: for idx in range(1,number+1): port = fiber_cell.put(pin_name,0,pitch*(idx-number/2-1/2),0) nd.Pin('g'+str(idx),pin=port.pin[pin_name]).put() x_out = port.pin['b0'].x nd.Pin(name='b0').put(x_out,0,180) if (show_pins): nd.put_stub(pinsize=3) self.cell = C class GC_SiN_Si_Dual_Layer: """ GC SiN Si Dual Layer primitive component. This component builds the GC SiN Si Dual Layer layout cell. Parameters ---------- name : str, optional Unique identifier for the device cell. Default is None. w_teeth_SiN : list or float, optional Width parameter in microns. Default is 0.5. gap_teeth_SiN : list or float, optional Spacing or gap parameter in microns. Default is 0.5. w_teeth_Si : list or float, optional Width parameter in microns. Default is 0.5. gap_teeth_Si : list or float, optional Spacing or gap parameter in microns. Default is 0.5. ori_teeth_offset : float, optional Value for the ori_teeth_offset parameter. Default is 5.0. n_teeth_Si : float, optional Value for the n_teeth_Si parameter. Default is 30. n_teeth_SiN : float, optional Value for the n_teeth_SiN parameter. Default is 30. A_gc_taper : float, optional Angle parameter in degrees. Default is 25.0. R_teeth_ori_SiN : float, optional Radius parameter in microns. Default is 40.0. R_teeth_ori_Si : float, optional Radius parameter in microns. Default is 40.0. L_end_Si : float, optional Length parameter in microns. Default is 0.2. L_end_SiN : float, optional Length parameter in microns. Default is 5.0. w_port : float, optional Width parameter in microns. Default is 0.9. A_anti_rfl : float, optional Angle parameter in degrees. Default is 4.0. layer_SiN_slab : str, optional Layer or cross-section name used by the device. Default is None. layer_Si_slab : str, optional Layer or cross-section name used by the device. Default is None. layer_Si_teeth : str, optional Layer or cross-section name used by the device. Default is None. layer_SiN_teeth : str, optional Layer or cross-section name used by the device. Default is None. layer_SiN_etch : str, optional Layer or cross-section name used by the device. Default is None. layer_Si_etch : str, optional Layer or cross-section name used by the device. Default is None. layer_ox_open : str, optional Layer or cross-section name used by the device. Default is None. """ def __init__(self, name:str=None, w_teeth_SiN:'list|float' = 0.5, gap_teeth_SiN:'list|float' = 0.5, w_teeth_Si:'list|float' = 0.5, gap_teeth_Si:'list|float' = 0.5, ori_teeth_offset:float = 5.0, n_teeth_Si:float=30, n_teeth_SiN:float=30, A_gc_taper:float=25.0, R_teeth_ori_SiN:float=40.0, R_teeth_ori_Si:float=40.0, L_end_Si:float=0.2, L_end_SiN:float=5.0, w_port : float = 0.9, A_anti_rfl:float = 4.0, layer_SiN_slab:str=None, layer_Si_slab:str=None, layer_Si_teeth:str=None, layer_SiN_teeth:str=None, layer_SiN_etch:str=None, layer_Si_etch:str=None, layer_ox_open:str=None, ): self.name = name self.w_teeth_SiN = w_teeth_SiN self.gap_teeth_SiN = gap_teeth_SiN self.w_teeth_Si = w_teeth_Si self.gap_teeth_Si = gap_teeth_Si self.ori_teeth_offset = ori_teeth_offset self.n_teeth_SiN = n_teeth_SiN self.n_teeth_Si = n_teeth_Si self.A_gc_taper = A_gc_taper self.w_port = w_port self.L_end_Si = L_end_Si self.L_end_SiN = L_end_SiN self.A_anti_rfl = A_anti_rfl self.R_teeth_ori_SiN = R_teeth_ori_SiN self.R_teeth_ori_Si = R_teeth_ori_Si self.layer_SiN_slab = layer_SiN_slab self.layer_Si_slab = layer_Si_slab self.layer_Si_teeth = layer_Si_teeth self.layer_SiN_teeth = layer_SiN_teeth self.layer_SiN_etch = layer_SiN_etch self.layer_Si_etch = layer_Si_etch self.layer_ox_open = layer_ox_open self.cell = self.generate_gds() def generate_gds(self): """ creating instance cell or not """ if (self.name is None) : self.instantiate = False else : self.instantiate = True """ """ if (isinstance(self.w_teeth_SiN,list) or isinstance(self.w_teeth_SiN,np.ndarray)): n_teeth_SiN = len(self.w_teeth_SiN) elif (isinstance(self.w_teeth_SiN,float)): n_teeth_SiN = self.n_teeth_SiN w_teeth_SiN = [w_teeth_SiN]*n_teeth_SiN """ """ if (isinstance(self.w_teeth_Si,list) or isinstance(self.w_teeth_Si,np.ndarray)): n_teeth_Si = len(self.w_teeth_Si) elif (isinstance(self.w_teeth_Si,float)): n_teeth_Si = self.n_teeth_Si w_teeth_Si = [w_teeth_Si]*n_teeth_Si with nd.Cell(instantiate=self.instantiate, name=self.name) as C: """ Creating SiN layer grating """ ## whole area where the grating area covered L_gc = self.R_teeth_ori_SiN + self.L_end_SiN + sum(self.w_teeth_SiN) + sum(self.gap_teeth_SiN) w_box_gc = L_gc*np.sin(self.A_gc_taper/2*np.pi/180)*2 L_box_gc = L_gc*np.cos(self.A_gc_taper/2*np.pi/180) x_slab = [0,L_box_gc,L_gc+w_box_gc*np.sin(self.A_anti_rfl*np.pi/180),L_gc,L_box_gc,0] y_slab = [self.w_port/2,w_box_gc/2,w_box_gc/2,-w_box_gc/2,-w_box_gc/2,-self.w_port/2] _my_polygon(layer_wg=self.layer_SiN_slab,vtx=np.c_[x_slab,y_slab]).put(0,0,0) # circle(radius=self.R_teeth_ori_SiN/2,angle=self.A_gc_taper,layer=self.layer_SiN_slab, # width=self.R_teeth_ori_SiN).cell.put(0,0,-self.A_gc_taper/2) A_etch_ext = 4 ## Placing teeth r_in = self.R_teeth_ori_SiN for idxT in range(0,n_teeth_SiN): r_out = r_in + self.gap_teeth_SiN[idxT] circle(radius=(r_out+r_in)/2,angle=self.A_gc_taper+A_etch_ext,layer=self.layer_SiN_etch, width=self.gap_teeth_Si[idxT]).cell.put(0,0,-self.A_gc_taper/2-A_etch_ext/2) r_in = r_out + self.w_teeth_SiN[idxT] """ Creating Si layer grating """ w_Si_slab = sum(self.w_teeth_Si)+sum(self.gap_teeth_Si) R_Si_slab = self.R_teeth_ori_Si+w_Si_slab/2 circle(radius=R_Si_slab,angle=self.A_gc_taper,layer=self.layer_Si_slab, width=w_Si_slab).cell.put(0,0,-self.A_gc_taper/2) ## Placing teeth r_in = self.R_teeth_ori_Si for idxT in range(0,n_teeth_Si): r_out = r_in + self.gap_teeth_Si[idxT] if (self.layer_Si_etch is not None): circle(radius=(r_out+r_in)/2,angle=self.A_gc_taper+A_etch_ext,layer=self.layer_Si_etch, width=self.gap_teeth_Si[idxT]).cell.put(0,0,-self.A_gc_taper/2-A_etch_ext/2) elif (self.layer_Si_teeth is not None): circle(radius=r_out+(self.w_teeth_Si[idxT])/2,angle=self.A_gc_taper,layer=self.layer_Si_teeth, width=self.w_teeth_Si[idxT]).cell.put(0,0,-self.A_gc_taper/2) r_in = r_out + self.w_teeth_Si[idxT] return C