def fill_poly(poly): bbox = poly.bbox() poly_reg = Region(poly) t_reg = Region() # Draw boundary around holes in the polygon for hole_i in range(0, poly.holes()): points = [p for p in poly.each_point_hole(hole_i)] points.append(points[0]) boundary = Path(points, 2 * d) poly_reg -= Region(boundary) # Draw boundary around the outer edge of the polygon points = [p for p in poly.each_point_hull()] points.append(points[0]) boundary = Path(points, 2 * d) poly_reg -= Region(boundary) # Fill the boundary box with holes y = bbox.p1.y + height while y < bbox.p2.y - height: x = bbox.p1.x + width while x < bbox.p2.x - width: box = pya.Box().from_dbox( pya.DBox(DPoint(x, y), DPoint(x + width, y + height))) x += dx t_reg.insert(box) y += dy # Select only inner holes holes_inside = t_reg.select_inside(poly_reg) for box in holes_inside.each(): poly.insert_hole(list(box.each_point_hull())) return poly
def fill_holes(obj, dx=10e3, dy=8e3, width=5e3, height=5e3, d=0): if (obj.is_cell_inst()): return None poly = obj.shape.polygon bbox = poly.bbox() poly_reg = Region(poly) t_reg = Region() y = bbox.p1.y + height while (y < bbox.p2.y - height): x = bbox.p1.x + width while (x < bbox.p2.x - width): box = pya.Box().from_dbox( pya.DBox(DPoint(x, y), DPoint(x + width, y + height))) x += dx t_reg.clear() t_reg.insert(box) qwe = t_reg.select_inside(poly_reg) if (qwe.is_empty()): continue edge_pairs = qwe.inside_check(poly_reg, d) if (edge_pairs.is_empty()): poly.insert_hole(box) y += dy obj.shape.polygon = poly
def split_polygons(obj, max_pts=200, print_tree=False): """ Recursively splitting polygons in region or in polygons list until every resulted polygon has less than `max_pts` points. Parameters ---------- obj : Union[Region, List[Polygon]] Structure to operate on max_pts : int maximum points in each polygon Returns ------- Union[pya.Region, list[pya.Polygon]] Resulting structure that has the same type as input structure. """ # global split_shift_str if isinstance(obj, pya.Region): result_reg = Region() reg_to_split = obj # if print_tree: # print(split_shift_str + "got reg") for poly in reg_to_split: result_reg.insert(split_polygons([poly.dup()], max_pts, print_tree)) return result_reg elif isinstance(obj, list): if isinstance(obj[0], pya.Polygon): # if this is list of polygons polygons_list = obj # if print_tree: # print(split_shift_str + f"got array of {len(polygons_list)} polygons") resulting_polygons_list = [] for i, poly in enumerate(polygons_list): if poly.num_points() < max_pts: # if print_tree: # print(split_shift_str + f"polygon #{k} is ok") # recursion base (if all polygons satisfy this condition) resulting_polygons_list.append(poly) else: # if print_tree: # print(split_shift_str + f"polygon #{k} needs dividing") # split_shift_str += "\t" resulting_polygons_list.extend( split_polygons(poly.split(), max_pts, print_tree)) # if print_tree: # split_shift_str = split_shift_str[:-1] return resulting_polygons_list else: raise ValueError( "`split_polygons` function: List is supplied as argument, but this is not " "list of `pya.Polygon` objects") else: raise ValueError( "`split_polygons` function: Unknown argument received as `obj`" "only `pya.Region` or `list[pya.Polygons]` are supported")
def extend_photo_overetching(self): tmp_reg = Region() ep = pya.EdgeProcessor() for poly in self.region_ph.each(): tmp_reg.insert( ep.simple_merge_p2p([ poly.sized(FABRICATION.OVERETCHING, FABRICATION.OVERETCHING, 2) ], False, False, 1)) self.region_ph = tmp_reg
class MyDesign(ChipDesign): def draw(self): info_bridges1 = pya.LayerInfo(3, 0) # bridge photo layer 1 self.region_bridges1 = Region() self.layer_bridges1 = self.layout.layer(info_bridges1) info_bridges2 = pya.LayerInfo(4, 0) # bridge photo layer 2 self.region_bridges2 = Region() self.layer_bridges2 = self.layout.layer(info_bridges2) self.lv.add_missing_layers() self.draw_chip() self.draw_arc() self.bridgify_arc() def draw_chip(self): self.chip = CHIP_10x10_12pads self.chip_box: pya.DBox = self.chip.box self.region_bridges2.insert(self.chip_box) self.region_ph.insert(self.chip_box) def draw_arc(self): self.Z0 = CPWParameters(200e3, 100e3) cpw_start = (self.chip_box.p1 + self.chip_box.p2)/2 self.arc = CPWArc( z0=self.Z0, start=cpw_start, R=1.2e6, delta_alpha=5/4*np.pi, trans_in=DTrans.R0 ) self.arc.place(self.region_ph) def bridgify_arc(self): print(isinstance(self.arc, CPWArc)) print(type(self.arc)) Bridge1.bridgify_CPW( self.arc, bridges_step=100e3, dest=self.region_bridges1, dest2=self.region_bridges2, ) def _transfer_regs2cell(self): self.cell.shapes(self.layer_ph).insert(self.region_ph) self.cell.shapes(self.layer_el).insert(self.region_el) self.cell.shapes(self.layer_bridges1).insert(self.region_bridges1) self.cell.shapes(self.layer_bridges2).insert(self.region_bridges2) self.lv.zoom_fit()
def inverse_destination(self, dest: Union[Region, Cell], layer_i: int = -1): """ Inverses empty regions and solid polygons on the given destination `dest` and `layer`. If layer is not specified, destination `dest` is interpreted as `pya.Region` instance. Otherwise, `dest` is interpreted as `pya.Cell` instance Parameters ---------- dest : Union[Region, Cell] destination, interpreted either as Region or Cell instances depending on whether layer was provided layer_i : Optional[int] positive layer index. Returns ------- None """ tmp_reg = Region() tmp_reg.insert(self.chip_box) if layer_i == -1: dest_reg = dest dest_reg ^= tmp_reg else: r_cell = Region(dest.begin_shapes_rec(layer_i)) r_cell ^= tmp_reg temp_layer_i = dest.layout().layer( pya.LayerInfo(PROGRAM.LAYER1_NUM, 0)) # Moving layers. # Due to internal representation, region polygons are actually # point to polygons in a cell. So we can dest.shapes(temp_layer_i).insert(r_cell) dest.layout().clear_layer(layer_i) dest.layout().move_layer(temp_layer_i, layer_i) dest.layout().delete_layer(temp_layer_i)
def extended_region(reg, extension=0): """ extends region in outer direction by `extension` value. extension is orthogonal to region`s polygons edges. Parameters ---------- reg : Region pya.Region instance extension : float extension in nm Returns ------- Region extended version of region """ tmp_reg = Region() ep = pya.EdgeProcessor() for poly in reg.each(): tmp_reg.insert( ep.simple_merge_p2p([poly.sized(extension, extension, 2)], False, False, 1)) return tmp_reg
xmon_fork_gnd_gap = 20e3 fork_x_span = cross_width + 2 * (xmon_fork_gnd_gap + fork_metal_width) fork_y_span = None # Xmon-fork parameters # -20e3 for Xmons in upper sweet-spot # -10e3 for Xmons in lower sweet-spot import itertools xmon_fork_penetrations = [-20e3, -10e3, -20e3, -10e3, -20e3] # clear this cell and layer cell.clear() tmp_reg = Region() # faster to call `place` on single region # place chip metal layer chip_box = pya.Box(DPoint(0, 0), DPoint(CHIP.dx, CHIP.dy)) tmp_reg.insert(chip_box) contact_pads = CHIP.get_contact_pads() for contact_pad in contact_pads: contact_pad.place(tmp_reg) # place readout waveguide ro_line_turn_radius = 200e3 ro_line_dy = 600e3 cpwrl_ro = CPW_RL_Path( contact_pads[-1].end, shape="LRLRL", cpw_parameters=Z0, turn_radiuses=[ro_line_turn_radius]*2, segment_lengths=[ro_line_dy, CHIP.pcb_feedline_d, ro_line_dy], turn_angles=[pi/2, pi/2], trans_in=Trans.R270 ) cpwrl_ro.place(tmp_reg)
class Design5Q(ChipDesign): def __init__(self, cell_name): super().__init__(cell_name) info_el2 = pya.LayerInfo(3, 0) # for DC contact deposition self.region_el2 = Region() self.layer_el2 = self.layout.layer(info_el2) info_bridges1 = pya.LayerInfo(4, 0) # bridge photo layer 1 self.region_bridges1 = Region() self.layer_bridges1 = self.layout.layer(info_bridges1) info_bridges2 = pya.LayerInfo(5, 0) # bridge photo layer 2 self.region_bridges2 = Region() self.layer_bridges2 = self.layout.layer(info_bridges2) # layer with polygons that will protect structures located # on the `self.region_el` - e-beam litography layer info_el_protection = pya.LayerInfo(6, 0) self.region_el_protection = Region() self.layer_el_protection = self.layout.layer(info_el_protection) self.lv.add_missing_layers() # has to call it once more to add new layers ### ADDITIONAL VARIABLES SECTION START ### # chip rectangle and contact pads self.chip = CHIP_10x10_12pads self.chip.pcb_gap -= 2 * FABRICATION.OVERETCHING self.chip.pcb_width += 2 * FABRICATION.OVERETCHING self.chip.pcb_Z = CPWParameters(self.chip.pcb_width, self.chip.pcb_gap) self.chip_box: pya.DBox = self.chip.box self.z_md_fl: CPWParameters = CPWParameters(11e3, 5.7e3) # Z = 50.09 E_eff = 6.235 (E = 11.45) self.ro_Z: CPWParameters = self.chip.chip_Z self.contact_pads: list[ContactPad] = self.chip.get_contact_pads( [self.z_md_fl] * 10 + [self.ro_Z] * 2, FABRICATION.OVERETCHING ) # readout line parameters self.ro_line_turn_radius: float = 200e3 self.ro_line_dy: float = 1600e3 self.cpwrl_ro_line: CPW_RL_Path = None self.Z0: CPWParameters = CHIP_10x10_12pads.chip_Z # resonators objects list self.resonators: List[EMResonatorTL3QbitWormRLTailXmonFork] = [] # distance between nearest resonators central conductors centers # constant step between resonators origin points along x-axis. self.resonators_dx: float = 900e3 # resonator parameters self.L_coupling_list: list[float] = [1e3 * x for x in [310, 320, 320, 310, 300]] # corresponding to resonanse freq is linspaced in interval [6,9) GHz self.L0 = 1150e3 self.L1_list = [1e3 * x for x in [60.7218, 91.3339, 133.001, 137.77, 79.9156]] self.r = 60e3 self.N_coils = [3] * len(self.L1_list) self.L2_list = [self.r] * len(self.L1_list) self.L3_list = [0e3] * len(self.L1_list) # to be constructed self.L4_list = [self.r] * len(self.L1_list) self.width_res = 20e3 self.gap_res = 10e3 self.Z_res = CPWParameters(self.width_res, self.gap_res) self.to_line_list = [56e3] * len(self.L1_list) self.fork_metal_width = 10e3 self.fork_gnd_gap = 15e3 self.xmon_fork_gnd_gap = 14e3 # resonator-fork parameters # for coarse C_qr evaluation self.fork_y_spans = [x * 1e3 for x in [8.73781, 78.3046, 26.2982, 84.8277, 35.3751]] # xmon parameters self.xmon_x_distance: float = 545e3 # from simulation of g_12 # for fine C_qr evaluation self.xmon_dys_Cg_coupling = [14e3] * 5 self.xmons: list[XmonCross] = [] self.cross_len_x = 180e3 self.cross_width_x = 60e3 self.cross_gnd_gap_x = 20e3 self.cross_len_y = 155e3 self.cross_width_y = 60e3 self.cross_gnd_gap_y = 20e3 # squids self.squids: List[AsymSquid] = [] self.test_squids: List[AsymSquid] = [] self.el_dc_contacts: List[List[ElementBase, ElementBase]] = [] # md and flux lines attributes self.shift_fl_y = self.cross_len_y + 60e3 self.shift_md_x = 60e3 self.shift_md_y = 510e3 self.cpwrl_md1: CPW_RL_Path = None self.cpwrl_md1_end: MDriveLineEnd = None self.cpwrl_fl1: CPW_RL_Path = None self.cpwrl_fl1_end: FluxLineEnd = None self.cpwrl_md2: CPW_RL_Path = None self.cpwrl_md2_end: MDriveLineEnd = None self.cpwrl_fl2: CPW_RL_Path = None self.cpwrl_fl2_end: FluxLineEnd = None self.cpwrl_md3: CPW_RL_Path = None self.cpwrl_md3_end: MDriveLineEnd = None self.cpwrl_fl3: CPW_RL_Path = None self.cpwrl_fl3_end: FluxLineEnd = None self.cpwrl_md4: CPW_RL_Path = None self.cpwrl_md4_end: MDriveLineEnd = None self.cpwrl_fl4: CPW_RL_Path = None self.cpwrl_fl4_end: FluxLineEnd = None self.cpwrl_md5: CPW_RL_Path = None self.cpwrl_md5_end: MDriveLineEnd = None self.cpwrl_fl5: CPW_RL_Path = None self.cpwrl_fl5_end: FluxLineEnd = None # marks self.marks: List[MarkBolgar] = [] ### ADDITIONAL VARIABLES SECTION END ### def draw(self, design_params=None): self.draw_chip() ''' Only creating object. This is due to the drawing of xmons and resonators require draw xmons, then draw resonators and then draw additional xmons. This is ugly and that how this was before migrating to `ChipDesign` based code structure This is also the reason why `self.__init__` is flooded with design parameters that are used across multiple drawing functions. TODO: This drawings sequence can be decoupled in the future. ''' self.create_resonator_objects() self.draw_readout_waveguide() self.draw_xmons_and_resonators() self.draw_md_and_flux_lines() # self.draw_test_structures() # self.draw_josephson_loops() # self.draw_el_dc_contacts() # self.draw_el_protection() # self.draw_photo_el_marks() # self.draw_bridges() # self.draw_pinning_holes() # self.inverse_destination(self.region_ph) # self.split_polygons_in_layers(max_pts=180) def _transfer_regs2cell(self): # this too methods assumes that all previous drawing # functions are placing their object on regions # in order to avoid extensive copying of the polygons # to/from cell.shapes during the logic operations on # polygons self.cell.shapes(self.layer_ph).insert(self.region_ph) self.cell.shapes(self.layer_el).insert(self.region_el) self.cell.shapes(self.layer_el2).insert(self.region_el2) self.cell.shapes(self.layer_bridges1).insert(self.region_bridges1) self.cell.shapes(self.layer_bridges2).insert(self.region_bridges2) self.cell.shapes(self.layer_el_protection).insert(self.region_el_protection) self.lv.zoom_fit() def draw_chip(self): # self.region_bridges2.insert(self.chip_box) self.region_ph.insert(self.chip_box) for contact_pad in self.contact_pads: contact_pad.place(self.region_ph) def create_resonator_objects(self): # fork at the end of resonator parameters fork_x_span = self.cross_width_y + 2 * (self.xmon_fork_gnd_gap + self.fork_metal_width) ### RESONATORS TAILS CALCULATIONS SECTION START ### # key to the calculations can be found in hand-written format here: # https://drive.google.com/file/d/1wFmv5YmHAMTqYyeGfiqz79a9kL1MtZHu/view?usp=sharing # x span between left long vertical line and # right-most center of central conductors resonators_widths = [2 * self.r + L_coupling for L_coupling in self.L_coupling_list] x1 = 2 * self.resonators_dx + resonators_widths[2] / 2 - 2 * self.xmon_x_distance x2 = x1 + self.xmon_x_distance - self.resonators_dx x3 = resonators_widths[2] / 2 x4 = 3 * self.resonators_dx - (x1 + 3 * self.xmon_x_distance) x5 = 4 * self.resonators_dx - (x1 + 4 * self.xmon_x_distance) res_tail_shape = "LRLRL" tail_turn_radiuses = self.r # list corrected for resonator-qubit coupling geomtry, so all transmons centers are placed # along single horizontal line self.L0_list = [self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling] self.L2_list[0] += 6 * self.Z_res.b self.L2_list[1] += 0 self.L2_list[3] += 3 * self.Z_res.b self.L2_list[4] += 6 * self.Z_res.b self.L3_list[0] = x1 self.L3_list[1] = x2 self.L3_list[2] = x3 self.L3_list[3] = x4 self.L3_list[4] = x5 self.L4_list[1] += 6 * self.Z_res.b self.L4_list[2] += 6 * self.Z_res.b self.L4_list[3] += 3 * self.Z_res.b tail_segment_lengths_list = [[L2, L3, L4 + FABRICATION.OVERETCHING] for L2, L3, L4 in zip(self.L2_list, self.L3_list, self.L4_list)] tail_turn_angles_list = [ [pi / 2, -pi / 2], [pi / 2, -pi / 2], [pi / 2, -pi / 2], [-pi / 2, pi / 2], [-pi / 2, pi / 2], ] tail_trans_in_list = [ Trans.R270, Trans.R270, Trans.R270, Trans.R270, Trans.R270 ] ### RESONATORS TAILS CALCULATIONS SECTION END ### pars = list( zip( self.L1_list, self.to_line_list, self.L_coupling_list, self.fork_y_spans, tail_segment_lengths_list, tail_turn_angles_list, tail_trans_in_list, self.L0_list, self.N_coils ) ) for res_idx, params in enumerate(pars): # parameters exctraction L1 = params[0] to_line = params[1] L_coupling = params[2] fork_y_span = params[3] tail_segment_lengths = params[4] tail_turn_angles = params[5] tail_trans_in = params[6] L0 = params[7] n_coils = params[8] # deduction for resonator placements # under condition that Xmon-Xmon distance equals # `xmon_x_distance` worm_x = self.contact_pads[-1].end.x + (res_idx + 1 / 2) * self.resonators_dx worm_y = self.contact_pads[-1].end.y - self.ro_line_dy - to_line resonator_cpw = CPWParameters(self.Z_res.width + 2 * FABRICATION.OVERETCHING, self.Z_res.gap - 2 * FABRICATION.OVERETCHING) self.resonators.append( EMResonatorTL3QbitWormRLTailXmonFork( resonator_cpw, DPoint(worm_x, worm_y), L_coupling, L0, L1, self.r, n_coils, tail_shape=res_tail_shape, tail_turn_radiuses=tail_turn_radiuses, tail_segment_lengths=tail_segment_lengths, tail_turn_angles=tail_turn_angles, tail_trans_in=tail_trans_in, fork_x_span=fork_x_span + 2 * FABRICATION.OVERETCHING, fork_y_span=fork_y_span, fork_metal_width=self.fork_metal_width + 2 * FABRICATION.OVERETCHING, fork_gnd_gap=self.fork_gnd_gap - 2 * FABRICATION.OVERETCHING ) ) # print([self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling]) # print(self.L1_list) # print(self.L2_list) # print(self.L3_list) # print(self.L4_list) def draw_readout_waveguide(self): ''' Subdividing horizontal waveguide adjacent to resonators into several waveguides. Even segments of this adjacent waveguide are adjacent to resonators. Bridges will be placed on odd segments later. Returns ------- None ''' # place readout waveguide ro_line_turn_radius = self.ro_line_turn_radius ro_line_dy = self.ro_line_dy ## calculating segment lengths of subdivided coupling part of ro coplanar ## # value that need to be added to `L_coupling` to get width of resonators bbox. def get_res_extension(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return resonator.Z0.b + 2 * resonator.r def get_res_width(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return (resonator.L_coupling + get_res_extension(resonator)) res_line_segments_lengths = [ self.resonators[0].origin.x - self.contact_pads[-1].end.x - get_res_extension(self.resonators[0]) / 2 ] # length from bend to first bbox of first resonator for i, resonator in enumerate(self.resonators[:-1]): resonator_extension = get_res_extension(resonator) resonator_width = get_res_width(resonator) next_resonator_extension = get_res_extension(self.resonators[i + 1]) # order of adding is from left to right (imagine chip geometry in your head to follow) res_line_segments_lengths.extend( [ resonator_width, # `resonator_extension` accounts for the next resonator extension # in this case all resonator's extensions are equal self.resonators_dx - (resonator_width - resonator_extension / 2) - next_resonator_extension / 2 ] ) res_line_segments_lengths.extend( [ get_res_width(self.resonators[-1]), self.resonators_dx / 2 ] ) # first and last segment will have length `self.resonator_dx/2` res_line_total_length = sum(res_line_segments_lengths) segment_lengths = [ro_line_dy] + res_line_segments_lengths + \ [ro_line_dy / 2, res_line_total_length - self.chip.pcb_feedline_d, ro_line_dy / 2] self.cpwrl_ro_line = CPW_RL_Path( self.contact_pads[-1].end, shape="LR" + ''.join(['L'] * len(res_line_segments_lengths)) + "RLRLRL", cpw_parameters=CPWParameters(self.Z0.width + 2 * FABRICATION.OVERETCHING, self.Z0.gap - 2 * FABRICATION.OVERETCHING), turn_radiuses=[ro_line_turn_radius] * 4, segment_lengths=segment_lengths, turn_angles=[pi / 2, pi / 2, pi / 2, -pi / 2], trans_in=Trans.R270 ) self.cpwrl_ro_line.place(self.region_ph) def draw_xmons_and_resonators(self): for resonator, fork_y_span, xmon_dy_Cg_coupling in \ list(zip( self.resonators, self.fork_y_spans, self.xmon_dys_Cg_coupling )): xmon_center = (resonator.fork_x_cpw.start + resonator.fork_x_cpw.end) / 2 + \ DVector(0, -xmon_dy_Cg_coupling - resonator.fork_metal_width / 2) # changes start # xmon_center += DPoint( 0, -(self.cross_len_y + self.cross_width_x / 2 + min(self.cross_gnd_gap_y, self.xmon_fork_gnd_gap)) + FABRICATION.OVERETCHING ) self.xmons.append( XmonCross(xmon_center, self.cross_len_x, self.cross_width_x + 2 * FABRICATION.OVERETCHING, self.cross_gnd_gap_x - 2 * FABRICATION.OVERETCHING, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y + 2 * FABRICATION.OVERETCHING, sideY_gnd_gap=self.cross_gnd_gap_y - 2 * FABRICATION.OVERETCHING) ) self.xmons[-1].place(self.region_ph) resonator.place(self.region_ph) xmonCross_corrected = XmonCross( xmon_center, sideX_length=self.cross_len_x, sideX_width=self.cross_width_x + 2 * FABRICATION.OVERETCHING, sideX_gnd_gap=self.cross_gnd_gap_x - 2 * FABRICATION.OVERETCHING, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y + 2 * FABRICATION.OVERETCHING, sideY_gnd_gap=min(self.cross_gnd_gap_y, self.xmon_fork_gnd_gap) - 2 * FABRICATION.OVERETCHING) xmonCross_corrected.place(self.region_ph) def draw_md_and_flux_lines(self): """ Drawing of md (microwave drive) and flux tuning lines for 5 qubits Returns ------- """ contact_pads = self.contact_pads ctr_line_turn_radius = 100e3 xmon_center = self.xmons[-1].center xmon_x_distance = self.xmon_x_distance cross_width_y = self.cross_width_y cross_width_x = self.cross_width_x cross_len_x = self.cross_len_x cross_len_y = self.cross_len_y cross_gnd_gap_y = self.cross_gnd_gap_y cross_gnd_gap_x = self.cross_gnd_gap_x width_res = self.Z_res.width tmp_reg = self.region_ph z_md_fl_corrected = CPWParameters( self.z_md_fl.width + 2 * FABRICATION.OVERETCHING, self.z_md_fl.gap - 2 * FABRICATION.OVERETCHING ) shift_fl_y = self.shift_fl_y shift_md_x = self.shift_md_x shift_md_y = self.shift_md_y md0_md5_gnd = 5e3 flux_end_width = self.cross_width_x + 2 * self.cross_gnd_gap_x - 2 * FABRICATION.OVERETCHING md_transition = 25e3 md_z1_params = z_md_fl_corrected md_z1_length = 245e3 shift_md_x_side = md_z1_length + md_transition + md_z1_params.b / 2 + cross_len_x + cross_width_x / 2 + cross_gnd_gap_x # place caplanar line 1md self.cpwrl_md1 = CPW_RL_Path( contact_pads[0].end, shape="LRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[ 2 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16, ( (contact_pads[0].end.y - xmon_center.y) ** 2 + (9 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16) ** 2 ) ** 0.5, 5 * (-contact_pads[ 0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16 - md0_md5_gnd], turn_angles=[-atan2(contact_pads[0].end.y - xmon_center.y, 9 * (-contact_pads[ 0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16), atan2(contact_pads[0].end.y - xmon_center.y, 9 * (-contact_pads[ 0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16)], trans_in=Trans.R0 ) self.cpwrl_md1.place(tmp_reg) self.cpwrl_md1_end = MDriveLineEnd(list(self.cpwrl_md1.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md1_end.place(tmp_reg) # place caplanar line 1 fl self.cpwrl_fl1 = CPW_RL_Path( contact_pads[1].end, shape="LRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius], segment_lengths=[ -contact_pads[1].end.x + xmon_center.x - 4 * xmon_x_distance - cross_len_x, xmon_center.y - contact_pads[1].end.y - cross_width_x / 2 - cross_gnd_gap_x - z_md_fl_corrected.width + FABRICATION.OVERETCHING ], turn_angles=[pi / 2], trans_in=Trans.R0 ) self.cpwrl_fl1.place(tmp_reg) self.cpwrl_fl1_end = FluxLineEnd(self.cpwrl_fl1.end, z_md_fl_corrected, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl1_end.place(tmp_reg) # place caplanar line 2md self.cpwrl_md2 = CPW_RL_Path( contact_pads[3].end, shape="LRLRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 3, segment_lengths=[ (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 4, (-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2, ( ( (-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2 ) ** 2 + ( 5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8 ) ** 2 ) ** 0.5, (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8 ], turn_angles=[-pi / 2, atan2(5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8, ( -contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2), pi / 2 - atan2(5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8, ( -contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2)], trans_in=Trans.R90 ) self.cpwrl_md2.place(tmp_reg) self.cpwrl_md2_end = MDriveLineEnd( list(self.cpwrl_md2.primitives.values())[-1], md_z1_params, md_transition, md_z1_length ) self.cpwrl_md2_end.place(tmp_reg) # place caplanar line 2 fl self.cpwrl_fl2 = CPW_RL_Path( contact_pads[2].end, shape="LRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[(-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4, ( (3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4) ** 2 + ( 7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8) ** 2) ** 0.5, (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8], turn_angles=[atan2(7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4), pi / 2 - atan2(7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4)], trans_in=Trans.R0 ) self.cpwrl_fl2.place(tmp_reg) self.cpwrl_fl2_end = FluxLineEnd(self.cpwrl_fl2.end, z_md_fl_corrected, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl2_end.place(tmp_reg) # place caplanar line 3md self.cpwrl_md3_1 = CPW_RL_Path( contact_pads[5].end, shape="LRLRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 3, segment_lengths=[ (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 4, (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2, ( ( (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2 ) ** 2 + ( 5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8 ) ** 2 ) ** 0.5 - 400e3, (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8], turn_angles=[pi / 2, -atan2(5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8, (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2), -pi / 2 + atan2(5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8, ( contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2)], trans_in=Trans.R90 ) self.cpwrl_md3_1.place(tmp_reg) dy = self.cpwrl_md2.end.y - self.cpwrl_md3_1.end.y dx = self.cpwrl_md3_1.end.x - (self.xmons[2].center.x + shift_md_x) md3_2_radius = ctr_line_turn_radius - 50e3 self.cpwrl_md3_2 = CPW_RL_Path( self.cpwrl_md3_1.end, shape="LRLR", cpw_parameters=z_md_fl_corrected, turn_radiuses=[md3_2_radius] * 2, segment_lengths=[dy - md3_2_radius, dx], turn_angles=[pi / 2, -pi / 2], trans_in=Trans.R90 ) self.cpwrl_md3_2.place(tmp_reg) self.cpwrl_md3_end = MDriveLineEnd( list(self.cpwrl_md3_2.primitives.values())[-1], md_z1_params, md_transition, md_z1_length ) self.cpwrl_md3_end.place(tmp_reg) # place caplanar line 3 fl fl_l1 = (self.xmons[2].cpw_bempt.end.y - contact_pads[4].end.y) / 4 fl_l3 = fl_l1 dr = self.xmons[2].cpw_bempt.end - contact_pads[4].end dr.y = dr.y - fl_l1 - fl_l3 - z_md_fl_corrected.width turn_angle = atan2(-dr.x, dr.y) fl_l2 = dr.abs() self.cpwrl_fl3 = CPW_RL_Path( self.contact_pads[4].end, shape="LRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[fl_l1, fl_l2, fl_l3], turn_angles=[turn_angle, -turn_angle], trans_in=Trans.R90 ) self.cpwrl_fl3.place(tmp_reg) self.cpwrl_fl3_end = FluxLineEnd(self.cpwrl_fl3.end, z_md_fl_corrected, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl3_end.place(tmp_reg) # place caplanar line 4 md md4_len_y = -contact_pads[7].end.y + xmon_center.y - shift_md_y md4_fl4_dx = 100e3 self.cpwrl_md4 = CPW_RL_Path( contact_pads[7].end, shape="LRLRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=ctr_line_turn_radius / 2, segment_lengths=[ contact_pads[7].end.x - xmon_center.x + xmon_x_distance - shift_md_x - md4_fl4_dx, md4_len_y - ctr_line_turn_radius / 2, md4_fl4_dx, ctr_line_turn_radius / 2 ], turn_angles=[-pi / 2, pi / 2, -pi / 2], trans_in=Trans.R180 ) self.cpwrl_md4.place(tmp_reg) self.cpwrl_md4_end = MDriveLineEnd( list(self.cpwrl_md4.primitives.values())[-1], md_z1_params, md_transition, md_z1_length ) self.cpwrl_md4_end.place(tmp_reg) # place caplanar line 4 fl self.cpwrl_fl4 = CPW_RL_Path( contact_pads[6].end, shape="LRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[(contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4, ((6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8) ** 2 + (3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4) ** 2) ** 0.5, 2 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8], turn_angles=[-atan2(6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4), -pi / 2 + atan2(6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4)], trans_in=Trans.R180 ) self.cpwrl_fl4.place(tmp_reg) self.cpwrl_fl4_end = FluxLineEnd(self.cpwrl_fl4.end, z_md_fl_corrected, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl4_end.place(tmp_reg) # place caplanar line 5 md md5_l1 = 2 * (contact_pads[9].end.y - xmon_center.y) / 3 md5_dy = 400e3 md5_dx = 400e3 md5_angle = atan2( 2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3, (contact_pads[9].end.y - xmon_center.y) / 3 ) self.cpwrl_md5 = CPW_RL_Path( contact_pads[9].end, shape="LRLRLRLRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 4, segment_lengths=[ md5_dy, md5_dx / sin(pi / 4), md5_l1 - md5_dy - md5_dx / tan(pi / 4) - md5_dx / tan(md5_angle), md5_dx / sin(md5_angle) + ( ( (contact_pads[9].end.y - xmon_center.y) / 3 ) ** 2 + ( 2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3 ) ** 2 ) ** (0.5), (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3 - md0_md5_gnd ], turn_angles=[ pi / 4, -pi / 4, -md5_angle, -(pi / 2 - md5_angle) ], trans_in=Trans.R270 ) # self.cpwrl_md5 = CPW_RL_Path( # contact_pads[9].end, shape="LRLRL", cpw_parameters=z_md_fl_corrected, # turn_radiuses=[ctr_line_turn_radius] * 2, # segment_lengths=[ # md5_l1, # ( # ( # (contact_pads[9].end.y - xmon_center.y) / 3 # ) ** 2 + # ( # 2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3 # ) ** 2 # ) ** (0.5), # (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3 - md0_md5_gnd # ], # turn_angles=[ # md5_angle, # -md5_angle - pi / 2 # ], # trans_in=Trans.R270 # ) self.cpwrl_md5.place(tmp_reg) self.cpwrl_md5_end = MDriveLineEnd( list(self.cpwrl_md5.primitives.values())[-1], md_z1_params, md_transition, md_z1_length ) self.cpwrl_md5_end.place(tmp_reg) # place caplanar line 5 fl self.cpwrl_fl5_1 = CPW_RL_Path( contact_pads[8].end, shape="LRLRLR", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius] * 3, segment_lengths=[ (contact_pads[8].end.x - xmon_center.x) / 4, ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3 + 50e3, ( (2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3) ** 2 + ((contact_pads[8].end.x - xmon_center.x) / 2) ** 2 ) ** 0.5 ], turn_angles=[ pi / 2, -atan2((contact_pads[8].end.x - xmon_center.x) / 2, 2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3), - pi / 2 + atan2((contact_pads[8].end.x - xmon_center.x) / 2, 2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3) ], trans_in=Trans.R180 ) self.cpwrl_fl5_1.place(tmp_reg) self.cpwrl_fl5_2 = CPW_RL_Path( self.cpwrl_fl5_1.end, shape="LRL", cpw_parameters=z_md_fl_corrected, turn_radiuses=[ctr_line_turn_radius], segment_lengths=[ self.cpwrl_fl5_1.end.x - (self.xmons[4].center.x + cross_width_y / 2 + cross_len_x + cross_gnd_gap_x - flux_end_width / 2), self.xmons[4].center.y - self.cpwrl_fl5_1.end.y - cross_width_x / 2 - cross_gnd_gap_x - z_md_fl_corrected.width + FABRICATION.OVERETCHING ], turn_angles=[-pi / 2], trans_in=Trans.R180 ) self.cpwrl_fl5_2.place(tmp_reg) self.cpwrl_fl5_end = FluxLineEnd(self.cpwrl_fl5_2.end, z_md_fl_corrected, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl5_end.place(tmp_reg) def draw_josephson_loops(self): new_pars_squid = AsymSquidParams( pad_r=5e3, pads_distance=30e3, p_ext_width=10e3, p_ext_r=200, sq_len=15e3, sq_area=200e6, j_width_1=95, j_width_2=348, intermediate_width=500, b_ext=1e3, j_length=94, n=20, bridge=180, j_length_2=250 ) # place left squid xmon0 = self.xmons[0] center1 = DPoint( self.cpwrl_fl1_end.end.x, xmon0.center.y - (xmon0.sideX_width + xmon0.sideX_gnd_gap) / 2 ) squid = AsymSquid(center1, new_pars_squid, 0) self.squids.append(squid) squid.place(self.region_el) # place intermediate squids for xmon_cross in self.xmons[1:-1]: squid_center = (xmon_cross.cpw_bempt.start + xmon_cross.cpw_bempt.end) / 2 squid = AsymSquid(squid_center, new_pars_squid, 0) self.squids.append(squid) squid.place(self.region_el) # place right squid xmon5 = self.xmons[4] center5 = DPoint( self.cpwrl_fl5_end.end.x, xmon5.center.y - (xmon5.sideX_width + xmon5.sideX_gnd_gap) / 2 ) squid = AsymSquid(center5, new_pars_squid, 0) self.squids.append(squid) squid.place(self.region_el) def draw_el_dc_contacts(self, squids: AsymSquid = None): # if argument is omitted, use all squids stored in `self.squids` if squids is None: squids = self.squids + self.test_squids for squid in squids: r_pad = squid.params.pad_r center_up = squid.pad_up.center + DPoint(0, r_pad) center_down = squid.pad_down.center + DPoint(0, -r_pad) big_circle_r = 1.5 * r_pad self.el_dc_contacts.append( [Circle(center_up, big_circle_r), Circle(center_down, big_circle_r)] ) for contact in self.el_dc_contacts[-1]: contact.place(self.region_el2) # DC contacts has to have intersection with empty layer in photo litography # to ensure that first e-beam layer does not broke at the step between # substrate and photolytography polygons. # Following rectangle pads are cutted from photo region to ensure # DC contacts are covering aforementioned level step. squid_pad_r = squid.params.pad_r squid_pads_d = squid.params.pads_distance - 2 * squid_pad_r rec_width = 1.6 * squid_pad_r - 2 * FABRICATION.OVERETCHING rec_height = 1.6 * squid_pad_r - FABRICATION.OVERETCHING # Rectangle for top DC contact pad p1 = squid.origin + DVector(-rec_width / 2, squid_pads_d / 2) + \ DVector(0, -FABRICATION.OVERETCHING) rec_top = Rectangle(p1, rec_width, rec_height, inverse=True) rec_top.place(self.region_ph) # Rectangle for bottom DC contact pad p2 = squid.origin + DVector(-rec_width / 2, -squid_pads_d / 2) + \ DVector(0, FABRICATION.OVERETCHING) rec_bot = Rectangle(p2, rec_width, -rec_height, inverse=True) rec_bot.place(self.region_ph) def draw_test_structures(self): new_pars_squid = AsymSquidParams( pad_r=5e3, pads_distance=30e3, p_ext_width=10e3, p_ext_r=200, sq_len=15e3, sq_area=200e6, j_width_1=94, j_width_2=347, intermediate_width=500, b_ext=1e3, j_length=94, n=20, bridge=180, j_length_2=250 ) struct_centers = [DPoint(1e6, 4e6), DPoint(8.7e6, 5.7e6), DPoint(6.5e6, 2.7e6)] for struct_center in struct_centers: ## JJ test structures ## # test structure with big critical current test_struct1 = TestStructurePads(struct_center) test_struct1.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text("48.32 nA", 0.001, 50, False, 0, 0) text_bl = test_struct1.empty_rectangle.origin + DPoint( test_struct1.gnd_gap, -4 * test_struct1.gnd_gap ) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg test_jj = AsymSquid(test_struct1.center, new_pars_squid, side=1) self.test_squids.append(test_jj) test_jj.place(self.region_el) # test structure with low critical current test_struct2 = TestStructurePads(struct_center + DPoint(0.3e6, 0)) test_struct2.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text("9.66 nA", 0.001, 50, False, 0, 0) text_bl = test_struct2.empty_rectangle.origin + DPoint( test_struct2.gnd_gap, -4 * test_struct2.gnd_gap ) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg test_jj = AsymSquid(test_struct2.center, new_pars_squid, side=-1) self.test_squids.append(test_jj) test_jj.place(self.region_el) # test structure for bridge DC contact test_struct3 = TestStructurePads(struct_center + DPoint(0.6e6, 0)) test_struct3.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text("DC", 0.001, 50, False, 0, 0) text_bl = test_struct3.empty_rectangle.origin + DPoint( test_struct3.gnd_gap, -4 * test_struct3.gnd_gap ) text_reg.transform(ICplxTrans(1.0, 0, False, test_struct3.center.x, text_bl.y)) self.region_ph -= text_reg test_bridges = [] for i in range(3): bridge = Bridge1(test_struct3.center + DPoint(50e3 * (i - 1), 0), gnd_touch_dx=20e3) test_bridges.append(bridge) bridge.place(self.region_bridges1, region_name="bridges_1") bridge.place(self.region_bridges2, region_name="bridges_2") # bandages test structures test_dc_el2_centers = [ DPoint(2.5e6, 2.4e6), DPoint(4.2e6, 1.6e6), DPoint(9.0e6, 3.8e6) ] for struct_center in test_dc_el2_centers: test_struct1 = TestStructurePads(struct_center) test_struct1.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text("Bandage", 0.001, 40, False, 0, 0) text_bl = test_struct1.empty_rectangle.origin + DPoint( test_struct1.gnd_gap, -4 * test_struct1.gnd_gap ) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg rec_width = 10e3 rec_height = test_struct1.rectangles_gap + 2 * FABRICATION.OVERETCHING + 2 * rec_width p1 = struct_center - DVector(rec_width / 2, rec_height / 2) dc_rec = Rectangle(p1, rec_width, rec_height) dc_rec.place(self.region_el2) def draw_el_protection(self): protection_a = 300e3 for squid in (self.squids + self.test_squids): self.region_el_protection.insert( pya.Box().from_dbox( pya.DBox( squid.origin - 0.5 * DVector(protection_a, protection_a), squid.origin + 0.5 * DVector(protection_a, protection_a) ) ) ) def draw_photo_el_marks(self): marks_centers = [ DPoint(1e6, 9e6), DPoint(1e6, 1e6), DPoint(9e6, 1e6), DPoint(9e6, 9e6), DPoint(8e6, 4e6), DPoint(1e6, 6e6) ] for mark_center in marks_centers: self.marks.append( MarkBolgar(mark_center, overetching=FABRICATION.OVERETCHING) ) self.marks[-1].place(self.region_ph) def draw_bridges(self): bridges_step = 150e3 # for resonators for resonator in self.resonators: for name, res_primitive in resonator.primitives.items(): if "coil0" in name: # skip L_coupling coplanar. # bridgyfy all in "coil0" except for the first cpw that # is adjacent to readout line and has length equal to `L_coupling` for primitive in list(res_primitive.primitives.values())[1:]: Bridge1.bridgify_CPW( primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2 ) continue elif "fork" in name: # skip fork primitives continue else: # bridgify everything else Bridge1.bridgify_CPW( res_primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2 ) # for contact wires for key, val in self.__dict__.items(): if "cpwrl_md" in key: Bridge1.bridgify_CPW( val, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2 ) elif "cpwrl_fl" in key: Bridge1.bridgify_CPW( val, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2, avoid_points=[squid.origin for squid in self.squids], avoid_distance=500e3 ) # for readout waveguide bridgified_primitives_idxs = list(range(2)) bridgified_primitives_idxs += list(range(2, 2 * (len(self.resonators) + 1) + 1, 2)) bridgified_primitives_idxs += list(range( 2 * (len(self.resonators) + 1) + 1, len(self.cpwrl_ro_line.primitives.values())) ) for idx, primitive in enumerate(self.cpwrl_ro_line.primitives.values()): if idx in bridgified_primitives_idxs: Bridge1.bridgify_CPW( primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2 ) def draw_pinning_holes(self): selection_region = Region( pya.Box(Point(100e3, 100e3), Point(101e3, 101e3)) ) tmp_ph = self.region_ph.dup() other_regs = tmp_ph.select_not_interacting(selection_region) reg_to_fill = self.region_ph.select_interacting(selection_region) filled_reg = fill_holes(reg_to_fill) self.region_ph = filled_reg + other_regs def split_polygons_in_layers(self, max_pts=200): self.region_ph = split_polygons(self.region_ph, max_pts) self.region_bridges2 = split_polygons(self.region_bridges2, max_pts) for poly in self.region_ph: if poly.num_points() > max_pts: print("exists photo") for poly in self.region_ph: if poly.num_points() > max_pts: print("exists bridge2")
class Design5Q(ChipDesign): def __init__(self, cell_name): super().__init__(cell_name) info_el2 = pya.LayerInfo(3, 0) # for DC contact deposition self.region_el2 = Region() self.layer_el2 = self.layout.layer(info_el2) info_bridges1 = pya.LayerInfo(4, 0) # bridge photo layer 1 self.region_bridges1 = Region() self.layer_bridges1 = self.layout.layer(info_bridges1) info_bridges2 = pya.LayerInfo(5, 0) # bridge photo layer 2 self.region_bridges2 = Region() self.layer_bridges2 = self.layout.layer(info_bridges2) # layer with polygons that will protect structures located # on the `self.region_el` - e-beam litography layer info_el_protection = pya.LayerInfo(6, 0) self.region_el_protection = Region() self.layer_el_protection = self.layout.layer(info_el_protection) # has to call it once more to add new layers self.lv.add_missing_layers() ### ADDITIONAL VARIABLES SECTION START ### # chip rectangle and contact pads self.chip = CHIP_10x10_12pads self.chip_box: pya.DBox = self.chip.box # Z = 50.09 E_eff = 6.235 (E = 11.45) self.z_md_fl: CPWParameters = CPWParameters(11e3, 5.7e3) self.ro_Z: CPWParameters = self.chip.chip_Z self.contact_pads: list[ContactPad] = self.chip.get_contact_pads( [self.z_md_fl] * 10 + [self.ro_Z] * 2) # readout line parameters self.ro_line_turn_radius: float = 200e3 self.ro_line_dy: float = 1600e3 self.cpwrl_ro_line: CPWRLPath = None self.Z0: CPWParameters = CHIP_10x10_12pads.chip_Z # resonators objects list self.resonators: List[EMResonatorTL3QbitWormRLTailXmonFork] = [] # distance between nearest resonators central conductors centers # constant step between resonators origin points along x-axis. self.resonators_dx: float = 900e3 # resonator parameters self.L_coupling_list: list[float] = [ 1e3 * x for x in [310, 320, 320, 310, 300] ] # corresponding to resonanse freq is linspaced in interval [6,9) GHz self.L0 = 1150e3 self.L1_list = [ 1e3 * x for x in [50.7218, 96.3339, 138.001, 142.77, 84.9156] ] self.r = 60e3 self.N_coils = [3] * len(self.L1_list) self.L2_list = [self.r] * len(self.L1_list) self.L3_list = [0e3] * len(self.L1_list) # to be constructed self.L4_list = [self.r] * len(self.L1_list) self.width_res = 20e3 self.gap_res = 10e3 self.Z_res = CPWParameters(self.width_res, self.gap_res) self.to_line_list = [56e3] * len(self.L1_list) self.fork_metal_width = 10e3 self.fork_gnd_gap = 15e3 self.xmon_fork_gnd_gap = 14e3 # resonator-fork parameters # for coarse C_qr evaluation self.fork_y_spans = [ x * 1e3 for x in [8.73781, 78.3046, 26.2982, 84.8277, 35.3751] ] # xmon parameters self.xmon_x_distance: float = 545e3 # from simulation of g_12 # for fine C_qr evaluation self.xmon_dys_Cg_coupling = [14e3] * 5 self.xmons: list[XmonCross] = [] self.cross_len_x = 180e3 self.cross_width_x = 60e3 self.cross_gnd_gap_x = 20e3 self.cross_len_y = 155e3 self.cross_width_y = 60e3 self.cross_gnd_gap_y = 20e3 # fork at the end of resonator parameters self.fork_x_span = self.cross_width_y + 2 * (self.xmon_fork_gnd_gap + self.fork_metal_width) # squids self.squids: List[AsymSquidDCFlux] = [] self.test_squids: List[AsymSquidDCFlux] = [] # el-dc concacts attributes # required extension of el_dc contacts into the el polygon self.dc_cont_el_ext = 0.0e3 # 1.8e3 # required extension into the photo polygon self.dc_cont_ph_ext = 1.4e3 # minimum distance from el_dc contact polygon perimeter points to # dc + el polygons perimeter. (el-dc contact polygon lies within # el + dc polygons) self.dc_cont_clearance = 1e3 # [nm] = [m*1e-9] # el-photo layer cut box width self.dc_cont_cut_width = \ SQUID_PARAMETERS.flux_line_contact_width - 2e3 # cut box clearance from initial el ^ ph regions self.dc_cont_cut_clearance = 1e3 self.el_dc_contacts: List[List[ElementBase, ...]] = [] # microwave and flux drive lines parameters self.ctr_lines_turn_radius = 100e3 self.cont_lines_y_ref: float = None # nm self.md234_cross_bottom_dy = 55e3 self.md234_cross_bottom_dx = 60e3 self.cpwrl_md1: DPathCPW = None self.cpwrl_fl1: DPathCPW = None self.cpwrl_md2: DPathCPW = None self.cpwrl_fl2: DPathCPW = None self.cpwrl_md3: DPathCPW = None self.cpwrl_fl3: DPathCPW = None self.cpwrl_md4: DPathCPW = None self.cpwrl_fl4: DPathCPW = None self.cpwrl_md5: DPathCPW = None self.cpwrl_fl5: DPathCPW = None self.cpw_fl_lines: List[DPathCPW] = [] self.cpw_md_lines: List[DPathCPW] = [] # marks self.marks: List[MarkBolgar] = [] ### ADDITIONAL VARIABLES SECTION END ### def draw(self, design_params=None, q_idx=0): self.draw_chip() ''' Only creating object. This is due to the drawing of xmons and resonators require draw xmons, then draw resonators and then draw additional xmons. This is ugly and that how this was before migrating to `ChipDesign` based code structure This is also the reason why `self.__init__` is flooded with design parameters that are used across multiple drawing functions. TODO: This drawings sequence can be decoupled in the future. ''' self.create_resonator_objects(q_idx=q_idx) # self.draw_readout_waveguide() self.draw_xmons_and_resonators(q_idx=q_idx) # self.draw_josephson_loops() # # self.draw_microwave_drvie_lines() # self.draw_flux_control_lines() # # self.draw_test_structures() # self.draw_el_dc_contacts() # self.draw_el_protection() # # self.draw_photo_el_marks() # self.draw_bridges() # self.draw_pinning_holes() # self.extend_photo_overetching() # self.inverse_destination(self.region_ph) # self.split_polygons_in_layers(max_pts=180) def _transfer_regs2cell(self): # this too methods assumes that all previous drawing # functions are placing their object on regions # in order to avoid extensive copying of the polygons # to/from cell.shapes during the logic operations on # polygons self.cell.shapes(self.layer_ph).insert(self.region_ph) self.cell.shapes(self.layer_el).insert(self.region_el) self.cell.shapes(self.layer_el2).insert(self.region_el2) self.cell.shapes(self.layer_bridges1).insert(self.region_bridges1) self.cell.shapes(self.layer_bridges2).insert(self.region_bridges2) self.cell.shapes(self.layer_el_protection).insert( self.region_el_protection) self.lv.zoom_fit() def draw_chip(self): self.region_bridges2.insert(self.chip_box) self.region_ph.insert(self.chip_box) for contact_pad in self.contact_pads: contact_pad.place(self.region_ph) def create_resonator_objects(self, q_idx): ### RESONATORS TAILS CALCULATIONS SECTION START ### # key to the calculations can be found in hand-written format here: # https://drive.google.com/file/d/1wFmv5YmHAMTqYyeGfiqz79a9kL1MtZHu/view?usp=sharing # x span between left long vertical line and # right-most center of central conductors resonators_widths = [ 2 * self.r + L_coupling for L_coupling in self.L_coupling_list ] x1 = 2 * self.resonators_dx + resonators_widths[ 2] / 2 - 2 * self.xmon_x_distance x2 = x1 + self.xmon_x_distance - self.resonators_dx x3 = resonators_widths[2] / 2 x4 = 3 * self.resonators_dx - (x1 + 3 * self.xmon_x_distance) x5 = 4 * self.resonators_dx - (x1 + 4 * self.xmon_x_distance) res_tail_shape = "LRLRL" tail_turn_radiuses = self.r # list corrected for resonator-qubit coupling geomtry, so all transmons centers are placed # along single horizontal line self.L0_list = [ self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling ] self.L2_list[0] += 6 * self.Z_res.b self.L2_list[1] += 0 self.L2_list[3] += 3 * self.Z_res.b self.L2_list[4] += 6 * self.Z_res.b self.L3_list[0] = x1 self.L3_list[1] = x2 self.L3_list[2] = x3 self.L3_list[3] = x4 self.L3_list[4] = x5 self.L4_list[1] += 6 * self.Z_res.b self.L4_list[2] += 6 * self.Z_res.b self.L4_list[3] += 3 * self.Z_res.b tail_segment_lengths_list = [[ L2, L3, L4 ] for L2, L3, L4 in zip(self.L2_list, self.L3_list, self.L4_list)] tail_turn_angles_list = [ [pi / 2, -pi / 2], [pi / 2, -pi / 2], [pi / 2, -pi / 2], [-pi / 2, pi / 2], [-pi / 2, pi / 2], ] tail_trans_in_list = [ Trans.R270, Trans.R270, Trans.R270, Trans.R270, Trans.R270 ] ### RESONATORS TAILS CALCULATIONS SECTION END ### pars = list( zip(self.L1_list, self.to_line_list, self.L_coupling_list, self.fork_y_spans, tail_segment_lengths_list, tail_turn_angles_list, tail_trans_in_list, self.L0_list, self.N_coils)) for res_idx, params in enumerate(pars): # parameters exctraction L1 = params[0] to_line = params[1] L_coupling = params[2] fork_y_span = params[3] tail_segment_lengths = params[4] tail_turn_angles = params[5] tail_trans_in = params[6] L0 = params[7] n_coils = params[8] # deduction for resonator placements # under condition that Xmon-Xmon distance equals # `xmon_x_distance` worm_x = self.contact_pads[-1].end.x + (res_idx + 1 / 2) * self.resonators_dx worm_y = self.contact_pads[-1].end.y - self.ro_line_dy - to_line self.resonators.append( EMResonatorTL3QbitWormRLTailXmonFork( self.Z_res, DPoint(worm_x, worm_y), L_coupling, L0, L1, self.r, n_coils, tail_shape=res_tail_shape, tail_turn_radiuses=tail_turn_radiuses, tail_segment_lengths=tail_segment_lengths, tail_turn_angles=tail_turn_angles, tail_trans_in=tail_trans_in, fork_x_span=self.fork_x_span, fork_y_span=fork_y_span, fork_metal_width=self.fork_metal_width, fork_gnd_gap=self.fork_gnd_gap)) if res_idx == q_idx: self.resonators[-1].place(self.region_ph) # print([self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling]) # print(self.L1_list) # print(self.L2_list) # print(self.L3_list) # print(self.L4_list) def draw_readout_waveguide(self): ''' Subdividing horizontal waveguide adjacent to resonators into several waveguides. Even segments of this adjacent waveguide are adjacent to resonators. Bridges will be placed on odd segments later. Returns ------- None ''' # place readout waveguide ro_line_turn_radius = self.ro_line_turn_radius ro_line_dy = self.ro_line_dy ## calculating segment lengths of subdivided coupling part of ro coplanar ## # value that need to be added to `L_coupling` to get width of resonators bbox. def get_res_extension(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return resonator.Z0.b + 2 * resonator.r def get_res_width(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return (resonator.L_coupling + get_res_extension(resonator)) res_line_segments_lengths = [ self.resonators[0].origin.x - self.contact_pads[-1].end.x - get_res_extension(self.resonators[0]) / 2 ] # length from bend to first bbox of first resonator for i, resonator in enumerate(self.resonators[:-1]): resonator_extension = get_res_extension(resonator) resonator_width = get_res_width(resonator) next_resonator_extension = get_res_extension(self.resonators[i + 1]) # order of adding is from left to right (imagine chip geometry in your head to follow) res_line_segments_lengths.extend([ resonator_width, # `resonator_extension` accounts for the next resonator extension # in this case all resonator's extensions are equal self.resonators_dx - (resonator_width - resonator_extension / 2) - next_resonator_extension / 2 ]) res_line_segments_lengths.extend( [get_res_width(self.resonators[-1]), self.resonators_dx / 2]) # first and last segment will have length `self.resonator_dx/2` res_line_total_length = sum(res_line_segments_lengths) segment_lengths = [ro_line_dy] + res_line_segments_lengths + \ [ro_line_dy / 2, res_line_total_length - self.chip.pcb_feedline_d, ro_line_dy / 2] self.cpwrl_ro_line = CPWRLPath( self.contact_pads[-1].end, shape="LR" + ''.join(['L'] * len(res_line_segments_lengths)) + "RLRLRL", cpw_parameters=self.Z0, turn_radiuses=[ro_line_turn_radius] * 4, segment_lengths=segment_lengths, turn_angles=[pi / 2, pi / 2, pi / 2, -pi / 2], trans_in=Trans.R270) self.cpwrl_ro_line.place(self.region_ph) def draw_xmons_and_resonators(self, q_idx): for i, (resonator, fork_y_span, xmon_dy_Cg_coupling) in \ enumerate( list( zip( self.resonators, self.fork_y_spans, self.xmon_dys_Cg_coupling ) ) ): xmon_center = \ ( resonator.fork_x_cpw.start + resonator.fork_x_cpw.end ) / 2 + \ DVector( 0, -xmon_dy_Cg_coupling - resonator.fork_metal_width / 2 ) # changes start # xmon_center += DPoint( 0, -(self.cross_len_y + self.cross_width_x / 2 + self.cross_gnd_gap_y)) self.xmons.append( XmonCross(xmon_center, sideX_length=self.cross_len_x, sideX_width=self.cross_width_x, sideX_gnd_gap=self.cross_gnd_gap_x, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y, sideY_gnd_gap=self.cross_gnd_gap_y, sideX_face_gnd_gap=self.cross_gnd_gap_x, sideY_face_gnd_gap=self.cross_gnd_gap_y)) if i == q_idx: self.xmons[-1].place(self.region_ph) resonator.place(self.region_ph) xmonCross_corrected = XmonCross( xmon_center, sideX_length=self.cross_len_x, sideX_width=self.cross_width_x, sideX_gnd_gap=self.cross_gnd_gap_x, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y, sideY_gnd_gap=max( 0, self.fork_x_span - 2 * self.fork_metal_width - self.cross_width_y - max(self.cross_gnd_gap_y, self.fork_gnd_gap)) / 2) if i == q_idx: xmonCross_corrected.place(self.region_ph) def draw_josephson_loops(self): # place left squid xmon0 = self.xmons[0] xmon0_xmon5_loop_shift = self.cross_len_x / 3 center1 = DPoint( xmon0.cpw_l.end.x + xmon0_xmon5_loop_shift, xmon0.center.y - (xmon0.sideX_width + xmon0.sideX_gnd_gap) / 2) squid = AsymSquidDCFlux(center1, SQUID_PARAMETERS, 0) self.squids.append(squid) squid.place(self.region_el) # place intermediate squids for xmon_cross in self.xmons[1:-1]: squid_center = (xmon_cross.cpw_bempt.start + xmon_cross.cpw_bempt.end) / 2 squid = AsymSquidDCFlux(squid_center, SQUID_PARAMETERS, 0) self.squids.append(squid) squid.place(self.region_el) # place right squid xmon5 = self.xmons[4] center5 = DPoint( xmon5.cpw_r.end.x - xmon0_xmon5_loop_shift, xmon5.center.y - (xmon5.sideX_width + xmon5.sideX_gnd_gap) / 2) squid = AsymSquidDCFlux(center5, SQUID_PARAMETERS, 0) self.squids.append(squid) squid.place(self.region_el) def draw_microwave_drvie_lines(self): self.cont_lines_y_ref = self.xmons[0].cpw_bempt.end.y - 200e3 tmp_reg = self.region_ph # place caplanar line 1md _p1 = self.contact_pads[0].end _p2 = _p1 + DPoint(1e6, 0) _p3 = self.xmons[0].cpw_l.end + DVector(-1e6, 0) _p4 = self.xmons[0].cpw_l.end + DVector(-66.2e3, 0) _p5 = _p4 + DVector(11.2e3, 0) self.cpwrl_md1 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5], cpw_parameters=[self.z_md_fl] * 5 + [CPWParameters(width=0, gap=self.z_md_fl.b / 2)], turn_radiuses=self.ctr_lines_turn_radius) self.cpwrl_md1.place(tmp_reg) self.cpw_md_lines.append(self.cpwrl_md1) # place caplanar line 2md _p1 = self.contact_pads[3].end _p2 = _p1 + DPoint(0, 500e3) _p3 = DPoint(self.xmons[1].cpw_b.end.x + self.md234_cross_bottom_dx, self.cont_lines_y_ref) _p4 = DPoint(_p3.x, self.xmons[1].cpw_b.end.y - self.md234_cross_bottom_dy) _p5 = _p4 + DPoint(0, 10e3) self.cpwrl_md2 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5], cpw_parameters=[self.z_md_fl] * 5 + [CPWParameters(width=0, gap=self.z_md_fl.b / 2)], turn_radiuses=self.ctr_lines_turn_radius) self.cpwrl_md2.place(tmp_reg) self.cpw_md_lines.append(self.cpwrl_md2) # place caplanar line 3md _p1 = self.contact_pads[5].end _p2 = _p1 + DPoint(0, 500e3) _p3 = _p2 + DPoint(-2e6, 2e6) _p5 = DPoint(self.xmons[2].cpw_b.end.x + self.md234_cross_bottom_dx, self.cont_lines_y_ref) _p4 = DPoint(_p5.x, _p5.y - 1e6) _p6 = DPoint(_p5.x, self.xmons[2].cpw_b.end.y - self.md234_cross_bottom_dy) _p7 = _p6 + DPoint(0, 10e3) self.cpwrl_md3 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5, _p6, _p7], cpw_parameters=[self.z_md_fl] * 8 + [CPWParameters(width=0, gap=self.z_md_fl.b / 2)], turn_radiuses=self.ctr_lines_turn_radius) self.cpwrl_md3.place(tmp_reg) self.cpw_md_lines.append(self.cpwrl_md3) # place caplanar line 4md _p1 = self.contact_pads[7].end _p2 = _p1 + DPoint(-3e6, 0) _p3 = DPoint(self.xmons[3].cpw_b.end.x + self.md234_cross_bottom_dx, self.cont_lines_y_ref) _p4 = DPoint(_p3.x, self.xmons[3].cpw_b.end.y - self.md234_cross_bottom_dy) _p5 = _p4 + DPoint(0, 10e3) self.cpwrl_md4 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5], cpw_parameters=[self.z_md_fl] * 5 + [CPWParameters(width=0, gap=self.z_md_fl.b / 2)], turn_radiuses=self.ctr_lines_turn_radius) self.cpwrl_md4.place(tmp_reg) self.cpw_md_lines.append(self.cpwrl_md4) # place caplanar line 5md _p1 = self.contact_pads[9].end _p2 = _p1 + DPoint(0, -0.5e6) _p3 = _p2 + DPoint(1e6, -1e6) _p4 = self.xmons[4].cpw_r.end + DVector(1e6, 0) _p5 = self.xmons[4].cpw_r.end + DVector(66.2e3, 0) _p6 = _p5 + DVector(11.2e3, 0) self.cpwrl_md5 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5, _p6], cpw_parameters=[self.z_md_fl] * 8 + [CPWParameters(width=0, gap=self.z_md_fl.b / 2)], turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_md5.place(tmp_reg) self.cpw_md_lines.append(self.cpwrl_md5) def draw_flux_control_lines(self): tmp_reg = self.region_ph # place caplanar line 1 fl _p1 = self.contact_pads[1].end _p2 = self.contact_pads[1].end + DPoint(1e6, 0) _p3 = DPoint(self.squids[0].bot_dc_flux_line_left.start.x, self.cont_lines_y_ref) _p4 = DPoint(_p3.x, self.xmons[0].center.y - self.xmons[0].cpw_l.b / 2) self.cpwrl_fl1 = DPathCPW( points=[_p1, _p2, _p3, _p4], cpw_parameters=self.z_md_fl, turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_fl1.place(tmp_reg) self.cpw_fl_lines.append(self.cpwrl_fl1) # place caplanar line 2 fl _p1 = self.contact_pads[2].end _p2 = self.contact_pads[2].end + DPoint(1e6, 0) _p3 = DPoint(self.squids[1].bot_dc_flux_line_left.start.x, self.cont_lines_y_ref) _p4 = DPoint(_p3.x, self.xmons[1].cpw_bempt.end.y) self.cpwrl_fl2 = DPathCPW( points=[_p1, _p2, _p3, _p4], cpw_parameters=self.z_md_fl, turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_fl2.place(tmp_reg) self.cpw_fl_lines.append(self.cpwrl_fl2) # place caplanar line 3 fl _p1 = self.contact_pads[4].end _p2 = self.contact_pads[4].end + DPoint(0, 1e6) _p3 = _p2 + DPoint(-1e6, 1e6) _p4 = DPoint(self.squids[2].bot_dc_flux_line_left.start.x, self.cont_lines_y_ref) _p5 = DPoint(_p4.x, self.xmons[2].cpw_bempt.end.y) self.cpwrl_fl3 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5], cpw_parameters=self.z_md_fl, turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_fl3.place(tmp_reg) self.cpw_fl_lines.append(self.cpwrl_fl3) # place caplanar line 4 fl _p1 = self.contact_pads[6].end _p2 = self.contact_pads[6].end + DPoint(-1.5e6, 0) _p3 = DPoint(self.squids[3].bot_dc_flux_line_left.start.x, self.cont_lines_y_ref) _p4 = DPoint(_p3.x, self.xmons[3].cpw_bempt.end.y) self.cpwrl_fl4 = DPathCPW( points=[_p1, _p2, _p3, _p4], cpw_parameters=self.z_md_fl, turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_fl4.place(tmp_reg) self.cpw_fl_lines.append(self.cpwrl_fl4) # place caplanar line 5 fl _p1 = self.contact_pads[8].end _p2 = self.contact_pads[8].end + DPoint(-0.3e6, 0) _p4 = DPoint(self.squids[4].bot_dc_flux_line_left.start.x, self.cont_lines_y_ref) _p3 = _p4 + DVector(2e6, 0) _p5 = DPoint(_p4.x, self.xmons[4].center.y - self.xmons[4].cpw_l.b / 2) self.cpwrl_fl5 = DPathCPW( points=[_p1, _p2, _p3, _p4, _p5], cpw_parameters=self.z_md_fl, turn_radiuses=self.ctr_lines_turn_radius, ) self.cpwrl_fl5.place(tmp_reg) self.cpw_fl_lines.append(self.cpwrl_fl5) def draw_el_dc_contacts(self): test_samples_el2: List[Region] = [] test_samples_ph_cut: List[Region] = [] for squid, xmon, fl_line in zip(self.squids, self.xmons, self.cpw_fl_lines): # dc contact pad has to be completely # inside union of both e-beam and photo deposed # metal regions. # `self.dc_cont_clearance` represents minimum distance # from dc contact pad`s perimeter to the perimeter of the # e-beam and photo-deposed metal perimeter. top_rect1 = CPW(start=squid.pad_top.center, end=squid.ph_el_conn_pad.end + DPoint(0, self.dc_cont_clearance), width=squid.ph_el_conn_pad.width - 2 * self.dc_cont_clearance, gap=0) if xmon == self.xmons[0]: end = DPoint(squid.origin.x, xmon.center.y - xmon.cpw_l.width / 2) elif xmon == self.xmons[-1]: end = DPoint(squid.origin.x, xmon.center.y - xmon.cpw_r.width / 2) else: end = xmon.cpw_b.end end += DPoint(0, self.dc_cont_clearance) top_rect2 = CPW(start=squid.ph_el_conn_pad.start + DPoint(0, squid.pad_top.r + self.dc_cont_ph_ext), end=end, width=2 * (squid.pad_top.r + self.dc_cont_ph_ext), gap=0) left_bottom = list( squid.bot_dc_flux_line_left.primitives.values())[0] bot_left_shape1 = CPW( start=fl_line.end + DPoint(0, -self.dc_cont_clearance), end=left_bottom.start, width=left_bottom.width - 2 * self.dc_cont_clearance, gap=0) bot_left_shape2 = CPW( start=fl_line.end + DPoint(0, -self.dc_cont_clearance), end=left_bottom.start + DPoint(0, -self.dc_cont_ph_ext), width=left_bottom.width + 2 * self.dc_cont_ph_ext, gap=0) right_bottom = list( squid.bot_dc_flux_line_right.primitives.values())[0] bot_right_shape1 = CPW( start=DPoint(right_bottom.start.x, fl_line.end.y) + DPoint(0, self.dc_cont_clearance), end=right_bottom.start + DPoint(0, -self.dc_cont_ph_ext), width=right_bottom.width - 2 * self.dc_cont_clearance, gap=0) bot_right_shape2 = CPW( start=DPoint(right_bottom.start.x, fl_line.end.y) + DPoint(0, -self.dc_cont_clearance), end=right_bottom.start + DPoint(0, -self.dc_cont_ph_ext), width=right_bottom.width + 2 * self.dc_cont_ph_ext, gap=0) self.el_dc_contacts.append([ top_rect1, top_rect2, bot_left_shape1, bot_left_shape2, bot_right_shape1, bot_right_shape2 ]) test_sample_reg_el2 = Region() for contact in self.el_dc_contacts[-1]: contact.place(self.region_el2) contact.place(test_sample_reg_el2) test_samples_el2.append(test_sample_reg_el2) # DC contacts has to have intersection with empty # layer in photo litography. This is needed in order # to ensure that first e-beam layer does not # broke at the step between substrate and # photolytography polygons. # Following rectangle pads are cutted from photo region # to ensure DC contacts are covering aforementioned level step. test_ph_cut = Region() # Rectangle to cut for top DC contact pad rec_top = CPW(start=squid.ph_el_conn_pad.start + DPoint(0, -self.dc_cont_cut_clearance), end=squid.ph_el_conn_pad.end, width=0, gap=self.dc_cont_cut_width / 2) rec_top.place(self.region_ph) test_ph_cut |= rec_top.empty_region # Rectangle for bottom left DC contact pad left_bot = CPW( start=fl_line.end + DPoint(0, self.cross_gnd_gap_x / 6), end=left_bottom.start + DPoint(0, self.dc_cont_cut_clearance), width=0, gap=self.dc_cont_cut_width / 2) left_bot.place(self.region_ph) test_ph_cut |= left_bot.empty_region # Rectangle for bottom right DC contact pad right_bot = CPW(start=DPoint(right_bottom.start.x, fl_line.end.y) + DPoint(0, self.cross_gnd_gap_x), end=right_bottom.start + DPoint(0, self.dc_cont_cut_clearance), width=0, gap=self.dc_cont_cut_width / 2) right_bot.place(self.region_ph) test_ph_cut |= right_bot.empty_region test_samples_ph_cut.append(test_ph_cut) self.region_el2.merge() for squid in self.test_squids: trans = DCplxTrans(1, 0, False, -self.squids[0].origin + squid.origin) test_reg_el2 = test_samples_el2[0].dup().transformed(trans) self.region_el2 |= test_reg_el2 cut_reg_ph = test_samples_ph_cut[0].dup().transformed(trans) self.region_ph -= cut_reg_ph def draw_test_structures(self): struct_centers = [ DPoint(1e6, 4e6), DPoint(8.7e6, 5.7e6), DPoint(6.5e6, 2.7e6) ] for struct_center in struct_centers: ## JJ test structures ## # test structure with big critical current test_struct1 = TestStructurePads(struct_center) test_struct1.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text( "48.32 nA", 0.001, 50, False, 0, 0) text_bl = test_struct1.empty_rectangle.origin + DPoint( test_struct1.gnd_gap, -4 * test_struct1.gnd_gap) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg test_jj = AsymSquidDCFlux(test_struct1.center, SQUID_PARAMETERS, side=1) self.test_squids.append(test_jj) test_jj.place(self.region_el) # test structure with low critical current test_struct2 = TestStructurePads(struct_center + DPoint(0.3e6, 0)) test_struct2.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text( "9.66 nA", 0.001, 50, False, 0, 0) text_bl = test_struct2.empty_rectangle.origin + DPoint( test_struct2.gnd_gap, -4 * test_struct2.gnd_gap) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg test_jj = AsymSquidDCFlux(test_struct2.center, SQUID_PARAMETERS, side=-1) self.test_squids.append(test_jj) test_jj.place(self.region_el) # test structure for bridge DC contact test_struct3 = TestStructurePads(struct_center + DPoint(0.6e6, 0)) test_struct3.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text( "DC", 0.001, 50, False, 0, 0) text_bl = test_struct3.empty_rectangle.origin + DPoint( test_struct3.gnd_gap, -4 * test_struct3.gnd_gap) text_reg.transform( ICplxTrans(1.0, 0, False, test_struct3.center.x, text_bl.y)) self.region_ph -= text_reg test_bridges = [] for i in range(3): bridge = Bridge1(test_struct3.center + DPoint(50e3 * (i - 1), 0), gnd_touch_dx=20e3) test_bridges.append(bridge) bridge.place(self.region_bridges1, region_name="bridges_1") bridge.place(self.region_bridges2, region_name="bridges_2") # bandages test structures test_dc_el2_centers = [ DPoint(2.5e6, 2.4e6), DPoint(4.2e6, 1.6e6), DPoint(9.0e6, 3.8e6) ] for struct_center in test_dc_el2_centers: test_struct1 = TestStructurePads(struct_center) test_struct1.place(self.region_ph) text_reg = pya.TextGenerator.default_generator().text( "Bandage", 0.001, 40, False, 0, 0) text_bl = test_struct1.empty_rectangle.origin + DPoint( test_struct1.gnd_gap, -4 * test_struct1.gnd_gap) text_reg.transform(ICplxTrans(1.0, 0, False, text_bl.x, text_bl.y)) self.region_ph -= text_reg rec_width = 10e3 rec_height = test_struct1.rectangles_gap + 2 * rec_width p1 = struct_center - DVector(rec_width / 2, rec_height / 2) dc_rec = Rectangle(p1, rec_width, rec_height) dc_rec.place(self.region_el2) def draw_el_protection(self): protection_a = 300e3 for squid in (self.squids + self.test_squids): self.region_el_protection.insert(pya.Box().from_dbox( pya.DBox( squid.origin - 0.5 * DVector(protection_a, protection_a), squid.origin + 0.5 * DVector(protection_a, protection_a)))) def draw_photo_el_marks(self): marks_centers = [ DPoint(1e6, 9e6), DPoint(1e6, 1e6), DPoint(9e6, 1e6), DPoint(9e6, 9e6), DPoint(8e6, 4e6), DPoint(1e6, 6e6) ] for mark_center in marks_centers: self.marks.append(MarkBolgar(mark_center)) self.marks[-1].place(self.region_ph) def draw_bridges(self): bridges_step = 150e3 fl_bridges_step = 150e3 # for resonators for resonator in self.resonators: for name, res_primitive in resonator.primitives.items(): if "coil0" in name: # skip L_coupling coplanar. # bridgyfy all in "coil0" except for the first cpw that # is adjacent to readout line and has length equal to `L_coupling` for primitive in list( res_primitive.primitives.values())[1:]: Bridge1.bridgify_CPW(primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) continue elif "fork" in name: # skip fork primitives continue else: # bridgify everything else Bridge1.bridgify_CPW(res_primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) # for contact wires for key, val in self.__dict__.items(): if "cpwrl_md" in key: Bridge1.bridgify_CPW(val, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) elif "cpwrl_fl" in key: Bridge1.bridgify_CPW( val, fl_bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2, avoid_points=[squid.origin for squid in self.squids], avoid_distance=400e3) for cpw_fl in self.cpw_fl_lines: bridge_center1 = cpw_fl.end + DVector(0, -40e3) br = Bridge1(center=bridge_center1, trans_in=Trans.R90) br.place(dest=self.region_bridges1, region_name="bridges_1") br.place(dest=self.region_bridges2, region_name="bridges_2") # for readout waveguide bridgified_primitives_idxs = list(range(2)) bridgified_primitives_idxs += list( range(2, 2 * (len(self.resonators) + 1) + 1, 2)) bridgified_primitives_idxs += list( range(2 * (len(self.resonators) + 1) + 1, len(self.cpwrl_ro_line.primitives.values()))) for idx, primitive in enumerate( self.cpwrl_ro_line.primitives.values()): if idx in bridgified_primitives_idxs: Bridge1.bridgify_CPW(primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) def draw_pinning_holes(self): selection_region = Region( pya.Box(Point(100e3, 100e3), Point(101e3, 101e3))) tmp_ph = self.region_ph.dup() other_regs = tmp_ph.select_not_interacting(selection_region) reg_to_fill = self.region_ph.select_interacting(selection_region) filled_reg = fill_holes(reg_to_fill) self.region_ph = filled_reg + other_regs def extend_photo_overetching(self): tmp_reg = Region() ep = pya.EdgeProcessor() for poly in self.region_ph.each(): tmp_reg.insert( ep.simple_merge_p2p([ poly.sized(FABRICATION.OVERETCHING, FABRICATION.OVERETCHING, 2) ], False, False, 1)) self.region_ph = tmp_reg def split_polygons_in_layers(self, max_pts=200): self.region_ph = split_polygons(self.region_ph, max_pts) self.region_bridges2 = split_polygons(self.region_bridges2, max_pts) for poly in self.region_ph: if poly.num_points() > max_pts: print("exists photo") for poly in self.region_ph: if poly.num_points() > max_pts: print("exists bridge2")
r, L2, N, gnd_width) worm.place(cell, layer_photo) resonators.append(worm) qbit_bbox = pya.Box().from_dbox(qbit_bbox) h = qbit_bbox.height() reg = worm.gnd_reg + (Region( pya.Box(qbit_bbox.p1 - Point(2 * h, 2 * h), qbit_bbox.p2 + Point(2 * h, dCap))) - Region(qbit.metal_region.bbox())) new_reg = Region() # reg.merged_semantics=False r_cell = Region(cell.begin_shapes_rec(layer_photo)) reg = reg & r_cell for poly in reg: poly_temp = fill_holes(poly) new_reg.insert(poly_temp) # r_cell.merged_semantics=False r_cell = r_cell - reg r_cell = r_cell + new_reg temp_i = cell.layout().layer(pya.LayerInfo(PROGRAM.LAYER1_NUM, 0)) cell.shapes(temp_i).insert(r_cell) cell.layout().clear_layer(layer_photo) cell.layout().move_layer(temp_i, layer_photo) cell.layout().delete_layer(temp_i) # place top array of resonators with qBits N_top = N_bottom - 1 delta = 1e6 step_top = step_bot resonators = [] qbits = []
cross_gnd_gap) / 2 worm = EMResonator_TL2Qbit_worm3_1_XmonFork( Z_res, DPoint(worm_x, y - to_line), L_coupling, L0, L1, r, L2, N, fork_x_span, fork_y_span, fork_metal_width, fork_gnd_gap) xmon_center = (worm.fork_y_cpw1.end + worm.fork_y_cpw2.end) / 2 xmon_center += DPoint( 0, -(cross_len + cross_width / 2) + xmon_fork_penetration) xmonCross = XmonCross(xmon_center, cross_width, cross_len, cross_gnd_gap) # calculate simulation box dimensions chip_box = pya.Box(DPoint(0, 0), DPoint(CHIP.dx, CHIP.dy)) # placing all objects in proper order and translating them to origin photo_reg.insert(chip_box) # translating all objects so that chip.p1 at coordinate origin xmonCross.place(photo_reg) worm.place(photo_reg) xmonCross_corrected = XmonCross(xmon_center, cross_width, cross_len, xmon_fork_gnd_gap) xmonCross_corrected.place(photo_reg) Z0.place(photo_reg) cell.shapes(layer_photo).insert(photo_reg) # delete Xmon cross shapes = cell.shapes(layer_photo)
def fill_holes(cell, layer, obj, dx=15e3, dy=15e3, width=10e3, height=10e3, d=50e3): """ @brief: Fills an object with a grid of holes Warning: don't use this method for the same region twice @params: cell layer obj dx : int period of a grid in horizonatal direction dy : int period of a grid in vertical direction width : int width of a hole height : int height of a hole d : int padding """ if (obj.is_cell_inst()): return None poly = obj.shape.polygon print(poly.holes()) bbox = poly.bbox() poly_reg = Region(poly) t_reg = Region() # Fill the boundary box with holes y = bbox.p1.y + height while y < bbox.p2.y - height: x = bbox.p1.x + width while x < bbox.p2.x - width: box = pya.Box().from_dbox( pya.DBox(DPoint(x, y), DPoint(x + width, y + height))) x += dx t_reg.insert(box) y += dy # Draw boundary around holes in the polygon for hole_i in range(0, poly.holes()): points = [p for p in poly.each_point_hole(hole_i)] points.append(points[0]) boundary = Path(points, d) poly_reg -= Region(boundary) # Draw boundary around the outer edge of the polygon points = [p for p in poly.each_point_hull()] points.append(points[0]) boundary = Path(points, d) poly_reg -= Region(boundary) # Select only inner holes qwe = t_reg.select_inside(poly_reg) # Subtract holes from the layer r_cell = Region(cell.begin_shapes_rec(layer)) temp_i = cell.layout().layer(pya.LayerInfo(PROGRAM.LAYER1_NUM, 0)) cell.shapes(temp_i).insert(r_cell - qwe) cell.layout().clear_layer(layer) cell.layout().move_layer(temp_i, layer) cell.layout().delete_layer(temp_i)
def fill_holes(obj, dx=40e3, dy=40e3, width=32e3, height=32e3, d=150e3): """ Fills an object with a grid of holes Warning: don't use this method for the same region twice Parameters ---------- obj dx : float period of a grid in horizonatal direction dy : float period of a grid in vertical direction width : float width of a hole height : float height of a hole d : float padding from polygon boundaries. For both external and internal boundaries in case polygon consist of holes. Returns ------- None """ def fill_poly(poly): bbox = poly.bbox() poly_reg = Region(poly) t_reg = Region() # Draw boundary around holes in the polygon for hole_i in range(0, poly.holes()): points = [p for p in poly.each_point_hole(hole_i)] points.append(points[0]) boundary = Path(points, 2 * d) poly_reg -= Region(boundary) # Draw boundary around the outer edge of the polygon points = [p for p in poly.each_point_hull()] points.append(points[0]) boundary = Path(points, 2 * d) poly_reg -= Region(boundary) # Fill the boundary box with holes y = bbox.p1.y + height while y < bbox.p2.y - height: x = bbox.p1.x + width while x < bbox.p2.x - width: box = pya.Box().from_dbox( pya.DBox(DPoint(x, y), DPoint(x + width, y + height))) x += dx t_reg.insert(box) y += dy # Select only inner holes holes_inside = t_reg.select_inside(poly_reg) for box in holes_inside.each(): poly.insert_hole(list(box.each_point_hull())) return poly if isinstance(obj, pya.ObjectInstPath): print("obj inst") if (obj.is_cell_inst()): return None poly = obj.shape.polygon poly = fill_poly(poly) obj.shape.polygon = poly elif isinstance(obj, pya.Polygon): poly = obj return fill_poly(poly) elif isinstance(obj, Region): reg = obj result_reg = Region() for poly in reg: result_reg.insert(fill_holes(poly)) return result_reg
class Design5Q(ChipDesign): def __init__(self, cell_name): super().__init__(cell_name) info_el2 = pya.LayerInfo(3, 0) # for DC contact deposition self.region_el2 = Region() self.layer_el2 = self.layout.layer(info_el2) info_bridges1 = pya.LayerInfo(4, 0) # bridge photo layer 1 self.region_bridges1 = Region() self.layer_bridges1 = self.layout.layer(info_bridges1) info_bridges2 = pya.LayerInfo(5, 0) # bridge photo layer 2 self.region_bridges2 = Region() self.layer_bridges2 = self.layout.layer(info_bridges2) self.lv.add_missing_layers( ) # has to call it once more to add new layers ### ADDITIONAL VARIABLES SECTION START ### self.contact_pads: list[ContactPad] = None # chip rectangle and contact pads self.chip = CHIP_10x10_12pads self.chip_box = self.chip.box # readout line parameters self.ro_line_turn_radius: float = 200e3 self.ro_line_dy: float = 1600e3 self.cpwrl_ro_line: CPW_RL_Path = None self.Z0 = CPWParameters(CHIP_10x10_12pads.chip_cpw_width, CHIP_10x10_12pads.chip_cpw_gap) # resonators objects list self.resonators: List[EMResonatorTL3QbitWormRLTailXmonFork] = [] # distance between nearest resonators central conductors centers # constant step between resonators origin points along x-axis. self.resonators_dx = 790e3 # resonator parameters self.L_coupling_list = [1e3 * x for x in [230, 225, 225, 220, 215]] # corresponding to resonanse freq is linspaced in interval [6,9) GHz self.L0 = 1600e3 self.L1_list = [1e3 * x for x in [53.7163, 73, 91, 87, 48]] self.r = 60e3 self.N = 5 self.L2_list = [self.r] * len(self.L1_list) self.L3_list = [None] * len(self.L1_list) # to be constructed self.L4_list = [self.r] * len(self.L1_list) self.width_res = 20e3 self.gap_res = 10e3 self.Z_res = CPWParameters(self.width_res, self.gap_res) self.to_line_list = [53e3] * len(self.L1_list) self.fork_metal_width = 20e3 self.fork_gnd_gap = 10e3 self.xmon_fork_gnd_gap = 25e3 # resonator-fork parameters # -20e3 for Xmons in upper sweet-spot # -10e3 for Xmons in lower sweet-spot self.xmon_fork_penetration_list = [-25e3] * len(self.L1_list) # xmon parameters self.cross_width: float = 60e3 self.cross_len: float = 60e3 self.cross_gnd_gap: float = 20e3 self.xmon_x_distance: float = 485e3 # from simulation of g_12 self.xmon_dys_Cg_coupling = [ 1e3 * x for x in [11.2065, 9.31433, 12.0965, 10.0777, 12.9573] ] self.xmons: list[XmonCross] = [] self.cross_len_x = 180e3 self.cross_width_x = 60e3 self.cross_gnd_gap_x = 20e3 self.cross_len_y = 60e3 self.cross_width_y = 60e3 self.cross_gnd_gap_y = 20e3 # squids self.squids: List[AsymSquid] = [] self.el_dc_contacts: List[List[ElementBase, ElementBase]] = [] # md and flux lines attributes self.shift_fl_y = self.cross_len_y + 70e3 self.shift_md_x = 150e3 self.shift_md_y = 360e3 self.cpwrl_md1: CPW_RL_Path = None self.cpwrl_md1_end: MDriveLineEnd = None self.cpwrl_fl1: CPW_RL_Path = None self.cpwrl_fl1_end: FluxLineEnd = None self.cpwrl_md2: CPW_RL_Path = None self.cpwrl_md2_end: MDriveLineEnd = None self.cpwrl_fl2: CPW_RL_Path = None self.cpwrl_fl2_end: FluxLineEnd = None self.cpwrl_md3: CPW_RL_Path = None self.cpwrl_md3_end: MDriveLineEnd = None self.cpwrl_fl3: CPW_RL_Path = None self.cpwrl_fl3_end: FluxLineEnd = None self.cpwrl_md4: CPW_RL_Path = None self.cpwrl_md4_end: MDriveLineEnd = None self.cpwrl_fl4: CPW_RL_Path = None self.cpwrl_fl4_end: FluxLineEnd = None self.cpwrl_md5: CPW_RL_Path = None self.cpwrl_md5_end: MDriveLineEnd = None self.cpwrl_fl5: CPW_RL_Path = None self.cpwrl_fl5_end: FluxLineEnd = None # marks self.marks: List[Mark2] = [] ### ADDITIONAL VARIABLES SECTION END ### def draw(self, design_params=None): self.draw_chip() ''' Only creating object. This is due to the drawing of xmons and resonators require draw xmons, then draw resonators and then draw additional xmons. This is ugly and that how this was before migrating to `ChipDesign` based code structure This is also the reason why `self.__init__` is flooded with design parameters that are used across multiple drawing functions. TODO: This drawings sequence can be decoupled in the future. ''' self.create_resonator_objects() self.draw_readout_waveguide() self.draw_xmons_and_resonators() self.draw_md_and_flux_lines() self.draw_josephson_loops() self.draw_el_dc_contacts() self.draw_photo_el_marks() self.draw_bridges() # self.inverse_destination(self.region_ph) self.cell.shapes(self.layer_ph).insert(self.region_ph) self.cell.shapes(self.layer_el).insert(self.region_el) self.cell.shapes(self.layer_el2).insert(self.region_el2) self.cell.shapes(self.layer_bridges1).insert(self.region_bridges1) self.cell.shapes(self.layer_bridges2).insert(self.region_bridges2) def draw_chip(self): self.region_ph.insert(self.chip_box) self.contact_pads = self.chip.get_contact_pads() for contact_pad in self.contact_pads: contact_pad.place(self.region_ph) def create_resonator_objects(self): # fork at the end of resonator parameters fork_x_span = self.cross_width + 2 * (self.xmon_fork_gnd_gap + self.fork_metal_width) ### RESONATORS TAILS CALCULATIONS SECTION START ### # key to the calculations can be found in hand-written format here: # https://drive.google.com/file/d/1wFmv5YmHAMTqYyeGfiqz79a9kL1MtZHu/view?usp=sharing # x span between left long vertical line and # right-most center of central conductors resonators_widths = [ 2 * self.r + L_coupling for L_coupling in self.L_coupling_list ] x1 = 2 * self.resonators_dx + resonators_widths[ 2] / 2 - 2 * self.xmon_x_distance x2 = x1 + self.xmon_x_distance - self.resonators_dx x3 = resonators_widths[2] / 2 x4 = 3 * self.resonators_dx - (x1 + 3 * self.xmon_x_distance) x5 = 4 * self.resonators_dx - (x1 + 4 * self.xmon_x_distance) res_tail_shape = "LRLRL" tail_turn_radiuses = self.r # list corrected for resonator-qubit coupling geomtry, so all transmons centers are placed # along single horizontal line self.L0_list = [ self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling ] self.L2_list[0] += 6 * self.Z_res.b self.L2_list[1] += 0 self.L2_list[3] += 3 * self.Z_res.b self.L2_list[4] += 6 * self.Z_res.b self.L3_list[0] = x1 self.L3_list[1] = x2 self.L3_list[2] = x3 self.L3_list[3] = x4 self.L3_list[4] = x5 self.L4_list[1] += 6 * self.Z_res.b self.L4_list[2] += 6 * self.Z_res.b self.L4_list[3] += 3 * self.Z_res.b tail_segment_lengths_list = [[ L2, L3, L4 ] for L2, L3, L4 in zip(self.L2_list, self.L3_list, self.L4_list)] tail_turn_angles_list = [ [pi / 2, -pi / 2], [pi / 2, -pi / 2], [pi / 2, -pi / 2], [-pi / 2, pi / 2], [-pi / 2, pi / 2], ] tail_trans_in_list = [ Trans.R270, Trans.R270, Trans.R270, Trans.R270, Trans.R270 ] ### RESONATORS TAILS CALCULATIONS SECTION END ### pars = list( zip(self.L1_list, self.to_line_list, self.L_coupling_list, self.xmon_fork_penetration_list, tail_segment_lengths_list, tail_turn_angles_list, tail_trans_in_list, self.L0_list)) for res_idx, params in enumerate(pars): # parameters exctraction L1 = params[0] to_line = params[1] L_coupling = params[2] xmon_fork_penetration = params[3] tail_segment_lengths = params[4] tail_turn_angles = params[5] tail_trans_in = params[6] L0 = params[7] # deduction for resonator placements # under condition that Xmon-Xmon distance equals # `xmon_x_distance` worm_x = self.contact_pads[-1].end.x + (res_idx + 1 / 2) * self.resonators_dx worm_y = self.contact_pads[-1].end.y - self.ro_line_dy - to_line # `fork_y_span` based on coupling modulated with # xmon_fork_penetration from `self.xmon_fork_penetration` fork_y_span = xmon_fork_penetration + self.xmon_fork_gnd_gap self.resonators.append( EMResonatorTL3QbitWormRLTailXmonFork( self.Z_res, DPoint(worm_x, worm_y), L_coupling, L0, L1, self.r, self.N, tail_shape=res_tail_shape, tail_turn_radiuses=tail_turn_radiuses, tail_segment_lengths=tail_segment_lengths, tail_turn_angles=tail_turn_angles, tail_trans_in=tail_trans_in, fork_x_span=fork_x_span, fork_y_span=fork_y_span, fork_metal_width=self.fork_metal_width, fork_gnd_gap=self.fork_gnd_gap)) # print([self.L0 - xmon_dy_Cg_coupling for xmon_dy_Cg_coupling in self.xmon_dys_Cg_coupling]) # print(self.L1_list) # print(self.L2_list) # print(self.L3_list) # print(self.L4_list) def draw_readout_waveguide(self): ''' Subdividing horizontal waveguide adjacent to resonators into several waveguides. Even segments of this adjacent waveguide are adjacent to resonators. Bridges will be placed on odd segments later. Returns ------- None ''' # place readout waveguide ro_line_turn_radius = self.ro_line_turn_radius ro_line_dy = self.ro_line_dy pcb_feedline_d = CHIP_10x10_12pads.pcb_feedline_d # first and last segment will have length `self.resonator_dx/2` res_line_total_length = (1 + self.resonators_dx) * len(self.resonators) ## calculating segment lengths of subdivided coupling part of ro coplanar ## # value that need to be added to `L_coupling` to get width of resonators bbox. def get_res_extension(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return resonator.Z0.b + 2 * resonator.r def get_res_width(resonator: EMResonatorTL3QbitWormRLTailXmonFork): return (resonator.L_coupling + get_res_extension(resonator)) res_line_segments_lengths = [ self.resonators[0].origin.x - self.contact_pads[-1].end.x - get_res_extension(self.resonators[0]) / 2 ] # length from bend to first bbox of first resonator for i, resonator in enumerate(self.resonators[:-1]): resonator_extension = get_res_extension(resonator) resonator_width = get_res_width(resonator) next_resonator_extension = get_res_extension(self.resonators[i + 1]) # order of adding is from left to right (imagine chip geometry in your head to follow) res_line_segments_lengths.extend([ resonator_width, # `resonator_extension` accounts for the next resonator extension # in this case all resonator's extensions are equal self.resonators_dx - (resonator_width - resonator_extension / 2) - next_resonator_extension / 2 ]) res_line_segments_lengths.extend( [get_res_width(self.resonators[-1]), self.resonators_dx / 2]) segment_lengths = [ro_line_dy] + res_line_segments_lengths + \ [ro_line_dy / 2, res_line_total_length - self.chip.pcb_feedline_d, ro_line_dy / 2] self.cpwrl_ro_line = CPW_RL_Path( self.contact_pads[-1].end, shape="LR" + ''.join(['L'] * len(res_line_segments_lengths)) + "RLRLRL", cpw_parameters=self.Z0, turn_radiuses=[ro_line_turn_radius] * 4, segment_lengths=segment_lengths, turn_angles=[pi / 2, pi / 2, pi / 2, -pi / 2], trans_in=Trans.R270) self.cpwrl_ro_line.place(self.region_ph) def draw_xmons_and_resonators(self): for resonator, xmon_fork_penetration, xmon_dy_Cg_coupling in zip( self.resonators, self.xmon_fork_penetration_list, self.xmon_dys_Cg_coupling): xmon_center = (resonator.fork_y_cpw1.end + resonator.fork_y_cpw2.end) / 2 + \ DVector(0, -xmon_dy_Cg_coupling) xmon_center += DPoint( 0, -(self.cross_len + self.cross_width / 2) + xmon_fork_penetration) self.xmons.append( XmonCross(xmon_center, self.cross_len_x, self.cross_width_x, self.cross_gnd_gap_x, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y, sideY_gnd_gap=self.cross_gnd_gap_y)) self.xmons[-1].place(self.region_ph) resonator.place(self.region_ph) xmonCross_corrected = XmonCross(xmon_center, sideX_length=self.cross_len_x, sideX_width=self.cross_width_x, sideX_gnd_gap=self.cross_gnd_gap_x, sideY_length=self.cross_len_y, sideY_width=self.cross_width_y, sideY_gnd_gap=min( self.cross_gnd_gap_y, self.xmon_fork_gnd_gap)) xmonCross_corrected.place(self.region_ph) def draw_md_and_flux_lines(self): """ Drawing of md (microwave drive) and flux tuning lines for 5 qubits Returns ------- """ contact_pads = self.contact_pads ctr_line_turn_radius = 200e3 xmon_center = self.xmons[-1].center xmon_x_distance = self.xmon_x_distance cross_width_y = self.cross_width_y cross_width_x = self.cross_width_x cross_len_x = self.cross_len_x cross_gnd_gap_y = self.cross_gnd_gap_y cross_gnd_gap_x = self.cross_gnd_gap_x width_res = self.Z_res.width tmp_reg = self.region_ph z_md_fl = self.Z0 shift_fl_y = self.shift_fl_y shift_md_x = self.shift_md_x shift_md_y = self.shift_md_y flux_end_width = self.cross_width_x + 2 * self.cross_gnd_gap_x md_transition = 25e3 md_z1_params = CPWParameters( 7e3, 4e3 ) # Z = 50.04 Ohm, E_eff = 6.237 (E_0 = 11.45) (8e3, 4.15e3) md_z1_length = 100e3 shift_md_x_side = md_z1_length + md_transition + md_z1_params.b / 2 + cross_len_x + cross_width_x / 2 + cross_gnd_gap_x # place caplanar line 1md self.cpwrl_md1 = CPW_RL_Path( contact_pads[0].end, shape="LRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[ 2 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16, ((contact_pads[0].end.y - xmon_center.y)**2 + (9 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16)**2)**0.5, 5 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16 - 140e3 ], turn_angles=[ -atan2( contact_pads[0].end.y - xmon_center.y, 9 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16), atan2( contact_pads[0].end.y - xmon_center.y, 9 * (-contact_pads[0].end.x + xmon_center.x - 4 * xmon_x_distance - shift_md_x_side) / 16) ], trans_in=Trans.R0) self.cpwrl_md1.place(tmp_reg) self.cpwrl_md1_end = MDriveLineEnd( list(self.cpwrl_md1.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md1_end.place(tmp_reg) # place caplanar line 1 fl self.cpwrl_fl1 = CPW_RL_Path( contact_pads[1].end, shape="LRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius], segment_lengths=[(-contact_pads[1].end.x + xmon_center.x - 4 * xmon_x_distance - cross_len_x), (-contact_pads[1].end.y + xmon_center.y - cross_width_y / 2 - cross_gnd_gap_y - width_res) ], turn_angles=[pi / 2], trans_in=Trans.R0) self.cpwrl_fl1.place(tmp_reg) self.cpwrl_fl1_end = FluxLineEnd(self.cpwrl_fl1.end, z_md_fl, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl1_end.place(tmp_reg) # place caplanar line 2md self.cpwrl_md2 = CPW_RL_Path( contact_pads[3].end, shape="LRLRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 3, segment_lengths=[ (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 4, (-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2, (((-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2)**2 + (5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8)**2)**0.5, (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8 ], turn_angles=[ -pi / 2, atan2( 5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8, (-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2), pi / 2 - atan2( 5 * (-contact_pads[3].end.y + xmon_center.y - shift_md_y) / 8, (-contact_pads[3].end.x + xmon_center.x + shift_md_x - 3 * xmon_x_distance) / 2) ], trans_in=Trans.R90) self.cpwrl_md2.place(tmp_reg) self.cpwrl_md2_end = MDriveLineEnd( list(self.cpwrl_md2.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md2_end.place(tmp_reg) # place caplanar line 2 fl self.cpwrl_fl2 = CPW_RL_Path( contact_pads[2].end, shape="LRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[ (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4, ((3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4)**2 + (7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8)**2)**0.5, (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8 ], turn_angles=[ atan2( 7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4), pi / 2 - atan2( 7 * (-contact_pads[2].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (-contact_pads[2].end.x + xmon_center.x - 3 * xmon_x_distance) / 4) ], trans_in=Trans.R0) self.cpwrl_fl2.place(tmp_reg) self.cpwrl_fl2_end = FluxLineEnd(self.cpwrl_fl2.end, z_md_fl, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl2_end.place(tmp_reg) # place caplanar line 3md self.cpwrl_md3 = CPW_RL_Path( contact_pads[5].end, shape="LRLRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 3, segment_lengths=[ (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 4, (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2, (((contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2)**2 + (5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8)**2)**0.5, (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8 ], turn_angles=[ pi / 2, -atan2( 5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8, (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2), -pi / 2 + atan2( 5 * (-contact_pads[5].end.y + xmon_center.y - shift_md_y) / 8, (contact_pads[5].end.x - xmon_center.x - shift_md_x + 2 * xmon_x_distance) / 2) ], trans_in=Trans.R90) self.cpwrl_md3.place(tmp_reg) self.cpwrl_md3_end = MDriveLineEnd( list(self.cpwrl_md3.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md3_end.place(tmp_reg) # place caplanar line 3 fl fl_l1 = (self.xmons[2].cpw_bempt.end.y - contact_pads[4].end.y) / 4 fl_l3 = fl_l1 dr = self.xmons[2].cpw_bempt.end - contact_pads[4].end dr.y = dr.y - fl_l1 - fl_l3 - z_md_fl.width turn_angle = atan2(-dr.x, dr.y) fl_l2 = dr.abs() self.cpwrl_fl3 = CPW_RL_Path(self.contact_pads[4].end, shape="LRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[fl_l1, fl_l2, fl_l3], turn_angles=[turn_angle, -turn_angle], trans_in=Trans.R90) self.cpwrl_fl3.place(tmp_reg) self.cpwrl_fl3_end = FluxLineEnd(self.cpwrl_fl3.end, z_md_fl, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl3_end.place(tmp_reg) # place caplanar line 4 md self.cpwrl_md4 = CPW_RL_Path( contact_pads[7].end, shape="LRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius], segment_lengths=[ contact_pads[7].end.x - xmon_center.x + xmon_x_distance - shift_md_x, -contact_pads[7].end.y + xmon_center.y - shift_md_y ], turn_angles=[-pi / 2], trans_in=Trans.R180) self.cpwrl_md4.place(tmp_reg) self.cpwrl_md4_end = MDriveLineEnd( list(self.cpwrl_md4.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md4_end.place(tmp_reg) # place caplanar line 4 fl self.cpwrl_fl4 = CPW_RL_Path( contact_pads[6].end, shape="LRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[ (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4, ((6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8)**2 + (3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4)**2)**0.5, 2 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8 ], turn_angles=[ -atan2( 6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4), -pi / 2 + atan2( 6 * (-contact_pads[6].end.y + xmon_center.y - shift_fl_y) / 8, 3 * (contact_pads[6].end.x - xmon_center.x + xmon_x_distance) / 4) ], trans_in=Trans.R180) self.cpwrl_fl4.place(tmp_reg) self.cpwrl_fl4_end = FluxLineEnd(self.cpwrl_fl4.end, z_md_fl, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl4_end.place(tmp_reg) # place caplanar line 5 md self.cpwrl_md5 = CPW_RL_Path( contact_pads[9].end, shape="LRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 2, segment_lengths=[ 2 * (contact_pads[9].end.y - xmon_center.y) / 3, (((contact_pads[9].end.y - xmon_center.y) / 3)**2 + (2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3)**2)**(0.5), (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3 - 140e3 ], turn_angles=[ -atan2( 2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3, (contact_pads[9].end.y - xmon_center.y) / 3), atan2( 2 * (contact_pads[9].end.x - xmon_center.x - shift_md_x_side) / 3, (contact_pads[9].end.y - xmon_center.y) / 3) - pi / 2 ], trans_in=Trans.R270) self.cpwrl_md5.place(tmp_reg) self.cpwrl_md5_end = MDriveLineEnd( list(self.cpwrl_md5.primitives.values())[-1], md_z1_params, md_transition, md_z1_length) self.cpwrl_md5_end.place(tmp_reg) # place caplanar line 5 fl self.cpwrl_fl5 = CPW_RL_Path( contact_pads[8].end, shape="LRLRLRLRL", cpw_parameters=z_md_fl, turn_radiuses=[ctr_line_turn_radius] * 4, segment_lengths=[ (contact_pads[8].end.x - xmon_center.x) / 4, ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3 + 50e3, ((2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3)**2 + ((contact_pads[8].end.x - xmon_center.x) / 2)**2)**0.5, (contact_pads[8].end.x - xmon_center.x) / 4 - cross_len_x, 230e3 ], turn_angles=[ pi / 2, -atan2( (contact_pads[8].end.x - xmon_center.x) / 2, 2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3), -pi / 2 + atan2( (contact_pads[8].end.x - xmon_center.x) / 2, 2 * ((contact_pads[8].end.y - xmon_center.y) + 250e3) / 3), -pi / 2 ], trans_in=Trans.R180) self.cpwrl_fl5.place(tmp_reg) self.cpwrl_fl5_end = FluxLineEnd(self.cpwrl_fl5.end, z_md_fl, width=flux_end_width, trans_in=Trans.R0) self.cpwrl_fl5_end.place(tmp_reg) def draw_josephson_loops(self): new_pars_squid = AsymSquidParams(pad_r=5e3, pads_distance=30e3, p_ext_width=5e3, p_ext_r=200, sq_len=7e3, sq_area=35e6, j_width_1=94, j_width_2=347, intermediate_width=500, b_ext=1e3, j_length=94, n=20, bridge=180, j_length_2=250) # place left squid xmon0 = self.xmons[0] center1 = DPoint( self.cpwrl_fl1_end.end.x, xmon0.center.y - (xmon0.sideX_width + xmon0.sideX_gnd_gap) / 2) squid = AsymSquid(center1, new_pars_squid, 0) squid.place(self.region_el) self.squids.append(squid) # place intermediate squids for xmon_cross in self.xmons[1:-1]: squid_center = (xmon_cross.cpw_bempt.start + xmon_cross.cpw_bempt.end) / 2 squid = AsymSquid(squid_center, new_pars_squid, 0) squid.place(self.region_el) # place right squid xmon5 = self.xmons[4] center5 = DPoint( self.cpwrl_fl5_end.end.x, xmon5.center.y - (xmon5.sideX_width + xmon5.sideX_gnd_gap) / 2) squid = AsymSquid(center5, new_pars_squid, 0) squid.place(self.region_el) def draw_el_dc_contacts(self): for squid in self.squids: r_pad = squid.params.pad_r center_up = squid.pad_up.center + DPoint(0, r_pad) center_down = squid.pad_down.center + DPoint(0, -r_pad) self.el_dc_contacts.append( [Circle(center_up, 2 * r_pad), Circle(center_down, 2 * r_pad)]) for contact in self.el_dc_contacts[-1]: contact.place(self.region_el2) def draw_photo_el_marks(self): marks_centers = [DPoint(1200e3, 9000e3), DPoint(8000e3, 4000e3)] for mark_center in marks_centers: self.marks.append(Mark2(mark_center)) self.marks[-1].place(self.region_ph) def draw_bridges(self): self.region_bridges2.insert(self.chip_box) bridges_step = 50e3 # for resonators for resonator in self.resonators: for name, res_primitive in resonator.primitives.items(): if "coil0" in name: # skip L_coupling coplanar. bridgify_lambda = lambda x: Bridge1.bridgify_CPW( x, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) # bridgyfy all in "coil0" except for the first cpw that # is adjacent to readout line and has length equal to `L_coupling` map(bridgify_lambda, list(res_primitive.primitives.values())[1:]) continue elif "fork" in name: # skip fork primitives continue else: # bridgify everything else Bridge1.bridgify_CPW(res_primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) # for contact wires for key, val in self.__dict__.items(): if ("cpwrl_md" in key) or ("cpwrl_fl" in key): Bridge1.bridgify_CPW(val, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2) # for readout waveguide bridgified_primitives_idxs = list(range(2)) bridgified_primitives_idxs += list( range(2, 2 * (len(self.resonators) + 1) + 1, 2)) bridgified_primitives_idxs += list( range(2 * (len(self.resonators) + 1) + 1, len(self.cpwrl_ro_line.primitives.values()))) for idx, primitive in enumerate( self.cpwrl_ro_line.primitives.values()): if idx in bridgified_primitives_idxs: Bridge1.bridgify_CPW(primitive, bridges_step, dest=self.region_bridges1, dest2=self.region_bridges2)
def __init__(self, origin, params, trans_in=None): self.params = params self.a = params[0] self.b = params[1] self.jos1_b = params[2] self.jos1_a = params[3] self.f1 = params[4] self.d1 = params[5] self.jos2_b = params[6] self.jos2_a = params[7] self.f2 = params[8] self.d2 = params[9] self.w = params[10] self.dCap = params[11] self.gap = params[12] self.square_a = params[13] self.dSquares = params[14] self.alum_over = params[15] self.B1_width = params[16] # calculated parameters self.B2_width = self.a + self.jos1_a / 2 - self.jos2_a / 2 - self.w self.qbit_width = 2 * self.w + self.B2_width self.B3_width = self.b - self.jos2_a / 2 - self.jos1_a / 2 - 2 * self.w self.B1_height = (self.dSquares - 2 * self.w - self.b) / 2 self._alpha_1 = 0.5 self._length1 = (self.b + 2 * self.w + self.jos1_b) self._alpha_2 = 0.5 self._length2 = (self.b + 2 * self.w - 2 * self.f2 - self.jos2_b ) #length self._length_right = (self.b + 2 * self.w - 2 * self.jos2_b) / 3 self.p0 = DPoint(0, 0) self.p1 = self.p0 - DPoint(0, self.dCap + self.square_a) self.p2 = self.p1 + DPoint(self.square_a / 2 - self.qbit_width / 2, -(self.dCap + self.B1_height)) self.p3 = self.p2 + DPoint(self.qbit_width - self.w, 0) self.p4 = self.p2 + DPoint(self.w, -self.w) self.p5 = self.p3 + DPoint(2 * self.w + self.jos2_a, -(2 * self._length_right + 2 * self.jos2_b)) self.p6 = self.p3 - DPoint(0, self.b + 2 * self.w) self.p7 = self.p6 - DPoint(self.B3_width, 0) self.p8 = self.p7 - DPoint(self.w, self.B1_height) self.p9 = self.p1 - DPoint( 0, self.square_a + self.b + 2 * self.B1_height + 2 * self.w) self.SQ1 = pya.DBox(self.p1, self.p1 + DPoint(self.square_a, self.square_a)) self._B1p1 = self.p2 + DPoint( (self.B2_width + 2 * self.w) / 2 - self.B1_width / 2, 0) self.B1 = pya.DBox( self._B1p1, self._B1p1 + DPoint(self.B1_width, self.B1_height + self.alum_over)) self.B2 = pya.DBox(self.p4, self.p3) self.B3 = pya.DBox(self.p7, self.p7 + DPoint(self.B3_width, self.w)) self._B4p1 = self.p8 + DPoint( (self.B3_width + 2 * self.w) / 2 - self.B1_width / 2, -self.alum_over) self.B4 = pya.DBox( self._B4p1, self._B4p1 + DPoint(self.B1_width, self.B1_height + self.alum_over)) self.SQ2 = pya.DBox(self.p9, self.p9 + DPoint(self.square_a, self.square_a)) self.poly_1 = self._make_polygon(self._length1 * self._alpha_1, self.w, self.d1, self.f1, self.jos1_b) self.poly_1.transform(DCplxTrans(1.0, 270, False, self.p2)) self.poly_2 = self._make_polygon(self._length1 * (1 - self._alpha_1), self.w, self.d1, self.f1, self.jos1_b) self.poly_2.transform(DCplxTrans(1.0, 90, False, self.p7)) self.poly_3 = self._make_polygon(self._length_right + self.jos2_b, self.w, self.d2, self.f2, self.jos2_b) self.poly_3.transform(DCplxTrans(1.0, 270, False, self.p3)) self.poly_4 = self._make_polygon( self._length_right + self.jos2_b - self.f2, self.w, self.d2, self.f2, self.jos2_b) self.poly_4.transform( DCplxTrans( 1.0, 270, True, self.p5 + DPoint(0, self._length_right + self.jos2_b - self.f2))) _poly_tmp = self._make_polygon( self._length_right + self.jos2_b - self.f2, self.w, self.d2, self.f2, self.jos2_b) _poly_tmp.transform( DCplxTrans(1.0, 90, False, self.p5 + DPoint(0, self.jos2_b + self.f2))) _reg_tmp4 = Region() _reg_tmp4.insert(SimplePolygon().from_dpoly(self.poly_4)) _reg_tmp = Region() _reg_tmp.insert(SimplePolygon().from_dpoly(_poly_tmp)) self._reg_tmp_to_metal = (_reg_tmp + _reg_tmp4).merged() self.poly_5 = self._make_polygon(self._length_right + self.jos2_b, self.w, self.d2, self.f2, self.jos2_b) self.poly_5.transform(DCplxTrans(1.0, 90, True, self.p6)) super().__init__(origin, trans_in)
def produce_impl(self): import pya from operator import xor from math import pi, cos, sin from pya import Region, Polygon import math #fetch parameters dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes #Layers definitions LayerSi = self.layer LayerSiN = ly.layer(self.LayerSi) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) #LayerTextN = ly.layer(LayerText) # cell: layout cell to place the layout # LayerSiN: which layer to use # x, y: location of the origin # r: radius # w: waveguide width # length units in dbu x = 0 w_top = self.w_top / dbu r = self.radius / dbu w_bot = self.w_bot / dbu deltaW = w_bot - w_top nptsFactor = 12 # function to generate points to create circle def circle(x, w_top, r): npts = 32 * nptsFactor theta = 2 * math.pi / npts # increment, in radians pts = [] for i in range(0, npts): pts.append( Point.from_dpoint( DPoint((x + r * math.cos(i * theta)) / 1, (w_top + r * math.sin(i * theta)) / 1))) return pts # function to generate points to create innercircle def inner_circle(x, w_top, r): npts = 32 * nptsFactor theta = 2 * math.pi / npts # increment, in radians pts = [] for i in range(0, npts): pts.append( Point.from_dpoint( DPoint((x + r * math.cos(i * theta)) / 1, (w_top + r * math.sin(i * theta)) / 1))) return pts # Outer circle x = 0 y = 0 r_out = r + w_top / 2 ring = Region() ring_cell = circle(0, 0, r_out) ring_poly = Polygon(ring_cell) #ring_t = ring_poly.transformed(Trans(x,y)) ring.insert(ring_poly) # Inner erasing circle r_in = r - (w_top) / 2 - deltaW / 2 x = 0 y = -deltaW / 2 # Inner Circle hole = Region() hole_cell = inner_circle(x, y, r_in) hole_poly = Polygon(hole_cell) #hole_t = hole_poly.transformed(Trans(x,y)) hole.insert(hole_poly) # perform inversion: phc = ring - hole self.cell.shapes(LayerSiN).insert(phc) # inversion can also be done by performing the XOR function # upper_poly = xor(Region(Polygon(circleu_pts)),Region(Polygon(rectu_pts))) return "Tapered_Ring(R=" + ('%.3f-%.3f' % (r, w_bot)) + ")"