Basic usage of ports and nodes

All components in Finesse 3 have Port instances associated with them, which in turn contain Node objects representing the coupling type and direction at this connection point.

Accessing ports

Ports of a component can be accessed via the component through the names that they were assigned during construction. For example, a Mirror has two ports (p1 and p2) which can be grabbed directly:

from finesse.components import Mirror

M1 = Mirror("M1")
print(M1.p1)
print(M1.p2)
<Port M1.p1 @ 0x7f8bdc199730>
<Port M1.p2 @ 0x7f8bdc0bd700>

One can also get a read-only tuple of all the ports of a component:

print(M1.ports)
(<Port M1.p1 @ 0x7f8bdc199730>, <Port M1.p2 @ 0x7f8bdc0bd700>, <Port M1.mech @ 0x7f8bdbff0f70>, <Port M1.phase_sig @ 0x7f8bfc6e7ee0>)

What does a port contain?

In the example code below we show the main properties of a Port - namely its type, the nodes that it holds and the component that it is attached to (which is always a Space instance for optical connections):

from finesse import Model
from finesse.components import Mirror, Space

M1 = Mirror("M1")
M2 = Mirror("M2")

model = Model()
# connect M1 <-> M2 in a model via a Space of length 1m
model.chain(M1, Space("M1_M2", L=10), M2)

print(f"Port M1.p2 name = {M1.p2.name}")
print(f"Port M1.p2 type = {M1.p2.type}")
print(f"Port M1.p2 owning component = {M1.p2.component}")
print(f"Port M1.p2 attached component = {M1.p2.attached_to}")
print(f"Port M1.p2 nodes = {M1.p2.nodes}")
Port M1.p2 name = p2
Port M1.p2 type = NodeType.OPTICAL
Port M1.p2 owning component = <'M1' @ 0x7f493d7d1190 (Mirror)>
Port M1.p2 attached component = <'M1_M2' @ 0x7f491dff91f0 (Space)>
Port M1.p2 nodes = (<OpticalNode M1.p2.i @ 0x7f491e077b80>, <OpticalNode M1.p2.o @ 0x7f491e077bb0>)

Accessing nodes — via a component

Nodes can be accessed directly through components or via the Model instance that their owning component is associated with (see next section for details). To access all the nodes of a component:

print(f"All nodes of M1 = {M1.nodes}")
All nodes of M1 = {'M1.p1.i': <OpticalNode M1.p1.i @ 0x7f491e2220d0>, 'M1.p1.o': <OpticalNode M1.p1.o @ 0x7f491e222160>, 'M1.p2.i': <OpticalNode M1.p2.i @ 0x7f491e077b80>, 'M1.p2.o': <OpticalNode M1.p2.o @ 0x7f491e077bb0>, 'M1.mech.z': <MechanicalNode M1.mech.z @ 0x7f491e077c10>, 'M1.mech.yaw': <MechanicalNode M1.mech.yaw @ 0x7f491e077ca0>, 'M1.mech.pitch': <MechanicalNode M1.mech.pitch @ 0x7f491e077d00>, 'M1.phase_sig.i': <ElectricalNode M1.phase_sig.i @ 0x7f491e077c70>, 'M1.phase_sig.o': <ElectricalNode M1.phase_sig.o @ 0x7f491e077d30>}

Or all the optical nodes:

print(f"Optical nodes of M1 = {M1.optical_nodes}")
Optical nodes of M1 = (<OpticalNode M1.p1.i @ 0x7f491e2220d0>, <OpticalNode M1.p1.o @ 0x7f491e222160>, <OpticalNode M1.p2.i @ 0x7f491e077b80>, <OpticalNode M1.p2.o @ 0x7f491e077bb0>)

Get a single optical node of a component by its direction:

print(f"Input node of port M1.n1 = {M1.p1.i}")
print(f"Output node of port M1.n1 = {M1.p1.o}")
Input node of port M1.n1 = <OpticalNode M1.p1.i @ 0x7f491e2220d0>
Output node of port M1.n1 = <OpticalNode M1.p1.o @ 0x7f491e222160>

Accessing nodes — via the model

Nodes play an important role in the Model class as the Node.full_name property forms the node_type of the underlying directed graph object (stored in Model.network). Thus, we use Node instances to report on graph data as well as perform operations on this graph - see the networkx DiGraph documentation for details on these methods and attributes.

You can also access all the nodes of a given NodeType in a Model instance with (e.g. for optical nodes):

print(f"All optical nodes in model: {model.optical_nodes}")
All optical nodes in model: [<OpticalNode M1.p1.i @ 0x7f491e2220d0>, <OpticalNode M1.p1.o @ 0x7f491e222160>, <OpticalNode M1.p2.i @ 0x7f491e077b80>, <OpticalNode M1.p2.o @ 0x7f491e077bb0>, <OpticalNode M2.p1.i @ 0x7f491e077f70>, <OpticalNode M2.p1.o @ 0x7f491e077fa0>, <OpticalNode M2.p2.i @ 0x7f491dff9040>, <OpticalNode M2.p2.o @ 0x7f491dff9070>]

Node names are used as keys in the network of a Model to get data on the node itself and edges connected to the node:

print(model.network.nodes[model.M2.p1.i.full_name])
print(model.network[model.M2.p1.i.full_name][model.M2.p1.o.full_name])
{'weakref': <weakref at 0x7f491e002270; to 'OpticalNode' at 0x7f491e077f70>, 'owner': <weakref at 0x7f491e0796d0; to 'Mirror' at 0x7f493d7d1130>, 'optical': True}
{'name': 'M2.p1.i->M2.p1.o', 'in_ref': <weakref at 0x7f491e002270; to 'OpticalNode' at 0x7f491e077f70>, 'out_ref': <weakref at 0x7f491e0022c0; to 'OpticalNode' at 0x7f491e077fa0>, 'owner': <weakref at 0x7f491e0796d0; to 'Mirror' at 0x7f493d7d1130>, 'length': 1, 'coupling_type': <CouplingType.OPTICAL_TO_OPTICAL: 0>}