Source code for gplugins.common.utils.get_effective_indices
"""Calculate effective index for a 1D mode."""
from __future__ import annotations
from typing import Any, Literal, cast
import numpy as np
import numpy.typing as npt
from scipy.optimize import fsolve
[docs]
def get_effective_indices(
core_material: float,
nsubstrate: float,
clad_materialding: float,
thickness: float,
wavelength: float,
polarization: Literal["te", "tm"],
) -> list[float]:
"""Returns the effective refractive indices for a 1D mode.
Args:
core_material: Refractive index of the core material.
nsubstrate: Refractive index of the substrate.
clad_materialding: Refractive index of the cladding.
thickness: Thickness of the film in um.
wavelength: Wavelength in um.
polarization: Either "te" or "tm".
.. code::
----------------- |
clad_materialding inf
----------------- |
core_material thickness
----------------- |
nsubstrate inf
----------------- |
.. code::
import gplugins as sim
neffs = sim.get_effective_indices(
core_material=3.4777,
clad_materialding=1.444,
nsubstrate=1.444,
thickness=0.22,
wavelength=1.55,
polarization="te",
)
"""
epsilon_core = core_material**2
epsilon_cladding = clad_materialding**2
epsilon_substrate = nsubstrate**2
thickness *= 1e-6
wavelength *= 1e-6
if polarization == "te":
tm = False
elif polarization == "tm":
tm = True
else:
raise ValueError('Polarization must be "te" or "tm"')
k_0 = 2 * np.pi / wavelength
def k_f(e_eff: npt.NDArray[np.floating[Any]]) -> npt.NDArray[np.floating[Any]]:
return k_0 * np.sqrt(epsilon_core - e_eff) / (epsilon_core if tm else 1)
def k_s(e_eff: npt.NDArray[np.floating[Any]]) -> npt.NDArray[np.floating[Any]]:
return (
k_0 * np.sqrt(e_eff - epsilon_substrate) / (epsilon_substrate if tm else 1)
)
def k_c(e_eff: npt.NDArray[np.floating[Any]]) -> npt.NDArray[np.floating[Any]]:
return k_0 * np.sqrt(e_eff - epsilon_cladding) / (epsilon_cladding if tm else 1)
def objective(
e_eff: npt.NDArray[np.floating[Any]],
) -> npt.NDArray[np.floating[Any]]:
return 1 / np.tan(k_f(e_eff) * thickness) - (
k_f(e_eff) ** 2 - k_s(e_eff) * k_c(e_eff)
) / (k_f(e_eff) * (k_s(e_eff) + k_c(e_eff)))
# scan roughly for indices
# use a by 1e-10 smaller search area to avoid division by zero
x = np.linspace(
min(epsilon_substrate, epsilon_cladding) + 1e-10, epsilon_core - 1e-10, 1000
)
indices_temp = x[np.abs(objective(x)) < 0.1]
if not len(indices_temp):
return []
# and then use fsolve to get exact indices
indices_temp = cast(npt.NDArray[np.floating[Any]], fsolve(objective, indices_temp))
indices: list[float] = []
for index in indices_temp:
if not any(np.isclose(index, i, atol=1e-5) for i in indices):
indices.append(index)
return cast(list[float], np.sqrt(indices).tolist())
def test_effective_index() -> None:
neff = get_effective_indices(
core_material=3.4777,
clad_materialding=1.444,
nsubstrate=1.444,
thickness=0.22,
wavelength=1.55,
polarization="te",
)
assert np.isclose(neff[0], 2.8494636999424405)
if __name__ == "__main__":
print(
get_effective_indices(
core_material=3.4777,
clad_materialding=1.444,
nsubstrate=1.444,
thickness=0.22,
wavelength=1.55,
polarization="te",
)
)