Source code for qpdk.cells.resonator

"""Resonator components."""

from __future__ import annotations

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

from qpdk.tech import LAYER


[docs] @gf.cell_with_module_name def resonator_cpw( length: float = 1000.0, width: float = 10.0, gap: float = 6.0, meander_pitch: float = 50.0, meander_width: float = 200.0, coupling_gap: float = 5.0, coupling_length: float = 100.0, layer_metal: LayerSpec = LAYER.M1_DRAW, layer_gap: LayerSpec = LAYER.M1_ETCH, port_type: str = "electrical", ) -> Component: """Creates a coplanar waveguide (CPW) resonator. A CPW resonator consists of a meandered coplanar waveguide with coupling gaps for capacitive coupling to feedlines or qubits. Args: length: Total length of the resonator in μm. width: Width of the center conductor in μm. gap: Gap width on each side of the center conductor in μm. meander_pitch: Pitch between meander segments in μm. meander_width: Width of each meander section in μm. coupling_gap: Gap for capacitive coupling in μm. coupling_length: Length of the coupling region in μm. layer_metal: Layer for the metal conductor. layer_gap: Layer for the gaps (ground plane). port_type: Type of port to add to the component. Returns: Component: A gdsfactory component with the CPW resonator geometry. """ c = Component() # Calculate number of meander sections needed num_meanders = int(length / meander_width) if meander_width > 0 else 1 actual_length = num_meanders * meander_width # Create meander path path_points = [] x, y = 0.0, 0.0 for i in range(num_meanders): if i == 0: # First segment path_points.extend([(x, y), (x + meander_width, y)]) x += meander_width else: # Alternate up and down if i % 2 == 1: y += meander_pitch path_points.extend([(x, y), (x - meander_width, y)]) x -= meander_width else: y += meander_pitch path_points.extend([(x, y), (x + meander_width, y)]) x += meander_width # Create the CPW path path = gf.Path(np.array(path_points)) # Create cross section for CPW cpw_xs = gf.cross_section.strip(width=width, layer=layer_metal) # Create the resonator structure resonator_path = gf.path.extrude(path, cpw_xs) c.add_ref(resonator_path) # Create ground plane with gaps total_width = meander_width + 2 * meander_pitch total_height = (num_meanders - 1) * meander_pitch + width + 2 * gap # Ground plane ground_plane = gf.components.rectangle( size=(total_width + 2 * gap, total_height + 2 * gap), layer=layer_metal, ) ground_ref = c.add_ref(ground_plane) ground_ref.move((-gap, -gap)) # Create gaps by boolean subtraction gap_width = width + 2 * gap # Create gap path along the resonator gap_path = gf.Path(np.array(path_points)) gap_xs = gf.cross_section.strip(width=gap_width, layer=layer_gap) gap_structure = gf.path.extrude(gap_path, gap_xs) # Subtract gaps from ground plane gf.boolean( ground_ref, gap_structure, operation="not", layer=layer_metal, ) # Add coupling regions # Input coupling coupling_in = gf.components.rectangle( size=(coupling_length, coupling_gap), layer=layer_gap, ) coupling_in_ref = c.add_ref(coupling_in) coupling_in_ref.move((-coupling_length / 2, -width / 2 - gap - coupling_gap / 2)) # Output coupling coupling_out = gf.components.rectangle( size=(coupling_length, coupling_gap), layer=layer_gap, ) coupling_out_ref = c.add_ref(coupling_out) coupling_out_ref.move( (x - coupling_length / 2, y + width / 2 + gap + coupling_gap / 2) ) # Add ports for coupling c.add_port( name="input", center=(0, -width / 2 - gap - coupling_gap), width=coupling_length, orientation=270, layer=layer_metal, port_type=port_type, ) c.add_port( name="output", center=(x, y + width / 2 + gap + coupling_gap), width=coupling_length, orientation=90, layer=layer_metal, port_type=port_type, ) # Add metadata c.info["resonator_type"] = "cpw" c.info["length"] = actual_length c.info["width"] = width c.info["gap"] = gap c.info["frequency_estimate"] = ( 3e8 / (2 * actual_length * 1e-6) / 1e9 ) # GHz, rough estimate c.flatten() return c
[docs] @gf.cell_with_module_name def resonator_lumped( capacitor_fingers: int = 4, capacitor_finger_length: float = 20.0, capacitor_finger_gap: float = 2.0, capacitor_thickness: float = 5.0, inductor_width: float = 2.0, inductor_turns: int = 3, inductor_radius: float = 20.0, coupling_gap: float = 5.0, layer_metal: LayerSpec = LAYER.M1_DRAW, port_type: str = "electrical", ) -> Component: """Creates a lumped element resonator with interdigital capacitor and spiral inductor. A lumped resonator consists of a capacitive element (interdigital capacitor) and an inductive element (spiral inductor) forming an LC circuit. Args: capacitor_fingers: Number of fingers in the interdigital capacitor. capacitor_finger_length: Length of each capacitor finger in μm. capacitor_finger_gap: Gap between capacitor fingers in μm. capacitor_thickness: Thickness of capacitor fingers in μm. inductor_width: Width of the inductor wire in μm. inductor_turns: Number of turns in the spiral inductor. inductor_radius: Radius of the spiral inductor in μm. coupling_gap: Gap for capacitive coupling 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 lumped resonator geometry. """ c = Component() # Create interdigital capacitor capacitor = gf.get_component( "interdigital_capacitor", fingers=capacitor_fingers, finger_length=capacitor_finger_length, finger_gap=capacitor_finger_gap, thickness=capacitor_thickness, layer=layer_metal, ) c.add_ref(capacitor) # Create spiral inductor # Create a cross section with the specified width inductor_cross_section = gf.cross_section.strip( width=inductor_width, layer=layer_metal, ) inductor = gf.components.spiral( n_loops=inductor_turns, cross_section=inductor_cross_section, ) ind_ref = c.add_ref(inductor) # Position inductor next to capacitor cap_width = 2 * capacitor_thickness + capacitor_finger_length + capacitor_finger_gap ind_ref.move((cap_width + 20.0, 0)) # Connect capacitor and inductor connection = gf.components.rectangle( size=(20.0, inductor_width), layer=layer_metal, ) conn_ref = c.add_ref(connection) conn_ref.move((cap_width, -inductor_width / 2)) # Connect to inductor input connection2 = gf.components.rectangle( size=(inductor_width, 20.0), layer=layer_metal, ) conn2_ref = c.add_ref(connection2) conn2_ref.move((cap_width + 20.0 - inductor_width / 2, -20.0)) # Add coupling elements for input/output coupling_cap_in = gf.components.rectangle( size=(capacitor_thickness, coupling_gap), layer=layer_metal, ) coupling_in_ref = c.add_ref(coupling_cap_in) coupling_in_ref.move( ( -coupling_gap - capacitor_thickness, capacitor_fingers * capacitor_thickness / 2, ) ) coupling_cap_out = gf.components.rectangle( size=(capacitor_thickness, coupling_gap), layer=layer_metal, ) coupling_out_ref = c.add_ref(coupling_cap_out) coupling_out_ref.move( (cap_width + coupling_gap, capacitor_fingers * capacitor_thickness / 2) ) # Add ports c.add_port( name="input", center=( -coupling_gap - capacitor_thickness / 2, capacitor_fingers * capacitor_thickness / 2, ), width=coupling_gap, orientation=180, layer=layer_metal, port_type=port_type, ) c.add_port( name="output", center=( cap_width + coupling_gap + capacitor_thickness / 2, capacitor_fingers * capacitor_thickness / 2, ), width=coupling_gap, orientation=0, layer=layer_metal, port_type=port_type, ) # Add metadata c.info["resonator_type"] = "lumped" c.info["capacitor_fingers"] = capacitor_fingers c.info["inductor_turns"] = inductor_turns c.info["inductor_radius"] = inductor_radius return c
[docs] @gf.cell_with_module_name def resonator_quarter_wave( length: float = 2500.0, width: float = 10.0, gap: float = 6.0, short_stub_length: float = 50.0, coupling_gap: float = 5.0, coupling_length: float = 100.0, layer_metal: LayerSpec = LAYER.M1_DRAW, layer_gap: LayerSpec = LAYER.M1_ETCH, port_type: str = "electrical", ) -> Component: """Creates a quarter-wave coplanar waveguide resonator. A quarter-wave resonator is shorted at one end and has maximum electric field at the open end, making it suitable for capacitive coupling. Args: length: Length of the quarter-wave resonator in μm. width: Width of the center conductor in μm. gap: Gap width on each side of the center conductor in μm. short_stub_length: Length of the shorting stub in μm. coupling_gap: Gap for capacitive coupling in μm. coupling_length: Length of the coupling region in μm. layer_metal: Layer for the metal conductor. layer_gap: Layer for the gaps. port_type: Type of port to add to the component. Returns: Component: A gdsfactory component with the quarter-wave resonator geometry. """ c = Component() # Create main resonator line main_line = gf.components.rectangle( size=(length, width), layer=layer_metal, ) c.add_ref(main_line) # Create shorting stub at one end short_stub = gf.components.rectangle( size=(short_stub_length, width + 2 * gap), layer=layer_metal, ) short_ref = c.add_ref(short_stub) short_ref.move((length, -gap)) # Create ground planes ground_top = gf.components.rectangle( size=(length + short_stub_length + 2 * gap, gap), layer=layer_metal, ) ground_top_ref = c.add_ref(ground_top) ground_top_ref.move((-gap, width)) ground_bottom = gf.components.rectangle( size=(length + short_stub_length + 2 * gap, gap), layer=layer_metal, ) ground_bottom_ref = c.add_ref(ground_bottom) ground_bottom_ref.move((-gap, -gap)) # Create coupling region at open end coupling_region = gf.components.rectangle( size=(coupling_length, coupling_gap), layer=layer_gap, ) coupling_ref = c.add_ref(coupling_region) coupling_ref.move((-coupling_length, width / 2 - coupling_gap / 2)) # Add port for coupling c.add_port( name="coupling", center=(-coupling_length / 2, width / 2), width=coupling_gap, orientation=180, layer=layer_metal, port_type=port_type, ) # Add metadata c.info["resonator_type"] = "quarter_wave" c.info["length"] = length c.info["width"] = width c.info["gap"] = gap c.info["frequency_estimate"] = ( 3e8 / (4 * length * 1e-6) / 1e9 ) # GHz, rough estimate return c
if __name__ == "__main__": from qpdk import PDK PDK.activate() # c = resonator_cpw() c = resonator_lumped() # c = resonator_quarter_wave() c.show()