Source code for qpdk.models.couplers

"""S-parameter models for couplers."""

import jax.numpy as jnp
import sax
from gdsfactory.typings import CrossSectionSpec
from jax.typing import ArrayLike
from skrf import Frequency

from qpdk.models.generic import capacitor, tee
from qpdk.models.media import cross_section_to_media
from qpdk.models.waveguides import straight


def cpw_cpw_coupling_capacitance(
    length: float,
    gap: float,
    cross_section: CrossSectionSpec,
    f: ArrayLike = jnp.array([5e9]),
) -> float:
    """Calculate the coupling capacitance between two parallel CPWs.

    TODO: this is a placeholder function and needs to be implemented properly.

    Args:
        length: The coupling length in µm.
        gap: The gap between the two CPWs in µm.
        cross_section: The cross-section of the CPW.
        f: Frequency array in Hz.

    Returns:
        The total coupling capacitance in Farads.
    """
    # Create a media instance to extract parameters. Frequency doesn't matter for geometry.
    media = cross_section_to_media(cross_section)
    media_instance = media(frequency=Frequency.from_f(f, unit="Hz"))
    ep_r = media_instance.ep_r  # noqa: F841
    w_m = getattr(media_instance, "w", 10e-6)  # noqa: F841
    s_m = getattr(media_instance, "s", 6e-6)  # noqa: F841
    length_m = length * 1e-6  # noqa: F841
    gap_m = gap * 1e-6  # noqa: F841

    # TODO: Find a paper with some values

    # TODO hardcoded placeholder value
    return 60e-15


[docs] def coupler_straight( f: ArrayLike = jnp.array([5e9]), length: int | float = 20.0, gap: int | float = 0.27, cross_section: CrossSectionSpec = "cpw", ) -> sax.SType: """S-parameter model for two coupled coplanar waveguides, :func:`~qpdk.cells.waveguides.coupler_straight`. Args: f: Array of frequency points in Hz length: Physical length of coupling section in µm gap: Gap between the coupled waveguides in µm cross_section: The cross-section of the CPW. Returns: sax.SType: S-parameters dictionary .. code:: o2──────▲───────o3 │gap o1──────▼───────o4 """ straight_settings = {"length": length / 2, "cross_section": cross_section} capacitor_settings = { "capacitance": cpw_cpw_coupling_capacitance( length, gap, cross_section, f ), # gap * 1e-18 * f, # TODO implement FEM simulation retrieval or use some paper "z0": cross_section_to_media(cross_section)( frequency=Frequency.from_f(f, unit="Hz") ).z0, } # Create straight instances with shared settings straight_instances = { f"straight_{i}_{j}": { "component": "straight", "settings": straight_settings, } for i in [1, 2] for j in [1, 2] } tee_instances = {f"tee_{i}": {"component": "tee"} for i in [1, 2]} circuit, _ = sax.circuit( netlist={ "instances": { **straight_instances, **tee_instances, "capacitor": { "component": "capacitor", "settings": capacitor_settings, }, }, "connections": { "straight_1_1,o1": "tee_1,o1", "straight_1_2,o1": "tee_1,o2", "straight_2_1,o1": "tee_2,o1", "straight_2_2,o1": "tee_2,o2", "tee_1,o3": "capacitor,o1", "tee_2,o3": "capacitor,o2", }, "ports": { "o2": "straight_1_1,o2", "o3": "straight_1_2,o2", "o1": "straight_2_1,o2", "o4": "straight_2_2,o2", }, }, models={ "straight": straight, "capacitor": capacitor, "tee": tee, }, ) return circuit(f=f)
if __name__ == "__main__": from matplotlib import pyplot as plt # Define frequency range from 1 GHz to 10 GHz with 201 points f = jnp.linspace(1e9, 10e9, 201) # Calculate coupler S-parameters for a 20 um straight coupler with 0.27 um gap coupler = coupler_straight(f=f, length=20, gap=0.27) # Create figure with single plot for comparison fig, ax = plt.subplots(1, 1, figsize=(10, 6)) # Define S-parameters to plot s_params = [ (("o1", "o1"), "$S_{11}$ Reflection"), (("o1", "o2"), "$S_{12}$ Coupled branch 1"), (("o1", "o3"), "$S_{13}$ Coupled branch 2"), (("o1", "o4"), "$S_{14}$ Insertion loss (direct through)"), ] # Plot each S-parameter for both coupler implementations default_color_cycler = plt.cm.tab10.colors for idx, (ports, label) in enumerate(s_params): color = default_color_cycler[idx % len(default_color_cycler)] # Plot both implementations with same color but different linestyles ax.plot( f / 1e9, 20 * jnp.log10(jnp.abs(coupler[ports])), linestyle="-", color=color, label=f"{label} coupler_straight", ) # Configure plot ax.set_xlabel("Frequency [GHz]") ax.set_ylabel("$S$-parameter [dB]") ax.set_title(r"$S$-parameters: $\mathtt{coupler\_straight}$") ax.grid(True, which="both") ax.legend() plt.tight_layout() plt.show() # Example calculation of coupling capacitance from qpdk.tech import coplanar_waveguide cs = coplanar_waveguide(width=10, gap=6) coupling_capacitance = cpw_cpw_coupling_capacitance( length=20.0, gap=0.27, cross_section=cs, f=f ) print( "Coupling capacitance for 20 um length and 0.27 um gap:", coupling_capacitance, "F", )