Source code for touchstone.parser.models.frequency_point
"""
FrequencyPoint class representing a single frequency point and its S-parameters.
A :class:`FrequencyPoint` encapsulates one row of a Touchstone dataset —
a single frequency value together with its N × N matrix of complex
network parameters.
"""
from dataclasses import dataclass
from typing import Any
import numpy as np
from .network_parameter import NetworkParameter
[docs]
@dataclass(frozen=True)
class FrequencyPoint:
"""Encapsulates a single frequency point and its N × N matrix of network parameters.
Attributes:
frequency_hz: The frequency value in Hz (must be non-negative).
_s_parameters: The N × N complex NumPy array of S-parameters
at this frequency.
Raises:
ValueError: If the frequency is negative or the S-parameter array
is not a square matrix.
Example:
>>> point = data[0] # first frequency point
>>> point.frequency_hz
1000000000.0
>>> point.number_of_ports
2
>>> s11 = point[0, 0]
"""
frequency_hz: float
_s_parameters: np.ndarray
[docs]
def __post_init__(self):
"""Validate inputs after initialization.
Raises:
ValueError: If frequency is negative or S-parameters are not
a square matrix.
"""
if self.frequency_hz < 0:
raise ValueError(f"Frequency cannot be negative. Got {self.frequency_hz}")
if (
self._s_parameters.ndim != 2
or self._s_parameters.shape[0] != self._s_parameters.shape[1]
):
raise ValueError(
f"S-parameters must be a square matrix. Got shape {self._s_parameters.shape}"
)
@property
def number_of_ports(self) -> int:
"""The number of ports (N for an N × N matrix).
Returns:
int: The dimension of the S-parameter matrix.
"""
return self._s_parameters.shape[0]
[docs]
def get_parameter_matrix(self) -> np.ndarray:
"""Get a defensive copy of the complex S-parameter matrix.
Returns a copy so that external code cannot mutate the
internal state of this frozen dataclass.
Returns:
numpy.ndarray: An N × N complex array.
"""
return self._s_parameters.copy()
[docs]
def __getitem__(self, indices: tuple[int, int]) -> NetworkParameter:
"""Access a specific network parameter by ``[row, col]`` index.
Args:
indices: A tuple of ``(row, col)`` indices, **0-indexed**.
Returns:
NetworkParameter: The network parameter at the specified index.
Raises:
IndexError: If indices are not a 2-tuple or are out of range.
"""
if not isinstance(indices, tuple) or len(indices) != 2:
raise IndexError("Indices must be a tuple of (row, col).")
row, col = indices
if not (0 <= row < self.number_of_ports and 0 <= col < self.number_of_ports):
raise IndexError(
f"Index [{row}, {col}] is out of range for {self.number_of_ports}-port data."
)
return NetworkParameter(self._s_parameters[row, col])
[docs]
def __str__(self) -> str:
"""Return a human-readable string representation.
Returns:
str: A string showing the frequency and port count.
"""
return f"FrequencyPoint(freq={self.frequency_hz:.2e} Hz, ports={self.number_of_ports})"
[docs]
def __repr__(self) -> str:
"""Return a developer-friendly string representation.
Returns:
str: Same as :meth:`__str__`.
"""
return self.__str__()