import nazca as nd import numpy as np import nazca.trace as trace import nazca.cfg as cfg from nazca.netlist import interconnect_logger import math as m import nazca.cp as cp from ..structures import * from ..structures import _my_polygon # from .dev_passive import * def ic_exception(msg=''): """Raise interconnect exception if conditions apply.""" if cfg.interconnect_raise: if cfg.interconnect_msgnum is None\ or cfg.interconnect_msgcnt == cfg.interconnect_msgnum: raise Exception(msg) ''' Maxwell's lib of routing for both electrics and photonics Based on Nazca.Interconnect ''' class Route(nd.interconnects.Interconnect): def __init__(self, radius=None, width=None, angle=90, Ltp_mm = 10, width2_mm=1.0, MM_route = False, xs=None, layer=None, adapt_width=False, adapt_xs=False, instantiate=False, pinstyle=None, offset=None, varname=None, doc='', PCB=False, modes=None,sharp_patch=True): self.PCB= PCB self.sharp_patch = sharp_patch self.Ltp_mm = Ltp_mm if (width2_mm is None): self.width2_mm = width else : self.width2_mm = width2_mm self.MM_route = MM_route if (self.PCB==True): self.MM_route = False ## Override the multimode routing super().__init__(radius=radius, width=width, angle=angle, xs=xs, layer=layer, adapt_width=adapt_width, adapt_xs=adapt_xs, instantiate=instantiate, pinstyle=pinstyle, offset=offset, varname=varname, doc=doc, PCB=PCB, modes=modes) def connPatch(self,): pass def rt_bend (self,width=3,xs='strip',angle=90,layer=None,pin=None): trace.trace_start() pin = self._getpinout(pin) with nd.Cell(instantiate=False) as ICcell: if (layer==None): for layers,growx,growy,acc in nd.layeriter(xs=xs): (a1,b1), (a2,b2),c1,c2 = growx nd.strt(length=width*(a1-a2)+(b1-b2),width=width*(a1-a2)+(b1-b2),layer=layers).put(-(width*(a1-a2)+(b1-b2))/2,0,0) nd.Pin('a0',io=1,xs=xs,width=pin.width).put(0,0,0) nd.Pin('b0',io=0,xs=xs,width=pin.width).put(0,0,angle) else : nd.strt(length=width,width=width,layer=layer).put(-width/2,0,0) nd.Pin('a0',io=1,xs=xs,width=pin.width).put(0) nd.Pin('b0',io=0,xs=xs,width=pin.width).put(angle) trace.trace_stop() ICcell.length_geo = trace.trace_length() cfg.cp=pin return ICcell def strt_mm(self,pin=None,width2=None,Ltp=None,width1=None,Lstart=0.5,length=None,xs=None,arrow=False): pin = self._getpinout(pin) xs = self._getxs(pin, xs) width1 = self._getwidth(pin, width1, xs) pinflip = not nd.get_xsection(xs).symmetry if (Ltp==None): Ltp = self.Ltp_mm if (width2==None): width2 = self.width2_mm if pin is None: pin = cp.here() if (width2==None): width2 = self._getwidth(pin, width2, xs) if (length>(Ltp*2+Lstart*2)): Lmm = length-Ltp*2-Lstart*2 else : Lmm = 0 trace.trace_start() with nd.Cell(instantiate=False) as ICcell: if (Lmm==0): tr = self.strt(length=length,width=width1).put(0) nd.Pin('a0', io=0, width=width1, xs=xs).put(tr.pin['a0']) nd.Pin('b0', io=1, width=width1, xs=xs).put(tr.pin['b0']) else: tr = self.strt(length=Lstart,width=width1).put(0) self.taper(width1=width1,length=Ltp,width2=width2).put() if (Lmm==None): Lmm=0 self.strt(length=Lmm,width=width2).put() self.taper(width1=width2,length=Ltp,width2=width1).put() tl = self.strt(length=Lstart,width=width1).put() patch = self.strt(length=0.1,width=width1).put(tr.pin['b0'].move(-0.05,0,0)) patch = self.strt(length=0.1,width=width1).put(tl.pin['a0'].move(-0.05,0,0)) nd.Pin('a0', io=0, width=width1, xs=xs).put(tr.pin['a0']) nd.Pin('b0', io=1, width=width1, xs=xs).put(tl.pin['b0']) if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() cfg.cp=pin return ICcell """ Multimode swithing """ def line_mm(self,length=None,width=None,xs=None,width_mm=None,Ltp=None,): if (self.MM_route): return self.strt_mm(length=length,width1=width,xs=xs,width2=width_mm,Ltp=Ltp) else: return self.line(length,width,xs) """ Multimode waveguide inside strt_p2p """ def strt_mm_p2p(self,pin1=None, pin2=None,width2=None,Ltp=None,width1=None,Lstart=0.5,length=None,xs=None,arrow=False,name=None): parse, (length, b) = self._strt_p2p_solve(pin1=pin1, pin2=pin2, width=width1, xs=xs) pin1, pin2, xs, width, _, radius1, radius2 = parse if (Ltp==None): Ltp = self.Ltp_mm if (width2==None): width2 = self.width2_mm if (Ltp*2 + Lstart*2 >= length): return self.strt_p2p(pin1=pin1,pin2=pin2,width=width,xs=xs,name=None,arrow=arrow) else : if name is None: name = 'ic_strt_p2p' with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() s1 = self.line(length=Lstart, width=width, xs=xs).put(0,0,b) p1 = nd.Pin('a0', io=0, width=width, xs=xs).put(s1.pin['a0'].rot(-b)) s1 = self._taper(length=Ltp,width1=width,width2=width2, xs=xs).put() e1 = self.line(length=length-Ltp*2-Lstart*2, width=width2, xs=xs).put() d1 = self._taper(length=Ltp,width1=width2,width2=width, xs=xs).put() d1 = self.line(length=Lstart, width=width, xs=xs).put() p2 = nd.Pin('b0', io=1, width=width, xs=xs).put(d1.pin['b0'].rot(-b-pin1.a+pin2.a+180)) if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0']) trace.trace_stop() ICcell.length_geo = trace.trace_length() ICcell.pin2 = pin2 # connect_opt needed because the extra rot in p1 and p2 does hide the line optical connection: nd.connect_path(p1, p2, trace.trace_length()) cfg.cp = pin1 return ICcell def bend_p2p(self,pin1=None, pin2=None, radius=None, width=None, xs=None, name=None, arrow=False, width_mm=None,Ltp=None,sharp_path=True): ''' Another package of IC.strt_bend_strt_p2p, which gives the possiblity to attach pins with right angle ''' return self.strt_bend_strt_p2p_mine(pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs, name=name, arrow=arrow, width_mm=width_mm,Ltp=Ltp) def ubend_route(self, pin=None, offset=20.0, radius=None, width=None, xs=None, length=0, name=None, arrow=False, balance=0, end_angle=False, width_mm=None,Ltp=None): if pin is None: pin = cp.here() pin2 = pin.move(0, offset, 0) return self.ubend_p2p(pin1=pin, pin2=pin2, radius=radius, width=width, xs=xs, length=length, name=name, arrow=arrow, balance=balance, end_angle=end_angle,width_mm=width_mm,Ltp=Ltp) def sbend_route(self, radius=None, width=None, pin=None, xs=None, offset=20, Ltot=None, length1=0, length2=0, name=None, arrow=False, Amax=90.0,original_function=False): pin = self._getpinout(pin) xs = self._getxs(pin, xs) width = self._getwidth(pin, width, xs) radius = self._getradius(pin, radius, xs) if pin is None: pin = cp.here() ## Revised bt HGL 2023.1.4 if offset < 2*radius : offset_length = 2*np.sqrt(np.power(radius,2)-np.power(radius-np.abs(offset/2),2)) else : offset_length = 2*radius pin2 = pin.move(length1+length2+offset_length+1, offset, 180) ## revise in 2022.12.11 return self.sbend_p2p(pin1=pin, pin2=pin2, radius=radius, width=width, xs=xs, Lstart=length1, name=name, arrow=arrow, BendEndFlag=1,original_function=True) def bend_strt_bend_p2p_mine(self, pin1=None, pin2=None, radius=None, radius1=None, radius2=None, width=None, xs=None, length1=0, length2=0, ictype='shortest', name=None, arrow=False): """Generate a point-to-point bend-straight-bend interconnect. Args: pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp) pin2 (Node | Instance | tuple(x, y, a)): end pin radius1 (float): optional first bend radius in um radius2 (float): optional second bend radius im um width (float): optional waveguide width in um xs (str): optional xsection ictype (str): interconnection type (default='shortest') options: 'shortest', 'll', 'lr', 'rl', rr', 'all' name (str): optional new name for the component arrow (bool): draw connection arrows (default=True) Returns: Cell: bend_strt_bend element Example: Create and place a bend-straight-bend guide to connect two specific points:: import nazca as nd from nazca.interconnects import Interconnect ic = Interconnect(width=2.0, radius=10.0) guide = ic.bend_strt_bend_p2p(pin1=(0, 0, 0), pin2=(40, 20, 90)) guide.put() nd.export_plt() """ if radius is not None: if radius1 is None: radius1 = radius if radius2 is None: radius2 = radius parse, curves = self._bend_strt_bend_p2p_solve(pin1, pin2, xs=xs, width=width, radius1=radius1, radius2=radius2, length1=length1, length2=length2, ictype=ictype) pin1, pin2, xs, width, _, radius1, radius2 = parse pinflip = not nd.get_xsection(xs).symmetry instantiate = self.instantiate if ictype == 'all': instantiate = True variations = curves['variations'] else: variations = [curves] cells = [] if not curves['found']: interconnect_logger(curves['message'], 'error') return self.strt_p2p(pin1, pin2, xs='error') else: for curve in variations: par = curve['solution'] shape = curve['type'] param = curve['solution'] Ltot = param['Ltot'] L = param['L'] b = param['angle1'] e = param['angle2'] if name is None: name = 'ic_bend_strt_bend_p2p' with nd.Cell("{}_{}".format(name, shape), instantiate=instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() if length1 > 0: # if (self.MM_route): # e1 = self.strt_mm(length=length1, width1=width, xs=xs).put() # else: # e1 = self.line(length1, width, xs=xs).put() e1 = self.line_mm(length=length1, width1=width, xs=xs).put() self._circle_(radius1, angle=m.degrees(b), width=width, xs=xs,sharp_patch=self.sharp_patch).put() # print(m.degrees()) else: e1 = self._circle_(radius1, angle=m.degrees(b), width=width, xs=xs,sharp_patch=self.sharp_patch).put() # if (self.MM_route): # self.strt_mm(length=L, width1=width, xs=xs).put() # else : # self.line(L, width, xs).put() self.line_mm(length=L, width=width, xs=xs).put() self._circle_(radius2, angle=m.degrees(e), width=width, xs=xs,sharp_patch=self.sharp_patch).put() if length2 > 0: # if (self.MM_route): # self.strt_mm(length=length2, width1=width, xs=xs).put() # else: # self.line(length2, width, xs).put() self.line_mm(length=length2, width=width, xs=xs).put() nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0']) nd.Pin('b0', io=1, width=width, xs=xs).put() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() ICcell.pin2 = pin2 cfg.cp = pin1 cells.append(ICcell) if not cells: msg = "No solution in bend_strt_bend_p2p." interconnect_logger(msg, 'warning') ic_exception(msg) elif len(cells) == 1: return cells[0] elif len(cells) > 1: with nd.Cell('{}_{}'.format(name, xs), cnt=True) as ICgroup: ICcell.group = 'interconnect' trace.trace_start() for cell in cells: cell.put(180) trace.trace_stop() ICgroup.length_geo = trace.trace_length() cfg.cp = pin1 return ICgroup def bend_route(self, radius=None, angle=None, width=None, pin=None, xs=None, length1=0.1, length2=0.1, name=None, arrow=False, offset=None,sharp_patch=True,original_function=False): if (original_function or self.PCB): return self.bend(radius=radius, angle=angle, width=width, pin=pin, xs=xs, length1=length1, length2=length2, name=name, arrow=arrow, offset=offset) else: return self.bend_mine(radius=radius, angle=angle, width=width, pin=pin, xs=xs, length1=length1, length2=length2, name=name, arrow=arrow, offset=offset) def bend_route_p2p(self, pin1=None, pin2=None, radius=None, width=None, xs=None, name=None, arrow=False,original_function=False): if (original_function or self.PCB): return self.strt_bend_strt_p2p(pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs, name=name, arrow=arrow) else: return self.strt_bend_strt_p2p_mine(pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs, name=name, arrow=arrow) def _circle_(self,radius = 10, width = 0.45, angle=360, n_points = 65,xs=None,layer=None,sharp_patch=True, theta_start=None,theta_stop=None): with nd.Cell(instantiate=False) as C: if (angle is not None): theta_start = 0 theta_stop = angle else : angle = theta_stop - theta_start """ """ bend_dir = np.sign(angle) for layers,growx,growy,acc in nd.layeriter(xs=xs): (a1,b1), (a2,b2),c1,c2 = growx theta = np.linspace(theta_start,theta_stop,n_points) theta = theta/180*np.pi vtx_outer_x = np.sin(bend_dir*theta)*(radius+width*a1 + b1) vtx_outer_y =-bend_dir*np.cos(theta)*(radius+width*a1 + b1) vtx_outer = np.c_[vtx_outer_x,vtx_outer_y] if (radius+width*a2+b1>0.0000001 or np.abs(angle-0)<360): vtx_inner_x = np.sin(bend_dir*theta)*(radius+width*a2 + b2) vtx_inner_y = -bend_dir*np.cos(theta)*(radius+width*a2 + b2) vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)] vtx = np.r_[vtx_outer,vtx_inner] else : vtx = vtx_outer if (sharp_patch==True and b2!=0 and b1!=0): """ Creating rectangle for sharp-patching """ L_patch = max([max(vtx_outer_x),max(vtx_inner_x)])-min([min(vtx_outer_x),min(vtx_inner_x)]) X_patch = 1/2*(max([max(vtx_outer_x),max(vtx_inner_x)])+min([min(vtx_outer_x),min(vtx_inner_x)])) W_patch = (max([max(vtx_outer_y),max(vtx_inner_y)])-min([min(vtx_outer_y),min(vtx_inner_y)])) Y_patch = 1/2*(max([max(vtx_outer_y),max(vtx_inner_y)])+min([min(vtx_outer_y),min(vtx_inner_y)])) nd.strt(length=L_patch,width=W_patch,layer=layers).put(X_patch-L_patch/2,Y_patch,0) _my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0) nd.Pin(name='a0',width=width,xs=xs).put(radius*np.sin(bend_dir*theta_start/180*np.pi),-bend_dir*radius*np.cos(theta_start/180*np.pi),theta_start+180) nd.Pin(name='b0',width=width,xs=xs).put(radius*np.sin(bend_dir*theta_stop/180*np.pi) ,-bend_dir*radius*np.cos(theta_stop/180*np.pi) ,theta_stop) # if (angle>0): # nd.Pin(name='a0',width=width,xs=xs).put(radius*np.cos(0/180*np.pi),radius*np.sin(0/180*np.pi),0-90) # nd.Pin(name='b0',width=width,xs=xs).put(radius*np.cos(angle/180*np.pi),radius*np.sin(angle/180*np.pi),angle+90) # else : # nd.Pin(name='a0',width=width,xs=xs).put(radius*np.cos(0/180*np.pi),radius*np.sin(0/180*np.pi), 90) # nd.Pin(name='b0',width=width,xs=xs).put(radius*np.cos(angle/180*np.pi),radius*np.sin(angle/180*np.pi),270+angle) return C def bend_mine(self, radius=None, angle=None, width=None, pin=None, xs=None, length1=0, length2=0, name=None, arrow=False, offset=None,sharp_patch=True): pin = self._getpinout(pin) xs = self._getxs(pin, xs) width = self._getwidth(pin, width, xs) radius = self._getradius(pin, radius, xs) pinflip = not nd.get_xsection(xs).symmetry if offset is None: offset = self.offset if angle is None: angle = self.angle if name is None: name = 'ic_bend' with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() if length1 > 0: # if (self.MM_route): # e1 = self.strt_mm(length=length1, width1=width, xs=xs).put(0) # else: # e1 = self.line(length=length1, width=width, xs=xs).put(0) e1 = self.line_mm(length=length1, width=width, xs=xs).put(0) e2 = self._circle_(radius=radius, width=width, angle=angle, xs=xs,sharp_patch=self.sharp_patch).put() else: e1 =self._circle_(radius=radius, width=width, angle=angle, xs=xs,sharp_patch=self.sharp_patch).put() e2 = e1 if length2 > 0: # if (self.MM_route): # e2 = self.strt_mm(length=length2, width1=width, xs=xs).put() # else: # e2 = self.line(length=length2, width=width, xs=xs).put() e2 = self.line_mm(length=length2, width=width, xs=xs).put() nd.Pin('b0', io=1, width=width, xs=xs, pin=e2.pin['b0']).put() nd.Pin('a0', io=0, width=width, xs=xs, pin=e1.pin['a0']).put() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() if pin is not None: cfg.cp = pin return ICcell def tube_mine(self, geo, showpins=False, name=None, xs=None, arrow=False,Ltp=None,width_mm=None,sharp_patch=None): """draw interconnect based on symbols. Returns: Cell: Tube element based on the provided elements """ if (sharp_patch is None): sharp_patch = self.sharp_patch pinflip = not nd.get_xsection(xs).symmetry if name is None: name = 'ic_tube' with nd.Cell(name, instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() el_list=[] new_geo=[] for i, tube in enumerate(geo): if tube[0] == 's': if abs(tube[1][0])<1.e-5: continue if (self.MM_route): el_list.append(self.strt_mm(length=tube[1][0], width1=tube[1][1], arrow=False,Ltp=Ltp,width2=width_mm).put()) else: el_list.append(self.strt(length=tube[1][0], width=tube[1][1], arrow=False).put()) new_geo.append(tube) elif tube[0] == 'b': if abs(tube[1][0])<1.e-5: continue if (len(el_list)>0): angle_pre = el_list[-1].pin['b0'].a else: angle_pre = 0 el_list.append(self._circle_(theta_start=angle_pre,theta_stop=angle_pre + tube[1][0], angle=None, radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=sharp_patch).put()) # el_list.append(self.bend(angle=tube[1][0],radius=tube[1][1], width=tube[1][2]).put()) # el_list.append(self._circle_(theta_start=0,theta_stop=tube[1][0], angle=None, # radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=self.sharp_patch).put()) # el_list.append(self._circle_(angle=tube[1][0], radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=self.sharp_patch).put()) new_geo.append(tube) else: cfg.interconnect_errcnt += 1 msg = "IC-{} Tube element not recognized {} in cell '{}'".\ format(cfg.interconnect_errcnt, tube, cfg.cells[-1].cell_name) interconnect_logger(msg, 'error') ic_exception(msg) if el_list: nd.Pin('a0', io=0, width=el_list[0].pin['a0'].width, xs=xs).put(el_list[0].pin['a0']) nd.Pin('b0', io=1, width=el_list[-1].pin['b0'].width, xs=xs).put(el_list[-1].pin['b0']) else: # nothing to place. TODO: Handle in cell.put(), where empty cells can be skipped, e.g. with cell.void = True nd.Pin('a0', io=0, width=None, xs=xs).put(0, 0, 180) nd.Pin('b0', io=1, width=None, xs=xs).put(0, 0, 0) trace.trace_stop() ICcell.length_geo = trace.trace_length() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) return ICcell def strt_bend_strt_p2p_mine(self, pin1=None, pin2=None, radius=None, radius1=None, radius2=None, width=None, xs=None, length1=0, length2=0, ictype='shortest', name=None, arrow=False, width_mm=None,Ltp =None): parse, params = self._strt_bend_strt_p2p_solve(pin1=pin1, pin2=pin2, xs=xs, width=width, radius=radius) pin1, pin2, xs, width, _, radius1, radius2 = parse pinflip = not nd.get_xsection(xs).symmetry if params['found']: solution = params['solution'] L1 = solution['length1'] L2 = solution['length2'] da = solution['da'] if name is None: name = 'ic_strt_bend_strt_p2p' cfg.cp = pin1 if (self.PCB==True): C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow) else: C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow,width_mm=width_mm,Ltp=Ltp) C.pin2 = pin2 return C else: interconnect_logger(params['message'], 'warning') ic_exception(params['message']) # goto bend_strt_bend: return self.bend_strt_bend_p2p_mine(pin1, pin2, radius1=radius, radius2=radius, width=width, xs=xs, arrow=arrow, name=name) ## HGL's sbend ############################################################################################### def sbend_p2p_mine(self, pin1=None, pin2=None, width=None, radius=None, Lstart=0, doFirst=1, arrow=False, width_mm=None,Ltp=None,sharp_patch=False): """ Create pin-to-pin s-bend interconnect. This code is made by myself to avoid the possible gap between two bend The direction of the end pin is ignored and is assumed to be parallel with pin1. Args: pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp) pin2 (Node | Instance | tuple(x, y, a)): end pin width (float): width of the interconnect in um radius (float): bend radius of the interconnect in um xs (str): optional xsection of sbend doFirst (int): (default=1) Lstart (float): straight waveguide length at beginning (positive value) or end (negative value) of sbend arrow (bool): draw connection arrows (default=True) Returns: Cell: sbend element Example: Create and place a sbend to connect two specific points:: import nazca as nd from nazca.interconnects import Interconnect ic = Interconnect(width=2.0, radius=10.0) guide = ic.sbend_p2p(pin1=(0), pin2=(40, 20)) guide.put() nd.export_plt() """ # if pin1.width != pin1.width: # print("--Be carefull that width of two ends of this sbend does not equal to each other.--") res = 0.2 parse = self._p2p_parse(pin1, pin2) pin1, pin2, _, _, _, _, _ = parse if width is None: width = self.width if radius is None: radius = self.radius ## Add code to solve the problem when (pin1.a-pin2.a)!=180 angle_offset = 180-abs(pin1.a-pin2.a) if abs(angle_offset)>0.01: bend = self.bend(radius=radius,width=width,angle=-angle_offset*np.sign(pin1.a-pin2.a),xs=self.xs).put(pin2) pin2 = bend.pin['b0'] ## Do sbend connecting a = pin1.a a = a*np.pi/180 lateral_length = (pin2.x-pin1.x)*np.cos(a) + (pin2.y-pin1.y)*np.sin(a) offset_length = -(pin2.x-pin1.x)*np.sin(a) + (pin2.y-pin1.y)*np.cos(a) cp.goto_pin(pin1) with nd.Cell(name="my_sbend", instantiate=False, cnt=True) as ICcell: ## Add Lstart strt strt = nd.strt(length=Lstart, width=width, xs=self.xs).put(0, 0) lateral_length = lateral_length - Lstart ## Add sbend if abs(offset_length) >= 2*radius: bend1 = nd.bend(radius=radius, width=width, xs=self.xs, angle=90*np.sign(offset_length)).put() nd.strt(length=abs(offset_length)-2*radius, width=width, xs=self.xs).put() bend2 = nd.bend(radius=radius, width=width, xs=self.xs, angle=-90*np.sign(offset_length)).put() nd.strt(length=lateral_length-2*radius, width=width, xs=self.xs).put() # Add patch for lay, grow, acc, polyline in nd.layeriter(xs=self.xs): (a1, b1), (a2, b2), c1, c2 = grow if b1!=0 and b2!=0: if offset_length>0: patch_poly = [ (0,width*a2+b2), (abs(radius)-(width*a2+b2),width*a2+b2), (abs(radius)-(width*a2+b2),abs(radius)), (0,abs(radius)) ] nd.Polygon(points=patch_poly,layer=lay).put(bend1.pin['a0'].move(0,0,180)) patch_poly = [ (0,-(width*a1+b1)), (abs(radius)+(width*a1+b1),-(width*a1+b1)), (abs(radius)+(width*a1+b1),abs(radius)), (0,abs(radius)) ] nd.Polygon(points=patch_poly,layer=lay).put(bend2.pin['b0'].move(0,0,180)) elif offset_length<0: patch_poly = [ (0,(width*a1+b1)), (abs(radius)+(width*a1+b1),(width*a1+b1)), (abs(radius)+(width*a1+b1),-abs(radius)), (0,-abs(radius)) ] nd.Polygon(points=patch_poly,layer=lay).put(bend1.pin['a0'].move(0,0,180)) patch_poly = [ (0,-(width*a2+b2)), (abs(radius)-(width*a2+b2),-(width*a2+b2)), (abs(radius)-(width*a2+b2),-abs(radius)), (0,-abs(radius)) ] nd.Polygon(points=patch_poly,layer=lay).put(bend2.pin['b0'].move(0,0,180)) if lateral_length-2*radius < 0: print("--Be carefull that the length of last strt in strt-sbend-strt is negative.--") elif offset_length == 0: nd.strt(length=lateral_length, width=width, xs=self.xs).put() elif abs(offset_length) < 2*radius: # Sbend parameter sbend_length = 2 * np.sqrt(np.power(radius,2) - np.power(radius-abs(offset_length)/2,2)) theta_range = np.arccos((radius-abs(offset_length)/2) / radius) theta_step = res / radius theta_list = np.arange(0, theta_range, theta_step) # Center of two bend center1 = {"x":0, "y":radius*np.sign(offset_length)} center2 = {"x":sbend_length, "y":offset_length-radius*np.sign(offset_length)} for lay, grow, acc, polyline in nd.layeriter(xs=self.xs): (a1, b1), (a2, b2), c1, c2 = grow Ro = radius + width * a1 + b1 Ri = radius + width * a2 + b2 p1 = [(center1["x"] + Ri*np.sin(theta), center1["y"] - np.sign(offset_length)*Ri*np.cos(theta)) for theta in theta_list] p2 = [(center2['x'] - Ro*np.sin(theta), center2['y'] + np.sign(offset_length)*Ro*np.cos(theta)) for theta in reversed(theta_list)] p3 = [(center2['x'] - Ri*np.sin(theta), center2['y'] + np.sign(offset_length)*Ri*np.cos(theta)) for theta in theta_list] p4 = [(center1["x"] + Ro*np.sin(theta), center1["y"] - np.sign(offset_length)*Ro*np.cos(theta)) for theta in reversed(theta_list)] sbend_poly = p1 + p2 + p3 + p4 nd.Polygon(points=sbend_poly, layer=lay).put() if b1!=0 and b2!=0: if offset_length>0: patch_poly = [ (0,width*a2+b2), (sbend_length,width*a2+b2), (sbend_length,width*a1+b1+offset_length), (0,width*a1+b1+offset_length) ] nd.Polygon(points=patch_poly,layer=lay).put(strt.pin['b0']) elif offset_length<0: patch_poly = [ (0,width*a1+b1), (sbend_length,width*a1+b1), (sbend_length,width*a2+b2+offset_length), (0,width*a2+b2+offset_length) ] nd.Polygon(points=patch_poly,layer=lay).put(strt.pin['b0']) nd.strt(length=lateral_length-sbend_length, width=width, xs=self.xs).put(Lstart+sbend_length, offset_length) nd.Pin(name='a0').put(0, 0, 180) nd.Pin(name='b0').put(lateral_length+Lstart, offset_length, 0) if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0']) return ICcell def sbend_p2p(self, pin1=None, pin2=None, width=None, radius=None, Amax=90, xs=None, doStrFirst=1, Lstart=0, BendEndFlag=1, ref=None, name=None, arrow=False, bsb=True, sharp_patch=None, width_mm=None,Ltp=None): # if (original_function or self.PCB==True or self.sharp_patch==False): if (sharp_patch is None): sharp_patch = self.sharp_patch """ Note : This part is directly copy from sbend_p2p in nazca.Interconnect """ parse, params = self._sbend_p2p_solve( pin1=pin1, pin2=pin2, width=width, radius=radius, length1=Lstart, doStrFirst=doStrFirst, ref=ref, Amax=Amax) pin1, pin2, xs, width, _, radius1, radius2 = parse pinflip = not nd.get_xsection(xs).symmetry if not params['found'] or abs(params['solution']['angle']) < 0*m.pi: interconnect_logger(params['message'], 'info') #TODO: alterative back up if La is so long that Lb is negative? if bsb: msg = "replacing sbend with bend_strt_bend." interconnect_logger(msg, 'info') return self.bend_strt_bend_p2p( pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs, name=name, arrow=arrow, width_mm=width_mm, Ltp=Ltp) else: if (self.MM_route and self.PCB is False): return self.strt_mm_p2p(pin1=pin1,pin2=pin2,xs='error',arrow=arrow,width2=width_mm,Ltp=Ltp) else: return self.strt_p2p(pin1, pin2, xs='error', arrow=arrow) else: if name is None: name = 'ic_sbend' cfg.cp = pin1 if (self.MM_route==False or self.PCB==True or sharp_patch==False): C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow) else: C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow, width_mm=width_mm, Ltp=Ltp,sharp_patch=sharp_patch) C.pin2 = pin2 return C """ Original Function ends """ # else : # return self.sbend_p2p_mine(pin1=pin1, pin2=pin2, width=width, radius=radius, Lstart=Lstart, doFirst=doStrFirst, arrow=arrow) def bend_strt_bend_p2p(self, pin1=None, pin2=None, radius=None, radius1=None, radius2=None, width=None, xs=None, length1=0, length2=0, ictype='shortest', name=None, arrow=True, width_mm=None,Ltp=None, ): """Generate a point-to-point bend-straight-bend interconnect. Args: pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp) pin2 (Node | Instance | tuple(x, y, a)): end pin radius1 (float): optional first bend radius in um radius2 (float): optional second bend radius im um width (float): optional waveguide width in um xs (str): optional xsection ictype (str): interconnection type (default='shortest') options: 'shortest', 'll', 'lr', 'rl', rr', 'all' name (str): optional new name for the component arrow (bool): draw connection arrows (default=True) Returns: Cell: bend_strt_bend element Example: Create and place a bend-straight-bend guide to connect two specific points:: import nazca as nd from nazca.interconnects import Interconnect ic = Interconnect(width=2.0, radius=10.0) guide = ic.bend_strt_bend_p2p(pin1=(0, 0, 0), pin2=(40, 20, 90)) guide.put() nd.export_plt() """ if radius is not None: if radius1 is None: radius1 = radius if radius2 is None: radius2 = radius parse, curves = self._bend_strt_bend_p2p_solve(pin1, pin2, xs=xs, width=width, radius1=radius1, radius2=radius2, length1=length1, length2=length2, ictype=ictype) pin1, pin2, xs, width, _, radius1, radius2 = parse pinflip = not nd.get_xsection(xs).symmetry instantiate = self.instantiate if ictype == 'all': instantiate = True variations = curves['variations'] else: variations = [curves] cells = [] if not curves['found']: interconnect_logger(curves['message'], 'error') return self.strt_p2p(pin1, pin2, xs='error') else: for curve in variations: par = curve['solution'] shape = curve['type'] param = curve['solution'] Ltot = param['Ltot'] L = param['L'] b = param['angle1'] e = param['angle2'] if name is None: name = 'ic_bend_strt_bend_p2p' with nd.Cell("{}_{}".format(name, shape), instantiate=instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() if length1 > 0: e1 = self.line_mm(length=length1, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put() self._circle_(radius=radius1, angle=m.degrees(b), width=width, xs=xs).put() else: e1 = self._circle_(radius=radius1, angle=m.degrees(b), width=width, xs=xs).put() self.line_mm(length=L, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put() self._circle_(radius=radius2, angle=m.degrees(e), width=width, xs=xs).put() if length2 > 0: self.line_mm(length=length2, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put() nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0']) nd.Pin('b0', io=1, width=width, xs=xs).put() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() ICcell.pin2 = pin2 cfg.cp = pin1 cells.append(ICcell) if not cells: msg = "No solution in bend_strt_bend_p2p." interconnect_logger(msg, 'warning') ic_exception(msg) elif len(cells) == 1: return cells[0] elif len(cells) > 1: with nd.Cell('{}_{}'.format(name, xs), cnt=True) as ICgroup: ICcell.group = 'interconnect' trace.trace_start() for cell in cells: cell.put(180) trace.trace_stop() ICgroup.length_geo = trace.trace_length() cfg.cp = pin1 return ICgroup """ Functions Reconstructed with default """ def ubend_p2p(self, pin1=None, pin2=None, radius=None, width=None, xs=None, length=0.1, name=None, arrow=False, balance=0, end_angle=False,original_function=False,Ltp=None,width_mm=None): parse, params = self._ubend_p2p_solve( pin1, pin2, length=length, width=width, radius=radius, balance=balance, end_angle=end_angle ) pin1, pin2, xs, width, _, radius1, radius2 = parse if name is None: name = 'ic_ubend_p2p' cfg.cp = pin1 if (original_function or self.PCB): C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow) C.pin2 = pin2 else: C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow,Ltp=Ltp,width_mm=width_mm) C.pin2 = pin2 return C def strt(self, length=None, width=None, pin=None, xs=None, edge1=None, edge2=None, edgepoints=50, name=None, arrow=False, gridpatch=None): pin = self._getpinout(pin) xs = self._getxs(pin, xs) width = self._getwidth(pin, width, xs) pinflip = not nd.get_xsection(xs).symmetry if gridpatch is None: gridpatch = self.gridpatch if length is None: length = self.length if name is None: name = 'ic_strt' with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() e1 = self.line(length=length, width=width, xs=xs, edge1=edge1, edge2=edge2, edgepoints=edgepoints, gridpatch=gridpatch).put(0) nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0']) nd.Pin('b0', io=1, width=width, xs=xs).put() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() if pin is not None: cfg.cp = pin return ICcell def taper(self, length=None, width1=None, width2=None, shift=0, xs=None, pin=None, name=None, patch=False, arrow=False): pin = self._getpinout(pin) xs = self._getxs(pin, xs) width1 = self._getwidth(pin, width1, xs, adapt=False) width2 = self._getwidth(pin, width2, xs, adapt=False) pinflip = not nd.get_xsection(xs).symmetry if length is None: length = self.length with nd.Cell('taper_{}'.format(xs), instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() e1 = self._taper(length=length, width1=width1, width2=width2, shift=shift, xs=xs).put(0) nd.Pin('a0', io=0, width=width1, xs=xs).put(e1.pin['a0']) nd.Pin('b0', io=1, width=width2, xs=xs).put() if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0'], flip=pinflip) trace.trace_stop() ICcell.length_geo = trace.trace_length() if patch : width_max = np.max(np.array([width1,width2])) for lay, grow, acc, polyline in nd.layeriter(xs=self.xs): if "CLD" in lay : (a1, b1), (a2, b2), c1, c2 = grow nd.strt(length=length,width=width_max*(a1-a2)+b1-b2,layer=lay).put(0,0,0) if pin is not None: cfg.cp = pin return ICcell def taper_p2p(self, pin1=None, pin2=None, width1=None, width2=None, xs=None, name=None, arrow=False ): save_adapt = self.adapt_width self.adapt_width = True # Adapt taper width to what it connects to. parse = self._p2p_parse( pin1=pin1, pin2=pin2, width1=width1, width2=width2, xs=xs ) pin1, pin2, xs, width1, width2, radius1, radius2 = parse length, shift, _ = nd.diff(pin1, pin2) if name is None: name = 'ic_taper_p2p' with nd.Cell(f'{name}_{xs}', instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() e1 = self._taper( length=length, width1=width1, width2=width2, shift=shift, xs=xs ).put(0, 0, pin1.xya()[2]) p1 = nd.Pin('a0', io=0, width=width1, xs=xs).put(e1.pin['a0']) p2 = nd.Pin('b0', io=1, width=width2, xs=xs).put(e1.pin['b0']) if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0']) trace.trace_stop() ICcell.length_geo = trace.trace_length() ICcell.pin2 = pin2 # connect_opt needed because the extra rot in p1 and p2 does hide the line # optical connection: nd.connect_path(p1, p2, trace.trace_length()) cfg.cp = pin1 self.adapt_width = save_adapt # restore previous value return ICcell def strt_p2p(self, pin1=None, pin2=None, width=None, xs=None, name=None, arrow=False ): parse, (length, b) = self._strt_p2p_solve(pin1=pin1, pin2=pin2, width=width, xs=xs) pin1, pin2, xs, width, _, radius1, radius2 = parse if name is None: name = 'ic_strt_p2p' with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell: ICcell.group = 'interconnect' trace.trace_start() e1 = self.line(length=length, width=width, xs=xs).put(0, 0, b) p1 = nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0'].rot(-b)) p2 = nd.Pin('b0', io=1, width=width, xs=xs).put(e1.pin['b0'].rot(-b-pin1.a+pin2.a+180)) if arrow: self.arrow.put(ICcell.pin['a0']) self.arrow.put(ICcell.pin['b0']) trace.trace_stop() ICcell.length_geo = trace.trace_length() ICcell.pin2 = pin2 # connect_opt needed because the extra rot in p1 and p2 does hide the line optical connection: nd.connect_path(p1, p2, trace.trace_length()) cfg.cp = pin1 return ICcell