diff --git a/qiskit_metal/_gui/_imgs/components/resonator_lumped.PNG b/qiskit_metal/_gui/_imgs/components/resonator_lumped.PNG new file mode 100644 index 000000000..227f0b6da Binary files /dev/null and b/qiskit_metal/_gui/_imgs/components/resonator_lumped.PNG differ diff --git a/qiskit_metal/_gui/_imgs/components/transmon_concentric_type_2.PNG b/qiskit_metal/_gui/_imgs/components/transmon_concentric_type_2.PNG new file mode 100644 index 000000000..830f74b6f Binary files /dev/null and b/qiskit_metal/_gui/_imgs/components/transmon_concentric_type_2.PNG differ diff --git a/qiskit_metal/_gui/_imgs/components/tunable_coupler_02.PNG b/qiskit_metal/_gui/_imgs/components/tunable_coupler_02.PNG new file mode 100644 index 000000000..f4f2136c2 Binary files /dev/null and b/qiskit_metal/_gui/_imgs/components/tunable_coupler_02.PNG differ diff --git a/qiskit_metal/qlibrary/__init__.py b/qiskit_metal/qlibrary/__init__.py index 3e50b3001..22965f53a 100644 --- a/qiskit_metal/qlibrary/__init__.py +++ b/qiskit_metal/qlibrary/__init__.py @@ -71,6 +71,17 @@ LineTee CapNInterdigitalTee TunableCoupler01 + TunableCoupler02 + + +Resonator +------------ + +.. autosummary:: + :toctree: + + ReadoutResFC + ResonatorLumped Terminations @@ -109,6 +120,7 @@ jj_dolan jj_manhattan TransmonConcentric + TransmonConcentricType2 TransmonCross TransmonCrossFL TransmonInterdigitated @@ -150,6 +162,7 @@ from .couplers.line_tee import LineTee from .couplers.cap_n_interdigital_tee import CapNInterdigitalTee from .couplers.tunable_coupler_01 import TunableCoupler01 + from .couplers.tunable_coupler_02 import TunableCoupler02 from .lumped.cap_n_interdigital import CapNInterdigital from .lumped.cap_3_interdigital import Cap3Interdigital from .lumped.resonator_coil_rect import ResonatorCoilRect @@ -167,6 +180,7 @@ from .qubits.JJ_Dolan import jj_dolan from .qubits.JJ_Manhattan import jj_manhattan from .qubits.transmon_concentric import TransmonConcentric + from .qubits.transmon_concentric_type_2 import TransmonConcentricType2 from .qubits.transmon_cross import TransmonCross from .qubits.transmon_cross_fl import TransmonCrossFL from .qubits.Transmon_Interdigitated import TransmonInterdigitated @@ -176,5 +190,7 @@ from .qubits.transmon_pocket_teeth import TransmonPocketTeeth from .qubits.SQUID_loop import SQUID_LOOP from .qubits.star_qubit import StarQubit + from .resonator.readoutres_fc import ReadoutResFC + from .resonator.resonator_lumped import ResonatorLumped from .tlines import anchored_path diff --git a/qiskit_metal/qlibrary/couplers/tunable_coupler_02.py b/qiskit_metal/qlibrary/couplers/tunable_coupler_02.py new file mode 100644 index 000000000..24519579a --- /dev/null +++ b/qiskit_metal/qlibrary/couplers/tunable_coupler_02.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +"""Tunable Coupler, as shown in Phys. Rev. Research 2, 033447 (2020). +""" + +from math import sin, cos +import numpy as np +from qiskit_metal import draw, Dict +from qiskit_metal.qlibrary.core import BaseQubit + + +class TunableCoupler02(BaseQubit): + """One of the tunable couplers + + .. image:: + tunable_coupler_02.png + + .. meta:: + Tunable Coupler 2 + + BaseQubit Default Options: + * connection_pads: empty Dict -- Currently not used, connection count is static. (WIP) + Default Options: + * pocket_width: '3mm' -- The width (x-axis) of the transmon pocket + * pocket_height: '2m' -- The height (y-axis) of the transmon pocket + * pos_x: '0mm' -- the x-coordinate location of the JJ in the middle of the qubit + * pos_y: '0mm' -- the y-coordinate location of the JJ in the middle of the qubit + * orientation: '0' -- the angle of rotation + * pad_radius: '0.3mm' -- the radius of the two circular pads on either side of the JJ + * bus_width: '0.1mm' -- the width of the CPW resonator in the coupler + * bus_inner_length: '0.5mm' -- length of the CPW between the JJ and the circular pad + * bus_outer_length: '0.5mm' -- length of the CPW between the circular pad and the in/out pin + * bus_JJ_height: '0.5mm' -- vertical distance between the main cpw line and the offset JJ + * JJ_width: '0.1mm' -- the width of the JJ + * JJ_length: '0.4mm' -- the length of the JJ + * fbl_width: '0.1mm' -- the width of the flux bias line drawn near the JJ + * fbl_length: '0.5mm' -- the length of the flux bias line drawn near the JJ + * fbl_offset: '0.1mm' -- the lateral offset between the flux bias line and the JJ + * layer: '1' -- the default design layer + """ + + default_options = Dict(pocket_width='3mm', + pocket_height='2mm', + pos_x='0mm', + pos_y='0mm', + orientation='0', + pad_radius='0.3mm', + bus_width='0.1mm', + bus_inner_length='0.5mm', + bus_outer_length='0.5mm', + bus_JJ_height='0.5mm', + JJ_width='0.1mm', + JJ_length='0.4mm', + fbl_width='0.1mm', + fbl_length='0.5mm', + fbl_offset='0.1mm', + layer='1') + + component_metadata = Dict(short_name='Pocket', + _qgeometry_table_path='True', + _qgeometry_table_poly='True', + _qgeometry_table_junction='True') + + TOOLTIP = """One of the tunable couplers""" + + def make(self): + """Builds the component.""" + """Convert self.options into QGeometry.""" + p = self.parse_options() # Parse the string options into numbers + # draw the Josephson Junction + JJ = draw.LineString([ + (-0.5 * p.JJ_length, p.bus_JJ_height - 0.5 * p.JJ_width), + (0.5 * p.JJ_length, p.bus_JJ_height - 0.5 * p.JJ_width) + ]) + # draw the individual components of the CPW resonator + bus_vertical_left = draw.rectangle( + p.bus_width, p.bus_JJ_height, + -0.5 * p.JJ_length - 0.5 * p.bus_width, 0.5 * p.bus_JJ_height) + bus_vertical_right = draw.rectangle( + p.bus_width, p.bus_JJ_height, 0.5 * p.JJ_length + 0.5 * p.bus_width, + 0.5 * p.bus_JJ_height) + bus_left = draw.rectangle( + p.bus_inner_length + p.bus_outer_length + 2.0 * p.pad_radius, + p.bus_width, -0.5 * p.JJ_length - p.bus_width - 0.5 * + (p.bus_inner_length + p.bus_outer_length + 2.0 * p.pad_radius), + 0.5 * p.bus_width) + bus_right = draw.rectangle( + p.bus_inner_length + p.bus_outer_length + 2.0 * p.pad_radius, + p.bus_width, 0.5 * p.JJ_length + p.bus_width + 0.5 * + (p.bus_inner_length + p.bus_outer_length + 2.0 * p.pad_radius), + 0.5 * p.bus_width) + # draw the circular charge pads + left_pad = draw.Point( + -0.5 * p.JJ_length - p.bus_width - p.bus_inner_length - + 0.5 * p.pad_radius, 0.5 * p.bus_width).buffer(p.pad_radius) + right_pad = draw.Point( + 0.5 * p.JJ_length + p.bus_width + p.bus_inner_length + + 0.5 * p.pad_radius, 0.5 * p.bus_width).buffer(p.pad_radius) + # draw the flux bias line + fbl = draw.rectangle( + p.fbl_width, p.fbl_length, + -0.5 * p.JJ_length - p.bus_width - p.fbl_offset - 0.5 * p.fbl_width, + p.bus_JJ_height + 0.5 * p.fbl_length) + # draw the pocket surrounding the qubit + pocket = draw.rectangle(0.0, 0.0, p.pocket_width, p.pocket_height) + # Translate and rotate all shapes + objects = [ + JJ, bus_vertical_left, bus_vertical_right, bus_left, bus_right, + left_pad, right_pad, fbl, pocket + ] + objects = draw.rotate(objects, p.orientation, origin=(0, 0)) + objects = draw.translate(objects, xoff=p.pos_x, yoff=p.pos_y) + [ + JJ, bus_vertical_left, bus_vertical_right, bus_left, bus_right, + left_pad, right_pad, fbl, pocket + ] = objects + # give each poly a name to send to qgeometry + geom_jj = {'poly1': JJ} + geom_bus_vertical_left = {'poly2': bus_vertical_left} + geom_bus_vertical_right = {'poly3': bus_vertical_right} + geom_bus_left = {'poly4': bus_left} + geom_bus_right = {'poly5': bus_right} + geom_left_pad = {'poly6': left_pad} + geom_right_pad = {'poly7': right_pad} + geom_fbl = {'poly8': fbl} + #geom_pocket = {'poly9': pocket} + # add to qgeometry + self.add_qgeometry('junction', + geom_jj, + layer=p.layer, + subtract=False, + width=p.JJ_width) + self.add_qgeometry('poly', + geom_bus_vertical_left, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', + geom_bus_vertical_right, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', geom_bus_left, layer=p.layer, subtract=False) + self.add_qgeometry('poly', + geom_bus_right, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', geom_left_pad, layer=p.layer, subtract=False) + self.add_qgeometry('poly', + geom_right_pad, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', geom_fbl, layer=p.layer, subtract=False) + + #self.add_qgeometry('poly', geom_pocket, layer=1, subtract=True) + ########################################################################### + # Add Qpin connections + # define a function that both rotates and translates the qpin coordinates + def qpin_rotate_translate(x): + y = list(x) + z = [0.0, 0.0] + z[0] = y[0] * cos(p.orientation * 3.14159 / 180) - y[1] * sin( + p.orientation * 3.14159 / 180) + z[1] = y[0] * sin(p.orientation * 3.14159 / 180) + y[1] * cos( + p.orientation * 3.14159 / 180) + z[0] = z[0] + p.pos_x + z[1] = z[1] + p.pos_y + x = (z[0], z[1]) + return x + + # Right-hand pin + qp1b = (0.5 * p.JJ_length + p.bus_width + p.bus_inner_length + + p.bus_outer_length + 2.0 * p.pad_radius, 0.5 * p.bus_width) + qp1a = (0.5 * p.JJ_length + p.bus_width + 0.9 * p.bus_inner_length + + p.bus_outer_length + 2.0 * p.pad_radius, 0.5 * p.bus_width) + qp1a = qpin_rotate_translate(qp1a) + qp1b = qpin_rotate_translate(qp1b) + self.add_pin('pin1', + points=np.array([qp1a, qp1b]), + width=0.01, + input_as_norm=True) + # Left-hand pin + qp2b = (-0.5 * p.JJ_length - p.bus_width - p.bus_inner_length - + p.bus_outer_length - 2.0 * p.pad_radius, 0.5 * p.bus_width) + qp2a = (-0.5 * p.JJ_length - p.bus_width - 0.9 * p.bus_inner_length - + p.bus_outer_length - 2.0 * p.pad_radius, 0.5 * p.bus_width) + qp2a = qpin_rotate_translate(qp2a) + qp2b = qpin_rotate_translate(qp2b) + self.add_pin('pin2', + points=np.array([qp2a, qp2b]), + width=0.01, + input_as_norm=True) + # Flux Bias Line pin + qp3b = (-0.5 * p.JJ_length - p.bus_width - p.fbl_offset - + 0.5 * p.fbl_width, p.bus_JJ_height + p.fbl_length) + qp3a = (-0.5 * p.JJ_length - p.bus_width - p.fbl_offset - + 0.5 * p.fbl_width, p.bus_JJ_height + 0.9 * p.fbl_length) + qp3a = qpin_rotate_translate(qp3a) + qp3b = qpin_rotate_translate(qp3b) + self.add_pin('fbl', + points=np.array([qp3a, qp3b]), + width=0.01, + input_as_norm=True) diff --git a/qiskit_metal/qlibrary/qubits/transmon_concentric.py b/qiskit_metal/qlibrary/qubits/transmon_concentric.py index 42aa2f6ae..10d73028a 100644 --- a/qiskit_metal/qlibrary/qubits/transmon_concentric.py +++ b/qiskit_metal/qlibrary/qubits/transmon_concentric.py @@ -59,6 +59,7 @@ class TransmonConcentric(BaseQubit): * pocket_w: '1500um' -- Transmon pocket width * pocket_h: '1000um' -- Transmon pocket height * cpw_width: '10.0um' -- Width of the readout resonator and flux bias line + * layer: '1' -- the design layer where the component will go """ # default drawing options @@ -80,7 +81,8 @@ class TransmonConcentric(BaseQubit): pocket_w='1500um', # transmon pocket width pocket_h='1000um', # transmon pocket height cpw_width='10.0um', # width of the readout resonator and flux bias line - inductor_width='5.0um' # width of the Josephson Junctions + inductor_width='5.0um', # width of the Josephson Junctions + layer='1' # design layer ) """Default drawing options""" @@ -168,24 +170,24 @@ def qpin_rotate_translate(x): self.add_qgeometry('path', geom_rr, - layer=1, + layer=p.layer, subtract=False, width=p.cpw_width) self.add_qgeometry('path', geom_fbl, - layer=1, + layer=p.layer, subtract=False, width=p.cpw_width) - self.add_qgeometry('poly', geom_outer, layer=1, subtract=False) - self.add_qgeometry('poly', geom_inner, layer=1, subtract=False) + self.add_qgeometry('poly', geom_outer, layer=p.layer, subtract=False) + self.add_qgeometry('poly', geom_inner, layer=p.layer, subtract=False) self.add_qgeometry('junction', geom_jjt, - layer=1, + layer=p.layer, subtract=False, width=p.inductor_width) self.add_qgeometry('junction', geom_jjb, - layer=1, + layer=p.layer, subtract=False, width=p.inductor_width) self.add_qgeometry('poly', geom_pocket, layer=1, subtract=True) diff --git a/qiskit_metal/qlibrary/qubits/transmon_concentric_type_2.py b/qiskit_metal/qlibrary/qubits/transmon_concentric_type_2.py new file mode 100644 index 000000000..0a9084201 --- /dev/null +++ b/qiskit_metal/qlibrary/qubits/transmon_concentric_type_2.py @@ -0,0 +1,363 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +"""Concentric Transmon, as shown in Phys. Rev. Appl. 10, 034050 (2018). +""" +from math import sin, cos +import numpy as np +from qiskit_metal import draw, Dict +from qiskit_metal.qlibrary.core import BaseQubit + + +class TransmonConcentricType2(BaseQubit): + """The base `TrasmonConcentric` class + Inherits `BaseQubit` class. + Metal transmon object consisting of a circle surrounding by a concentric + ring. There are two Josephson Junction connecting the circle to the ring; + one at the south end and one at the north end. There is a readout resonator. + + .. image:: + transmon_concentric_type_2.png + + .. meta:: + Transmon Concentric Type 2 + + BaseQubit Default Options: + * connection_pads: empty Dict -- the dictionary which contains all active + connection lines for the qubit. + * _default_connection_pads: empty Dict -- the default values for the + (if any) connection lines of the qubit. + Default Options: + * width: '1000um' -- Width of transmon pocket + * height: '1000um' -- Height of transmon pocket + * rad_o: '170um' -- Outer radius + * rad_i: '115um' -- Inner radius + * gap: '35um' -- Radius of gap between two pads + * jj_w: '10um' -- Josephson Junction width + * res_s: '100um' -- Space between top electrode and readout resonator + * res_ext: '100um' -- Extension of readout resonator in x-direction + beyond midpoint of transmon + * fbl_rad: '100um' -- Radius of the flux bias line loop + * fbl_sp: '100um' -- Spacing between metal pad and flux bias loop + * fbl_gap: '80um' -- Space between parallel lines of the flux bias loop + * fbl_ext: '300um' -- Run length of flux bias line between circular + loop and edge of pocket + * pocket_w: '1500um' -- Transmon pocket width + * pocket_h: '1000um' -- Transmon pocket height + * cpw_width: '10.0um' -- Width of the readout resonator and flux bias line + * layer: '1' -- default layer + """ + + # default drawing options + default_options = Dict(pos_x='0mm', + pos_y='0mm', + orientation='0', + rad_outer='2mm', + rad_inner='1mm', + gap='0.5mm', + jj_w='0.2mm', + finger_NE_width='0.1mm', + finger_NE_length='0.5mm', + coupler_NE_width='0.1mm', + coupler_NE_gap='0.1mm', + finger_NW_width='0.1mm', + finger_NW_length='0.5mm', + coupler_NW_width='0.1mm', + coupler_NW_gap='0.1mm', + finger_SW_width='0.1mm', + finger_SW_length='0.5mm', + coupler_SW_width='0.1mm', + coupler_SW_gap='0.1mm', + finger_E_width='0.1mm', + finger_E_length='0.5mm', + box_E_width='0.7mm', + box_E_length='0.7mm', + finger_N_width='0.1mm', + finger_N_length='2mm', + triangle_base='2mm', + triangle_height='1mm', + pocket_w='10mm', + pocket_h='10mm', + layer='1') + """Default drawing options""" + + def make(self): + """Convert self.options into QGeometry.""" + p = self.parse_options() # Parse the string options into numbers + # draw the concentric pad regions + outer_pad = draw.Point(0, 0).buffer(p.rad_outer) + space = draw.Point(0, 0).buffer((p.gap + p.rad_inner)) + cutout = draw.rectangle(8.0 * p.finger_N_width, p.finger_N_length, 0.0, + p.rad_inner + 0.5 * p.finger_N_length) + outer_pad = draw.subtract(outer_pad, space) + outer_pad = draw.subtract(outer_pad, cutout) + inner_pad = draw.Point(0, 0).buffer(p.rad_inner) + # draw the Josephson Junction + JJ = draw.LineString([ + (p.rad_inner * cos(45 * 3.14159 / 180), + -1.0 * p.rad_inner * sin(45 * 3.14159 / 180)), + ((p.rad_inner + p.gap) * cos(45 * 3.14159 / 180), + -1.0 * (p.rad_inner + p.gap) * sin(45 * 3.14159 / 180)) + ]) + # draw the northeast finger + finger_NE = draw.LineString([ + (p.rad_outer * cos(45 * 3.14159 / 180), + p.rad_outer * sin(45 * 3.14159 / 180)), + ((p.rad_outer + p.finger_NE_length) * cos(45 * 3.14159 / 180), + (p.rad_outer + p.finger_NE_length) * sin(45 * 3.14159 / 180)) + ]) + # draw the coupling resonator to the northeast finger + r = (p.rad_outer + 0.5 * p.finger_NE_length + 0.5 * + (0.5 * p.finger_NE_length + p.coupler_NE_gap + p.coupler_NE_width)) + coupler_NE = draw.rectangle( + 2.0 * p.coupler_NE_width + p.finger_NE_width + 2 * p.coupler_NE_gap, + 0.5 * p.finger_NE_length + p.coupler_NE_gap + p.coupler_NE_width, + r * cos(45 * 3.14159 / 180), r * sin(45 * 3.14159 / 180)) + coupler_NE = draw.rotate(coupler_NE, + -45, + origin=(r * cos(45 * 3.14159 / 180), + r * sin(45 * 3.14159 / 180))) + l = p.rad_outer + 0.5 * p.finger_NE_length + 0.5 * ( + 0.5 * p.finger_NE_length + p.coupler_NE_gap) + coupler_NE_cut = draw.rectangle( + p.finger_NE_width + 2.0 * p.coupler_NE_gap, + 0.5 * p.finger_NE_length + p.coupler_NE_gap + + 0.05 * p.finger_NE_length, l * cos(45 * 3.145159 / 180), + l * sin(45 * 3.14159 / 180)) + coupler_NE_cut = draw.rotate(coupler_NE_cut, + -45, + origin=(l * cos(45 * 3.14159 / 180), + l * sin(45 * 3.14159 / 180))) + coupler_NE = draw.subtract(coupler_NE, coupler_NE_cut) + # draw the northwest finger + finger_NW = draw.LineString([ + (-1.0 * p.rad_outer * cos(45 * 3.14159 / 180), + p.rad_outer * sin(45 * 3.14159 / 180)), + (-1.0 * (p.rad_outer + p.finger_NW_length) * + cos(45 * 3.14159 / 180), + (p.rad_outer + p.finger_NW_length) * sin(45 * 3.14159 / 180)) + ]) + # draw the coupling resonator to the northwest finger + r_nw = ( + p.rad_outer + 0.5 * p.finger_NW_length + 0.5 * + (0.5 * p.finger_NW_length + p.coupler_NW_gap + p.coupler_NW_width)) + coupler_NW = draw.rectangle( + 2.0 * p.coupler_NW_width + p.finger_NW_width + 2 * p.coupler_NW_gap, + 0.5 * p.finger_NW_length + p.coupler_NW_gap + p.coupler_NW_width, + -1.0 * r_nw * cos(45 * 3.14159 / 180), + r_nw * sin(45 * 3.14159 / 180)) + coupler_NW = draw.rotate(coupler_NW, + 45, + origin=(-1.0 * r_nw * cos(45 * 3.14159 / 180), + r_nw * sin(45 * 3.14159 / 180))) + l_nw = p.rad_outer + 0.5 * p.finger_NW_length + 0.5 * ( + 0.5 * p.finger_NW_length + p.coupler_NW_gap) + coupler_NW_cut = draw.rectangle( + p.finger_NW_width + 2.0 * p.coupler_NW_gap, + 0.5 * p.finger_NW_length + p.coupler_NW_gap + + 0.05 * p.finger_NW_length, -1.0 * l_nw * cos(45 * 3.145159 / 180), + l_nw * sin(45 * 3.14159 / 180)) + coupler_NW_cut = draw.rotate( + coupler_NW_cut, + 45, + origin=(-1.0 * l_nw * cos(45 * 3.14159 / 180), + l_nw * sin(45 * 3.14159 / 180))) + coupler_NW = draw.subtract(coupler_NW, coupler_NW_cut) + # draw the southwest finger + finger_SW = draw.LineString([ + (-1.0 * p.rad_outer * cos(45 * 3.14159 / 180), + -1.0 * p.rad_outer * sin(45 * 3.14159 / 180)), + (-1.0 * (p.rad_outer + p.finger_SW_length) * + cos(45 * 3.14159 / 180), -1.0 * + (p.rad_outer + p.finger_SW_length) * sin(45 * 3.14159 / 180)) + ]) + # draw the coupling resonator to the southwest finger + r_sw = ( + p.rad_outer + 0.5 * p.finger_SW_length + 0.5 * + (0.5 * p.finger_SW_length + p.coupler_SW_gap + p.coupler_SW_width)) + coupler_SW = draw.rectangle( + 2.0 * p.coupler_SW_width + p.finger_SW_width + 2 * p.coupler_SW_gap, + 0.5 * p.finger_SW_length + p.coupler_SW_gap + p.coupler_SW_width, + -1.0 * r_sw * cos(45 * 3.14159 / 180), + -1.0 * r_sw * sin(45 * 3.14159 / 180)) + coupler_SW = draw.rotate(coupler_SW, + 135, + origin=(-1.0 * r_sw * cos(45 * 3.14159 / 180), + -1.0 * r_sw * sin(45 * 3.14159 / 180))) + l_sw = p.rad_outer + 0.5 * p.finger_SW_length + 0.5 * ( + 0.5 * p.finger_SW_length + p.coupler_SW_gap) + coupler_SW_cut = draw.rectangle( + p.finger_SW_width + 2.0 * p.coupler_SW_gap, + 0.5 * p.finger_SW_length + p.coupler_SW_gap + + 0.05 * p.finger_SW_length, -1.0 * l_sw * cos(45 * 3.145159 / 180), + -1.0 * l_sw * sin(45 * 3.14159 / 180)) + coupler_SW_cut = draw.rotate( + coupler_SW_cut, + 135, + origin=(-1.0 * l_sw * cos(45 * 3.14159 / 180), + -1.0 * l_sw * sin(45 * 3.14159 / 180))) + coupler_SW = draw.subtract(coupler_SW, coupler_SW_cut) + # draw the east finger with the rectangular coupling pad + finger_E = draw.LineString([(-1.0 * p.rad_outer, 0.0), + (-1.0 * (p.rad_outer + p.finger_E_length), + 0.0)]) + box = draw.rectangle( + p.box_E_width, p.box_E_length, + -1.0 * (p.rad_outer + p.finger_E_length + 0.5 * p.box_E_width), 0.0) + # draw the north finger with the triangular coupling pad + finger_N = draw.LineString([(0.0, p.rad_inner), + (0.0, p.rad_inner + p.finger_N_length)]) + # draw the top triangular coupling pad + padtop_i = draw.rectangle( + p.triangle_base, p.triangle_height, 0.0, + p.rad_inner + p.finger_N_length + 0.5 * p.triangle_height) + cut1 = draw.rotate(padtop_i, + 45, + origin=(0.5 * p.triangle_base, p.rad_inner + + p.finger_N_length + p.triangle_height)) + padtop = draw.subtract(padtop_i, cut1) + cut2 = draw.rotate(padtop_i, + 315, + origin=(-0.5 * p.triangle_base, p.rad_inner + + p.finger_N_length + p.triangle_height)) + padtop = draw.subtract(padtop, cut2) + padtop = draw.translate(padtop, 0.0, -.05 * p.triangle_height) + # draw the transmon pocket bounding box + # pocket = draw.rectangle(p.pocket_w, p.pocket_h) + # Translate and rotate all shapes + objects = [ + outer_pad, inner_pad, JJ, finger_NE, finger_NW, finger_SW, finger_E, + box, finger_N, padtop, coupler_NE, coupler_NW, coupler_SW + ] + objects = draw.rotate(objects, p.orientation, origin=(0, 0)) + objects = draw.translate(objects, xoff=p.pos_x, yoff=p.pos_y) + [ + outer_pad, inner_pad, JJ, finger_NE, finger_NW, finger_SW, finger_E, + box, finger_N, padtop, coupler_NE, coupler_NW, coupler_SW + ] = objects + ############################################################## + # Use the geometry to create Metal QGeometry + geom_outer = {'poly1': outer_pad} + geom_inner = {'poly2': inner_pad} + geom_jj = {'poly3': JJ} + geom_finger_NE = {'finger_NE': finger_NE} + geom_finger_NW = {'finger_NW': finger_NW} + geom_finger_SW = {'finger_SW': finger_SW} + geom_finger_E = {'finger_E': finger_E} + geom_box_E = {'box_E': box} + geom_finger_N = {'finger_N': finger_N} + geom_pad_top = {'pad_top': padtop} + geom_coupler_NE = {'coupler_NE': coupler_NE} + geom_coupler_NW = {'coupler_NW': coupler_NW} + geom_coupler_SW = {'coupler_SW': coupler_SW} + self.add_qgeometry('poly', geom_outer, layer=p.layer, subtract=False) + self.add_qgeometry('poly', geom_inner, layer=p.layer, subtract=False) + self.add_qgeometry('junction', + geom_jj, + layer=p.layer, + subtract=False, + width=p.jj_w) + self.add_qgeometry('path', + geom_finger_NE, + layer=p.layer, + subtract=False, + width=p.finger_NE_width) + self.add_qgeometry('path', + geom_finger_NW, + layer=p.layer, + subtract=False, + width=p.finger_NW_width) + self.add_qgeometry('path', + geom_finger_SW, + layer=p.layer, + subtract=False, + width=p.finger_SW_width) + self.add_qgeometry('path', + geom_finger_E, + layer=p.layer, + subtract=False, + width=p.finger_E_width) + self.add_qgeometry('poly', geom_box_E, layer=p.layer, subtract=False) + self.add_qgeometry('path', + geom_finger_N, + layer=p.layer, + subtract=False, + width=p.finger_N_width) + self.add_qgeometry('poly', geom_pad_top, layer=p.layer, subtract=False) + self.add_qgeometry('poly', + geom_coupler_NE, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', + geom_coupler_NW, + layer=p.layer, + subtract=False) + self.add_qgeometry('poly', + geom_coupler_SW, + layer=p.layer, + subtract=False) + + ########################################################################### + # Add Qpin connections + # define a function that both rotates and translates the qpin coordinates + def qpin_rotate_translate(x): + y = list(x) + z = [0.0, 0.0] + z[0] = y[0] * cos(p.orientation * 3.14159 / 180) - y[1] * sin( + p.orientation * 3.14159 / 180) + z[1] = y[0] * sin(p.orientation * 3.14159 / 180) + y[1] * cos( + p.orientation * 3.14159 / 180) + z[0] = z[0] + p.pos_x + z[1] = z[1] + p.pos_y + x = (z[0], z[1]) + return x + + # Northeast coupler + qp1a_r = p.rad_outer + p.finger_NE_length + p.coupler_NE_gap + qp1a = (qp1a_r * cos(45 * 3.14159 / 180), + qp1a_r * sin(45 * 3.14159 / 180)) + qp1b_r = p.rad_outer + p.finger_NE_length + p.coupler_NE_gap + p.coupler_NE_width + qp1b = (qp1b_r * cos(45 * 3.14159 / 180), + qp1b_r * sin(45 * 3.14159 / 180)) + qp1a = qpin_rotate_translate(qp1a) + qp1b = qpin_rotate_translate(qp1b) + self.add_pin('pin1', + points=np.array([qp1a, qp1b]), + width=0.01, + input_as_norm=True) + # Northwest coupler + qp2a_r = p.rad_outer + p.finger_NW_length + p.coupler_NW_gap + qp2a = (-1.0 * qp2a_r * cos(45 * 3.14159 / 180), + qp2a_r * sin(45 * 3.14159 / 180)) + qp2b_r = p.rad_outer + p.finger_NW_length + p.coupler_NW_gap + p.coupler_NW_width + qp2b = (-1.0 * qp2b_r * cos(45 * 3.14159 / 180), + qp2b_r * sin(45 * 3.14159 / 180)) + qp2a = qpin_rotate_translate(qp2a) + qp2b = qpin_rotate_translate(qp2b) + self.add_pin('pin2', + points=np.array([qp2a, qp2b]), + width=0.01, + input_as_norm=True) + # Southwest coupler + qp3a_r = p.rad_outer + p.finger_SW_length + p.coupler_SW_gap + qp3a = (-1.0 * qp3a_r * cos(45 * 3.14159 / 180), + -1.0 * qp3a_r * sin(45 * 3.14159 / 180)) + qp3b_r = p.rad_outer + p.finger_SW_length + p.coupler_SW_gap + p.coupler_SW_width + qp3b = (-1.0 * qp3b_r * cos(45 * 3.14159 / 180), + -1.0 * qp3b_r * sin(45 * 3.14159 / 180)) + qp3a = qpin_rotate_translate(qp3a) + qp3b = qpin_rotate_translate(qp3b) + self.add_pin('pin3', + points=np.array([qp3a, qp3b]), + width=0.01, + input_as_norm=True) diff --git a/qiskit_metal/qlibrary/resonator/resonator_lumped.py b/qiskit_metal/qlibrary/resonator/resonator_lumped.py new file mode 100644 index 000000000..6f50fda70 --- /dev/null +++ b/qiskit_metal/qlibrary/resonator/resonator_lumped.py @@ -0,0 +1,495 @@ +# -*- coding: utf-8 -*- +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +"""Lumped Resonator, as shown in Phys. Rev. Appl. 10, 034050 (2018). +""" +from math import sin, cos +import numpy as np +from qiskit_metal import draw, Dict +from qiskit_metal.qlibrary.core import QComponent + + +class ResonatorLumped(QComponent): + ''' + The base ResonatorLumped class + Inherits the QComponent class + + .. image:: + resonator_lumped.png + + .. meta:: + Lumped Resonator + + Default Options: + * pos_x: '0um' -- x-coordinate of the bottom center of the resonator + * pos_y: '0um' -- y-coordinate of the bottom center of the resonator + * orientation: '0' -- angle of rotation of the resonator + * box_width: '5mm' -- the width of the rectangular resonator structure + * box_height: '5mm' -- the height of the rectangular resonator structure + * perimeter_thicknes: '0.01mm' -- the width of the resonator along the perimeter + * res_width: '0.01m' -- the width of the resonator + * initial: '0.1m' -- the length of the lower straight resonator segment + * n_turns: '10' -- the number of turns in the curved resonator segment + * turn_radius: '0.1mm' - the radius of the turns in the curved resonator segment + * inner_space: '0.1mm' - the space between the curved resonator + * outer_space: '0.1mm' -- the space between rectangular and curved resonator turns + * final: '2.5mm' -- the length of the upper straight segment of the resonator + * break_width: '0.2mm' -- the width of the "break" in the top of the rectangular resonator + * layer: '1' -- the layer of the component + ''' + + default_options = Dict( + pos_x='0 um', + pos_y='0 um', + orientation='0', + box_width='5mm', + box_height='5mm', + perimeter_thickness='0.01mm', + res_width='0.01mm', + initial='0.1mm', + n_turns='10', + turn_radius='0.1mm', + inner_space='0.1mm', + outer_space='0.1mm', + final='2.5mm', + break_width='0.2mm', + layer='1', + ) + + ########################## + def make(self): + """Builds the component.""" + """Convert self.options into QGeometry.""" + p = self.parse_options() # Parse the string options into numbers + + # draw the perimeter + box_out = draw.rectangle(p.box_width, p.box_height, 0.0, 0.0) + box_in = draw.rectangle(p.box_width - 2.0 * p.perimeter_thickness, + p.box_height - 2.0 * p.perimeter_thickness, 0.0, + 0.0) + perimeter = draw.subtract(box_out, box_in) + perimeter = draw.translate(perimeter, xoff=0.0, yoff=0.5 * p.box_height) + break_rect = draw.rectangle(p.break_width, p.break_width, 0.0, + p.box_height) + perimeter = draw.subtract(perimeter, break_rect) + # start the resonator + initial = draw.LineString([(0.0, p.perimeter_thickness), + (0.0, p.perimeter_thickness + p.initial)]) + # Draw the first quarter turn + centerx, centery = -1.0 * p.turn_radius, p.perimeter_thickness + p.initial + radius = p.turn_radius + start_angle, end_angle = 0, 90 # In degrees + numsegments = 1000 + # The coordinates of the arc + theta = np.radians(np.linspace(start_angle, end_angle, numsegments)) + x = centerx + radius * np.cos(theta) + y = centery + radius * np.sin(theta) + arc = draw.LineString(np.column_stack([x, y])) + # Draw the first half-turn (left); this gets repeated + centerx2 = -0.5 * p.box_width + p.outer_space + p.turn_radius + centery2 = p.perimeter_thickness + p.initial + 2.0 * p.turn_radius + radius2 = p.turn_radius + start_angle2, end_angle2 = 270, 90 # In degrees + numsegments2 = 1000 + # The coordinates of the arc + theta2 = np.radians(np.linspace(start_angle2, end_angle2, numsegments2)) + x2 = centerx2 + radius2 * np.cos(theta2) + y2 = centery2 + radius2 * np.sin(theta2) + arc_left_1 = draw.LineString(np.column_stack([x2, y2])) + # Draw the first half-turn (right); this gets repeated + centerx3 = 0.5 * p.box_width - p.outer_space - p.turn_radius + centery3 = p.perimeter_thickness + p.initial + 4.0 * p.turn_radius + radius3 = p.turn_radius + start_angle3, end_angle3 = 90, -90 # In degrees + numsegments3 = 1000 + # The coordinates of the arc + theta3 = np.radians(np.linspace(start_angle3, end_angle3, numsegments3)) + x3 = centerx3 + radius3 * np.cos(theta3) + y3 = centery3 + radius3 * np.sin(theta3) + arc_right_1 = draw.LineString(np.column_stack([x3, y3])) + # bottom half-line + line1 = draw.LineString([ + (-1.0 * p.turn_radius - 0.0 * p.res_width, + p.perimeter_thickness + p.initial + p.turn_radius), + (-0.5 * p.box_width + p.outer_space + p.turn_radius, + p.perimeter_thickness + p.initial + p.turn_radius) + ]) + # bottom full-line (this one gets repeated) + line2 = draw.LineString([ + (-0.5 * p.box_width + p.outer_space + p.turn_radius, + p.initial + 3.0 * p.turn_radius + p.res_width), + (0.5 * p.box_width - p.outer_space - p.turn_radius, + p.initial + 3.0 * p.turn_radius + p.res_width) + ]) + # repeat the full fline + line3 = draw.translate(line2, 0.0, 2.0 * p.turn_radius) + line4 = draw.translate(line3, 0.0, 2.0 * p.turn_radius) + line5 = draw.translate(line4, 0.0, 2.0 * p.turn_radius) + line6 = draw.translate(line5, 0.0, 2.0 * p.turn_radius) + line7 = draw.translate(line6, 0.0, 2.0 * p.turn_radius) + line8 = draw.translate(line7, 0.0, 2.0 * p.turn_radius) + line9 = draw.translate(line8, 0.0, 2.0 * p.turn_radius) + line10 = draw.translate(line9, 0.0, 2.0 * p.turn_radius) + line11 = draw.translate(line10, 0.0, 2.0 * p.turn_radius) + line12 = draw.translate(line11, 0.0, 2.0 * p.turn_radius) + line13 = draw.translate(line12, 0.0, 2.0 * p.turn_radius) + line14 = draw.translate(line13, 0.0, 2.0 * p.turn_radius) + # hard code the position for now; make it adjustable later + line_last = draw.translate( + line1, 0.5 * p.box_width - 0.0 * p.turn_radius - p.outer_space, + 28 * p.turn_radius) + # draw the line exiting the rectangle + final = draw.LineString([ + (0.0, p.perimeter_thickness + p.initial + 30.0 * p.turn_radius), + (0.0, + p.perimeter_thickness + p.initial + 30.0 * p.turn_radius + p.final) + ]) + arc_last = draw.rotate(arc, + 180, + origin=(0.0, p.perimeter_thickness + p.initial + + 1.5 * p.turn_radius)) + arc_last = draw.translate(arc_last, 0.0, p.initial + 26 * p.turn_radius) + # repeat the left turns + arc_left_2 = draw.translate(arc_left_1, 0.0, 4.0 * p.turn_radius) + arc_left_3 = draw.translate(arc_left_2, 0.0, 4.0 * p.turn_radius) + arc_left_4 = draw.translate(arc_left_3, 0.0, 4.0 * p.turn_radius) + arc_left_5 = draw.translate(arc_left_4, 0.0, 4.0 * p.turn_radius) + arc_left_6 = draw.translate(arc_left_5, 0.0, 4.0 * p.turn_radius) + arc_left_7 = draw.translate(arc_left_6, 0.0, 4.0 * p.turn_radius) + # repeat the right turns + arc_right_2 = draw.translate(arc_right_1, 0.0, 4.0 * p.turn_radius) + arc_right_3 = draw.translate(arc_right_2, 0.0, 4.0 * p.turn_radius) + arc_right_4 = draw.translate(arc_right_3, 0.0, 4.0 * p.turn_radius) + arc_right_5 = draw.translate(arc_right_4, 0.0, 4.0 * p.turn_radius) + arc_right_6 = draw.translate(arc_right_5, 0.0, 4.0 * p.turn_radius) + arc_right_7 = draw.translate(arc_right_6, 0.0, 4.0 * p.turn_radius) + # Translate and rotate all shapes + objects = [ + perimeter, initial, arc, arc_left_1, arc_left_2, arc_left_3, + arc_left_4, arc_left_5, arc_left_6, arc_left_7, arc_right_1, + arc_right_2, arc_right_2, arc_right_3, arc_right_4, arc_right_4, + arc_right_5, arc_right_6, arc_right_7, arc_last, final, line1, + line2, line3, line4, line5, line6, line7, line8, line9, line10, + line11, line12, line13, line14, line_last + ] + # first translate so that the origin is at the middle of the loop + objects = draw.translate(objects, 0.0, -0.5 * p.box_height) + # now translate and rotate according to the values specified in the dictionary + objects = draw.rotate(objects, p.orientation, origin=(0, 0)) + objects = draw.translate(objects, xoff=p.pos_x, yoff=p.pos_y) + [ + perimeter, initial, arc, arc_left_1, arc_left_2, arc_left_3, + arc_left_4, arc_left_5, arc_left_6, arc_left_7, arc_right_1, + arc_right_2, arc_right_2, arc_right_3, arc_right_4, arc_right_4, + arc_right_5, arc_right_6, arc_right_7, arc_last, final, line1, + line2, line3, line4, line5, line6, line7, line8, line9, line10, + line11, line12, line13, line14, line_last + ] = objects + # give polys names for qgeometry + geom_perimeter = {'poly1': perimeter} + geom_initial = {'poly2': initial} + geom_arc = {'poly3': arc} + geom_arc_left_1 = {'poly4': arc_left_1} + geom_arc_left_2 = {'poly4': arc_left_2} + geom_arc_left_3 = {'poly4': arc_left_3} + geom_arc_left_4 = {'poly4': arc_left_4} + geom_arc_left_5 = {'poly4': arc_left_5} + geom_arc_left_6 = {'poly4': arc_left_6} + geom_arc_left_7 = {'poly4': arc_left_7} + geom_arc_right_1 = {'poly4': arc_right_1} + geom_arc_right_2 = {'poly4': arc_right_2} + geom_arc_right_3 = {'poly4': arc_right_3} + geom_arc_right_4 = {'poly4': arc_right_4} + geom_arc_right_5 = {'poly4': arc_right_5} + geom_arc_right_6 = {'poly4': arc_right_6} + geom_arc_right_7 = {'poly4': arc_right_7} + geom_arc_last = {'poly4': arc_last} + geom_final = {'poly': final} + geom_line1 = {'poly5': line1} + geom_line2 = {'poly6': line2} + geom_line3 = {'poly7': line3} + geom_line4 = {'poly8': line4} + geom_line5 = {'poly9': line5} + geom_line6 = {'poly10': line6} + geom_line7 = {'poly11': line7} + geom_line8 = {'poly11': line8} + geom_line9 = {'poly9': line9} + geom_line10 = {'poly10': line10} + geom_line11 = {'poly11': line11} + geom_line12 = {'poly12': line12} + geom_line13 = {'poly13': line13} + geom_line14 = {'poly14': line14} + geom_line_last = {'poly14': line_last} + # add to qgeometry + self.add_qgeometry('poly', + geom_perimeter, + layer=p.layer, + subtract=False) + self.add_qgeometry('path', + geom_initial, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_1, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_2, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_3, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_4, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_5, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_6, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_left_7, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_1, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_2, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_3, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_4, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_5, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_6, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_right_7, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_arc_last, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line1, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line2, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line3, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line4, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line5, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line6, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line7, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line8, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line9, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line10, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line11, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line12, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line13, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line14, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_line_last, + layer=p.layer, + subtract=False, + width=p.res_width) + self.add_qgeometry('path', + geom_final, + layer=p.layer, + subtract=False, + width=p.res_width) + + ########################## + # Add Qpin connections + # define a function that both rotates and translates the qpin coordinates + def qpin_rotate_translate(x): + y = list(x) + z = [0.0, 0.0] + z[0] = y[0] * cos(p.orientation * 3.14159 / 180) - y[1] * sin( + p.orientation * 3.14159 / 180) + z[1] = y[0] * sin(p.orientation * 3.14159 / 180) + y[1] * cos( + p.orientation * 3.14159 / 180) + z[0] = z[0] + p.pos_x + z[1] = z[1] + p.pos_y + x = (z[0], z[1]) + return x + + # East pin + qp1a = (0.5 * p.box_width - p.perimeter_thickness, 0.0) + qp1b = (0.5 * p.box_width, 0.0) + qp1a = qpin_rotate_translate(qp1a) + qp1b = qpin_rotate_translate(qp1b) + self.add_pin('pin_east', + points=np.array([qp1a, qp1b]), + width=0.01, + input_as_norm=True) + # North-East pin + qpa_ne = (0.5 * p.box_width - p.perimeter_thickness, + 0.5 * p.box_height - p.perimeter_thickness) + qpb_ne = (0.5 * p.box_width, 0.5 * p.box_height) + qpa_ne = qpin_rotate_translate(qpa_ne) + qpb_ne = qpin_rotate_translate(qpb_ne) + self.add_pin('pin_ne', + points=np.array([qpa_ne, qpb_ne]), + width=0.01, + input_as_norm=True) + # South-East pin + qpa_se = (0.5 * p.box_width - p.perimeter_thickness, + -0.5 * p.box_height + p.perimeter_thickness) + qpb_se = (0.5 * p.box_width, -0.5 * p.box_height) + qpa_se = qpin_rotate_translate(qpa_se) + qpb_se = qpin_rotate_translate(qpb_se) + self.add_pin('pin_se', + points=np.array([qpa_se, qpb_se]), + width=0.01, + input_as_norm=True) + # North-West pin + qpa_nw = (-0.5 * p.box_width + p.perimeter_thickness, + 0.5 * p.box_height - p.perimeter_thickness) + qpb_nw = (-0.5 * p.box_width, 0.5 * p.box_height) + qpa_nw = qpin_rotate_translate(qpa_nw) + qpb_nw = qpin_rotate_translate(qpb_nw) + self.add_pin('pin_nw', + points=np.array([qpa_nw, qpb_nw]), + width=0.01, + input_as_norm=True) + # West pin + qp2a = (-0.5 * p.box_width + p.perimeter_thickness, 0.0) + qp2b = (-0.5 * p.box_width, 0.0) + qp2a = qpin_rotate_translate(qp2a) + qp2b = qpin_rotate_translate(qp2b) + self.add_pin('pin_west', + points=np.array([qp2a, qp2b]), + width=0.01, + input_as_norm=True) + # South-West pin + qpa_sw = (-0.5 * p.box_width + p.perimeter_thickness, + -0.5 * p.box_height + p.perimeter_thickness) + qpb_sw = (-0.5 * p.box_width, -0.5 * p.box_height) + qpa_sw = qpin_rotate_translate(qpa_sw) + qpb_sw = qpin_rotate_translate(qpb_sw) + self.add_pin('pin_sw', + points=np.array([qpa_sw, qpb_sw]), + width=0.01, + input_as_norm=True) + # South pin + qpa_s = (0.0, -0.5 * p.box_height + p.perimeter_thickness) + qpb_s = (0.0, -0.5 * p.box_height) + qpa_s = qpin_rotate_translate(qpa_s) + qpb_s = qpin_rotate_translate(qpb_s) + self.add_pin('pin_s', + points=np.array([qpa_s, qpb_s]), + width=0.01, + input_as_norm=True) + # north pin + qpa_n = (0.0, -0.5 * p.box_height + p.perimeter_thickness + p.initial + + 30.0 * p.turn_radius) + qpb_n = (0.0, -0.5 * p.box_height + p.perimeter_thickness + p.initial + + 30.0 * p.turn_radius + p.final) + qpa_n = qpin_rotate_translate(qpa_n) + qpb_n = qpin_rotate_translate(qpb_n) + self.add_pin('pin_n', + points=np.array([qpa_n, qpb_n]), + width=0.01, + input_as_norm=True) diff --git a/qiskit_metal/tests/test_qlibrary_2_options.py b/qiskit_metal/tests/test_qlibrary_2_options.py index c1db9fdd6..ac5294d44 100644 --- a/qiskit_metal/tests/test_qlibrary_2_options.py +++ b/qiskit_metal/tests/test_qlibrary_2_options.py @@ -288,7 +288,7 @@ def test_qlibrary_transmon_concentric_options(self): design, 'my_name') options = my_transmon_concentric.default_options - self.assertEqual(len(options), 16) + self.assertEqual(len(options), 17) self.assertEqual(options['width'], '1000um') self.assertEqual(options['height'], '1000um') self.assertEqual(options['rad_o'], '170um') diff --git a/tutorials/Appendix C Circuit examples/F. Small-quantum-chips/Full Physical Design of iSWAP Gates.ipynb b/tutorials/Appendix C Circuit examples/F. Small-quantum-chips/Full Physical Design of iSWAP Gates.ipynb new file mode 100644 index 000000000..5530d431d --- /dev/null +++ b/tutorials/Appendix C Circuit examples/F. Small-quantum-chips/Full Physical Design of iSWAP Gates.ipynb @@ -0,0 +1,781 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "af4d0d2a", + "metadata": {}, + "source": [ + "# Full Chip Designs of iSWAP Gates " + ] + }, + { + "cell_type": "markdown", + "id": "3650bd31", + "metadata": {}, + "source": [ + "In this demo notebook, we'll go through the physical designs of three examples of iSWAP gates based on published reports in the literature. Full physical design includes the two (or more) qubits making up the iSWAP gate as well as all CPW resonators, terminations and couplers. \n", + "\n", + "The three specific designs that we'll go through can be found here:\n", + "1. Phys. Rev. Appl. 10, 034050 (2018) - iSWAP gate based on one fixed-frequency qubit and one qubit with a flux-tunable frequency. \n", + "2. Phys. Rev. Research 2, 033447 (2020) - iSWAP gate based on two fixed-frequency qubits and a tunable coupler\n", + "3. Phys. Rev. X 11, 021058 (2021) - iSWAP gate based on two fixed-frequency X-mon qubits and an interdigitated tunable coupler. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ad15f59", + "metadata": {}, + "outputs": [], + "source": [ + "import qiskit_metal as metal\n", + "from qiskit_metal import designs, draw\n", + "from qiskit_metal import MetalGUI, Dict, open_docs\n", + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "markdown", + "id": "7340380f", + "metadata": {}, + "source": [ + "### Example #1: One fixed-frequency qubit and one flux-tunable qubit " + ] + }, + { + "cell_type": "markdown", + "id": "8895b1b2", + "metadata": {}, + "source": [ + "For this iSWAP gate design, we'll need to import the concentric qubits used in the design as well as unique lumped resonators, straight transmission lines and launch pads:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf1237bf", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_metal.qlibrary.qubits.transmon_concentric_type_2 import TransmonConcentricType2\n", + "from qiskit_metal.qlibrary.resonator.resonator_lumped import ResonatorLumped\n", + "from qiskit_metal.qlibrary.tlines.straight_path import RouteStraight\n", + "from qiskit_metal.qlibrary.terminations.launchpad_wb import LaunchpadWirebond\n", + "from qiskit_metal.qlibrary.terminations.launchpad_wb_coupled import LaunchpadWirebondCoupled" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "abd3ccb3", + "metadata": {}, + "outputs": [], + "source": [ + "design = designs.DesignPlanar()\n", + "gui = MetalGUI(design)" + ] + }, + { + "cell_type": "markdown", + "id": "f370f0cc", + "metadata": {}, + "source": [ + "Let's first instantiate the six custom components; the two qubits, two launch pads and two lumped resonators: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "461856ec", + "metadata": {}, + "outputs": [], + "source": [ + "design.overwrite_enabled = True\n", + "q1 = TransmonConcentricType2(design, 'qubit1', options=dict(pos_x='0.0mm',orientation='-45'))\n", + "q2 = TransmonConcentricType2(design, 'qubit2', options=dict(pos_y='10.0mm',orientation='135'))\n", + "res1 = ResonatorLumped(design, 'resonator1', options=dict(pos_x='10.0mm', pos_y='10.0mm',orientation='-45'))\n", + "res2 = ResonatorLumped(design, 'resonator2', options=dict(pos_x='-10.0mm', pos_y='0.0mm',orientation='45'))\n", + "\n", + "pad1 = LaunchpadWirebond(design, 'P1_C', options = dict(pos_x='0mm', pos_y='-5mm', orientation='90',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "\n", + "pad2 = LaunchpadWirebond(design, 'P2_C', options = dict(pos_x='-13mm', pos_y='-5mm', orientation='90',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "\n", + "pad3 = LaunchpadWirebond(design, 'P3_C', options = dict(pos_x='15mm', pos_y='7mm', orientation='180',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "3ac91079", + "metadata": {}, + "source": [ + "Now let's connect everything with CPWs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "609ab4ec", + "metadata": {}, + "outputs": [], + "source": [ + "design.variables['cpw_width'] = '0.1mm'\n", + "\n", + "# Connect the two qubits\n", + "route1 = RouteStraight(design, 'route1', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='qubit1',pin='pin2'),\n", + " end_pin=Dict(component='qubit2',pin='pin2')),\n", + " lead=Dict(start_straight='0.0mm',end_straight='0.0mm')))\n", + "\n", + "# Connect top qubit to lumped resonator\n", + "route2 = RouteStraight(design, 'route2', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='resonator1',pin='pin_sw'),\n", + " end_pin=Dict(component='qubit2',pin='pin3')),\n", + " lead=Dict(start_straight='0.0mm',end_straight='0.0mm')))\n", + "\n", + "# Connect bottom qubit to lumped resonator\n", + "route3 = RouteStraight(design, 'route3', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='resonator2',pin='pin_se'),\n", + " end_pin=Dict(component='qubit1',pin='pin3')),\n", + " lead=Dict(start_straight='0.0mm',end_straight='0.0mm')))\n", + "\n", + "# Connect top lumped resonator to launch pad\n", + "route4 = RouteStraight(design, 'route4', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='resonator1',pin='pin_east'),\n", + " end_pin=Dict(component='P3_C',pin='tie')),\n", + " lead=Dict(start_straight='1mm',end_straight='0.0mm')))\n", + "\n", + "# Connect bottom lumped resonator to launch pad\n", + "route5 = RouteStraight(design, 'route5', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='resonator2',pin='pin_west'),\n", + " end_pin=Dict(component='P2_C',pin='tie')),\n", + " lead=Dict(start_straight='1mm',end_straight='0.0mm')))\n", + "\n", + "from qiskit_metal.qlibrary.terminations.open_to_ground import OpenToGround\n", + "otg11 = OpenToGround(design, 'open1i', options=dict(pos_x='0mm', pos_y='-2.5mm', orientation='90'))\n", + "\n", + "# Connect bottom qubit to launch pad\n", + "route5 = RouteStraight(design, 'route6', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='open1i',pin='open'),\n", + " end_pin=Dict(component='P1_C',pin='tie')),\n", + " lead=Dict(start_straight='0mm',end_straight='0.0mm')))\n", + "\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "0b128620", + "metadata": {}, + "source": [ + "And we're done! Here's a look at the final full chip design:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "012446b0", + "metadata": {}, + "outputs": [], + "source": [ + "gui.screenshot()" + ] + }, + { + "cell_type": "markdown", + "id": "75a854e6", + "metadata": {}, + "source": [ + "### Example #2: Two fixed-frequencies transmon qubits and flux-tunable coupler " + ] + }, + { + "cell_type": "markdown", + "id": "4c4aa2de", + "metadata": {}, + "source": [ + "First let's delete the existing objects from the previous design: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4be658ba", + "metadata": {}, + "outputs": [], + "source": [ + "design.delete_all_components()\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "a7c70de2", + "metadata": {}, + "source": [ + "Now let's instantiate the six components used in this design: two transmon pocket qubits, a flux tunable qubit and three launch pads:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0f6aee1", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_metal.qlibrary.qubits.transmon_pocket_6 import TransmonPocket6\n", + "from qiskit_metal.qlibrary.couplers.tunable_coupler_02 import TunableCoupler02\n", + "from qiskit_metal.qlibrary.tlines.straight_path import RouteStraight\n", + "from qiskit_metal.qlibrary.tlines.meandered import RouteMeander\n", + "from qiskit_metal.qlibrary.terminations.launchpad_wb import LaunchpadWirebond" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b58fdb60", + "metadata": {}, + "outputs": [], + "source": [ + "Q1 = TransmonPocket6(design,'Q1', options = dict(\n", + " pos_x='-4mm', \n", + " pos_y='0.0mm',\n", + " orientation='90',\n", + " pad_width='1mm',\n", + " pad_height='0.4mm',\n", + " pocket_width='1.5mm',\n", + " pocket_height='1.2mm',\n", + " pad_gap='0.05mm',\n", + " connection_pads = dict(\n", + " bus_03 = dict(loc_W=0, loc_H=-1, pad_width = '100um', pad_gap = '50um'),\n", + " readout = dict(loc_W=0, loc_H=1, pad_width = '100um', pad_gap = '50um'))))\n", + "\n", + "Q2 = TransmonPocket6(design,'Q2', options = dict(\n", + " pos_x='4mm', \n", + " pos_y='0.0mm',\n", + " orientation='90',\n", + " pad_width='1mm',\n", + " pad_height='0.4mm',\n", + " pocket_width='1.5mm',\n", + " pocket_height='1.2mm',\n", + " pad_gap='0.05mm',\n", + " connection_pads = dict(\n", + " bus_03 = dict(loc_W=0, loc_H=-1, pad_width = '100um', pad_gap = '50um'),\n", + " readout = dict(loc_W=0, loc_H=1, pad_width = '100um', pad_gap = '50um'))))\n", + "\n", + "\n", + "TC = TunableCoupler02(design, 'TC')\n", + "\n", + "pad1 = LaunchpadWirebond(design, 'P1_C', options = dict(pos_x='-7mm', pos_y='0.5mm', orientation='90',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "\n", + "pad2 = LaunchpadWirebond(design, 'P2_C', options = dict(pos_x='7mm', pos_y='0.5mm', orientation='90',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "\n", + "pad3 = LaunchpadWirebond(design, 'P3_C', options = dict(pos_x='3mm', pos_y='3mm', orientation='-180',\n", + " pad_width='0.5mm', pad_height='1mm', lead_length='0um'))\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "b5757644", + "metadata": {}, + "source": [ + "Now we can connect all of the components with CPWs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7beb7e90", + "metadata": {}, + "outputs": [], + "source": [ + "design.variables['cpw_width'] = '0.01mm'\n", + "\n", + "# Connect the two qubits\n", + "route1 = RouteStraight(design, 'route1', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q1',pin='bus_03'),\n", + " end_pin=Dict(component='TC',pin='pin2')),\n", + " lead=Dict(start_straight='0.0mm',end_straight='0.0mm')))\n", + "\n", + "route2 = RouteStraight(design, 'route2', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q2',pin='readout'),\n", + " end_pin=Dict(component='TC',pin='pin1')),\n", + " lead=Dict(start_straight='0.0mm',end_straight='0.0mm')))\n", + "\n", + "route3 = RouteStraight(design, 'route3', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='P3_C',pin='tie'),\n", + " end_pin=Dict(component='TC',pin='fbl')),\n", + " lead=Dict(start_straight='3.4mm',end_straight='0.0mm'),\n", + " fillet='90um'))\n", + "\n", + "route4 = RouteMeander(design, 'route4', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q1',pin='readout'),\n", + " end_pin=Dict(component='P1_C',pin='tie')),\n", + " lead=Dict(start_straight='0mm',end_straight='0.5mm'), \n", + " meander=Dict(asymmetry='0mm'),\n", + " total_length='10mm',\n", + " fillet='90um'))\n", + "\n", + "route5 = RouteMeander(design, 'route5', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q2',pin='bus_03'),\n", + " end_pin=Dict(component='P2_C',pin='tie')),\n", + " lead=Dict(start_straight='0mm',end_straight='0.5mm'), \n", + " meander=Dict(asymmetry='0mm'),\n", + " total_length='10mm',\n", + " fillet='90um'))\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "4cc2b64f", + "metadata": {}, + "source": [ + "Here's a look at the final, complete design:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b017396e", + "metadata": {}, + "outputs": [], + "source": [ + "gui.screenshot()" + ] + }, + { + "cell_type": "markdown", + "id": "0dd3c804", + "metadata": {}, + "source": [ + "The above design closely matches the chip design depicted in Fig. 1 of Phys. Rev. Research 2, 033447 (2020). " + ] + }, + { + "cell_type": "markdown", + "id": "017a63ec", + "metadata": {}, + "source": [ + "### Example #3: Two Fixed-Frequency Xmon Qubits with a Tunable Coupler " + ] + }, + { + "cell_type": "markdown", + "id": "36c8aaf6", + "metadata": {}, + "source": [ + "This third example is similar to the preceeding 3-qubit design but uses Xmon qubits (instead of transmon pocket qubits) and in interdigitated capacitive coupler. We will again start by deleting the previous design. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ea8ec1c", + "metadata": {}, + "outputs": [], + "source": [ + "design.delete_all_components()\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a037406", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_metal.qlibrary.couplers.tunable_coupler_01 import TunableCoupler01\n", + "from qiskit_metal.qlibrary.qubits.transmon_cross_fl import TransmonCrossFL\n", + "from qiskit_metal.qlibrary.terminations.launchpad_wb import LaunchpadWirebond" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8835859c", + "metadata": {}, + "outputs": [], + "source": [ + "design.overwrite_enabled = True\n", + "design.chips.main" + ] + }, + { + "cell_type": "markdown", + "id": "76802cdf", + "metadata": {}, + "source": [ + "We will first place the two X-mon qubits with the flux-tunable coupler located between them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ac2b9ec", + "metadata": {}, + "outputs": [], + "source": [ + "xmon_options1 = dict(\n", + " connection_pads=dict(\n", + " #a = dict( connector_location = '0', connector_type = '0'), \n", + " b = dict(connector_location = '90', connector_type = '1'),\n", + " c = dict(connector_location = '180', connector_type = '0'),\n", + " ),\n", + " pos_x='0.3mm',\n", + ")\n", + "\n", + "xmon_options2 = dict(\n", + " connection_pads=dict(\n", + " #a = dict( connector_location = '0', connector_type = '0'), \n", + " b = dict(connector_location = '90', connector_type = '1'),\n", + " c = dict(connector_location = '180', connector_type = '0'),\n", + " ),\n", + " pos_x='-0.3mm',\n", + " orientation='180',\n", + ")\n", + "\n", + "# Create a new Transmon Cross object with name 'Q1' \n", + "q1 = TransmonCrossFL(design, 'Q1', options=xmon_options1)\n", + "q2 = TransmonCrossFL(design, 'Q2', options=xmon_options2)\n", + "\n", + "TC = TunableCoupler01(design, \"TC\", options = dict(pos_y='0.15mm', a_height='100um'))\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "3df6df33", + "metadata": {}, + "source": [ + "Next, we will place the launch pads:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9509472", + "metadata": {}, + "outputs": [], + "source": [ + "T_west = LaunchpadWirebond(design, 'T_west', options=dict(\n", + " pos_x='-1.5mm',\n", + " pos_y='0.0mm',\n", + "))\n", + "\n", + "T_east = LaunchpadWirebond(design, 'T_east', options=dict(\n", + " pos_x='1.5mm',\n", + " pos_y='0.0mm',\n", + " orientation='180',\n", + "))\n", + "\n", + "T_NE = LaunchpadWirebond(design, 'T_NE', options=dict(\n", + " pos_x='0.5mm',\n", + " pos_y='1mm',\n", + " orientation='-90',\n", + "))\n", + "\n", + "T_NW = LaunchpadWirebond(design, 'T_NW', options=dict(\n", + " pos_x='-0.5mm',\n", + " pos_y='1mm',\n", + " orientation='-90',\n", + "))\n", + "\n", + "T_SW = LaunchpadWirebond(design, 'T_SW', options=dict(\n", + " pos_x='-0.5mm',\n", + " pos_y='-1mm',\n", + " orientation='90',\n", + "))\n", + "\n", + "T_SE = LaunchpadWirebond(design, 'T_SE', options=dict(\n", + " pos_x='0.5mm',\n", + " pos_y='-1mm',\n", + " orientation='90',\n", + "))\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "1519e946", + "metadata": {}, + "source": [ + "Next we'll import the relevant routing modules:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a63c40d", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_metal.qlibrary.tlines.meandered import RouteMeander\n", + "from qiskit_metal.qlibrary.tlines.anchored_path import RouteAnchors\n", + "from qiskit_metal.qlibrary.tlines.pathfinder import RoutePathfinder\n", + "from qiskit_metal.qlibrary.tlines.straight_path import RouteStraight\n", + "from qiskit_metal.qlibrary.tlines.framed_path import RouteFramed\n", + "from qiskit_metal.qlibrary.tlines.mixed_path import RouteMixed" + ] + }, + { + "cell_type": "markdown", + "id": "4779a0eb", + "metadata": {}, + "source": [ + "We'll create the straight routing segments first, which connect the top/bottom launch pads with the qubits and coupler:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40bd24be", + "metadata": {}, + "outputs": [], + "source": [ + "route1 = RouteStraight(design, 'cpw_1', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q1',pin='flux_line'),\n", + " end_pin=Dict(component='T_SE',pin='tie')),\n", + " lead=Dict(start_straight='0.25mm',end_straight='0.25mm')))\n", + "\n", + "route2 = RouteStraight(design, 'cpw_2', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q2',pin='b'),\n", + " end_pin=Dict(component='T_SW',pin='tie')),\n", + " lead=Dict(start_straight='0.25mm',end_straight='0.25mm')))\n", + "\n", + "route3 = RouteStraight(design, 'cpw_3', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='Q1',pin='b'),\n", + " end_pin=Dict(component='T_NE',pin='tie')),\n", + " lead=Dict(start_straight='0.25mm',end_straight='0.25mm')))\n", + "\n", + "route4 = RouteStraight(design, 'cpw_4', \n", + " options=Dict(pin_inputs=Dict(start_pin=Dict(component='TC',pin='Flux'),\n", + " end_pin=Dict(component='T_NW',pin='tie')),\n", + " lead=Dict(start_straight='0.25mm',end_straight='0.25mm')))\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "4f8ba5ef", + "metadata": {}, + "source": [ + "Next we'll create the east-west transmission line connecting the east/west launch pads:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "485e1ad4", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import OrderedDict\n", + "jogs = OrderedDict()\n", + "jogs[0] = [\"R\", '0.5mm']\n", + "jogs[1] = [45, '0.5mm']\n", + "jogs[2] = [45, '1.75mm']\n", + "jogs[3] = [45, '0.5mm']\n", + "jogs[4] = [45, '0.5mm']\n", + "jogs[5] = [\"R\", '0.5mm']\n", + "\n", + "options = Dict(\n", + " total_length= '4.75mm',\n", + " pin_inputs=Dict(\n", + " start_pin=Dict(\n", + " component= 'T_east',\n", + " pin= 'tie'),\n", + " end_pin=Dict(\n", + " component= 'T_west',\n", + " pin= 'tie')),\n", + " lead=Dict(\n", + " start_straight='0.25mm',\n", + " end_straight='0.1mm',\n", + " start_jogged_extension=jogs),\n", + " #start_jogged_extension=jogs,\n", + " #end_jogged_extension=jogs),\n", + " meander=Dict(\n", + " asymmetry='0.0mm')\n", + ")\n", + "\n", + "\n", + "cpw5 = RouteMeander(design,'tline', options=options)\n", + "#cpw5 = RouteStraight(design,options=options)\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "bf600b14", + "metadata": {}, + "source": [ + "Some of the CPWs are capacitively coupled to the main transmission line, so we will create several open-to-grounds at the appropriate locations for our CPWs to terminate:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6f4662c", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_metal.qlibrary.terminations.open_to_ground import OpenToGround\n", + "otg1 = OpenToGround(design, 'open1', options=dict(pos_x='0.25mm', pos_y='0.8mm', orientation='0'))\n", + "otg2 = OpenToGround(design, 'open2', options=dict(pos_x='1.1mm', pos_y='0.0mm', orientation='-90'))\n", + "otg3 = OpenToGround(design, 'open3', options=dict(pos_x='-1.1mm', pos_y='0.0mm', orientation='90'))\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "d6d0c000", + "metadata": {}, + "source": [ + "Now we can route the meandering CPWs to create the capacitive coupling between the qubits/coupler and the main transmission line:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d76c12ab", + "metadata": {}, + "outputs": [], + "source": [ + "options2 = Dict(\n", + " fillet='40um',\n", + " total_length= '1.5mm',\n", + " pin_inputs=Dict(\n", + " start_pin=Dict(\n", + " component= 'TC',\n", + " pin= 'Control'),\n", + " end_pin=Dict(\n", + " component= 'open1',\n", + " pin= 'open')),\n", + " lead=Dict(\n", + " start_straight='0.1mm',\n", + " end_straight='0.1mm'),\n", + " meander=Dict(\n", + " spacing='90um',\n", + " asymmetry='0mm')\n", + ")\n", + "\n", + "coupler_middle = RouteMeander(design,'coupler_mid', options=options2)\n", + "\n", + "options_right = Dict(\n", + " fillet='40um',\n", + " total_length= '2mm',\n", + " pin_inputs=Dict(\n", + " start_pin=Dict(\n", + " component= 'Q1',\n", + " pin= 'c'),\n", + " end_pin=Dict(\n", + " component= 'open2',\n", + " pin= 'open')),\n", + " lead=Dict(\n", + " start_straight='0.1mm',\n", + " end_straight='0.1mm'),\n", + " meander=Dict(\n", + " spacing='90um',\n", + " asymmetry='0mm')\n", + ")\n", + "\n", + "coupler_right = RouteMeander(design,'coupler_right', options=options_right)\n", + "\n", + "options_left = Dict(\n", + " fillet='40um',\n", + " total_length= '2mm',\n", + " pin_inputs=Dict(\n", + " start_pin=Dict(\n", + " component= 'Q2',\n", + " pin= 'c'),\n", + " end_pin=Dict(\n", + " component= 'open3',\n", + " pin= 'open')),\n", + " lead=Dict(\n", + " start_straight='0.1mm',\n", + " end_straight='0.1mm'),\n", + " meander=Dict(\n", + " spacing='90um',\n", + " asymmetry='0mm')\n", + ")\n", + "\n", + "coupler_left = RouteMeander(design,'coupler_left', options=options_left)\n", + "\n", + "gui.rebuild()\n", + "gui.autoscale()" + ] + }, + { + "cell_type": "markdown", + "id": "2f88b332", + "metadata": {}, + "source": [ + "Now the full chip design is complete. Here is a screenshot of the final design:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df887a2f", + "metadata": {}, + "outputs": [], + "source": [ + "gui.screenshot()" + ] + }, + { + "cell_type": "markdown", + "id": "3b8920c5", + "metadata": {}, + "source": [ + "The above design looks very similar to the chip shown in Fig. 1 of Phys. Rev. X 11, 021058 (2021.)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "metal_qamp_v2", + "language": "python", + "name": "metal_qamp_v2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}