South Korea Qiskit Hackathon’20 - Qiskit Metal exercise

Preparation

[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 simple two qubit chip. 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 want a transmon (in this case choosing ones with charge lines), meandered and simple transmission lines, capacitor couplers, and launchers for wirebond connections. So we will import these, and also create a design instance and launch the GUI.

Design the layout

[3]:
from qiskit_metal.qlibrary.qubits.transmon_pocket_cl import TransmonPocketCL

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

from qiskit_metal.qlibrary.lumped.cap_3_interdigital import Cap3Interdigital
from qiskit_metal.qlibrary.terminations.launchpad_wb import LaunchpadWirebond
[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'}}

We will add the two qubits. We know we will want each qubit to have two connection pads, one for readout, and another for the connection to the other qubit. 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).

[6]:
TransmonPocketCL.get_template_options(design)
[6]:
{'pos_x': '0um',
 'pos_y': '0um',
 'connection_pads': {},
 '_default_connection_pads': {'pad_gap': '15um',
  'pad_width': '125um',
  'pad_height': '30um',
  'pad_cpw_shift': '5um',
  'pad_cpw_extent': '25um',
  'cpw_width': 'cpw_width',
  'cpw_gap': 'cpw_gap',
  'cpw_extend': '100um',
  'pocket_extent': '5um',
  'pocket_rise': '65um',
  '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',
 'make_CL': True,
 'cl_gap': '6um',
 'cl_width': '10um',
 'cl_length': '20um',
 'cl_ground_gap': '6um',
 'cl_pocket_edge': '0',
 'cl_off_center': '50um',
 '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 then add those two qubits with the options we are wanting to define, these can all be modified easily later too. The rebuild command is included so the changes can be seen immediately in the GUI.

[7]:
options =  dict(
    pad_width = '425 um',
    pocket_height = '650um',
    cl_pocket_edge = '180',
    connection_pads=dict(
        readout = dict(loc_W=+1, loc_H=+1),
        bus = dict(loc_W=-1, loc_H=-1, )
    ))

Q1 = TransmonPocketCL(design,'Q1', options = dict(
        pos_x='0.7mm',
        pos_y='0mm',
        gds_cell_name ='FakeJunction_01',
        hfss_inductance ='14nH',
        **options))

Q2 = TransmonPocketCL(design,'Q2', options = dict(
        pos_x='-0.7mm',
        pos_y='0mm',
        gds_cell_name ='FakeJunction_02',
        hfss_inductance ='12nH',
        orientation = '180',
        **options))

gui.rebuild()
gui.autoscale()

We will next connect the two transmons together to form a bus using RoutePathfinder, an auto drawing CPW transmission line. We simply have to give the start and end location using the component pins of what we are trying to connect to. We can use the GUI to confirm the pin names we want to give as inputs.

Pins also play an important role with rendering and simulations, as any unconnected pin can be defined as a short, open, or driven terminal.

[8]:
bus_Q1_Q2 = RoutePathfinder(design, 'Bus_Q1_Q2', options = dict(
                                            fillet='99um',
                                            lead=dict(end_straight='250um'),
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q1',
                                                    pin='bus'),
                                                end_pin=Dict(
                                                    component='Q2',
                                                    pin='bus')
                                            )))

gui.rebuild()
gui.autoscale()

Then the readout structures can be added, being the capacitor couplers and meandered transmission lines to form the readout resonators.

[9]:
cap_Q1 = Cap3Interdigital(design, 'Cap_Q1', options= dict(pos_x='2.5mm', pos_y='0.25mm', orientation='90', finger_length = '40um'))
cap_Q2 = Cap3Interdigital(design, 'Cap_Q2', options= dict(pos_x='-2.5mm', pos_y='-0.25mm', orientation='-90', finger_length = '40um'))

gui.rebuild()
gui.autoscale()
[10]:
readout_Q1 = RouteMeander(design,'Readout_Q1', options = dict(
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q1',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Cap_Q1',
                                                    pin='a')
                                            ),
                                            lead=Dict(
                                                start_straight='0.325mm',
                                                end_straight = '125um'#put jogs here
                                            ),
                                            meander=Dict(
                                                asymmetry = '-50um'),
                                            fillet = "99um",
                                            total_length = '5mm'))

gui.rebuild()
gui.autoscale()
[11]:
readout_Q2 = RouteMeander(design,'Readout_Q2', options = dict(
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Q2',
                                                    pin='readout'),
                                                end_pin=Dict(
                                                    component='Cap_Q2',
                                                    pin='a')
                                            ),
                                            lead=Dict(
                                                start_straight='0.325mm',
                                                end_straight = '125um'#put jogs here
                                            ),
                                            meander=Dict(
                                                asymmetry = '-50um'),
                                            fillet = "99um",
                                            total_length = '6mm'))

gui.rebuild()
gui.autoscale()

With the launchers and short transmission lines to connect them to the capacitors and the charge lines.

[12]:
launch_Q1_read = LaunchpadWirebond(design, 'Launch_Q1_Read', options = dict(pos_x = '3.5mm', orientation = '180'))
launch_Q2_read = LaunchpadWirebond(design, 'Launch_Q2_Read', options = dict(pos_x = '-3.5mm', orientation = '0'))

launch_Q1_cl = LaunchpadWirebond(design, 'Launch_Q1_CL', options = dict(pos_x = '1.35mm', pos_y = '-2.5mm', orientation = '90'))
launch_Q2_cl = LaunchpadWirebond(design, 'Launch_Q2_CL', options = dict(pos_x = '-1.35mm', pos_y = '2.5mm', orientation = '-90'))

gui.rebuild()
gui.autoscale()
[13]:
tl_Q1 = RoutePathfinder(design, 'TL_Q1', options = dict(
                                            fillet='99um',
                                            lead=dict(end_straight='150um'),
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Launch_Q1_Read',
                                                    pin='tie'),
                                                end_pin=Dict(
                                                    component='Cap_Q1',
                                                    pin='b')
                                            )))

tl_Q2 = RoutePathfinder(design, 'TL_Q2', options = dict(
                                            fillet='99um',
                                            lead=dict(end_straight='150um'),
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Launch_Q2_Read',
                                                    pin='tie'),
                                                end_pin=Dict(
                                                    component='Cap_Q2',
                                                    pin='b')
                                            )))

gui.rebuild()
gui.autoscale()
[14]:
tl_Q1_cl = RoutePathfinder(design, 'TL_Q1_CL', options = dict(
                                            fillet='99um',
                                            lead=dict(end_straight='150um'),
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Launch_Q1_CL',
                                                    pin='tie'),
                                                end_pin=Dict(
                                                    component='Q1',
                                                    pin='Charge_Line')
                                            )))

tl_Q2_cl = RoutePathfinder(design, 'TL_Q2_CL', options = dict(
                                            fillet='99um',
                                            lead=dict(end_straight='150um'),
                                            pin_inputs=Dict(
                                                start_pin=Dict(
                                                    component='Launch_Q2_CL',
                                                    pin='tie'),
                                                end_pin=Dict(
                                                    component='Q2',
                                                    pin='Charge_Line')
                                            )))

gui.rebuild()
gui.autoscale()
[15]:
gui.screenshot()
../../_images/circuit-examples_full-design-flow-examples_Exercise-for-the-South-Korea-Hackathon%2720_24_0.png

Simulation and Analysis

With our fully designed chip now laid out, we can start to focus on tuning the components to hit the circuit parameters we are interested in. These will tend to be; - qubit: frequency, the anharmonicity/alpha, and the coupling strength (as chi, g, or other) - busses: frequency (if resonant), the coupling strength - readout: frequency, coupling strength, coupling to external lines (as kappa, Q_external, or other)

Analysis via capacitance matrix and LOM

All of the qubit paramters can initially be tuned via a capacitance matrix and the lumped oscillator method. This analysis is not as accurate as others, but allows for fairly fast and small simulations.

We will start by selecting the Analysis class of interest.

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

We can see what options are available for the analysis and make any changes we may wish, such as increasing the buffer size for the bounding box when simulation subsections of the chip.

[17]:
sim_p = c1.sim.setup
[18]:
sim_p.name = 'QubitTune'
sim_p.max_passes = 15
sim_p.min_converged_passes = 2
sim_p.percent_error = 0.1

c1.sim.setup
[18]:
{'name': 'QubitTune',
 '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.1,
 'percent_refinement': 30,
 'auto_increase_solution_order': True,
 'solution_order': 'High',
 'solver_type': 'Iterative'}

With the design and simulation setup, we render the qubit. For LOM analysis, we want the connection pads to be terminated with opens. We indicate this by stating which unconnected pins should have open terminations.

During the execution of the next cell, make sure to deal with any pop up boxes from Ansys as it opens up (if it was closed)

[19]:
c1.sim.run(name="Qubit1", components=['Q1'], open_terminations=[('Q1', 'readout'), ('Q1', 'bus')])
INFO 10:07AM [connect_project]: Connecting to Ansys Desktop API...
INFO 10:07AM [load_ansys_project]:      Opened Ansys App
INFO 10:07AM [load_ansys_project]:      Opened Ansys Desktop v2020.2.0
INFO 10:07AM [load_ansys_project]:      Opened Ansys Project
        Folder:    C:/Ansoft/
        Project:   Project23
INFO 10:07AM [connect_design]: No active design found (or error getting active design).
INFO 10:07AM [connect]:          Connected to project "Project23". No design detected
INFO 10:07AM [connect_design]:  Opened active design
        Design:    Qubit1_q3d [Solution type: Q3D]
WARNING 10:07AM [connect_setup]:        No design setup detected.
WARNING 10:07AM [connect_setup]:        Creating Q3D default setup.
INFO 10:07AM [get_setup]:       Opened setup `Setup`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 10:07AM [get_setup]:       Opened setup `QubitTune`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 10:07AM [analyze]: Analyzing setup QubitTune
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpihmmvyj4.txt, C, , QubitTune:LastAdaptive, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 1, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpwtqzabkl.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 1, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpw29pkf6r.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 2, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp_3iff39l.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 3, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpdcnewiuw.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 4, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpg4xhijml.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 5, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpjyj_jhhn.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 6, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpn_oauu6k.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 7, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpmub93rqq.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 8, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpmciuxb2g.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 9, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmps434g9vw.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 10, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmptwpcvf77.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 11, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpqfysr16x.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 12, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpsk74ngpm.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 13, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp0pzx9126.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 14, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmpcjbs3_xh.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 15, False
INFO 10:09AM [get_matrix]: Exporting matrix data to (C:\Temp\tmp88p7cm_q.txt, C, , QubitTune:AdaptivePass, "Original", "ohm", "nH", "fF", "mSie", 5000000000, Maxwell, 16, False

Once complete, we can grab the capacitance matrix (next cell) and/or (following cells) call on an LOM analysis of the simulation.

[20]:
c1.sim.capacitance_matrix
[20]:
bus_connector_pad_Q1 cl_metal_Q1 ground_main_plane pad_bot_Q1 pad_top_Q1 readout_connector_pad_Q1
bus_connector_pad_Q1 50.94746 -0.01089 -34.51380 -13.78934 -1.64509 -0.13815
cl_metal_Q1 -0.01089 16.45248 -15.95762 -0.22648 -0.13072 -0.05207
ground_main_plane -34.51380 -15.95762 223.34149 -39.67813 -39.72822 -34.46752
pad_bot_Q1 -13.78934 -0.22648 -39.67813 90.65573 -32.51137 -1.65418
pad_top_Q1 -1.64509 -0.13072 -39.72822 -32.51137 90.63398 -13.78691
readout_connector_pad_Q1 -0.13815 -0.05207 -34.46752 -1.65418 -13.78691 50.95160
[21]:
c1.setup.junctions = Dict({'Lj': 14, 'Cj': 2})
c1.setup.freq_readout = 7.0
c1.setup.freq_bus = [0.1, 0.1]  # list of the bus frequencies

c1.run_lom()
c1.lumped_oscillator_all
[3, 4] [5 0 1]
Predicted Values

Transmon Properties
f_Q 5.061570 [GHz]
EC 311.563069 [MHz]
EJ 11.671114 [GHz]
alpha -368.270728 [MHz]
dispersion 122.011358 [KHz]
Lq 13.994355 [nH]
Cq 62.171130 [fF]
T1 146.353071 [us]

**Coupling Properties**

tCqbus1 -6.070356 [fF]
gbus1_in_MHz -91.032968 [MHz]
χ_bus1 -1.408342 [MHz]
1/T1bus1 1087.472253 [Hz]
T1bus1 146.353107 [us]

tCqbus2 6.068136 [fF]
gbus2_in_MHz 1.305996 [MHz]
χ_bus2 -0.000106 [MHz]
1/T1bus2 0.000267 [Hz]
T1bus2 597183178.606260 [us]

tCqbus3 0.047788 [fF]
gbus3_in_MHz 0.010286 [MHz]
χ_bus3 -0.000000 [MHz]
1/T1bus3 0.000000 [Hz]
T1bus3 9627859915854.152344 [us]
Bus-Bus Couplings
gbus1_2 0.100231 [MHz]
gbus1_3 0.004458 [MHz]
gbus2_3 0.000039 [MHz]
[21]:
fQ EC EJ alpha dispersion gbus chi_in_MHz χr MHz gr MHz
1 5.433205 363.111929 11.671114 -437.282573 412.984875 [-83.73617714546809, 1.1128526337938494, 0.021... [-1.994068841431115, -7.989263976338063e-05, -... 1.994069 83.736177
2 5.359559 352.526273 11.671114 -422.910894 328.817583 [-85.55836719637072, 1.2075255039009807, 0.011... [-1.8712048806620007, -9.333269933033329e-05, ... 1.871205 85.558367
3 5.289608 342.642825 11.671114 -409.587339 263.228965 [-86.58355838924892, 1.2287794333424544, 0.009... [-1.7356985051848983, -9.594339235372308e-05, ... 1.735699 86.583558
4 5.225892 333.78405 11.671114 -397.721712 213.807793 [-86.0689723014061, 1.2277846691044672, 0.0100... [-1.5700613626258655, -9.516083595729247e-05, ... 1.570061 86.068972
5 5.181793 327.732575 11.671114 -389.657353 184.582809 [-86.93865718651006, 1.2380032996345856, 0.010... [-1.5083189493249503, -9.63170258686212e-05, -... 1.508319 86.938657
6 5.143493 322.529414 11.671114 -382.749903 162.123297 [-87.53699141755507, 1.2635437523017465, 0.010... [-1.4520753068740948, -9.994380459904714e-05, ... 1.452075 87.536991
7 5.122549 319.704668 11.671114 -379.010078 150.892951 [-88.90295086829036, 1.2722707773971214, 0.010... [-1.4563031695757316, -0.00010111569119596609,... 1.456303 88.902951
8 5.103385 317.132734 11.671114 -375.611156 141.225627 [-89.0288019265413, 1.2787672658122802, 0.0100... [-1.4235997603008703, -0.00010195477935975125,... 1.423600 89.028802
9 5.092977 315.741024 11.671114 -373.774404 136.208482 [-89.58507857804825, 1.2841634593028393, 0.010... [-1.421663253364802, -0.00010271006641627329, ... 1.421663 89.585079
10 5.084037 314.548466 11.671114 -372.201855 132.025608 [-89.75013779296071, 1.2893744190101681, 0.010... [-1.4101109966641798, -0.00010345292930883918,... 1.410111 89.750138
11 5.078996 313.877183 11.671114 -371.317232 129.717505 [-90.3167187695716, 1.295820610268069, 0.01029... [-1.4184853890828102, -0.00010443741877410954,... 1.418485 90.316719
12 5.072608 313.027676 11.671114 -370.198317 126.843837 [-90.53023682621938, 1.2988231682250144, 0.010... [-1.413230425615815, -0.00010485521159682165, ... 1.413230 90.530237
13 5.068138 312.434126 11.671114 -369.41691 124.866964 [-90.71509224226395, 1.3013751515724767, 0.010... [-1.4106702883237312, -0.00010522084928401761,... 1.410670 90.715092
14 5.065364 312.066053 11.671114 -368.932498 123.653727 [-90.92467056390035, 1.3048977251333183, 0.010... [-1.4120265412685657, -0.00010576206087240421,... 1.412027 90.924671
15 5.06157 311.563069 11.671114 -368.270728 122.011358 [-91.03296800373701, 1.305996499030395, 0.0102... [-1.4083417525016215, -0.00010590030281529206,... 1.408342 91.032968

We can finally observe how the main quantum parameters have converged after the previous simulation.

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

Note that it is possible for a (physically) converged simulation, to not have quite converged in the quantum realm.

Looking at the convergence plots from the previous cell, we can see that we did not have enough passes for the simulation. In this case, we want to modify our simulation setup, ‘QubitTune’, to improve our convergence (increase number of passes, or changing some other parameters).

Once we will know that simulation has converged, we can use those results to make appropriate changes to our qubit layout. Say our anharmonicity/alpha is higher than we want? Since we know Ec~ 1/C, we can change parts of the transmon pocket to lower the total capacitance seen across the junction. If g to the bus is too low, we can increase the width of the connection pad so that the capacitance between the connection pad and charge island is greater.

One must also keep in mind that any such changes will impact the other parameters, so careful tweaks and iterations of simulation/analysis often end up being necessary.

If wanting to make changes and re-render your design, you first should clear your current design.

[23]:
c1.sim.renderer.clean_active_design()

When done with all the simulations, you can disconnect from Ansys EDT.

[24]:
c1.sim.close()

Analysis via eigenmode and EPR

Now, although the previous simulation and analysis captured all of the parameters of the qubits and coupling to the bus, a more accurate (all be it slower) approach is to render the qubits and their coupling into an eigenmode simulation and perform EPR analysis on the result.

We again start by selecting the Analysis class of interest.

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

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

[26]:
em_p = eig_qb.sim.setup
[27]:
em_p.name = 'QubitTune'
em_p.min_freq_ghz = 3
em_p.n_modes = 2
em_p.max_passes = 12
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': '14 nH', 'Cj1': '0 fF', 'Lj2': '12 nH', 'Cj2': '0 fF'})

eig_qb.sim.setup
[27]:
{'name': 'QubitTune',
 'reuse_selected_design': True,
 'min_freq_ghz': 3,
 'n_modes': 2,
 'max_delta_f': 0.1,
 'max_passes': 12,
 'min_passes': 1,
 'min_converged': 2,
 'pct_refinement': 30,
 'basis_order': 1,
 'vars': {'Lj1': '14 nH', 'Cj1': '0 fF', 'Lj2': '12 nH', 'Cj2': '0 fF'}}

We can now run the simulation on the specified layout.

[28]:
eig_qb.sim.run(name="Q1_Q2_Bus", components=['Bus_Q1_Q2', 'Q1', 'Q2'], open_terminations=[])
INFO 10:10AM [connect_project]: Connecting to Ansys Desktop API...
INFO 10:10AM [load_ansys_project]:      Opened Ansys App
INFO 10:10AM [load_ansys_project]:      Opened Ansys Desktop v2020.2.0
INFO 10:10AM [load_ansys_project]:      Opened Ansys Project
        Folder:    C:/Ansoft/
        Project:   Project23
INFO 10:10AM [connect_design]:  Opened active design
        Design:    Qubit1_q3d [Solution type: Q3D]
INFO 10:10AM [get_setup]:       Opened setup `Setup`  (<class 'pyEPR.ansys.AnsysQ3DSetup'>)
INFO 10:10AM [connect]:         Connected to project "Project23" and design "Qubit1_q3d" 😀

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

Once completed, we can check the convergence to see if more passes might be necessary.

[29]:
eig_qb.sim.plot_convergences()

This eigenmode simulation is also a quick method to check for any bus or readout resonator frequencies. If happy with the simulation, we can then jump to some EPR analysis.

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.

[30]:
eig_qb.del_junction()
eig_qb.add_junction('jj1', 'Lj1', 'Cj1', rect='JJ_rect_Lj_Q1_rect_jj', line='JJ_Lj_Q1_rect_jj_')
eig_qb.add_junction('jj2', 'Lj2', 'Cj2', rect='JJ_rect_Lj_Q2_rect_jj', line='JJ_Lj_Q2_rect_jj_')
eig_qb.setup.sweep_variable = 'Lj1'
eig_qb.setup
[30]:
{'junctions': {'jj1': {'Lj_variable': 'Lj1',
   'Cj_variable': 'Cj1',
   'rect': 'JJ_rect_Lj_Q1_rect_jj',
   'line': 'JJ_Lj_Q1_rect_jj_'},
  'jj2': {'Lj_variable': 'Lj2',
   'Cj_variable': 'Cj2',
   'rect': 'JJ_rect_Lj_Q2_rect_jj',
   'line': 'JJ_Lj_Q2_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.

[31]:
eig_qb.run_epr()
# (pyEPR allows to switch modes: eprd.set_mode(1))
Design "Q1_Q2_Bus_hfss" info:
        # eigenmodes    2
        # variations    1
Design "Q1_Q2_Bus_hfss" info:
        # eigenmodes    2
        # variations    1

        energy_elec_all       = 8.21441028255477e-25
        energy_elec_substrate = 7.56817871884e-25
        EPR of substrate = 92.1%

        energy_mag    = 3.16335953678474e-27
        energy_mag % of energy_elec_all  = 0.4%


Variation 0  [1/1]

  Mode 0 at 5.35 GHz   [1/2]
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
               99.6%  4.107e-25 1.582e-27

    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)= 96.93%
        jj1             0.995427  (+)        0.0315155
                Energy fraction (Lj over Lj&Cj)= 97.36%
        jj2             3.30364e-05  (+)        8.96519e-07
                (U_tot_cap-U_tot_ind)/mean=1.59%
Calculating Qdielectric_main for mode 0 (0/1)
p_dielectric_main_0 = 0.9213295243984593

  Mode 1 at 5.78 GHz   [2/2]
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
               99.6%  7.231e-25  3.25e-27

    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)= 96.43%
        jj1             3.38351e-05  (+)        1.25157e-06
                Energy fraction (Lj over Lj&Cj)= 96.93%
        jj2             0.994201  (+)        0.0315222
                (U_tot_cap-U_tot_ind)/mean=1.62%
Calculating Qdielectric_main for mode 1 (1/1)
p_dielectric_main_1 = 0.9213896904120465
WARNING 10:13AM [__init__]: <p>Error: <class 'IndexError'></p>

ANALYSIS DONE. Data saved to:

C:\data-pyEPR\Project23\Q1_Q2_Bus_hfss\2021-07-20 10-13-32.npz


         Differences in variations:



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

Starting the diagonalization
Finished the diagonalization
Pm_norm=
modes
0    1.032291
1    1.032911
dtype: float64

Pm_norm idx =
     jj1    jj2
0   True  False
1  False   True
*** P (participation matrix, not normlz.)
        jj1       jj2
0  0.965013  0.000032
1  0.000033  0.963818

*** S (sign-bit matrix)
   s_jj1  s_jj2
0      1      1
1      1      1
*** P (participation matrix, normalized.)
         1  3.2e-05
   3.3e-05        1

*** Chi matrix O1 PT (MHz)
    Diag is anharmonicity, off diag is full cross-Kerr.
       304   0.0398
    0.0398      304

*** Chi matrix ND (MHz)
       345    0.696
     0.696      341

*** Frequencies O1 PT (MHz)
0    5047.467672
1    5480.401018
dtype: float64

*** Frequencies ND (MHz)
0    5027.574497
1    5462.263203
dtype: float64

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

Mode frequencies (MHz)

Numerical diagonalization

Lj1 14
eigenmode
0 5047.47
1 5480.40

Kerr Non-linear coefficient table (MHz)

Numerical diagonalization

0 1
Lj1
14 0 344.93 0.70
1 0.70 341.29

From the analysis results we can determine the qubits anharmonicities and coupling strength.

Other analysis is still being added, such as the impedance analysis, though some already be done manually by renderering to a driven modal simulation and performing frequency sweeps to extract the S-Parameters or Impedance matrix. This would be an easy way to, say, determine the external quality factor of a readout resonator. See tutorial notebooks 4.3

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

[32]:
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.

[33]:
qhk21_gds = design.renderers.gds

The various options for the gds renderer can 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.

[34]:
qhk21_gds.options
[34]:
{'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'}
[35]:
qhk21_gds.options['path_filename'] = '../resources/Fake_Junctions.GDS'
qhk21_gds.options['no_cheese']['buffer']='50um'
[36]:
qhk21_gds.export_to_gds('QHK21_Tutorial.gds')
[36]:
1

(optional) Now that the design is finished, we can close the GUI.

[37]:
# 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