from finesse.components.general import Connector, InteractionType
from finesse.components.node import NodeDirection, NodeType
from finesse.exceptions import FinesseException
from finesse.parameter import float_parameter
from finesse.utilities import refractive_index
[docs]
@float_parameter("fc", "Central frequency", units="Hz")
@float_parameter("bandwidth", "Bandpass bandwidth", units="Hz")
# IMPORTANT: renaming this class impacts the katscript spec and should be avoided!
class OpticalBandpassFilter(Connector):
    """An idealised optical bandpass filter that will transmit an optical frequency
    around some central frequency with a 3dB bandwidth.
    Notes
    -----
    Currently this is a classical component. No quantum noise losses are applied to
    any filtered fields or modes, which means no additional vaccum noise is injected
    back into the system for this lost information.
    Parameters
    ----------
    name : str
        Name of element
    fc : float, symbol
        Central frequency
    bandwidth : float, symbol
        Filter 3dB bandwidth
    filter_hom : [tuple | None], optional
        Individual higher order modes can be filtered and transmitted by setting this
        to a tuple of (n, m). If ``None`` then no mode filtering is done. This cannot
        be changed during a simulation.
    """
    def __init__(self, name, fc=0, bandwidth=1000, filter_hom=None):
        super().__init__(name)
        self.fc = fc
        self.bandwidth = bandwidth
        self.filter_hom = filter_hom
        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):
        from finesse.simulations.sparse.simulation import SparseMatrixSimulation
        if isinstance(sim, SparseMatrixSimulation):
            _, is_changing = self._eval_parameters()
            refill = (
                sim.is_component_in_mismatch_couplings(self)
                or len(is_changing)
                or sim.carrier.any_frequencies_changing
                or (sim.signal.any_frequencies_changing if sim.signal else False)
            )
            from .modal.optical_bandpass import (
                optical_bandpass_carrier_fill,
                optical_bandpass_signal_fill,
                OpticalBandpassWorkspace,
            )
            hom_filter_index = None
            if self.filter_hom is not None:
                if sim.is_modal:
                    try:
                        hom_filter_index = sim.model.mode_index_map[
                            tuple(self.filter_hom)
                        ]
                    except KeyError:
                        raise FinesseException(
                            f"No HG mode {self.filter_hom} was found in this model."
                        )
                else:
                    hom_filter_index = 0  # planewave
            ws = OpticalBandpassWorkspace(self, sim, hom_filter_index)
            # This assumes that nr1/nr2 cannot change during a simulation
            ws.nr1 = refractive_index(self.p1)
            ws.nr2 = refractive_index(self.p2)
            # TODO ddb refractive index should be equal on
            # both sides of the lens as we are using the thin
            # lens approximation
            assert ws.nr1 == ws.nr2
            ws.carrier.add_fill_function(optical_bandpass_carrier_fill, refill)
            ws.signal.add_fill_function(optical_bandpass_signal_fill, refill)
            if sim.is_modal:
                # Set the coupling matrix information
                # ABCDs are same in each direction
                ws.set_knm_info(
                    "P1i_P2o",
                    nr_from=ws.nr1,
                    nr_to=ws.nr2,
                    is_transmission=True,
                )
                # TODO nr reversed here for now until it's forced to be same on both sides
                ws.set_knm_info(
                    "P2i_P1o",
                    nr_from=ws.nr2,
                    nr_to=ws.nr1,
                    is_transmission=True,
                )
            return ws
        else:
            raise Exception(
                f"Optical bandpass filter does not handle a simulation of type {sim}"
            )