# Copyright (c) 2025 Martin Pflaum
# This file is part of the diffinytrace project, licensed under the MIT License.
__all__ = [
"RefractiveIndex",
"materials"
]
import torch.nn as nn
import torch
import numpy as np
from .plotting.wavelength import PlotableWavelength
[docs]
class RefractiveIndex(nn.Module,PlotableWavelength):
r"""This class is used to calculate the refractive index of a material.
At material interfaces, the transmitted direction :math:`\mathbf{D'}` is computed based on the surface normal
:math:`\mathbf{N} = \nabla s / \|\nabla s\|` and the incident direction :math:`\mathbf{D}`, using Snell's law (see :cite:`do`):
.. math::
\mathbf{D'} = \mathbf{N} \sqrt{1 - (1 - \cos^2 \psi_i) \eta^2} + \eta (\mathbf{D} - \mathbf{N} \cos \psi_i),
where :math:`\cos \psi_i = \mathbf{D} \cdot \mathbf{N}` and :math:`\eta = n / n'` is the ratio of the refractive indices
of the two materials.
We have implemented the refractive indices as a class that is initialized by a refractive index function and the start
and end wavelengths for which the function is valid. This makes it very convenient to use with the RefractiveIndex.info
database of optical constants (see :cite:`polyanskiy2024refractiveindex`), since this database often provides Python
functions for wavelength-dependent refractive indices.
Example:
Below is an example of how to set up an optical material in our ray tracing library:
>>> import diffinytrace as dit
>>> BaSF = dit.RefractiveIndex(
>>> lambda x: (1 + 1.65554268 / (1 - 0.0104485644 / x**2) +
>>> 0.17131977 / (1 - 0.0499394756 / x**2) +
>>> 1.33664448 / (1 - 118.961472 / x**2))**0.5,
>>> [0.365, 2.5]
>>> )
>>> dit.plotting.wavelength.plot(
>>> BaSF, title="Refractive index of BaSF (Barium dense flint)"
>>> )
Args:
func (callable): A function that takes a wavelength in μm and returns the refractive index.
bounds (tuple): A tuple containing the minimum and maximum wavelength in μm.
"""
def __init__(self,func,bounds):
nn.Module.__init__(self)
PlotableWavelength.__init__(self,bounds,"n [1]")
self.func = func
self.bounds = bounds
[docs]
def forward(self,wl):
"""Calculates the refractive index for given wavelengths.
Args:
wl (torch.Tensor or float): Wavelength in μm.
Returns:
torch.Tensor: Refractive index at the given wavelengths.
"""
if not torch.is_tensor(wl):
wl = torch.tensor(wl)
vmin,vmax = self.bounds
if not (((vmin <=wl).float()*(wl<=vmax).float())==1.0).all():
print(f"The wavelength should be given in μm and between {vmin} and {vmax}. Fallback to constant val.")
out = self.func(wl)
if isinstance(out,float):
return out*torch.ones_like(wl)
if isinstance(out,np.ndarray):
out = torch.tensor(out,device=wl.device,dtype=wl.dtype)
out[vmin > wl] = self.func(vmin)
out[wl>vmax] = self.func(vmax)
if torch.is_tensor(out):
if len(out.shape) == 0:
return out*torch.ones_like(wl)
return out
"""
All material data is from https://refractiveindex.info/. Please verify the equation and ranges by ur self and the references.
"""
materials = {
"NONE": RefractiveIndex(lambda x: 1.0,(0.0,torch.inf)),
"AIR": RefractiveIndex(lambda x: 1+0.05792105/(238.0185-x**-2)+0.00167917/(57.362-x**-2),(0.23,1.69)),#P. E. Ciddor. Refractive index of air: new equations for the visible and near infrared, Appl. Optics 35, 1566-1573 (1996)
"HELIUM": RefractiveIndex(lambda x: (1+4977.77e-8/(1-28.54e-6/x**2)+1856.94e-8/(1-7.76e-3/x**2))**.5,(0.48,2.06)),#C. R. Mansfield and E. R. Peck. Dispersion of helium, J. Opt. Soc. Am. 59, 199-203 (1969)
"PMMA": RefractiveIndex(lambda x: (1+0.99654/(1-0.00787/x**2)+0.18964/(1-0.02191/x**2)+0.00411/(1-3.85727/x**2))**.5,(0.405,1.08)),#Marcin Szczurowski
"NBK7": RefractiveIndex(lambda x: (1+1.03961212/(1-0.00600069867/x**2)+0.231792344/(1-0.0200179144/x**2)+1.01046945/(1-103.560653/x**2))**.5,(0.3,2.5)),#SCHOTT
"BAF10": RefractiveIndex(lambda x: (1+1.5851495/(1-0.00926681282/x**2)+0.143559385/(1-0.0424489805/x**2)+1.08521269/(1-105.613573/x**2))**.5,(0.35,2.5)),#SCHOTT
"BAK1": RefractiveIndex(lambda x: (1+1.12365662/(1-0.00644742752/x**2)+0.309276848/(1-0.0222284402/x**2)+0.881511957/(1-107.297751/x**2))**.5,(0.3,2.5)),#SCHOTT
"FK51A": RefractiveIndex(lambda x: (1+0.971247817/(1-0.00472301995/x**2)+0.216901417/(1-0.0153575612/x**2)+0.904651666/(1-168.68133/x**2))**.5,(0.29,2.5)),#SCHOTT
"LASF9": RefractiveIndex(lambda x: (1+2.00029547/(1-0.0121426017/x**2)+0.298926886/(1-0.0538736236/x**2)+1.80691843/(1-156.530829/x**2))**.5,(0.365,2.5)),#SCHOTT
"SF5": RefractiveIndex(lambda x: (1+1.52481889/(1-0.011254756/x**2)+0.187085527/(1-0.0588995392/x**2)+1.42729015/(1-129.141675/x**2))**.5,(0.37,2.5)),#SCHOTT
"SF10": RefractiveIndex(lambda x: (1+1.62153902/(1-0.0122241457/x**2)+0.256287842/(1-0.0595736775/x**2)+1.64447552/(1-147.468793/x**2))**.5,(0.38,2.5)),#SCHOTT
"SF11": RefractiveIndex(lambda x: (1+1.73759695/(1-0.013188707/x**2)+0.313747346/(1-0.0623068142/x**2)+1.89878101/(1-155.23629/x**2))**.5,(0.37,2.5)),#SCHOTT
}