# This code is part of KQCircuits
# Copyright (C) 2024 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 logging
from typing import Callable
from kqcircuits.pya_resolver import pya
from kqcircuits.simulations.epr.util import extract_child_simulation, EPRTarget, create_bulk_and_mer_partition_regions
from kqcircuits.simulations.partition_region import PartitionRegion
# Partition region and correction cuts definitions for Swissmon qubit
[docs]
def partition_regions(simulation: EPRTarget, prefix: str = "") -> list[PartitionRegion]:
    metal_edge_dimension = 4.0
    metal_edge_margin = pya.DPoint(metal_edge_dimension, metal_edge_dimension)
    cross_poly = pya.DPolygon([simulation.refpoints[f"epr_cross_{idx:02d}"] for idx in range(12)]).sized(
        metal_edge_dimension + simulation.island_r
    )
    result = create_bulk_and_mer_partition_regions(
        name=f"{prefix}cross",
        face=simulation.face_ids[0],
        metal_edge_dimensions=metal_edge_dimension,
        region=cross_poly,
        vertical_dimensions=3.0,
        visualise=True,
    )
    # These are added to include the waveguides attached to couplers in the coupler region
    def _get_coupler_wg_offset(i, s):
        coupler_wg_offsets = [{"min": pya.DPoint(-150, 0)}, {"max": pya.DPoint(0, 150)}, {"max": pya.DPoint(150, 0)}]
        return coupler_wg_offsets[i].get(s, pya.DPoint(0, 0))
    for idx in range(3):
        # Need to check if coupler is present
        if float(simulation.cpl_length[idx]) > 0:
            cplr_region = pya.DBox(
                simulation.refpoints[f"epr_cplr{idx}_min"] - metal_edge_margin + _get_coupler_wg_offset(idx, "min"),
                simulation.refpoints[f"epr_cplr{idx}_max"] + metal_edge_margin + _get_coupler_wg_offset(idx, "max"),
            )
            result += create_bulk_and_mer_partition_regions(
                name=f"{prefix}{idx}cplr",
                face=simulation.face_ids[0],
                metal_edge_dimensions=metal_edge_dimension,
                region=cplr_region,
                vertical_dimensions=3.0,
                visualise=True,
            )
    return result 
[docs]
def correction_cuts(simulation: EPRTarget, prefix: str = "") -> dict[str, dict]:
    cross_corner = simulation.refpoints["epr_cross_09"]
    cross_corner_h = (cross_corner.y - simulation.refpoints["epr_cross_06"].y) / 2
    coupler_corner = (
        simulation.refpoints["epr_cplr0_max"]
        if float(simulation.cpl_length[0]) > 0
        else simulation.refpoints["epr_cross_08"]
    )
    half_gap = float(simulation.gap_width[1]) / 2
    if len(set(simulation.gap_width)) > 1:
        logging.warning("Partition regions for Swissmon with varying gaps are not implemented")
        logging.warning(
            "Using correction with %s gap for all arms with gap widths %s", str(2 * half_gap), str(simulation.gap_width)
        )
    cross_xsection_center = pya.DPoint((cross_corner.x + coupler_corner.x) / 2, cross_corner.y - cross_corner_h)
    half_cut_length = 30.0 + cross_corner_h
    result = {
        f"{prefix}crossmer": {
            "p1": cross_xsection_center + pya.DPoint(0, -half_cut_length),
            "p2": cross_xsection_center + pya.DPoint(0, half_cut_length),
        }
    }
    half_gap = simulation.b / 2
    if float(simulation.cpl_length[0]) > 0:
        xsection_point = float(simulation.cpl_gap[0]) / 2 + float(simulation.cpl_width[0]) / 2
        result[f"{prefix}0cplrmer"] = {
            "p1": simulation.refpoints["port_cplr0"] + pya.DPoint(-half_cut_length + half_gap, xsection_point),
            "p2": simulation.refpoints["port_cplr0"] + pya.DPoint(half_cut_length + half_gap, xsection_point),
        }
    if float(simulation.cpl_length[1]) > 0:
        xsection_point = float(simulation.cpl_gap[1]) / 2 + float(simulation.cpl_width[1]) / 2
        result[f"{prefix}1cplrmer"] = {
            "p1": simulation.refpoints["port_cplr1"] + pya.DPoint(xsection_point, half_cut_length - half_gap),
            "p2": simulation.refpoints["port_cplr1"] + pya.DPoint(xsection_point, -half_cut_length - half_gap),
        }
    if float(simulation.cpl_length[2]) > 0:
        xsection_point = float(simulation.cpl_gap[2]) / 2 + float(simulation.cpl_width[2]) / 2
        result[f"{prefix}2cplrmer"] = {
            "p1": simulation.refpoints["port_cplr2"] + pya.DPoint(-half_cut_length - half_gap, xsection_point),
            "p2": simulation.refpoints["port_cplr2"] + pya.DPoint(half_cut_length - half_gap, xsection_point),
        }
    return result