from typing import Any, Optional import nazca as nd import numpy as np from ..pic import taper_xs2xs from ...electronics import Vias import nazca.interconnects as IC from ...basic import __list_convert__,__array_convert__ class waveguide_PIN: def __init__(self, xs: str="rib", ## note, this xsection cannot directly mixed iwth 'rib' L_wg: float = 100, w_wg: float = 1.2, xs_pn_ct: str = 'strip_cor', w_itr : float = 2.0, w_p : float = 4.0, w_n : float = 4.0, w_p_ct : float = 4.0, w_n_ct : float = 4.0, xs_heater: str = 'heater', xs_via_h2m: str = 'via_h2m', w_metal_pn: float = 8, sz_via_i2m: float = 0.25, sp_via_i2m: float = 0.35, sp_sc: float = 1, xs_p : str = 'pw', xs_n : str = 'nw', xs_pcont : str = 'pp', xs_ncont : str = 'np', xs_sa : str = 'sa', xs_via_s2m: str = 'via_s2m', xs_metal: str = 'metal', rib_taper: bool = True, L_taper: float = 30, xs_port: str = 'strip', show_pins: bool = True, ) -> None: self.xs = xs self.L_wg = L_wg self.w_wg = w_wg self.w_itr = w_itr self.xs_p = __list_convert__(xs_p,'xs_p','mxpic::active::rings') self.xs_n = __list_convert__(xs_n,'xs_n','mxpic::active::rings') self.w_p = __array_convert__(w_p,'w_p','mxpic::active::rings') self.w_n = __array_convert__(w_n,'w_n','mxpic::active::rings') # self.w_p = w_p # self.w_n = w_n # self.xs_n = xs_n # self.xs_p = xs_p self.w_p_ct = w_p_ct self.w_n_ct = w_n_ct self.xs_pn_ct = xs_pn_ct self.xs_heater = xs_heater self.xs_via_h2m = xs_via_h2m if (w_metal_pn==None or w_metal_pn>min([w_n_ct,w_p_ct])-2*sp_sc): w_metal_pn = min([w_n_ct,w_p_ct])-2*sp_sc w_metal_pn = max([0,w_metal_pn]) self.w_metal_pn = w_metal_pn ## w_metal_pn <= w_p_ct - 2*sp_ct self.sz_via_i2m = sz_via_i2m self.sp_via_i2m = sp_via_i2m self.xs_p = xs_p self.xs_n = xs_n self.xs_pcont = xs_pcont self.xs_ncont = xs_ncont self.xs_sa = xs_sa self.sp_sc = sp_sc self.xs_via_s2m = xs_via_s2m self.xs_metal = xs_metal self.rib_taper = rib_taper self.L_taper = L_taper self.xs_port = xs_port self.cell = self.generate_gds(show_pins) def generate_gds(self,show_pins=False): with nd.Cell(instantiate=False) as C: nd.add_xsection(name='temp') for layers,growx,growy,acc in nd.layeriter(xs=self.xs): (a1,b1), (a2,b2),c1,c2 = growx if (b1==0 and b2==0 and layers.find("COR")!=-1): ## this is the core of the waveguide w_total = self.w_wg core = nd.strt(length=self.L_wg,width=self.w_wg,layer=layers).put(0,0,0) elif (layers.find("COR")!=-1): ## this is the core of the slab area ## revised in 2023.1.4, w_n chaned into list or array w_total = self.w_itr + sum(self.w_n) + sum(self.w_p) + self.w_n_ct + self.w_p_ct # w_total = self.w_itr + self.w_n + self.w_p + self.w_n_ct + self.w_p_ct y_center = (-(sum(self.w_n) + self.w_n_ct) + sum(self.w_p) + self.w_p_ct)/2 nd.strt(length=self.L_wg,width=w_total,layer=layers).put(0,y_center,0) else : w_total = (self.w_itr + sum(self.w_n) + sum(self.w_p) + self.w_n_ct + self.w_p_ct)*(a1-a2)+(b1-b2) # w_total = (self.w_itr + self.w_n + self.w_p + self.w_n_ct + self.w_p_ct)*(a1-a2)+(b1-b2) y_center = (-(sum(self.w_n) + self.w_n_ct) + sum(self.w_p) + self.w_p_ct)/2 nd.strt(length=self.L_wg,width=w_total,layer=layers).put(0,y_center,0) nd.add_layer2xsection(xsection='temp',layer=layers,leftedge=(a1,(w_total-self.w_wg)/2),rightedge=(a2,-(w_total-self.w_wg)/2)) if (self.rib_taper): tp_L = taper_xs2xs(xs_1='temp',xs_2=self.xs_port,L_taper=self.L_taper,w_1=self.w_wg,w_2=self.w_wg).cell.put(core.pin['a0']) tp_R = taper_xs2xs(xs_1='temp',xs_2=self.xs_port,L_taper=self.L_taper,w_1=self.w_wg,w_2=self.w_wg).cell.put(core.pin['b0']) nd.Pin(name='a1',pin=tp_L.pin['b0']).put() nd.Pin(name='b1',pin=tp_R.pin['b0']).put() else: nd.Pin(name='a1',pin=core.pin['a0']).put() nd.Pin(name='b1',pin=core.pin['b0']).put() ## adding p doping area _y_ = self.w_itr/2 for itn in range(0,len(self.w_p)): _y_ = _y_ + self.w_p[itn]/2 nd.strt(length=self.L_wg,width=self.w_p[itn],xs=self.xs_p[itn]).put(0,_y_,0) _y_ = _y_ + self.w_p[itn]/2 if (self.xs_pn_ct!=None): nd.strt(length=self.L_wg,width=self.w_p_ct,xs=self.xs_pn_ct).put(0, _y_+self.w_p_ct/2,0) _y_ = -self.w_itr/2 for itn in range(0,len(self.w_n)): _y_ = _y_ - self.w_n[itn]/2 nd.strt(length=self.L_wg,width=self.w_n[itn],xs=self.xs_n[itn]).put(0,_y_,0) _y_ = _y_ - self.w_n[itn]/2 if (self.xs_pn_ct!=None): nd.strt(length=self.L_wg,width=self.w_n_ct,xs=self.xs_pn_ct).put(0, _y_-self.w_p_ct/2,0) # nd.strt(length=self.L_wg,width=self.w_p_ct,xs=self.xs_pcont).put(0, self.w_itr/2+self.w_p+self.w_p_ct/2,0) # if (self.xs_pn_ct!=None): # nd.strt(length=self.L_wg,width=self.w_p_ct,xs=self.xs_pn_ct).put(0, self.w_itr/2+self.w_p+self.w_p_ct/2,0) # y_ct = self.w_itr/2+self.w_p+self.w_p_ct - self.w_metal_pn/2-self.sp_sc # via_p_ct = Vias(xs=self.xs_via_s2m,sz=self.sz_via_i2m,spacing=self.sp_via_i2m,area=[self.L_wg-self.sp_sc*2,self.w_metal_pn], # xs_l1=self.xs_sa,xs_l2=self.xs_metal).cell.put(self.L_wg/2,y_ct,0) # nd.Pin(name='ep1',pin=via_p_ct.pin['a0']).put() # ## adding n doping area # nd.strt(length=self.L_wg,width=self.w_n,xs=self.xs_n).put(0,-self.w_itr/2-self.w_n/2,0) # nd.strt(length=self.L_wg,width=self.w_n_ct,xs=self.xs_ncont).put(0,-self.w_itr/2-self.w_n-self.w_n_ct/2,0) # if (self.xs_pn_ct!=None): # nd.strt(length=self.L_wg,width=self.w_p_ct,xs=self.xs_pn_ct).put(0, -self.w_itr/2-self.w_n-self.w_n_ct/2,0) # y_ct = -self.w_itr/2-self.w_n-self.w_n_ct + self.w_metal_pn/2+self.sp_sc # via_n_ct = Vias(xs=self.xs_via_s2m,sz=self.sz_via_i2m,spacing=self.sp_via_i2m,area=[self.L_wg-self.sp_sc*2,self.w_metal_pn], # xs_l1=self.xs_sa,xs_l2=self.xs_metal).cell.put(self.L_wg/2,y_ct,0) # nd.Pin(name='en1',pin=via_n_ct.pin['a0']).put() if (show_pins): nd.put_stub() return C class WGDoped(): def __init__(self, name: Optional[str]=None, w_wg: float = 0.5, w_port: float = 0.5, Ltp_port: int = 10, L_wg: int = 200, xs_wg: str = "rib", xs_n: list = ['nld','np'], xs_p: list = ['pld','pp'], w_n: list = [0.5,1], w_p: list = [0.5,1], w_ht: float = 0, L_ht: Any = None, xs_ht: str = "heater", w_mt: Optional[float] = None, xs_mt: str = "metal", xs_cont_wg: Optional[str] = None, w_i: Optional[float] = None, dope_offset: int = 0, via_s2m: Any = None, via_h2m: Any = None, dope_ovlp: float = 0.2, cell_xs_transition: Any = None, ) -> None: """_summary_ Args: w_wg (float, optional): Core width of the waveguide. Defaults to 0.5. xs_wg (str, optional): cross-section definition of the waveguide. Defaults to "rib". xs_n (list, optional): _description_. Defaults to ['nld','np']. xs_p (list, optional): _description_. Defaults to ['pld','pp']. w_n (list, optional): _description_. Defaults to [0.5,1]. w_p (list, optional): _description_. Defaults to [0.5,1]. w_i (_type_, optional): _description_. Defaults to None. dope_offset (int, optional): Offset value of the PN junction center to waveguide center. Defaults to 0. via_i2m (_type_, optional): _description_. Defaults to None. via_h2m (_type_, optional): _description_. Defaults to None. """ self.name = name if (name is None): self.instantiate = False else: self.instantiate = True """ Input value quality check """ if (len(xs_p)!=len(w_p)): raise Exception("In , input parameter doesn't have same length (w_p & xs_p)") if (len(xs_n)!=len(w_n)): raise Exception("In , input parameter doesn't have same length (w_n & xs_n)") self.w_wg = w_wg self.xs_wg = xs_wg self.xs_n = xs_n self.xs_p = xs_p self.w_n = w_n self.w_p = w_p self.xs_cont_wg = xs_cont_wg self.L_wg = L_wg self.w_mt = w_mt self.xs_mt = xs_mt self.w_ht = w_ht self.xs_ht = xs_ht self.L_ht = L_ht self.w_port = w_port self.Ltp_port = Ltp_port if (w_i is None): self.w_i = w_wg else : self.w_i = w_i self.dope_ovlp = dope_ovlp self.dope_offset = dope_offset self.via_s2m = via_s2m self.via_h2m = via_h2m self.cell_xs_transition = cell_xs_transition self.cell = self.generate_gds() def generate_gds(self): """ Generation of GDS pattern Returns: _type_: _description_ """ with nd.Cell(name=self.name,instantiate=self.instantiate) as C: Lstrt = self.L_wg - self.Ltp_port w_n_cont = self.w_n[-1] w_p_cont = self.w_p[-1] """ Placing Doping areas UPPER """ _y_ = self.w_i/2 + self.dope_offset for _idx_dop_ in range(len(self.w_n)): _y_ = _y_ + self.w_n[_idx_dop_]/2 nd.strt(xs=self.xs_n[_idx_dop_],width=self.w_n[_idx_dop_]+int(_idx_dop_>0)*int(_idx_dop_0)*int(_idx_dop_0 and self.xs_ht is not None): if (self.L_ht is None): L_ht = self.L_wg else: L_ht = self.L_ht ht = nd.strt(xs=self.xs_ht,width=self.w_ht,length=L_ht).put(self.L_wg/2-L_ht/2,0,0) if (isinstance(self.via_h2m,nd.Cell)): viaHT=self.via_h2m elif(hasattr(self.via_h2m,"cell")): viaHT=self.via_h2m.cell VL=viaHT.put(ht.pin['a0']) VR=viaHT.put(ht.pin['b0']) VL.raise_pins(['b0'],['ep3']) VR.raise_pins(['b0'],['en3']) """ Adding vias """ if (self.via_s2m is not None): via_s2m_N = Vias(xs=self.via_s2m.xs,sz=self.via_s2m.sz,spacing=self.via_s2m.spacing,sp_via_xs=self.via_s2m.sp_via_xs,xs_l1=self.via_s2m.xs_l1, area=[self.L_wg,w_n_cont]).cell.put(self.L_wg/2,yContN,0) via_s2m_P = Vias(xs=self.via_s2m.xs,sz=self.via_s2m.sz,spacing=self.via_s2m.spacing,sp_via_xs=self.via_s2m.sp_via_xs,xs_l1=self.via_s2m.xs_l1, area=[self.L_wg,w_p_cont]).cell.put(self.L_wg/2,yContP,0) yPvia = yContP yNvia = yContN wPvia = self.w_p[-1]-self.via_s2m.sp_via_xs*2 wNvia = self.w_n[-1]-self.via_s2m.sp_via_xs*2 """ Adding metals """ if (self.w_mt is not None): nd.strt(xs=self.xs_mt,length=self.w_mt,width=self.L_wg+0.2).put(self.L_wg/2,yContN-w_n_cont/2,90) nd.strt(xs=self.xs_mt,length=self.w_mt,width=self.L_wg+0.2).put(self.L_wg/2,yContP+w_p_cont/2,-90) yPvia = yContP+w_p_cont/2-self.w_mt/2 yNvia = yContN-w_n_cont/2+self.w_mt/2 wPvia = self.w_mt wNvia = self.w_mt nd.Pin(name="ep1",width=wPvia).put(0,yPvia,180) nd.Pin(name="ep2",width=wPvia).put(self.L_wg,yPvia,0) nd.Pin(name="en1",width=wNvia).put(0,yNvia,180) nd.Pin(name="en2",width=wNvia).put(self.L_wg,yNvia,0) if (self.cell_xs_transition is not None): xs1 = self.cell_xs_transition.put('a0',coreOUT.pin['b0']) xs2 = self.cell_xs_transition.put('a0',coreIN.pin['a0']) xs1.raise_pins(['b0'],['a1']) xs2.raise_pins(['b0'],['b1']) else: coreIN.raise_pins(['a0'],['b1']) coreOUT.raise_pins(['b0'],['a1']) # nd.put_stub() return C