def chip(size=(13000, 18000), keepout=2000, name='chip01', text_size=250, layer_text=10, layer=99): k = keepout DX = size[0] DY = size[1] OUT = pg.rectangle(size=size, layer=layer) IN = pg.rectangle(size=(DX - 2 * k, DY - 2 * k), layer=layer) IN.move((k, k)) CHIP = pg.boolean(A=OUT, B=IN, operation='A-B', layer=layer) #Add name L = pg.text(text=name, size=text_size, layer=1, justify='center') CHIP.add_ref(L).move((DX / 2, k - text_size - 200)) #Add markers M = global_markers() offset = 110 CHIP.add_ref(M).move([k - offset, k - offset]) CHIP.add_ref(M).move([DX - k + offset - 3000, k - offset]) CHIP.add_ref(M).move([k - offset, DY - k + offset]) CHIP.add_ref(M).move([DX - k + offset - 3000, DY - k + offset]) return CHIP
def eom_sym(wg_width, length, middle_e_width, e_e_gap, chip_width, offset, radius): euler_y = mod_euler(radius=radius, angle=-45)[1][1] euler_x = mod_euler(radius=radius, angle=-45)[1][0] wg_wg_sep = (middle_e_width + e_e_gap) / 2 - 2 * euler_y straight = wg_wg_sep * np.sqrt(2) if wg_wg_sep < 0: raise Exception( "middle_e_width is set too small with respect to Euler radius") left = chip_width / 2 - length / 2 - 2 * euler_x - wg_wg_sep + offset right = left P1 = Path() P1.append(pp.straight(length=left)) P1.append(mod_euler(radius=radius, angle=-45)[0]) P1.append(pp.straight(length=straight)) P1.append(mod_euler(radius=radius, angle=45)[0]) P1.append(pp.straight(length=length)) P1.append(mod_euler(radius=radius, angle=45)[0]) P1.append(pp.straight(length=straight)) P1.append(mod_euler(radius=radius, angle=-45)[0]) P1.append(pp.straight(length=right)) X = CrossSection() X.add(width=wg_width, offset=0, layer=1) waveguide_device1 = P1.extrude(X) E = Device('EOM_GHz') b1 = E.add_ref(waveguide_device1) b2 = E.add_ref(waveguide_device1) b2.mirror((0, 0), (1, 0)) square = middle_e_width * 0.6 square_rec_offset = (middle_e_width - square) / 2 e_left = left + 2 * euler_x + wg_wg_sep e_right = left + 2 * euler_x + wg_wg_sep + length - square #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=10) S = pg.rectangle(size=(square, square), layer=2) #top electrode h_top = middle_e_width / 2 + e_e_gap E.add_ref(R).move([e_left, h_top]) E.add_ref(S).move([e_left, h_top + square_rec_offset]) E.add_ref(S).move([e_right, h_top + square_rec_offset]) #middle electrode E.add_ref(R).move([e_left, -middle_e_width / 2]) E.add_ref(S).move([e_left, -square / 2]) E.add_ref(S).move([e_right, -square / 2]) #bottom electrode h_bot = -3 * middle_e_width / 2 - e_e_gap E.add_ref(R).move([e_left, h_bot]) E.add_ref(S).move([e_left, h_bot + square_rec_offset]) E.add_ref(S).move([e_right, h_bot + square_rec_offset]) #E << R return E
def test_align(): D = Device() # Create different-sized rectangles and add them to D then distribute them [ D.add_ref( pg.rectangle(size=[n * 15 + 20, n * 15 + 20]).move((n, n * 4))) for n in [0, 2, 3, 1, 2] ] D.distribute(elements='all', direction='x', spacing=5, separation=True) # Align top edges D.align(elements='all', alignment='ymax') h = D.hash_geometry(precision=1e-4) assert (h == '38025959a80e46e47eabcf3f096c6273427dabc3') D = Device() # Create different-sized rectangles and add them to D then distribute them [ D.add_ref( pg.rectangle(size=[n * 15 + 20, n * 15 + 20]).move((n, n * 4))) for n in [0, 2, 3, 1, 2] ] D.distribute(elements='all', direction='x', spacing=5, separation=True) # Align top edges D.align(elements='all', alignment='y') h = D.hash_geometry(precision=1e-4) assert (h == 'ed32ee1ce1f3da8f6216020877d6c1b64097c600')
def test_distribute(): D = Device() # Create different-sized rectangles and add them to D [ D.add_ref( pg.rectangle(size=[n * 15 + 20, n * 15 + 20]).move((n, n * 4))) for n in [0, 2, 3, 1, 2] ] # Distribute all the rectangles in D along the x-direction with a separation of 5 D.distribute( elements='all', # either 'all' or a list of objects direction='x', # 'x' or 'y' spacing=5, separation=True) h = D.hash_geometry(precision=1e-4) assert (h == '1aa688d7dfb59e94d28dd0d9b8f324ff30281d70') D = Device() [ D.add_ref( pg.rectangle(size=[n * 15 + 20, n * 15 + 20]).move((n, n * 4))) for n in [0, 2, 3, 1, 2] ] D.distribute(elements='all', direction='x', spacing=100, separation=False, edge='xmin') h = D.hash_geometry(precision=1e-4) assert (h == '18be0ef1db78095233d2f3ae5f065d9f453a6c07')
def poling_region(length=4000, period=5, dutycycle=0.4, gap=25, Lfinger=50, layer=10, pad_width=50): #Calculations Wfinger = period * dutycycle Nfinger = int(length / period) + 1 length = Nfinger * period - (1 - dutycycle) * period P = Device('Poling Electrodes') #Positive side R = pg.rectangle([length, pad_width], layer=layer) F = pg.rectangle([Wfinger, Lfinger], layer=layer) P << R a = P.add_array(F, columns=Nfinger, rows=1, spacing=(period, 0)) a.move([0, pad_width]) #Negative side R2 = pg.rectangle([length, pad_width], layer=layer) r2 = P.add_ref(R2) r2.move([0, pad_width + Lfinger + gap]) return P
def give_loopmirror(gap=.5): # just an example of augmenting a normal phidl device (loop_mirror_terminator) # giving it as a phidl Device as well as port, bounding box, and source things used by MEEP D = Device('loopmirror') cell = D << pg.rectangle([31, 15], layer=lys['FLOORPLAN']) cell.center = (0, 0) access = D << pg.compass([8, .35], layer=lys['wg_deep']) access.y = cell.y access.xmin = cell.xmin mmi = mmi1x2(gap_mmi=.5) loop = D << loop_mirror_terminator(y_splitter=mmi) loop.connect('wg_in_1', access.ports['E']) medium_map = get_layer_mapping(lys) port = D << pg.rectangle([.1, 1], layer=1) source = D << pg.rectangle([.1, 1], layer=2) port.y = 0 source.y = 0 port.x = loop.xmin - 6 source.x = loop.xmin - 7 D.flatten() return D
def dcpm(L, elec_w, e_e_gap, via, wg_width): P = Path() P.append(pp.straight(length=L)) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) DCPM = Device() DCPM << P.extrude(X) R1 = pg.rectangle(size=(L, elec_w), layer=40) R2 = pg.rectangle(size=(L, elec_w), layer=40) DCPM << R1.move([0, e_e_gap / 2]) DCPM << R2.move([0, -elec_w - e_e_gap / 2]) return DCPM
def dcimthin(wg_width, off_chip, W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi, length, radius, x_pos, y_pos, middle_e_width, e_e_gap): if e_e_gap == 9: angle = 12.5196846104787 elif e_e_gap == 7: angle = 11.83919343835311 elif e_e_gap == 11: angle = 13.165741242693 # Parameters mmi_length = L_tp * 2 + L_mmi side_electrode_width = middle_e_width * 2 eulerX = mod_euler(radius=radius, angle=angle)[1][0] racetrack_length = eulerX * 4 + length #mzi_length = racetrack_length + mmi_length*2 side = x_pos #side = (chip_width - mzi_length)/2 # Devices M = mzi(length, radius, angle, wg_width, Y_mmi) mmiL = mmi(W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi) mmiR = mmi(W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi) mmiR.mirror().move([mmi_length, 0]) E = Device('EOM') #E << pg.rectangle(size=(side+off_chip,wg_width), layer=1).move([-off_chip,y_pos-wg_width/2]) E << mmiL.move([side, y_pos]) E << M.move([side + mmi_length, y_pos]) E << mmiR.move([side + mmi_length + racetrack_length, y_pos]) #E << pg.rectangle(size=(side+off_chip,wg_width), layer=1).move([side+mzi_length,y_pos-wg_width/2]) e_left = side + mmi_length + 2 * eulerX #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=40) R2 = pg.rectangle(size=(length, side_electrode_width), layer=40) #top electrode h_top = middle_e_width / 2 + e_e_gap + y_pos E.add_ref(R2).move([e_left, h_top]) #middle electrode E.add_ref(R).move([e_left, y_pos - middle_e_width / 2]) #bottom electrode h_bot = -middle_e_width / 2 - side_electrode_width - e_e_gap + y_pos E.add_ref(R2).move([e_left, h_bot]) return E, e_left
def global_markers(layer_marker=10, layer_mask=20): D = Device('Global Markers') R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array #Add marker cover cover = pg.bbox(bbox=a.bbox, layer=layer_mask) D << pg.offset(cover, distance=100, layer=layer_mask) return D
def dcim(im_gap, im_length, coupler_l, im_r, im_angle, elec_w, e_e_gap, via, wg_width, V_Groove_Spacing): P = Path() euler_y = mod_euler(radius=im_r, angle=im_angle)[1][1] euler_x = mod_euler(radius=im_r, angle=im_angle)[1][0] l_bend = ((V_Groove_Spacing - im_gap - 4 * euler_y - wg_width) / 2) / np.sin(np.pi * im_angle / 180) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.straight(length=l_bend)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=coupler_l)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.straight(length=im_length)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=coupler_l)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=l_bend)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.movey(V_Groove_Spacing) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) IM = Device('IM') IM << P.extrude(X) IM << P.extrude(X).mirror(p1=[1, V_Groove_Spacing / 2], p2=[2, V_Groove_Spacing / 2]) R1 = pg.rectangle(size=(im_length, elec_w), layer=40) R2 = pg.rectangle(size=(im_length, elec_w), layer=40) R3 = pg.rectangle(size=(im_length, elec_w), layer=40) R4 = pg.rectangle(size=(im_length, elec_w), layer=40) movex = euler_x * 4 + coupler_l + l_bend * np.cos(np.pi * im_angle / 180) movey = euler_y * 2 + im_gap / 2 + wg_width IM << R1.move( [movex, V_Groove_Spacing / 2 + movey + e_e_gap / 2 - wg_width / 2]) IM << R2.move([ movex, V_Groove_Spacing / 2 + movey - e_e_gap / 2 - elec_w - wg_width / 2 ]) IM << R3.move( [movex, V_Groove_Spacing / 2 - movey + e_e_gap / 2 + wg_width / 2]) IM << R4.move([ movex, V_Groove_Spacing / 2 - movey - e_e_gap / 2 - elec_w + wg_width / 2 ]) return IM, movex
def _draw_unit_cell(self): o = self.origin rect=pg.rectangle(size=(self.coverage*self.pitch,self.length),\ layer=self.layer) rect.move(origin=(0, 0), destination=o.coord) unitcell = Device() r1 = unitcell << rect unitcell.absorb(r1) rect_partialetch=pg.rectangle(\ size=(\ (1-self.coverage)*self.pitch,self.length-self.y_offset),\ layer=LayoutDefault.layerPartialEtch) rect_partialetch.move(origin=o.coord,\ destination=(self.pitch*self.coverage,self.y_offset)) rp1 = unitcell << rect_partialetch rp2 = unitcell << rect_partialetch rp2.move(destination=(self.pitch, 0)) r2 = unitcell << rect r2.move(origin=o.coord,\ destination=(o+Point(self.pitch,self.y_offset)).coord) r3 = unitcell << rect r3.move(origin=o.coord,\ destination=(o+Point(2*self.pitch,0)).coord) unitcell.absorb(r2) unitcell.absorb(r3) unitcell.name = "UnitCell" del rect, rect_partialetch return unitcell
def __init__(self, params): SividdleDevice.__init__(self, name=params['name']) # Generate binary bitmap out of image bitmap = image_to_binary_bitmap(params['image'], params['threshold'], params['dither']) x_image = bitmap.shape[0] y_image = bitmap.shape[1] if params['image_device'] is None: # Define pixel polygon pixel = pg.rectangle(size=(params['pixel_size'], params['pixel_size']), layer=params['layer']) else: pixel = params['image_device'] for x in range(x_image): for y in range(y_image): if bitmap[x, y] == 1: reference = self.add_ref(pixel) reference.move([x * pixel.xsize, y * pixel.ysize]) # Shift center of bounding box to origin. self.center = [0, 0]
def test_packer(): np.random.seed(5) D_list = [ pg.ellipse(radii=np.random.rand(2) * n + 2).move(np.random.rand(2) * 100 + 2) for n in range(50) ] D_list += [ pg.rectangle(size=np.random.rand(2) * n + 2).move(np.random.rand(2) * 1000 + 2) for n in range(50) ] D_packed_list = pg.packer( D_list, # Must be a list or tuple of Devices spacing=1.25, # Minimum distance between adjacent shapes aspect_ratio=(1, 2), # Shape of the box max_size=( None, None), # Limits the size into which the shapes will be packed density=1.5, sort_by_area=True, # Pre-sorts the shapes by area verbose=False, ) # The function will return a list of packed Devices. If not all the Devices # in D_list can fit in the area `max_size`, it will fill up the first box to # capacity then create another, repeating until all the shapes are packed # into boxes of max_size. (`max_size` can be (None, None)) # of `max_size` as is necessary D = D_packed_list[0] h = D.hash_geometry(precision=1e-4) assert (h == 'd90e43693a5840bdc21eae85f56fdaa57fdb88b2')
def _draw_unit_cell(self): o = self.origin rect=pg.rectangle(size=(self.coverage*self.pitch,self.length),\ layer=self.layer) rect.move(origin=(0, 0), destination=o.coord) unitcell = Device() r1 = unitcell << rect unitcell.absorb(r1) r2 = unitcell << rect r2.move(origin=o.coord,\ destination=(o+Point(self.pitch,self.y_offset)).coord) r3 = unitcell << rect r3.move(origin=o.coord,\ destination=(o+Point(2*self.pitch,0)).coord) unitcell.absorb(r2) unitcell.absorb(r3) unitcell.name = "UnitCell" del rect return unitcell
def add_passivation(cell, margin, scale, layer): rect = pg.rectangle(size=(cell.xsize * scale.x, cell.ysize * scale.y)) margin_rect = pg.rectangle(size=(margin.x * cell.xsize, margin.y * cell.ysize)) rect.move(origin=rect.center, destination=cell.center) margin_rect.move(origin=margin_rect.center, destination=cell.center) pa = pg.boolean(rect, margin_rect, operation='xor') for l in layer: cell.add_polygon(pa.get_polygons(), layer=(l, 0))
def __init__(self, params): SividdleDevice.__init__(self, name='etchslab') # retrieve parameters self.id_string = params['id_string'] self.expose_layer = params['expose_layer'] self.label_layer = params['label_layer'] self.length_slab = params['length_slab'] self.width_slit = params['width_slit'] self.width_slab = params['width_slab'] slit = pg.rectangle(size=(self.width_slit, self.length_slab), layer=self.expose_layer).rotate(90) self << pg.copy(slit).movey((self.width_slit + self.width_slab) * 0.5) self << pg.copy(slit).movey(-(self.width_slit + self.width_slab) * 0.5) self.add_label(text='{} \n slab_width = {:.2f} \ \n slit_width = {:.2f} \ \n slab_length = {:.2f}'.format(self.id_string, self.width_slab, self.width_slit, self.length_slab), position=(self.xmin, self.ymax), layer=self.label_layer) # Shift center of bounding box to origin. self.center = [0, 0]
def draw(self): if self.shape == 'square': cell=pg.rectangle(size=(self.size,self.size),\ layer=self.layer) elif self.shape == 'circle': cell=pg.circle(radius=self.size/2,\ layer=self.layer) else: raise ValueError("Via shape can be \'square\' or \'circle\'") cell.move(origin=(0,0),\ destination=self.origin.coord) cell.add_port(Port(name='conn',\ midpoint=cell.center,\ width=cell.xmax-cell.xmin,\ orientation=90)) return cell
def draw(self): ''' Generates layout cell based on current parameters. 'conn' port is included in the cell. Returns ------- cell : phidl.Device. ''' o = self.origin pad=pg.rectangle(size=self.size.coord,\ layer=self.layer).move(origin=(0,0),\ destination=o.coord) cell = Device(self.name) r1 = cell << pad cell.absorb(r1) r2 = cell << pad r2.move(origin=o.coord,\ destination=(o+self.distance).coord) cell.absorb(r2) cell.add_port(name='conn',\ midpoint=(o+Point(self.size.x/2,self.size.y)).coord,\ width=self.size.x,\ orientation=90) del pad return cell
def _draw_padded_via(self): viaref=DeviceReference(self.via.draw()) size=float(self.via.size*self.over_via) port=viaref.ports['conn'] trace=pg.rectangle(size=(size,size),layer=self.pad_layers[0]) trace.move(origin=trace.center,\ destination=viaref.center) trace2=pg.copy_layer(trace,layer=self.pad_layers[0],new_layer=self.pad_layers[1]) cell=Device(self.name) cell.absorb(cell<<trace) cell.absorb(cell<<trace2) cell.add(viaref) port.midpoint=(port.midpoint[0],cell.ymax) port.width=size cell.add_port(port) if bottom_conn==False: cell.remove_layers(layers=[self.pad_layers[1]]) return cell
def via_test(x_pos, l_testpad, h_testpad, via, chip_width, chip_height): offset = 10 D = Device('via test') D << pg.rectangle(size=(l_testpad, h_testpad), layer=10).move( [x_pos - l_testpad / 2, chip_height / 2 - h_testpad / 2]) D << pg.rectangle(size=(via, via), layer=2).move( [x_pos - l_testpad / 2, chip_height / 2 - via / 2]) D << pg.rectangle(size=(via, via), layer=2).move( [x_pos + l_testpad / 2 - via, chip_height / 2 - via / 2]) D << pg.rectangle(size=(l_testpad, h_testpad), layer=3).move([ x_pos - l_testpad / 2 + via - l_testpad + offset, chip_height / 2 - h_testpad / 2 ]) D << pg.rectangle(size=(l_testpad, h_testpad), layer=3).move([ x_pos + l_testpad / 2 - via - offset, chip_height / 2 - h_testpad / 2 ]) return D
def bonding_pads(margin, pad, chip_width, chip_height): num = 18 sep = (chip_width - 2 * margin) / num P = pg.rectangle(size=(pad, pad), layer=3).move([margin, margin]) P << pg.rectangle(size=(pad, pad), layer=3).move( [margin, chip_height - margin - pad]) for i in range(1, int(num / 2)): P << pg.rectangle(size=(pad, pad), layer=3).move( [margin + i * sep, margin]) P << pg.rectangle(size=(pad, pad), layer=3).move( [margin + i * sep, chip_height - margin - pad]) E = Device('bonding_pads') b1 = E.add_ref(P) b2 = E.add_ref(P) b2.mirror((chip_width / 2, chip_height), (chip_width / 2, 0)) return E, sep
def contact_pads(size=(150, 150), label='', label_size=50, layer=10): # P = Device('Pad') R = pg.rectangle(size, layer) if label != '': L = pg.text(label, label_size, layer=layer) L.move([10, 10]) P = pg.boolean(A=R, B=L, operation='A-B', layer=layer) else: P = R return P
def triplet_pad(x, y, pad, pad_g, elec_m, elec, elec_s, e_e_gap, pos): R = pg.rectangle(size=(pad, pad), layer=70) D = Device() rect1 = D << R rect2 = D << R rect3 = D << R rect1.move([x, y]) rect2.move([x - pad_g, y]) rect3.move([x + pad_g, y]) #middle connector pad xm1 = x - elec_m / 2 xm2 = x - pad / 2 xm3 = x + pad / 2 xm4 = x + elec_m / 2 xpts = (xm1 + pad / 2, xm2 + pad / 2, xm3 + pad / 2, xm4 + pad / 2) ypts = (y + pad + elec, y + elec, y + elec, y + pad + elec) if pos == 'u': ypts = (y - elec, y, y, y - elec) D.add_polygon([xpts, ypts], layer=70) text_size = 20 L = pg.text(text='S', size=text_size, layer=97, justify='center') D.add_ref(L).move((x + pad / 3, y)) #left connector pad xm1 = x - elec_m / 2 - e_e_gap - elec_s xm2 = x - pad / 2 - pad_g xm3 = x + pad / 2 - pad_g xm4 = x - elec_m / 2 - e_e_gap xpts = (xm1 + pad / 2, xm2 + pad / 2, xm3 + pad / 2, xm4 + pad / 2) ypts = (y + pad + elec, y + elec, y + elec, y + pad + elec) if pos == 'u': ypts = (y - elec, y, y, y - elec) D.add_polygon([xpts, ypts], layer=70) L = pg.text(text='G', size=text_size, layer=97, justify='center') D.add_ref(L).move((x - pad_g + +pad / 3, y)) #left connector pad xm1 = x + elec_m / 2 + e_e_gap xm2 = x + pad / 2 + pad_g - pad xm3 = x + pad / 2 + pad_g xm4 = x + elec_m / 2 + e_e_gap + elec_s xpts = (xm1 + pad / 2, xm2 + pad / 2, xm3 + pad / 2, xm4 + pad / 2) ypts = (y + pad + elec, y + elec, y + elec, y + pad + elec) if pos == 'u': ypts = (y - elec, y, y, y - elec) D.add_polygon([xpts, ypts], layer=70) L = pg.text(text='G', size=text_size, layer=97, justify='center') D.add_ref(L).move((x + pad_g + +pad / 3, y)) return D
def wb_pad(x, y, pad, name): R = pg.rectangle(size=(pad, pad - 0.5), layer=70) D = Device() rect1 = D << R rect1.move([x, y]) text_size = 20 L = pg.text(text=name, size=text_size, layer=97, justify='center') D.add_ref(L).move((x + pad / 3, y)) return D
def test_write_and_import_gds(): D = Device() D.add_ref(pg.rectangle(size=[1.5, 2.7], layer=[3, 2])) D.add_ref(pg.rectangle(size=[0.8, 2.5], layer=[9, 7])) D.add_array(pg.rectangle(size=[1, 2], layer=[4, 66]), rows=3, columns=2, spacing=[14, 7.5]) D.add_array(pg.rectangle(size=[1.5, 2.5], layer=[4, 67]), rows=1, columns=2, spacing=[14, 7.5]) D.add_polygon([[3, 4, 5], [6.7, 8.9, 10.15]], layer=[7, 8]) D.add_polygon([[3, 4, 5], [1.7, 8.9, 10.15]], layer=[7, 9]) precision = 1e-4 unit = 1e-6 h1 = D.hash_geometry(precision=precision) D.write_gds('temp.gds', precision=unit * precision, unit=1e-6) Dimport = pg.import_gds('temp.gds', flatten=False) h2 = Dimport.hash_geometry(precision=precision) assert (h1 == h2)
def __init__(self, params): SividdleDevice.__init__(self, name='aligment_mark') interim_alignment_mark = SividdleDevice('interim_alignment_mark') # Add four squares defining the alignment mark interim_alignment_mark << pg.rectangle( size=(params['d_large'], params['d_large']), layer=params['layer']) interim_alignment_mark << pg.rectangle( size=(params['d_small'], params['d_small']), layer=params['layer']).movex(params['d_large'] + params['sep']) interim_alignment_mark << pg.rectangle( size=(params['d_small'], params['d_small']), layer=params['layer']).movey(params['d_large'] + params['sep']) interim_alignment_mark << pg.rectangle( size=(params['d_large'], params['d_large']), layer=params['layer']).move([ params['d_small'] + params['sep'], params['d_small'] + params['sep'] ]) # Add interim device to self, invert if choosen. if params['invert']: self << interim_alignment_mark.invert(params['layer']) else: self << interim_alignment_mark # Add bounding box # Make exposure box. if params['exposure_box']: exposure_box_width = self.xsize \ + 2 * params['exposure_box_dx'] exposure_box = pg.rectangle(size=(exposure_box_width, exposure_box_width), layer=params['exposure_box_layer']) self << exposure_box.move(( self.xsize * 0.5 - exposure_box.xsize * 0.5, self.ysize * 0.5 - exposure_box.ysize * 0.5, )) # Center device # Shift center of bounding box to origin self.center = [0, 0] # Add dot at alignment point if params['make_dot']: dot = pg.rectangle(size=(params['dot_size'], params['dot_size']), layer=params['dot_layer']) dot.move( (-params['dot_size'] * 0.5, -params['dot_size'] * 0.5)) self << dot
def draw(self): name = self.name o = self.origin pad_x = self.size.x if pad_x > self.pitch * 9 / 10: pad_x = self.pitch * 9 / 10 warnings.warn("Pad size too large, capped to pitch*9/10") pad_cell=pg.rectangle(size=(pad_x,self.size.y),\ layer=self.layer) pad_cell.move(origin=(0,0),\ destination=o.coord) cell = Device(self.name) dp = Point(self.pitch, 0) pad_gnd_sx = cell << pad_cell pad_sig = cell << pad_cell pad_sig.move(origin=o.coord,\ destination=(o+dp).coord) pad_gnd_dx = cell << pad_cell pad_gnd_dx.move(origin=o.coord,\ destination=(o+dp*2).coord) cell.add_port(Port(name='sig',\ midpoint=(o+Point(pad_x/2+self.pitch,self.size.y)).coord,\ width=pad_x,\ orientation=90)) cell.add_port(Port(name='gnd_left',\ midpoint=(o+Point(pad_x/2,self.size.y)).coord,\ width=pad_x,\ orientation=90)) cell.add_port(Port(name='gnd_right',\ midpoint=(o+Point(pad_x/2+2*self.pitch,self.size.y)).coord,\ width=pad_x,\ orientation=90)) return cell
def rfpm(wg_width, length, middle_e_width, e_e_gap): side_electrode_width = middle_e_width * 2 P = Path() P.append(pp.straight(length=length)) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) RFPM = Device() RFPM << P.extrude(X) Rt = pg.rectangle(size=(length, side_electrode_width), layer=40) Rm = pg.rectangle(size=(length, middle_e_width), layer=40) Rb = pg.rectangle(size=(length, side_electrode_width), layer=40) RFPM << Rt.move([0, e_e_gap / 2]) RFPM << Rm.move([0, -middle_e_width - e_e_gap / 2]) RFPM << Rb.move( [0, -middle_e_width - side_electrode_width - e_e_gap - e_e_gap / 2]) square = middle_e_width * 0.9 side_height = side_electrode_width * 0.9 square_rec_offset = (side_electrode_width - side_height) / 2 square_rec_offset_m = (middle_e_width - square) / 2 e_left = 0 e_right = e_left + length - square #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=40) R2 = pg.rectangle(size=(length, side_electrode_width), layer=40) S = pg.rectangle(size=(square, square), layer=50) S2 = pg.rectangle(size=(square, side_height), layer=50) #top electrode h_top = e_e_gap / 2 RFPM.add_ref(S2).move([e_left, h_top + square_rec_offset]) RFPM.add_ref(S2).move([e_right, h_top + square_rec_offset]) #middle electrode h_mid = -middle_e_width - e_e_gap / 2 RFPM.add_ref(S).move([e_left, h_mid + square_rec_offset_m]) RFPM.add_ref(S).move([e_right, h_mid + square_rec_offset_m]) #bottom electrode h_bot = -middle_e_width - side_electrode_width - e_e_gap - e_e_gap / 2 RFPM.add_ref(S2).move([e_left, h_bot + square_rec_offset]) RFPM.add_ref(S2).move([e_right, h_bot + square_rec_offset]) return RFPM
def test_group(): # Test all types D = Device() E1 = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=0) E2 = pg.rectangle(size=(4, 2), layer=0).movex(15) e1 = D << E1 e2 = D << E2 e3 = D << E2 e4 = D.add_label('hello', position=(1.5, -1.5)) e5 = pg.snspd() e6 = D.add_polygon([(8, 6, 7, 9, 7, 0), (6, 8, 9, 5, 7, 0)]) e7 = D.add_array(pg.cross()) e2verify = D << E2 # Test creation and addition G = Group() G.add(e1) G.add(e2) G.add([e3, e4, e5]) G += (e6, e7) assert np.allclose(G.bbox.flatten(), np.array([-10., -8.5, 105., 105.])) # Test movement G.move((2, 7)) e2verify.move((2, 7)) assert np.allclose(G.bbox.flatten(), np.array([-8., -1.5, 107., 112.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation # Test rotation G.rotate(90, center=(5, 5)) e2verify.rotate(90, center=(5, 5)) assert np.allclose(G.bbox.flatten(), np.array([-102., -8., 11.5, 107.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation # Test mirroring G.mirror(p1=(1, 1), p2=(-1, 1)) e2verify.mirror(p1=(1, 1), p2=(-1, 1)) assert np.allclose(G.bbox.flatten(), np.array([-102., -105., 11.5, 10.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation h = D.hash_geometry(precision=1e-4) assert (h == '3964acb3971771c6e70ceb587c2ae8b37f2ed112')
def test_pack(): import phidl.geometry as pg D_list = [pg.ellipse(radii=np.random.rand(2) * n + 2) for n in range(2)] D_list += [pg.rectangle(size=np.random.rand(2) * n + 2) for n in range(2)] D_packed_list = pack( D_list, # Must be a list or tuple of Components spacing=1.25, # Minimum distance between adjacent shapes aspect_ratio=(2, 1), # (width, height) ratio of the rectangular bin max_size=( None, None), # Limits the size into which the shapes will be packed density= 1.05, # Values closer to 1 pack tighter but require more computation sort_by_area=True, # Pre-sorts the shapes by area ) c = D_packed_list[0] # Only one bin was created, so we plot that # print(len(c.get_dependencies())) assert len(c.get_dependencies()) == 4