# This code is part of KQCircuits
# Copyright (C) 2023 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 pi
from kqcircuits.pya_resolver import pya
from kqcircuits.util.parameters import Param, pdt
from kqcircuits.elements.element import Element
from kqcircuits.elements.waveguide_coplanar import WaveguideCoplanar
from kqcircuits.util.refpoints import WaveguideToSimPort
[docs]
class HangerResonator(Element):
    """
    Hanger Resonator
    """
    coupling_length = Param(pdt.TypeDouble, "Length of the resonator center part (coupling)", 500, unit="μm")
    head_length = Param(pdt.TypeDouble, "Length of the resonator left waveguide (head) ", 300, unit="μm")
    resonator_length = Param(pdt.TypeDouble, "Total length of the resonator", 1000, unit="μm")
    res_a = Param(pdt.TypeDouble, "Trace width of resonator line", 10, unit="μm")
    res_b = Param(pdt.TypeDouble, "Gap width of resonator line", 6, unit="μm")
    ground_width = Param(pdt.TypeDouble, "Trace width of middle ground", 10, unit="μm")
[docs]
    def build(self):
        # If turn radius is smaller than half of the trace width it will create some overlapping masks and sharp angles
        if self.r < self.res_a / 2:
            self.raise_error_on_cell(f"Turn radius must be at least res_a/2, now r={self.r}, res_a/2={self.res_a/2}")
        # probe line, origin at the center of the trace
        points_pl = [pya.DPoint(0, 0)]
        points_pl.append(pya.DPoint(self.coupling_length, 0))
        # distance from origin to start of the wg trace
        wg_start_height = -self.a / 2 - self.b - self.ground_width - self.res_b
        # x distance from pl port to center trace of vertical waveguides
        corner_x = self.r
        # corner arc length
        corner_length = pi * self.r / 2
        head_length_down = self.head_length - corner_length
        points = []
        if head_length_down > 0:
            # left side, head
            # left leg
            p1 = pya.DPoint(-corner_x, wg_start_height - self.res_a / 2 - self.r - head_length_down)
            points.append(p1)
            # corner
            p2 = pya.DPoint(-corner_x, wg_start_height - self.res_a / 2)
            length_without_tail = self.head_length + self.coupling_length + corner_length
        # If head lenght is too small don't create the curve on left side
        else:
            # Add a stub corresponding to head length if head length is shorter than the corner
            p2 = pya.DPoint(-max(self.head_length, 0), wg_start_height - self.res_a / 2)
            length_without_tail = self.coupling_length + corner_length
        points.append(p2)
        # If given resonator length is too small, don't create downwards tail
        if length_without_tail >= self.resonator_length:
            # Add a stub corresponding to tail length if head length is shorter than the corner
            x_tail_offset = max(0, self.resonator_length - (length_without_tail - corner_length))
            p3 = pya.DPoint(self.coupling_length + x_tail_offset, wg_start_height - self.res_a / 2)
            points.append(p3)
        else:
            tail_length = self.resonator_length - length_without_tail
            # right leg (tail)
            p3 = pya.DPoint(self.coupling_length + corner_x, wg_start_height - self.res_a / 2)
            p4 = pya.DPoint(self.coupling_length + corner_x, wg_start_height - self.res_a / 2 - self.r - tail_length)
            points.append(p3)
            points.append(p4)
        cells_pl, _ = self.insert_cell(WaveguideCoplanar, path=points_pl)
        cells_resonator, _ = self.insert_cell(WaveguideCoplanar, path=points, a=self.res_a, b=self.res_b)
        self.copy_port("a", cells_pl)
        self.copy_port("b", cells_pl)
        self.copy_port("a", cells_resonator, "resonator_a")
        self.copy_port("b", cells_resonator, "resonator_b") 
[docs]
    @classmethod
    def get_sim_ports(cls, simulation):
        return [
            WaveguideToSimPort("port_a", use_internal_ports=False, a=simulation.a, b=simulation.b),
            WaveguideToSimPort("port_b", use_internal_ports=False, a=simulation.a, b=simulation.b),
            WaveguideToSimPort("port_resonator_a", a=simulation.res_a, b=simulation.res_b),
            WaveguideToSimPort("port_resonator_b", a=simulation.res_a, b=simulation.res_b),
        ]