Source code for reasitic.network.threeport
"""3-port to 2-port network reduction.
Mirrors the binary's ``reduce_3port_z_to_2port_y``
(``asitic_kernel.c:8652``, address ``0x080881a8``):
The standard reduction is to invert the full 3×3 Z, then take the
2×2 sub-block of Y between the two retained ports. That is
mathematically equivalent to grounding the third port (V₃ = 0) and
solving for I₁, I₂ in terms of V₁, V₂.
We also provide :func:`z_to_s_3port` corresponding to
``z_to_s_3port_50ohm`` (``0x080884b8``).
"""
from __future__ import annotations
import numpy as np
[docs]
def reduce_3port_z_to_2port_y(Z3: np.ndarray, ground_port: int = 2) -> np.ndarray:
"""Reduce a 3×3 Z matrix to a 2×2 Y by grounding ``ground_port``.
Returns the 2×2 Y matrix between the two non-grounded ports
(with the relative ordering preserved). The default
``ground_port=2`` matches the binary's behaviour of grounding
port 3 (zero-indexed: port 2).
"""
if Z3.shape != (3, 3):
raise ValueError(f"expected 3x3 matrix, got {Z3.shape}")
if ground_port not in (0, 1, 2):
raise ValueError("ground_port must be 0, 1 or 2")
Y3 = np.linalg.inv(Z3)
keep = [i for i in range(3) if i != ground_port]
return np.asarray(Y3[np.ix_(keep, keep)], dtype=complex)
[docs]
def z_to_s_3port(Z3: np.ndarray, z0_ohm: float = 50.0) -> np.ndarray:
"""Convert a 3×3 Z matrix to a scattering matrix S₃.
.. math::
S = (Z - Z_0 I)(Z + Z_0 I)^{-1}
Mirrors ``z_to_s_3port_50ohm`` (``0x080884b8``).
"""
if Z3.shape != (3, 3):
raise ValueError(f"expected 3x3 matrix, got {Z3.shape}")
eye = np.eye(3, dtype=complex)
return np.asarray(np.linalg.solve((Z3 + z0_ohm * eye).T, (Z3 - z0_ohm * eye).T).T)