Source code for materials.ideal
"""Ideal Material
This module contains the IdealMaterial class, which represents an ideal
material with a fixed refractive index and extinction coefficient for all
wavelengths.
Kramer Harrison, 2024
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import optiland.backend as be
from optiland.materials.base import BaseMaterial
if TYPE_CHECKING:
from optiland.propagation.base import BasePropagationModel
[docs]
class IdealMaterial(BaseMaterial):
"""Represents an ideal material with a fixed refractive index and extinction
coefficient for all wavelengths.
Evaluation preserves each stored parameter's dtype when the backend's default
precision changes, while converting to the active backend and device.
Attributes:
index (float): The refractive index of the material.
absorp (float): The extinction coefficient of the material.
"""
def __init__(
self,
n: float,
k: float = 0,
propagation_model: BasePropagationModel | None = None,
):
super().__init__(propagation_model)
self.index = be.array([n])
self.absorp = be.array([k])
@property
def display_name(self) -> str:
"""Show a constant index; identify air only when extinction is zero."""
if self.index.item() == 1 and self.absorp.item() == 0:
return "Air"
return str(self.index.item())
def _cache_state(self) -> tuple | None:
"""Track live index/extinction values, including in-place writes."""
return self._state_key((self.index, self.absorp))
def _calculate_n(self, wavelength, **kwargs):
"""Returns the refractive index of the material.
Args:
wavelength (float or be.ndarray): The wavelength(s) of light in microns.
This argument is not used by this material model as the index is
constant.
Returns:
float or be.ndarray: The refractive index of the material. Returns a
scalar if wavelength is scalar, otherwise an array of the same shape
as wavelength, filled with the constant refractive index.
"""
index = self._as_backend_array(self.index, preserve_dtype=True)[0]
if be.is_array_like(wavelength) and be.size(wavelength) > 1:
# Broadcast the parameter itself: multiplying by default-precision
# ones can downcast a scalar parameter and lose its precision.
return self._broadcast_like(index, wavelength)
return index
def _calculate_k(self, wavelength, **kwargs):
"""Returns the extinction coefficient of the material.
Args:
wavelength (float or be.ndarray): The wavelength(s) of light in microns.
This argument is not used by this material model as the value is
constant.
Returns:
float or be.ndarray: The extinction coefficient of the material. Returns a
scalar if wavelength is scalar, otherwise an array of the same shape
as wavelength, filled with the constant extinction coefficient.
"""
absorp = self._as_backend_array(self.absorp, preserve_dtype=True)[0]
if be.is_array_like(wavelength) and be.size(wavelength) > 1:
# A broadcast view preserves both parameter precision and gradients.
return self._broadcast_like(absorp, wavelength)
return absorp
[docs]
def to_dict(self):
"""Returns a dictionary representation of the material.
Returns:
dict: A dictionary representation of the material.
"""
material_dict = super().to_dict()
material_dict.update({"index": self.index.item(), "absorp": self.absorp.item()})
return material_dict
[docs]
@classmethod
def from_dict(cls, data):
"""Creates a material from a dictionary representation.
Args:
data (dict): The dictionary representation of the material.
Returns:
IdealMaterial: The material.
"""
return cls(data["index"], data.get("absorp", 0))