# This code is part of KQCircuits
# Copyright (C) 2022 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).
import math
from kqcircuits.elements.element import Element
from kqcircuits.junctions.squid import Squid
from kqcircuits.junctions.manhattan import Manhattan
from kqcircuits.junctions.manhattan_single_junction import ManhattanSingleJunction
from kqcircuits.util.parameters import Param, pdt, add_parameters_from
from kqcircuits.qubits.qubit import Qubit
from kqcircuits.pya_resolver import pya
from kqcircuits.util.refpoints import WaveguideToSimPort, JunctionSimPort
[docs]
@add_parameters_from(Squid, junction_type="Manhattan")
@add_parameters_from(Manhattan)
@add_parameters_from(ManhattanSingleJunction)
class DoublePads(Qubit):
"""A two-island qubit, consisting of two rounded rectangles shunted by a junction, with one capacitive coupler.
Contains a coupler on the north edge and two separate qubit islands in the center
joined by a junction or SQUID loaded from another library.
Refpoint for a readout line at the opening to the coupler and a modifiable refpoint for
a driveline.
"""
ground_gap = Param(pdt.TypeList, "Width, height of the ground gap (µm, µm)", [700, 700])
ground_gap_r = Param(pdt.TypeDouble, "Ground gap rounding radius", 50, unit="μm")
coupler_extent = Param(pdt.TypeList, "Width, height of the coupler (µm, µm)", [150, 20])
coupler_r = Param(pdt.TypeDouble, "Coupler rounding radius", 10, unit="μm")
coupler_a = Param(pdt.TypeDouble, "Width of the coupler waveguide center conductor", Element.a, unit="μm")
coupler_offset = Param(pdt.TypeDouble, "Distance from first qubit island to coupler", 20, unit="μm")
squid_offset = Param(pdt.TypeDouble, "Offset between SQUID center and qubit center", 0, unit="μm")
island1_extent = Param(pdt.TypeList, "Width, height of the first qubit island (µm, µm)", [500, 100])
island1_r = Param(pdt.TypeDouble, "First qubit island rounding radius", 50, unit="μm")
island2_extent = Param(pdt.TypeList, "Width, height of the second qubit island (µm, µm)", [500, 100])
island2_r = Param(pdt.TypeDouble, "Second qubit island rounding radius", 50, unit="μm")
drive_position = Param(pdt.TypeList, "Coordinate for the drive port (µm, µm)", [-450, 0])
island1_taper_width = Param(pdt.TypeDouble, "First qubit island tapering width on the island side", 10, unit="µm")
island1_taper_junction_width = Param(
pdt.TypeDouble, "First qubit island tapering width on the junction side", 10, unit="µm"
)
island2_taper_width = Param(pdt.TypeDouble, "Second qubit island tapering width on the island side", 10, unit="µm")
island2_taper_junction_width = Param(
pdt.TypeDouble, "Second qubit island tapering width on the junction side", 10, unit="µm"
)
island_island_gap = Param(pdt.TypeDouble, "Island to island gap distance", 70, unit="µm")
with_squid = Param(pdt.TypeBoolean, "Boolean whether to include the squid", True)
[docs]
def build(self):
# Qubit base
ground_gap_points = [
pya.DPoint(float(self.ground_gap[0]) / 2, float(self.ground_gap[1]) / 2),
pya.DPoint(float(self.ground_gap[0]) / 2, -float(self.ground_gap[1]) / 2),
pya.DPoint(-float(self.ground_gap[0]) / 2, -float(self.ground_gap[1]) / 2),
pya.DPoint(-float(self.ground_gap[0]) / 2, float(self.ground_gap[1]) / 2),
]
ground_gap_polygon = pya.DPolygon(ground_gap_points)
ground_gap_region = pya.Region(ground_gap_polygon.to_itype(self.layout.dbu))
ground_gap_region.round_corners(
self.ground_gap_r / self.layout.dbu, self.ground_gap_r / self.layout.dbu, self.n
)
# SQUID
# Create temporary SQUID cell to calculate SQUID height
temp_squid_cell = self.add_element(Squid, junction_type=self.junction_type)
temp_squid_ref = self.get_refpoints(temp_squid_cell)
squid_height = temp_squid_ref["port_common"].distance(pya.DPoint(0, 0))
# Now actually add SQUID
squid_transf = pya.DCplxTrans(1, 0, False, pya.DVector(0, self.squid_offset - squid_height / 2))
if self.with_squid:
self.produce_squid(squid_transf)
taper_height = (self.island_island_gap - squid_height) / 2
# First island
island1_region = self._build_island1(squid_height, taper_height)
# Second island
island2_region = self._build_island2(squid_height, taper_height)
# Coupler gap
coupler_region = self._build_coupler(
(self.squid_offset + squid_height / 2) + taper_height + float(self.island1_extent[1])
)
self.cell.shapes(self.get_layer("base_metal_gap_wo_grid")).insert(
ground_gap_region - coupler_region - island1_region - island2_region
)
# Protection
protection_polygon = pya.DPolygon(
[
p + pya.DVector(math.copysign(self.margin, p.x), math.copysign(self.margin, p.y))
for p in ground_gap_points
]
)
protection_region = pya.Region(protection_polygon.to_itype(self.layout.dbu))
protection_region.round_corners(
(self.ground_gap_r + self.margin) / self.layout.dbu,
(self.ground_gap_r + self.margin) / self.layout.dbu,
self.n,
)
self.add_protection(protection_region)
# Coupler port
self.add_port(
"cplr",
pya.DPoint(0, float(self.ground_gap[1]) / 2),
direction=pya.DVector(pya.DPoint(0, float(self.ground_gap[1]))),
)
# Drive port
self.add_port(
"drive",
pya.DPoint(float(self.drive_position[0]), float(self.drive_position[1])),
direction=pya.DVector(float(self.drive_position[0]), float(self.drive_position[1])),
)
# Probepoints
self.refpoints["probe_island_1"] = pya.DPoint(
0, self.squid_offset + squid_height / 2 + taper_height + float(self.island1_extent[1]) / 2
)
self.refpoints["probe_island_2"] = pya.DPoint(
0, self.squid_offset - squid_height / 2 - taper_height - float(self.island2_extent[1]) / 2
)
def _build_coupler(self, first_island_top_edge):
coupler_top_edge = first_island_top_edge + self.coupler_offset + float(self.coupler_extent[1])
coupler_polygon = pya.DPolygon(
[
pya.DPoint(-float(self.coupler_extent[0]) / 2, coupler_top_edge),
pya.DPoint(-float(self.coupler_extent[0]) / 2, first_island_top_edge + self.coupler_offset),
pya.DPoint(float(self.coupler_extent[0]) / 2, first_island_top_edge + self.coupler_offset),
pya.DPoint(float(self.coupler_extent[0]) / 2, coupler_top_edge),
]
)
coupler_region = pya.Region(coupler_polygon.to_itype(self.layout.dbu))
coupler_region.round_corners(self.coupler_r / self.layout.dbu, self.coupler_r / self.layout.dbu, self.n)
coupler_path_polygon = pya.DPolygon(
[
pya.DPoint(-self.coupler_a / 2, (float(self.ground_gap[1]) / 2)),
pya.DPoint(self.coupler_a / 2, (float(self.ground_gap[1]) / 2)),
pya.DPoint(self.coupler_a / 2, coupler_top_edge),
pya.DPoint(-self.coupler_a / 2, coupler_top_edge),
]
)
coupler_path = pya.Region(coupler_path_polygon.to_itype(self.layout.dbu))
return coupler_region + coupler_path
def _build_island1(self, squid_height, taper_height):
island1_bottom = self.squid_offset + squid_height / 2
island1_polygon = pya.DPolygon(
[
pya.DPoint(
-float(self.island1_extent[0]) / 2, island1_bottom + taper_height + float(self.island1_extent[1])
),
pya.DPoint(
float(self.island1_extent[0]) / 2, island1_bottom + taper_height + float(self.island1_extent[1])
),
pya.DPoint(float(self.island1_extent[0]) / 2, island1_bottom + taper_height),
pya.DPoint(-float(self.island1_extent[0]) / 2, island1_bottom + taper_height),
]
)
island1_region = pya.Region(island1_polygon.to_itype(self.layout.dbu))
island1_region.round_corners(self.island1_r / self.layout.dbu, self.island1_r / self.layout.dbu, self.n)
island1_taper = pya.Region(
pya.DPolygon(
[
pya.DPoint(self.island1_taper_width / 2, island1_bottom + taper_height),
pya.DPoint(self.island1_taper_junction_width / 2, island1_bottom),
pya.DPoint(-self.island1_taper_junction_width / 2, island1_bottom),
pya.DPoint(-self.island1_taper_width / 2, island1_bottom + taper_height),
]
).to_itype(self.layout.dbu)
)
return island1_region + island1_taper
def _build_island2(self, squid_height, taper_height):
island2_top = self.squid_offset - squid_height / 2
island2_polygon = pya.DPolygon(
[
pya.DPoint(
-float(self.island2_extent[0]) / 2, island2_top - taper_height - float(self.island2_extent[1])
),
pya.DPoint(
float(self.island2_extent[0]) / 2, island2_top - taper_height - float(self.island2_extent[1])
),
pya.DPoint(float(self.island2_extent[0]) / 2, island2_top - taper_height),
pya.DPoint(-float(self.island2_extent[0]) / 2, island2_top - taper_height),
]
)
island2_region = pya.Region(island2_polygon.to_itype(self.layout.dbu))
island2_region.round_corners(self.island2_r / self.layout.dbu, self.island2_r / self.layout.dbu, self.n)
island2_taper = pya.Region(
pya.DPolygon(
[
pya.DPoint(self.island2_taper_width / 2, island2_top - taper_height),
pya.DPoint(self.island2_taper_junction_width / 2, island2_top),
pya.DPoint(-self.island2_taper_junction_width / 2, island2_top),
pya.DPoint(-self.island2_taper_width / 2, island2_top - taper_height),
]
).to_itype(self.layout.dbu)
)
return island2_region + island2_taper
[docs]
@classmethod
def get_sim_ports(cls, simulation): # pylint: disable=unused-argument
return [JunctionSimPort(), WaveguideToSimPort("port_cplr", side="top")]