Source code for qpdk.cells.junction

"""Josephson junction components."""

from __future__ import annotations

from operator import itemgetter
from typing import TypedDict, Unpack

import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import ComponentSpec, LayerSpec
from klayout.db import DCplxTrans

from qpdk.cells.waveguides import straight
from qpdk.tech import (
    LAYER,
    josephson_junction_cross_section_narrow,
    josephson_junction_cross_section_wide,
)


class SingleJosephsonJunctionWireParams(TypedDict):
    """Type definition for single Josephson junction wire parameters.

    Args:
        wide_straight_length: Length of the wide straight section in µm.
        narrow_straight_length: Length of the narrow straight section in µm.
        taper_length: Length of the taper section in µm.
        cross_section_wide: Cross-section specification for the wide section.
        cross_section_narrow: Cross-section specification for the narrow section.
        layer_patch: Layer for the patch that creates the overlap region.
        size_patch: Size of the patch that creates the overlap region.
    """

    wide_straight_length: float
    narrow_straight_length: float
    taper_length: float
    cross_section_wide: LayerSpec
    cross_section_narrow: LayerSpec
    layer_patch: LayerSpec
    size_patch: tuple[float, float]


_single_josephson_junction_wire_defaults = SingleJosephsonJunctionWireParams(
    wide_straight_length=8.3,
    narrow_straight_length=0.5,
    taper_length=4.7,
    cross_section_wide=josephson_junction_cross_section_wide,
    cross_section_narrow=josephson_junction_cross_section_narrow,
    layer_patch=LAYER.JJ_PATCH,
    size_patch=(1.5, 1.0),
)


[docs] @gf.cell def single_josephson_junction_wire( **kwargs: Unpack[SingleJosephsonJunctionWireParams], ) -> Component: """Creates a single wire to use in a Josephson junction. Args: kwargs: :class:`~SingleJosephsonJunctionWireParams` parameters. """ c = Component() wire_params = _single_josephson_junction_wire_defaults | kwargs ( wide_straight_length, narrow_straight_length, taper_length, cross_section_wide, cross_section_narrow, layer_patch, size_patch, ) = itemgetter( "wide_straight_length", "narrow_straight_length", "taper_length", "cross_section_wide", "cross_section_narrow", "layer_patch", "size_patch", )(wire_params) # Widest straight section with patch wide_straight_ref = c << straight( length=wide_straight_length, cross_section=cross_section_wide ) # Add the tapered transition section taper_ref = c << gf.c.taper_cross_section( length=taper_length, cross_section1=cross_section_wide, cross_section2=cross_section_narrow, linear=True, ) # Narrow straight section with overlap narrow_straight_ref = c << straight( length=narrow_straight_length, cross_section=cross_section_narrow ) # Connect all taper_ref.connect("o1", wide_straight_ref.ports["o2"]) narrow_straight_ref.connect("o1", taper_ref.ports["o2"]) # Add patch to wide section if layer_patch: patch = c << gf.components.rectangle( size=size_patch, layer=layer_patch, centered=True, ) # Overlap with one fourth offset to one side patch.move( (wide_straight_ref.dbbox().p1.x - size_patch[0] / 4, wide_straight_ref.y) ) # Add port at wide end c.add_port( port=wide_straight_ref.ports["o1"], name="o1", cross_section=cross_section_wide ) # Add port at narrow end c.add_port( port=narrow_straight_ref.ports["o2"], name="o2", cross_section=cross_section_narrow, ) return c
[docs] @gf.cell def josephson_junction( junction_overlap_displacement: float = 1.8, **kwargs: Unpack[SingleJosephsonJunctionWireParams], ) -> Component: """Creates a single Josephson junction component. A Josephson junction consists of two superconducting electrodes separated by a thin insulating barrier allowing tunnelling. Args: junction_overlap_displacement: Displacement of the overlap region in µm. Measured from the centers of the junction ports kwargs: :class:`~SingleJosephsonJunctionWireParams` for single wires. """ c = Component() # Wire configuration parameters wire_params = _single_josephson_junction_wire_defaults | kwargs # Left wire left_wire = c << single_josephson_junction_wire(**wire_params) # Right wire right_wire = c << single_josephson_junction_wire(**wire_params) total_length = sum( map( # pyright: ignore[reportArgumentType] wire_params.get, ("wide_straight_length", "narrow_straight_length", "taper_length"), ) ) # Position left wire on top of right wire with rotation left_wire.dcplx_trans = ( right_wire.ports["o2"].dcplx_trans * DCplxTrans.R90 * DCplxTrans( -total_length + junction_overlap_displacement, 0, ) ) right_wire.dcplx_trans *= DCplxTrans(junction_overlap_displacement, 0) # Add ports at wide ends c.add_port( name="left_wide", port=left_wire.ports["o1"], ) c.add_port( name="right_wide", port=right_wire.ports["o1"], ) # One port at overlap c.add_port( name="overlap", center=( left_wire.ports["o2"].dcplx_trans * DCplxTrans(-junction_overlap_displacement, 0) ).disp.to_p(), width=left_wire.ports["o2"].width, orientation=left_wire.ports["o2"].orientation, layer=left_wire.ports["o2"].layer, port_type=left_wire.ports["o2"].port_type, ) # breakpoint() return c
[docs] @gf.cell def squid_junction( junction_spec: ComponentSpec = josephson_junction, loop_area: float = 4, ) -> Component: """Creates a SQUID (Superconducting Quantum Interference Device) junction component. A SQUID consists of two Josephson junctions connected in parallel, forming a loop. See :cite:`clarkeSQUIDHandbook2004` for details. Args: junction_spec: Component specification for the Josephson junction component. loop_area: Area of the SQUID loop in µm². This does not take into account the junction wire widths. """ c = Component() junction_comp = gf.get_component(junction_spec) left_junction = c << junction_comp right_junction = c << junction_comp # Form a cross by positioning overlaps on top of each other right_junction.dcplx_trans = ( left_junction.ports["overlap"].dcplx_trans * DCplxTrans.R90 * DCplxTrans( -left_junction.xmax + (left_junction.xmax - left_junction.ports["overlap"].x), 0, ) ) # Start adding area by displacing junctions displacement_xy = loop_area**0.5 right_junction.dcplx_trans *= DCplxTrans((displacement_xy,) * 2) # Add ports from junctions with descriptive names for junction_name, junction in [("left", left_junction), ("right", right_junction)]: for port_side in ["left", "right"]: port_name = f"{junction_name}_{port_side}_wide" c.add_port(name=port_name, port=junction.ports[f"{port_side}_wide"]) # Overlaps and their center c.add_port(name="left_overlap", port=left_junction.ports["overlap"]) c.add_port(name="right_overlap", port=right_junction.ports["overlap"]) c.add_port( name="loop_center", center=( ( left_junction.ports["overlap"].dcplx_trans.disp + right_junction.ports["overlap"].dcplx_trans.disp ) / 2 ).to_p(), layer=left_junction.ports["overlap"].layer, width=left_junction.ports["overlap"].width, ) return c
if __name__ == "__main__": from qpdk import PDK PDK.activate() # c = josephson_junction() c = squid_junction() c.pprint_ports() c.show()