dof.py 3.33 KB
Newer Older
1
from finesse.components.node import NodeType, NodeDirection, Node
2
from finesse.components.general import Connector, ModelElement
3
from finesse.components.workspace import ConnectorWorkspace, Connections
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
import numpy as np


class DOFWorkspace(ConnectorWorkspace):
    def __init__(self, owner, sim, refill):
        super().__init__(owner, sim, refill, Connections(sim))


class DegreeOfFreedom(Connector):
    def __init__(self, name, node, amplitude=1, *node_amp_pairs):
        Connector.__init__(self, name)
        self.__nodes = tuple((node, *node_amp_pairs[::2]))
        self.__amplitudes = tuple((amplitude, *node_amp_pairs[1::2]))

        if len(self.drives) != len(self.amplitudes):
            raise Exception(
                f"Nodes and amplitudes were not the same length, {len(self.drives)} vs {len(self.amplitudes)}"
            )

        for node in self.drives:
            if not isinstance(node, Node):
                raise Exception(
                    f"Signal generator ({name}) input `{node}` should be a Node"
                )
            if node.direction != NodeDirection.INPUT:
                raise Exception(
                    f"Signal generator ({name}) input `{node}` is an output node"
                )
            if not (
                node.type == NodeType.ELECTRICAL or node.type == NodeType.MECHANICAL
            ):
                raise Exception(
                    f"Signal generator ({name}) input `{node}` should be an electrical or mechanical node"
                )

        for amp in self.amplitudes:
            if not (np.isscalar(amp) and np.real(amp)):
                raise Exception(
                    f"Signal generator ({name}) amplitude `{amp}` is not a real number"
                )

        self._add_to_model_namespace = True
        self._namespace = ".dofs"

        self._add_port("p1", NodeType.ELECTRICAL)
        self.p1._add_node("i", NodeDirection.INPUT)

        self._add_port("p2", node.type)
        for i, node in enumerate(self.drives):
            self.p2._add_node(f"o{i}", None, self.drives[i])
            self._register_node_coupling(f"P1_P2{i}", self.p1.i, self.p2.nodes[-1])

    @property
    def node(self):
        ":getter: Returns the node to be used for driving."
        return self.p1.i

    def eval(self, omega):
        return 1

    @property
    def drives(self):
        ":getter: Returns The nodes this degree of freedom drives."
        return self.__nodes

    @property
    def amplitudes(self):
        ":getter: Returns the node amplitude which a node is driven."
        return self.__amplitudes

    def _get_workspace(self, sim):
75
        if sim.signal:
76
            ws = DOFWorkspace(self, sim, False)
Daniel Brown's avatar
Daniel Brown committed
77
            ws.set_fill_function(self.__fill)
78 79 80 81 82 83 84 85 86
            ws.drives = self.drives
            ws.amplitudes = self.amplitudes
            return ws
        else:
            return None

    def __fill(self, ws):
        # All connections are just the amplitude
        for idx in range(len(ws.drives)):
Daniel Brown's avatar
Daniel Brown committed
87
            with ws.sim.component_edge_fill3(ws.owner_id, idx, 0, 0,) as mat:
88
                mat[:] = ws.amplitudes[idx]
89 90 91 92 93 94 95 96 97 98 99 100


class Readout(ModelElement):
    def __init__(self, name, node):
        super().__init__(name)
        self.__node = node
        self._add_to_model_namespace = True
        self._namespace = ".readouts"

    @property
    def node(self):
        return self.__node