"""Utility functions related to component objects."""
import os
[docs]def refractive_index(port, symbolic=False):
"""Obtains the refractive index of the space attached to the port/node `port`.
Parameters
----------
port : :class:`.Port` or :class:`.Node`
Port or node.
Returns
-------
nr : float
The refractive index of the space attached to the port / node.
Returns unity if no space is present.
"""
get_nr = lambda space, symbol: space.nr.ref if symbol else space.nr.eval()
space = port.space
if space is not None:
nr = get_nr(space, symbolic)
else:
from finesse.components import Beamsplitter
# If we're at a beamsplitter then get nr
# of port on same surface
if isinstance(port.component, Beamsplitter):
adj_port = port.component.get_adjacent_port(port)
space = adj_port.space
if space is None:
# FIXME (sjr) This should probably raise an exception but parsing
# (at least for legacy files) means that both spaces
# can be None initially somehow when symbolising
# Beamsplitter ABCD matrices
nr = 1
else:
nr = get_nr(space, symbolic)
else:
nr = 1
return nr
[docs]def names_to_nodes(model, names, default_hints=()):
"""Attempts to convert a list of node/dof/ports into nodes. This is to provide a way
for actions to convert string names into nodes for simulation use. It attempts to
provide useful default behaviours, and can accept "hints" on how to select nodes.
Parameters
----------
names : iterable[str|(str, iterable[str])]
A collection of names to convert. A (name, hint) pair can also be provided
where hint is an iterable of strings.
default_hints : iterable[str]
Default hint to use with particular set of names if no hints are provided.
Notes
-----
Posible hints when name is a Port or DOF:
- `input` : try and select a singular input node
- `output` : try and select a singular output node
Examples
--------
Selecting various nodes from a model with and without hinting:
>>> import finesse
>>> from finesse.utilities.components import names_to_nodes
>>> model = finesse.Model()
>>> model.parse('''
... l l1 P=100k
... mod mod1 f=10M midx=0.1 order=1 mod_type=pm
... m m1 R=1-m1.T T=0.014 Rc=-1984 xbeta=0n
... m m2 R=1 T=0 Rc=2245 xbeta=0n phi=0
... link(l1, mod1, m1, 3994, m2)
... dof DARM m1.dofs.z -1 m2.dofs.z +1
... ''')
>>> names_to_nodes(model, ('m1.p1', 'DARM.AC'), default_hints=('output'))
[<OpticalNode m1.p1.o @ 0x7f92c9a5c880>,
<SignalNode DARM.AC.o @ 0x7f92c9a0e5b0>]
>>> names_to_nodes(model, ('m1.p1', 'DARM.AC'), default_hints=('input'))
[<OpticalNode m1.p1.i @ 0x7f92c9a5ca60>,
<SignalNode DARM.AC.i @ 0x7f92c9a0e460>]
>>> names_to_nodes(model, ('m1.p1.o', 'DARM.AC.i'))
[<OpticalNode m1.p1.o @ 0x7f92c9a5c880>,
<SignalNode DARM.AC.i @ 0x7f92c9a0e460>]
Hints do not insist on a particular output. For example, this is valid:
>>> names_to_nodes(model, ('m1.p1.o', ('DARM.AC.o', "input")))
"""
from finesse.components import Port, Node, DegreeOfFreedom
rtn = []
for name in names:
# Actions can provide hints on how to select nodes
try:
name, hints = name
except Exception:
hints = default_hints
obj = model.get(name)
is_port = isinstance(obj, Port)
is_node = isinstance(obj, Node)
is_dof = isinstance(obj, DegreeOfFreedom)
# Grab the AC port by default for DOFs
if is_dof:
obj = obj.AC
is_port = True
is_dof = False
if is_port:
N = len(obj.nodes)
if N == 1:
# select only single node from port
obj = obj.nodes[0]
elif "input" in hints:
is_input_node = tuple(_.is_input for _ in obj.nodes)
if is_input_node.count(True) == 1:
idx = is_input_node.index(True)
obj = obj.nodes[idx]
else:
raise ValueError(
f"Port {repr(obj)} does not have a single input node so you must specify which to use. It has \n{ os.linesep.join((repr(_) for _ in obj.nodes)) }"
)
elif "output" in hints:
is_output_node = tuple(not _.is_input for _ in obj.nodes)
if is_output_node.count(True) == 1:
idx = is_output_node.index(True)
obj = obj.nodes[idx]
else:
raise ValueError(
f"Port {repr(obj)} does not have a single output node so you must specify which to use. It has \n{ os.linesep.join((repr(_) for _ in obj.nodes)) }"
)
else:
raise ValueError(
f"Port {repr(obj)} does not have a single node to select from, so you must specify which to use. It has: \n{ os.linesep.join((repr(_) for _ in obj.nodes)) }"
)
elif not (is_node or is_port):
raise ValueError(f"Value {repr(obj)} was neither a Port nor a Node")
rtn.append(obj)
return rtn