Skip to content

Radio frequency (RF) Models

For more information on these RF models, see Ref. 1.

rf

Sax generic RF models.

Functions:

Name Description
admittance

Generalized two-port admittance element.

capacitor

Ideal two-port capacitor model.

electrical_open

Electrical open connection Sax model.

electrical_short

Electrical short connection Sax model.

gamma_0_load

Connection with given reflection coefficient.

impedance

Generalized two-port impedance element.

inductor

Ideal two-port inductor model.

lc_shunt_component

SAX component for a 1-port shunted LC resonator.

tee

Ideal three-port RF power divider/combiner (T-junction).

admittance

admittance(*, f: FloatArrayLike = DEFAULT_FREQUENCY, y: ComplexLike = 1 / 50) -> SDict

Generalized two-port admittance element.

Parameters:

Name Type Description Default
f FloatArrayLike

Frequency in Hz

DEFAULT_FREQUENCY
y ComplexLike

Admittance in siemens

1 / 50

Returns:

Type Description
SDict

S-dictionary representing the admittance element

References

[@pozar2012]

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
s = sax.models.rf.admittance(f=f, y=1 / 75)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Magnitude")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True)
def admittance(
    *, f: sax.FloatArrayLike = DEFAULT_FREQUENCY, y: sax.ComplexLike = 1 / 50
) -> sax.SDict:
    r"""Generalized two-port admittance element.

    Args:
        f: Frequency in Hz
        y: Admittance in siemens

    Returns:
        S-dictionary representing the admittance element

    References:
        [@pozar2012]

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        s = sax.models.rf.admittance(f=f, y=1 / 75)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Magnitude")
        plt.legend()
        ```
    """
    one = jnp.ones_like(jnp.asarray(f))
    sdict = {
        ("o1", "o1"): 1 / (1 + y) * one,
        ("o1", "o2"): y / (1 + y) * one,
        ("o2", "o2"): 1 / (1 + y) * one,
    }
    return sax.reciprocal(sdict)

capacitor

capacitor(
    *,
    f: FloatArrayLike = DEFAULT_FREQUENCY,
    capacitance: FloatLike = 1e-15,
    z0: ComplexLike = 50,
) -> SDict

Ideal two-port capacitor model.

Parameters:

Name Type Description Default
f FloatArrayLike

Frequency in Hz

DEFAULT_FREQUENCY
capacitance FloatLike

Capacitance in Farads

1e-15
z0 ComplexLike

Reference impedance in Ω.

50

Returns:

Type Description
SDict

S-dictionary representing the capacitor element

References

[@pozar2012]

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
s = sax.models.rf.capacitor(f=f, capacitance=1e-12, z0=50)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Magnitude")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True)
def capacitor(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    capacitance: sax.FloatLike = 1e-15,
    z0: sax.ComplexLike = 50,
) -> sax.SDict:
    r"""Ideal two-port capacitor model.

    Args:
        f: Frequency in Hz
        capacitance: Capacitance in Farads
        z0: Reference impedance in Ω.

    Returns:
        S-dictionary representing the capacitor element

    References:
        [@pozar2012]

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        s = sax.models.rf.capacitor(f=f, capacitance=1e-12, z0=50)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Magnitude")
        plt.legend()
        ```
    """
    angular_frequency = 2 * jnp.pi * jnp.asarray(f)
    capacitor_impedance = 1 / (1j * angular_frequency * capacitance)
    return impedance(f=f, z=capacitor_impedance, z0=z0)

electrical_open

electrical_open(*, f: FloatArrayLike = DEFAULT_FREQUENCY, n_ports: int = 1) -> SDict

Electrical open connection Sax model.

Useful for specifying some ports to remain open while not exposing them for connections in circuits.

Parameters:

Name Type Description Default
f FloatArrayLike

Array of frequency points in Hz

DEFAULT_FREQUENCY
n_ports int

Number of ports to set as opened

1

Returns:

Type Description
SDict

S-dictionary where :math:S = I_\text{n\_ports}

References

[@pozar2012]

Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True, static_argnames=("n_ports"))
def electrical_open(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    n_ports: int = 1,
) -> sax.SDict:
    r"""Electrical open connection Sax model.

    Useful for specifying some ports to remain open while not exposing
    them for connections in circuits.

    Args:
        f: Array of frequency points in Hz
        n_ports: Number of ports to set as opened

    Returns:
        S-dictionary where :math:`S = I_\text{n\_ports}`

    References:
        [@pozar2012]
    """
    return gamma_0_load(f=f, gamma_0=1, n_ports=n_ports)

electrical_short

electrical_short(*, f: FloatArrayLike = DEFAULT_FREQUENCY, n_ports: int = 1) -> SDict

Electrical short connection Sax model.

Parameters:

Name Type Description Default
f FloatArrayLike

Array of frequency points in Hz

DEFAULT_FREQUENCY
n_ports int

Number of ports to set as shorted

1

Returns:

Type Description
SDict

S-dictionary where :math:S = -I_\text{n\_ports}

References

[@pozar2012]

Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True, static_argnames=("n_ports"))
def electrical_short(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    n_ports: int = 1,
) -> sax.SDict:
    r"""Electrical short connection Sax model.

    Args:
        f: Array of frequency points in Hz
        n_ports: Number of ports to set as shorted

    Returns:
        S-dictionary where :math:`S = -I_\text{n\_ports}`

    References:
        [@pozar2012]
    """
    return gamma_0_load(f=f, gamma_0=-1, n_ports=n_ports)

gamma_0_load

gamma_0_load(
    *, f: FloatArrayLike = DEFAULT_FREQUENCY, gamma_0: Complex = 0, n_ports: int = 1
) -> SType

Connection with given reflection coefficient.

Parameters:

Name Type Description Default
f FloatArrayLike

Array of frequency points in Hz

DEFAULT_FREQUENCY
gamma_0 Complex

Reflection coefficient Γ₀ of connection

0
n_ports int

Number of ports in component. The diagonal ports of the matrix are set to Γ₀ and the off-diagonal ports to 0.

1

Returns:

Type Description
SType

sax.SType: S-parameters dictionary where :math:S = \Gamma_0I_\text{n\_ports}

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
gamma_0 = 0.5 * np.exp(1j * np.pi / 4)
s = sax.models.rf.gamma_0_load(f=f, gamma_0=gamma_0, n_ports=2)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
plt.plot(f / 1e9, np.abs(s[("o2", "o1")]), label="|S21|")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Magnitude")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True, static_argnames=("n_ports"))
def gamma_0_load(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    gamma_0: sax.Complex = 0,
    n_ports: int = 1,
) -> sax.SType:
    r"""Connection with given reflection coefficient.

    Args:
        f: Array of frequency points in Hz
        gamma_0: Reflection coefficient Γ₀ of connection
        n_ports: Number of ports in component. The diagonal ports of the matrix
            are set to Γ₀ and the off-diagonal ports to 0.

    Returns:
        sax.SType: S-parameters dictionary where :math:`S = \Gamma_0I_\text{n\_ports}`

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        gamma_0 = 0.5 * np.exp(1j * np.pi / 4)
        s = sax.models.rf.gamma_0_load(f=f, gamma_0=gamma_0, n_ports=2)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o1")]), label="|S21|")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Magnitude")
        plt.legend()
        ```
    """
    f = jnp.asarray(f)
    f_flat = f.ravel()
    sdict = {
        (f"o{i}", f"o{i}"): jnp.full(f_flat.shape[0], gamma_0)
        for i in range(1, n_ports + 1)
    }
    sdict |= {
        (f"o{i}", f"o{j}"): jnp.zeros(f_flat.shape[0], dtype=complex)
        for i in range(1, n_ports + 1)
        for j in range(i + 1, n_ports + 1)
    }
    return sax.reciprocal({k: v.reshape(*f.shape) for k, v in sdict.items()})

impedance

impedance(
    *, f: FloatArrayLike = DEFAULT_FREQUENCY, z: ComplexLike = 50, z0: ComplexLike = 50
) -> SDict

Generalized two-port impedance element.

Parameters:

Name Type Description Default
f FloatArrayLike

Frequency in Hz

DEFAULT_FREQUENCY
z ComplexLike

Impedance in Ω

50
z0 ComplexLike

Reference impedance in Ω.

50

Returns:

Type Description
SDict

S-dictionary representing the impedance element

References

[@pozar2012]

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
s = sax.models.rf.impedance(f=f, z=75, z0=50)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Magnitude")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True)
def impedance(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    z: sax.ComplexLike = 50,
    z0: sax.ComplexLike = 50,
) -> sax.SDict:
    r"""Generalized two-port impedance element.

    Args:
        f: Frequency in Hz
        z: Impedance in Ω
        z0: Reference impedance in Ω.

    Returns:
        S-dictionary representing the impedance element

    References:
        [@pozar2012]

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        s = sax.models.rf.impedance(f=f, z=75, z0=50)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Magnitude")
        plt.legend()
        ```
    """
    one = jnp.ones_like(jnp.asarray(f))
    sdict = {
        ("o1", "o1"): z / (z + 2 * z0) * one,
        ("o1", "o2"): 2 * z0 / (2 * z0 + z) * one,
        ("o2", "o2"): z / (z + 2 * z0) * one,
    }
    return sax.reciprocal(sdict)

inductor

inductor(
    *,
    f: FloatArrayLike = DEFAULT_FREQUENCY,
    inductance: FloatLike = 1e-12,
    z0: ComplexLike = 50,
) -> SDict

Ideal two-port inductor model.

Parameters:

Name Type Description Default
f FloatArrayLike

Frequency in Hz

DEFAULT_FREQUENCY
inductance FloatLike

Inductance in Henries

1e-12
z0 ComplexLike

Reference impedance in Ω.

50

Returns:

Type Description
SDict

S-dictionary representing the inductor element

References

[@pozar2012]

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
s = sax.models.rf.inductor(f=f, inductance=1e-9, z0=50)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Magnitude")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True)
def inductor(
    *,
    f: sax.FloatArrayLike = DEFAULT_FREQUENCY,
    inductance: sax.FloatLike = 1e-12,
    z0: sax.ComplexLike = 50,
) -> sax.SDict:
    r"""Ideal two-port inductor model.

    Args:
        f: Frequency in Hz
        inductance: Inductance in Henries
        z0: Reference impedance in Ω.

    Returns:
        S-dictionary representing the inductor element

    References:
        [@pozar2012]

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        s = sax.models.rf.inductor(f=f, inductance=1e-9, z0=50)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o1")]), label="|S11|")
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]), label="|S12|")
        plt.plot(f / 1e9, np.abs(s[("o2", "o2")]), label="|S22|")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Magnitude")
        plt.legend()
        ```
    """
    angular_frequency = 2 * jnp.pi * jnp.asarray(f)
    inductor_impedance = 1j * angular_frequency * inductance
    return impedance(f=f, z=inductor_impedance, z0=z0)

lc_shunt_component

lc_shunt_component(
    f: FloatArrayLike = 5000000000.0,
    inductance: FloatLike = 1e-09,
    capacitance: FloatLike = 1e-12,
    z0: FloatLike = 50,
) -> SDict

SAX component for a 1-port shunted LC resonator.

Source code in src/sax/models/rf.py
@jax.jit
def lc_shunt_component(
    f: sax.FloatArrayLike = 5e9,
    inductance: sax.FloatLike = 1e-9,
    capacitance: sax.FloatLike = 1e-12,
    z0: sax.FloatLike = 50,
) -> sax.SDict:
    """SAX component for a 1-port shunted LC resonator."""
    f = jnp.asarray(f)
    instances = {
        "L": inductor(f=f, inductance=inductance, z0=z0),
        "C": capacitor(f=f, capacitance=capacitance, z0=z0),
        "gnd": electrical_short(f=f, n_ports=1),
        "tee_1": tee(f=f),
        "tee_2": tee(f=f),
    }
    connections = {
        "L,o1": "tee_1,o1",
        "C,o1": "tee_1,o2",
        "L,o2": "tee_2,o1",
        "C,o2": "tee_2,o2",
        "gnd,o1": "tee_2,o3",
    }
    ports = {
        "o1": "tee_1,o3",
    }

    return sax.backends.evaluate_circuit_fg((connections, ports), instances)

tee

tee(*, f: FloatArrayLike = DEFAULT_FREQUENCY) -> SDict

Ideal three-port RF power divider/combiner (T-junction).

Parameters:

Name Type Description Default
f FloatArrayLike

Array of frequency points in Hz

DEFAULT_FREQUENCY

Returns:

Type Description
SDict

S-dictionary representing ideal RF T-junction behavior

Examples:

# mkdocs: render
import matplotlib.pyplot as plt
import numpy as np
import sax

sax.set_port_naming_strategy("optical")

f = np.linspace(1e9, 10e9, 500)
s = sax.models.rf.tee(f=f)
plt.figure()
plt.plot(f / 1e9, np.abs(s[("o1", "o2")]) ** 2, label="|S12|^2")
plt.plot(f / 1e9, np.abs(s[("o1", "o3")]) ** 2, label="|S13|^2")
plt.plot(f / 1e9, np.abs(s[("o2", "o3")]) ** 2, label="|S23|^2")
plt.xlabel("Frequency [GHz]")
plt.ylabel("Power")
plt.legend()
Source code in src/sax/models/rf.py
@partial(jax.jit, inline=True)
def tee(*, f: sax.FloatArrayLike = DEFAULT_FREQUENCY) -> sax.SDict:
    """Ideal three-port RF power divider/combiner (T-junction).

    Args:
        f: Array of frequency points in Hz

    Returns:
        S-dictionary representing ideal RF T-junction behavior

    Examples:
        ```python
        # mkdocs: render
        import matplotlib.pyplot as plt
        import numpy as np
        import sax

        sax.set_port_naming_strategy("optical")

        f = np.linspace(1e9, 10e9, 500)
        s = sax.models.rf.tee(f=f)
        plt.figure()
        plt.plot(f / 1e9, np.abs(s[("o1", "o2")]) ** 2, label="|S12|^2")
        plt.plot(f / 1e9, np.abs(s[("o1", "o3")]) ** 2, label="|S13|^2")
        plt.plot(f / 1e9, np.abs(s[("o2", "o3")]) ** 2, label="|S23|^2")
        plt.xlabel("Frequency [GHz]")
        plt.ylabel("Power")
        plt.legend()
        ```
    """
    f = jnp.asarray(f)
    f_flat = f.ravel()
    sdict = {(f"o{i}", f"o{i}"): jnp.full(f_flat.shape[0], -1 / 3) for i in range(1, 4)}
    sdict |= {
        (f"o{i}", f"o{j}"): jnp.full(f_flat.shape[0], 2 / 3)
        for i in range(1, 4)
        for j in range(i + 1, 4)
    }
    return sax.reciprocal({k: v.reshape(*f.shape) for k, v in sdict.items()})

  1. David M. Pozar. Microwave Engineering. John Wiley & Sons, Inc., 4 edition, 2012. ISBN 978-0-470-63155-3.