Source code for finesse.components.frequency_loss

"""A component that introduces some loss for particular frequencies or light, relative
to the carrier frequency.

This can be used to introduce sideband imbalance, for example.
"""

import logging

import numpy as np

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

LOGGER = logging.getLogger(__name__)


[docs]class FrequencyLossWorkspace(ConnectorWorkspace): def __init__(self, owner, sim): super().__init__(owner, sim, False, False)
[docs]@float_parameter("loss", "Loss") @float_parameter( "phase", "Phase", units="Degrees", ) @float_parameter( "f", "Frequency", units="Hz", ) # IMPORTANT: renaming this class impacts the katscript spec and should be avoided! class FrequencyLoss(Connector): """Represents an unphysical element which introduces a loss and/or phase for a particular frequency. Parameters ---------- name : str Name of newly created lens. f : float, optional Frequency to apply loss and phase to. loss : float, optional Fractional loss at the frequency phase : float, optional Phase change at the frequency """ def __init__(self, name, f, loss=0, phase=0): super().__init__(name) self.f = f self.loss = loss self.phase = phase self._add_port("p1", NodeType.OPTICAL) self.p1._add_node("i", NodeDirection.INPUT) self.p1._add_node("o", NodeDirection.OUTPUT) self._add_port("p2", NodeType.OPTICAL) self.p2._add_node("i", NodeDirection.INPUT) self.p2._add_node("o", NodeDirection.OUTPUT) # optic to optic couplings self._register_node_coupling( "P1i_P2o", self.p1.i, self.p2.o, interaction_type=InteractionType.TRANSMISSION, ) self._register_node_coupling( "P2i_P1o", self.p2.i, self.p1.o, interaction_type=InteractionType.TRANSMISSION, ) def _get_workspace(self, sim): ws = FrequencyLossWorkspace(self, sim) ws.I = np.eye(sim.model_settings.num_HOMs, dtype=np.complex128) ws.carrier.add_fill_function(self._fill_carrier, False) ws.signal.add_fill_function(self._fill_signal, False) return ws def _fill_optical_matrix(self, ws, matrix, connections, signal_fill): for freq in matrix.optical_frequencies.frequencies: M = ws.I # Suppress carrier and it's signal sidebands if freq.f == ws.values.f or ( signal_fill and freq.audio_carrier.f == ws.values.f ): M *= (1 - ws.values.loss) * np.exp(1j * np.radians(ws.values.phase)) with matrix.component_edge_fill3( ws.owner_id, connections.P1i_P2o_idx, freq.index, freq.index, ) as mat: mat[:] = M with matrix.component_edge_fill3( ws.owner_id, connections.P2i_P1o_idx, freq.index, freq.index, ) as mat: mat[:] = M def _fill_carrier(self, ws): self._fill_optical_matrix(ws, ws.sim.carrier, ws.carrier.connections, False) def _fill_signal(self, ws): self._fill_optical_matrix(ws, ws.sim.signal, ws.signal.connections, True)