thin_film.stack

Thin film optics stack class with inlined TMM.

This class encapsulates both the stack structure (incident/substrate, layers) and the numerical Transfer Matrix Method (TMM) to compute complex amplitude coefficients (r, t) and power coefficients (R, T, A) for s, p and unpolarized cases.

Corentin Nannini, 2025

Classes

ThinFilmStack(incident_material, ...)

Multilayer thin-film stack with inlined TMM calculations.

class ThinFilmStack(incident_material: BaseMaterial, substrate_material: BaseMaterial, layers: list[Layer] = <factory>, reference_wl_um: float | None = None, reference_AOI_deg: float | None = 0)[source]

Multilayer thin-film stack with inlined TMM calculations.

This class encapsulates both the stack structure (incident/substrate, layers) and the numerical Transfer Matrix Method (TMM) to compute complex amplitude coefficients (r, t) and power coefficients (R, T, A) for s, p and unpolarized cases.

Units and conventions: - Wavelength in microns (µm) internally; convenience helpers accept nm. - AOI in radians internally; convenience helpers accept degrees. - Layers are ordered from the incident side to the substrate side.

Parameters:
  • incident_material (BaseMaterial) – Incident medium (e.g., air).

  • substrate_material (BaseMaterial) – Substrate medium (e.g., glass).

  • layers (list[Layer], optional) – Ordered layers between incident and substrate. Defaults to None.

  • reference_wl_um (float | None, optional) – Reference wavelength for thickness quarter-wave calculations. Defaults to None.

  • reference_AOI_deg (float | None, optional) – Reference angle of incidence in degrees for thickness quarter-wave calculations. Defaults to 0 (normal incidence).

Examples

>>> from optiland.materials import IdealMaterial, Material
>>> from optiland.thin_film import ThinFilmStack
>>> air, glass = IdealMaterial(1.0), IdealMaterial(1.52)
>>> tf = ThinFilmStack(incident_material=air, substrate_material=glass)
>>> # 100 nm SiO2 on glass
>>> SiO2 = Material("SiO2", reference="Gao")
>>> tf.add_layer_nm(SiO2, 100.0)
>>> R = tf.reflectance_nm_deg([550.0], [0.0], polarization="s")
>>> T = tf.transmittance_nm_deg([550.0], [0.0], polarization="s")
>>> A = tf.absorptance_nm_deg([550.0], [0.0], polarization="s")
RTA(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') tuple[Any, Any, Any][source]

Return (R, T, A) for given wavelength(s) in µm and AOI(s) in radians.

RTA_nm_deg(wavelength_nm: float | Any, aoi_deg: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') tuple[Any, Any, Any][source]

Return (R, T, A) for given wavelength(s) in nm and AOI(s) in degrees.

absorptance(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]
absorptance_nm_deg(wavelength_nm: float | Any, aoi_deg: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]
add_layer(material: BaseMaterial, thickness_um: float, name: str | None = None) ThinFilmStack[source]

Append a layer to the stack.

Parameters:
  • material – Optiland material providing n(λ), k(λ).

  • thickness_um – Thickness in microns (µm).

  • name – Optional label.

Returns:

self for chaining.

add_layer_nm(material: BaseMaterial, thickness_nm: float, name: str | None = None) ThinFilmStack[source]

Append a layer, thickness in nm.

Parameters:
  • material – Optiland material providing n(λ), k(λ).

  • thickness_nm – Thickness in nanometers.

  • name – Optional label.

add_layer_qwot(material: BaseMaterial, qwot_thickness: float = 1.0, name: str | None = None) ThinFilmStack[source]

Append a quarter-wave optical thickness (QWOT) layer at the reference wavelength and angle of incidence.

Parameters:
  • material – Optiland material providing n(λ), k(λ).

  • name – Optional label.

Raises:

ValueError – If reference_wl_um is not set.

compute_rtRAT_nm_deg(wavelength_nm: float | Any, aoi_deg: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') dict[str, float | Any][source]

Same as coefficients() but inputs in nm and degrees.

compute_rtRTA(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') dict[str, Any][source]

Compute complex and power coefficients over λ×θ grids.

Parameters:
  • wavelength_um – Wavelength(s) in microns (scalar or array). Use helpers

  • nm. (for)

  • aoi_rad – Angle(s) of incidence in radians (scalar or array). Use helpers

  • degrees. (for)

  • polarization – ‘s’, ‘p’ or ‘u’ (unpolarized averages powers of s and p). default ‘u’.

Returns:

Dict with keys ‘r’,’t’,’R’,’T’,’A’. Shapes are (Nλ, Nθ).

Note: - For unpolarized ‘u’, r, t are s-polarization amplitudes; R, T, A are averaged powers.

compute_rtRTA_elementwise(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') dict[str, Any][source]

Compute complex and power coefficients element-wise (no grid).

Use this when wavelength and aoi have matching shapes (e.g. per-ray).

copy(incident: BaseMaterial | None = None, substrate: BaseMaterial | None = None)[source]

Creates a copy of the stack with optionally new surrounding materials.

deep_copy() ThinFilmStack[source]

Create a deep copy with new Layer instances (materials are shared).

Returns:

A new ThinFilmStack with independent layers.

incident_material: BaseMaterial
insert_layer(index: int, material: BaseMaterial, thickness_um: float, name: str | None = None) ThinFilmStack[source]

Insert a layer at an arbitrary position.

Parameters:
  • index – Position to insert at (0 = closest to incident).

  • material – Optiland material providing n(λ), k(λ).

  • thickness_um – Thickness in microns (µm).

  • name – Optional label.

Returns:

self for chaining.

insert_layer_nm(index: int, material: BaseMaterial, thickness_nm: float, name: str | None = None) ThinFilmStack[source]

Insert a layer at an arbitrary position, thickness in nm.

Parameters:
  • index – Position to insert at (0 = closest to incident).

  • material – Optiland material providing n(λ), k(λ).

  • thickness_nm – Thickness in nanometers.

  • name – Optional label.

Returns:

self for chaining.

layers: list[Layer]
plot_structure(ax: Axes = None) tuple[Figure, Axes][source]

Plots a schematic representation of the thin film stack structure. This method visualizes the stack as a series of colored rectangles, each representing a material layer, the substrate, and the incident medium. Each rectangle’s height corresponds to the physical thickness of the layer (in micrometers), and colors are assigned uniquely to each material. The substrate is plotted at the bottom, followed by the stack layers, and the incident medium at the top. Material names or refractive indices are used as labels in the legend.

Parameters:

ax (plt.Axes, optional) – The axes on which to plot the structure. If None, a new figure and axes are created.

Returns:

The matplotlib Figure and Axes objects

containing the plot.

Return type:

tuple[plt.Figure, plt.Axes]

plot_structure_thickness(ax: Axes = None) tuple[Figure, Axes][source]

Plots the thickness of each layer in the thin film stack as a bar chart. Each bar represents a layer, with its height corresponding to the layer’s thickness in nanometers. Bars are colored according to the material of each layer, and a legend is provided to identify materials. :param ax: The matplotlib Axes object to plot on.

If None, a new figure and axes will be created.

Returns:

The matplotlib Figure and Axes objects

containing the plot.

Return type:

tuple[plt.Figure, plt.Axes]

reference_AOI_deg: float | None = 0
reference_wl_um: float | None = None
reflectance(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]
reflectance_nm_deg(wavelength_nm: float | Any, aoi_deg: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]
remove_layer(index: int) Layer[source]

Remove and return the layer at index.

Parameters:

index – Index of the layer to remove.

Returns:

The removed Layer.

split_layer(layer_index: int, position_fraction: float) ThinFilmStack[source]

Split a layer into two layers of the same material.

The original layer at layer_index is replaced by two layers whose combined thickness equals the original. Useful for needle insertion within a layer.

Parameters:
  • layer_index – Index of the layer to split.

  • position_fraction – Fraction (0..1) at which to split. 0.3 means the first sub-layer gets 30 % of the original thickness.

Returns:

self for chaining.

substrate_material: BaseMaterial
transmittance(wavelength_um: float | Any, aoi_rad: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]
transmittance_nm_deg(wavelength_nm: float | Any, aoi_deg: float | Any = 0.0, polarization: Literal['s', 'p', 'u'] = 'u') Any[source]