Source code for finesse.components.wire

"""
Wire-type objects representing electrical connections between components.
"""

import logging
import numpy as np

from finesse.components.general import Connector, borrows_nodes

LOGGER = logging.getLogger(__name__)

from finesse.components.workspace import ConnectorWorkspace
from finesse.components.node import NodeType, NodeDirection
from finesse.parameter import float_parameter


[docs]class WireWorkspace(ConnectorWorkspace): pass
[docs]@float_parameter("delay", "Delay", validate="_check_delay", units="s") @borrows_nodes() class Wire(Connector): """Represents a wire between two electrical components in the interferometer configuration. Parameters ---------- name : str, optional Name of newly created wire. portA, portB : :class:`.Port`, optional Ports to connect. """
[docs] def __init__(self, name=None, portA=None, portB=None, delay=0): if (portA is None) != (portB is None): LOGGER.warn( "Can't construct a wire with only one port " "connected, ignoring ports." ) portA = None portB = None if portA is not None and portA.type != NodeType.ELECTRICAL: raise Exception("PortA argument is not an electrical port") if portB is not None and portB.type != NodeType.ELECTRICAL: raise Exception("PortB argument is not an electrical port") if name is None: if portA is not None and portB is not None: compA = portA.component.name compB = portB.component.name name = f"{compA}_{portA.name}__{compB}_{portB.name}" else: raise ValueError( "Cannot create an unconnected wire without " "providing a name" ) super().__init__(name) self.delay = delay self._add_to_model_namespace = False self.__portA = portA self.__portB = portB self._add_port("p1", NodeType.ELECTRICAL) self._add_port("p2", NodeType.ELECTRICAL) if portA is not None and portB is not None: self.connect(portA, portB)
@property def portA(self): return self.__portA @property def portB(self): return self.__portB def _check_delay(self, value): if value < 0: raise ValueError("Delay of a wire must not be negative.") return value
[docs] def connect(self, portA, portB): """ Sets the ports of this `Wire`. Parameters ---------- portA : :class:`.Port`, optional Port to connect portB : :class:`.Port`, optional Port to connect """ if portA.nodes[0].direction == portB.nodes[0].direction: raise Exception( f"Can not connect two {portA.nodes[0].direction} nodes, {portA.nodes[0]} to {portB.nodes[0]}." ) # From the Wire's perspective the input and output # nodes are swapped around for its ports if portA.nodes[0].direction == NodeDirection.INPUT: self.p1._add_node("i", None, portB.o) self.p2._add_node("o", None, portA.i) else: self.p1._add_node("i", None, portA.o) self.p2._add_node("o", None, portB.i) self._register_node_coupling("P1i_P2o", self.p1.i, self.p2.o)
def _get_workspace(self, sim): if sim.signal: _, is_changing = self._eval_parameters() # Most wires are zero delay, so don't bother refilling them all the time refill = ( (sim.model.fsig.f.is_changing or sim.signal.any_frequencies_changing) and self.delay.is_changing and self.delay != 0 ) ws = WireWorkspace(self, sim, refill, refill) ws.frequencies = sim.signal.electrical_frequencies[self.p1.i].frequencies # Set the fill function for this simulation ws.signal.add_fill_function(self.fill, refill) return ws else: return None
[docs] def fill(self, ws): for _ in ws.frequencies: if ws.signal.connections.P1i_P2o_idx > -1: with ws.sim.signal.component_edge_fill3( ws.owner_id, ws.signal.connections.P1i_P2o_idx, _.index, _.index, ) as mat: mat[:] = np.exp(1j * ws.values.delay * _.f)