Source code for elli.result

# Encoding: utf-8
from typing import List

import numpy as np
import numpy.typing as npt
from numpy.lib.scimath import sqrt


def _polar_index(index: str) -> int:
    """Return polarization index for character 'index'.

    Args:
        index (str): Polarisation index, valid are: 'p', 's', 'R', 'L'.
    Returns:
        int: 'p', 'L' -> 0
                's', 'R' -> 1
    """
    if index in ['p', 'L']:
        return 0
    if index in ['s', 'R']:
        return 1

    return ValueError('Wrong index given for variable.')


[docs]class Result: """Record of a simulation result.""" @property def rho(self) -> npt.NDArray: rho = np.dot(self.rho_matrix, self.experiment.jones_vector) rho = rho[:, 0] / rho[:, 1] return rho @property def psi(self) -> npt.NDArray: return np.rad2deg(np.arctan(np.abs(self.rho))) @property def delta(self) -> npt.NDArray: return -np.angle(self.rho, deg=True) @property def rho_matrix(self) -> npt.NDArray: r_ss = self.jones_matrix_r[..., 1, 1] return self.jones_matrix_r / r_ss[:, None, None] @property def psi_matrix(self) -> npt.NDArray: return np.rad2deg(np.arctan(np.abs(self.rho_matrix))) @property def delta_matrix(self) -> npt.NDArray: return -np.angle(self.rho_matrix, deg=True) @property def mueller_matrix(self) -> npt.NDArray: a = np.array([[1, 0, 0, 1], [1, 0, 0, -1], [0, 1, 1, 0], [0, 1j, -1j, 0]]) # Kronecker product of S and S* s_kron_s_star = np.einsum('aij,akl->aikjl', np.conjugate(self.rho_matrix), self.rho_matrix).reshape([self.rho_matrix.shape[0], 4, 4]) mueller_matrix = np.real(a @ s_kron_s_star @ np.linalg.inv(a)) mm11 = mueller_matrix[:, 0, 0] return mueller_matrix / mm11[:, None, None] @property def jones_matrix_t(self) -> npt.NDArray: return self._jones_matrix_t @property def jones_matrix_r(self) -> npt.NDArray: return self._jones_matrix_r @property def jones_matrix_tc(self) -> npt.NDArray: c = 1 / sqrt(2) * np.array([[1, 1], [1j, -1j]]) return np.einsum('ij,...jk,kl->...il', np.linalg.inv(c), self._jones_matrix_t, c) @property def jones_matrix_rc(self) -> npt.NDArray: c = 1 / sqrt(2) * np.array([[1, 1], [1j, -1j]]) d = 1 / sqrt(2) * np.array([[-1, -1], [-1j, 1j]]) return np.einsum('ij,...jk,kl->...il', np.linalg.inv(d), self._jones_matrix_r, c) @property def R(self) -> npt.NDArray: return np.abs(self._jones_matrix_r) ** 2 @property def T(self) -> npt.NDArray: return np.abs(self._jones_matrix_t) ** 2 * self._power_correction[:, None, None] @property def Rc(self) -> npt.NDArray: return np.abs(self.jones_matrix_rc) ** 2 @property def Tc(self) -> npt.NDArray: return np.abs(self.jones_matrix_tc) ** 2 * self._power_correction[:, None, None] def __init__(self, experiment: "Experiment", jones_matrix_r: npt.NDArray, jones_matrix_t: npt.NDArray, power_correction: npt.NDArray = None) -> None: """Creates result object, to store simulation data. Gets called by solvers. Args: experiment (Experiment): Evalatued experiment, with structure and experimental parameters. jones_matrix_r (npt.NDArray): Jones matrix for the reflection direction. jones_matrix_t (npt.NDArray): Jones matrix for the transmission direction. power_correction (npt.NDArray): Correction factors, to get the power transmission values. """ self.experiment = experiment self._jones_matrix_r = jones_matrix_r self._jones_matrix_t = jones_matrix_t if power_correction is None: self._power_correction = np.ones_like(self.experiment.lbda) else: self._power_correction = power_correction
[docs] def get(self, name: str) -> npt.NDArray: """Return the data for the requested variable 'name'. Args: name (str): Variable name to return. Examples for 'name': 'r_sp' : Amplitude reflection coefficient from 's' to 'p' polarization. 'r_LR' : Reflection from circular right to circular left polarization. 'T_pp' : Power transmission coefficient from 'p' to 'p' polarization. 'Ψ_ps', 'Δ_pp' : Ellipsometry parameters. 'psi', 'delta', 'rho': Reduced ellipsometry parameters, the whole matricies are returned by 'psi_matrix'. Returns: npt.NDArray: Array of data. """ return self.__getattr__(name)
def __getattr__(self, name: str) -> npt.NDArray: """Return the data for the requested variable 'name'. Args: name (str): Variable name to return. Examples for 'name'... 'r_sp' : Amplitude reflection coefficient from 's' to 'p' polarization. 'r_LR' : Reflection from circular right to circular left polarization. 'T_pp' : Power transmission coefficient from 'p' to 'p' polarization. 'Ψ_ps', 'Δ_pp' : Ellipsometry parameters. 'psi', 'delta', 'rho': Reduced ellipsometry parameters, the whole matricies are returned by 'psi_matrix'. Returns: npt.NDArray: Array of data. """ names = name.rsplit('_', 1) if names[0] == 'Ψ': names[0] = 'psi' elif names[0] == 'Δ': names[0] = 'delta' elif names[0] == 'ρ': names[0] = 'rho' if names[0] not in ['psi', 'delta', 'rho', 'r', 't', 'rc', 'tc', 'R', 'T', 'Rc', 'Tc', 'jones_matrix_r', 'jones_matrix_t', 'jones_matrix_rc', 'jones_matrix_tc']: raise AttributeError(f"'Result' object has no attribute '{name}'") if len(names) > 1: (i, j) = map(_polar_index, names[1]) if names[0] in ['psi', 'delta', 'rho']: if len(names) == 1: return self.__getattribute__(names[0]) return self.__getattribute__(names[0] + '_matrix')[:, i, j] if names[0] in ['r', 'rc', 't', 'tc']: if len(names) == 1: return self.__getattribute__('jones_matrix_' + names[0]) return self.__getattribute__('jones_matrix_' + names[0])[:, i, j] return self.__getattribute__(names[0])[:, i, j]
[docs]class ResultList(): """Class to make a row of Results easier to handle. """ def __init__(self, results: List[Result] = None) -> None: """Creates an ResultList object. Args: results (List[Result], optional): List of results to store. Defaults to None. """ if results is None: self.results = [] else: self.results = results
[docs] def append(self, result: Result) -> None: """Append a single Result to the ResultList. Args: result (Result): Additional Result to store. """ self.results.append(result)
def __len__(self) -> int: """Returns lenght of ResultList. Returns: int: Number of Results in ResultList. """ return len(self.results) def __getattr__(self, name: str) -> npt.NDArray: """Returns the data for the requested variable 'name' of all results. Args: name (str): Variable name to return. Examples for 'name'... 'r_sp' : Amplitude reflection coefficient from 's' to 'p' polarization. 'r_LR' : Reflection from circular right to circular left polarization. 'T_pp' : Power transmission coefficient from 'p' to 'p' polarization. 'Ψ_ps', 'Δ_pp' : Ellipsometry parameters. 'psi', 'delta', 'rho': Reduced ellipsometry parameters, the whole matricies are returned by 'psi_matrix'. Returns: npt.NDArray: Array of data. """ names = name.rsplit('_', 1) if names[0] == 'Ψ': names[0] = 'psi' elif names[0] == 'Δ': names[0] = 'delta' elif names[0] == 'ρ': names[0] = 'rho' if names[0] not in ['psi', 'delta', 'rho', 'r', 't', 'rc', 'tc', 'R', 'T', 'Rc', 'Tc', 'jones_matrix_r', 'jones_matrix_t', 'jones_matrix_rc', 'jones_matrix_tc']: raise AttributeError(f"'Result' object has no attribute '{name}'") if len(names) > 1: (i, j) = map(_polar_index, names[1]) if names[0] in ['psi', 'delta', 'rho']: if len(names) == 1: return np.squeeze(np.array([ result.__getattribute__(names[0]) for result in self.results])) return np.squeeze(np.array([ result.__getattribute__(names[0] + '_matrix')[:, i, j] for result in self.results])) if names[0] in ['r', 'rc', 't', 'tc']: if len(names) == 1: return np.squeeze(np.array([ result.__getattribute__('jones_matrix_' + names[0]) for result in self.results])) return np.squeeze(np.array([ result.__getattribute__('jones_matrix_' + names[0])[:, i, j] for result in self.results])) return np.squeeze(np.array([ result.__getattribute__(names[0])[:, i, j] for result in self.results]))