Source code for qpdk.cells.coupler_capacitive

"""Capacitive coupler components."""

from __future__ import annotations

import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import LayerSpec

from qpdk.tech import LAYER


[docs] @gf.cell_with_module_name def coupler_capacitive( pad_width: float = 20.0, pad_height: float = 90.0, gap: float = 2.0, feed_width: float = 10.0, feed_length: float = 30.0, layer_metal: LayerSpec = LAYER.M1_DRAW, port_type: str = "electrical", ) -> Component: """Creates a capacitive coupler for quantum circuits. A capacitive coupler consists of two metal pads separated by a small gap, providing capacitive coupling between circuit elements like qubits and resonators. .. code:: ______ ______ _______ | | | | _______ | | | | | || | | feed1 | | pad1 | ====gap==== | pad2 || feed2 | | | | | | || | |_______| | | | ||_______| |______| |______| Args: pad_width: Width of each coupling pad in μm. pad_height: Height of each coupling pad in μm. gap: Gap between the coupling pads in μm. feed_width: Width of the feed lines in μm. feed_length: Length of the feed lines in μm. layer_metal: Layer for the metal structures. port_type: Type of port to add to the component. Returns: Component: A gdsfactory component with the capacitive coupler geometry. """ c = Component() # Create left coupling pad left_pad = gf.components.rectangle( size=(pad_width, pad_height), layer=layer_metal, ) left_pad_ref = c.add_ref(left_pad) left_pad_ref.move((-pad_width - gap / 2, -pad_height / 2)) # Create right coupling pad right_pad = gf.components.rectangle( size=(pad_width, pad_height), layer=layer_metal, ) right_pad_ref = c.add_ref(right_pad) right_pad_ref.move((gap / 2, -pad_height / 2)) # Create left feed line left_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) left_feed_ref = c.add_ref(left_feed) left_feed_ref.move((-pad_width - gap / 2 - feed_length, -feed_width / 2)) # Create right feed line right_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) right_feed_ref = c.add_ref(right_feed) right_feed_ref.move((gap / 2 + pad_width, -feed_width / 2)) # Add ports c.add_port( name="left", center=(-pad_width - gap / 2 - feed_length, 0), width=feed_width, orientation=180, layer=layer_metal, port_type=port_type, ) c.add_port( name="right", center=(gap / 2 + pad_width + feed_length, 0), width=feed_width, orientation=0, layer=layer_metal, port_type=port_type, ) # Add metadata c.info["coupler_type"] = "capacitive" c.info["pad_width"] = pad_width c.info["pad_height"] = pad_height c.info["gap"] = gap c.info["coupling_area"] = pad_width * pad_height return c
[docs] @gf.cell_with_module_name def coupler_interdigital( fingers: int = 6, finger_length: float = 30.0, finger_width: float = 2.0, finger_gap_vertical: float = 2.0, finger_gap_horizontal: float = 3.0, feed_width: float = 10.0, feed_length: float = 30.0, layer_metal: LayerSpec = LAYER.M1_DRAW, port_type: str = "electrical", ) -> Component: """Creates an interdigital capacitive coupler. Each side includes a base column (a vertical metal block) to which the fingers are attached. - The width of the base column is equal to the height of the fingers. - The finger_length parameter refers only to the length of the fingers *extending from the base*, and does NOT include the base column width Args: fingers: Number of fingers per side. finger_length: Length of each finger in μm (see note above). finger_width: Width of each finger in μm. finger_gap_vertical: Vertical gap between fingers in μm (g1). finger_gap_horizontal: Horizontal gap between fingers in μm (g2). feed_width: Width of the feed lines in μm. feed_length: Length of the feed lines in μm. layer_metal: Layer for the metal structures. port_type: Type of port to add to the component. Returns: Component: A gdsfactory component with the interdigital coupler geometry. .. code:: ┌────────┐ base columns ↓ ↓ ┌────────┐ ┌────────┐ │ │█████████████ █│ │ │ │█ g1 █│ │ │ │█ <─g2─> █████████████│ │ │ │█ █│ │ │ feed1 │█████████████ █│ feed2 │ │ │█ █│ │ │ │█ █████████████│ │ │ │█ █│ │ │ │█████████████ █│ │ └────────┘█ █└────────┘ """ c = Component() # Calculate total dimensions total_width = finger_length + finger_gap_horizontal total_height = fingers * finger_width + (fingers - 1) * finger_gap_vertical # Create left side base column left_base = gf.components.rectangle( size=(finger_width, total_height), layer=layer_metal, ) left_base_ref = c.add_ref(left_base) left_base_ref.move((-total_width / 2 - finger_width, -total_height / 2)) # Create right side base column right_base = gf.components.rectangle( size=(finger_width, total_height), layer=layer_metal, ) right_base_ref = c.add_ref(right_base) right_base_ref.move((total_width / 2, -total_height / 2)) # Create interdigital fingers for i in range(fingers): left_finger = gf.components.rectangle( size=(finger_length, finger_width), layer=layer_metal, ) left_finger_ref = c.add_ref(left_finger) # We start from a left finger x_pos = -finger_length / 2 + (-1) ** (i + 1) * finger_gap_horizontal / 2 y_pos = ( total_height / 2 - finger_width - i * (finger_width + finger_gap_vertical) ) left_finger_ref.move((x_pos, y_pos)) # Create feed lines left_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) left_feed_ref = c.add_ref(left_feed) left_feed_ref.move((-total_width / 2 - finger_width - feed_length, -feed_width / 2)) right_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) right_feed_ref = c.add_ref(right_feed) right_feed_ref.move((total_width / 2 + finger_width, -feed_width / 2)) # Add ports c.add_port( name="left", center=(-total_width / 2 - finger_width - feed_length, 0), width=feed_width, orientation=180, layer=layer_metal, port_type=port_type, ) c.add_port( name="right", center=(total_width / 2 + finger_width + feed_length, 0), width=feed_width, orientation=0, layer=layer_metal, port_type=port_type, ) # Add metadata c.info["coupler_type"] = "interdigital" c.info["fingers"] = fingers c.info["finger_length"] = finger_length c.info["finger_width"] = finger_width c.info["finger_gap_horizontal"] = finger_gap_horizontal c.info["finger_gap_vertical"] = finger_gap_vertical return c
[docs] @gf.cell_with_module_name def coupler_tunable( pad_width: float = 30.0, pad_height: float = 40.0, gap: float = 3.0, tuning_pad_width: float = 15.0, tuning_pad_height: float = 20.0, tuning_gap: float = 1.0, feed_width: float = 10.0, feed_length: float = 30.0, layer_metal: LayerSpec = LAYER.M1_DRAW, layer_tuning: LayerSpec = LAYER.M1_CUTOUT, port_type: str = "electrical", ) -> Component: """Creates a tunable capacitive coupler with voltage control. A tunable coupler includes additional electrodes that can be voltage-biased to change the coupling strength dynamically. Args: pad_width: Width of main coupling pads in μm. pad_height: Height of main coupling pads in μm. gap: Gap between main coupling pads in μm. tuning_pad_width: Width of tuning pads in μm. tuning_pad_height: Height of tuning pads in μm. tuning_gap: Gap to tuning pads in μm. feed_width: Width of feed lines in μm. feed_length: Length of feed lines in μm. layer_metal: Layer for main metal structures. layer_tuning: Layer for tuning electrodes. port_type: Type of port to add to the component. Returns: Component: A gdsfactory component with the tunable coupler geometry. .. code:: (connected to feed) _______ | | | tpad1 | | | |_______| tuning gap ______ ______ _______ | | | | _______ | | | | | || | | feed1 | | pad1 | gap | pad2 || feed2 | | | | | | || | |_______| | | | ||_______| |______| |______| tuning gap _______ | | | tpad2 | | | |_______| (connected to feed) """ c = Component() # Create main coupling pads left_pad = gf.components.rectangle( size=(pad_width, pad_height), layer=layer_metal, ) left_pad_ref = c.add_ref(left_pad) left_pad_ref.move((-pad_width - gap / 2, -pad_height / 2)) right_pad = gf.components.rectangle( size=(pad_width, pad_height), layer=layer_metal, ) right_pad_ref = c.add_ref(right_pad) right_pad_ref.move((gap / 2, -pad_height / 2)) # Create tuning pads above and below top_tuning_pad = gf.components.rectangle( size=(tuning_pad_width, tuning_pad_height), layer=layer_tuning, ) top_tuning_ref = c.add_ref(top_tuning_pad) top_tuning_ref.move((-tuning_pad_width / 2, pad_height / 2 + tuning_gap)) bottom_tuning_pad = gf.components.rectangle( size=(tuning_pad_width, tuning_pad_height), layer=layer_tuning, ) bottom_tuning_ref = c.add_ref(bottom_tuning_pad) bottom_tuning_ref.move( (-tuning_pad_width / 2, -pad_height / 2 - tuning_gap - tuning_pad_height) ) # Create feed lines for main pads left_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) left_feed_ref = c.add_ref(left_feed) left_feed_ref.move((-pad_width - gap / 2 - feed_length, -feed_width / 2)) right_feed = gf.components.rectangle( size=(feed_length, feed_width), layer=layer_metal, ) right_feed_ref = c.add_ref(right_feed) right_feed_ref.move((gap / 2 + pad_width, -feed_width / 2)) # Create tuning feed lines top_tuning_feed = gf.components.rectangle( size=(feed_width, feed_length), layer=layer_tuning, ) top_tuning_feed_ref = c.add_ref(top_tuning_feed) top_tuning_feed_ref.move( (-feed_width / 2, pad_height / 2 + tuning_gap + tuning_pad_height) ) bottom_tuning_feed = gf.components.rectangle( size=(feed_width, feed_length), layer=layer_tuning, ) bottom_tuning_feed_ref = c.add_ref(bottom_tuning_feed) bottom_tuning_feed_ref.move( ( -feed_width / 2, -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length, ) ) # Add ports c.add_port( name="left", center=(-pad_width - gap / 2 - feed_length, 0), width=feed_width, orientation=180, layer=layer_metal, port_type=port_type, ) c.add_port( name="right", center=(gap / 2 + pad_width + feed_length, 0), width=feed_width, orientation=0, layer=layer_metal, port_type=port_type, ) c.add_port( name="tuning_top", center=(0, pad_height / 2 + tuning_gap + tuning_pad_height + feed_length), width=feed_width, orientation=90, layer=layer_tuning, port_type=port_type, ) c.add_port( name="tuning_bottom", center=(0, -pad_height / 2 - tuning_gap - tuning_pad_height - feed_length), width=feed_width, orientation=270, layer=layer_tuning, port_type=port_type, ) # Add metadata c.info["coupler_type"] = "tunable" c.info["pad_width"] = pad_width c.info["pad_height"] = pad_height c.info["gap"] = gap c.info["tuning_pad_width"] = tuning_pad_width c.info["tuning_pad_height"] = tuning_pad_height c.info["tuning_gap"] = tuning_gap return c
if __name__ == "__main__": from qpdk import PDK PDK.activate() c = coupler_capacitive() # c = coupler_interdigital() c.pprint_ports() c.show()