Source code for finesse.analysis.actions.dc

import numpy as np

from ...solutions import BaseSolution
from .base import Action
from ...components.node import OpticalNode


[docs]class DCFieldsSolutions(BaseSolution): """Contains the result of a :class:`DCFields` action. The returned fields are the internal description of the carrier are all nodes, modes, and frequencies. Fields are defined as P=|E|^2. Attributes ---------- homs : list The Hermite-Gaussian higher order mode indices at each node, of each frequency. nodes : tuple[str] Name of nodes the fields were extracted at frequencies : array_like[dtype=float] Values of the optical frequencies at each node fields : array_like Field data array with shape [nodes, frequencies, HOMs.shape[0]] Notes ----- The `fields` attribute will contain all the required information with its ordering defined by the `nodes`, `frequencies`, and `homs` array. For easier indexing you can access the solution directly with node names. Select all the frequencies and modes at l1.p1.i: >>> sol['l1.p1.i'] Select all the first modes at all frequencies and modes at l1.p1.i: >>> sol['l1.p1.i', :, 0] Select all the frequencies and modes at two nodes: >>> sol[('l1.p1.i', 'l1.p1.o')] The same nodes can be selected directly with the OpticalNodes: >>> sol[(model.l1.p1.i, model.l1.p1.o)] Select all the HG02 modes at every node and frequency >>> sol[:, :, sol.homs.index([0, 2]))] Select all the HG02 modes at every output node and every frequency >>> idx = np.array([node.split(".")[-1] == "o" for node in sol.nodes]) >>> sol[idx, :, sol.homs.index([0, 2])] The frequency and mode selection must be a slice or an array of integer or boolean values. """ homs = None nodes = None frequencies = None fields = None
[docs] def selector(self, key): def node_name(key): if isinstance(key, str): return key elif isinstance(key, OpticalNode): return key.full_name else: raise TypeError("key should be a string or optical node") if isinstance(key, (str, int, slice, np.ndarray, OpticalNode)): # specifying a single node or a slice of multiple nodes key = [key] elif isinstance(key, tuple): # this is either a tuple specifying multiple nodes or # specifying multiple dimensions assert len(key) > 1 if len(key) in [2, 3]: if isinstance(key[1], (str, OpticalNode)): # specifying multiple nodes key = [key] else: # specifying multiple dimensions key = list(key) else: # specifying multiple nodes key = [list(key)] else: raise TypeError( f"Unrecognized key type {type(key)}." "Multiple nodes should be specified by tuples, slices, or ndarrays." ) if not isinstance(key[0], (slice, int, np.ndarray)): nodes = np.array([node_name(n) for n in np.atleast_1d(key[0])]) for i in nodes: if i not in self.nodes: raise KeyError(f"`{i}` is not in the solution nodes") key[0] = tuple(self.nodes.index(i) for i in nodes) return tuple(key)
def __getitem__(self, key): return self.fields[self.selector(key)]
[docs]class DCFields(Action): """An action that saves the DC (carrier) fields at all nodes, frequencies, and higher order modes for the current state of the simulation. Parameters ---------- name : str, optional Name of the solution generated by this action """ def __init__(self, *, name="dcfields"): super().__init__(name) def _requests(self, model, memo, first=True): pass def _do(self, state): sol = DCFieldsSolutions(self.name) # do the DC solve again just in case a parameter has changed and another # action hasn't done so already state.sim.run_carrier() sol.homs = state.model.homs.tolist() sol.nodes = tuple(state.sim.carrier.nodes.keys()) sol.frequencies = np.array( [f.f for f in state.sim.carrier.optical_frequencies.frequencies] ) sol.fields = np.reshape( state.sim.carrier.M().rhs_view[0, :].copy(), (len(sol.nodes), len(sol.frequencies), len(sol.homs)), order="C", ) # rescale into sqrt(W) units sol.fields /= np.sqrt(2) return sol