Source code for squadds.components.qubits

# -*- coding: utf-8 -*-
import numpy as np
from qiskit_metal import Dict, draw
from qiskit_metal.qlibrary.core import BaseQubit

from .jjs import JjDolan, SquidLoopDolan


[docs] class TransmonCross(BaseQubit): # pylint: disable=invalid-name """The base `TransmonCross` class. Inherits `BaseQubit` class. Simple Metal Transmon Cross object. Creates the X cross-shaped island, the "junction" on the south end, and up to 3 connectors on the remaining arms (claw or gap). 'claw_width' and 'claw_gap' define the width/gap of the CPW line that makes up the connector. Note, DC SQUID currently represented by single inductance sheet Add connectors to it using the `connection_pads` dictionary. See BaseQubit for more information. Sketch: Below is a sketch of the qubit :: claw_length Claw: _________ Gap: | ________________ _________ ____________ ______| | _________| |____________ | |________________ |_________ .. image:: transmon_cross.png .. meta:: Transmon Cross BaseQubit Default Options: * connection_pads: Empty Dict -- The dictionary which contains all active connection lines for the qubit. * _default_connection_pads: empty Dict -- The default values for the (if any) connection lines of the qubit. Default Options: * cross_width: '20um' -- Width of the CPW center trace making up the Crossmon * cross_length: '200um' -- Length of one Crossmon arm (from center) * cross_gap: '20um' -- Width of the CPW gap making up the Crossmon * _default_connection_pads: Dict * connector_type: '0' -- 0 = Claw type, 1 = gap type * claw_length: '30um' -- Length of the claw 'arms', measured from the connector center trace * ground_spacing: '5um' -- Amount of ground plane between the connector and Crossmon arm (minimum should be based on fabrication capabilities) * claw_width: '10um' -- The width of the CPW center trace making up the claw/gap connector * claw_gap: '6um' -- The gap of the CPW center trace making up the claw/gap connector * connector_location: '0' -- 0 => 'west' arm, 90 => 'north' arm, 180 => 'east' arm * style: 'default' -- Change the Josephson Junction style. NOTE TO USER: Non-default choices shouldn't be rendered for simulation. They are for GDS rendering. Choose from the following: * 'default' -- Used for simulation in Ansys * 'SQUID_LOOP_Dolan' -- Makes a SQUID loop in Dolan style. Use 'SQUID_LOOP' for options. * 'Dolan_JJ' -- Makes a Josephson Junction in Dolan style. Use 'Dolan_JJ' for options. * SQUID_LOOP: Dict * SQUID_width: '40um' -- Interior width (x-axis) of the SQUID LOOP * SQUID_length: '5um' -- Interior length (y-axis) of the SQUID LOOP * SQUID_offset: '5um' -- Distance between the edge of the bottom pad and the grounding plane across of the cross * JJ_width: '150nm' -- Josephson Junction width. Governs Lj. * JJ_flip: False -- Switch the direction of the Josephson Junctions. * Dolan_JJ: Dict * JJ_width: '150nm' -- Josephson Junction width. Governs Lj. * JJ_flip: False -- Switch the direction of the Josephson Junctions. """ default_options = Dict( cross_width='20um', cross_length='200um', cross_gap='20um', chip='main', _default_connection_pads=Dict( connector_type='0', # 0 = Claw type, 1 = gap type claw_length='30um', ground_spacing='5um', claw_width='10um', claw_gap='6um', claw_cpw_length='40um', claw_cpw_width='10um', connector_location= '0' # 0 => 'west' arm, 90 => 'north' arm, 180 => 'east' arm ), SQUID_LOOP = Dict(SQUID_width = '40um', SQUID_length = '5um', SQUID_offset = '5um', JJ_width = '150nm', JJ_flip = False), Dolan_JJ = Dict(JJ_width = '150nm', JJ_flip = False), style='default', layer='5') """Default options.""" component_metadata = Dict(short_name='Cross', _qgeometry_table_poly='True', _qgeometry_table_junction='True') """Component metadata""" TOOLTIP = """Simple Metal Transmon Cross used at LFL""" ##############################################MAKE######################################################
[docs] def make(self): """This is executed by the GUI/user to generate the qgeometry for the component.""" self.make_pocket() self.make_connection_pads()
###################################TRANSMON#############################################################
[docs] def make_pocket(self): """Makes a basic Crossmon, 4 arm cross.""" # self.p allows us to directly access parsed values (string -> numbers) form the user option p = self.p cross_width = p.cross_width cross_length = p.cross_length cross_gap = p.cross_gap # access to chip name chip = p.chip # Creates the cross and the etch equivalent. cross_line = draw.shapely.ops.unary_union([ draw.LineString([(0, cross_length), (0, -cross_length)]), draw.LineString([(cross_length, 0), (-cross_length, 0)]) ]) cross = cross_line.buffer(cross_width / 2, cap_style=2) cross_etch = cross.buffer(cross_gap, cap_style=3, join_style=2) ### Choose JJ / SQUID Style ### # If we're working with a default style if (p.style == 'default'): jj = draw.LineString([(0, -cross_length), (0, -cross_length - cross_gap)]) rect_jj = draw.rotate(jj, p.orientation, origin=(0, 0)) rect_jj = draw.translate(jj, p.pos_x, p.pos_y) self.add_qgeometry('junction', dict(rect_jj=rect_jj), width=cross_width, chip=chip) elif (p.style == 'SQUID_LOOP_Dolan') or (p.style == 'JJ_Dolan'): # LFL Standard Parameters pad_width = 0.010 #mm SQUID_thickness = 0.002 #mm jj1_pad04 = 0.0005 # Pad should extend 5um past base # Cut out pin structures from qubit T_pin = draw.Polygon([(0,0), (0.001,0), (0.001,0.002), (0.003,0.002), (0.003,0.004), (-0.003,0.004), (-0.003,0.002), (-0.001,0.002), (-0.001,0) ]) T_pin = draw.translate(T_pin, 0, -cross_length) cross = draw.subtract(cross, T_pin) ## If we're working w/ SQUID_LOOP_Dolan style if p.style == 'SQUID_LOOP_Dolan': # SQUID Parameters sl = p.SQUID_LOOP # Make the other T pin, goes on the outside T_pin2 = draw.rotate(T_pin, -90, origin=(0, -cross_length)) T_pin2 = draw.translate(T_pin2, cross_gap + cross_width/2, -cross_gap + pad_width/2 + sl.SQUID_offset) # Rotate and translate the JJ jjy = -p.cross_length - p.cross_gap + sl.SQUID_length / 2 + SQUID_thickness / 2 + pad_width /2 + sl.SQUID_offset jjxprime = -np.sin(np.radians(p.orientation)) * jjy jjyprime = np.cos(np.radians(p.orientation)) * jjy # Make the JJ auto generate to the TransmonCross jj_options = dict(pos_x = p.pos_x + jjxprime, pos_y = p.pos_y + jjyprime, orientation = 180 + p.orientation, SQUID_length = sl.SQUID_length, SQUID_width = sl.SQUID_width, stem1_length = cross_gap - sl.SQUID_offset - pad_width/2 - 1.5 * SQUID_thickness - sl.SQUID_length - jj1_pad04, stem2_length = cross_width/2 + cross_gap - jj1_pad04 - SQUID_thickness - sl.SQUID_width/2, JJ_flip = sl.JJ_flip, JJ_width = sl.JJ_width) SQUID_LOOP_Dolan(self.design, self.name + '_SQUID_LOOP_Dolan', options=jj_options) ### Add to QGeometry polys = T_pin2 polys = draw.rotate(polys, p.orientation, origin=(0, 0)) polys = draw.translate(polys, p.pos_x, p.pos_y) T_pin2 = polys self.add_qgeometry('poly', dict(T_pin2=T_pin2), subtract=True, chip=chip) ## If we're working w/ a JJ_Dolan Style else: # JJ Parameters pj = p.Dolan_JJ # Make the 2nd T shaped pin T_pin2 = draw.rotate(T_pin, 180, origin=(0, -(cross_length + cross_gap/2))) # Rotate and translate the JJ jjy = -cross_gap/2 - cross_length jjxprime = -np.sin(np.radians(p.orientation)) * jjy jjyprime = np.cos(np.radians(p.orientation)) * jjy # Make the JJ auto generate to the TransmonCross jj_options = Dict(pos_x = p.pos_x + jjxprime, pos_y = p.pos_y + jjyprime, orientation = p.orientation, bridge_length = cross_gap - 2 * jj1_pad04, JJ_width = pj.JJ_width) if (pj.JJ_flip == True): jj_options.orientation = p.orientation + 180 JjDolan(self.design, self.name + '_JJ_Dolan', options=jj_options) ### Add QGeometries polys = T_pin2 polys = draw.rotate(polys, p.orientation, origin=(0, 0)) polys = draw.translate(polys, p.pos_x, p.pos_y) T_pin2 = polys self.add_qgeometry('poly', dict(T_pin2=T_pin2), subtract=True, chip=chip) # Handling for future styles??! else: raise ValueError("You entered an invalid 'options.style'. Choose from 'default', 'SQUID_LOOP_Dolan', or 'JJ_Dolan'") #rotate and translate polys = [cross, cross_etch] polys = draw.rotate(polys, p.orientation, origin=(0, 0)) polys = draw.translate(polys, p.pos_x, p.pos_y) [cross, cross_etch] = polys # generate qgeometry self.add_qgeometry('poly', dict(cross=cross), chip=chip) self.add_qgeometry('poly', dict(cross_etch=cross_etch), subtract=True, chip=chip)
############################CONNECTORS##################################################################################################
[docs] def make_connection_pads(self): """Goes through connector pads and makes each one.""" for name in self.options.connection_pads: self.make_connection_pad(name)
[docs] def make_connection_pad(self, name: str): """Makes individual connector pad. Args: name (str) : Name of the connector pad """ # self.p allows us to directly access parsed values (string -> numbers) form the user option p = self.p cross_width = p.cross_width cross_length = p.cross_length cross_gap = p.cross_gap # access to chip name chip = p.chip pc = self.p.connection_pads[name] # parser on connector options c_g = pc.claw_gap c_l = pc.claw_length c_w = pc.claw_width c_c_w = pc.claw_cpw_width c_c_l = pc.claw_cpw_length g_s = pc.ground_spacing con_loc = pc.connector_location claw_cpw = draw.box(-c_w, -c_c_w / 2, -c_c_l - c_w, c_c_w / 2) if pc.connector_type == 0: # Claw connector t_claw_height = 2*c_g + 2 * c_w + 2*g_s + \ 2*cross_gap + cross_width # temp value claw_base = draw.box(-c_w, -(t_claw_height) / 2, c_l, t_claw_height / 2) claw_subtract = draw.box(0, -t_claw_height / 2 + c_w, c_l, t_claw_height / 2 - c_w) claw_base = claw_base.difference(claw_subtract) connector_arm = draw.shapely.ops.unary_union([claw_base, claw_cpw]) connector_etcher = draw.buffer(connector_arm, c_g) else: connector_arm = draw.box(0, -c_w / 2, -4 * c_w, c_w / 2) connector_etcher = draw.buffer(connector_arm, c_g) # Making the pin for tracking (for easy connect functions). # Done here so as to have the same translations and rotations as the connector. Could # extract from the connector later, but since allowing different connector types, # this seems more straightforward. port_line = draw.LineString([(-c_c_l - c_w, -c_c_w / 2), (-c_c_l - c_w, c_c_w / 2)]) claw_rotate = 0 if con_loc > 135: claw_rotate = 180 elif con_loc > 45: claw_rotate = -90 # Rotates and translates the connector polygons (and temporary port_line) polys = [connector_arm, connector_etcher, port_line] polys = draw.translate(polys, -(cross_length + cross_gap + g_s + c_g), 0) polys = draw.rotate(polys, claw_rotate, origin=(0, 0)) polys = draw.rotate(polys, p.orientation, origin=(0, 0)) polys = draw.translate(polys, p.pos_x, p.pos_y) [connector_arm, connector_etcher, port_line] = polys # Generates qgeometry for the connector pads self.add_qgeometry('poly', {f'{name}_connector_arm': connector_arm}, layer=p.layer, chip=chip) self.add_qgeometry('poly', {f'{name}_connector_etcher': connector_etcher}, subtract=True, chip=chip) self.add_pin(name, port_line.coords, c_c_w)
[docs] class TransmonCrossFL(TransmonCross): # pylint: disable=invalid-name """The base `TransmonCrossFL` class. Inherits `TransmonCross` class Description: Simple Metal Transmon Cross object. Creates the X cross-shaped island, the "junction" on the south end, and up to 3 connectors on the remaining arms (claw or gap). 'claw_width' and 'claw_gap' define the width/gap of the CPW line that makes up the connector. Note, DC SQUID currently represented by single inductance sheet Add connectors to it using the `connection_pads` dictonary. See BaseQubit for more information. Flux line is added by default to the 'south' arm where the DC SQUID is located, default is a symmetric T style Default Options: Convention: Values (unless noted) are strings with units included, (e.g., '30um') * make_fl - (Boolean) If True, adds a flux line * fl_style - (String) Choose a style to construct the flux line * "none" - Qiskit's default. * t_top - length of the flux line for mutual inductance to the SQUID * t_inductive_gap - amount of metallization between the flux line and SQUID * t_offset - degree by which the tail of the T is offset from the center * t_width - width of the flux line's transmission line center trace * t_gap - dielectric gap of the flux line's transmission line * "tapered" - Tapered / trapazoidal flux line. * t_length length of the flux line * t_width_i width of side further away from the qubit * t_width_f width of side closer to the qubit * t_gap_i dielectric gap of flux line's tranmission line, further away from qubit * t_gap_f dielectric gap of flux line's tranmission line, closer to qubit * t_punch_through length of how much the flux line punches through ground plane * t_hanger_para_length length of hanger which moves along the x-axis * t_hanger_para_width width of hanger which moves along the x-axis * t_hanger_para_gap dielectric gap of the x-axis hanger * t_hanger_perp_length length of hanger which moves along the y-axis * t_hanger_perp_width width of hanger which moves along the y-axis * t_hanger_perp_gap dielectric gap of the y-axis hanger .. image:: transmon_cross_fl.png .. meta:: Transmon Cross Flux Line """ component_metadata = Dict(short_name='Q', _qgeometry_table_poly='True', _qgeometry_table_path='True') """Component metadata""" default_options = Dict(make_fl=True, fl_style='default', fl_options=Dict(t_top='15um', t_offset='0um', t_inductive_gap='3um', t_width='5um', t_gap='3um'), LFL_options=Dict(t_width_i='10um', t_width_f='4.25um', t_length='90um', t_gap_i ='6um', t_gap_f ='2.5um', t_punch_through = '5um', t_hanger_para_length='40um', t_hanger_para_width='2.5um', t_hanger_para_gap = '2.5um', t_hanger_perp_length='55um', t_hanger_perp_width='2.7um', t_hanger_perp_gap = '3.2um')) """Default drawing options""" TOOLTIP = """The base `TransmonCrossFL` class."""
[docs] def make(self): """Define the way the options are turned into QGeometry.""" super().make() if self.options.make_fl == True: if (self.options.fl_style == "default") or (self.options.fl_style == None): self.make_flux_line() elif self.options.fl_style == "tapered": self.make_flux_line_tapered() else: pass
#####################################################################
[docs] def make_flux_line(self): """Creates the charge line if the user has charge line option to TRUE. This is Qiskit Metal's default. """ # Grab option values pf = self.p.fl_options p = self.p #Make the T flux line h_line = draw.LineString([(-pf.t_top / 2, 0), (pf.t_top / 2, 0)]) v_line = draw.LineString([(pf.t_offset, 0), (pf.t_offset, -0.03)]) parts = [h_line, v_line] # Move the flux line down to the SQUID parts = draw.translate( parts, 0, -(p.cross_length + p.cross_gap + pf.t_inductive_gap + pf.t_width / 2 + pf.t_gap)) # Rotate and translate based on crossmon location parts = draw.rotate(parts, p.orientation, origin=(0, 0)) parts = draw.translate(parts, p.pos_x, p.pos_y) [h_line, v_line] = parts # Adding to qgeometry table self.add_qgeometry('path', { 'h_line': h_line, 'v_line': v_line }, width=pf.t_width, layer=p.layer) self.add_qgeometry('path', { 'h_line_sub': h_line, 'v_line_sub': v_line }, width=pf.t_width + 2 * pf.t_gap, subtract=True, layer=p.layer) # Generating pin pin_line = v_line.coords self.add_pin("flux_line", points=pin_line, width=pf.t_width, gap=pf.t_gap, input_as_norm=True)
#####################################################################
[docs] def make_flux_line_tapered(self): """ Creates the charge line which is taper w/ a hanging bar. Activates when all of these conditions are met: - self.options.make_fl == True - self.options.fl_options.lfl_style == "tapered" """ # Grab option values pf = self.p.LFL_options p = self.p ##### Fast Flux Line Element ##### # Make tappered FF Line tapper = draw.Polygon([(pf.t_width_i / 2, 0), (- pf.t_width_i/2, 0), (-pf.t_width_f / 2, pf.t_length), (-pf.t_width_f/2 + pf.t_hanger_para_length, pf.t_length), (-pf.t_width_f/2 + pf.t_hanger_para_length, pf.t_length - pf.t_hanger_perp_length), (-pf.t_width_f/2 + pf.t_hanger_para_length - pf.t_hanger_perp_width, pf.t_length - pf.t_hanger_perp_length), (-pf.t_width_f/2 + pf.t_hanger_para_length - pf.t_hanger_perp_width, pf.t_length - pf.t_hanger_para_width), ((pf.t_length - pf.t_hanger_para_width)*(pf.t_width_f - pf.t_width_i) / (2 * pf.t_length) + pf.t_width_i/2, pf.t_length - pf.t_hanger_para_width)]) # Make subtraction sub_tapper = draw.Polygon([(pf.t_width_i / 2 + pf.t_gap_i, 0), (- pf.t_width_i/2 - pf.t_gap_i, 0), (-pf.t_width_f / 2 - pf.t_gap_f, pf.t_length + pf.t_hanger_para_gap), (-pf.t_width_f/2 + pf.t_hanger_para_length + pf.t_hanger_perp_gap, pf.t_length + pf.t_hanger_para_gap), (-pf.t_width_f/2 + pf.t_hanger_para_length + pf.t_hanger_perp_gap, pf.t_length - pf.t_hanger_perp_length), (-pf.t_width_f/2 + pf.t_hanger_para_length - pf.t_hanger_perp_width - pf.t_hanger_perp_gap, pf.t_length - pf.t_hanger_perp_length), (-pf.t_width_f/2 + pf.t_hanger_para_length - pf.t_hanger_perp_width - pf.t_hanger_perp_gap, pf.t_length - pf.t_hanger_para_width - pf.t_hanger_para_gap), ((pf.t_length - pf.t_hanger_para_width - pf.t_hanger_para_gap)/((pf.t_length - pf.t_hanger_para_gap)/((pf.t_width_f - pf.t_width_i)/2 + pf.t_gap_f - pf.t_gap_i)) + pf.t_width_i / 2 + pf.t_gap_i,pf.t_length - pf.t_hanger_para_width - pf.t_hanger_para_gap)]) # Translate all the parts to the edge of the parts = [tapper, sub_tapper] parts = draw.translate( parts, 0, -(p.cross_length + p.cross_gap + pf.t_length - pf.t_punch_through)) parts = draw.rotate(parts, p.orientation, origin=(0, 0)) parts = draw.translate(parts, p.pos_x, p.pos_y) tapper, sub_tapper = parts ##### Adding to qgeometry table ##### self.add_qgeometry('poly', dict(tapper=tapper), layer=p.layer) self.add_qgeometry('poly', dict(sub_tapper=sub_tapper), subtract=True, layer=p.layer)