Source code for kqcircuits.elements.waveguide_coplanar_splitter

# 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 math import cos, sin, pi, radians

from kqcircuits.pya_resolver import pya
from kqcircuits.util.parameters import Param, pdt, add_parameters_from
from kqcircuits.util.geometry_helper import arc_points
from kqcircuits.elements.element import Element
from kqcircuits.elements.airbridges.airbridge import Airbridge


[docs] @add_parameters_from(Airbridge, "airbridge_type") class WaveguideCoplanarSplitter(Element): """ The PCell declaration of a multiway waveguide splitter. The number of ports is defined by the length of the parameter lists. Ports are labelled by letters starting from ``a`` .. MARKERS_FOR_PNG -9,4 -1,-8 9,9 """ lengths = Param(pdt.TypeList, "Waveguide length per port, measured from origin", [11, 11, 11]) angles = Param(pdt.TypeList, "Angle of each port (degrees)", [0, 180, 270]) use_airbridges = Param(pdt.TypeBoolean, "Use airbridges at a distance from the centre", False) bridge_distance = Param(pdt.TypeDouble, "Bridges distance from centre", 80) a_list = Param( pdt.TypeList, "Center conductor widths", [], unit="[μm]", docstring="List of center conductor widths for each port." " If empty, self.a will be used for all ports instead. [μm]", ) b_list = Param( pdt.TypeList, "Gap widths", [], unit="[μm]", docstring="List of gap widths for each port." " If empty, self.b will be used for all ports instead. [μm]", ) port_names = Param(pdt.TypeList, "Port names", ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"])
[docs] def build(self): gap_shapes = [] trace_shapes = [] avoidance_shapes = [] # Tolerance to make sure that the trace shape is larger than the gap shape after integer conversion rounding_tolerance = 10 * self.layout.dbu # Convert a, b to lists of right length a_list = self.a_list if (len(self.a_list) > 0 and self.a_list[0] != "") else [self.a] * len(self.angles) b_list = self.b_list if (len(self.b_list) > 0 and self.b_list[0] != "") else [self.b] * len(self.angles) for length_str, angle_str, port_name, a, b in zip(self.lengths, self.angles, self.port_names, a_list, b_list): angle_deg = float(angle_str) angle_rad = radians(angle_deg) length = float(length_str) a = float(a) b = float(b) # Generate port shapes gap_shapes.append( self._get_port_shape(angle_rad=angle_rad, length=length, width=a + 2 * b).to_itype(self.layout.dbu) ) trace_shapes.append( self._get_port_shape(angle_rad=angle_rad, length=length + rounding_tolerance, width=a).to_itype( self.layout.dbu ) ) avoidance_shapes.append( self._get_port_shape( angle_rad=angle_rad, length=length + self.margin, width=a + 2 * b + 2 * self.margin ).to_itype(self.layout.dbu) ) # Port refpoints self.add_port( port_name, pya.DPoint(length * cos(angle_rad), length * sin(angle_rad)), pya.DVector(self.r * cos(angle_rad), self.r * sin(angle_rad)), ) # Waveguide length annotation self.cell.shapes(self.get_layer("waveguide_path")).insert( pya.DPath([self.refpoints[f"port_{port_name}"], self.refpoints["base"]], a) ) # Airbridges if self.use_airbridges: ab_trans = pya.DCplxTrans( 1, angle_deg, False, self.bridge_distance * cos(angle_rad), self.bridge_distance * sin(angle_rad) ) ab_cell = self.add_element(Airbridge, pad_length=14, pad_extra=2) self.insert_cell(ab_cell, ab_trans) # Merge and insert shapes self.cell.shapes(self.get_layer("base_metal_gap_wo_grid")).insert( pya.Region(gap_shapes) - pya.Region(trace_shapes) ) self.add_protection(pya.Region(avoidance_shapes).merged())
def _get_port_shape(self, angle_rad, length, width): # Generate a shape consisting of a rectangle (length, width) starting at (0, 0), with a round cap at the origin # side. r = width / 2 # Radius of round cap # Straight section c = cos(angle_rad) s = sin(angle_rad) points = [ pya.DPoint(length * c - r * s, length * s + r * c), pya.DPoint(length * c + r * s, length * s - r * c), ] # Corner section angles = [radians(float(angle)) for angle in self.angles] prev_rad = min([2 * pi if a == angle_rad else (angle_rad - a) % (2 * pi) for a in angles]) next_rad = min([2 * pi if a == angle_rad else (a - angle_rad) % (2 * pi) for a in angles]) if prev_rad <= pi / 2: dist = r * cos(prev_rad) / sin(prev_rad) if length > dist: points.append(pya.DPoint(dist * c + r * s, dist * s - r * c)) else: points += arc_points(r, angle_rad - pi / 2, angle_rad - prev_rad, self.n) points.append(pya.DPoint(0.0, 0.0)) if next_rad <= pi / 2: dist = r * cos(next_rad) / sin(next_rad) if length > dist: points.append(pya.DPoint(dist * c - r * s, dist * s + r * c)) else: points += arc_points(r, angle_rad + next_rad, angle_rad + pi / 2, self.n) return pya.DPolygon(points)
[docs] def t_cross_parameters( a=Element.get_schema()["a"].default, b=Element.get_schema()["b"].default, a2=Element.a, b2=Element.b, length_extra=0, length_extra_side=0, **kwargs, ): """A utility function to easily produce T-cross splitter (old WaveguideCoplanarTCross). Args: a: Width of center conductor b: Width of gap a2: Center conductor width of the side waveguide b2: Gap of the side waveguide length_extra: Extra length length_extra_side: Extra length of the side waveguide Returns: dictionary of parameters for WaveguideCoplanarSplitter """ length = a2 / 2 + b2 + length_extra length2 = a / 2 + b + length_extra_side return { "lengths": [length, length, length2], "angles": [0, 180, 270], "a_list": [a, a, a2], "b_list": [b, b, b2], "port_names": ["right", "left", "bottom"], **kwargs, }