Conditions dataclass

A dataclass representing the environmental conditions.

Attributes:
  • temperature (float) –

    The temperature in Celsius. None if unspecified.

  • pressure (float) –

    The pressure in MPa. None if unspecified.

Source code in pyindexrepo/main.py
@dataclass
class Conditions:
    """
    A dataclass representing the environmental conditions.

    Attributes:
        temperature (float, optional): The temperature in Celsius. None if unspecified.
        pressure (float, optional): The pressure in MPa. None if unspecified.
    """

    temperature: float | None = None
    pressure: float | None = None

    @staticmethod
    def parse_temperature(temperature):
        """Parse temperature string to Celsius."""
        if isinstance(temperature, str):
            if "K" in temperature:
                return float(temperature.split()[0]) - 273.15
            elif "C" in temperature:
                return float(temperature.split()[0])
            else:
                warnings.warn("Temperature unit not recognized. Assuming Kelvin.")
                return float(temperature.split()[0]) - 273.15
        return Specs.parse_float(temperature)

    @staticmethod
    def parse_pressure(pressure):
        """Parse pressure string to MPa."""
        if isinstance(pressure, str):
            return Specs.parse_float(pressure.replace(" MPa", ""))
        return Specs.parse_float(pressure)

    @staticmethod
    def read_conditions_from_yaml(conditions_dict):
        """Read conditions from a YAML dictionary and create a Conditions object."""
        return Conditions(
            temperature=Conditions.parse_temperature(conditions_dict.get("temperature")),
            pressure=Conditions.parse_pressure(conditions_dict.get("pressure")),
        )

parse_pressure(pressure) staticmethod

Parse pressure string to MPa.

Source code in pyindexrepo/main.py
@staticmethod
def parse_pressure(pressure):
    """Parse pressure string to MPa."""
    if isinstance(pressure, str):
        return Specs.parse_float(pressure.replace(" MPa", ""))
    return Specs.parse_float(pressure)

parse_temperature(temperature) staticmethod

Parse temperature string to Celsius.

Source code in pyindexrepo/main.py
@staticmethod
def parse_temperature(temperature):
    """Parse temperature string to Celsius."""
    if isinstance(temperature, str):
        if "K" in temperature:
            return float(temperature.split()[0]) - 273.15
        elif "C" in temperature:
            return float(temperature.split()[0])
        else:
            warnings.warn("Temperature unit not recognized. Assuming Kelvin.")
            return float(temperature.split()[0]) - 273.15
    return Specs.parse_float(temperature)

read_conditions_from_yaml(conditions_dict) staticmethod

Read conditions from a YAML dictionary and create a Conditions object.

Source code in pyindexrepo/main.py
@staticmethod
def read_conditions_from_yaml(conditions_dict):
    """Read conditions from a YAML dictionary and create a Conditions object."""
    return Conditions(
        temperature=Conditions.parse_temperature(conditions_dict.get("temperature")),
        pressure=Conditions.parse_pressure(conditions_dict.get("pressure")),
    )

FormulaIndexData dataclass

A dataclass representing formula-based index data.

Attributes:
  • formula (callable) –

    A callable function that computes the refractive index (n) or extinction coefficient (k) for a given wavelength.

  • coefficients (array) –

    An array of coefficients required by the formula function.

  • min_wl (float) –

    The minimum wavelength (in microns) for which the formula is valid. Default is negative infinity.

  • max_wl (float) –

    The maximum wavelength (in microns) for which the formula is valid. Default is positive infinity.

Source code in pyindexrepo/main.py
@dataclass
class FormulaIndexData:
    """
    A dataclass representing formula-based index data.

    Attributes:
        formula (callable): A callable function that computes the refractive index (n) or extinction coefficient (k) for a given wavelength.
        coefficients (np.array): An array of coefficients required by the formula function.
        min_wl (float, optional): The minimum wavelength (in microns) for which the formula is valid. Default is negative infinity.
        max_wl (float, optional): The maximum wavelength (in microns) for which the formula is valid. Default is positive infinity.
    """

    formula: callable | str
    coefficients: np.array
    min_wl: float = field(default=-np.inf)
    max_wl: float = field(default=np.inf)

    def __post_init__(self):
        self.coefficients = np.array(self.coefficients, dtype=float)
        if isinstance(self.formula, str):
            self.formula = getattr(dispersion_formulas, self.formula)

    def get_n_or_k(self, wavelength: float | np.ndarray) -> float | np.ndarray:
        if isinstance(wavelength, float) or isinstance(wavelength, np.ndarray):
            return self.formula(wavelength, self.coefficients)
        elif isinstance(wavelength, list):
            return self.formula(np.array(wavelength), self.coefficients)
        else:
            raise ValueError(f"The datatype {type(wavelength)} is not supported.")

Material dataclass

A dataclass representing a material's properties.

Attributes:
  • n (TabulatedIndexData | FormulaIndexData | None) –

    The refractive index (n) data for the material. It can be an instance of TabulatedIndexData or FormulaIndexData, or None if unspecified.

  • k (TabulatedIndexData | FormulaIndexData | None) –

    The extinction coefficient (k) data for the material. It can be an instance of TabulatedIndexData or FormulaIndexData, or None if unspecified.

  • specs (Specs | None) –

    An instance of the Specs dataclass representing material specifications. None if unspecified.

  • conditions (Conditions | None) –

    (Conditions | None, optional): An instance of the Conditions dataclass representing environmental conditions.

  • yaml_data (YAMLLibraryData) –

    An instance of the YAMLLibraryData class representing YAML library data. None if unspecified.

  • name (str) –

    The name of the material. Normally extracted from the YAML data, but can be overridden. Empty string if unspecified.

Source code in pyindexrepo/main.py
@dataclass
class Material:
    """
    A dataclass representing a material's properties.

    Attributes:
        n (TabulatedIndexData | FormulaIndexData | None, optional): The refractive index (n) data for the material.
            It can be an instance of TabulatedIndexData or FormulaIndexData, or None if unspecified.
        k (TabulatedIndexData | FormulaIndexData | None, optional): The extinction coefficient (k) data for the material.
            It can be an instance of TabulatedIndexData or FormulaIndexData, or None if unspecified.
        specs (Specs | None, optional): An instance of the Specs dataclass representing material specifications.
            None if unspecified.
        conditions: (Conditions | None, optional): An instance of the Conditions dataclass representing environmental conditions.
        yaml_data (YAMLLibraryData, optional): An instance of the YAMLLibraryData class representing YAML library data.
            None if unspecified.
        name (str, optional): The name of the material. Normally extracted from the YAML data, but can be overridden. Empty string if unspecified.
    """

    n: TabulatedIndexData | FormulaIndexData | None = field(default=None)
    k: TabulatedIndexData | FormulaIndexData | None = field(default=None)
    specs: Specs | None = field(default=None)
    conditions: Conditions | None = field(default=None)
    yaml_data: YAMLLibraryData = field(default=None)
    name: str = field(default=yaml_data.name if yaml_data else "")

    def get_n(self, wavelength: float | np.ndarray) -> float | np.ndarray:
        """Get refractive index n for a given wavelength or array of wavelengths.

        Args:
            wavelength: Wavelength or array of wavelengths in micrometers.

        Returns:
            float or array of floats: Refractive index n for the given wavelength
        """
        if self.n is None:
            warnings.warn("No n data. Returning 0")
            return np.zeros_like(wavelength)
        return self.n.get_n_or_k(wavelength)

    def get_k(self, wavelength: float | np.ndarray) -> float | np.ndarray:
        """Get extinction coefficient k for a given wavelength or array of wavelengths.

        Args:
            wavelength: Wavelength or array of wavelengths in micrometers.

        Returns:
            float or array of floats: Extinction coefficient k for the given wavelength
        """
        if self.k is None:
            warnings.warn("No k data. Returning 0")
            return np.zeros_like(wavelength)
        return self.k.get_n_or_k(wavelength)

    def get_nk(self, wavelength: float | np.ndarray) -> tuple[float | np.ndarray, float | np.ndarray]:
        """Get refractive index n and extinction coefficient k for a given wavelength or array of wavelengths.

        Args:
            wavelength: Wavelength or array of wavelengths in micrometers.

        Returns:
            tuple of floats or arrays of floats: Refractive index n and extinction coefficient k for the given wavelength
        """
        return self.get_n(wavelength), self.get_k(wavelength)

    def get_n_at_temperature(
        self, wavelength: float | np.ndarray, temperature: float, P: float = 0.10133
    ) -> float | np.ndarray:
        """Get refractive index n at a given temperature for a given wavelength or array of wavelengths.

        Args:
            wavelength: Wavelength or array of wavelengths in micrometers.
            temperature: Temperature in degrees
            P: Pressure in MPa. Default is 0.10133 MPa (1 atm).

        Returns:
            float or array of floats: Refractive index n at the given temperature for the given wavelength
        """
        assert self.specs is not None, "There are no specs available for this material"
        assert self.specs.thermal_expansion is not None, (
            "There is no thermal dispersion formula available " "for this material"
        )

        if self.specs.wavelength_is_vacuum:
            n_abs = self.get_n(wavelength)
            return n_abs + self.specs.thermal_dispersion.delta_n_abs(
                n_abs,
                wavelength,
                temperature - self.conditions.temperature + 273.15,
                self.specs.thermal_dispersion.coefficients,
            )
        else:
            rel_wavelength = (
                wavelength
                * dispersion_formulas.n_air(wavelength, temperature, P)
                / dispersion_formulas.n_air(wavelength)
            )
            n_rel = self.get_n(rel_wavelength)
            n_abs = dispersion_formulas.relative_to_absolute(
                n_rel, rel_wavelength, self.conditions.temperature - 273.15, 0.10133
            )
            n_abs += self.specs.thermal_dispersion.delta_n_abs(
                n_abs,
                rel_wavelength,
                temperature - self.conditions.temperature + 273.15,
                *self.specs.thermal_dispersion.coefficients,
            )
            return dispersion_formulas.absolute_to_relative(
                n_abs, rel_wavelength, temperature, P
            )

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

get_k(wavelength)

Get extinction coefficient k for a given wavelength or array of wavelengths.

Parameters:
  • wavelength (float | ndarray) –

    Wavelength or array of wavelengths in micrometers.

Returns:
  • float | ndarray

    float or array of floats: Extinction coefficient k for the given wavelength

Source code in pyindexrepo/main.py
def get_k(self, wavelength: float | np.ndarray) -> float | np.ndarray:
    """Get extinction coefficient k for a given wavelength or array of wavelengths.

    Args:
        wavelength: Wavelength or array of wavelengths in micrometers.

    Returns:
        float or array of floats: Extinction coefficient k for the given wavelength
    """
    if self.k is None:
        warnings.warn("No k data. Returning 0")
        return np.zeros_like(wavelength)
    return self.k.get_n_or_k(wavelength)

get_n(wavelength)

Get refractive index n for a given wavelength or array of wavelengths.

Parameters:
  • wavelength (float | ndarray) –

    Wavelength or array of wavelengths in micrometers.

Returns:
  • float | ndarray

    float or array of floats: Refractive index n for the given wavelength

Source code in pyindexrepo/main.py
def get_n(self, wavelength: float | np.ndarray) -> float | np.ndarray:
    """Get refractive index n for a given wavelength or array of wavelengths.

    Args:
        wavelength: Wavelength or array of wavelengths in micrometers.

    Returns:
        float or array of floats: Refractive index n for the given wavelength
    """
    if self.n is None:
        warnings.warn("No n data. Returning 0")
        return np.zeros_like(wavelength)
    return self.n.get_n_or_k(wavelength)

get_n_at_temperature(wavelength, temperature, P=0.10133)

Get refractive index n at a given temperature for a given wavelength or array of wavelengths.

Parameters:
  • wavelength (float | ndarray) –

    Wavelength or array of wavelengths in micrometers.

  • temperature (float) –

    Temperature in degrees

  • P (float, default: 0.10133 ) –

    Pressure in MPa. Default is 0.10133 MPa (1 atm).

Returns:
  • float | ndarray

    float or array of floats: Refractive index n at the given temperature for the given wavelength

Source code in pyindexrepo/main.py
def get_n_at_temperature(
    self, wavelength: float | np.ndarray, temperature: float, P: float = 0.10133
) -> float | np.ndarray:
    """Get refractive index n at a given temperature for a given wavelength or array of wavelengths.

    Args:
        wavelength: Wavelength or array of wavelengths in micrometers.
        temperature: Temperature in degrees
        P: Pressure in MPa. Default is 0.10133 MPa (1 atm).

    Returns:
        float or array of floats: Refractive index n at the given temperature for the given wavelength
    """
    assert self.specs is not None, "There are no specs available for this material"
    assert self.specs.thermal_expansion is not None, (
        "There is no thermal dispersion formula available " "for this material"
    )

    if self.specs.wavelength_is_vacuum:
        n_abs = self.get_n(wavelength)
        return n_abs + self.specs.thermal_dispersion.delta_n_abs(
            n_abs,
            wavelength,
            temperature - self.conditions.temperature + 273.15,
            self.specs.thermal_dispersion.coefficients,
        )
    else:
        rel_wavelength = (
            wavelength
            * dispersion_formulas.n_air(wavelength, temperature, P)
            / dispersion_formulas.n_air(wavelength)
        )
        n_rel = self.get_n(rel_wavelength)
        n_abs = dispersion_formulas.relative_to_absolute(
            n_rel, rel_wavelength, self.conditions.temperature - 273.15, 0.10133
        )
        n_abs += self.specs.thermal_dispersion.delta_n_abs(
            n_abs,
            rel_wavelength,
            temperature - self.conditions.temperature + 273.15,
            *self.specs.thermal_dispersion.coefficients,
        )
        return dispersion_formulas.absolute_to_relative(
            n_abs, rel_wavelength, temperature, P
        )

get_nk(wavelength)

Get refractive index n and extinction coefficient k for a given wavelength or array of wavelengths.

Parameters:
  • wavelength (float | ndarray) –

    Wavelength or array of wavelengths in micrometers.

Returns:
  • tuple[float | ndarray, float | ndarray]

    tuple of floats or arrays of floats: Refractive index n and extinction coefficient k for the given wavelength

Source code in pyindexrepo/main.py
def get_nk(self, wavelength: float | np.ndarray) -> tuple[float | np.ndarray, float | np.ndarray]:
    """Get refractive index n and extinction coefficient k for a given wavelength or array of wavelengths.

    Args:
        wavelength: Wavelength or array of wavelengths in micrometers.

    Returns:
        tuple of floats or arrays of floats: Refractive index n and extinction coefficient k for the given wavelength
    """
    return self.get_n(wavelength), self.get_k(wavelength)

RefractiveIndexLibrary dataclass

The dataclass representing the refractive index library using data from RefractiveIndex.info.

Attributes:
  • path_to_library (Path) –

    The path to the refractive index library YAML file. Default is a path pointing to a default library file.

  • auto_upgrade (bool) –

    Automatically upgrade the library when initialized if set to True. Default is False.

  • force_upgrade (bool) –

    Forcefully upgrade the library even if not necessary if set to True. Default is False.

  • materials_yaml ((List[YAMLLibraryData], read - only)) –

    A list of YAML library data instances representing materials.

  • materials_dict ((Dict[str, Dict[str, Dict[str, Material]]], read - only)) –

    A dictionary of materials organized by catalog, category, and name.

  • materials_list ((List[Material], read - only)) –

    A list of all materials contained in the library.

  • github_sha ((str, read - only)) –

    The GitHub SHA corresponding to the version of the library data.

Source code in pyindexrepo/main.py
@dataclass
class RefractiveIndexLibrary:
    """
    The dataclass representing the refractive index library using data from RefractiveIndex.info.

    Attributes:
        path_to_library (Path, optional): The path to the refractive index library YAML file.
            Default is a path pointing to a default library file.
        auto_upgrade (bool, optional): Automatically upgrade the library when initialized if set to True.
            Default is False.
        force_upgrade (bool, optional): Forcefully upgrade the library even if not necessary if set to True.
            Default is False.
        materials_yaml (List[YAMLLibraryData], read-only): A list of YAML library data instances representing materials.
        materials_dict (Dict[str, Dict[str, Dict[str, Material]]], read-only): A dictionary of materials organized by catalog, category, and name.
        materials_list (List[Material], read-only): A list of all materials contained in the library.
        github_sha (str, read-only): The GitHub SHA corresponding to the version of the library data.
    """

    path_to_library: Path = field(
        default=Path(__file__)
        .absolute()
        .parent.parent.joinpath("database/catalog-nk.yml")
    )
    auto_upgrade: bool = field(default=False)
    force_upgrade: bool = field(default=False)
    materials_yaml: list[YAMLLibraryData] = field(default_factory=list, init=False)
    materials_dict: dict[str, dict[str, dict[str, Material | YAMLLibraryData]]] = field(
        default_factory=dict, init=False
    )
    materials_list: list[Material | YAMLLibraryData] = field(default_factory=list, init=False)
    github_sha: str = field(default="", init=False)

    def _is_library_outdated(self) -> bool:
        """Checks if local library is outdated"""
        if self.path_to_library.parent.joinpath(".local_sha").exists():
            # get local release tag
            with open(self.path_to_library.parent.joinpath(".local_sha"), "r") as file:
                local_tag = file.readline().strip()
            # get current release tag on GitHub
            try:
                release_url = "https://api.github.com/repos/polyanskiy/refractiveindex.info-database/releases/latest"
                response = requests.get(release_url)
                self.github_sha = response.json()["tag_name"]
            except KeyError:
                logger.warning(
                    "Couldn't get the latest release tag on GitHub. Database cannot be updated."
                )
                self.github_sha = ""
                return False
            return not (self.github_sha == local_tag)
        else:
            logger.info("No local library exists.")
            return True

    def _download_latest_commit(self) -> bool:
        """Download latest library from GitHub.

        Downloads the latest library from the refractiveindex.info GitHub repository
         and extracts the necessary data files.

        Returns:
            bool: True if the library was successfully downloaded, False otherwise.
        """
        if self._is_library_outdated() or self.force_upgrade:
            logger.info("New Library available... Downloading...")
            release_url = "https://api.github.com/repos/polyanskiy/refractiveindex.info-database/releases/latest"
            response = requests.get(release_url)
            release_data = response.json()
            zip_url = release_data["zipball_url"]
            response = requests.get(zip_url)

            with open(self.path_to_library.with_suffix(".zip"), "wb") as file:
                file.write(response.content)
            with zipfile.ZipFile(self.path_to_library.with_suffix(".zip"), "r") as file:
                file_list = file.namelist()
                subfolder_files = [
                    file
                    for file in file_list
                    if file.startswith(f"{file_list[0]}database/data")
                    and file.endswith(".yml")
                ]
                subfolder_files.append(f"{file_list[0]}database/catalog-nk.yml")
                for fn in subfolder_files:
                    logger.debug(fn)
                    # create a new Path object for the file to extract
                    extract_path = self.path_to_library.parent / Path(
                        "/".join(Path(fn).parts[2:])
                    )
                    extract_path.parent.mkdir(parents=True, exist_ok=True)
                    # open the file in the zipfile and write it to disk
                    with file.open(fn) as zf, extract_path.open("wb") as of:
                        of.write(zf.read())

            with open(self.path_to_library.parent.joinpath(".local_sha"), "w") as file:
                file.write(self.github_sha)
            return True
        else:
            return False

    def _load_from_yaml(self):
        """Load data from yaml file for internal use

        Returns:
            None
        """
        logger.info("load from yaml")
        with open(self.path_to_library, encoding='utf-8') as f:
            yaml_data = yaml.safe_load(f)

            for s in yaml_data:
                for book in s.get("content", []):
                    if "BOOK" not in book:
                        continue
                    for page in book.get("content", []):
                        if "PAGE" not in page:
                            continue
                        self.materials_yaml.append(
                            YAMLLibraryData(
                                name=page["name"],
                                lib_page=page["PAGE"],
                                lib_book=book["BOOK"],
                                lib_shelf=s["SHELF"],
                                lib_data=page["data"],
                                lib_path=self.path_to_library.parent.joinpath("data", page["data"]),
                            )
                        )

    def _convert_to_material_dict(self):
        """Convert yaml data to Material objects

        Returns:
            None
        """
        for m in self.materials_yaml:
            # try to load material from yaml
            mat = yaml_to_material(self.path_to_library.parent.joinpath("data").joinpath(m.lib_data), m.lib_shelf,
                               m.lib_book, m.lib_page, m.name)
            if mat:
                # add material to dict, use shelf, book and page as keys
                self.materials_dict.setdefault(m.lib_shelf, {}).setdefault(m.lib_book, {})[m.lib_page] = mat
                self.materials_list.append(mat)
                # Save each material to a separate pickle file
                material_pickle_path = self.path_to_library.parent.joinpath(
                    f"pickled/{m.lib_shelf}_{m.lib_book}_{m.lib_page}.pkl")
                # create pickled folder if it doesn't exist
                if not material_pickle_path.parent.is_dir():
                    material_pickle_path.parent.mkdir()
                with open(material_pickle_path, "wb") as f:
                    pickle.dump(mat, f, pickle.HIGHEST_PROTOCOL)

        with open(self.path_to_library.with_suffix(".pickle"), "wb") as f:
            pickle.dump(self.materials_yaml, f, pickle.HIGHEST_PROTOCOL)
        # with open(self.path_to_library.with_suffix(".pickle2"), "wb") as f:
        #     pickle.dump(self.materials_dict, f, pickle.HIGHEST_PROTOCOL)

    def _load_from_pickle(self):
        logger.info("load from pickle")
        with open(self.path_to_library.with_suffix(".pickle"), "rb") as f:
            self.materials_yaml = pickle.load(f)
        for m in self.materials_yaml:
            self.materials_dict.setdefault(m.lib_shelf, {}).setdefault(m.lib_book, {})[m.lib_page] = m
            self.materials_list.append(m)
        #
        # for sd in self.materials_dict.values():
        #     for bd in sd.values():
        #         for mat in bd.values():
        #             self.materials_list.append(mat)
        logger.info("... done.")

    def __post_init__(self):
        upgraded = False
        # create database folder if it doesn't exist
        try:
            if not self.path_to_library.parent.is_dir():
                self.path_to_library.parent.mkdir(parents=True, exist_ok=True)
        except Exception as e:
            warnings.warn(f"Failed to create directory: {e}")
            return

        # check if the folder is empty or upgrade is needed
        try:
            if self.auto_upgrade or self.force_upgrade or not os.listdir(self.path_to_library.parent):
                upgraded = self._download_latest_commit()
        except Exception as e:
            warnings.warn(f"Failed to check directory contents or download latest commit: {e}")
            return

        # load data from the appropriate source
        try:
            if self.path_to_library.exists():
                if self.path_to_library.with_suffix(".pickle").exists() and not upgraded:
                    self._load_from_pickle()
                else:
                    self._load_from_yaml()
                    self._convert_to_material_dict()
            else:
                warnings.warn(
                    "Path to library does not exist! Please check path or activate auto_upgrade to download."
                )
        except Exception as e:
            warnings.warn(f"Failed to load data: {e}")

    def search_material_by_page_name(
        self, page_name: str, exact_match: bool = False
    ) -> Material | list[Material] | None:
        """Search Material by name

        Search a Material by page name as given at refractiveindex.info.
        Sometimes, the name is not unique, so the function returns either a single Material or a list of Materials
        or None if it doesn't find a match.

        Args:
            page_name: name of the material as given as page name on refractiveindex.info
            exact_match: if True, only exact matches are considered. Default is False. e.g. if False,
            'BK7' will return all BK7 glasses, including N-BK7, K-BK7, etc.

        Returns:
            Material or list of Materials matching the Name

        Examples:
            >>> db = RefractiveIndexLibrary()
            >>> bk7 = db.search_material_by_page_name('N-BK7')[0]  # returns a list of different BK7 glasses
            >>> print(bk7.get_n(0.5875618))
            1.5168000345005885
        """
        materials = []
        if exact_match:
            for m in self.materials_list:
                if page_name == m.yaml_data.name:
                    if isinstance(m, Material):
                        materials.append(m)
                    elif isinstance(m, YAMLLibraryData):
                        materials.append(self.get_material(m.lib_shelf, m.lib_book, m.lib_page))
                    else:
                        warnings.warn("Unknown material type.")
        else:
            for m in self.materials_list:
                if isinstance(m, YAMLLibraryData):
                    if page_name in m.name:
                        materials.append(self.get_material(m.lib_shelf, m.lib_book, m.lib_page))
                elif isinstance(m, Material):
                    if page_name in m.yaml_data.name:
                        materials.append(m)
                else:
                    warnings.warn("Unknown material type.")
        return (
            materials[0]
            if len(materials) == 1
            else materials if len(materials) > 1 else None
        )

    def search_material_by_n(
        self,
        n: float,
        wl: float = 0.5875618,
        filter_shelf: str | None = None,
        filter_book: str | None = None,
    ) -> list[Material]:
        """Search Material by refractive index

        Look for a material with a specific refractive index at a certain wavelength.
        In return, you get a sorted list of materials with index [0] being the closest to input n.

        Args:
            n: refractive index
            wl: wavelength
            filter_shelf: if given, only materials containing this string in their shelf name are considered
            filter_book: if given, only materials containing this string in their book name are considered

        Examples:
            >>> db = RefractiveIndexLibrary()
            >>> # get 3 closest OHARA glasses with n=1.5 at 0.55microns:
            >>> materials = db.search_material_by_n(1.5, wl=0.55, filter_book="ohara")[:3]
            >>> print(materials[0].yaml_data.name, materials[0].get_n(0.55))
            BSL3 1.4999474387027893
            >>> print(materials[1].yaml_data.name, materials[1].get_n(0.55))
            S-FPL51Y 1.498313496038896
            >>> print(materials[2].yaml_data.name, materials[2].get_n(0.55))
            S-FPL51 1.498303051383454

        Returns:
            sorted list of materials matching search criteria

        """
        materials = []
        materials_n_distance = []
        for shelf_m, d in self.materials_dict.items():
            if not (shelf_m == filter_shelf or filter_shelf is None):
                continue
            for book_name, book_m in d.items():
                if filter_book is not None:
                    if filter_book.lower() not in book_name.lower():
                        continue

                for mat in book_m.values():
                    materials.append(mat)
                    try:
                        materials_n_distance.append(abs(mat.get_n(wl) - n))
                    except ValueError:
                        materials_n_distance.append(99)

        return [
            x
            for _, x in sorted(
                zip(materials_n_distance, materials), key=lambda pair: pair[0]
            )
        ]

    def get_material(self, shelf: str, book: str, page: str) -> Material:
        """Get Material by shelf, book, page name

        Select Material by specifying shelf, book and page as given on refractiveindex.info

        Args:
            shelf: shelf name
            book: book name
            page: page name

        Returns:
            Material object

        Examples:
            >>> db = RefractiveIndexLibrary()
            >>> bk7 = db.get_material("specs", "SCHOTT-optical", "N-BK7")
            >>> print(bk7.get_n(0.5875618))
            1.5168000345005885
        """
        if shelf not in self.materials_dict:
            raise ValueError(f"Shelf {shelf} not found in database.")
        if book not in self.materials_dict[shelf]:
            raise ValueError(f"Book {book} not found in database.")
        if page not in self.materials_dict[shelf][book]:
            raise ValueError(f"Page {page} not found in database.")
        if not self.materials_dict[shelf][book][page]:
            raise ValueError(f"Material {shelf}/{book}/{page} not found in database.")
        # check if material is a string or YAMLLibraryData object, if so load it and replace it in the dict
        if isinstance(self.materials_dict[shelf][book][page], YAMLLibraryData):
            # check if pickled material exists
            material_pickle_path = self.path_to_library.parent.joinpath(
                f"pickled/{shelf}_{book}_{page}.pkl"
            )
            if material_pickle_path.exists():
                with open(material_pickle_path, "rb") as f:
                    self.materials_dict[shelf][book][page] = pickle.load(f)
            else:

                self.materials_dict[shelf][book][page] = yaml_to_material(
                    self.materials_dict[shelf][book][page].lib_path,
                    shelf,
                    book,
                    page,
                    self.materials_dict[shelf][book][page].name,
                )

        return self.materials_dict[shelf][book][page]

    def get_material_by_path(self, yaml_path: str) -> Material:
        """Get material by path

        Args:
            yaml_path: path as shown on refractive index when hovered over 'CSV - comma separated data'

        Returns:
            Material object
        """
        mat_found = [
            m
            for m in self.materials_list
            if str(m.yaml_data.lib_path).lower().endswith(yaml_path.lower() + ".yml")
        ]
        return mat_found[0] if mat_found else None

get_material(shelf, book, page)

Get Material by shelf, book, page name

Select Material by specifying shelf, book and page as given on refractiveindex.info

Parameters:
  • shelf (str) –

    shelf name

  • book (str) –

    book name

  • page (str) –

    page name

Returns:

Examples:

>>> db = RefractiveIndexLibrary()
>>> bk7 = db.get_material("specs", "SCHOTT-optical", "N-BK7")
>>> print(bk7.get_n(0.5875618))
1.5168000345005885
Source code in pyindexrepo/main.py
def get_material(self, shelf: str, book: str, page: str) -> Material:
    """Get Material by shelf, book, page name

    Select Material by specifying shelf, book and page as given on refractiveindex.info

    Args:
        shelf: shelf name
        book: book name
        page: page name

    Returns:
        Material object

    Examples:
        >>> db = RefractiveIndexLibrary()
        >>> bk7 = db.get_material("specs", "SCHOTT-optical", "N-BK7")
        >>> print(bk7.get_n(0.5875618))
        1.5168000345005885
    """
    if shelf not in self.materials_dict:
        raise ValueError(f"Shelf {shelf} not found in database.")
    if book not in self.materials_dict[shelf]:
        raise ValueError(f"Book {book} not found in database.")
    if page not in self.materials_dict[shelf][book]:
        raise ValueError(f"Page {page} not found in database.")
    if not self.materials_dict[shelf][book][page]:
        raise ValueError(f"Material {shelf}/{book}/{page} not found in database.")
    # check if material is a string or YAMLLibraryData object, if so load it and replace it in the dict
    if isinstance(self.materials_dict[shelf][book][page], YAMLLibraryData):
        # check if pickled material exists
        material_pickle_path = self.path_to_library.parent.joinpath(
            f"pickled/{shelf}_{book}_{page}.pkl"
        )
        if material_pickle_path.exists():
            with open(material_pickle_path, "rb") as f:
                self.materials_dict[shelf][book][page] = pickle.load(f)
        else:

            self.materials_dict[shelf][book][page] = yaml_to_material(
                self.materials_dict[shelf][book][page].lib_path,
                shelf,
                book,
                page,
                self.materials_dict[shelf][book][page].name,
            )

    return self.materials_dict[shelf][book][page]

get_material_by_path(yaml_path)

Get material by path

Parameters:
  • yaml_path (str) –

    path as shown on refractive index when hovered over 'CSV - comma separated data'

Returns:
Source code in pyindexrepo/main.py
def get_material_by_path(self, yaml_path: str) -> Material:
    """Get material by path

    Args:
        yaml_path: path as shown on refractive index when hovered over 'CSV - comma separated data'

    Returns:
        Material object
    """
    mat_found = [
        m
        for m in self.materials_list
        if str(m.yaml_data.lib_path).lower().endswith(yaml_path.lower() + ".yml")
    ]
    return mat_found[0] if mat_found else None

search_material_by_n(n, wl=0.5875618, filter_shelf=None, filter_book=None)

Search Material by refractive index

Look for a material with a specific refractive index at a certain wavelength. In return, you get a sorted list of materials with index [0] being the closest to input n.

Parameters:
  • n (float) –

    refractive index

  • wl (float, default: 0.5875618 ) –

    wavelength

  • filter_shelf (str | None, default: None ) –

    if given, only materials containing this string in their shelf name are considered

  • filter_book (str | None, default: None ) –

    if given, only materials containing this string in their book name are considered

Examples:

>>> db = RefractiveIndexLibrary()
>>> # get 3 closest OHARA glasses with n=1.5 at 0.55microns:
>>> materials = db.search_material_by_n(1.5, wl=0.55, filter_book="ohara")[:3]
>>> print(materials[0].yaml_data.name, materials[0].get_n(0.55))
BSL3 1.4999474387027893
>>> print(materials[1].yaml_data.name, materials[1].get_n(0.55))
S-FPL51Y 1.498313496038896
>>> print(materials[2].yaml_data.name, materials[2].get_n(0.55))
S-FPL51 1.498303051383454
Returns:
  • list[Material]

    sorted list of materials matching search criteria

Source code in pyindexrepo/main.py
def search_material_by_n(
    self,
    n: float,
    wl: float = 0.5875618,
    filter_shelf: str | None = None,
    filter_book: str | None = None,
) -> list[Material]:
    """Search Material by refractive index

    Look for a material with a specific refractive index at a certain wavelength.
    In return, you get a sorted list of materials with index [0] being the closest to input n.

    Args:
        n: refractive index
        wl: wavelength
        filter_shelf: if given, only materials containing this string in their shelf name are considered
        filter_book: if given, only materials containing this string in their book name are considered

    Examples:
        >>> db = RefractiveIndexLibrary()
        >>> # get 3 closest OHARA glasses with n=1.5 at 0.55microns:
        >>> materials = db.search_material_by_n(1.5, wl=0.55, filter_book="ohara")[:3]
        >>> print(materials[0].yaml_data.name, materials[0].get_n(0.55))
        BSL3 1.4999474387027893
        >>> print(materials[1].yaml_data.name, materials[1].get_n(0.55))
        S-FPL51Y 1.498313496038896
        >>> print(materials[2].yaml_data.name, materials[2].get_n(0.55))
        S-FPL51 1.498303051383454

    Returns:
        sorted list of materials matching search criteria

    """
    materials = []
    materials_n_distance = []
    for shelf_m, d in self.materials_dict.items():
        if not (shelf_m == filter_shelf or filter_shelf is None):
            continue
        for book_name, book_m in d.items():
            if filter_book is not None:
                if filter_book.lower() not in book_name.lower():
                    continue

            for mat in book_m.values():
                materials.append(mat)
                try:
                    materials_n_distance.append(abs(mat.get_n(wl) - n))
                except ValueError:
                    materials_n_distance.append(99)

    return [
        x
        for _, x in sorted(
            zip(materials_n_distance, materials), key=lambda pair: pair[0]
        )
    ]

search_material_by_page_name(page_name, exact_match=False)

Search Material by name

Search a Material by page name as given at refractiveindex.info. Sometimes, the name is not unique, so the function returns either a single Material or a list of Materials or None if it doesn't find a match.

Parameters:
  • page_name (str) –

    name of the material as given as page name on refractiveindex.info

  • exact_match (bool, default: False ) –

    if True, only exact matches are considered. Default is False. e.g. if False,

Returns:
  • Material | list[Material] | None

    Material or list of Materials matching the Name

Examples:

>>> db = RefractiveIndexLibrary()
>>> bk7 = db.search_material_by_page_name('N-BK7')[0]  # returns a list of different BK7 glasses
>>> print(bk7.get_n(0.5875618))
1.5168000345005885
Source code in pyindexrepo/main.py
def search_material_by_page_name(
    self, page_name: str, exact_match: bool = False
) -> Material | list[Material] | None:
    """Search Material by name

    Search a Material by page name as given at refractiveindex.info.
    Sometimes, the name is not unique, so the function returns either a single Material or a list of Materials
    or None if it doesn't find a match.

    Args:
        page_name: name of the material as given as page name on refractiveindex.info
        exact_match: if True, only exact matches are considered. Default is False. e.g. if False,
        'BK7' will return all BK7 glasses, including N-BK7, K-BK7, etc.

    Returns:
        Material or list of Materials matching the Name

    Examples:
        >>> db = RefractiveIndexLibrary()
        >>> bk7 = db.search_material_by_page_name('N-BK7')[0]  # returns a list of different BK7 glasses
        >>> print(bk7.get_n(0.5875618))
        1.5168000345005885
    """
    materials = []
    if exact_match:
        for m in self.materials_list:
            if page_name == m.yaml_data.name:
                if isinstance(m, Material):
                    materials.append(m)
                elif isinstance(m, YAMLLibraryData):
                    materials.append(self.get_material(m.lib_shelf, m.lib_book, m.lib_page))
                else:
                    warnings.warn("Unknown material type.")
    else:
        for m in self.materials_list:
            if isinstance(m, YAMLLibraryData):
                if page_name in m.name:
                    materials.append(self.get_material(m.lib_shelf, m.lib_book, m.lib_page))
            elif isinstance(m, Material):
                if page_name in m.yaml_data.name:
                    materials.append(m)
            else:
                warnings.warn("Unknown material type.")
    return (
        materials[0]
        if len(materials) == 1
        else materials if len(materials) > 1 else None
    )

Specs dataclass

A dataclass representing material specifications.

Attributes:
  • n_is_absolute (bool) –

    Indicates whether the refractive index is given in absolute units. True if it is, False if not, and None if unspecified.

  • wavelength_is_vacuum (bool) –

    Specifies whether the wavelength is given in vacuum. True if it is, False if not, and None if unspecified.

  • thermal_dispersion (ThermalDispersion) –

    An instance of the ThermalDispersion class representing thermal dispersion information. None if unspecified.

  • nd (float) –

    The refractive index (n) of the material. None if unspecified.

  • Vd (float) –

    The Abbe number (Vd) of the material. None if unspecified.

  • glass_code (float) –

    The glass code associated with the material. None if unspecified.

  • glass_status (str) –

    The status or classification of the glass material as a string. None if unspecified.

  • density (float) –

    The density of the material. None if unspecified.

  • thermal_expansion (List[ThermalExpansion]) –

    A list of instances of the ThermalExpansion class representing thermal expansion properties. None if unspecified.

  • climatic_resistance (float) –

    The material's resistance to climatic conditions. None if unspecified.

  • stain_resistance (float) –

    The material's resistance to staining. None if unspecified.

  • acid_resistance (float) –

    The material's resistance to acids. None if unspecified.

  • alkali_resistance (float) –

    The material's resistance to alkalis. None if unspecified.

  • phosphate_resistance (float) –

    The material's resistance to phosphates. None if unspecified.

Source code in pyindexrepo/main.py
@dataclass
class Specs:
    """
    A dataclass representing material specifications.

    Attributes:
        n_is_absolute (bool, optional): Indicates whether the refractive index is given in absolute units.
            True if it is, False if not, and None if unspecified.
        wavelength_is_vacuum (bool, optional): Specifies whether the wavelength is given in vacuum.
            True if it is, False if not, and None if unspecified.

        thermal_dispersion (ThermalDispersion, optional): An instance of the ThermalDispersion class representing thermal dispersion information.
            None if unspecified.
        nd (float, optional): The refractive index (n) of the material. None if unspecified.
        Vd (float, optional): The Abbe number (Vd) of the material. None if unspecified.
        glass_code (float, optional): The glass code associated with the material. None if unspecified.
        glass_status (str, optional): The status or classification of the glass material as a string. None if unspecified.
        density (float, optional): The density of the material. None if unspecified.
        thermal_expansion (List[ThermalExpansion], optional): A list of instances of the ThermalExpansion class representing thermal expansion properties.
            None if unspecified.
        climatic_resistance (float, optional): The material's resistance to climatic conditions. None if unspecified.
        stain_resistance (float, optional): The material's resistance to staining. None if unspecified.
        acid_resistance (float, optional): The material's resistance to acids. None if unspecified.
        alkali_resistance (float, optional): The material's resistance to alkalis. None if unspecified.
        phosphate_resistance (float, optional): The material's resistance to phosphates. None if unspecified.
    """

    n_is_absolute: bool | None = None
    wavelength_is_vacuum: bool | None = None
    temperature: float | None = None
    thermal_dispersion: ThermalDispersion | None = None
    nd: float | None = None
    Vd: float | None = None
    glass_code: float | None = None
    glass_status: str | None = None
    density: float | None = None
    thermal_expansion: List[ThermalExpansion] | None = None
    climatic_resistance: float | None = None
    stain_resistance: float | None = None
    acid_resistance: float | None = None
    alkali_resistance: float | None = None
    phosphate_resistance: float | None = None

    @staticmethod
    def parse_float(value, default=None):
        """Convert value to float if possible, otherwise return default."""
        try:
            if isinstance(value, (int, float)):
                return float(value)
            elif isinstance(value, str):
                return float(value.split()[0])
        except (ValueError, AttributeError):
            pass
        return default

    @staticmethod
    def parse_coefficients(coefficients):
        """Parse coefficients string into a numpy array."""
        if not coefficients:
            return None
        try:
            return np.array([float(val) for val in coefficients.split()], dtype=float)
        except (ValueError, AttributeError):
            return None



    @staticmethod
    def parse_density(density):
        """Parse density string to float."""
        if isinstance(density, str):
            return Specs.parse_float(density.replace(" g/cm<sup>3</sup>", ""))
        return Specs.parse_float(density)

    @staticmethod
    def parse_thermal_dispersion(td_dict):
        """Parse a ThermalDispersion object from dictionary."""
        formula_type = td_dict.get("type")
        if formula_type in ["Schott formula", "formula A"]:
            coefficients = Specs.parse_coefficients(td_dict.get("coefficients"))
            return ThermalDispersion(formula_type=formula_type, coefficients=coefficients)
        elif formula_type == "dn/dT":
            value = Specs.parse_float(td_dict.get("value"))
            return ThermalDispersion(formula_type=formula_type, coefficients=value)
        else:
            warnings.warn(f"Thermal Dispersion formula {formula_type} not implemented yet.")
            return None

    @staticmethod
    def parse_temperature_range(tr_dict):
        """Parse a TemperatureRange object from dictionary."""
        if not tr_dict or "temperature_range" not in tr_dict:
            return None
        try:
            min_temp, max_temp = map(float, tr_dict["temperature_range"].split())
            return TemperatureRange(min=min_temp, max=max_temp)
        except (ValueError, AttributeError):
            return None

    @staticmethod
    def read_specs_from_yaml(specs_dict):
        """Read specs from a YAML dictionary and create a Specs object."""
        # Thermal Dispersion
        thermal_dispersion_list = [
            Specs.parse_thermal_dispersion(td)
            for td in specs_dict.get("thermal_dispersion", [])
        ]
        if len(thermal_dispersion_list) > 1:
            warnings.warn("Multiple thermal dispersion values found. Only the first will be used.")

        # Temperature Range
        temperature_range_list = [
            Specs.parse_temperature_range(tr)
            for tr in specs_dict.get("thermal_expansion", [])
        ]

        # Density
        density = Specs.parse_density(specs_dict.get("density"))

        # Thermal Expansion
        thermal_expansion = None
        if specs_dict.get("thermal_expansion"):
            thermal_expansion = [
                ThermalExpansion(
                    temperature_range=tr,
                    coefficient=Specs.parse_float(te_dict.get("coefficient") or te_dict.get("value"))
                )
                for tr, te_dict in zip(temperature_range_list, specs_dict["thermal_expansion"])
            ]

        # Create Specs object
        return Specs(
            n_is_absolute=specs_dict.get("n_is_absolute"),
            wavelength_is_vacuum=specs_dict.get("wavelength_is_vacuum"),
            thermal_dispersion=thermal_dispersion_list[0] if thermal_dispersion_list else None,
            nd=specs_dict.get("nd"),
            Vd=specs_dict.get("Vd"),
            glass_code=specs_dict.get("glass_code"),
            glass_status=specs_dict.get("glass_status"),
            density=density,
            thermal_expansion=thermal_expansion,
            climatic_resistance=specs_dict.get("climatic_resistance"),
            stain_resistance=specs_dict.get("stain_resistance"),
            acid_resistance=specs_dict.get("acid_resistance"),
            alkali_resistance=specs_dict.get("alkali_resistance"),
            phosphate_resistance=specs_dict.get("phosphate_resistance"),
        )

    def get_coefficient_of_thermal_expansion(self, temperature: float) -> float:
        """Returns the coefficient of thermal expansion for a given temperature."""
        if self.thermal_expansion is not None:
            # sort by total temperature range and return coefficient for smallest range
            self.thermal_expansion.sort(
                key=lambda exp: exp.temperature_range.max - exp.temperature_range.min
            )
            for expansion in self.thermal_expansion:
                if (
                    expansion.temperature_range.min
                    <= temperature
                    <= expansion.temperature_range.max
                ):
                    return expansion.coefficient
            # if temperature is outside any temperature range, return the value for the closest temperature range
            self.thermal_expansion.sort(
                key=lambda exp, t: min(
                    abs(t - exp.temperature_range.max),
                    abs(t - exp.temperature_range.min),
                )
            )

            warnings.warn(
                "Temperature is outside any temperature range, returning closest value as coefficient"
            )
            return self.thermal_expansion[0].coefficient
        else:
            warnings.warn("No thermal expansion data. Returning 0.0")
            return 0.0

get_coefficient_of_thermal_expansion(temperature)

Returns the coefficient of thermal expansion for a given temperature.

Source code in pyindexrepo/main.py
def get_coefficient_of_thermal_expansion(self, temperature: float) -> float:
    """Returns the coefficient of thermal expansion for a given temperature."""
    if self.thermal_expansion is not None:
        # sort by total temperature range and return coefficient for smallest range
        self.thermal_expansion.sort(
            key=lambda exp: exp.temperature_range.max - exp.temperature_range.min
        )
        for expansion in self.thermal_expansion:
            if (
                expansion.temperature_range.min
                <= temperature
                <= expansion.temperature_range.max
            ):
                return expansion.coefficient
        # if temperature is outside any temperature range, return the value for the closest temperature range
        self.thermal_expansion.sort(
            key=lambda exp, t: min(
                abs(t - exp.temperature_range.max),
                abs(t - exp.temperature_range.min),
            )
        )

        warnings.warn(
            "Temperature is outside any temperature range, returning closest value as coefficient"
        )
        return self.thermal_expansion[0].coefficient
    else:
        warnings.warn("No thermal expansion data. Returning 0.0")
        return 0.0

parse_coefficients(coefficients) staticmethod

Parse coefficients string into a numpy array.

Source code in pyindexrepo/main.py
@staticmethod
def parse_coefficients(coefficients):
    """Parse coefficients string into a numpy array."""
    if not coefficients:
        return None
    try:
        return np.array([float(val) for val in coefficients.split()], dtype=float)
    except (ValueError, AttributeError):
        return None

parse_density(density) staticmethod

Parse density string to float.

Source code in pyindexrepo/main.py
@staticmethod
def parse_density(density):
    """Parse density string to float."""
    if isinstance(density, str):
        return Specs.parse_float(density.replace(" g/cm<sup>3</sup>", ""))
    return Specs.parse_float(density)

parse_float(value, default=None) staticmethod

Convert value to float if possible, otherwise return default.

Source code in pyindexrepo/main.py
@staticmethod
def parse_float(value, default=None):
    """Convert value to float if possible, otherwise return default."""
    try:
        if isinstance(value, (int, float)):
            return float(value)
        elif isinstance(value, str):
            return float(value.split()[0])
    except (ValueError, AttributeError):
        pass
    return default

parse_temperature_range(tr_dict) staticmethod

Parse a TemperatureRange object from dictionary.

Source code in pyindexrepo/main.py
@staticmethod
def parse_temperature_range(tr_dict):
    """Parse a TemperatureRange object from dictionary."""
    if not tr_dict or "temperature_range" not in tr_dict:
        return None
    try:
        min_temp, max_temp = map(float, tr_dict["temperature_range"].split())
        return TemperatureRange(min=min_temp, max=max_temp)
    except (ValueError, AttributeError):
        return None

parse_thermal_dispersion(td_dict) staticmethod

Parse a ThermalDispersion object from dictionary.

Source code in pyindexrepo/main.py
@staticmethod
def parse_thermal_dispersion(td_dict):
    """Parse a ThermalDispersion object from dictionary."""
    formula_type = td_dict.get("type")
    if formula_type in ["Schott formula", "formula A"]:
        coefficients = Specs.parse_coefficients(td_dict.get("coefficients"))
        return ThermalDispersion(formula_type=formula_type, coefficients=coefficients)
    elif formula_type == "dn/dT":
        value = Specs.parse_float(td_dict.get("value"))
        return ThermalDispersion(formula_type=formula_type, coefficients=value)
    else:
        warnings.warn(f"Thermal Dispersion formula {formula_type} not implemented yet.")
        return None

read_specs_from_yaml(specs_dict) staticmethod

Read specs from a YAML dictionary and create a Specs object.

Source code in pyindexrepo/main.py
@staticmethod
def read_specs_from_yaml(specs_dict):
    """Read specs from a YAML dictionary and create a Specs object."""
    # Thermal Dispersion
    thermal_dispersion_list = [
        Specs.parse_thermal_dispersion(td)
        for td in specs_dict.get("thermal_dispersion", [])
    ]
    if len(thermal_dispersion_list) > 1:
        warnings.warn("Multiple thermal dispersion values found. Only the first will be used.")

    # Temperature Range
    temperature_range_list = [
        Specs.parse_temperature_range(tr)
        for tr in specs_dict.get("thermal_expansion", [])
    ]

    # Density
    density = Specs.parse_density(specs_dict.get("density"))

    # Thermal Expansion
    thermal_expansion = None
    if specs_dict.get("thermal_expansion"):
        thermal_expansion = [
            ThermalExpansion(
                temperature_range=tr,
                coefficient=Specs.parse_float(te_dict.get("coefficient") or te_dict.get("value"))
            )
            for tr, te_dict in zip(temperature_range_list, specs_dict["thermal_expansion"])
        ]

    # Create Specs object
    return Specs(
        n_is_absolute=specs_dict.get("n_is_absolute"),
        wavelength_is_vacuum=specs_dict.get("wavelength_is_vacuum"),
        thermal_dispersion=thermal_dispersion_list[0] if thermal_dispersion_list else None,
        nd=specs_dict.get("nd"),
        Vd=specs_dict.get("Vd"),
        glass_code=specs_dict.get("glass_code"),
        glass_status=specs_dict.get("glass_status"),
        density=density,
        thermal_expansion=thermal_expansion,
        climatic_resistance=specs_dict.get("climatic_resistance"),
        stain_resistance=specs_dict.get("stain_resistance"),
        acid_resistance=specs_dict.get("acid_resistance"),
        alkali_resistance=specs_dict.get("alkali_resistance"),
        phosphate_resistance=specs_dict.get("phosphate_resistance"),
    )

TabulatedIndexData dataclass

A dataclass representing tabulated index data.

Attributes:
  • wl (ndarray | list[float]) –

    An array or list containing wavelength values (in microns).

  • n_or_k (ndarray | list[float]) –

    An array or list containing refractive index (n) or extinction coefficient (k) values.

  • ip ((callable, read - only)) –

    A callable property to perform interpolation on the data.

  • interpolation_func (str) –

    Indicates what interpolation function to use. Default is 'interp1d'. Can also be 'spline'.

  • bounds_error (bool) –

    Indicates whether to raise an error for out-of-bounds queries. True to raise an error, False to suppress errors. Default is True.

Source code in pyindexrepo/main.py
@dataclass
class TabulatedIndexData:
    """
    A dataclass representing tabulated index data.

    Attributes:
        wl (np.ndarray | list[float]): An array or list containing wavelength values (in microns).
        n_or_k (np.ndarray | list[float]): An array or list containing refractive index (n) or extinction coefficient (k) values.
        ip (callable, read-only): A callable property to perform interpolation on the data.
        interpolation_func (str, optional): Indicates what interpolation function to use. Default is 'interp1d'. Can also be 'spline'.
        bounds_error (bool, optional): Indicates whether to raise an error for out-of-bounds queries.
            True to raise an error, False to suppress errors. Default is True.
    """

    wl: np.ndarray | list[float]
    n_or_k: np.ndarray | list[float]
    ip: callable = field(init=False)
    interpolation_func: str = field(default="interp1d")
    bounds_error: bool = field(default=True)

    def __post_init__(self):
        if self.interpolation_func == 'interp1d':
            self.ip = interp1d(
                np.atleast_1d(self.wl),
                np.atleast_1d(self.n_or_k),
                bounds_error=self.bounds_error,
            )
        elif self.interpolation_func == 'spline':
            self.ip = interp1d(
                np.atleast_1d(self.wl),
                np.atleast_1d(self.n_or_k),
                kind='cubic',
                bounds_error=self.bounds_error,
            )
        else:
            raise ValueError(f"Interpolation function {self.interpolation_func} not supported.")

    def get_n_or_k(self, wavelength):
        return self.ip(wavelength)

ThermalDispersion dataclass

Thermal Dispersion

Deals with thermal dispersion of material. For a given formula_type and coefficients, delta_n_abs points to a function that is called with the arguments (n_ref, wavelength, coefficients*), where n_ref is the refractive index at reference temperature, wavelength the wavelength(s) value(s) and coefficients the array of the thermal dispersion coefficients. For speed reasons, the function delta_n_abs is outsourced to the dispersion_formulas.py file and speed up by numba.

Attributes:
  • formula_type (str | None) –

    name of the formula. Supported are 'Schott formula'

  • coefficients (ndarray | float | None) –

    array with thermal dispersion coefficients (lengths depends on formula type)

  • delta_n_abs (callable) –

    function called with the arguments (n_ref, wavelength, coefficients*)

Source code in pyindexrepo/main.py
@dataclass
class ThermalDispersion:
    """Thermal Dispersion

    Deals with thermal dispersion of material. For a given formula_type and coefficients, delta_n_abs points to a
    function that is called with the arguments (n_ref, wavelength, coefficients*), where n_ref is the
    refractive index at reference temperature, wavelength the wavelength(s) value(s) and coefficients the array of the
    thermal dispersion coefficients.
    For speed reasons, the function delta_n_abs is outsourced to the dispersion_formulas.py file and speed up by numba.

    Attributes:
        formula_type: name of the formula. Supported are 'Schott formula'
        coefficients: array with thermal dispersion coefficients (lengths depends on formula type)
        delta_n_abs: function called with the arguments (n_ref, wavelength, coefficients*)

    """

    formula_type: str | None = None
    coefficients: np.ndarray | float | None = None
    delta_n_abs: callable = field(init=False)

    def __post_init__(self):
        if self.formula_type in ["Schott formula", "formula A"]:
            self.delta_n_abs = getattr(
                dispersion_formulas, "delta_absolute_temperature"
            )
        elif self.formula_type == "dn/dT":
            self.delta_n_abs = getattr(
                dispersion_formulas, "n_absolute_with_given_dndt"
            )
        else:
            logger.warning("Thermal Dispersion formula not implemented yet")

ThermalExpansion dataclass

Thermal expansion Deals with thermal expansion of the material. Attributes: temperature_range: temperature range where thermal expansion coefficient is considered valid coefficient: thermal expansion coefficient [1/K]

Source code in pyindexrepo/main.py
@dataclass
class ThermalExpansion:
    """Thermal expansion
    Deals with thermal expansion of the material.
    Attributes:
        temperature_range: temperature range where thermal expansion coefficient is considered valid
        coefficient: thermal expansion coefficient [1/K]
    """

    temperature_range: TemperatureRange
    coefficient: float

YAMLMaterialData dataclass

A dataclass representing material data in YAML format.

Attributes:
  • n_data (YAMLRefractiveIndexData) –

    An instance of YAMLRefractiveIndexData containing refractive index (n) data.

  • k_data (YAMLRefractiveIndexData) –

    An instance of YAMLRefractiveIndexData containing extinction coefficient (k) data.

  • comments (str) –

    Additional comments or notes related to the material data. Default is an empty string, indicating no comments.

  • references (str) –

    References or sources of information for the material data. Default is an empty string, indicating no references.

Source code in pyindexrepo/main.py
@dataclass
class YAMLMaterialData:
    """
    A dataclass representing material data in YAML format.

    Attributes:
        n_data (YAMLRefractiveIndexData): An instance of YAMLRefractiveIndexData containing refractive index (n) data.
        k_data (YAMLRefractiveIndexData): An instance of YAMLRefractiveIndexData containing extinction coefficient (k) data.
        comments (str, optional): Additional comments or notes related to the material data.
            Default is an empty string, indicating no comments.
        references (str, optional): References or sources of information for the material data.
            Default is an empty string, indicating no references.
    """

    n_data: YAMLRefractiveIndexData
    k_data: YAMLRefractiveIndexData
    comments: str = field(default="")
    references: str = field(default="")

YAMLRefractiveIndexData dataclass

A dataclass representing refractive index data in YAML format.

Attributes:
  • data_type (str) –

    The type of refractive index data, such as 'tabulated', 'formula', etc.

  • wavelength_range (str) –

    The range of wavelengths for which the data is valid. Default is an empty string, indicating unspecified range.

  • coefficients (str) –

    A string containing coefficients or formula details for formula-based data. Default is an empty string, indicating unspecified coefficients.

  • data (str) –

    A string containing the actual data in YAML format. Default is an empty string, indicating unspecified data.

Source code in pyindexrepo/main.py
@dataclass
class YAMLRefractiveIndexData:
    """
    A dataclass representing refractive index data in YAML format.

    Attributes:
        data_type (str): The type of refractive index data, such as 'tabulated', 'formula', etc.
        wavelength_range (str, optional): The range of wavelengths for which the data is valid.
            Default is an empty string, indicating unspecified range.
        coefficients (str, optional): A string containing coefficients or formula details for formula-based data.
            Default is an empty string, indicating unspecified coefficients.
        data (str, optional): A string containing the actual data in YAML format.
            Default is an empty string, indicating unspecified data.
    """

    data_type: str
    wavelength_range: str = field(default="")
    coefficients: str = field(default="")
    data: str = field(default="")

load_material(m, path_to_library)

Helper function to load a material.

Source code in pyindexrepo/main.py
def load_material(m, path_to_library):
    """Helper function to load a material."""
    mat = yaml_to_material(
        path_to_library.parent.joinpath("data").joinpath(m.lib_data),
        m.lib_shelf, m.lib_book, m.lib_page, m.name
    )
    if mat:
        return m.lib_shelf, m.lib_book, m.lib_page, mat
    return None

yaml_to_material(filepath, lib_shelf, lib_book, lib_page, lib_name)

Converts RefractiveIndex.info YAML to Material

Reads a yaml file of the refractiveindex database and converts it to a Material object.

Parameters:
  • filepath (str | Path) –

    path to yaml file

  • lib_shelf (str) –

    RefractiveIndex.info shelf name

  • lib_book (str) –

    RefractiveIndex.info book name

  • lib_page (str) –

    RefractiveIndex.info page name

  • lib_name (str) –

    RefractiveIndex.info material name

Returns:
Source code in pyindexrepo/main.py
def yaml_to_material(filepath: str | Path, lib_shelf: str, lib_book: str, lib_page: str,
                     lib_name: str) -> Material | None:
    """Converts RefractiveIndex.info YAML to Material

Reads a yaml file of the refractiveindex database and converts it to a Material object.

Args:
    filepath: path to yaml file
    lib_shelf: RefractiveIndex.info shelf name
    lib_book: RefractiveIndex.info book name
    lib_page: RefractiveIndex.info page name
    lib_name: RefractiveIndex.info material name

Returns:
    Material object
"""
    filepath = Path(filepath) if isinstance(filepath, str) else filepath
    def fill_variables_from_data_dict(data):
        """Helper function to split data in yaml into Material attributes"""
        _wl_min = _wl_max = _wl = _n = _k = _coefficients = _formula = None
        data_type = data["type"]

        if "tabulated" in data_type:
            # Load tabulated data
            raw_data = np.loadtxt(data["data"].split("\n"))

            # Ensure data is 2D even for a single row
            if raw_data.ndim == 1:
                raw_data = raw_data[np.newaxis, :]  # Convert 1D array to 2D

            _wl = raw_data[:, 0]  # Wavelength is always the first column
            if "nk" in data_type:
                _n, _k = raw_data[:, 1], raw_data[:, 2]  # n and k values
            elif "n" in data_type:
                _n = raw_data[:, 1]  # Only n values
            elif "k" in data_type:
                _k = raw_data[:, 1]  # Only k values

            _wl_min, _wl_max = np.min(_wl), np.max(_wl)
        elif "formula" in data_type:
            _wl_range = data.get("wavelength_range") or data.get("range")
            try:
                _wl_min, _wl_max = [float(w) for w in _wl_range.split()]
            except ValueError:
                _wl_min, _wl_max = (None, None)
            _coefficients = np.array([float(c) for c in data["coefficients"].split()])
            _formula = data_type.split()[1]
        return _wl, _wl_min, _wl_max, _n, _k, _coefficients, _formula

    try:
        with open(filepath, encoding='utf-8') as f:
            yaml_content = f.read()  # Read the file once
            # yaml_parser = YAML()
            d = yaml.safe_load(yaml_content)  # Parse the YAML content

            data_blocks = d.get("DATA", [])
            specs = Specs.read_specs_from_yaml(d.get("PROPERTIES", None)) if "PROPERTIES" in d else None
            conditions = Conditions.read_conditions_from_yaml(d.get("CONDITIONS", None)) if "CONDITIONS" in d else None

            n_class, k_class = None, None
            if data_blocks:
                if isinstance(data_blocks, list):
                    n_data = data_blocks[0]
                    wl_n, wl_min_n, wl_max_n, n, k, coefficients, formula = fill_variables_from_data_dict(n_data)
                    if formula:
                        n_class = FormulaIndexData(getattr(dispersion_formulas, f"formula_{formula}"), coefficients,
                                                   wl_min_n, wl_max_n)
                    elif n is not None:
                        n_class = TabulatedIndexData(wl_n, n, 'interp1d', True)
                        k_class = TabulatedIndexData(wl_n, k, 'interp1d', True) if k is not None else None

                    k_data = next((item for item in data_blocks[1:] if item), {"type": ""})
                    wl_k, wl_min_k, wl_max_k, _, k, coefficients_k, formula_k = fill_variables_from_data_dict(k_data)
                    if formula_k:
                        k_class = FormulaIndexData(getattr(dispersion_formulas, f"formula_{formula_k}"), coefficients_k,
                                                   wl_min_k, wl_max_k)
                    elif k is not None:
                        k_class = TabulatedIndexData(wl_k, k, 'interp1d', True)
                else:
                    raise NotImplementedError("Dict data blocks not implemented yet.")

            return Material(
                n_class,
                k_class,
                specs,
                conditions,
                YAMLLibraryData(lib_name, yaml_content, lib_shelf, lib_book, lib_page, filepath),
            )
    except Exception as e:
        logger.warning(f"Could not convert/load data in {filepath}: {e}")
        return None

absolute_to_relative(n_abs, wl, T=20.0, P=0.10133)

Converts absolute refractive index to relative

Formula (5) of Schott TIE-19

Parameters:
  • n_abs (float | ndarray) –

    absolute refractive index.

  • wl (float | ndarray) –

    wavelength [micron]

  • T (float, default: 20.0 ) –

    Temperature [°Celsius] of absolute data

  • P (float, default: 0.10133 ) –

    Pressure [MPa]

Returns:

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def absolute_to_relative(
    n_abs: float | np.ndarray,
    wl: float | np.ndarray,
    T: float = 20.0,
    P: float = 0.10133,
):
    """Converts absolute refractive index to relative

    Formula (5) of [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads)

    Args:
        n_abs: absolute refractive index.
        wl: wavelength [micron]
        T: Temperature [°Celsius] of absolute data
        P: Pressure [MPa]

    Returns:

    """
    return n_abs / n_air(wl, T, P)

delta_absolute_temperature(n_abs_ref, wl, dT, D0, D1, D2, E0, E1, w_tk)

deltaT of absolute refractive index at certain Temperature

Returns the temperature coefficient of the absolute refractive index for given wavelength and temperature Formula (3) of Schott TIE-19

Parameters:
  • n_abs_ref (float | ndarray) –

    absolute refractive index at reference temperature

  • wl (float | ndarray) –

    wavelength in vacuum [micron]

  • dT (float) –

    temperature difference between reference and actual temperature

  • D0 (float) –

    constant depending on material

  • D1 (float) –

    constant depending on material

  • D2 (float) –

    constant depending on material

  • E0 (float) –

    constant depending on material

  • E1 (float) –

    constant depending on material

  • w_tk (float) –

    constant depending on material

Returns:

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def delta_absolute_temperature(
    n_abs_ref: float | np.ndarray,
    wl: float | np.ndarray,
    dT: float,
    D0: float,
    D1: float,
    D2: float,
    E0: float,
    E1: float,
    w_tk: float,
):
    """deltaT of absolute refractive index at certain Temperature

    Returns the temperature coefficient of the absolute refractive index for given wavelength and temperature
    Formula (3) of [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads)

    Args:
        n_abs_ref: absolute refractive index at reference temperature
        wl: wavelength in vacuum [micron]
        dT: temperature difference between reference and actual temperature
        D0: constant depending on material
        D1: constant depending on material
        D2: constant depending on material
        E0: constant depending on material
        E1: constant depending on material
        w_tk: constant depending on material

    Returns:

    """
    return (
        (n_abs_ref**2 - 1.0)
        / (2.0 * n_abs_ref)
        * (
            D0 * dT
            + D1 * dT**2
            + D2 * dT**3
            + (E0 * dT + E1 * dT**2) / (wl**2 - w_tk**2)
        )
    )

dn_absolute_temperature(n_abs_ref, wl, dT, D0, D1, D2, E0, E1, w_tk)

dn/dT of absolute refractive index at certain Temperature

Returns the temperature coefficient of the absolute refractive index for given wavelength and temperature Formula (2) of Schott TIE-19

Parameters:
  • n_abs_ref (float | ndarray) –

    absolute refractive index at reference temperature

  • wl (float | ndarray) –

    wavelength in vacuum [micron]

  • dT (float) –

    temperature difference between reference and actual temperature

  • D0 (float) –

    constant depending on material

  • D1 (float) –

    constant depending on material

  • D2 (float) –

    constant depending on material

  • E0 (float) –

    constant depending on material

  • E1 (float) –

    constant depending on material

  • w_tk (float) –

    constant depending on material

Returns:

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def dn_absolute_temperature(
    n_abs_ref: float | np.ndarray,
    wl: float | np.ndarray,
    dT: float,
    D0: float,
    D1: float,
    D2: float,
    E0: float,
    E1: float,
    w_tk: float,
):
    """dn/dT of absolute refractive index at certain Temperature

    Returns the temperature coefficient of the absolute refractive index for given wavelength and temperature
    Formula (2) of [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads)

    Args:
        n_abs_ref: absolute refractive index at reference temperature
        wl: wavelength in vacuum [micron]
        dT: temperature difference between reference and actual temperature
        D0: constant depending on material
        D1: constant depending on material
        D2: constant depending on material
        E0: constant depending on material
        E1: constant depending on material
        w_tk: constant depending on material

    Returns:

    """
    return (
        (n_abs_ref**2 - 1.0)
        / (2.0 * n_abs_ref)
        * (
            D0
            + 2.0 * D1 * dT
            + 3 * D2 * dT**2
            + ((E0 + 2 * E1 * dT) / (wl**2 - w_tk**2))
        )
    )

dn_dt_air(wl, T, P)

Temperature coefficient dn/dT of air

Calculates the temperature dependence of the refractive index of air as given in Schott TIE-19 - Formula (10):

Parameters:
  • wl (float | ndarray) –

    wavelength

  • T (float) –

    air temperature [°Celsius]

  • P (float) –

    air pressure [MPa]

Returns:
  • dn/dT of air

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def dn_dt_air(wl: float | np.ndarray, T: float, P: float):
    """Temperature coefficient dn/dT of air

    Calculates the temperature dependence of the refractive index of air as given in
    [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads) - Formula (10):

    Args:
        wl: wavelength
        T: air temperature [°Celsius]
        P: air pressure [MPa]

    Returns:
        dn/dT of air
    """
    return -0.00367 * (n_air(wl, T, P) - 1.0) / (1.0 + 0.00367 * T)

formula_1(wavelength, coefficients)

Sellmeier (preferred) dispersion formula

The formula has the general form:

\[ {n}^{2}-1={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}^{2}}+\frac{{C}_{4}{\lambda }^{2}}{{\lambda }^{2}-{C}_{5}^{2}}+\frac{{C}_{6}{\lambda }^{2}}{{\lambda }^{2}-{C}_{7}^{2}}+\frac{{C}_{8}{\lambda }^{2}}{{\lambda }^{2}-{C}_{9}^{2}}+\frac{{C}_{10}{\lambda }^{2}}{{\lambda }^{2}-{C}_{11}^{2}}+\frac{{C}_{12}{\lambda }^{2}}{{\lambda }^{2}-{C}_{13}^{2}}+\frac{{C}_{14}{\lambda }^{2}}{{\lambda }^{2}-{C}_{15}^{2}}+\frac{{C}_{16}{\lambda }^{2}}{{\lambda }^{2}-{C}_{17}^{2}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_1(wavelength, coefficients):
    r"""Sellmeier (preferred) dispersion formula

    The formula has the general form:


    $$
    {n}^{2}-1={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}^{2}}+\frac{{C}_{4}{\lambda }^{2}}{{\lambda }^{2}-{C}_{5}^{2}}+\frac{{C}_{6}{\lambda }^{2}}{{\lambda }^{2}-{C}_{7}^{2}}+\frac{{C}_{8}{\lambda }^{2}}{{\lambda }^{2}-{C}_{9}^{2}}+\frac{{C}_{10}{\lambda }^{2}}{{\lambda }^{2}-{C}_{11}^{2}}+\frac{{C}_{12}{\lambda }^{2}}{{\lambda }^{2}-{C}_{13}^{2}}+\frac{{C}_{14}{\lambda }^{2}}{{\lambda }^{2}-{C}_{15}^{2}}+\frac{{C}_{16}{\lambda }^{2}}{{\lambda }^{2}-{C}_{17}^{2}}
    $$


    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    nsq = np.ones_like(wavelength) + coefficients[0]
    for i in range(1, len(coefficients), 2):
        nsq = nsq + formula_1_helper(wavelength, coefficients[i], coefficients[i + 1])
    return np.sqrt(nsq)

formula_2(wavelength, coefficients)

Sellmeier-2 dispersion formula.

The formula has the general form:

\[ {n}^{2}-1={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}}+\frac{{C}_{4}{\lambda }^{2}}{{\lambda }^{2}-{C}_{5}}+\frac{{C}_{6}{\lambda }^{2}}{{\lambda }^{2}-{C}_{7}}+\frac{{C}_{8}{\lambda }^{2}}{{\lambda }^{2}-{C}_{9}}+\frac{{C}_{10}{\lambda }^{2}}{{\lambda }^{2}-{C}_{11}}+\frac{{C}_{12}{\lambda }^{2}}{{\lambda }^{2}-{C}_{13}}+\frac{{C}_{14}{\lambda }^{2}}{{\lambda }^{2}-{C}_{15}}+\frac{{C}_{16}{\lambda }^{2}}{{\lambda }^{2}-{C}_{17}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_2(wavelength, coefficients):
    r"""Sellmeier-2 dispersion formula.

    The formula has the general form:

    $$
    {n}^{2}-1={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}}+\frac{{C}_{4}{\lambda }^{2}}{{\lambda }^{2}-{C}_{5}}+\frac{{C}_{6}{\lambda }^{2}}{{\lambda }^{2}-{C}_{7}}+\frac{{C}_{8}{\lambda }^{2}}{{\lambda }^{2}-{C}_{9}}+\frac{{C}_{10}{\lambda }^{2}}{{\lambda }^{2}-{C}_{11}}+\frac{{C}_{12}{\lambda }^{2}}{{\lambda }^{2}-{C}_{13}}+\frac{{C}_{14}{\lambda }^{2}}{{\lambda }^{2}-{C}_{15}}+\frac{{C}_{16}{\lambda }^{2}}{{\lambda }^{2}-{C}_{17}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    nsq = np.ones_like(wavelength) + coefficients[0]
    for i in range(1, len(coefficients), 2):
        nsq = nsq + formula_2_helper(wavelength, coefficients[i], coefficients[i + 1])
    return np.sqrt(nsq)

formula_3(wavelength, coefficients)

Polynomial dispersion formula.

The formula has the general form:

\[ {n}^{2}={C}_{1}+{C}_{2}{\lambda }^{{C}_{3}}+{C}_{4}{\lambda }^{{C}_{5}}+{C}_{6}{\lambda }^{{C}_{7}}+{C}_{8}{\lambda }^{{C}_{9}}+{C}_{10}{\lambda }^{{C}_{11}}+{C}_{12}{\lambda }^{{C}_{13}}+{C}_{14}{\lambda }^{{C}_{15}}+{C}_{16}{\lambda }^{{C}_{17}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_3(wavelength, coefficients):
    r"""Polynomial dispersion formula.

    The formula has the general form:

    $$
    {n}^{2}={C}_{1}+{C}_{2}{\lambda }^{{C}_{3}}+{C}_{4}{\lambda }^{{C}_{5}}+{C}_{6}{\lambda }^{{C}_{7}}+{C}_{8}{\lambda }^{{C}_{9}}+{C}_{10}{\lambda }^{{C}_{11}}+{C}_{12}{\lambda }^{{C}_{13}}+{C}_{14}{\lambda }^{{C}_{15}}+{C}_{16}{\lambda }^{{C}_{17}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    nsq = np.ones_like(wavelength) * coefficients[0]
    for i in range(1, len(coefficients), 2):
        nsq = nsq + formula_3457_helper(
            wavelength, coefficients[i], coefficients[i + 1]
        )
    return np.sqrt(nsq)

formula_4(wavelength, coefficients)

RefractiveIndex.INFO dispersion formula

The formula has the general form:

\[ {n}^{2}={C}_{1}+\frac{{C}_{2}{\lambda }^{{C}_{3}}}{{\lambda }^{2}-{C}_{4}^{{C}_{5}}}+\frac{{C}_{6}{\lambda }^{{C}_{7}}}{{\lambda }^{2}-{C}_{8}^{{C}_{9}}}+{C}_{10}{\lambda }^{{C}_{11}}+{C}_{12}{\lambda }^{{C}_{13}}+{C}_{14}{\lambda }^{{C}_{15}}+{C}_{16}{\lambda }^{{C}_{17}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_4(wavelength, coefficients):
    r"""RefractiveIndex.INFO dispersion formula

    The formula has the general form:

    $$
    {n}^{2}={C}_{1}+\frac{{C}_{2}{\lambda }^{{C}_{3}}}{{\lambda }^{2}-{C}_{4}^{{C}_{5}}}+\frac{{C}_{6}{\lambda }^{{C}_{7}}}{{\lambda }^{2}-{C}_{8}^{{C}_{9}}}+{C}_{10}{\lambda }^{{C}_{11}}+{C}_{12}{\lambda }^{{C}_{13}}+{C}_{14}{\lambda }^{{C}_{15}}+{C}_{16}{\lambda }^{{C}_{17}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    nsq = np.zeros_like(wavelength) + coefficients[0]
    for i in range(1, min(9, len(coefficients)), 4):
        nsq = nsq + formula_4_helper1(
            wavelength,
            coefficients[i],
            coefficients[i + 1],
            coefficients[i + 2],
            coefficients[i + 3],
        )
    if len(coefficients) > 8:
        for i in range(9, len(coefficients), 2):
            nsq = nsq + formula_3457_helper(
                wavelength, coefficients[i], coefficients[i + 1]
            )
    return np.sqrt(nsq)

formula_5(wavelength, coefficients)

Cauchy dispersion formula

The formula has the general form:

\[ n={C}_{1}+{C}_{2}{\lambda }^{{C}_{3}}+{C}_{4}{\lambda }^{{C}_{5}}+{C}_{6}{\lambda }^{{C}_{7}}+{C}_{8}{\lambda }^{{C}_{9}}+{C}_{10}{\lambda }^{{C}_{11}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_5(wavelength, coefficients):
    r"""Cauchy dispersion formula

    The formula has the general form:

    $$
    n={C}_{1}+{C}_{2}{\lambda }^{{C}_{3}}+{C}_{4}{\lambda }^{{C}_{5}}+{C}_{6}{\lambda }^{{C}_{7}}+{C}_{8}{\lambda }^{{C}_{9}}+{C}_{10}{\lambda }^{{C}_{11}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    n = np.zeros_like(wavelength) + coefficients[0]
    for i in range(1, len(coefficients), 2):
        n = n + formula_3457_helper(wavelength, coefficients[i], coefficients[i + 1])
    return n

formula_6(wavelength, coefficients)

Gases dispersion formula

The formula has the general form:

\[ n-1={C}_{1}+\frac{{C}_{2}}{{C}_{3}-{\lambda }^{-2}}+\frac{{C}_{4}}{{C}_{5}-{\lambda }^{-2}}+\frac{{C}_{6}}{{C}_{7}-{\lambda }^{-2}}+\frac{{C}_{8}}{{C}_{9}-{\lambda }^{-2}}+\frac{{C}_{10}}{{C}_{11}-{\lambda }^{-2}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_6(wavelength, coefficients):
    r"""Gases dispersion formula

    The formula has the general form:

    $$
    n-1={C}_{1}+\frac{{C}_{2}}{{C}_{3}-{\lambda }^{-2}}+\frac{{C}_{4}}{{C}_{5}-{\lambda }^{-2}}+\frac{{C}_{6}}{{C}_{7}-{\lambda }^{-2}}+\frac{{C}_{8}}{{C}_{9}-{\lambda }^{-2}}+\frac{{C}_{10}}{{C}_{11}-{\lambda }^{-2}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients

    """
    n = np.ones_like(wavelength) + coefficients[0]
    for i in range(1, len(coefficients), 2):
        n = n + formula_6_helper(wavelength, coefficients[i], coefficients[i + 1])
    return n

formula_7(wavelength, coefficients)

Herzberger dispersion formula

The formula has the general form:

\[ n={C}_{1}+\frac{{C}_{2}}{{\lambda }^{2}-0.028}+{C}_{3}{\left(\frac{1}{{\lambda }^{2}-0.028}\right)}^{2}+{C}_{4}{\lambda }^{2}+{C}_{5}{\lambda }^{4}+{C}_{6}{\lambda }^{6} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_7(wavelength, coefficients):
    r"""Herzberger dispersion formula

    The formula has the general form:

    $$
    n={C}_{1}+\frac{{C}_{2}}{{\lambda }^{2}-0.028}+{C}_{3}{\left(\frac{1}{{\lambda }^{2}-0.028}\right)}^{2}+{C}_{4}{\lambda }^{2}+{C}_{5}{\lambda }^{4}+{C}_{6}{\lambda }^{6}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    n = np.zeros_like(wavelength) + coefficients[0]
    n = n + formula_7_helper1(wavelength, coefficients[1], 1)
    n = n + formula_7_helper1(wavelength, coefficients[2], 2)
    for i in range(3, len(coefficients)):
        n = n + formula_3457_helper(wavelength, coefficients[i], float64(2 * (i - 2)))
    return n

formula_8(wavelength, coefficients)

Formula 8 dispersion formula from refractiveindex.info

The formula has the general form:

\[ \frac{{n}^{2}-1}{{n}^{2}+2}={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}}+{C}_{4}{\lambda }^{2} \]
Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_8(wavelength, coefficients):
    r"""Formula 8 dispersion formula from refractiveindex.info

    The formula has the general form:

    $$
    \frac{{n}^{2}-1}{{n}^{2}+2}={C}_{1}+\frac{{C}_{2}{\lambda }^{2}}{{\lambda }^{2}-{C}_{3}}+{C}_{4}{\lambda }^{2}
    $$

    """
    return np.sqrt(
        (
            wavelength**2
            * (
                2 * coefficients[0]
                + 2 * coefficients[1]
                + 2 * coefficients[3] * wavelength**2
                + 1
            )
            - coefficients[2]
            * (2 * coefficients[0] + 2 * coefficients[3] * wavelength**2 + 1)
        )
        / (
            coefficients[2] * (coefficients[0] + coefficients[3] * wavelength**2 - 1)
            - wavelength**2
            * (coefficients[0] + coefficients[1] + coefficients[3] * wavelength ** 2 - 1)
        )
    )

formula_9(wavelength, coefficients)

Formula 9 dispersion formula from refractiveindex.info

The formula has the general form:

\[ {n}^{2}={C}_{1}+\frac{{C}_{2}}{{\lambda }^{2}-{C}_{3}}+\frac{{C}_{4}(\lambda -{C}_{5})}{{(\lambda -{C}_{5})}^{2}+{C}_{6}} \]
Parameters:
  • wavelength

    wavelength

  • coefficients

    list of coefficients

Source code in pyindexrepo/dispersion_formulas.py
@njit(
    [
        float64(float64, float64[:]),
        float64[:](float64[:], float64[:]),
    ],
    cache=True,
)
def formula_9(wavelength, coefficients):
    r"""Formula 9 dispersion formula from refractiveindex.info

    The formula has the general form:

    $$
    {n}^{2}={C}_{1}+\frac{{C}_{2}}{{\lambda }^{2}-{C}_{3}}+\frac{{C}_{4}(\lambda -{C}_{5})}{{(\lambda -{C}_{5})}^{2}+{C}_{6}}
    $$

    Args:
        wavelength: wavelength
        coefficients: list of coefficients
    """
    return np.sqrt(
        coefficients[0]
        + coefficients[1] / (wavelength**2 - coefficients[2])
        + coefficients[3]
        * (wavelength - coefficients[4])
        / ((wavelength - coefficients[4]) ** 2 + coefficients[5])
    )

n_absolute_with_given_dndt(n_rel, wl, dT, coefficient)

Calculates absolute refractive index with given dn/dT

Formula (6) of Schott TIE-19

Parameters:
  • n_rel (float | ndarray) –

    relative refractive index

  • wl (float | ndarray) –

    wavelength [micron]

  • dT (float) –

    temperature difference [°Celsius]

  • coefficient (float) –

    dn/dT

Returns:

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def n_absolute_with_given_dndt(
        n_rel: float | np.ndarray, wl: float | np.ndarray, dT: float, coefficient: float
):
    """Calculates absolute refractive index with given dn/dT

    Formula (6) of [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads)

    Args:
        n_rel: relative refractive index
        wl: wavelength [micron]
        dT: temperature difference [°Celsius]
        coefficient: dn/dT

    Returns:

    """
    return n_rel + coefficient * dT

n_air(wl, T=20.0, P=0.10133)

Refractive index of air

Calculates the refractive index of air as described in Schott TIE-19 - Formula (8):

Parameters:
  • wl (float | ndarray) –

    wavelength

  • T (float, default: 20.0 ) –

    air temperature [°Celsius]

  • P (float, default: 0.10133 ) –

    air pressure [MPa]

Returns:
  • refractive index of air

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def n_air(wl: float | np.ndarray, T: float = 20.0, P: float = 0.10133):
    """Refractive index of air

    Calculates the refractive index of air as described in
    [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads) - Formula (8):

    Args:
        wl: wavelength
        T: air temperature [°Celsius]
        P: air pressure [MPa]

    Returns:
        refractive index of air
    """
    n_air_ref = 1.0 + 1e-8 * (
        6432.8
        + (2949810.0 * wl**2) / (146.0 * wl**2 - 1.0)
        + (25540.0 * wl**2) / (41.0 * wl**2 - 1.0)
    )
    return 1.0 + (n_air_ref - 1.0) / (1.0 + 3.4785e-3 * (T - 15.0)) * (P / 0.10133)

relative_to_absolute(n_rel, wl, T=20.0, P=0.10133)

Converts relative refractive index to absolute

Reverse of Formula (5) of Schott TIE-19

Parameters:
  • n_rel (float | ndarray) –

    relative refractive index

  • wl (float | ndarray) –

    wavelength [micron]

  • T (float, default: 20.0 ) –

    Temperature [°Celsius]

  • P (float, default: 0.10133 ) –

    Pressure [MPa]

Returns:

Source code in pyindexrepo/dispersion_formulas.py
@njit(cache=True)
def relative_to_absolute(
    n_rel: float | np.ndarray,
    wl: float | np.ndarray,
    T: float = 20.0,
    P: float = 0.10133,
):
    """Converts relative refractive index to absolute

    Reverse of Formula (5) of [Schott TIE-19](https://www.schott.com/en-gb/products/optical-glass-p1000267/downloads)

    Args:
        n_rel: relative refractive index
        wl: wavelength [micron]
        T: Temperature [°Celsius]
        P: Pressure [MPa]

    Returns:

    """
    return n_rel * n_air(wl, T, P)