Source code for pmrf.models.composite.transformed

"""
Models that transform the ports or layout of another model.
"""
import jax.numpy as jnp
from parax import field

from pmrf.core import Model, Frequency
        
[docs] class Renumbered(Model, transparent=True): """ A container that re-numbers the ports of a given `Model`. This is useful for creating complex network topologies by explicitly re-mapping the port indices of a sub-network. Attributes ---------- model : Model The underlying model to renumber. from_ports : tuple[int] The original port indices that map to `to_ports`. to_ports : tuple[int] The new port indices. Can be `None`, in which case `from_ports` must contain exactly two ports to be swapped. """ model: Model from_ports: tuple[int] to_ports: tuple[int] = None def __post_init__(self): model = self.model if self.to_ports is None: if len(self.from_ports) != 2: raise Exception("from_ports must have length==2 if to_ports is None") self.to_ports = (self.from_ports[1], self.from_ports[0]) if model.primary_property == 'a' and len(self.from_ports) != 2 and len(self.to_ports) != 2: raise ValueError("(from_ports, to_ports) must be either (0, 1) or (1, 0) for 'a' primary networks") if len(self.from_ports) != len(self.to_ports): raise ValueError("from_ports and to_ports must have the same length for Renumbered")
[docs] def renumber(self, p: jnp.ndarray) -> jnp.ndarray: """ Applies the port renumbering to a parameter matrix. Parameters ---------- p : jnp.ndarray The parameter matrix to renumber (e.g., S-parameters). Returns ------- jnp.ndarray The renumbered parameter matrix. """ p_new = p.copy() p_new = p_new.at[:, self.to_ports, :].set(p[:, self.from_ports, :]) p_new = p_new.at[:, :, self.to_ports].set(p_new[:, :, self.from_ports]) return p_new
[docs] def a(self, freq: Frequency) -> jnp.ndarray: return self.renumber(self.model.a(freq))
[docs] def s(self, freq: Frequency) -> jnp.ndarray: return self.renumber(self.model.s(freq))
[docs] def y(self, freq: Frequency) -> jnp.ndarray: return self.renumber(self.model.y(freq))
[docs] def z(self, freq: Frequency) -> jnp.ndarray: return self.renumber(self.model.z(freq))
[docs] class Flipped(Renumbered): """ A model container that flips the ports of a multi-port network. For a 2-port network, this is equivalent to swapping port 1 and port 2. For a 4-port network, ports (1,2) are swapped with (3,4), and so on. This is a convenient specialization of the `Renumbered` model. """ to_ports: tuple[int] = field(init=False) from_ports: tuple[int] = field(init=False) def __post_init__(self): if self.model.nports % 2 != 0: raise ValueError("You can only flip multiple-of-two-port Networks") n = int(self.model.nports / 2) self.to_ports = tuple(range(0, 2 * n)) self.from_ports = tuple(range(n, 2 * n)) + tuple(range(0, n)) super().__post_init__() self.name = 'flipped'
[docs] class Stacked(Model, transparent=True): """ A container that stacks multiple models in a block-diagonal fashion. This combines several `Model` objects into a single, larger model where the individual S-parameter matrices are placed along the diagonal of the combined S-parameter matrix. This represents a set of unconnected networks treated as a single component. Attributes ---------- models : tuple[Model, ...] The models to stack. """ models: tuple[Model, ...]
[docs] def s(self, freq: Frequency) -> jnp.ndarray: num_ports = sum(model.nports for model in self.models) s = jnp.zeros((freq.npoints, num_ports, num_ports), dtype=jnp.complex128) i = 0 for submodel in self.models: s_sub = submodel.s(freq) n_sub = submodel.nports s = s.at[:,i:i+n_sub,i:i+n_sub].set(s_sub) i += n_sub return s