Example full chip design

[1]:
%load_ext autoreload
%autoreload 2

Make sure to have the right kernel selected!

[2]:
import qiskit_metal as metal
from qiskit_metal import designs, draw
from qiskit_metal import MetalGUI, Dict, open_docs

%metal_heading Welcome to Qiskit Metal!

Welcome to Qiskit Metal!

Welcome to Qiskit Metal!

For this example tutorial, we will attempt to create a multi qubit chip with a variety of components. We will want to generate the layout, simulate/analyze and tune the chip to hit the parameters we are wanting, finally rendering to a GDS file.

One could generate subsections of the layout and tune individual components first, but in this case we will create all of the layout. We will be using both transmon pockets and crossmons, meandered and simple transmission lines, capacitive couplers, and launchers for wirebond connections. So we will import these, and also create a design instance and launch the GUI.

Layout

[3]:
from qiskit_metal.qlibrary.qubits.transmon_pocket_6 import TransmonPocket6
from qiskit_metal.qlibrary.qubits.transmon_cross_fl import TransmonCrossFL

from qiskit_metal.qlibrary.couplers.tunable_coupler_01 import TunableCoupler01

from qiskit_metal.qlibrary.tlines.meandered import RouteMeander
from qiskit_metal.qlibrary.tlines.pathfinder import RoutePathfinder
from qiskit_metal.qlibrary.tlines.anchored_path import RouteAnchors

from qiskit_metal.qlibrary.lumped.cap_n_interdigital import CapNInterdigital
from qiskit_metal.qlibrary.couplers.cap_n_interdigital_tee import CapNInterdigitalTee
from qiskit_metal.qlibrary.couplers.coupled_line_tee import CoupledLineTee

from qiskit_metal.qlibrary.terminations.launchpad_wb import LaunchpadWirebond
from qiskit_metal.qlibrary.terminations.launchpad_wb_coupled import LaunchpadWirebondCoupled
[4]:
design = metal.designs.DesignPlanar()

gui = metal.MetalGUI(design)

Since we are likely to be making many changes while tuning and modifying our design, we will enable overwriting. We can also check all of the chip properties to see if we want to change the size or any other parameter.

[5]:
design.overwrite_enabled = True
design.chips.main
[5]:
{'material': 'silicon',
 'layer_start': '0',
 'layer_end': '2048',
 'size': {'center_x': '0.0mm',
  'center_y': '0.0mm',
  'center_z': '0.0mm',
  'size_x': '9mm',
  'size_y': '6mm',
  'size_z': '-750um',
  'sample_holder_top': '890um',
  'sample_holder_bottom': '1650um'}}
[6]:
design.chips.main.size.size_x = '11mm'
design.chips.main.size.size_y = '9mm'

The Qubits

We will add a collection of qubits. First we will place a transmon pocket with six connection pads. We can see any options the qubit qcomponent has to figure out what we might want to modify when creating the component. This will include the components default options (which the component designer included) as well as renderer options (which are added based on what renderers are present in Metal).

[7]:
TransmonPocket6.get_template_options(design)
[7]:
{'pos_x': '0um',
 'pos_y': '0um',
 'connection_pads': {},
 '_default_connection_pads': {'pad_gap': '15um',
  'pad_width': '125um',
  'pad_height': '30um',
  'pad_cpw_shift': '0um',
  'pad_cpw_extent': '25um',
  'cpw_width': '10um',
  'cpw_gap': '6um',
  'cpw_extend': '100um',
  'pocket_extent': '5um',
  'pocket_rise': '0um',
  'loc_W': '+1',
  'loc_H': '+1'},
 'chip': 'main',
 'pad_gap': '30um',
 'inductor_width': '20um',
 'pad_width': '455um',
 'pad_height': '90um',
 'pocket_width': '650um',
 'pocket_height': '650um',
 'orientation': '0',
 'hfss_wire_bonds': False,
 'q3d_wire_bonds': False,
 'hfss_inductance': '10nH',
 'hfss_capacitance': 0,
 'hfss_resistance': 0,
 'hfss_mesh_kw_jj': 7e-06,
 'q3d_inductance': '10nH',
 'q3d_capacitance': 0,
 'q3d_resistance': 0,
 'q3d_mesh_kw_jj': 7e-06,
 'gds_cell_name': 'my_other_junction'}
[8]:
options =  dict(
    pad_width = '425 um',
    pocket_height = '650um',
    connection_pads=dict(
        readout = dict(loc_W=0, loc_H=-1, pad_width = '80um', pad_gap = '50um'),
        bus_01 = dict(loc_W=-1, loc_H=-1, pad_width = '60um', pad_gap = '10um'),
        bus_02 = dict(loc_W=-1, loc_H=+1, pad_width = '60um', pad_gap = '10um'),
        bus_03 = dict(loc_W=0, loc_H=+1, pad_width = '90um', pad_gap = '30um'),
        bus_04 = dict(loc_W=+1, loc_H=+1, pad_width = '60um', pad_gap = '10um'),
        bus_05 = dict(loc_W=+1, loc_H=-1, pad_width = '60um', pad_gap = '10um')
    ))

q_main = TransmonPocket6(design,'Q_Main', options = dict(
        pos_x='0mm',
        pos_y='-1mm',
        gds_cell_name ='FakeJunction_01',
        hfss_inductance ='14nH',
        **options))

gui.rebuild()
gui.autoscale()

We then will add a mixture of additional qubits. This is not (though do not let me stop any experimental investigation) a design one would normally create for any experiment of computational purpose, but allows for having a mixture of different components on one chip.

[9]:
TransmonCrossFL.get_template_options(design)
[9]:
{'pos_x': '0um',
 'pos_y': '0um',
 'connection_pads': {},
 '_default_connection_pads': {'connector_type': '0',
  'claw_length': '30um',
  'ground_spacing': '5um',
  'claw_width': '10um',
  'claw_gap': '6um',
  'connector_location': '0'},
 'cross_width': '20um',
 'cross_length': '200um',
 'cross_gap': '20um',
 'orientation': '0',
 'layer': '1',
 'make_fl': True,
 'fl_options': {'t_top': '15um',
  't_offset': '0um',
  't_inductive_gap': '3um',
  't_width': '5um',
  't_gap': '3um'},
 'hfss_wire_bonds': False,
 'q3d_wire_bonds': False,
 'hfss_inductance': '10nH',
 'hfss_capacitance': 0,
 'hfss_resistance': 0,
 'hfss_mesh_kw_jj': 7e-06,
 'q3d_inductance': '10nH',
 'q3d_capacitance': 0,
 'q3d_resistance': 0,
 'q3d_mesh_kw_jj': 7e-06,
 'gds_cell_name': 'my_other_junction'}

We will add two crossmons with flux lines to the west side of the chip, which we will couple to each other using a tunable coupler. To make sure the various readout and control lines will have space to connect to launchers at the chip edge, we have to be mindful of where we place them, and making sure we have enough space for routing while avoiding cross talk.

[10]:
Q1 = TransmonCrossFL(design, 'Q1', options = dict(pos_x = '-2.75mm', pos_y='-1.8mm',
                                                 connection_pads = dict(
                                                     bus_01 = dict(connector_location = '180',claw_length ='95um'),
                                                     readout = dict(connector_location = '0')),
                                                 fl_options = dict()))

Q2 = TransmonCrossFL(design, 'Q2', options = dict(pos_x = '-2.75mm', pos_y='-1.2mm', orientation = '180',
                                                 connection_pads = dict(
                                                     bus_02 = dict(connector_location = '0',claw_length ='95um'),
                                                     readout = dict(connector_location = '180')),
                                                 fl_options = dict()))


tune_c_Q12 = TunableCoupler01(design,'Tune_C_Q12', options = dict(pos_x = '-2.81mm', pos_y = '-1.5mm',
                                                                  orientation=90, c_width='500um'))

gui.rebuild()
gui.autoscale()

We then will add three transmon pockets to the north side of the chip, with the intention of having them in a linear series of coupling to each other, as well as the ‘main’ qubit to the south.

[11]:
Q3 = TransmonPocket6(design,'Q3', options = dict(
        pos_x='-3mm',
        pos_y='0.5mm',
        gds_cell_name ='FakeJunction_01',
        hfss_inductance ='14nH',
        connection_pads = dict(
            bus_03 = dict(loc_W=0, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            bus_q3_q4 = dict(loc_W=1, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            readout = dict(loc_W=0, loc_H=1, pad_width = '80um', pad_gap = '50um'))))

Q4 = TransmonPocket6(design,'Q4', options = dict(
        pos_x='0mm',
        pos_y='1mm',
        gds_cell_name ='FakeJunction_01',
        hfss_inductance ='14nH',
        connection_pads = dict(
            bus_04 = dict(loc_W=0, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            bus_q3_q4 = dict(loc_W=-1, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            bus_q4_q5 = dict(loc_W=1, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            readout = dict(loc_W=0, loc_H=1, pad_width = '80um', pad_gap = '50um'))))

Q5 = TransmonPocket6(design,'Q5', options = dict(
        pos_x='3mm',
        pos_y='0.5mm',
        gds_cell_name ='FakeJunction_01',
        hfss_inductance ='14nH',
        connection_pads = dict(
            bus_05 = dict(loc_W=0, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            bus_q4_q5 = dict(loc_W=-1, loc_H=-1, pad_width = '80um', pad_gap = '15um'),
            readout = dict(loc_W=0, loc_H=1, pad_width = '80um', pad_gap = '50um'))))

The Busses

We now couple the qubits to each other, primarily using RouteMeander. Although one needs to run simulations to properly tune the line lengths for target frequencies, an initial estimate could be determined from the below method;

[12]:
from qiskit_metal.analyses.em.cpw_calculations import guided_wavelength

def find_resonator_length(frequency, line_width, line_gap, N):
    #frequency in GHz
    #line_width/line_gap in um
    #N -> 2 for lambda/2, 4 for lambda/4

    [lambdaG, etfSqrt, q] = guided_wavelength(frequency*10**9, line_width*10**-6,
                                              line_gap*10**-6, 750*10**-6, 200*10**-9)
    return str(lambdaG/N*10**3)+" mm"

As we are not worried about a creating a functional chip in this tutorial, we will give the resonators somewhat arbitraty lengths. First coupling the two crossmons to Q_Main.

[13]:
bus_01 = RouteMeander(design,'Bus_01', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='bus_01'),
                                                end_pin=Dict(
                                                    component='Q1',
                                                    pin='bus_01')
                                            ),
                                            lead=Dict(
                                                start_straight='125um',
                                                end_straight = '225um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '1305um'),
                                            fillet = "99um",
                                            total_length = '6mm'))

bus_02 = RouteMeander(design,'Bus_02', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='bus_02'),
                                                end_pin=Dict(
                                                    component='Q2',
                                                    pin='bus_02')
                                            ),
                                            lead=Dict(
                                                start_straight='325um',
                                                end_straight = '125um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '450um'),
                                            fillet = "99um",
                                            total_length = '6.4mm'))


gui.rebuild()

Then the three transmon pockets on the north side to Q_Main.

[14]:
bus_03 = RouteMeander(design,'Bus_03', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='bus_03'),
                                                end_pin=Dict(
                                                    component='Q3',
                                                    pin='bus_03')
                                            ),
                                            lead=Dict(
                                                start_straight='225um',
                                                end_straight = '25um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '6.8mm'))

#To help set the right spacing, jogs can be used to set some initially controlled routing paths
from collections import OrderedDict
jogs_start = OrderedDict()
jogs_start[0] = ["L", '250um']
jogs_start[1] = ["R", '200um']

jogs_end = OrderedDict()
jogs_end[0] = ["L", '600um']

bus_04 = RouteMeander(design,'Bus_04', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='bus_04'),
                                                end_pin=Dict(
                                                    component='Q4',
                                                    pin='bus_04')
                                            ),
                                            lead=Dict(
                                                start_straight='225um',
                                                #end_straight = '25um',
                                                start_jogged_extension=jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '150um'),
                                            fillet = "99um",
                                            total_length = '7.2mm'))

bus_05 = RouteMeander(design,'Bus_05', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='bus_05'),
                                                end_pin=Dict(
                                                    component='Q5',
                                                    pin='bus_05')
                                            ),
                                            lead=Dict(
                                                start_straight='225um',
                                                end_straight = '25um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '7.6mm'))

gui.rebuild()

Finally the three transmon pockets on the north side to each other. This concludes the interconnectivity between the qubits.

[15]:
bus_q3_q4 = RouteMeander(design,'Bus_Q3_Q4', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q3',
                                                    pin='bus_q3_q4'),
                                                end_pin=Dict(
                                                    component='Q4',
                                                    pin='bus_q3_q4')
                                            ),
                                            lead=Dict(
                                                start_straight='125um',
                                                end_straight = '125um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '6.4mm'))

bus_q4_q5 = RouteMeander(design,'Bus_Q4_Q5', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q4',
                                                    pin='bus_q4_q5'),
                                                end_pin=Dict(
                                                    component='Q5',
                                                    pin='bus_q4_q5')
                                            ),
                                            lead=Dict(
                                                start_straight='125um',
                                                end_straight = '25um'
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '6.8mm'))

gui.rebuild()

The Readouts and Control Lines

The intention for this design is to have the three north transmon pockets be multiplexed to one readout line. The crossmons to their own readouts, as well as Q_Main. The tunable coupler, and the two crossmons also have flux lines which need to be connected to launchers. First we will place the wirebond launchers at the edges of the chip.

[16]:
launch_qmain_read = LaunchpadWirebond(design, 'Launch_QMain_Read', options = dict(pos_x = '2mm', pos_y ='-4mm', orientation = '90'))

launch_q1_fl = LaunchpadWirebond(design, 'Launch_Q1_FL', options = dict(pos_x = '0mm', pos_y ='-4mm', orientation = '90',
                                                                        trace_width = '5um',
                                                                        trace_gap = '3um',))
launch_q1_read = LaunchpadWirebondCoupled(design, 'Launch_Q1_Read', options = dict(pos_x = '-2mm', pos_y ='-4mm', orientation = '90'))

launch_tcoup_fl = LaunchpadWirebond(design, 'Launch_TuneC_FL', options = dict(pos_x = '-4mm', pos_y ='-4mm', orientation = '90',
                                                                             trace_width = '5um',
                                                                             trace_gap = '3um',))
launch_tcoup_read = LaunchpadWirebondCoupled(design, 'Launch_TuneC_Read', options = dict(pos_x = '-5mm', pos_y ='-3mm', orientation = '0'))

launch_q2_read = LaunchpadWirebondCoupled(design, 'Launch_Q2_Read', options = dict(pos_x = '-5mm', pos_y ='-1mm', orientation = '0'))
launch_q2_fl = LaunchpadWirebond(design, 'Launch_Q2_FL', options = dict(pos_x = '-5mm', pos_y ='1mm', orientation = '0',
                                                                       trace_width = '5um',
                                                                        trace_gap = '3um',))

launch_nw = LaunchpadWirebond(design, 'Launch_NW',options = dict(pos_x = '-5mm', pos_y='3mm', orientation=0))
launch_ne = LaunchpadWirebond(design, 'Launch_NE',options = dict(pos_x = '5mm', pos_y='3mm', orientation=180))


gui.rebuild()

We then will add in the readout resonators for Q_Main, Q1, Q2 and the tuneable coupler. We will add a finger capacitor for the Q_Main readout, instead of just using the LaunchpadWirebondCoupled.

[17]:
#Main Readout


read_q_main_cap = CapNInterdigital(design,'Read_Q_Main_Cap', options = dict(pos_x = '2mm', pos_y ='-3.5mm', orientation = '0'))

jogs_end = OrderedDict()
jogs_end[0] = ["L", '600um']

jogs_start = OrderedDict()
jogs_start[0] = ["L", '250um']

read_q_main = RouteMeander(design,'Read_Q_Main', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q_Main',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Read_Q_Main_Cap',
                                                    pin='north_end')
                                            ),
                                            lead=Dict(
                                                start_straight='725um',
                                                end_straight = '625um',
                                                start_jogged_extension = jogs_start,
                                                end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '5.6mm'))

read_q_main_cap_launch = RoutePathfinder(design, 'Read_Q_Main_Cap_Launch', options = dict(hfss_wire_bonds = True,
                                                    pin_inputs = dict(
                                                        start_pin=Dict(
                                                    component='Read_Q_Main_Cap',
                                                    pin='south_end'),
                                                end_pin=Dict(
                                                    component='Launch_QMain_Read',
                                                    pin='tie')),
                                                lead=Dict(
                                                start_straight='0um',
                                                end_straight = '0um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            )))



gui.rebuild()
[18]:
#Crossmon's Readouts

jogs_end = OrderedDict()
jogs_end[0] = ["L", '600um']

jogs_start = OrderedDict()
jogs_start[0] = ["L", '250um']

read_q1 = RouteMeander(design,'Read_Q1', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q1',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Launch_Q1_Read',
                                                    pin='tie')
                                            ),
                                            lead=Dict(
                                                start_straight='250um',
                                                end_straight = '25um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '6.8mm'))


jogs_end = OrderedDict()
jogs_end[0] = ["L", '600um']

jogs_start = OrderedDict()
jogs_start[0] = ["L", '250um']

read_tunec = RouteMeander(design,'Read_TuneC', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Tune_C_Q12',
                                                    pin='Control'),
                                                end_pin=Dict(
                                                    component='Launch_TuneC_Read',
                                                    pin='tie')
                                            ),
                                            lead=Dict(
                                                start_straight='1525um',
                                                end_straight = '125um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '50um'),
                                            fillet = "99um",
                                            total_length = '5.8mm'))

jogs_end = OrderedDict()
jogs_end[0] = ["L", '600um']

jogs_start = OrderedDict()
jogs_start[0] = ["L", '250um']



read_q2 = RouteMeander(design,'Read_Q2', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q2',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Launch_Q2_Read',
                                                    pin='tie')
                                            ),
                                            lead=Dict(
                                                start_straight='350um',
                                                end_straight = '0um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '-450um'),
                                            fillet = "99um",
                                            total_length = '5.4mm'))


gui.rebuild()

Finishing off this section of the chip by connecting the flux lines to appropraite wirebond launch pads.

[19]:
#Crossmon flux lines

flux_line_Q1 = RoutePathfinder(design,'Flux_Line_Q1', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q1',
                                                    pin='flux_line'),
                                                end_pin=Dict(
                                                    component='Launch_Q1_FL',
                                                    pin='tie')),
                                            fillet = '99um',
                                            trace_width = '5um',
                                            trace_gap = '3um',
                                            #anchors = anchors
                                            ))


jogs_start = OrderedDict()
jogs_start[0] = ["L", '750um']

flux_line_tunec = RoutePathfinder(design,'Flux_Line_TuneC', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Tune_C_Q12',
                                                    pin='Flux'),
                                                end_pin=Dict(
                                                    component='Launch_TuneC_FL',
                                                    pin='tie')),
                                            lead=Dict(
                                                start_straight='875um',
                                                end_straight = '350um',
                                                start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            fillet = '99um',
                                            trace_width = '5um',
                                            trace_gap = '3um',
                                            #anchors = anchors
                                            ))


jogs_start = OrderedDict()
jogs_start[0] = ["L", '525um']
jogs_start[1] = ["R", '625um']

flux_line_Q2 = RoutePathfinder(design,'Flux_Line_Q2', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q2',
                                                    pin='flux_line'),
                                                end_pin=Dict(
                                                    component='Launch_Q2_FL',
                                                    pin='tie')),
                                            lead=Dict(
                                                start_straight='175um',
                                                end_straight = '150um',
                                                start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            fillet = '99um',
                                            trace_width = '5um',
                                            trace_gap = '3um',
                                            #anchors = anchors
                                            ))

gui.rebuild()

Shifting our focus now to the three transmon pockets in the north. As we want these to be multiplexed to a single readout line, we will add in a few three port components, such as the CoupledLineTee and CapNInterdigitalTee. Q3 will have an inductive coupling to the readout line (as we want a lambda/4 resonator), Q4 will have a simple gap capacitor, and Q5 will have an interdigitated capacitor.

[20]:
q3_read_T = CoupledLineTee(design,'Q3_Read_T', options=dict(pos_x = '-3mm', pos_y = '3mm',
                                                        orientation = '0',
                                                        coupling_length = '200um',
                                                        open_termination = False))
#We use finger count to set the width of the gap capacitance, -> N*cap_width + (N-1)*cap_gap
q4_read_T = CapNInterdigitalTee(design,'Q4_Read_T', options=dict(pos_x = '0mm', pos_y = '3mm',
                                                           orientation = '0',
                                                           finger_length = '0um',
                                                           finger_count = '8'))
q5_read_T = CapNInterdigitalTee(design,'Q5_Read_T', options=dict(pos_x = '3mm', pos_y = '3mm',
                                                           orientation = '0',
                                                           finger_length = '50um',
                                                           finger_count = '11'))

gui.rebuild()

We add in the readout resonators to each respective qubit.

[21]:
read_q3 = RouteMeander(design,'Read_Q3', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q3',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Q3_Read_T',
                                                    pin='second_end')
                                            ),
                                            lead=Dict(
                                                start_straight='150um',
                                                end_straight = '150um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '0um'),
                                            fillet = "99um",
                                            total_length = '5mm'))

read_q4 = RouteMeander(design,'Read_Q4', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q4',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Q4_Read_T',
                                                    pin='second_end')
                                            ),
                                            lead=Dict(
                                                start_straight='125um',
                                                end_straight = '125um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '0um'),
                                            fillet = "99um",
                                            total_length = '5.8mm'))

read_q5 = RouteMeander(design,'Read_Q5', options = dict(hfss_wire_bonds = True,
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q5',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Q5_Read_T',
                                                    pin='second_end')
                                            ),
                                            lead=Dict(
                                                start_straight='125um',
                                                end_straight = '125um',
                                                #start_jogged_extension = jogs_start,
                                                #end_jogged_extension = jogs_end
                                            ),
                                            meander=Dict(
                                                asymmetry = '0um'),
                                            fillet = "99um",
                                            total_length = '5.4mm'))

gui.rebuild()

We complete the layout by connecting the multiplexed readout line to the launchpads on either side of the chip.

[22]:
mp_tl_01 = RoutePathfinder(design, 'ML_TL_01', options = dict(hfss_wire_bonds = True,
                                                    pin_inputs = dict(
                                                    start_pin=Dict(
                                                        component='Launch_NW',
                                                        pin='tie'),
                                                    end_pin=Dict(
                                                        component='Q3_Read_T',
                                                        pin='prime_start'))
                                            ))

mp_tl_02 = RoutePathfinder(design, 'ML_TL_02', options = dict(hfss_wire_bonds = True,
                                                    pin_inputs = dict(
                                                    start_pin=Dict(
                                                        component='Q3_Read_T',
                                                        pin='prime_end'),
                                                    end_pin=Dict(
                                                        component='Q4_Read_T',
                                                        pin='prime_start'))
                                            ))

mp_tl_03 = RoutePathfinder(design, 'ML_TL_03', options = dict(hfss_wire_bonds = True,
                                                    pin_inputs = dict(
                                                    start_pin=Dict(
                                                        component='Q4_Read_T',
                                                        pin='prime_end'),
                                                    end_pin=Dict(
                                                        component='Q5_Read_T',
                                                        pin='prime_start'))
                                            ))

mp_tl_04 = RoutePathfinder(design, 'ML_TL_04', options = dict(hfss_wire_bonds = True,
                                                    pin_inputs = dict(
                                                    start_pin=Dict(
                                                        component='Q5_Read_T',
                                                        pin='prime_end'),
                                                    end_pin=Dict(
                                                        component='Launch_NE',
                                                        pin='tie'))
                                            ))

gui.rebuild()

With this, we have completed the construction of our layout.

Now, anyone familiar with chip design might find some of the location choices to be sub-optimal, with large sections of your chip left unused, or perhaps some CPW transmission lines running a bit closer to each other than would be ideal for avoiding cross talk concerns. These could be address by shifting the origin of your chip, or modifying component options to better compact your layout and alleviate crosstalk concerns.

For this tutorial, we aren’t too concerned how much space we may use up on our fictional chip, so we will instead continue on to analysis and tuning.

[23]:
gui.autoscale()
gui.screenshot()
../../_images/circuit-examples_full-design-flow-examples_Example-full-chip-design_44_0.png

Analyze

First we want to quickly look at the qubit parameters. Initial simulation and analysis is to use a lumped element approximation, by extracting the capacitance matrix of the qubit. We first analyze the qubit Q_Main, by first extracting the matrix and then using LOM analysis on it.

For starters, let’s select the appropriate QAnalysis class.

[24]:
from qiskit_metal.analyses.quantization import LOManalysis
c1 = LOManalysis(design, "q3d")

We can check if we wish to change any of the default options for the analysis. You should modify the number of passes and convergence based on the accuracy you require for your simulation.

Depending on the complexity of the simulation, it could take a minute, or multiple hours. It is generally best to start with a small number of max_passes if you are unsure, so can you get a sense on the timing. As each adaptive pass adds additional tetrahedrons, the simulation time per pass will increase significantly (as well as the amount of system memory necessary).

[25]:
c1.sim.setup
[25]:
{'name': 'Setup',
 'reuse_selected_design': True,
 'freq_ghz': 5.0,
 'save_fields': False,
 'enabled': True,
 'max_passes': 15,
 'min_passes': 2,
 'min_converged_passes': 2,
 'percent_error': 0.5,
 'percent_refinement': 30,
 'auto_increase_solution_order': True,
 'solution_order': 'High',
 'solver_type': 'Iterative'}
[26]:
c1.sim.setup.name = 'Tune_Q_Main'
c1.sim.setup.max_passes = 16
c1.sim.setup.min_converged_passes = 2
c1.sim.setup.percent_error = 0.05
c1.sim.setup
[26]:
{'name': 'Tune_Q_Main',
 'reuse_selected_design': True,
 'freq_ghz': 5.0,
 'save_fields': False,
 'enabled': True,
 'max_passes': 16,
 'min_passes': 2,
 'min_converged_passes': 2,
 'percent_error': 0.05,
 'percent_refinement': 30,
 'auto_increase_solution_order': True,
 'solution_order': 'High',
 'solver_type': 'Iterative'}

Next we will want to run the simulation for Q_Main. To obtain the complete capacitance matrix from this simulation, we will want to terminate the unconnected pins of Q_Main with opens, so that they are regarded as isolated charge islands in the simulation. You will need to list all of the pin names in the call. To recall them, one can look at the GUI, or check the pin dictionary attached to Q_Main: q_main.pins.keys().

[27]:
c1.sim.run(name="Q_Main", components=['Q_Main'], open_terminations=[('Q_Main', 'readout'), ('Q_Main', 'bus_01'),('Q_Main', 'bus_02'),('Q_Main', 'bus_03'),
                                      ('Q_Main', 'bus_04'), ('Q_Main', 'bus_05')])
INFO 09:51AM [connect_project]: Connecting to Ansys Desktop API...
INFO 09:51AM [load_ansys_project]:      Opened Ansys App
INFO 09:51AM [load_ansys_project]:      Opened Ansys Desktop v2020.2.0
INFO 09:51AM [load_ansys_project]:      Opened Ansys Project
        Folder:    C:/Ansoft/
        Project:   Project23
INFO 09:51AM [connect_design]: No active design found (or error getting active design).
INFO 09:51AM [connect]:          Connected to project "Project23". No design detected
INFO 09:51AM [connect_design]:  Opened active design
        Design:    Q_Main_q3d [Solution type: Q3D]
WARNING 09:51AM [connect_setup]:        No design setup detected.
WARNING 09:51AM [connect_setup]:        Creating Q3D default setup.
INFO 09:51AM [get_setup]:       Opened setup `Setup`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 09:51AM [get_setup]:       Opened setup `Tune_Q_Main`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 09:51AM [analyze]: Analyzing setup Tune_Q_Main
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpjks6h_rl.txt, C, , Tune_Q_Main:LastAdaptive, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 1, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpmpomn8hh.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 1, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpo1bs65q6.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 2, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp27ap2rm7.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 3, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpid24udcc.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 4, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpanpc3fp0.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 5, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp7xe__y8o.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 6, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpzbj0ejdi.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 7, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp_kgnraw6.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 8, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpa99kh4q1.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 9, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp_e2tr4z4.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 10, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpfth1uezt.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 11, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpzhz29lld.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 12, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp2ly1bynz.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 13, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmppd5sj06r.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 14, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpx3ryjtku.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 15, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpqgpyoudh.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 16, False
INFO 09:55AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp0ypb80s2.txt, C, , Tune_Q_Main:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 17, False

With the simulation completed, we can look at the capacitance matrix.

[28]:
c1.sim.capacitance_matrix
[28]:
bus_01_connector_pad_Q_Main bus_02_connector_pad_Q_Main bus_03_connector_pad_Q_Main bus_04_connector_pad_Q_Main bus_05_connector_pad_Q_Main ground_main_plane pad_bot_Q_Main pad_top_Q_Main readout_connector_pad_Q_Main
bus_01_connector_pad_Q_Main 41.02176 -0.24001 -0.11719 -0.04481 -0.07874 -29.00822 -9.26982 -1.06835 -0.69738
bus_02_connector_pad_Q_Main -0.24001 41.00041 -0.75929 -0.07826 -0.04484 -28.98620 -1.07415 -9.21369 -0.10640
bus_03_connector_pad_Q_Main -0.11719 -0.75929 45.49485 -0.75936 -0.11710 -31.95144 -1.30891 -9.60366 -0.15780
bus_04_connector_pad_Q_Main -0.04481 -0.07826 -0.75936 41.02315 -0.24004 -29.00650 -1.07408 -9.21787 -0.10629
bus_05_connector_pad_Q_Main -0.07874 -0.04484 -0.11710 -0.24004 41.00041 -28.98777 -9.26969 -1.06800 -0.69718
ground_main_plane -29.00822 -28.98620 -31.95144 -29.00650 -28.98777 308.85307 -33.31374 -32.77926 -31.47933
pad_bot_Q_Main -9.26982 -1.07415 -1.30891 -1.07408 -9.26969 -33.31374 95.77645 -31.23494 -7.05105
pad_top_Q_Main -1.06835 -9.21369 -9.60366 -9.21787 -1.06800 -32.77926 -31.23494 97.45815 -1.13125
readout_connector_pad_Q_Main -0.69738 -0.10640 -0.15780 -0.10629 -0.69718 -31.47933 -7.05105 -1.13125 42.10847

But more importantly, we can use that matrix to run LOM analysis.

[29]:
c1.setup.junctions = Dict({'Lj': 14, 'Cj': 2})
c1.setup.freq_readout = 7.0
c1.setup.freq_bus = [5.6, 5.7, 5.8, 5.9, 6.0]  # list of the bus frequencies

c1.run_lom()
c1.lumped_oscillator_all
[6, 7] [8 0 1 2 3 4]
Predicted Values

Transmon Properties
f_Q 4.963607 [GHz]
EC 298.739021 [MHz]
EJ 11.671114 [GHz]
alpha -351.473280 [MHz]
dispersion 85.768110 [KHz]
Lq 13.994355 [nH]
Cq 64.839966 [fF]
T1 139.259252 [us]

**Coupling Properties**

tCqbus1 3.015572 [fF]
gbus1_in_MHz 43.820486 [MHz]
χ_bus1 -0.287307 [MHz]
1/T1bus1 223.182422 [Hz]
T1bus1 713.115943 [us]

tCqbus2 4.171076 [fF]
gbus2_in_MHz 48.504025 [MHz]
χ_bus2 -2.645931 [MHz]
1/T1bus2 287.292183 [Hz]
T1bus2 553.982853 [us]

tCqbus3 -3.999772 [fF]
gbus3_in_MHz -47.339949 [MHz]
χ_bus3 -1.980823 [MHz]
1/T1bus3 204.154172 [Hz]
T1bus3 779.582126 [us]

tCqbus4 -4.073126 [fF]
gbus4_in_MHz -49.034442 [MHz]
χ_bus4 -1.716249 [MHz]
1/T1bus4 169.570131 [Hz]
T1bus4 938.578876 [us]

tCqbus5 -4.001869 [fF]
gbus5_in_MHz -49.021470 [MHz]
χ_bus5 -1.415561 [MHz]
1/T1bus5 135.025422 [Hz]
T1bus5 1178.703542 [us]

tCqbus6 4.171183 [fF]
gbus6_in_MHz 51.958983 [MHz]
χ_bus6 -1.335697 [MHz]
1/T1bus6 123.643673 [Hz]
T1bus6 1287.206527 [us]
Bus-Bus Couplings
gbus1_2 3.325733 [MHz]
gbus1_3 1.912509 [MHz]
gbus1_4 2.174045 [MHz]
gbus1_5 1.979171 [MHz]
gbus1_6 3.559683 [MHz]
gbus2_3 2.144945 [MHz]
gbus2_4 2.034516 [MHz]
gbus2_5 1.819268 [MHz]
gbus2_6 1.928359 [MHz]
gbus3_4 3.378480 [MHz]
gbus3_5 1.912662 [MHz]
gbus3_6 1.881624 [MHz]
gbus4_5 3.496435 [MHz]
gbus4_6 2.177719 [MHz]
gbus5_6 2.376495 [MHz]
[29]:
fQ EC EJ alpha dispersion gbus chi_in_MHz χr MHz gr MHz
1 5.334856 349.017036 11.671114 -418.169804 304.17916 [46.33925002733205, 44.187331992065, -42.60569... [-0.5299131495471634, -9.027355440831576, -5.3... 0.529913 46.339250
2 5.293479 343.185353 11.671114 -410.316354 266.531163 [44.48347532561871, 43.91119291374231, -40.557... [-0.46063358481393896, -7.215266047760587, -4.... 0.460634 44.483475
3 5.17092 326.250466 11.671114 -387.687301 177.943279 [43.075350634431345, 44.952056360711815, -42.4... [-0.3648586676742546, -4.484695488473718, -2.8... 0.364859 43.075351
4 5.108373 317.801003 11.671114 -376.493737 143.687618 [43.13448774561514, 43.30507017705921, -44.091... [-0.3364016456250303, -3.3214019525977982, -2.... 0.336402 43.134488
5 5.074637 313.297364 11.671114 -370.553464 127.750448 [43.277211774574475, 45.44982592921459, -43.97... [-0.3238225188245248, -3.2664264401609686, -2.... 0.323823 43.277212
6 5.041406 308.897746 11.671114 -364.767715 113.602489 [43.129687798475445, 46.338514084696286, -45.2... [-0.3078723909186688, -3.0514480686225833, -2.... 0.307872 43.129688
7 5.023692 306.56726 11.671114 -361.709895 106.643367 [43.19413642807277, 46.657953431831764, -45.44... [-0.3017295453629039, -2.9276804873799436, -2.... 0.301730 43.194136
8 5.008631 304.593954 11.671114 -359.124452 101.0268 [43.35990049055785, 46.99778023673387, -46.254... [-0.2981493080591482, -2.837011006665539, -2.1... 0.298149 43.359900
9 4.995019 302.81687 11.671114 -356.799016 96.177974 [43.46480555843615, 47.44716630141518, -46.470... [-0.29435169781569703, -2.77574777235584, -2.0... 0.294352 43.464806
10 4.989574 302.107679 11.671114 -355.871761 94.296886 [43.597944296844695, 47.654276741440626, -46.5... [-0.294078462456382, -2.7551199777315323, -2.0... 0.294078 43.597944
11 4.98144 301.050061 11.671114 -354.489758 91.547639 [43.550558591120705, 48.01342068567195, -46.89... [-0.2903720998583706, -2.7305526898818075, -2.... 0.290372 43.550559
12 4.975829 300.321846 11.671114 -353.538756 89.693095 [43.677893070257284, 48.10303127872349, -47.06... [-0.28996661844835814, -2.6961457251648193, -2... 0.289967 43.677893
13 4.97308 299.965387 11.671114 -353.073412 88.796586 [43.74422948775013, 48.268028637087, -47.10813... [-0.28981954135701093, -2.693030802856854, -2.... 0.289820 43.744229
14 4.969275 299.47245 11.671114 -352.430083 87.568934 [43.73302237009102, 48.38885048108347, -47.226... [-0.2882553488551673, -2.6768170445299937, -2.... 0.288255 43.733022
15 4.966898 299.164667 11.671114 -352.028505 86.809485 [43.77570019556721, 48.46146286708871, -47.343... [-0.2879360055703413, -2.6664620128030103, -1.... 0.287936 43.775700
16 4.963607 298.739021 11.671114 -351.47328 85.76811 [43.82048616757409, 48.50402461483827, -47.339... [-0.2873072774566821, -2.6459305257029584, -1.... 0.287307 43.820486

Which gives us the qubits frequency, anharmonicity, and coupling strength to the different connection pads.

We can further check if these parameters converged well. If they have not, we may want to modify our simulation in order to get a more accurate result. We then will want to make modifications to our qubit options, such as pad_gap of the qubit, or modifying the size of the connection pads, in order to hit the desired qubit anharmonicity or readout chi values respectively.

[30]:
c1.plot_convergence();
c1.plot_convergence_chi()
INFO 09:55AM [hfss_report_full_convergence]: Creating report for variation 0
Design "Q_Main_q3d" info:
        # eigenmodes    0
        # variations    1

Once the analysis and tuning is complete, we can stop the analysis and the renderer.

[31]:
c1.sim.close()

Once each of our qubits have been run through LOM, we can begin to look at the resonant busses and readouts, and larger coupled sections of the chip. One such case could be looking at Q_Main, Q5, and Bus_05. This allows us not only to look at some of the parameters of the individual qubits, but also the bus frequency and if the qubits are coupled (via the bus) to the degree we wish.

Preparations

We will setup the design and simulation in the same manner as we did previously, but with the methods needed for an eigenmode simulation.

[32]:
from qiskit_metal.analyses.quantization import EPRanalysis
eig_qb = EPRanalysis(design, "hfss")

(optional) you can tune the wirebond size by directly modifying the renderer options.

[33]:
eig_qb.sim.renderer.options['wb_size'] = 5

Review and update the simulation setup by executing following two cells.

[34]:
em_p = eig_qb.sim.setup
[35]:
em_p.name = '3Modes'
em_p.min_freq_ghz = 4
em_p.n_modes = 3
em_p.max_passes = 10
em_p.max_delta_f = 0.1
em_p.min_converged = 2
# Design variables can also be added in for direct simulation sweeps.
em_p.vars = Dict({'Lj1': '13 nH', 'Cj1': '0 fF', 'Lj2': '15 nH', 'Cj2': '0 fF'})

eig_qb.sim.setup
[35]:
{'name': '3Modes',
 'reuse_selected_design': True,
 'min_freq_ghz': 4,
 'n_modes': 3,
 'max_delta_f': 0.1,
 'max_passes': 10,
 'min_passes': 1,
 'min_converged': 2,
 'pct_refinement': 30,
 'basis_order': 1,
 'vars': {'Lj1': '13 nH', 'Cj1': '0 fF', 'Lj2': '15 nH', 'Cj2': '0 fF'}}

Before we execute the analysis, we want to update the design if needed. For example we may want to modify the junction inductance of the two qubits based on the previous LOM analysis, so they are near the desired frequency. Further, one may want to change the length of the bus after initial simulations to get it to the target frequency:

[36]:
q_main.options.hfss_inductance
[36]:
'14nH'
[37]:
Q5.options.hfss_inductance
[37]:
'14nH'
[38]:
q_main.options.hfss_inductance = '13nH'
Q5.options.hfss_inductance = '15nH'
bus_05.options.total_length = '7.5mm'

gui.rebuild()

We can now run the simulation on the specified layout. All unconnected pins are left as shorts, as we are only concerned about simulating the resonant mode of the three components listed.

[39]:
eig_qb.sim.run(name="QMain_Q5_Bus05", components=['Q_Main', 'Q5','Bus_05'], open_terminations=[])
INFO 09:55AM [connect_project]: Connecting to Ansys Desktop API...
INFO 09:55AM [load_ansys_project]:      Opened Ansys App
INFO 09:55AM [load_ansys_project]:      Opened Ansys Desktop v2020.2.0
INFO 09:55AM [load_ansys_project]:      Opened Ansys Project
        Folder:    C:/Ansoft/
        Project:   Project23
INFO 09:55AM [connect_design]:  Opened active design
        Design:    Q_Main_q3d [Solution type: Q3D]
INFO 09:55AM [get_setup]:       Opened setup `Setup`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 09:55AM [connect]:         Connected to project "Project23" and design "Q_Main_q3d" 😀

INFO 09:55AM [connect_design]:  Opened active design
        Design:    QMain_Q5_Bus05_hfss [Solution type: Eigenmode]
WARNING 09:55AM [connect_setup]:        No design setup detected.
WARNING 09:55AM [connect_setup]:        Creating eigenmode default setup.
INFO 09:55AM [get_setup]:       Opened setup `Setup`  (<class 'pyEPR.ansys.HfssEMSetup'>)
INFO 09:55AM [get_setup]:       Opened setup `3Modes`  (<class 'pyEPR.ansys.HfssEMSetup'>)
INFO 09:55AM [analyze]: Analyzing setup 3Modes
09:59AM 09s INFO [get_f_convergence]: Saved convergences to C:\workspace\qiskit-metal\docs\circuit-examples\full-design-flow-examples\hfss_eig_f_convergence.csv

Once the simulation is complete, we can check to see if the convergence was good.

[40]:
eig_qb.sim.plot_convergences()

With the eigenmode simulation complete (and nicely converged) we can run some EPR analysis on the result.

At first we need to define the junctions in terms of name, inductance_variable, capacitance_variable, rectancle that was defined in the rendering to represent the junction port and line that was defined in the rendering to represent the direction of the current thorugh the junction.

[41]:
eig_qb.del_junction()
eig_qb.add_junction('jj1', 'Lj1', 'Cj1', rect='JJ_rect_Lj_Q_Main_rect_jj', line='JJ_Lj_Q_Main_rect_jj_')
eig_qb.add_junction('jj2', 'Lj2', 'Cj2', rect='JJ_rect_Lj_Q5_rect_jj', line='JJ_Lj_Q5_rect_jj_')
eig_qb.setup.sweep_variable = 'Lj1'
eig_qb.setup
[41]:
{'junctions': {'jj1': {'Lj_variable': 'Lj1',
   'Cj_variable': 'Cj1',
   'rect': 'JJ_rect_Lj_Q_Main_rect_jj',
   'line': 'JJ_Lj_Q_Main_rect_jj_'},
  'jj2': {'Lj_variable': 'Lj2',
   'Cj_variable': 'Cj2',
   'rect': 'JJ_rect_Lj_Q5_rect_jj',
   'line': 'JJ_Lj_Q5_rect_jj_'}},
 'dissipatives': {'dielectrics_bulk': ['main']},
 'cos_trunc': 8,
 'fock_trunc': 7,
 'sweep_variable': 'Lj1'}

Note in the previous cell output that the dissipatives have alrady been defined by default.

Now we can start looking at the EPR values. First we look at the electric field and subtrate participation. Then extract the kerr matrix.

[42]:
eig_qb.run_epr()
# (pyEPR allows to switch modes: eprd.set_mode(1))
Design "QMain_Q5_Bus05_hfss" info:
        # eigenmodes    3
        # variations    1
Design "QMain_Q5_Bus05_hfss" info:
        # eigenmodes    3
        # variations    1

        energy_elec_all       = 5.30807060349227e-24
        energy_elec_substrate = 4.86434514599116e-24
        EPR of substrate = 91.6%

        energy_mag    = 2.32419129800195e-26
        energy_mag % of energy_elec_all  = 0.4%


Variation 0  [1/1]

  Mode 0 at 4.81 GHz   [1/3]
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
               99.6%  2.654e-24 1.162e-26

    Calculating junction energy participation ration (EPR)
        method=`line_voltage`. First estimates:
        junction        EPR p_0j   sign s_0j    (p_capacitive)
                Energy fraction (Lj over Lj&Cj)= 97.68%
        jj1             3.29205e-06  (+)        7.83062e-08
                Energy fraction (Lj over Lj&Cj)= 97.33%
        jj2             0.994398  (+)        0.0272922
                (U_tot_cap-U_tot_ind)/mean=1.41%
Calculating Qdielectric_main for mode 0 (0/2)
p_dielectric_main_0 = 0.9164055095255939

  Mode 1 at 7.39 GHz   [2/3]
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
                0.1%  3.353e-24  3.35e-24

    Calculating junction energy participation ration (EPR)
        method=`line_voltage`. First estimates:
        junction        EPR p_1j   sign s_1j    (p_capacitive)
                Energy fraction (Lj over Lj&Cj)= 94.69%
        jj1             0.000383918  (+)        2.15125e-05
                Energy fraction (Lj over Lj&Cj)= 93.93%
        jj2             0.000723443  (+)        4.6774e-05
                (U_tot_cap-U_tot_ind)/mean=0.00%
Calculating Qdielectric_main for mode 1 (1/2)
p_dielectric_main_1 = 0.9185431895828703

  Mode 2 at 14.79 GHz   [3/3]
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
                0.0%  2.501e-24 2.501e-24

    Calculating junction energy participation ration (EPR)
        method=`line_voltage`. First estimates:
        junction        EPR p_2j   sign s_2j    (p_capacitive)
                Energy fraction (Lj over Lj&Cj)= 81.67%
        jj1             5.19757e-05  (+)        1.16685e-05
                Energy fraction (Lj over Lj&Cj)= 79.43%
        jj2             8.11545e-05  (+)        2.1022e-05
                (U_tot_cap-U_tot_ind)/mean=0.00%
Calculating Qdielectric_main for mode 2 (2/2)
p_dielectric_main_2 = 0.91725087597056
WARNING 09:59AM [__init__]: <p>Error: <class 'IndexError'></p>

ANALYSIS DONE. Data saved to:

C:\data-pyEPR\Project23\QMain_Q5_Bus05_hfss\2021-07-20 09-59-11.npz


         Differences in variations:



 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Variation 0

Starting the diagonalization
Finished the diagonalization
Pm_norm=
modes
0    1.028611
1    1.031457
2    1.123854
dtype: float64

Pm_norm idx =
     jj1    jj2
0  False   True
1  False  False
2  False  False
*** P (participation matrix, not normlz.)
        jj1       jj2
0  0.000003  0.967980
1  0.000384  0.000723
2  0.000052  0.000081

*** S (sign-bit matrix)
   s_jj1  s_jj2
0      1      1
1      1      1
2      1      1
*** P (participation matrix, normalized.)
   3.2e-06        1
   0.00038  0.00072
   5.2e-05  8.1e-05

*** Chi matrix O1 PT (MHz)
    Diag is anharmonicity, off diag is full cross-Kerr.
       264    0.588    0.132
     0.588 0.000408 0.000191
     0.132 0.000191 2.24e-05

*** Chi matrix ND (MHz)
       298      0.5    0.116
       0.5 0.000317 0.000173
     0.116 0.000173 2.28e-05

*** Frequencies O1 PT (MHz)
0     4550.033083
1     7388.261062
2    14788.982401
dtype: float64

*** Frequencies ND (MHz)
0     4533.470356
1     7388.272216
2    14788.988393
dtype: float64

*** Q_coupling
Empty DataFrame
Columns: []
Index: [0, 1, 2]

Mode frequencies (MHz)

Numerical diagonalization

Lj1 13
eigenmode
0 4550.03
1 7388.26
2 14788.98

Kerr Non-linear coefficient table (MHz)

Numerical diagonalization

0 1 2
Lj1
13 0 297.70 5.00e-01 1.16e-01
1 0.50 3.17e-04 1.73e-04
2 0.12 1.73e-04 2.28e-05

From the analysis results we can determine the qubits anharmonicities and coupling strength. Once the analysis and tuning is complete, we can close the connection to Ansys.

[43]:
eig_qb.sim.close()

Rendering to a GDS File

Once all of the tuning is complete, we will want to prepare a GDS file so we can create a mask and fabricate our chip. We first create a gds render instance.

[44]:
full_chip_gds = design.renderers.gds

The various options for the gds renderer can also be checked and changed as necessary. A key option is the gds file which holds the cells for your junction ebeam design. Make sure this is pointing at the correct file so they are placed in your final mask at the appropriate locations.

[45]:
full_chip_gds.options
[45]:
{'short_segments_to_not_fillet': 'True',
 'check_short_segments_by_scaling_fillet': '2.0',
 'gds_unit': 0.001,
 'ground_plane': 'True',
 'negative_mask': {'main': []},
 'corners': 'circular bend',
 'tolerance': '0.00001',
 'precision': '0.000000001',
 'width_LineString': '10um',
 'path_filename': '../resources/Fake_Junctions.GDS',
 'junction_pad_overlap': '5um',
 'max_points': '199',
 'cheese': {'datatype': '100',
  'shape': '0',
  'cheese_0_x': '25um',
  'cheese_0_y': '25um',
  'cheese_1_radius': '100um',
  'view_in_file': {'main': {1: True}},
  'delta_x': '100um',
  'delta_y': '100um',
  'edge_nocheese': '200um'},
 'no_cheese': {'datatype': '99',
  'buffer': '25um',
  'cap_style': '2',
  'join_style': '2',
  'view_in_file': {'main': {1: True}}},
 'bounding_box_scale_x': '1.2',
 'bounding_box_scale_y': '1.2'}
[46]:
full_chip_gds.options['path_filename'] ='../resources/Fake_Junctions.GDS'
full_chip_gds.options['no_cheese']['buffer']='50um'
[47]:
full_chip_gds.export_to_gds('Full_Chip_01.gds')
[47]:
1

(optional) With the design complete, we can close the GUI.

[48]:
# gui.main_window.close()

For more information, review the Introduction to Quantum Computing and Quantum Hardware lectures below

  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 1
Lecture Video Lecture Notes Lab
  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 2
Lecture Video Lecture Notes Lab
  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 3
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 1
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 2
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 3
Lecture Video Lecture Notes Lab