Skip to content

Materials

Real materials have a wavelength-dependent complex refractive index — a real part n (dispersion) and an imaginary part k (absorption). The Material class supplies these to the solvers, resolving material names against bundled catalogs.

You rarely construct Material objects by hand: every solver auto-wraps the values you pass in mat_in, mat_out, and mat_ls. You can freely mix:

  • scalars1.5, 2.10 (constant, lossless index)
  • complex2.40 + 0.001j (constant index with loss)
  • names"air", "N-BK7" (Sellmeier glass), "SiO2", "TiO2", "Ag" (thin-film n+k tables), resolved case-insensitively
from difftmm import IsotropicFilmSolver, Material, list_materials

print(len(list_materials()), "materials available")

solver = IsotropicFilmSolver(
    mat_in="air",
    mat_out="N-BK7",                 # Sellmeier dispersion (AGF catalog)
    mat_ls=["TiO2", "SiO2"],         # n+k tables for thin-film materials
    thickness_ls=[0.06, 0.10],
)

Two dispersion models are supported: Sellmeier (analytical, real n, from the bundled CDGM / SCHOTT / MISC / PLASTIC AGF glass catalogs) and linear interpolation (complex n + ik lookup tables, for thin-film materials). For the 4×4 anisotropic solver, per-axis dispersion is expressed as a (mat_x, mat_y, mat_z) tuple per layer.

Material

difftmm.Material

Material(name: 'str | float | complex | None' = None, device: 'torch.device | str' = 'cpu')

Optical material with wavelength-dependent complex refractive index.

Attributes:

Name Type Description
name str | float | complex

Lowercase material name for catalog materials, or the original numeric value for constant-dispersion materials.

dispersion str

'sellmeier' | 'interp' | 'constant'.

n float

Nominal refractive index at d-line (587 nm).

V float

Abbe number (1e38 for non-dispersive materials).

Source code in difftmm-src/difftmm/material/materials.py
def __init__(
    self,
    name: "str | float | complex | None" = None,
    device: "torch.device | str" = "cpu",
):
    self.device = (
        torch.device(device) if not isinstance(device, torch.device) else device
    )
    if isinstance(name, (float, complex)):
        n = complex(name)
        self._const_n = n
        self.name = name
        self.dispersion = "constant"
        self.n = n.real
        self.V = 1e38
    else:
        raw = "air" if name is None else name.strip().lower()
        self.name = raw
        self._load_dispersion()

to

to(device: device | str) -> 'Material'

Move cached interpolation tensors to the given device.

Returns self for chaining.

Source code in difftmm-src/difftmm/material/materials.py
def to(self, device: torch.device | str) -> "Material":
    """Move cached interpolation tensors to the given device.

    Returns self for chaining.
    """
    device = (
        torch.device(device) if not isinstance(device, torch.device) else device
    )
    self.device = device
    if hasattr(self, "_ref_wvlns") and self._ref_wvlns is not None:
        self._ref_wvlns = self._ref_wvlns.to(device)
        self._ref_n = self._ref_n.to(device)
        if self._ref_k is not None:
            self._ref_k = self._ref_k.to(device)
    return self

ior

ior(wvln: Tensor) -> torch.Tensor

Compute the complex refractive index at given wavelengths.

The TMM solvers always require complex output — even for lossless real-valued materials — because evanescent waves (beyond critical angle) make cos(theta_layer) imaginary during propagation.

Parameters:

Name Type Description Default
wvln Tensor

real tensor of wavelengths in μm.

required

Returns: torch.complex64 tensor with the same shape as wvln.

Source code in difftmm-src/difftmm/material/materials.py
def ior(self, wvln: torch.Tensor) -> torch.Tensor:
    """Compute the complex refractive index at given wavelengths.

    The TMM solvers always require complex output — even for lossless
    real-valued materials — because evanescent waves (beyond critical angle)
    make cos(theta_layer) imaginary during propagation.

    Args:
        wvln: real tensor of wavelengths in μm.
    Returns:
        torch.complex64 tensor with the same shape as `wvln`.
    """
    if self.dispersion == "constant":
        return torch.full(
            wvln.shape, self._const_n, dtype=torch.complex64, device=wvln.device
        )

    elif self.dispersion == "sellmeier":
        wvln2 = wvln**2
        n2 = (
            1.0
            + self.k1 * wvln2 / (wvln2 - self.l1 + 1e-30)
            + self.k2 * wvln2 / (wvln2 - self.l2 + 1e-30)
            + self.k3 * wvln2 / (wvln2 - self.l3 + 1e-30)
        )
        n = torch.sqrt(torch.clamp(n2, min=1e-30))
        return (n + 0j).to(torch.complex64)

    elif self.dispersion == "interp":
        self._ref_wvlns = self._ref_wvlns.to(wvln.device)
        self._ref_n = self._ref_n.to(wvln.device)
        ref_k = self._ref_k.to(wvln.device) if self._ref_k is not None else None
        return _linear_interp_complex(wvln, self._ref_wvlns, self._ref_n, ref_k)

    raise NotImplementedError(f"Dispersion {self.dispersion!r} not implemented.")

refractive_index

refractive_index(wvln: 'float | torch.Tensor')

Return the complex refractive index.

Parameters:

Name Type Description Default
wvln 'float | torch.Tensor'

float (returns Python complex) or tensor (returns complex tensor).

required

Returns:

Type Description

Python complex when wvln is a scalar, torch.complex64 tensor otherwise.

Source code in difftmm-src/difftmm/material/materials.py
def refractive_index(self, wvln: "float | torch.Tensor"):
    """Return the complex refractive index.

    Args:
        wvln: float (returns Python complex) or tensor (returns complex tensor).

    Returns:
        Python complex when wvln is a scalar, torch.complex64 tensor otherwise.
    """
    if isinstance(wvln, (int, float)):
        t = torch.tensor([float(wvln)], device=self.device)
        return complex(self.ior(t).item())
    return self.ior(wvln)

list_materials

difftmm.list_materials

list_materials() -> list[str]

Return all known material names from all bundled catalogs (sorted).

Returns:

Type Description
list[str]

Sorted list of lowercase material name strings.

Source code in difftmm-src/difftmm/material/materials.py
def list_materials() -> list[str]:
    """Return all known material names from all bundled catalogs (sorted).

    Returns:
        Sorted list of lowercase material name strings.
    """
    names: set[str] = set()
    names.add("air")
    names.update(_AGF_DATA.keys())
    names.update(_INTERP_NK_TABLE.keys())
    return sorted(names)