# This code is part of KQCircuits
# Copyright (C) 2021 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).
from kqcircuits.elements.element import Element
from kqcircuits.pya_resolver import pya
[docs]
class TestStructure(Element):
"""Base PCell declaration for test structures."""
LIBRARY_NAME = "Test Structure Library"
LIBRARY_DESCRIPTION = "Superconducting quantum circuit library for test structures."
LIBRARY_PATH = "test_structures"
[docs]
def produce_pad(self, x, y, pads_region, pad_width, pad_height):
"""Inserts a square pad shape to pads_region.
Args:
x: x-coordinate of the pad center
y: y-coordinate of the pad center
pads_region: Region to which the pad shape is inserted
pad_width: width (and height) of the pad
"""
offset_x = pad_width / 2
offset_y = pad_height / 2
pad = pya.DPolygon(
[
pya.DPoint(x - offset_x, y - offset_y),
pya.DPoint(x - offset_x, y + offset_y),
pya.DPoint(x + offset_x, y + offset_y),
pya.DPoint(x + offset_x, y - offset_y),
]
)
pads_region.insert(pad.to_itype(self.layout.dbu))
[docs]
def produce_four_point_pads(
self,
pads_region,
pad_width,
pad_height,
pad_spacing_x,
pad_spacing_y,
connect_pads,
trans=pya.DTrans(),
refpoint_prefix="probe",
refpoint_distance=None,
):
"""Inserts four pads to pads_region.
Args:
pads_region: Region to which the pad shapes are inserted
pad_width: width (and height) of the pads
pad_spacing_x: x-distance between the pads
pad_spacing_y: y-distance between the pads
connect_pads: Boolean determining if the two pads on each side are connected
trans: transformation applied to the pads
refpoint_prefix: prefix used for the refpoint names
refpoint_distance: Horizontal distance of refpoints from closest outer edges.
If None, the refpoints will be at the center of the pads.
"""
pad_offset_x = (pad_spacing_x + pad_width) / 2
pad_offset_y = (pad_spacing_y + pad_height) / 2
pos_sw = pya.DPoint(trans.disp.x - pad_offset_x, trans.disp.y - pad_offset_y)
pos_nw = pya.DPoint(trans.disp.x - pad_offset_x, trans.disp.y + pad_offset_y)
pos_ne = pya.DPoint(trans.disp.x + pad_offset_x, trans.disp.y + pad_offset_y)
pos_se = pya.DPoint(trans.disp.x + pad_offset_x, trans.disp.y - pad_offset_y)
self.produce_pad(pos_sw.x, pos_sw.y, pads_region, pad_width, pad_height)
self.produce_pad(pos_nw.x, pos_nw.y, pads_region, pad_width, pad_height)
self.produce_pad(pos_ne.x, pos_ne.y, pads_region, pad_width, pad_height)
self.produce_pad(pos_se.x, pos_se.y, pads_region, pad_width, pad_height)
if connect_pads:
pad_connection_box_width = 50
pad_connection_box = pya.DBox(pya.DPoint(0, 0), pya.DPoint(pad_connection_box_width, pad_spacing_y))
trans_left = pya.DTrans(-pad_spacing_x / 2 - pad_connection_box_width, -pad_spacing_y / 2)
trans_right = pya.DTrans(pad_spacing_x / 2, -pad_spacing_y / 2)
pads_region.insert((trans * trans_left * pad_connection_box).to_itype(self.layout.dbu))
pads_region.insert((trans * trans_right * pad_connection_box).to_itype(self.layout.dbu))
if refpoint_distance is None:
self.refpoints[f"{refpoint_prefix}_sw"] = pos_sw
self.refpoints[f"{refpoint_prefix}_nw"] = pos_nw
self.refpoints[f"{refpoint_prefix}_ne"] = pos_ne
self.refpoints[f"{refpoint_prefix}_se"] = pos_se
else:
self.refpoints[f"{refpoint_prefix}_sw"] = pos_sw + pya.DVector(-pad_width / 2 + refpoint_distance, 0)
self.refpoints[f"{refpoint_prefix}_nw"] = pos_nw + pya.DVector(-pad_width / 2 + refpoint_distance, 0)
self.refpoints[f"{refpoint_prefix}_ne"] = pos_ne + pya.DVector(pad_width / 2 - refpoint_distance, 0)
self.refpoints[f"{refpoint_prefix}_se"] = pos_se + pya.DVector(pad_width / 2 - refpoint_distance, 0)
[docs]
def produce_etched_region(self, metal_region, pos, width, height):
"""Produces structures in metal gap layer.
Subtracts metal_region from a region defined by width and height, and inserts resulting
region to metal gap layer. Also adds grid avoidance for the area.
Args:
metal_region: region subtracted from the region in metal gap layer
pos: center of the produced region (pya.DPoint)
width: width of the produced region
height: height of the produced region
"""
test_area = pya.DPolygon(
[
pya.DPoint(pos.x + width / 2, pos.y + height / 2),
pya.DPoint(pos.x + width / 2, pos.y - height / 2),
pya.DPoint(pos.x - width / 2, pos.y - height / 2),
pya.DPoint(pos.x - width / 2, pos.y + height / 2),
]
)
reg_test_area = pya.Region(test_area.to_itype(self.layout.dbu))
# etched region
reg_etch = reg_test_area - metal_region
self.cell.shapes(self.get_layer("base_metal_gap_wo_grid")).insert(reg_etch)
# grid avoidance region
reg_protect = reg_etch.extents(int(self.margin / self.layout.dbu))
self.cell.shapes(self.get_layer("ground_grid_avoidance")).insert(reg_protect)