diff --git a/MANIFEST.in b/MANIFEST.in index 6b5bcc73..ba2d1d90 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ -recursive-include src/elli/database/refractiveindexinfo-database *.yml \ No newline at end of file +recursive-include src/elli/database/refractiveindexinfo-database *.yml +include src/elli/formula_parser/dispersion_function_grammer.lark \ No newline at end of file diff --git a/README.md b/README.md index 5ba430e3..fbf2d876 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ the folder to install it in development mode: ```sh git clone https://github.com/PyEllips/pyElli cd pyElli -pip install -e .[fitting] +pip install -e ".[fitting]" ``` ## Acknowledgements diff --git a/pyproject.toml b/pyproject.toml index b25148a9..85850b1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,9 @@ dependencies = [ "h5py", "pyyaml", "importlib-resources", - "rapidfuzz" + "rapidfuzz", + "lark>=1.1.5", + "pint", ] [project.optional-dependencies] diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index 5b834ead..7193b689 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: # # pip-compile --extra=fitting --extra=testing --generate-hashes --output-file=requirements/dev-requirements.txt pyproject.toml requirements/fitting-requirements.txt # @@ -292,6 +292,12 @@ h5py==3.7.0 \ # via # -r requirements/fitting-requirements.txt # pyElli (pyproject.toml) +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d + # via + # -r requirements/fitting-requirements.txt + # nbconvert importlib-resources==5.10.0 \ --hash=sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668 \ --hash=sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437 @@ -451,6 +457,12 @@ kiwisolver==1.4.4 \ # via # -r requirements/fitting-requirements.txt # matplotlib +lark==1.1.5 \ + --hash=sha256:4b534eae1f9af5b4ea000bea95776350befe1981658eea3820a01c37e504bb4d \ + --hash=sha256:8476f9903e93fbde4f6c327f74d79e9b4bd0ed9294c5dfa3164ab8c581b5de2a + # via + # -r requirements/fitting-requirements.txt + # pyElli (pyproject.toml) lmfit==1.0.3 \ --hash=sha256:d067c3ea501f035af5d3c079e6e6e35dc3cc1ac7d439429a425b0aeb5a7858a2 # via @@ -828,6 +840,12 @@ pillow==9.3.0 \ # via # -r requirements/fitting-requirements.txt # matplotlib +pint==0.20.1 \ + --hash=sha256:387cf04078dc7dfe4a708033baad54ab61d82ab06c4ee3d4922b1e45d5626067 \ + --hash=sha256:68afe65665542ee3ec99f69f043b1d39bfe7c6d61b786940157138fd08b838fb + # via + # -r requirements/fitting-requirements.txt + # pyElli (pyproject.toml) plotly==5.10.0 \ --hash=sha256:4d36d9859b7a153b273562deeed8c292587a472eb1fd57cd4158ec89d9defadb \ --hash=sha256:989b13825cc974390aa0169479485d9257d37848a47bc63957251f8e1a7046ba @@ -1382,3 +1400,10 @@ widgetsnbextension==3.6.1 \ # via # -r requirements/fitting-requirements.txt # ipywidgets +zipp==3.15.0 \ + --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ + --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 + # via + # -r requirements/fitting-requirements.txt + # importlib-metadata + # importlib-resources diff --git a/requirements/fitting-requirements.txt b/requirements/fitting-requirements.txt index c500d794..fed27aa7 100644 --- a/requirements/fitting-requirements.txt +++ b/requirements/fitting-requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: # # pip-compile --extra=fitting --generate-hashes --output-file=requirements/fitting-requirements.txt pyproject.toml requirements/requirements.txt # @@ -202,6 +202,10 @@ h5py==3.7.0 \ # via # -r requirements/requirements.txt # pyElli (pyproject.toml) +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d + # via nbconvert importlib-resources==5.10.0 \ --hash=sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668 \ --hash=sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437 @@ -338,6 +342,12 @@ kiwisolver==1.4.4 \ --hash=sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09 \ --hash=sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c # via matplotlib +lark==1.1.5 \ + --hash=sha256:4b534eae1f9af5b4ea000bea95776350befe1981658eea3820a01c37e504bb4d \ + --hash=sha256:8476f9903e93fbde4f6c327f74d79e9b4bd0ed9294c5dfa3164ab8c581b5de2a + # via + # -r requirements/requirements.txt + # pyElli (pyproject.toml) lmfit==1.0.3 \ --hash=sha256:d067c3ea501f035af5d3c079e6e6e35dc3cc1ac7d439429a425b0aeb5a7858a2 # via pyElli (pyproject.toml) @@ -679,6 +689,12 @@ pillow==9.3.0 \ --hash=sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb \ --hash=sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0 # via matplotlib +pint==0.20.1 \ + --hash=sha256:387cf04078dc7dfe4a708033baad54ab61d82ab06c4ee3d4922b1e45d5626067 \ + --hash=sha256:68afe65665542ee3ec99f69f043b1d39bfe7c6d61b786940157138fd08b838fb + # via + # -r requirements/requirements.txt + # pyElli (pyproject.toml) plotly==5.10.0 \ --hash=sha256:4d36d9859b7a153b273562deeed8c292587a472eb1fd57cd4158ec89d9defadb \ --hash=sha256:989b13825cc974390aa0169479485d9257d37848a47bc63957251f8e1a7046ba @@ -1126,3 +1142,10 @@ widgetsnbextension==3.6.1 \ --hash=sha256:954e0faefdd414e4e013f17dbc7fd86f24cf1d243a3ac85d5f0fc2c2d2b50c66 \ --hash=sha256:9c84ae64c2893c7cbe2eaafc7505221a795c27d68938454034ac487319a75b10 # via ipywidgets +zipp==3.15.0 \ + --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ + --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 + # via + # -r requirements/requirements.txt + # importlib-metadata + # importlib-resources diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 9e8cd34f..42d363c4 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: # # pip-compile --generate-hashes --output-file=requirements/requirements.txt pyproject.toml # @@ -30,6 +30,10 @@ importlib-resources==5.10.0 \ --hash=sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668 \ --hash=sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437 # via pyElli (pyproject.toml) +lark==1.1.5 \ + --hash=sha256:4b534eae1f9af5b4ea000bea95776350befe1981658eea3820a01c37e504bb4d \ + --hash=sha256:8476f9903e93fbde4f6c327f74d79e9b4bd0ed9294c5dfa3164ab8c581b5de2a + # via pyElli (pyproject.toml) numpy==1.23.2 \ --hash=sha256:17e5226674f6ea79e14e3b91bfbc153fdf3ac13f5cc54ee7bc8fdbe820a32da0 \ --hash=sha256:2bd879d3ca4b6f39b7770829f73278b7c5e248c91d538aab1e506c628353e47f \ @@ -87,6 +91,10 @@ pandas==1.4.4 \ --hash=sha256:e7cc960959be28d064faefc0cb2aef854d46b827c004ebea7e79b5497ed83e7d \ --hash=sha256:ee6f1848148ed3204235967613b0a32be2d77f214e9623f554511047705c1e04 # via pyElli (pyproject.toml) +pint==0.20.1 \ + --hash=sha256:387cf04078dc7dfe4a708033baad54ab61d82ab06c4ee3d4922b1e45d5626067 \ + --hash=sha256:68afe65665542ee3ec99f69f043b1d39bfe7c6d61b786940157138fd08b838fb + # via pyElli (pyproject.toml) python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 @@ -270,3 +278,7 @@ six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via python-dateutil +zipp==3.15.0 \ + --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ + --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 + # via importlib-resources diff --git a/src/elli/database/refractive_index_info.py b/src/elli/database/refractive_index_info.py index 72091630..44e30df6 100644 --- a/src/elli/database/refractive_index_info.py +++ b/src/elli/database/refractive_index_info.py @@ -21,7 +21,12 @@ SellmeierCustomExponent, Table, ) -from ..dispersions.base_dispersion import Dispersion, DispersionSum +from ..dispersions.base_dispersion import ( + Dispersion, + DispersionSum, + IndexDispersion, + IndexDispersionSum, +) from ..materials import IsotropicMaterial WavelengthFilterType = Union[ @@ -282,7 +287,9 @@ def get_mat(self, book: str, page: str) -> IsotropicMaterial: """ return IsotropicMaterial(self.load_dispersion(book, page)) - def load_dispersion(self, book: str, page: str) -> Dispersion: + def load_dispersion( + self, book: str, page: str + ) -> Union[Dispersion, IndexDispersion]: """Load a dispersion from the refractive index database. Selection by material and source identifiers. @@ -307,6 +314,7 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: ) dispersion_list = [] + contains_index_dispersion = False for dispersion_relation in yml_file["DATA"]: if dispersion_relation["type"] == "tabulated nk": @@ -319,6 +327,7 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: df.set_index("Wavelength", inplace=True) dispersion = Table(lbda=df.index, n=df["n"] + 1j * df["k"]) + contains_index_dispersion = True elif dispersion_relation["type"] == "tabulated n": df = pd.read_table( @@ -330,6 +339,7 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: df.set_index("Wavelength", inplace=True) dispersion = Table(lbda=df.index, n=df["n"]) + contains_index_dispersion = True elif dispersion_relation["type"] == "tabulated k": df = pd.read_table( @@ -341,6 +351,7 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: df.set_index("Wavelength", inplace=True) dispersion = Table(lbda=df.index, n=0 + 1j * df["k"]) + contains_index_dispersion = True elif dispersion_relation["type"] == "formula 1": coeffs = list( @@ -415,6 +426,7 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: cauchy.add(f_i / 1e3**e_i, e_i) dispersion = cauchy + contains_index_dispersion = True else: raise ValueError("Unimplemented Format.") @@ -423,6 +435,13 @@ def load_dispersion(self, book: str, page: str) -> Dispersion: if len(dispersion_list) == 1: return dispersion_list[0] + + if contains_index_dispersion: + for i, dispersion in enumerate(dispersion_list): + if not isinstance(dispersion, IndexDispersion): + dispersion_list[i] = dispersion.as_index() + + return IndexDispersionSum(*dispersion_list) return DispersionSum(*dispersion_list) def get_reference(self, book: str, page: str) -> str: diff --git a/src/elli/dispersions/__init__.py b/src/elli/dispersions/__init__.py index a29bf0a1..54afb816 100644 --- a/src/elli/dispersions/__init__.py +++ b/src/elli/dispersions/__init__.py @@ -20,3 +20,4 @@ from .tauc_lorentz import TaucLorentz from .cody_lorentz import CodyLorentz from .pseudo_dielectric import PseudoDielectricFunction +from .formula import Formula, FormulaIndex diff --git a/src/elli/dispersions/base_dispersion.py b/src/elli/dispersions/base_dispersion.py index de4c6a69..62d4bb6d 100644 --- a/src/elli/dispersions/base_dispersion.py +++ b/src/elli/dispersions/base_dispersion.py @@ -1,7 +1,8 @@ # Encoding: utf-8 """Abstract base class and utility classes for pyElli dispersion""" from abc import ABC, abstractmethod -from typing import List, Union +from copy import deepcopy +from typing import List, Optional, Union import numpy as np import numpy.typing as npt @@ -16,13 +17,15 @@ class InvalidParameters(Exception): """Exception for invalid dispersion parameters.""" -class Dispersion(ABC): +class BaseDispersion(ABC): """Dispersion (abstract class). Functions provided for derived classes: * dielectric_function(lbda) : returns dielectric constant for wavelength 'lbda' """ + default_lbda_range = np.linspace(200, 1000, 801) + @property @abstractmethod def single_params_template(self) -> dict: @@ -43,7 +46,7 @@ def _guard_invalid_params(params1, params2): @staticmethod def _fill_params_dict(template: dict, *args, **kwargs) -> dict: - Dispersion._guard_invalid_params(list(kwargs.keys()), list(template.keys())) + BaseDispersion._guard_invalid_params(list(kwargs.keys()), list(template.keys())) if (len(kwargs) + len(args)) > len(template): raise InvalidParameters("Too many parameters") @@ -73,6 +76,10 @@ def __init__(self, *args, **kwargs): self.single_params_template, *args, **kwargs ) + for param in self.single_params: + if self.single_params[param] is None: + raise InvalidParameters(f"Please specify parameter {param}") + @abstractmethod def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: """Calculates the dielectric function in a given wavelength window. @@ -101,47 +108,23 @@ def add(self, *args, **kwargs) -> "Dispersion": return self - def _check_valid_operand(self, other: Union[int, float, "Dispersion"]): - if not isinstance(other, (int, float, Dispersion)): - raise TypeError( - f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'" - ) - - def _is_non_std_dispersion(self, other: Union[int, float, "Dispersion"]) -> bool: - return isinstance(other, (IndexDispersion, dispersions.Table)) - - def __radd__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": - """Add up the dielectric function of multiple models""" - return self.__add__(other) - - def __add__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": - """Add up the dielectric function of multiple models""" - self._check_valid_operand(other) - - if self._is_non_std_dispersion(other): - return other.__add__(self) - - if isinstance(other, DispersionSum): - other.dispersions.append(self) - return other - - if isinstance(other, (int, float)): - return DispersionSum(self, dispersions.EpsilonInf(other)) - - return DispersionSum(self, other) - - def get_dielectric(self, lbda: npt.ArrayLike) -> npt.NDArray: + def get_dielectric(self, lbda: Optional[npt.ArrayLike] = None) -> npt.NDArray: """Returns the dielectric constant for wavelength 'lbda' default unit (nm) in the convention ε1 + iε2.""" + lbda = self.default_lbda_range if lbda is None else lbda return np.asarray(self.dielectric_function(lbda), dtype=np.complex128) - def get_refractive_index(self, lbda: npt.ArrayLike) -> npt.NDArray: + def get_refractive_index(self, lbda: Optional[npt.ArrayLike] = None) -> npt.NDArray: """Returns the refractive index for wavelength 'lbda' default unit (nm) in the convention n + ik.""" + lbda = self.default_lbda_range if lbda is None else lbda + + if isinstance(self, IndexDispersion): + return self.refractive_index(lbda) return sqrt(self.dielectric_function(lbda)) def get_dielectric_df( - self, lbda: npt.ArrayLike = None, conjugate=False + self, lbda: Optional[npt.ArrayLike] = None, conjugate=False ) -> pd.DataFrame: """Returns the dielectric function as a pandas dataframe @@ -159,7 +142,7 @@ def get_dielectric_df( A pandas dataframe containing the wavelength as index and two rows containing ε1 and ε2. """ - lbda = np.linspace(200, 1000, 801) if lbda is None else lbda + lbda = self.default_lbda_range if lbda is None else lbda eps = self.get_dielectric(lbda) return pd.DataFrame( @@ -168,7 +151,7 @@ def get_dielectric_df( ) def get_refractive_index_df( - self, lbda: npt.ArrayLike = None, conjugate=False + self, lbda: Optional[npt.ArrayLike] = None, conjugate=False ) -> pd.DataFrame: """Returns the refractive index as a pandas dataframe @@ -186,7 +169,7 @@ def get_refractive_index_df( A pandas dataframe containing the wavelength as index and two rows containing n and k. """ - lbda = np.linspace(200, 1000, 801) if lbda is None else lbda + lbda = self.default_lbda_range if lbda is None else lbda nk = self.get_refractive_index(lbda) return pd.DataFrame( @@ -214,7 +197,58 @@ def _dict_to_str(dic): ) -class IndexDispersion(Dispersion): +class Dispersion(BaseDispersion): + """A dispersion based on a dielectric function formulation.""" + + def _check_valid_operand(self, other: Union[int, float, "Dispersion"]): + if not isinstance(other, (int, float, Dispersion, dispersions.TableEpsilon)): + raise TypeError( + f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'" + ) + + def __radd__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": + """Add up the dielectric function of multiple models""" + return self.__add__(other) + + def __add__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": + """Add up the dielectric function of multiple models""" + if isinstance(other, IndexDispersion): + raise TypeError( + "Cannot add refractive index and dielectric function based dispersions." + ) + self._check_valid_operand(other) + + if isinstance(other, dispersions.TableEpsilon): + return other.__add__(self) + + if isinstance(other, DispersionSum): + other.dispersions.append(self) + return other + + if isinstance(other, (int, float)): + return DispersionSum(self, dispersions.EpsilonInf(other)) + + return DispersionSum(self, other) + + def as_index(self): + """ + Returns this class as IndexDispersion. + This method may be used to add dielectric and index based dispersions. + Please ensure that you know what you are doing as building dielectric + and index based dispersions is normally mathematically wrong. + """ + index_class = deepcopy(self) + # pylint: disable=attribute-defined-outside-init + index_class.refractive_index = lambda lbda: sqrt( + index_class.dielectric_function(lbda) + ) + index_class.__class__ = IndexDispersion # pylint: disable=invalid-name + index_class.dielectric_function = self.dielectric_function + + return index_class + + +class IndexDispersion(BaseDispersion): """A dispersion based on a refractive index formulation.""" @abstractmethod @@ -228,29 +262,63 @@ def refractive_index(self, lbda: npt.ArrayLike) -> npt.NDArray: npt.NDArray: The refractive index for each wavelength point. """ - def __add__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": - self._check_valid_operand(other) + def _check_valid_operand(self, other: Union[int, float, "IndexDispersion"]): + if not isinstance(other, (int, float, IndexDispersion, dispersions.Table)): + raise TypeError( + f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'" + ) - if isinstance(other, IndexDispersion): - raise NotImplementedError( - "Adding of index based dispersions is not supported yet" + def __radd__( + self, other: Union[int, float, "IndexDispersion"] + ) -> "IndexDispersionSum": + """Add up the dielectric function of multiple models""" + return self.__add__(other) + + def __add__( + self, other: Union[int, float, "IndexDispersion"] + ) -> "IndexDispersionSum": + if isinstance(other, Dispersion): + raise TypeError( + "Cannot add refractive index and dielectric function based dispersions." ) - raise TypeError( - "Cannot add refractive index and dielectric function based dispersions." - ) + self._check_valid_operand(other) + + if isinstance(other, dispersions.Table): + return other.__add__(self) + + if isinstance(other, IndexDispersionSum): + other.index_dispersions.append(self) + return other + + if isinstance(other, (int, float)): + return IndexDispersionSum(self, dispersions.ConstantRefractiveIndex(other)) + + return IndexDispersionSum(self, other) def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: return self.refractive_index(lbda) ** 2 + def as_dielectric(self): + """ + Returns this class as Dispersion. + This method may be used to add dielectric and index based dispersions. + Please ensure that you know what you are doing as building dielectric + and index based dispersions is normally mathematically wrong. + """ + diel_disp = deepcopy(self) + diel_disp.__class__ = Dispersion # pylint: disable=invalid-name + diel_disp.dielectric_function = self.dielectric_function + return diel_disp + class DispersionFactory: """A factory class for dispersion objects""" @staticmethod def get_dispersion(identifier: str, *args, **kwargs) -> Dispersion: - """Creates a Dispersion object identified by its string name and initializes it with the - given parameters. + """Creates a Dispersion object identified by its string name and initializes it + with the given parameters. Args: identifier (str): Identifier of the Dispersion object, e.g. Cauchy. @@ -275,14 +343,14 @@ class DispersionSum(Dispersion): def __init__(self, *disps: Dispersion) -> None: super().__init__() - self.dispersions = list(disps) + + self.dispersions = [] + for disp in disps: + self += disp def __add__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": self._check_valid_operand(other) - if self._is_non_std_dispersion(other): - return other.__add__(self) - if isinstance(other, DispersionSum): self.dispersions += other.dispersions return self @@ -298,7 +366,7 @@ def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: dielectric_function = sum( disp.dielectric_function(lbda) for disp in self.dispersions ) - return dielectric_function + return np.array(dielectric_function) def __repr__(self): return ( @@ -307,3 +375,48 @@ def __repr__(self): + "\n\n" + "\n\n".join(map(str, self.dispersions)) ) + + +class IndexDispersionSum(IndexDispersion): + """Represents the sum of two index dispersions""" + + single_params_template: dict = {} + rep_params_template: dict = {} + index_dispersions: List[IndexDispersion] + + def __init__(self, *disps: IndexDispersion) -> None: + super().__init__() + + self.index_dispersions = [] + for disp in disps: + self += disp + + def __add__( + self, other: Union[int, float, "IndexDispersion"] + ) -> "IndexDispersionSum": + self._check_valid_operand(other) + + if isinstance(other, IndexDispersionSum): + self.index_dispersions += other.index_dispersions + return self + + if isinstance(other, (int, float)): + self.index_dispersions.append(dispersions.ConstantRefractiveIndex(n=other)) + return self + + self.index_dispersions.append(other) + return self + + def refractive_index(self, lbda: npt.ArrayLike) -> npt.NDArray: + refractive_index = sum( + disp.refractive_index(lbda) for disp in self.index_dispersions + ) + return np.array(refractive_index) + + def __repr__(self): + return ( + "IndexDispersionSum\n" + + "=" * 13 + + "\n\n" + + "\n\n".join(map(str, self.index_dispersions)) + ) diff --git a/src/elli/dispersions/cauchy.py b/src/elli/dispersions/cauchy.py index 0bea46c6..d12ca3c4 100644 --- a/src/elli/dispersions/cauchy.py +++ b/src/elli/dispersions/cauchy.py @@ -22,7 +22,8 @@ class Cauchy(IndexDispersion): Output: .. math:: \varepsilon^{1/2}(\lambda) = - \boldsymbol{n_0} + 100 \boldsymbol{n_1}/\lambda^2 + 10^7 \boldsymbol{n_2}/\lambda^4 + \boldsymbol{n_0} + 100 \boldsymbol{n_1}/\lambda^2 + + 10^7 \boldsymbol{n_2}/\lambda^4 + i (\boldsymbol{k_0} + 100 \boldsymbol{k_1}/\lambda^2 + 10^7 \boldsymbol{k_2}/\lambda^4) """ diff --git a/src/elli/dispersions/epsilon_inf.py b/src/elli/dispersions/epsilon_inf.py index c33eecef..bc6c9591 100644 --- a/src/elli/dispersions/epsilon_inf.py +++ b/src/elli/dispersions/epsilon_inf.py @@ -1,5 +1,6 @@ # Encoding: utf-8 """Constant epsilon infinity.""" +from typing import Any, Dict import numpy.typing as npt from .base_dispersion import Dispersion @@ -20,7 +21,7 @@ class EpsilonInf(Dispersion): """ single_params_template = {"eps": 1} - rep_params_template = {} + rep_params_template: Dict[str, Any] = {} def dielectric_function(self, _: npt.ArrayLike) -> npt.NDArray: return self.single_params.get("eps") diff --git a/src/elli/dispersions/formula.py b/src/elli/dispersions/formula.py new file mode 100644 index 00000000..fba98333 --- /dev/null +++ b/src/elli/dispersions/formula.py @@ -0,0 +1,127 @@ +"""A formula dispersion to parse dispersion values from a formula string.""" +from typing import Dict, List, Optional + +import numpy as np +import numpy.typing as npt + +from elli.units import ureg +from elli.dispersions.base_dispersion import BaseDispersion, Dispersion, IndexDispersion +from elli.formula_parser.parser import formula_parser, transformation_formula_parser + + +class FormulaParser(BaseDispersion): + r"""A formula dispersion""" + + @property + def single_params_template(self) -> dict: + return self.f_single_params + + @property + def rep_params_template(self) -> dict: + return self.f_rep_params + + def __init__( + self, + formula: str, + wavelength_axis_name: str, + single_params: Dict[str, float], + rep_params: Dict[str, npt.ArrayLike], + unit: Optional[str] = None, + ): + self.f_single_params: Dict[str, float] = single_params + self.f_axis_name: str = wavelength_axis_name + rep_params_len: Optional[int] = None + rep_params_sets: List[Dict[str, float]] = [] + + for key, values in rep_params.items(): + if not isinstance(values, (np.ndarray, list)): + raise ValueError( + "Repeated parameters must be given as dict of lists or numpy arrays" + ) + for i, value in enumerate(values): + if i >= len(rep_params_sets): + rep_params_sets.append({}) + rep_params_sets[i][key] = value + + if rep_params_len is None: + rep_params_len = len(values) + continue + if len(values) != rep_params_len: + raise ValueError( + f"All repeated parameters must have the same length." + f"Found {values} with length {len(values)}, " + f"but previous length was {rep_params_len}." + ) + + self.f_rep_params = {} + if rep_params_sets: + self.f_rep_params = rep_params_sets[0] + super().__init__() + + for rep_params_set in rep_params_sets: + self.add(**rep_params_set) + + self.rep_params_dl = {} + if self.rep_params: + self.rep_params_dl = { + k: np.array([dic[k] for dic in self.rep_params]) + for k in self.rep_params[0] + } + + self.formula = formula + + self._check_repr() + + self._dispersion_function = self.__dispersion_function + if unit is not None: + self._set_unit_conversion(unit) + + def _set_unit_conversion(self, unit: str): + quantity = ureg(unit) + if quantity.check("[length]"): + scaling = ureg("nm").to(unit).magnitude + self._dispersion_function = lambda lbda: self.__dispersion_function( + scaling * lbda + ) + return + + if quantity.check("[energy]"): + scaling = ureg("nm").to(unit).magnitude + self._dispersion_function = lambda lbda: self.__dispersion_function( + scaling / lbda + ) + return + + raise ValueError(f"Unsupported unit `{unit}`.") + + def _check_repr(self): + representation = formula_parser().parse(self.formula).data + + if isinstance(self, FormulaIndex) and not representation == "n": + raise ValueError( + f"Representation `{representation}` not supported by FormulaIndex" + ) + + if isinstance(self, Formula) and not representation == "eps": + raise ValueError( + f"Representation `{representation}` not supported by Formula" + ) + + def __dispersion_function(self, lbda: npt.ArrayLike) -> npt.NDArray: + return transformation_formula_parser( + self.f_axis_name, lbda, self.single_params, self.rep_params_dl + ).parse(self.formula)[1] + + +class Formula(Dispersion, FormulaParser): + r"A formula dispersion" + + def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: + return self._dispersion_function(lbda) + + +class FormulaIndex(IndexDispersion, FormulaParser): + r"""A formula dispersion in refractive index formulation""" + + def refractive_index(self, lbda: npt.ArrayLike) -> npt.NDArray: + return self._dispersion_function(lbda) diff --git a/src/elli/dispersions/pseudo_dielectric.py b/src/elli/dispersions/pseudo_dielectric.py index d06d853d..f0c6a7f0 100644 --- a/src/elli/dispersions/pseudo_dielectric.py +++ b/src/elli/dispersions/pseudo_dielectric.py @@ -45,10 +45,6 @@ class PseudoDielectricFunction(Dispersion): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - for param in self.single_params: - if self.single_params[param] is None: - raise InvalidParameters(f"Please specify parameter {param}") - rho = np.tan(np.deg2rad(self.single_params.get("psi"))) * np.exp( -1j * np.deg2rad(self.single_params.get("delta")) ) diff --git a/src/elli/dispersions/sellmeier.py b/src/elli/dispersions/sellmeier.py index e95c798c..668e06eb 100644 --- a/src/elli/dispersions/sellmeier.py +++ b/src/elli/dispersions/sellmeier.py @@ -13,7 +13,7 @@ class Sellmeier(Dispersion): Repeated parameters: :A: Coefficient for n\ :sup:`2` contribution. Defaults to 0. - :B: Resonance wavelength. Defaults to 0. Unit in µm\ :sup:`-2`. + :B: Resonance wavelength. Defaults to 0. Unit in µm\ :sup:`2`. Output: .. math:: diff --git a/src/elli/dispersions/sellmeier_custom.py b/src/elli/dispersions/sellmeier_custom.py index 0698b9a1..b611dd68 100644 --- a/src/elli/dispersions/sellmeier_custom.py +++ b/src/elli/dispersions/sellmeier_custom.py @@ -13,9 +13,9 @@ class SellmeierCustomExponent(Dispersion): Repeated parameters: :A: Coefficient for n\ :sup:`2` contribution. Defaults to 0. + :e_A: Exponent for the wavelength in the numerator. Defaults to 1. :B: Resonance wavelength. Defaults to 0. Unit in µm\ :sup:`-2`. - :e_A: - :e_B: + :e_B: Exponent for B. Defaults to 1. Output: .. math:: @@ -26,7 +26,7 @@ class SellmeierCustomExponent(Dispersion): """ single_params_template = {} - rep_params_template = {"A": 0, "B": 0, "e_A": 1, "e_B": 1} + rep_params_template = {"A": 0, "e_A": 1, "B": 0, "e_B": 1} def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: lbda = lbda / 1e3 diff --git a/src/elli/dispersions/table_epsilon.py b/src/elli/dispersions/table_epsilon.py index 57afa6da..b9c6a22b 100644 --- a/src/elli/dispersions/table_epsilon.py +++ b/src/elli/dispersions/table_epsilon.py @@ -1,11 +1,11 @@ # Encoding: utf-8 """Dispersion specified by a table of wavelengths (nm) and dielectric function values.""" -from typing import Union -import numpy as np +from typing import Any, Dict, Union import numpy.typing as npt import scipy.interpolate -from .base_dispersion import Dispersion, InvalidParameters +from .epsilon_inf import EpsilonInf +from .base_dispersion import Dispersion, InvalidParameters, DispersionSum class TableEpsilon(Dispersion): @@ -14,9 +14,9 @@ class TableEpsilon(Dispersion): wavelength range. Single parameters: - :lbda (list): Wavelengths in nm. Defaults to np.linspace(0, 3000, 1000). + :lbda (list): Wavelengths in nm. This value must be provided. :epsilon: Complex dielectric function values in the convention ε1 + iε2. - Defaults to np.ones(1000). + This value must be provided. Repeated parameters: -- @@ -25,11 +25,8 @@ class TableEpsilon(Dispersion): The interpolation in the given wavelength range. """ - single_params_template = { - "lbda": np.linspace(0, 3000, 1000), - "epsilon": np.ones(1000), - } - rep_params_template = {} + single_params_template = {"lbda": None, "epsilon": None} + rep_params_template: Dict[str, Any] = {} def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) @@ -50,8 +47,27 @@ def __init__(self, *args, **kwargs) -> None: kind="cubic", ) - def __add__(self, _: Union[int, float, "Dispersion"]) -> "DispersionSum": - raise NotImplementedError("Adding of tabular dispersions is not yet supported") + self.default_lbda_range = self.single_params.get("lbda") + + def __add__(self, other: Union[int, float, "Dispersion"]) -> "DispersionSum": + if isinstance(other, (int, float)): + return DispersionSum(self, EpsilonInf(eps=other)) + + if isinstance(other, TableEpsilon): + raise NotImplementedError( + "Adding of tabular dispersions is not yet supported" + ) + + if isinstance(other, Dispersion): + return DispersionSum(self, other) + + if isinstance(other, DispersionSum): + other.dispersions.append(self) + return other + + raise TypeError( + f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'" + ) def dielectric_function(self, lbda: npt.ArrayLike) -> npt.NDArray: return self.interpolation(lbda) diff --git a/src/elli/dispersions/table_index.py b/src/elli/dispersions/table_index.py index e6281c79..2eea0fe3 100644 --- a/src/elli/dispersions/table_index.py +++ b/src/elli/dispersions/table_index.py @@ -1,10 +1,12 @@ # Encoding: utf-8 """Dispersion specified by a table of wavelengths (nm) and refractive index values.""" -import numpy as np +from typing import Union import numpy.typing as npt import scipy.interpolate -from .base_dispersion import IndexDispersion, InvalidParameters +from elli.dispersions.constant_refractive_index import ConstantRefractiveIndex + +from .base_dispersion import IndexDispersion, IndexDispersionSum, InvalidParameters class Table(IndexDispersion): @@ -13,9 +15,9 @@ class Table(IndexDispersion): wavelength range. Single parameters: - :lbda (list): Wavelengths in nm. Defaults to np.linspace(0, 3000, 1000). + :lbda (list): Wavelengths in nm. This value must be provided. :n: Complex refractive index values in the convention n + ik. - Defaults to np.ones(1000). + This value must be provided. Repeated parameters: -- @@ -24,7 +26,7 @@ class Table(IndexDispersion): The interpolation in the given wavelength range. """ - single_params_template = {"lbda": np.linspace(0, 3000, 1000), "n": np.ones(1000)} + single_params_template = {"lbda": None, "n": None} rep_params_template = {} def __init__(self, *args, **kwargs) -> None: @@ -44,5 +46,29 @@ def __init__(self, *args, **kwargs) -> None: kind="cubic", ) + self.default_lbda_range = self.single_params.get("lbda") + + def __add__( + self, other: Union[int, float, "IndexDispersion"] + ) -> "IndexDispersionSum": + if isinstance(other, (int, float)): + return IndexDispersionSum(self, ConstantRefractiveIndex(eps=other)) + + if isinstance(other, Table): + raise NotImplementedError( + "Adding of tabular dispersions is not yet supported" + ) + + if isinstance(other, IndexDispersion): + return IndexDispersionSum(self, other) + + if isinstance(other, IndexDispersionSum): + other.dispersions.append(self) + return other + + raise TypeError( + f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'" + ) + def refractive_index(self, lbda: npt.ArrayLike) -> npt.NDArray: return self.interpolation(lbda) diff --git a/src/elli/formula_parser/__init__.py b/src/elli/formula_parser/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/elli/formula_parser/dispersion_function_grammar.lark b/src/elli/formula_parser/dispersion_function_grammar.lark new file mode 100644 index 00000000..7524723a --- /dev/null +++ b/src/elli/formula_parser/dispersion_function_grammar.lark @@ -0,0 +1,51 @@ +?assignment: "eps" "=" kkr_expression -> eps + | "n" "=" kkr_expression -> n + +?kkr_expression: expression + | "" "+" "1j" "*" term -> kkr_term + +?expression: term + | expression "+" term -> add + | expression "-" term -> sub + +?term: factor + | term "*" factor -> mul + | term "/" factor -> div + +?factor: power + | power "**" power -> power + + +?power: "(" expression ")" + | FUNC "(" expression ")" -> func + | "sum" "[" repeated_expression "]" -> sum_expr + | NAME -> single_param_name + | SIGNED_NUMBER -> number + | BUILTIN -> builtin + +?repeated_expression: repeated_term + | repeated_expression "+" repeated_term -> add + | repeated_expression "-" repeated_term -> sub + + +?repeated_term: repeated_factor + | repeated_term "*" repeated_factor -> mul + | repeated_term "/" repeated_factor -> div + +?repeated_factor: repeated_power + | repeated_power "**" repeated_power -> power + +?repeated_power: "(" repeated_expression ")" + | FUNC "(" repeated_expression ")" -> func + | SIGNED_NUMBER -> number + | NAME -> param_name + | BUILTIN -> builtin + +FUNC.1: "sin" | "cos" | "tan" | "sqrt" | "dawsn" | "ln" | "log" | "heaviside" +BUILTIN.1: "1j" | "pi" | "eps_0" | "hbar" | "h" | "c" + +%import common.CNAME -> NAME +%import common.SIGNED_NUMBER +%import common.WS_INLINE + +%ignore WS_INLINE \ No newline at end of file diff --git a/src/elli/formula_parser/parser.py b/src/elli/formula_parser/parser.py new file mode 100644 index 00000000..29d99a63 --- /dev/null +++ b/src/elli/formula_parser/parser.py @@ -0,0 +1,239 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""This modules creates a formula parser""" +import os +from operator import add, mul, neg, sub, truediv +from typing import Dict + +import numpy as np +import numpy.typing as npt +import scipy.constants as sc +from lark import Lark, Transformer, v_args +from scipy.special import dawsn # pylint: disable=no-name-in-module + + +@v_args(inline=True) +class FormulaTransformer(Transformer): + """Transformer class for parsing formulas""" + + single_params: Dict[str, float] + repeated_params: Dict[str, np.ndarray] + no_repeated_params: int + + number = float + add = add + sub = sub + mul = mul + div = truediv + neg = neg + power = pow + + def _check_and_set(self, repeated_params): + if not repeated_params: + self.no_repeated_params = 0 + return + + if not isinstance(repeated_params, dict): + raise ValueError( + f"Repeated parameters must be a dict but found {type(repeated_params)}" + ) + + params = iter(repeated_params.items()) + length = len(next(params)[1]) + for name, param in params: + if not isinstance(param, np.ndarray): + raise TypeError( + f"Expected {name} to be of type numpy ndarray but found type {type(param)}" + ) + if length != len(param): + raise ValueError("Repeated parameters must have all the same length.") + + self.no_repeated_params = length + + def _check_and_set_single(self, single_params): + for name, param in single_params.items(): + if not isinstance(param, (float, int)): + raise TypeError( + f"Expected {name} to be of type float but found type {type(param)}." + ) + self.single_params = single_params + + def __init__( + self, + x_axis_name: str, + x_axis_values: np.ndarray, + single_params: Dict[str, float], + repeated_params: Dict[str, np.ndarray], + ): + super().__init__() + if not isinstance(x_axis_name, str): + raise TypeError("x_axis_name must be a string.") + + if not isinstance(x_axis_values, np.ndarray): + raise TypeError("x_axis_values must be a numpy array.") + + self.x_axis_name = x_axis_name + self.x_axis_values = x_axis_values + + self._check_and_set(repeated_params) + self._check_and_set_single(single_params) + + self.repeated_params = repeated_params + self.single_params = single_params + + def eps(self, inp): + """Return an epsilon type formula""" + return "eps", inp + + # pylint: disable=invalid-name + def n(self, inp): + """Return an index type formula""" + return "n", inp + + def kkr_term(self, term): + """Calculate the kramers kronig transformation on the function""" + raise NotImplementedError("kkr transformation not yet implemented") + + def func(self, name, val): + """Evaluates a function""" + names = { + "sin": np.sin, + "cos": np.cos, + "tan": np.tan, + "sqrt": np.emath.sqrt, + "dawsn": dawsn, + "ln": np.log, + "log": np.log10, + "heaviside": np.heaviside, + } + + if name in names: + return names[name](val) + + raise ValueError(f"Unknown function: {name}") + + def builtin(self, name): + """Returns the values for builtin tokens""" + names = { + "1j": 1j, + "pi": sc.pi, + "eps_0": sc.epsilon_0, + "hbar": sc.hbar, + "h": sc.h, + "c": sc.c, + } + + if name in names: + return names[name] + + raise ValueError(f"Unknown constant: {name}") + + def sum_expr(self, expr): + """Sum an expression""" + return expr.sum(axis=1) + + def single_param_name(self, name): + """Return a parameter inside a non-repeated section""" + if name == self.x_axis_name: + return self.x_axis_values + + if name in self.single_params: + return self.single_params[name] + + raise ValueError(f"No such parameter {name}") + + def param_name(self, name): + """Return a parameter inside a repeated section""" + if name == self.x_axis_name: + return np.einsum( + "i,j->ij", self.x_axis_values, np.ones(self.no_repeated_params) + ) + + if name in self.single_params: + return self.single_params[name] + + if name in self.repeated_params: + return self.repeated_params[name] + + raise ValueError(f"No such parameter {name}") + + +def formula_parser(transformer=None): + """Creates a formula parser. + + Args: + transformer (lark.Transformer, optional): + A transformer to use with this parsing. + If set to None it generates the AST. + Defaults to None. + + Returns: + lark.Lark: The parsing object. + """ + __location__ = os.path.realpath( + os.path.join(os.getcwd(), os.path.dirname(__file__)) + ) + + with open( + f"{__location__}/dispersion_function_grammar.lark", encoding="utf-8" + ) as formula_grammar: + if transformer is None: + return Lark( + formula_grammar, + start="assignment", + parser="lalr", + ) + + return Lark( + formula_grammar, start="assignment", parser="lalr", transformer=transformer + ) + + +def transformation_formula_parser( + x_axis_name: str, + x_axis_values: npt.ArrayLike, + single_params: Dict[str, float], + repeated_params: Dict[str, np.ndarray], +): + """Creates a formula parser with the given parameters. + This parser is automatically evaluated based on the given parameters. + This is done directly whitout creating the abstract syntax tree. + + Args: + x_axis_name (str): The name of the x-axis to use replace in the formula string. + x_axis_values (np.ndarray): The value array of the x-axis to replace in the formula string. + single_params (Dict[str, float]): + A dictionary of parameter name and value of single parameters. + repeated_params (Dict[str, np.ndarray]): + A dictionary of parameter name and values of repeated paremters. + + Returns: + str, np.ndarray: + The evaluated formula. + The first tuple element indicates the type of dispersion + and may either be `n` for refractive index or `eps` for dielectric function. + The second element is the evluated formula one point for each x-axis input point. + """ + return formula_parser( + FormulaTransformer( + x_axis_name=x_axis_name, + x_axis_values=x_axis_values, + single_params=single_params, + repeated_params=repeated_params, + ) + ) diff --git a/src/elli/importer/nexus.py b/src/elli/importer/nexus.py index 136799ec..504fbd4d 100644 --- a/src/elli/importer/nexus.py +++ b/src/elli/importer/nexus.py @@ -8,7 +8,12 @@ import numpy as np import pandas as pd -from ..utils import calc_rho +from elli.dispersions.formula import Formula, FormulaIndex +from elli.dispersions.table_epsilon import TableEpsilon +from elli.dispersions.table_index import Table +from elli.materials import BiaxialMaterial, IsotropicMaterial, UniaxialMaterial + +from ..utils import calc_rho, conversion_wavelength_energy def read_nexus_psi_delta(nxs_filename: str) -> pd.DataFrame: @@ -63,3 +68,119 @@ def read_nexus_rho(nxs_filename: str) -> pd.DataFrame: and the wavelength as second column. """ return calc_rho(read_nexus_psi_delta(nxs_filename)) + + +def read_nexus_materials(filename: str): + """Read the optical materials from a nexus file. + + Args: + filename (str): The nexus filename. + """ + + def get_dispersion_function(dataset: h5py.Dataset): + single_params = {} + rep_params = {} + + def get_params(_: str, dataset: h5py.Dataset): + if dataset.attrs.get("NX_class", "") in ["NXdispersion_single_parameter"]: + param_name = dataset["name"][()].decode("utf-8") + single_params[param_name] = dataset["value"][()] + + if dataset.attrs.get("NX_class", "") in ["NXdispersion_repeated_parameter"]: + param_name = dataset["name"][()].decode("utf-8") + rep_params[param_name] = dataset["values"][()] + + identifier = None + if "wavelength_identifier" in dataset: + identifier = dataset["wavelength_identifier"][()].decode("utf-8") + unit = ( + f"{dataset['wavelength_unit'][()]} " + f"{dataset['wavelength_unit'].attrs.get('units', '')}" + ) + + if "energy_identifier" in dataset: + raise NotImplementedError( + "Using dispersions with an energy axis is not yet supported." + ) + + representation = dataset["representation"][()].decode("utf-8") + formula = dataset["formula"][()].decode("utf-8") + dataset.visititems(get_params) + + if representation == "eps": + return Formula(formula, identifier, single_params, rep_params, unit) + + if representation == "n": + return FormulaIndex(formula, identifier, single_params, rep_params, unit) + + raise ValueError(f"Unsupported representation {representation}") + + def get_dispersion_table(dataset: h5py.Dataset): + wavelength = None + if "wavelength" in dataset: + wavelength = dataset["wavelength"] + + if "energy" in dataset: + wavelength = conversion_wavelength_energy(dataset["energy"]) + + if wavelength is None: + raise ValueError("No wavelength array found in dataset.") + + if "refractive_index" in dataset: + return Table(lbda=wavelength, n=dataset["refractive_index"][()]) + + if "dielectric_function" in dataset: + return TableEpsilon( + lbda=wavelength, epsilon=dataset["dielectric_function"][()] + ) + + raise ValueError( + "Invalid dispersion table, neither `refractive_index` " + "nor `dielectric_function` found." + ) + + def get_dispersion(dataset: h5py.Dataset): + dispersion = None + for data in dataset.values(): + if data.attrs.get("NX_class") in ["NXdispersion_function"]: + dispersion = ( + get_dispersion_function(data) + if dispersion is None + else dispersion + get_dispersion_function(data) + ) + + if data.attrs.get("NX_class") in ["NXdispersion_table"]: + dispersion = ( + get_dispersion_table(data) + if dispersion is None + else dispersion + get_dispersion_table(data) + ) + + return dispersion + + def get_material(dataset: h5py.Dataset): + dispersions = {} + for dispersion in [f"dispersion_{axis}" for axis in ["x", "y", "z"]]: + if dispersion not in dataset: + if dispersion == "dispersion_x": + raise ValueError("Invalid dispersion file. `dispersion_x` missing") + continue + dispersions[dispersion] = get_dispersion(dataset[dispersion]) + + if len(dispersions) == 1: + return IsotropicMaterial(dispersions["dispersion_x"]) + if len(dispersions) == 2: + return UniaxialMaterial( + dispersions["dispersion_x"], dispersions["dispersion_z"] + ) + if len(dispersions) == 3: + return BiaxialMaterial(**dispersions) + + raise ValueError("Could not create material from nexus file.") + + entries = {} + with h5py.File(filename) as h5file: + for entry, data in h5file.items(): + entries[entry] = get_material(data) + + return entries diff --git a/src/elli/units.py b/src/elli/units.py new file mode 100644 index 00000000..b47b0079 --- /dev/null +++ b/src/elli/units.py @@ -0,0 +1,5 @@ +"""The pint unit registry for pyElli""" +from pint import UnitRegistry + +ureg = UnitRegistry() +ureg.enable_contexts("spectroscopy") diff --git a/tests/fixtures.py b/tests/fixtures.py new file mode 100644 index 00000000..125899e3 --- /dev/null +++ b/tests/fixtures.py @@ -0,0 +1,19 @@ +import os +from distutils import dir_util +from pytest import fixture + + +@fixture +def datadir(tmp_path, request): + """ + Fixture responsible for searching a folder with the same name of test + module and, if available, moving all contents to a temporary directory so + tests can use them freely. + """ + filename = request.module.__file__ + test_dir, _ = os.path.splitext(filename) + + if os.path.isdir(test_dir): + dir_util.copy_tree(test_dir, str(tmp_path)) + + return tmp_path diff --git a/tests/test_dispersion_adding.py b/tests/test_dispersion_adding.py index 172f7346..4740cf60 100644 --- a/tests/test_dispersion_adding.py +++ b/tests/test_dispersion_adding.py @@ -1,26 +1,63 @@ """Test adding of dispersions""" import pytest +import numpy as np from numpy.testing import assert_array_almost_equal from elli import Cauchy, Sellmeier -from elli.dispersions.base_dispersion import DispersionSum +from elli.dispersions.base_dispersion import DispersionSum, IndexDispersionSum from elli.dispersions.table_epsilon import TableEpsilon -def test_fail_on_adding_index_dispersion(): - """Test whether adding for an index based model fails""" - cauchy_err_str = "Adding of index based dispersions is not supported yet" - with pytest.raises(NotImplementedError) as sum_err: - _ = Cauchy() + Cauchy() +def test_adding_index_dispersion(): + """Test correct adding of index based dispersions""" + index_dispersion_sum = Cauchy() + Cauchy() - assert cauchy_err_str in str(sum_err.value) + assert_array_almost_equal( + index_dispersion_sum.get_dielectric(), + (Cauchy().get_refractive_index() * 2) ** 2, + ) def test_fail_on_adding_index_and_diel_dispersion(): """Test whether the adding fails for an index based and dielectric dispersion""" - for disp in [1, Sellmeier()]: - with pytest.raises(TypeError) as sum_err: - _ = disp + Cauchy() + with pytest.raises(TypeError) as sum_err: + _ = Sellmeier() + Cauchy() + + assert ( + "Cannot add refractive index and dielectric function based dispersions." + in str(sum_err.value) + ) + + +@pytest.mark.parametrize( + "dispersions", + [ + (DispersionSum(Sellmeier(), Sellmeier()), Cauchy()), + (IndexDispersionSum(Cauchy(), Cauchy()), Sellmeier()), + ( + DispersionSum(Sellmeier(), Sellmeier()), + IndexDispersionSum(Cauchy(), Cauchy()), + ), + (Sellmeier(), Cauchy()), + ], +) +def test_fail_on_adding_index_and_diel_dispersion_sum(dispersions): + """Adding of index based and diel based dispersion sums fails""" + + with pytest.raises(TypeError): + _ = dispersions[0] + dispersions[1] + + with pytest.raises(TypeError): + _ = dispersions[1] + dispersions[0] + + +def test_fail_on_adding_index_and_diel_dispersion_as_args(): + """ + The adding of dispersion fails for an index based and dielectric dispersion + when provided as args to DispersionSum + """ + with pytest.raises(TypeError) as sum_err: + DispersionSum(Sellmeier(), Cauchy()) assert ( "Cannot add refractive index and dielectric function based dispersions." @@ -62,11 +99,112 @@ def test_flat_dispersion_sum_on_multiple_add(): ) -def test_adding_of_tabular_dispersions(): - """Tests correct adding of tabular dispersions""" +def test_flat_dispersion_on_adding_with_dispersion_sum(): + """ + DispersionSum is kept flat even when a mixture of + Dispersions and DispersionSums are added + """ + dispersion_sum = ( + Sellmeier() + + DispersionSum(DispersionSum(Sellmeier(), Sellmeier()), Sellmeier()) + + Sellmeier() + ) + + assert isinstance(dispersion_sum, DispersionSum) + assert len(dispersion_sum.dispersions) == 5 + + for disp in dispersion_sum.dispersions: + assert isinstance(disp, Sellmeier) + + assert_array_almost_equal( + dispersion_sum.get_dielectric_df().values, + 5 * Sellmeier().get_dielectric_df().values, + ) + + +def test_multiple_dispersion_sum_args(): + """Multiple Dispersions can be provided via the DispersionSum args""" + dispersion_sum = DispersionSum(Sellmeier(), Sellmeier(), Sellmeier()) + + assert isinstance(dispersion_sum, DispersionSum) + assert len(dispersion_sum.dispersions) == 3 + + for disp in dispersion_sum.dispersions: + assert isinstance(disp, Sellmeier) + + assert_array_almost_equal( + dispersion_sum.get_dielectric_df().values, + 3 * Sellmeier().get_dielectric_df().values, + ) + + +def test_nested_dispersion_sum_args(): + """ + DispersionSum is kept flat even for nested + DispersionSum args + """ + dispersion_sum = DispersionSum( + DispersionSum(DispersionSum(Sellmeier(), Sellmeier()), Sellmeier()), Sellmeier() + ) + + assert isinstance(dispersion_sum, DispersionSum) + assert len(dispersion_sum.dispersions) == 4 + + for disp in dispersion_sum.dispersions: + assert isinstance(disp, Sellmeier) + + assert_array_almost_equal( + dispersion_sum.get_dielectric_df().values, + 4 * Sellmeier().get_dielectric_df().values, + ) + + +def test_flattening_of_dispersion_sum_args(): + """When a DispersionSum is passed via a DisperionSum arg it is flattened.""" + dispersion_sum = DispersionSum(Sellmeier(), DispersionSum(Sellmeier(), Sellmeier())) + + assert isinstance(dispersion_sum, DispersionSum) + assert len(dispersion_sum.dispersions) == 3 + + for disp in dispersion_sum.dispersions: + assert isinstance(disp, Sellmeier) + + assert_array_almost_equal( + dispersion_sum.get_dielectric_df().values, + 3 * Sellmeier().get_dielectric_df().values, + ) + + +def test_adding_of_float_to_tabular_epsilon_dispersion(): + """Tests correct adding of a float value to tabular dispersions""" + + table_eps = TableEpsilon(lbda=np.linspace(200, 1000, 801), epsilon=np.ones(801)) + 1 + + assert_array_almost_equal( + table_eps.get_dielectric(), + np.ones(801) * 2, + ) + + +def test_adding_of_dispersion_function_and_table(): + """Dispersion function and table can be added""" + + table = TableEpsilon(lbda=np.linspace(200, 1000, 801), epsilon=np.ones(801)) + sellmeier = Sellmeier().add(1, 1) + + assert_array_almost_equal( + (table + sellmeier).get_dielectric(), + table.get_dielectric() + sellmeier.get_dielectric(), + ) + + +def test_fail_on_adding_tabular_dispersions(): + """Adding two tabular dispersions should return an NotImplementedError""" with pytest.raises(NotImplementedError) as not_impl_err: - _ = TableEpsilon() + 1 + _ = TableEpsilon( + lbda=np.linspace(200, 1000, 801), epsilon=np.ones(801) + ) + TableEpsilon(lbda=np.linspace(200, 1000, 801), epsilon=np.ones(801)) assert ( str(not_impl_err.value) == "Adding of tabular dispersions is not yet supported" diff --git a/tests/test_dispersions.py b/tests/test_dispersions.py index 3ddb107c..dc36a643 100644 --- a/tests/test_dispersions.py +++ b/tests/test_dispersions.py @@ -59,8 +59,6 @@ def test_regression_dispersions_default(datadir): "Sellmeier", "Tanguy", "TaucLorentz", - "Table", - "TableEpsilon", ] lbda = np.linspace(400, 1000, 500) diff --git a/tests/test_dispersions/TableEpsilon_default.csv b/tests/test_dispersions/TableEpsilon_default.csv deleted file mode 100644 index 3a5a6f9f..00000000 --- a/tests/test_dispersions/TableEpsilon_default.csv +++ /dev/null @@ -1,501 +0,0 @@ -Wavelength,ϵ1,ϵ2 -400.0,0.9999999999999998,0.0 -401.2024048096192,1.0,0.0 -402.40480961923845,1.0000000000000002,0.0 -403.60721442885773,1.0000000000000002,0.0 -404.80961923847696,1.0000000000000002,0.0 -406.0120240480962,1.0000000000000002,0.0 -407.2144288577154,1.0000000000000002,0.0 -408.4168336673347,1.0000000000000004,0.0 -409.6192384769539,1.0000000000000002,0.0 -410.82164328657313,1.0000000000000002,0.0 -412.02404809619236,1.0,0.0 -413.22645290581164,1.0000000000000002,0.0 -414.42885771543087,1.0,0.0 -415.6312625250501,1.0000000000000002,0.0 -416.8336673346693,1.0000000000000002,0.0 -418.0360721442886,0.9999999999999999,0.0 -419.2384769539078,1.0,0.0 -420.44088176352705,1.0000000000000002,0.0 -421.64328657314627,1.0000000000000002,0.0 -422.84569138276555,1.0000000000000002,0.0 -424.0480961923848,1.0,0.0 -425.250501002004,1.0000000000000002,0.0 -426.4529058116232,1.0000000000000002,0.0 -427.6553106212425,1.0000000000000004,0.0 -428.85771543086173,1.0000000000000004,0.0 -430.06012024048096,1.0000000000000002,0.0 -431.2625250501002,1.0000000000000002,0.0 -432.46492985971946,1.0,0.0 -433.6673346693387,1.0,0.0 -434.8697394789579,1.0,0.0 -436.07214428857714,1.0,0.0 -437.2745490981964,1.0,0.0 -438.47695390781564,1.0,0.0 -439.67935871743487,1.0000000000000002,0.0 -440.8817635270541,1.0000000000000002,0.0 -442.0841683366734,1.0000000000000002,0.0 -443.2865731462926,1.0000000000000004,0.0 -444.4889779559118,1.0000000000000002,0.0 -445.69138276553105,1.0,0.0 -446.8937875751503,1.0,0.0 -448.09619238476955,1.0,0.0 -449.2985971943888,1.0,0.0 -450.501002004008,1.0000000000000002,0.0 -451.7034068136273,1.0000000000000002,0.0 -452.9058116232465,1.0000000000000002,0.0 -454.10821643286573,1.0000000000000002,0.0 -455.31062124248496,1.0000000000000002,0.0 -456.51302605210424,1.0,0.0 -457.71543086172346,1.0000000000000002,0.0 -458.9178356713427,1.0000000000000002,0.0 -460.1202404809619,1.0000000000000002,0.0 -461.32264529058114,1.0,0.0 -462.5250501002004,1.0000000000000002,0.0 -463.72745490981964,1.0000000000000002,0.0 -464.92985971943887,1.0000000000000002,0.0 -466.13226452905815,1.0,0.0 -467.3346693386774,1.0000000000000002,0.0 -468.5370741482966,1.0000000000000002,0.0 -469.7394789579158,1.0000000000000004,0.0 -470.94188376753505,1.0000000000000002,0.0 -472.1442885771543,1.0000000000000002,0.0 -473.34669338677355,1.0,0.0 -474.5490981963928,1.0000000000000002,0.0 -475.75150300601206,1.0000000000000002,0.0 -476.9539078156313,1.0,0.0 -478.1563126252505,1.0000000000000002,0.0 -479.35871743486973,1.0000000000000002,0.0 -480.56112224448896,1.0000000000000002,0.0 -481.76352705410824,1.0000000000000002,0.0 -482.96593186372746,1.0,0.0 -484.1683366733467,1.0,0.0 -485.37074148296597,0.9999999999999999,0.0 -486.5731462925852,1.0,0.0 -487.7755511022044,1.0000000000000002,0.0 -488.97795591182364,1.0,0.0 -490.18036072144287,1.0000000000000002,0.0 -491.38276553106215,1.0,0.0 -492.5851703406814,1.0,0.0 -493.7875751503006,1.0,0.0 -494.9899799599199,1.0000000000000004,0.0 -496.1923847695391,1.0,0.0 -497.3947895791583,1.0000000000000002,0.0 -498.59719438877755,1.0000000000000002,0.0 -499.7995991983968,1.0000000000000002,0.0 -501.00200400801606,1.0000000000000002,0.0 -502.2044088176353,1.0,0.0 -503.4068136272545,1.0,0.0 -504.6092184368738,1.0000000000000002,0.0 -505.811623246493,1.0000000000000002,0.0 -507.01402805611224,1.0000000000000002,0.0 -508.21643286573146,1.0000000000000002,0.0 -509.4188376753507,1.0,0.0 -510.62124248496997,1.0000000000000002,0.0 -511.8236472945892,0.9999999999999999,0.0 -513.0260521042085,1.0000000000000002,0.0 -514.2284569138277,1.0,0.0 -515.4308617234469,1.0,0.0 -516.6332665330661,0.9999999999999998,0.0 -517.8356713426854,0.9999999999999999,0.0 -519.0380761523046,1.0,0.0 -520.2404809619238,1.0,0.0 -521.442885771543,1.0,0.0 -522.6452905811623,0.9999999999999999,0.0 -523.8476953907816,1.0,0.0 -525.0501002004008,1.0000000000000002,0.0 -526.2525050100201,1.0000000000000002,0.0 -527.4549098196393,1.0000000000000002,0.0 -528.6573146292585,1.0,0.0 -529.8597194388777,0.9999999999999999,0.0 -531.062124248497,1.0000000000000002,0.0 -532.2645290581163,0.9999999999999999,0.0 -533.4669338677355,0.9999999999999998,0.0 -534.6693386773547,1.0,0.0 -535.871743486974,1.0,0.0 -537.0741482965932,1.0,0.0 -538.2765531062124,0.9999999999999998,0.0 -539.4789579158316,1.0000000000000002,0.0 -540.681362725451,1.0000000000000002,0.0 -541.8837675350701,1.0,0.0 -543.0861723446894,1.0,0.0 -544.2885771543087,1.0000000000000002,0.0 -545.4909819639279,0.9999999999999999,0.0 -546.6933867735471,0.9999999999999997,0.0 -547.8957915831663,1.0,0.0 -549.0981963927856,0.9999999999999998,0.0 -550.3006012024048,1.0,0.0 -551.5030060120241,1.0,0.0 -552.7054108216433,1.0,0.0 -553.9078156312626,0.9999999999999999,0.0 -555.1102204408818,1.0,0.0 -556.312625250501,0.9999999999999999,0.0 -557.5150300601202,1.0000000000000002,0.0 -558.7174348697395,1.0,0.0 -559.9198396793587,1.0000000000000002,0.0 -561.1222444889779,0.9999999999999998,0.0 -562.3246492985973,1.0000000000000002,0.0 -563.5270541082165,1.0,0.0 -564.7294589178357,1.0000000000000002,0.0 -565.9318637274549,1.0,0.0 -567.1342685370741,1.0,0.0 -568.3366733466934,1.0,0.0 -569.5390781563126,0.9999999999999998,0.0 -570.7414829659319,1.0000000000000002,0.0 -571.943887775551,1.0,0.0 -573.1462925851704,1.0,0.0 -574.3486973947896,1.0000000000000004,0.0 -575.5511022044088,1.0,0.0 -576.7535070140281,1.0000000000000004,0.0 -577.9559118236473,1.0,0.0 -579.1583166332665,1.0000000000000002,0.0 -580.3607214428857,0.9999999999999999,0.0 -581.5631262525051,0.9999999999999999,0.0 -582.7655310621243,0.9999999999999999,0.0 -583.9679358717435,1.0,0.0 -585.1703406813627,1.0,0.0 -586.372745490982,0.9999999999999999,0.0 -587.5751503006012,1.0000000000000002,0.0 -588.7775551102204,1.0,0.0 -589.9799599198398,1.0000000000000002,0.0 -591.1823647294589,1.0,0.0 -592.3847695390782,1.0,0.0 -593.5871743486974,1.0,0.0 -594.7895791583167,0.9999999999999999,0.0 -595.9919839679359,0.9999999999999999,0.0 -597.1943887775551,1.0,0.0 -598.3967935871743,1.0,0.0 -599.5991983967936,1.0,0.0 -600.8016032064129,1.0000000000000002,0.0 -602.0040080160321,0.9999999999999999,0.0 -603.2064128256513,1.0,0.0 -604.4088176352706,1.0,0.0 -605.6112224448898,1.0,0.0 -606.813627254509,0.9999999999999998,0.0 -608.0160320641282,0.9999999999999999,0.0 -609.2184368737476,1.0,0.0 -610.4208416833667,0.9999999999999999,0.0 -611.623246492986,1.0,0.0 -612.8256513026053,1.0,0.0 -614.0280561122245,1.0,0.0 -615.2304609218437,0.9999999999999998,0.0 -616.4328657314629,1.0000000000000004,0.0 -617.6352705410821,0.9999999999999998,0.0 -618.8376753507014,0.9999999999999997,0.0 -620.0400801603207,0.9999999999999999,0.0 -621.2424849699399,1.0,0.0 -622.4448897795592,1.0000000000000002,0.0 -623.6472945891784,1.0,0.0 -624.8496993987976,1.0,0.0 -626.0521042084168,1.0000000000000002,0.0 -627.2545090180361,1.0000000000000002,0.0 -628.4569138276554,1.0000000000000002,0.0 -629.6593186372745,1.0,0.0 -630.8617234468938,1.0000000000000002,0.0 -632.0641282565131,0.9999999999999999,0.0 -633.2665330661323,0.9999999999999999,0.0 -634.4689378757515,1.0,0.0 -635.6713426853707,1.0,0.0 -636.87374749499,1.0,0.0 -638.0761523046092,1.0000000000000002,0.0 -639.2785571142285,1.0,0.0 -640.4809619238476,1.0000000000000002,0.0 -641.683366733467,1.0000000000000002,0.0 -642.8857715430862,1.0000000000000002,0.0 -644.0881763527054,1.0000000000000002,0.0 -645.2905811623247,0.9999999999999999,0.0 -646.4929859719439,0.9999999999999999,0.0 -647.6953907815631,1.0,0.0 -648.8977955911823,1.0,0.0 -650.1002004008017,1.0,0.0 -651.3026052104209,1.0,0.0 -652.5050100200401,1.0000000000000002,0.0 -653.7074148296593,1.0000000000000002,0.0 -654.9098196392786,0.9999999999999998,0.0 -656.1122244488978,0.9999999999999998,0.0 -657.314629258517,1.0,0.0 -658.5170340681364,1.0,0.0 -659.7194388777555,1.0,0.0 -660.9218436873748,1.0,0.0 -662.124248496994,1.0000000000000002,0.0 -663.3266533066133,1.0,0.0 -664.5290581162325,1.0000000000000002,0.0 -665.7314629258517,1.0000000000000002,0.0 -666.933867735471,1.0000000000000002,0.0 -668.1362725450902,1.0,0.0 -669.3386773547095,1.0,0.0 -670.5410821643286,1.0,0.0 -671.7434869739479,0.9999999999999998,0.0 -672.9458917835672,1.0,0.0 -674.1482965931864,1.0,0.0 -675.3507014028056,1.0000000000000002,0.0 -676.5531062124248,1.0,0.0 -677.7555110220442,1.0000000000000002,0.0 -678.9579158316633,1.0,0.0 -680.1603206412826,1.0,0.0 -681.3627254509018,1.0000000000000002,0.0 -682.5651302605211,1.0,0.0 -683.7675350701403,1.0000000000000002,0.0 -684.9699398797595,1.0000000000000002,0.0 -686.1723446893789,1.0,0.0 -687.374749498998,0.9999999999999999,0.0 -688.5771543086173,0.9999999999999999,0.0 -689.7795591182364,1.0,0.0 -690.9819639278558,1.0,0.0 -692.184368737475,1.0,0.0 -693.3867735470942,1.0000000000000002,0.0 -694.5891783567134,1.0,0.0 -695.7915831663327,0.9999999999999999,0.0 -696.993987975952,1.0,0.0 -698.1963927855711,1.0,0.0 -699.3987975951904,0.9999999999999999,0.0 -700.6012024048097,1.0000000000000002,0.0 -701.8036072144289,1.0000000000000002,0.0 -703.0060120240481,1.0,0.0 -704.2084168336673,1.0,0.0 -705.4108216432867,1.0000000000000002,0.0 -706.6132264529058,1.0000000000000002,0.0 -707.8156312625251,0.9999999999999999,0.0 -709.0180360721442,1.0000000000000002,0.0 -710.2204408817636,0.9999999999999999,0.0 -711.4228456913828,1.0,0.0 -712.625250501002,1.0,0.0 -713.8276553106213,1.0,0.0 -715.0300601202405,1.0,0.0 -716.2324649298598,1.0000000000000002,0.0 -717.4348697394789,1.0,0.0 -718.6372745490983,1.0,0.0 -719.8396793587174,0.9999999999999997,0.0 -721.0420841683367,0.9999999999999997,0.0 -722.2444889779559,1.0,0.0 -723.4468937875752,1.0,0.0 -724.6492985971944,1.0,0.0 -725.8517034068136,1.0,0.0 -727.054108216433,1.0,0.0 -728.2565130260521,1.0000000000000002,0.0 -729.4589178356714,1.0,0.0 -730.6613226452906,1.0000000000000002,0.0 -731.8637274549098,1.0,0.0 -733.0661322645291,0.9999999999999998,0.0 -734.2685370741483,0.9999999999999999,0.0 -735.4709418837676,1.0,0.0 -736.6733466933867,0.9999999999999999,0.0 -737.8757515030061,1.0000000000000002,0.0 -739.0781563126252,1.0000000000000002,0.0 -740.2805611222445,1.0,0.0 -741.4829659318638,1.0,0.0 -742.685370741483,0.9999999999999998,0.0 -743.8877755511022,1.0,0.0 -745.0901803607214,1.0,0.0 -746.2925851703408,1.0,0.0 -747.4949899799599,1.0000000000000002,0.0 -748.6973947895792,1.0,0.0 -749.8997995991984,0.9999999999999999,0.0 -751.1022044088177,1.0000000000000002,0.0 -752.3046092184369,1.0,0.0 -753.5070140280561,1.0,0.0 -754.7094188376755,1.0000000000000002,0.0 -755.9118236472946,1.0,0.0 -757.1142284569139,0.9999999999999998,0.0 -758.316633266533,1.0,0.0 -759.5190380761524,1.0,0.0 -760.7214428857716,0.9999999999999999,0.0 -761.9238476953908,1.0,0.0 -763.12625250501,1.0000000000000002,0.0 -764.3286573146293,1.0000000000000002,0.0 -765.5310621242486,1.0,0.0 -766.7334669338677,1.0,0.0 -767.935871743487,1.0000000000000002,0.0 -769.1382765531063,1.0000000000000002,0.0 -770.3406813627255,1.0,0.0 -771.5430861723447,1.0,0.0 -772.7454909819639,1.0,0.0 -773.9478957915833,1.0,0.0 -775.1503006012024,1.0000000000000002,0.0 -776.3527054108217,1.0000000000000002,0.0 -777.5551102204408,1.0,0.0 -778.7575150300602,1.0000000000000002,0.0 -779.9599198396794,1.0,0.0 -781.1623246492986,1.0000000000000002,0.0 -782.3647294589179,1.0000000000000002,0.0 -783.5671342685371,1.0000000000000002,0.0 -784.7695390781564,1.0000000000000002,0.0 -785.9719438877755,1.0000000000000002,0.0 -787.1743486973949,1.0000000000000002,0.0 -788.376753507014,1.0,0.0 -789.5791583166333,0.9999999999999999,0.0 -790.7815631262525,1.0000000000000002,0.0 -791.9839679358718,1.0,0.0 -793.186372745491,1.0000000000000002,0.0 -794.3887775551102,1.0000000000000002,0.0 -795.5911823647295,0.9999999999999999,0.0 -796.7935871743487,0.9999999999999999,0.0 -797.995991983968,0.9999999999999998,0.0 -799.1983967935872,1.0,0.0 -800.4008016032064,1.0,0.0 -801.6032064128257,1.0,0.0 -802.8056112224449,1.0000000000000002,0.0 -804.0080160320642,1.0,0.0 -805.2104208416833,1.0000000000000002,0.0 -806.4128256513027,1.0,0.0 -807.6152304609218,1.0,0.0 -808.8176352705411,1.0,0.0 -810.0200400801604,1.0,0.0 -811.2224448897796,0.9999999999999999,0.0 -812.4248496993988,0.9999999999999999,0.0 -813.627254509018,0.9999999999999999,0.0 -814.8296593186374,1.0,0.0 -816.0320641282565,1.0,0.0 -817.2344689378758,1.0,0.0 -818.436873747495,1.0000000000000002,0.0 -819.6392785571143,1.0,0.0 -820.8416833667335,1.0000000000000002,0.0 -822.0440881763527,1.0000000000000002,0.0 -823.246492985972,1.0000000000000002,0.0 -824.4488977955912,1.0,0.0 -825.6513026052105,1.0000000000000002,0.0 -826.8537074148296,0.9999999999999999,0.0 -828.056112224449,1.0000000000000002,0.0 -829.2585170340682,1.0,0.0 -830.4609218436874,0.9999999999999999,0.0 -831.6633266533066,1.0,0.0 -832.8657314629259,1.0000000000000002,0.0 -834.0681362725452,1.0,0.0 -835.2705410821643,1.0,0.0 -836.4729458917836,1.0,0.0 -837.6753507014029,1.0,0.0 -838.8777555110221,0.9999999999999999,0.0 -840.0801603206413,1.0000000000000002,0.0 -841.2825651302605,1.0,0.0 -842.4849699398799,1.0,0.0 -843.687374749499,1.0,0.0 -844.8897795591183,1.0000000000000002,0.0 -846.0921843687374,1.0000000000000002,0.0 -847.2945891783568,1.0000000000000002,0.0 -848.496993987976,1.0,0.0 -849.6993987975952,1.0,0.0 -850.9018036072144,0.9999999999999999,0.0 -852.1042084168337,0.9999999999999999,0.0 -853.306613226453,1.0000000000000002,0.0 -854.5090180360721,1.0,0.0 -855.7114228456915,1.0,0.0 -856.9138276553107,1.0,0.0 -858.1162324649299,1.0,0.0 -859.3186372745491,0.9999999999999999,0.0 -860.5210420841684,1.0,0.0 -861.7234468937876,1.0,0.0 -862.9258517034068,1.0000000000000002,0.0 -864.1282565130261,1.0000000000000002,0.0 -865.3306613226453,1.0,0.0 -866.5330661322646,1.0,0.0 -867.7354709418838,1.0,0.0 -868.937875751503,0.9999999999999999,0.0 -870.1402805611223,1.0,0.0 -871.3426853707415,1.0000000000000002,0.0 -872.5450901803608,1.0000000000000002,0.0 -873.7474949899799,0.9999999999999999,0.0 -874.9498997995993,0.9999999999999999,0.0 -876.1523046092184,1.0,0.0 -877.3547094188377,1.0,0.0 -878.557114228457,0.9999999999999999,0.0 -879.7595190380762,1.0,0.0 -880.9619238476954,1.0000000000000002,0.0 -882.1643286573146,1.0,0.0 -883.366733466934,1.0,0.0 -884.5691382765531,1.0,0.0 -885.7715430861724,1.0,0.0 -886.9739478957916,1.0,0.0 -888.1763527054109,0.9999999999999999,0.0 -889.3787575150301,1.0,0.0 -890.5811623246493,1.0,0.0 -891.7835671342687,0.9999999999999998,0.0 -892.9859719438878,1.0,0.0 -894.1883767535071,1.0000000000000002,0.0 -895.3907815631262,1.0000000000000002,0.0 -896.5931863727455,1.0000000000000002,0.0 -897.7955911823648,0.9999999999999999,0.0 -898.997995991984,1.0,0.0 -900.2004008016032,1.0000000000000002,0.0 -901.4028056112224,0.9999999999999999,0.0 -902.6052104208418,1.0,0.0 -903.8076152304609,1.0,0.0 -905.0100200400802,1.0,0.0 -906.2124248496995,1.0000000000000002,0.0 -907.4148296593187,1.0,0.0 -908.6172344689379,1.0,0.0 -909.8196392785571,1.0000000000000002,0.0 -911.0220440881765,1.0,0.0 -912.2244488977956,1.0,0.0 -913.4268537074149,1.0,0.0 -914.6292585170341,0.9999999999999999,0.0 -915.8316633266534,1.0,0.0 -917.0340681362726,1.0,0.0 -918.2364729458918,1.0,0.0 -919.438877755511,1.0,0.0 -920.6412825651303,1.0000000000000004,0.0 -921.8436873747495,0.9999999999999999,0.0 -923.0460921843688,0.9999999999999999,0.0 -924.248496993988,1.0,0.0 -925.4509018036073,0.9999999999999999,0.0 -926.6533066132265,1.0,0.0 -927.8557114228457,0.9999999999999999,0.0 -929.058116232465,0.9999999999999999,0.0 -930.2605210420842,0.9999999999999998,0.0 -931.4629258517034,0.9999999999999998,0.0 -932.6653306613227,1.0,0.0 -933.867735470942,0.9999999999999999,0.0 -935.0701402805612,1.0,0.0 -936.2725450901804,1.0,0.0 -937.4749498997996,0.9999999999999999,0.0 -938.6773547094189,0.9999999999999997,0.0 -939.8797595190381,1.0,0.0 -941.0821643286573,0.9999999999999999,0.0 -942.2845691382765,1.0,0.0 -943.4869739478959,0.9999999999999999,0.0 -944.6893787575151,1.0000000000000002,0.0 -945.8917835671343,1.0000000000000002,0.0 -947.0941883767536,1.0,0.0 -948.2965931863728,1.0,0.0 -949.498997995992,1.0000000000000004,0.0 -950.7014028056112,1.0000000000000002,0.0 -951.9038076152304,1.0,0.0 -953.1062124248498,0.9999999999999999,0.0 -954.308617234469,1.0,0.0 -955.5110220440882,1.0,0.0 -956.7134268537075,1.0,0.0 -957.9158316633267,1.0,0.0 -959.1182364729459,1.0,0.0 -960.3206412825651,1.0000000000000002,0.0 -961.5230460921844,1.0,0.0 -962.7254509018037,1.0,0.0 -963.9278557114229,0.9999999999999999,0.0 -965.1302605210421,1.0,0.0 -966.3326653306614,1.0,0.0 -967.5350701402806,1.0,0.0 -968.7374749498998,1.0000000000000002,0.0 -969.939879759519,1.0,0.0 -971.1422845691383,1.0,0.0 -972.3446893787576,1.0000000000000002,0.0 -973.5470941883768,1.0000000000000002,0.0 -974.749498997996,1.0000000000000002,0.0 -975.9519038076153,1.0,0.0 -977.1543086172345,0.9999999999999998,0.0 -978.3567134268537,0.9999999999999999,0.0 -979.559118236473,1.0000000000000002,0.0 -980.7615230460922,1.0000000000000002,0.0 -981.9639278557115,1.0,0.0 -983.1663326653307,1.0,0.0 -984.36873747495,1.0,0.0 -985.5711422845692,1.0,0.0 -986.7735470941884,1.0000000000000002,0.0 -987.9759519038076,1.0,0.0 -989.1783567134269,1.0000000000000002,0.0 -990.3807615230461,1.0000000000000002,0.0 -991.5831663326654,0.9999999999999999,0.0 -992.7855711422847,0.9999999999999998,0.0 -993.9879759519039,1.0,0.0 -995.1903807615231,1.0,0.0 -996.3927855711423,1.0000000000000002,0.0 -997.5951903807616,1.0000000000000002,0.0 -998.7975951903808,1.0000000000000002,0.0 -1000.0,0.9999999999999999,0.0 diff --git a/tests/test_dispersions/Table_default.csv b/tests/test_dispersions/Table_default.csv deleted file mode 100644 index 3a5a6f9f..00000000 --- a/tests/test_dispersions/Table_default.csv +++ /dev/null @@ -1,501 +0,0 @@ -Wavelength,ϵ1,ϵ2 -400.0,0.9999999999999998,0.0 -401.2024048096192,1.0,0.0 -402.40480961923845,1.0000000000000002,0.0 -403.60721442885773,1.0000000000000002,0.0 -404.80961923847696,1.0000000000000002,0.0 -406.0120240480962,1.0000000000000002,0.0 -407.2144288577154,1.0000000000000002,0.0 -408.4168336673347,1.0000000000000004,0.0 -409.6192384769539,1.0000000000000002,0.0 -410.82164328657313,1.0000000000000002,0.0 -412.02404809619236,1.0,0.0 -413.22645290581164,1.0000000000000002,0.0 -414.42885771543087,1.0,0.0 -415.6312625250501,1.0000000000000002,0.0 -416.8336673346693,1.0000000000000002,0.0 -418.0360721442886,0.9999999999999999,0.0 -419.2384769539078,1.0,0.0 -420.44088176352705,1.0000000000000002,0.0 -421.64328657314627,1.0000000000000002,0.0 -422.84569138276555,1.0000000000000002,0.0 -424.0480961923848,1.0,0.0 -425.250501002004,1.0000000000000002,0.0 -426.4529058116232,1.0000000000000002,0.0 -427.6553106212425,1.0000000000000004,0.0 -428.85771543086173,1.0000000000000004,0.0 -430.06012024048096,1.0000000000000002,0.0 -431.2625250501002,1.0000000000000002,0.0 -432.46492985971946,1.0,0.0 -433.6673346693387,1.0,0.0 -434.8697394789579,1.0,0.0 -436.07214428857714,1.0,0.0 -437.2745490981964,1.0,0.0 -438.47695390781564,1.0,0.0 -439.67935871743487,1.0000000000000002,0.0 -440.8817635270541,1.0000000000000002,0.0 -442.0841683366734,1.0000000000000002,0.0 -443.2865731462926,1.0000000000000004,0.0 -444.4889779559118,1.0000000000000002,0.0 -445.69138276553105,1.0,0.0 -446.8937875751503,1.0,0.0 -448.09619238476955,1.0,0.0 -449.2985971943888,1.0,0.0 -450.501002004008,1.0000000000000002,0.0 -451.7034068136273,1.0000000000000002,0.0 -452.9058116232465,1.0000000000000002,0.0 -454.10821643286573,1.0000000000000002,0.0 -455.31062124248496,1.0000000000000002,0.0 -456.51302605210424,1.0,0.0 -457.71543086172346,1.0000000000000002,0.0 -458.9178356713427,1.0000000000000002,0.0 -460.1202404809619,1.0000000000000002,0.0 -461.32264529058114,1.0,0.0 -462.5250501002004,1.0000000000000002,0.0 -463.72745490981964,1.0000000000000002,0.0 -464.92985971943887,1.0000000000000002,0.0 -466.13226452905815,1.0,0.0 -467.3346693386774,1.0000000000000002,0.0 -468.5370741482966,1.0000000000000002,0.0 -469.7394789579158,1.0000000000000004,0.0 -470.94188376753505,1.0000000000000002,0.0 -472.1442885771543,1.0000000000000002,0.0 -473.34669338677355,1.0,0.0 -474.5490981963928,1.0000000000000002,0.0 -475.75150300601206,1.0000000000000002,0.0 -476.9539078156313,1.0,0.0 -478.1563126252505,1.0000000000000002,0.0 -479.35871743486973,1.0000000000000002,0.0 -480.56112224448896,1.0000000000000002,0.0 -481.76352705410824,1.0000000000000002,0.0 -482.96593186372746,1.0,0.0 -484.1683366733467,1.0,0.0 -485.37074148296597,0.9999999999999999,0.0 -486.5731462925852,1.0,0.0 -487.7755511022044,1.0000000000000002,0.0 -488.97795591182364,1.0,0.0 -490.18036072144287,1.0000000000000002,0.0 -491.38276553106215,1.0,0.0 -492.5851703406814,1.0,0.0 -493.7875751503006,1.0,0.0 -494.9899799599199,1.0000000000000004,0.0 -496.1923847695391,1.0,0.0 -497.3947895791583,1.0000000000000002,0.0 -498.59719438877755,1.0000000000000002,0.0 -499.7995991983968,1.0000000000000002,0.0 -501.00200400801606,1.0000000000000002,0.0 -502.2044088176353,1.0,0.0 -503.4068136272545,1.0,0.0 -504.6092184368738,1.0000000000000002,0.0 -505.811623246493,1.0000000000000002,0.0 -507.01402805611224,1.0000000000000002,0.0 -508.21643286573146,1.0000000000000002,0.0 -509.4188376753507,1.0,0.0 -510.62124248496997,1.0000000000000002,0.0 -511.8236472945892,0.9999999999999999,0.0 -513.0260521042085,1.0000000000000002,0.0 -514.2284569138277,1.0,0.0 -515.4308617234469,1.0,0.0 -516.6332665330661,0.9999999999999998,0.0 -517.8356713426854,0.9999999999999999,0.0 -519.0380761523046,1.0,0.0 -520.2404809619238,1.0,0.0 -521.442885771543,1.0,0.0 -522.6452905811623,0.9999999999999999,0.0 -523.8476953907816,1.0,0.0 -525.0501002004008,1.0000000000000002,0.0 -526.2525050100201,1.0000000000000002,0.0 -527.4549098196393,1.0000000000000002,0.0 -528.6573146292585,1.0,0.0 -529.8597194388777,0.9999999999999999,0.0 -531.062124248497,1.0000000000000002,0.0 -532.2645290581163,0.9999999999999999,0.0 -533.4669338677355,0.9999999999999998,0.0 -534.6693386773547,1.0,0.0 -535.871743486974,1.0,0.0 -537.0741482965932,1.0,0.0 -538.2765531062124,0.9999999999999998,0.0 -539.4789579158316,1.0000000000000002,0.0 -540.681362725451,1.0000000000000002,0.0 -541.8837675350701,1.0,0.0 -543.0861723446894,1.0,0.0 -544.2885771543087,1.0000000000000002,0.0 -545.4909819639279,0.9999999999999999,0.0 -546.6933867735471,0.9999999999999997,0.0 -547.8957915831663,1.0,0.0 -549.0981963927856,0.9999999999999998,0.0 -550.3006012024048,1.0,0.0 -551.5030060120241,1.0,0.0 -552.7054108216433,1.0,0.0 -553.9078156312626,0.9999999999999999,0.0 -555.1102204408818,1.0,0.0 -556.312625250501,0.9999999999999999,0.0 -557.5150300601202,1.0000000000000002,0.0 -558.7174348697395,1.0,0.0 -559.9198396793587,1.0000000000000002,0.0 -561.1222444889779,0.9999999999999998,0.0 -562.3246492985973,1.0000000000000002,0.0 -563.5270541082165,1.0,0.0 -564.7294589178357,1.0000000000000002,0.0 -565.9318637274549,1.0,0.0 -567.1342685370741,1.0,0.0 -568.3366733466934,1.0,0.0 -569.5390781563126,0.9999999999999998,0.0 -570.7414829659319,1.0000000000000002,0.0 -571.943887775551,1.0,0.0 -573.1462925851704,1.0,0.0 -574.3486973947896,1.0000000000000004,0.0 -575.5511022044088,1.0,0.0 -576.7535070140281,1.0000000000000004,0.0 -577.9559118236473,1.0,0.0 -579.1583166332665,1.0000000000000002,0.0 -580.3607214428857,0.9999999999999999,0.0 -581.5631262525051,0.9999999999999999,0.0 -582.7655310621243,0.9999999999999999,0.0 -583.9679358717435,1.0,0.0 -585.1703406813627,1.0,0.0 -586.372745490982,0.9999999999999999,0.0 -587.5751503006012,1.0000000000000002,0.0 -588.7775551102204,1.0,0.0 -589.9799599198398,1.0000000000000002,0.0 -591.1823647294589,1.0,0.0 -592.3847695390782,1.0,0.0 -593.5871743486974,1.0,0.0 -594.7895791583167,0.9999999999999999,0.0 -595.9919839679359,0.9999999999999999,0.0 -597.1943887775551,1.0,0.0 -598.3967935871743,1.0,0.0 -599.5991983967936,1.0,0.0 -600.8016032064129,1.0000000000000002,0.0 -602.0040080160321,0.9999999999999999,0.0 -603.2064128256513,1.0,0.0 -604.4088176352706,1.0,0.0 -605.6112224448898,1.0,0.0 -606.813627254509,0.9999999999999998,0.0 -608.0160320641282,0.9999999999999999,0.0 -609.2184368737476,1.0,0.0 -610.4208416833667,0.9999999999999999,0.0 -611.623246492986,1.0,0.0 -612.8256513026053,1.0,0.0 -614.0280561122245,1.0,0.0 -615.2304609218437,0.9999999999999998,0.0 -616.4328657314629,1.0000000000000004,0.0 -617.6352705410821,0.9999999999999998,0.0 -618.8376753507014,0.9999999999999997,0.0 -620.0400801603207,0.9999999999999999,0.0 -621.2424849699399,1.0,0.0 -622.4448897795592,1.0000000000000002,0.0 -623.6472945891784,1.0,0.0 -624.8496993987976,1.0,0.0 -626.0521042084168,1.0000000000000002,0.0 -627.2545090180361,1.0000000000000002,0.0 -628.4569138276554,1.0000000000000002,0.0 -629.6593186372745,1.0,0.0 -630.8617234468938,1.0000000000000002,0.0 -632.0641282565131,0.9999999999999999,0.0 -633.2665330661323,0.9999999999999999,0.0 -634.4689378757515,1.0,0.0 -635.6713426853707,1.0,0.0 -636.87374749499,1.0,0.0 -638.0761523046092,1.0000000000000002,0.0 -639.2785571142285,1.0,0.0 -640.4809619238476,1.0000000000000002,0.0 -641.683366733467,1.0000000000000002,0.0 -642.8857715430862,1.0000000000000002,0.0 -644.0881763527054,1.0000000000000002,0.0 -645.2905811623247,0.9999999999999999,0.0 -646.4929859719439,0.9999999999999999,0.0 -647.6953907815631,1.0,0.0 -648.8977955911823,1.0,0.0 -650.1002004008017,1.0,0.0 -651.3026052104209,1.0,0.0 -652.5050100200401,1.0000000000000002,0.0 -653.7074148296593,1.0000000000000002,0.0 -654.9098196392786,0.9999999999999998,0.0 -656.1122244488978,0.9999999999999998,0.0 -657.314629258517,1.0,0.0 -658.5170340681364,1.0,0.0 -659.7194388777555,1.0,0.0 -660.9218436873748,1.0,0.0 -662.124248496994,1.0000000000000002,0.0 -663.3266533066133,1.0,0.0 -664.5290581162325,1.0000000000000002,0.0 -665.7314629258517,1.0000000000000002,0.0 -666.933867735471,1.0000000000000002,0.0 -668.1362725450902,1.0,0.0 -669.3386773547095,1.0,0.0 -670.5410821643286,1.0,0.0 -671.7434869739479,0.9999999999999998,0.0 -672.9458917835672,1.0,0.0 -674.1482965931864,1.0,0.0 -675.3507014028056,1.0000000000000002,0.0 -676.5531062124248,1.0,0.0 -677.7555110220442,1.0000000000000002,0.0 -678.9579158316633,1.0,0.0 -680.1603206412826,1.0,0.0 -681.3627254509018,1.0000000000000002,0.0 -682.5651302605211,1.0,0.0 -683.7675350701403,1.0000000000000002,0.0 -684.9699398797595,1.0000000000000002,0.0 -686.1723446893789,1.0,0.0 -687.374749498998,0.9999999999999999,0.0 -688.5771543086173,0.9999999999999999,0.0 -689.7795591182364,1.0,0.0 -690.9819639278558,1.0,0.0 -692.184368737475,1.0,0.0 -693.3867735470942,1.0000000000000002,0.0 -694.5891783567134,1.0,0.0 -695.7915831663327,0.9999999999999999,0.0 -696.993987975952,1.0,0.0 -698.1963927855711,1.0,0.0 -699.3987975951904,0.9999999999999999,0.0 -700.6012024048097,1.0000000000000002,0.0 -701.8036072144289,1.0000000000000002,0.0 -703.0060120240481,1.0,0.0 -704.2084168336673,1.0,0.0 -705.4108216432867,1.0000000000000002,0.0 -706.6132264529058,1.0000000000000002,0.0 -707.8156312625251,0.9999999999999999,0.0 -709.0180360721442,1.0000000000000002,0.0 -710.2204408817636,0.9999999999999999,0.0 -711.4228456913828,1.0,0.0 -712.625250501002,1.0,0.0 -713.8276553106213,1.0,0.0 -715.0300601202405,1.0,0.0 -716.2324649298598,1.0000000000000002,0.0 -717.4348697394789,1.0,0.0 -718.6372745490983,1.0,0.0 -719.8396793587174,0.9999999999999997,0.0 -721.0420841683367,0.9999999999999997,0.0 -722.2444889779559,1.0,0.0 -723.4468937875752,1.0,0.0 -724.6492985971944,1.0,0.0 -725.8517034068136,1.0,0.0 -727.054108216433,1.0,0.0 -728.2565130260521,1.0000000000000002,0.0 -729.4589178356714,1.0,0.0 -730.6613226452906,1.0000000000000002,0.0 -731.8637274549098,1.0,0.0 -733.0661322645291,0.9999999999999998,0.0 -734.2685370741483,0.9999999999999999,0.0 -735.4709418837676,1.0,0.0 -736.6733466933867,0.9999999999999999,0.0 -737.8757515030061,1.0000000000000002,0.0 -739.0781563126252,1.0000000000000002,0.0 -740.2805611222445,1.0,0.0 -741.4829659318638,1.0,0.0 -742.685370741483,0.9999999999999998,0.0 -743.8877755511022,1.0,0.0 -745.0901803607214,1.0,0.0 -746.2925851703408,1.0,0.0 -747.4949899799599,1.0000000000000002,0.0 -748.6973947895792,1.0,0.0 -749.8997995991984,0.9999999999999999,0.0 -751.1022044088177,1.0000000000000002,0.0 -752.3046092184369,1.0,0.0 -753.5070140280561,1.0,0.0 -754.7094188376755,1.0000000000000002,0.0 -755.9118236472946,1.0,0.0 -757.1142284569139,0.9999999999999998,0.0 -758.316633266533,1.0,0.0 -759.5190380761524,1.0,0.0 -760.7214428857716,0.9999999999999999,0.0 -761.9238476953908,1.0,0.0 -763.12625250501,1.0000000000000002,0.0 -764.3286573146293,1.0000000000000002,0.0 -765.5310621242486,1.0,0.0 -766.7334669338677,1.0,0.0 -767.935871743487,1.0000000000000002,0.0 -769.1382765531063,1.0000000000000002,0.0 -770.3406813627255,1.0,0.0 -771.5430861723447,1.0,0.0 -772.7454909819639,1.0,0.0 -773.9478957915833,1.0,0.0 -775.1503006012024,1.0000000000000002,0.0 -776.3527054108217,1.0000000000000002,0.0 -777.5551102204408,1.0,0.0 -778.7575150300602,1.0000000000000002,0.0 -779.9599198396794,1.0,0.0 -781.1623246492986,1.0000000000000002,0.0 -782.3647294589179,1.0000000000000002,0.0 -783.5671342685371,1.0000000000000002,0.0 -784.7695390781564,1.0000000000000002,0.0 -785.9719438877755,1.0000000000000002,0.0 -787.1743486973949,1.0000000000000002,0.0 -788.376753507014,1.0,0.0 -789.5791583166333,0.9999999999999999,0.0 -790.7815631262525,1.0000000000000002,0.0 -791.9839679358718,1.0,0.0 -793.186372745491,1.0000000000000002,0.0 -794.3887775551102,1.0000000000000002,0.0 -795.5911823647295,0.9999999999999999,0.0 -796.7935871743487,0.9999999999999999,0.0 -797.995991983968,0.9999999999999998,0.0 -799.1983967935872,1.0,0.0 -800.4008016032064,1.0,0.0 -801.6032064128257,1.0,0.0 -802.8056112224449,1.0000000000000002,0.0 -804.0080160320642,1.0,0.0 -805.2104208416833,1.0000000000000002,0.0 -806.4128256513027,1.0,0.0 -807.6152304609218,1.0,0.0 -808.8176352705411,1.0,0.0 -810.0200400801604,1.0,0.0 -811.2224448897796,0.9999999999999999,0.0 -812.4248496993988,0.9999999999999999,0.0 -813.627254509018,0.9999999999999999,0.0 -814.8296593186374,1.0,0.0 -816.0320641282565,1.0,0.0 -817.2344689378758,1.0,0.0 -818.436873747495,1.0000000000000002,0.0 -819.6392785571143,1.0,0.0 -820.8416833667335,1.0000000000000002,0.0 -822.0440881763527,1.0000000000000002,0.0 -823.246492985972,1.0000000000000002,0.0 -824.4488977955912,1.0,0.0 -825.6513026052105,1.0000000000000002,0.0 -826.8537074148296,0.9999999999999999,0.0 -828.056112224449,1.0000000000000002,0.0 -829.2585170340682,1.0,0.0 -830.4609218436874,0.9999999999999999,0.0 -831.6633266533066,1.0,0.0 -832.8657314629259,1.0000000000000002,0.0 -834.0681362725452,1.0,0.0 -835.2705410821643,1.0,0.0 -836.4729458917836,1.0,0.0 -837.6753507014029,1.0,0.0 -838.8777555110221,0.9999999999999999,0.0 -840.0801603206413,1.0000000000000002,0.0 -841.2825651302605,1.0,0.0 -842.4849699398799,1.0,0.0 -843.687374749499,1.0,0.0 -844.8897795591183,1.0000000000000002,0.0 -846.0921843687374,1.0000000000000002,0.0 -847.2945891783568,1.0000000000000002,0.0 -848.496993987976,1.0,0.0 -849.6993987975952,1.0,0.0 -850.9018036072144,0.9999999999999999,0.0 -852.1042084168337,0.9999999999999999,0.0 -853.306613226453,1.0000000000000002,0.0 -854.5090180360721,1.0,0.0 -855.7114228456915,1.0,0.0 -856.9138276553107,1.0,0.0 -858.1162324649299,1.0,0.0 -859.3186372745491,0.9999999999999999,0.0 -860.5210420841684,1.0,0.0 -861.7234468937876,1.0,0.0 -862.9258517034068,1.0000000000000002,0.0 -864.1282565130261,1.0000000000000002,0.0 -865.3306613226453,1.0,0.0 -866.5330661322646,1.0,0.0 -867.7354709418838,1.0,0.0 -868.937875751503,0.9999999999999999,0.0 -870.1402805611223,1.0,0.0 -871.3426853707415,1.0000000000000002,0.0 -872.5450901803608,1.0000000000000002,0.0 -873.7474949899799,0.9999999999999999,0.0 -874.9498997995993,0.9999999999999999,0.0 -876.1523046092184,1.0,0.0 -877.3547094188377,1.0,0.0 -878.557114228457,0.9999999999999999,0.0 -879.7595190380762,1.0,0.0 -880.9619238476954,1.0000000000000002,0.0 -882.1643286573146,1.0,0.0 -883.366733466934,1.0,0.0 -884.5691382765531,1.0,0.0 -885.7715430861724,1.0,0.0 -886.9739478957916,1.0,0.0 -888.1763527054109,0.9999999999999999,0.0 -889.3787575150301,1.0,0.0 -890.5811623246493,1.0,0.0 -891.7835671342687,0.9999999999999998,0.0 -892.9859719438878,1.0,0.0 -894.1883767535071,1.0000000000000002,0.0 -895.3907815631262,1.0000000000000002,0.0 -896.5931863727455,1.0000000000000002,0.0 -897.7955911823648,0.9999999999999999,0.0 -898.997995991984,1.0,0.0 -900.2004008016032,1.0000000000000002,0.0 -901.4028056112224,0.9999999999999999,0.0 -902.6052104208418,1.0,0.0 -903.8076152304609,1.0,0.0 -905.0100200400802,1.0,0.0 -906.2124248496995,1.0000000000000002,0.0 -907.4148296593187,1.0,0.0 -908.6172344689379,1.0,0.0 -909.8196392785571,1.0000000000000002,0.0 -911.0220440881765,1.0,0.0 -912.2244488977956,1.0,0.0 -913.4268537074149,1.0,0.0 -914.6292585170341,0.9999999999999999,0.0 -915.8316633266534,1.0,0.0 -917.0340681362726,1.0,0.0 -918.2364729458918,1.0,0.0 -919.438877755511,1.0,0.0 -920.6412825651303,1.0000000000000004,0.0 -921.8436873747495,0.9999999999999999,0.0 -923.0460921843688,0.9999999999999999,0.0 -924.248496993988,1.0,0.0 -925.4509018036073,0.9999999999999999,0.0 -926.6533066132265,1.0,0.0 -927.8557114228457,0.9999999999999999,0.0 -929.058116232465,0.9999999999999999,0.0 -930.2605210420842,0.9999999999999998,0.0 -931.4629258517034,0.9999999999999998,0.0 -932.6653306613227,1.0,0.0 -933.867735470942,0.9999999999999999,0.0 -935.0701402805612,1.0,0.0 -936.2725450901804,1.0,0.0 -937.4749498997996,0.9999999999999999,0.0 -938.6773547094189,0.9999999999999997,0.0 -939.8797595190381,1.0,0.0 -941.0821643286573,0.9999999999999999,0.0 -942.2845691382765,1.0,0.0 -943.4869739478959,0.9999999999999999,0.0 -944.6893787575151,1.0000000000000002,0.0 -945.8917835671343,1.0000000000000002,0.0 -947.0941883767536,1.0,0.0 -948.2965931863728,1.0,0.0 -949.498997995992,1.0000000000000004,0.0 -950.7014028056112,1.0000000000000002,0.0 -951.9038076152304,1.0,0.0 -953.1062124248498,0.9999999999999999,0.0 -954.308617234469,1.0,0.0 -955.5110220440882,1.0,0.0 -956.7134268537075,1.0,0.0 -957.9158316633267,1.0,0.0 -959.1182364729459,1.0,0.0 -960.3206412825651,1.0000000000000002,0.0 -961.5230460921844,1.0,0.0 -962.7254509018037,1.0,0.0 -963.9278557114229,0.9999999999999999,0.0 -965.1302605210421,1.0,0.0 -966.3326653306614,1.0,0.0 -967.5350701402806,1.0,0.0 -968.7374749498998,1.0000000000000002,0.0 -969.939879759519,1.0,0.0 -971.1422845691383,1.0,0.0 -972.3446893787576,1.0000000000000002,0.0 -973.5470941883768,1.0000000000000002,0.0 -974.749498997996,1.0000000000000002,0.0 -975.9519038076153,1.0,0.0 -977.1543086172345,0.9999999999999998,0.0 -978.3567134268537,0.9999999999999999,0.0 -979.559118236473,1.0000000000000002,0.0 -980.7615230460922,1.0000000000000002,0.0 -981.9639278557115,1.0,0.0 -983.1663326653307,1.0,0.0 -984.36873747495,1.0,0.0 -985.5711422845692,1.0,0.0 -986.7735470941884,1.0000000000000002,0.0 -987.9759519038076,1.0,0.0 -989.1783567134269,1.0000000000000002,0.0 -990.3807615230461,1.0000000000000002,0.0 -991.5831663326654,0.9999999999999999,0.0 -992.7855711422847,0.9999999999999998,0.0 -993.9879759519039,1.0,0.0 -995.1903807615231,1.0,0.0 -996.3927855711423,1.0000000000000002,0.0 -997.5951903807616,1.0000000000000002,0.0 -998.7975951903808,1.0000000000000002,0.0 -1000.0,0.9999999999999999,0.0 diff --git a/tests/test_formula_dispersion.py b/tests/test_formula_dispersion.py new file mode 100644 index 00000000..43b24b42 --- /dev/null +++ b/tests/test_formula_dispersion.py @@ -0,0 +1,68 @@ +"""Tests for the formula dispersion""" +import numpy as np +from numpy.testing import assert_array_almost_equal +import pytest + +from elli.dispersions import Sellmeier, Formula +from elli.dispersions.cauchy import Cauchy +from elli.dispersions.formula import FormulaIndex + + +@pytest.mark.parametrize( + "ref_model, formula_model, formula, single_params, rep_params, unit", + [ + ( + Sellmeier, + Formula, + "eps = 1 + sum[A * (lbda * 1e-3)**2 / ((lbda * 1e-3) ** 2 - B)]", + {}, + {"A": [1, 1, 1], "B": [0.1, 0.1, 0.1]}, + "nm", + ), + ( + Sellmeier, + Formula, + "eps = 1 + sum[A * lbda**2 / (lbda** 2 - B)]", + {}, + {"A": [1, 1, 1], "B": [0.1, 0.1, 0.1]}, + "micrometer", + ), + ( + Cauchy, + FormulaIndex, + ( + "n = n0 + 1e2 * n1 / lbda ** 2 + 1e7 * n2 / lbda ** 4 + " + "1j * (k0 + 1e2 * k1 / lbda ** 2 + 1e7 * k2 / lbda ** 4)" + ), + {"n0": 1.5, "n1": 1, "n2": 1, "k0": 1, "k1": 1, "k2": 1}, + {}, + "nm", + ), + ], +) +def test_formula_reproduces_predefined( + ref_model, formula_model, formula, single_params, rep_params, unit +): + """The formula dispersion reproduces other dispersion models""" + lbda = np.linspace(400, 1500, 500) + + disp = ref_model(**single_params) + for params in zip(*rep_params.values()): + disp.add(*params) + + formula_disp = formula_model(formula, "lbda", single_params, rep_params, unit) + + assert_array_almost_equal( + disp.get_dielectric(lbda), formula_disp.get_dielectric(lbda) + ) + + +def test_formula_fails_on_wrong_repr(): + """The formula dispersion fails when it is tried to be + initialized with the wrong represenation""" + + with pytest.raises(ValueError): + Formula("n = 1", "", {}, {}) + + with pytest.raises(ValueError): + FormulaIndex("eps = 1", "", {}, {}) diff --git a/tests/test_nexus.py b/tests/test_nexus.py index 8e649106..700aff75 100644 --- a/tests/test_nexus.py +++ b/tests/test_nexus.py @@ -1,27 +1,10 @@ """Tests for a TiO2/SiO2/Si reference layer""" from __future__ import unicode_literals -import os -from distutils import dir_util - -import elli from pytest import fixture - -@fixture -def datadir(tmpdir, request): - """ - Fixture responsible for searching a folder with the same name of test - module and, if available, moving all contents to a temporary directory so - tests can use them freely. - """ - filename = request.module.__file__ - test_dir, _ = os.path.splitext(filename) - - if os.path.isdir(test_dir): - dir_util.copy_tree(test_dir, str(tmpdir)) - - return tmpdir +from fixtures import datadir # pylint: disable=unused-import +import elli @fixture @@ -30,7 +13,7 @@ def nexus_psi_delta_file(datadir): """ Fixture which returns the nexus file containting the psi / delta measurement data. """ - return datadir.join("ellips.test.nxs") + return datadir / "ellips.test.nxs" # pylint: disable=redefined-outer-name diff --git a/tests/test_nexus_formula.py b/tests/test_nexus_formula.py new file mode 100644 index 00000000..e941980d --- /dev/null +++ b/tests/test_nexus_formula.py @@ -0,0 +1,216 @@ +"""Test loading optical models from nexus dispersive material files""" +import numpy as np +from numpy.testing import assert_array_almost_equal +import h5py + +from fixtures import datadir # pylint: disable=unused-import +from elli.dispersions.table_index import Table +from elli.importer.nexus import read_nexus_materials +from elli.dispersions.base_dispersion import IndexDispersionSum +from elli.dispersions.polynomial import Polynomial +from elli.dispersions.sellmeier import Sellmeier +from elli.dispersions.sellmeier_custom import SellmeierCustomExponent +from elli.materials import BiaxialMaterial, IsotropicMaterial, UniaxialMaterial + + +def test_read_nexus_without_errors(datadir): + """All dispersive materials are loaded without errors""" + for file in datadir.iterdir(): + read_nexus_materials(file) + + +def test_read_nexus_nk_table(datadir): + """An isotropic nk table is loaded properly""" + nk_file = datadir / "MoTe2-Beal.nxs" + + with h5py.File(nk_file, "r") as h5file: + dispersion_path = "/entry/dispersion_x/dispersion_table_nk" + lbda = h5file[f"{dispersion_path}/wavelength"][()] + + table_manual = Table( + lbda=lbda, + n=h5file[f"{dispersion_path}/refractive_index"][()], + ) + + nexus_material = read_nexus_materials(nk_file)["entry"] + + assert isinstance(nexus_material, IsotropicMaterial) + + assert_array_almost_equal( + table_manual.get_dielectric(lbda), + nexus_material.dispersion_x.get_dielectric(lbda), + ) + + +def test_read_nexus_uniaxial_material_with_table(datadir): + """An uniaxial material with tabulated data is loaded correctly.""" + uniaxial_file = datadir / "MoSe2-Munkhbat-o.nxs" + + with h5py.File(uniaxial_file, "r") as h5file: + dispersion_x_path = "/entry/dispersion_x/dispersion_table_nk" + lbda = h5file[f"{dispersion_x_path}/wavelength"][()] + + table_x_manual = Table( + lbda=lbda, + n=h5file[f"{dispersion_x_path}/refractive_index"][()], + ) + + dispersion_z_path = "/entry/dispersion_z/dispersion_table_n" + lbda = h5file[f"{dispersion_z_path}/wavelength"][()] + + table_z_manual = Table( + lbda=lbda, n=h5file[f"{dispersion_z_path}/refractive_index"][()] + ) + + nexus_material = read_nexus_materials(uniaxial_file)["entry"] + + assert isinstance(nexus_material, UniaxialMaterial) + + assert_array_almost_equal( + table_x_manual.get_dielectric(lbda), + nexus_material.dispersion_x.get_dielectric(lbda), + ) + + assert_array_almost_equal( + table_z_manual.get_dielectric(lbda), + nexus_material.dispersion_z.get_dielectric(lbda), + ) + + +def test_read_nexus_for_sellmeier_table_sum(datadir): + """A sum of sellmeier and a table is read correctly""" + nexus = datadir / "Si-Chandler-Horowitz.nxs" + + def read_sellmeier(h5file: h5py.File): + sellmeier_path = "/entry/dispersion_x/sellmeier" + params_A = h5file[f"{sellmeier_path}/A/values"][()] + params_B = h5file[f"{sellmeier_path}/B/values"][()] + params_e1 = h5file[f"{sellmeier_path}/e1/values"][()] + params_e2 = h5file[f"{sellmeier_path}/e2/values"][()] + eps_inf = h5file["/entry/dispersion_x/sellmeier/eps_inf/value"][()] + + sellmeier = SellmeierCustomExponent() + for A, B, e1, e2 in zip(params_A, params_B, params_e1, params_e2): + sellmeier.add(A, e1, B, e2) + + return (sellmeier + eps_inf).as_index() + + with h5py.File(nexus, "r") as h5file: + table_path = "/entry/dispersion_x/dispersion_table_k" + lbda = h5file[f"{table_path}/wavelength"][()] + + table = Table(lbda=lbda, n=h5file[f"{table_path}/refractive_index"][()]) + + dispersion = table + read_sellmeier(h5file) + + nexus_material = read_nexus_materials(nexus)["entry"] + + assert isinstance(nexus_material, IsotropicMaterial) + assert isinstance(nexus_material.dispersion_x, IndexDispersionSum) + + assert_array_almost_equal( + dispersion.get_dielectric(lbda), + nexus_material.dispersion_x.get_dielectric(lbda), + ) + + +def test_read_nexus_sellmeier(datadir): + """A sellmeier dispersion is read correctly""" + nexus = datadir / "Si-Salzberg.nxs" + lbda = np.linspace(1357, 11040, 100) + + with h5py.File(nexus, "r") as h5file: + sellmeier_path = "/entry/dispersion_x/sellmeier" + params_A = h5file[f"{sellmeier_path}/A/values"][()] + params_B = h5file[f"{sellmeier_path}/B/values"][()] + + sellmeier = Sellmeier() + for A, B in zip(params_A, params_B): + sellmeier.add(A, B**2) + + nexus_material = read_nexus_materials(nexus)["entry"] + + assert isinstance(nexus_material, IsotropicMaterial) + + assert_array_almost_equal( + sellmeier.get_dielectric(lbda), nexus_material.dispersion_x.get_dielectric(lbda) + ) + + +def test_read_nexus_biaxial_material(datadir): + """A biaxial material is read correctly""" + nexus = datadir / "WTe2-Munkhbat-alpha.nxs" + + with h5py.File(nexus, "r") as h5file: + table_x_path = "/entry/dispersion_x/dispersion_table_nk" + table_y_path = "/entry/dispersion_y/dispersion_table_nk" + table_z_path = "/entry/dispersion_z/dispersion_table_n" + lbda = h5file[f"{table_x_path}/wavelength"][()] + + table_x = Table(lbda=lbda, n=h5file[f"{table_x_path}/refractive_index"][()]) + table_y = Table( + lbda=h5file[f"{table_y_path}/wavelength"][()], + n=h5file[f"{table_y_path}/refractive_index"][()], + ) + table_z = Table( + lbda=h5file[f"{table_z_path}/wavelength"][()], + n=h5file[f"{table_z_path}/refractive_index"][()], + ) + + nexus_material = read_nexus_materials(nexus)["entry"] + + assert isinstance(nexus_material, BiaxialMaterial) + + assert_array_almost_equal( + table_x.get_dielectric(lbda), nexus_material.dispersion_x.get_dielectric(lbda) + ) + + assert_array_almost_equal( + table_y.get_dielectric(lbda), nexus_material.dispersion_y.get_dielectric(lbda) + ) + + assert_array_almost_equal( + table_z.get_dielectric(lbda), nexus_material.dispersion_z.get_dielectric(lbda) + ) + + +def test_read_nexus_dispersion_sum(datadir): + """A dispersion sum is read correctly""" + nexus = datadir / "YVO4-Shi-e-20C.nxs" + lbda = np.linspace(480, 1340, 100) + + def read_sellmeier(h5file: h5py.File): + sellmeier_path = "/entry/dispersion_x/sellmeier" + params_A = h5file[f"{sellmeier_path}/A/values"][()] + params_B = h5file[f"{sellmeier_path}/B/values"][()] + params_e1 = h5file[f"{sellmeier_path}/e1/values"][()] + params_e2 = h5file[f"{sellmeier_path}/e2/values"][()] + eps_inf = h5file["/entry/dispersion_x/sellmeier/eps_inf/value"][()] + + sellmeier = SellmeierCustomExponent() + for A, B, e1, e2 in zip(params_A, params_B, params_e1, params_e2): + sellmeier.add(A, e1, B, e2) + + return sellmeier + eps_inf + + def read_poly(h5file: h5py.File): + polynomial_path = "/entry/dispersion_x/polynomial" + params_e = h5file[f"{polynomial_path}/e/values"][()] + params_f = h5file[f"{polynomial_path}/f/values"][()] + + poly = Polynomial(0) + for e, f in zip(params_e, params_f): + poly.add(f / 1e3**e, e) + + return poly + + with h5py.File(nexus, "r") as h5file: + disp = read_sellmeier(h5file) + read_poly(h5file) + + nexus_material = read_nexus_materials(nexus)["entry"] + + assert isinstance(nexus_material, IsotropicMaterial) + + assert_array_almost_equal( + disp.get_dielectric(lbda), nexus_material.dispersion_x.get_dielectric(lbda) + ) diff --git a/tests/test_nexus_formula/MoSe2-Munkhbat-o.nxs b/tests/test_nexus_formula/MoSe2-Munkhbat-o.nxs new file mode 100644 index 00000000..f040c24d Binary files /dev/null and b/tests/test_nexus_formula/MoSe2-Munkhbat-o.nxs differ diff --git a/tests/test_nexus_formula/MoTe2-Beal.nxs b/tests/test_nexus_formula/MoTe2-Beal.nxs new file mode 100644 index 00000000..35884bd3 Binary files /dev/null and b/tests/test_nexus_formula/MoTe2-Beal.nxs differ diff --git a/tests/test_nexus_formula/Si-Chandler-Horowitz.nxs b/tests/test_nexus_formula/Si-Chandler-Horowitz.nxs new file mode 100644 index 00000000..202a2a53 Binary files /dev/null and b/tests/test_nexus_formula/Si-Chandler-Horowitz.nxs differ diff --git a/tests/test_nexus_formula/Si-Salzberg.nxs b/tests/test_nexus_formula/Si-Salzberg.nxs new file mode 100644 index 00000000..536d79ab Binary files /dev/null and b/tests/test_nexus_formula/Si-Salzberg.nxs differ diff --git a/tests/test_nexus_formula/WTe2-Munkhbat-alpha.nxs b/tests/test_nexus_formula/WTe2-Munkhbat-alpha.nxs new file mode 100644 index 00000000..ff59aba0 Binary files /dev/null and b/tests/test_nexus_formula/WTe2-Munkhbat-alpha.nxs differ diff --git a/tests/test_nexus_formula/YVO4-Shi-e-20C.nxs b/tests/test_nexus_formula/YVO4-Shi-e-20C.nxs new file mode 100644 index 00000000..a7eab6d8 Binary files /dev/null and b/tests/test_nexus_formula/YVO4-Shi-e-20C.nxs differ diff --git a/tests/test_refractive_index_info.py b/tests/test_refractive_index_info.py index b95aefb7..a5c4ba18 100644 --- a/tests/test_refractive_index_info.py +++ b/tests/test_refractive_index_info.py @@ -72,10 +72,10 @@ def test_formula_4(self): disp = self.RII.load_dispersion("AgCl", "Tilton") # formula 4 np.testing.assert_almost_equal( - disp.get_refractive_index(1024), 2.0213900020277, decimal=3 + disp.get_refractive_index(1024), 2.0213900020277, decimal=12 ) np.testing.assert_almost_equal( - disp.get_refractive_index(10080), 1.9799748188746, decimal=4 + disp.get_refractive_index(10080), 1.9799748188746, decimal=12 ) def test_formula_5(self):