"""Accumulated Gouy phase detector."""
import logging
import numpy as np
from .general import Detector
from .compute.gaussian import GouyDetectorWorkspace
from ..env import warn
LOGGER = logging.getLogger(__name__)
[docs]class Gouy(Detector):
"""Detector to measure the accumulated gouy phase across a sequence of spaces.
This detector can operate in one of two modes, depending upon args given:
* automatically determine the spaces through an arbitrary path by specifying the
`from_node` and `to_node` arguments,
* OR provide a pre-determined sequence of spaces (or their names) as positional
arguments.
Whichever option is chosen, this detector will compute the same fundamental quantity;
that is the sum of the Gouy phases accumulated over each space (in degrees).
Parameters
----------
name : str
Name of newly created gouy detector.
*args : sequence
A sequence of spaces or space names.
from_node : node-like
An :class:`.OpticalNode` instance, or a data type which can be converted
to an optical node.
to_node : node-like
An :class:`.OpticalNode` instance, or a data type which can be converted
to an optical node.
via_node : node-like
An :class:`.OpticalNode` instance, or a data type which can be converted
to an optical node.
direction : str
Plane to detect in - 'x' for tangential, 'y' for sagittal. Defaults to 'x'.
"""
def __init__(
self, name, *args, from_node=None, to_node=None, via_node=None, direction="x"
):
Detector.__init__(
self, name, dtype=np.float64, unit="degrees", label="Gouy phase"
)
nodes = from_node, to_node, via_node
if args and any(node is not None for node in nodes):
raise ValueError(
f"Error in Gouy detector {self.name}:\n"
" Cannot specify both a sequence of spaces and a node path."
)
if not args and all(node is None for node in nodes):
raise ValueError(
f"Error in Gouy detector {self.name}:\n"
" Must specify EITHER a sequence of spaces or a node path."
)
self.direction = direction
self.__spaces = list(args)
self.__from_node = from_node
self.__to_node = to_node
self.__via_node = via_node
@property
def needs_fields(self):
return False
@property
def needs_trace(self):
return True
@property
def spaces(self):
"""The spaces that this detector will calculated the accumulated Gouy phase
over."""
return self.__spaces.copy()
def _lookup_spaces(self):
from finesse.components import Space
if not self.has_model:
raise RuntimeError(f"Bug detected! No model associated with {self.name}")
for i, item in enumerate(
self.__spaces
): # sequence of spaces / space names given
if isinstance(item, Space):
continue
if not isinstance(item, str):
raise TypeError(
f"Error in Gouy detector {self.name}:\n"
" Unexpected type in iterable of spaces. Expected a "
f"Space or str, but got type: {type(item)}"
)
space = self._model.elements.get(item, None)
if space is None:
raise LookupError(
f"Error in Gouy detector {self.name}:\n"
f" No space of name {item} in model."
)
if not isinstance(space, Space):
raise TypeError(
f"Error in Gouy detector {self.name}:\n"
f" Element of name {item} is not a Space."
)
self.__spaces[i] = space
# node path source, target given
if self.__from_node is not None and self.__to_node is not None:
path = self._model.path(self.__from_node, self.__to_node, self.__via_node)
self.__spaces = list(path.spaces)
if self.__spaces:
LOGGER.info(
"Gouy detector %s using spaces: %s",
self.name,
self.__spaces,
)
else:
warn(
f"Gouy detector {repr(self.name)} not using any spaces! This will "
f"always return values of zero in a simulation."
)
def _get_workspace(self, sim):
ws = GouyDetectorWorkspace(self, sim)
return ws