Path: blob/main/latex-templates/templates/optics/thin_film.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{article}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{siunitx}6\usepackage{booktabs}7\usepackage[makestderr]{pythontex}89\title{Optics: Thin Film Interference and Coatings}10\author{Computational Science Templates}11\date{\today}1213\begin{document}14\maketitle1516\section{Introduction}17Thin film interference creates colors in soap bubbles and enables anti-reflection coatings, high reflectors, and narrow-band filters. This analysis computes reflectance and transmittance spectra for single-layer and multi-layer thin film structures using Fresnel equations and the transfer matrix method, with applications in optical coating design.1819\section{Mathematical Framework}2021\subsection{Fresnel Equations}22At normal incidence, the amplitude reflection coefficient:23\begin{equation}24r_{ij} = \frac{n_i - n_j}{n_i + n_j}25\end{equation}2627For oblique incidence:28\begin{align}29r_s &= \frac{n_i\cos\theta_i - n_t\cos\theta_t}{n_i\cos\theta_i + n_t\cos\theta_t} \\30r_p &= \frac{n_t\cos\theta_i - n_i\cos\theta_t}{n_t\cos\theta_i + n_i\cos\theta_t}31\end{align}3233\subsection{Single Layer Reflectance}34For a single layer on a substrate:35\begin{equation}36R = \left|\frac{r_{01} + r_{12}e^{2i\delta}}{1 + r_{01}r_{12}e^{2i\delta}}\right|^237\end{equation}38where $\delta = \frac{2\pi n_1 d \cos\theta_1}{\lambda}$ is the optical phase thickness.3940\subsection{Transfer Matrix Method}41For a layer with refractive index $n$ and thickness $d$:42\begin{equation}43M = \begin{pmatrix} \cos\delta & \frac{i\sin\delta}{\eta} \\ i\eta\sin\delta & \cos\delta \end{pmatrix}44\end{equation}45where $\eta = n\cos\theta$ for s-polarization and $\eta = n/\cos\theta$ for p-polarization.4647\section{Environment Setup}48\begin{pycode}49import numpy as np50import matplotlib.pyplot as plt51plt.rc('text', usetex=True)52plt.rc('font', family='serif')5354def save_plot(filename, caption=""):55plt.savefig(filename, bbox_inches='tight', dpi=150)56print(r'\begin{figure}[htbp]')57print(r'\centering')58print(r'\includegraphics[width=0.95\textwidth]{' + filename + '}')59if caption:60print(r'\caption{' + caption + '}')61print(r'\end{figure}')62plt.close()63\end{pycode}6465\section{Single Layer Anti-Reflection Coating}66\begin{pycode}67def fresnel_r(n1, n2):68"""Fresnel reflection coefficient at normal incidence."""69return (n1 - n2) / (n1 + n2)7071def single_layer_reflectance(wavelength, d, n0, n1, n2):72"""Reflectance of single layer thin film."""73# Phase thickness74delta = 2 * np.pi * n1 * d / wavelength7576# Fresnel coefficients77r01 = fresnel_r(n0, n1)78r12 = fresnel_r(n1, n2)7980# Total reflection coefficient81r = (r01 + r12 * np.exp(2j * delta)) / (1 + r01 * r12 * np.exp(2j * delta))8283return np.abs(r)**28485# Wavelength range86wavelengths = np.linspace(400e-9, 800e-9, 500)8788# Single layer anti-reflection coating (MgF2 on glass)89n_air = 1.090n_MgF2 = 1.3891n_glass = 1.5292d_AR = 550e-9 / (4 * n_MgF2) # Quarter-wave at 550 nm9394R_AR = [single_layer_reflectance(lam, d_AR, n_air, n_MgF2, n_glass) for lam in wavelengths]9596# Uncoated glass97R_glass = fresnel_r(n_air, n_glass)**2 * np.ones_like(wavelengths)9899# Optimal coating: n_coating = sqrt(n_substrate)100n_optimal = np.sqrt(n_glass)101d_optimal = 550e-9 / (4 * n_optimal)102R_optimal = [single_layer_reflectance(lam, d_optimal, n_air, n_optimal, n_glass) for lam in wavelengths]103104fig, axes = plt.subplots(2, 2, figsize=(10, 8))105106# Plot 1: Anti-reflection coating107axes[0, 0].plot(wavelengths*1e9, np.array(R_AR)*100, 'b-', linewidth=2, label='MgF$_2$ ($n = 1.38$)')108axes[0, 0].plot(wavelengths*1e9, np.array(R_optimal)*100, 'g--', linewidth=2, label=f'Optimal ($n = {n_optimal:.2f}$)')109axes[0, 0].plot(wavelengths*1e9, R_glass*100, 'r--', linewidth=1.5, label='Uncoated')110axes[0, 0].set_xlabel('Wavelength (nm)')111axes[0, 0].set_ylabel('Reflectance (\\%)')112axes[0, 0].set_title('Single Layer Anti-Reflection Coating')113axes[0, 0].legend(fontsize=8)114axes[0, 0].grid(True, alpha=0.3)115116# Plot 2: Reflectance vs film thickness117thicknesses = np.linspace(0, 1000e-9, 200)118wavelength_plot = 550e-9119R_vs_d = [single_layer_reflectance(wavelength_plot, d, n_air, n_MgF2, n_glass) for d in thicknesses]120121axes[0, 1].plot(thicknesses*1e9, np.array(R_vs_d)*100, 'b-', linewidth=2)122axes[0, 1].axvline(x=d_AR*1e9, color='red', linestyle='--', alpha=0.7, label='QW thickness')123axes[0, 1].axhline(y=0, color='gray', linestyle='-', alpha=0.3)124axes[0, 1].set_xlabel('Film Thickness (nm)')125axes[0, 1].set_ylabel('Reflectance (\\%)')126axes[0, 1].set_title(f'Reflectance vs Thickness ($\\lambda = 550$ nm)')127axes[0, 1].legend()128axes[0, 1].grid(True, alpha=0.3)129130# Plot 3: Different coating materials131coating_materials = [132('MgF$_2$', 1.38, 'blue'),133('SiO$_2$', 1.46, 'green'),134('Al$_2$O$_3$', 1.76, 'orange'),135('ZrO$_2$', 2.0, 'red')136]137138for name, n_coat, color in coating_materials:139d_coat = 550e-9 / (4 * n_coat)140R_coat = [single_layer_reflectance(lam, d_coat, n_air, n_coat, n_glass) for lam in wavelengths]141axes[1, 0].plot(wavelengths*1e9, np.array(R_coat)*100, color=color, linewidth=2, label=name)142143axes[1, 0].axhline(y=R_glass[0]*100, color='gray', linestyle='--', alpha=0.5, label='Uncoated')144axes[1, 0].set_xlabel('Wavelength (nm)')145axes[1, 0].set_ylabel('Reflectance (\\%)')146axes[1, 0].set_title('Different Coating Materials')147axes[1, 0].legend(fontsize=8)148axes[1, 0].grid(True, alpha=0.3)149150# Plot 4: V-coating (two-layer AR)151n_L = 1.38 # MgF2152n_H = 2.0 # ZrO2153154# V-coating: QWOT of each material155d_L = 550e-9 / (4 * n_L)156d_H = 550e-9 / (4 * n_H)157158# Two-layer calculation159def two_layer_reflectance(wavelength, d1, n1, d2, n2, n0, n_sub):160delta1 = 2 * np.pi * n1 * d1 / wavelength161delta2 = 2 * np.pi * n2 * d2 / wavelength162163r01 = fresnel_r(n0, n1)164r12 = fresnel_r(n1, n2)165r23 = fresnel_r(n2, n_sub)166167# Calculate using characteristic matrix method168M1 = np.array([[np.cos(delta1), 1j*np.sin(delta1)/n1],169[1j*n1*np.sin(delta1), np.cos(delta1)]])170M2 = np.array([[np.cos(delta2), 1j*np.sin(delta2)/n2],171[1j*n2*np.sin(delta2), np.cos(delta2)]])172173M = M1 @ M2174175# Reflection coefficient176r = (n0*M[0,0] + n0*n_sub*M[0,1] - M[1,0] - n_sub*M[1,1]) / \177(n0*M[0,0] + n0*n_sub*M[0,1] + M[1,0] + n_sub*M[1,1])178179return np.abs(r)**2180181R_V = [two_layer_reflectance(lam, d_L, n_L, d_H, n_H, n_air, n_glass) for lam in wavelengths]182183axes[1, 1].plot(wavelengths*1e9, np.array(R_AR)*100, 'b-', linewidth=2, label='Single MgF$_2$')184axes[1, 1].plot(wavelengths*1e9, np.array(R_V)*100, 'g-', linewidth=2, label='V-coating (MgF$_2$/ZrO$_2$)')185axes[1, 1].axhline(y=R_glass[0]*100, color='gray', linestyle='--', alpha=0.5, label='Uncoated')186axes[1, 1].set_xlabel('Wavelength (nm)')187axes[1, 1].set_ylabel('Reflectance (\\%)')188axes[1, 1].set_title('Single vs Two-Layer AR Coating')189axes[1, 1].legend(fontsize=8)190axes[1, 1].grid(True, alpha=0.3)191192plt.tight_layout()193save_plot('ar_coating.pdf', 'Anti-reflection coatings: single layer, material comparison, and V-coating.')194195# Calculate minimum reflectance196R_min = min(R_AR)197\end{pycode}198199\section{High Reflector (Dielectric Mirror)}200\begin{pycode}201def multi_layer_reflectance(wavelength, layers, n_substrate, n_ambient=1.0):202"""Reflectance of multi-layer stack using transfer matrix method."""203# Initialize transfer matrix204M = np.eye(2, dtype=complex)205206for n, d in layers:207# Phase thickness208delta = 2 * np.pi * n * d / wavelength209210# Layer matrix211M_layer = np.array([212[np.cos(delta), 1j * np.sin(delta) / n],213[1j * n * np.sin(delta), np.cos(delta)]214])215M = M @ M_layer216217# Calculate reflection coefficient218eta0 = n_ambient219etas = n_substrate220221num = eta0 * M[0, 0] + eta0 * etas * M[0, 1] - M[1, 0] - etas * M[1, 1]222den = eta0 * M[0, 0] + eta0 * etas * M[0, 1] + M[1, 0] + etas * M[1, 1]223224r = num / den225return np.abs(r)**2226227# High/low index materials228n_H = 2.3 # TiO2229n_L = 1.38 # MgF2230lambda_0 = 550e-9231d_H = lambda_0 / (4 * n_H)232d_L = lambda_0 / (4 * n_L)233234fig, axes = plt.subplots(2, 2, figsize=(10, 8))235236# Plot 1: HR mirror with different number of pairs237pair_counts = [2, 4, 8, 16]238colors_pairs = ['blue', 'green', 'orange', 'red']239240for n_pairs, color in zip(pair_counts, colors_pairs):241layers_HR = []242for i in range(n_pairs):243layers_HR.append((n_H, d_H))244layers_HR.append((n_L, d_L))245246R_HR = [multi_layer_reflectance(lam, layers_HR, n_glass) for lam in wavelengths]247axes[0, 0].plot(wavelengths*1e9, np.array(R_HR)*100, color=color, linewidth=2,248label=f'{n_pairs} pairs')249250axes[0, 0].set_xlabel('Wavelength (nm)')251axes[0, 0].set_ylabel('Reflectance (\\%)')252axes[0, 0].set_title('High Reflector: (HL)$^N$ Stack')253axes[0, 0].legend(fontsize=8)254axes[0, 0].grid(True, alpha=0.3)255axes[0, 0].set_ylim([0, 105])256257# Plot 2: Peak reflectance vs number of pairs258pair_range = np.arange(1, 21)259peak_reflectances = []260261for n_pairs in pair_range:262layers = []263for i in range(n_pairs):264layers.append((n_H, d_H))265layers.append((n_L, d_L))266267R_peak = multi_layer_reflectance(lambda_0, layers, n_glass)268peak_reflectances.append(R_peak * 100)269270axes[0, 1].plot(pair_range, peak_reflectances, 'b-', linewidth=2)271axes[0, 1].axhline(y=99.9, color='gray', linestyle='--', alpha=0.7, label='99.9\\%')272axes[0, 1].set_xlabel('Number of H/L Pairs')273axes[0, 1].set_ylabel('Peak Reflectance (\\%)')274axes[0, 1].set_title('Peak Reflectance vs Stack Layers')275axes[0, 1].legend()276axes[0, 1].grid(True, alpha=0.3)277278# Plot 3: Bandwidth of HR mirror279# Theoretical bandwidth: Delta_lambda/lambda_0 = (4/pi) * arcsin((n_H - n_L)/(n_H + n_L))280contrast = (n_H - n_L) / (n_H + n_L)281bandwidth_theory = (4/np.pi) * np.arcsin(contrast)282283# Measure bandwidth from 16-pair stack284n_pairs = 16285layers_16 = []286for i in range(n_pairs):287layers_16.append((n_H, d_H))288layers_16.append((n_L, d_L))289290R_16 = np.array([multi_layer_reflectance(lam, layers_16, n_glass) for lam in wavelengths])291T_16 = 1 - R_16292293axes[1, 0].semilogy(wavelengths*1e9, T_16*100, 'b-', linewidth=2)294axes[1, 0].axhline(y=0.1, color='gray', linestyle='--', alpha=0.7, label='T = 0.1\\%')295axes[1, 0].set_xlabel('Wavelength (nm)')296axes[1, 0].set_ylabel('Transmittance (\\%)')297axes[1, 0].set_title(f'HR Mirror Stop Band ({n_pairs} pairs)')298axes[1, 0].legend()299axes[1, 0].grid(True, alpha=0.3, which='both')300301# Plot 4: Effect of index contrast302contrasts = [303(1.8, 1.38, 'Low contrast'),304(2.0, 1.38, 'Medium contrast'),305(2.3, 1.38, 'High contrast'),306(2.5, 1.38, 'Very high contrast')307]308colors_contrast = ['blue', 'green', 'orange', 'red']309310n_pairs = 8311for (nH, nL, label), color in zip(contrasts, colors_contrast):312dH = lambda_0 / (4 * nH)313dL = lambda_0 / (4 * nL)314layers = []315for i in range(n_pairs):316layers.append((nH, dH))317layers.append((nL, dL))318319R = [multi_layer_reflectance(lam, layers, n_glass) for lam in wavelengths]320axes[1, 1].plot(wavelengths*1e9, np.array(R)*100, color=color, linewidth=2, label=label)321322axes[1, 1].set_xlabel('Wavelength (nm)')323axes[1, 1].set_ylabel('Reflectance (\\%)')324axes[1, 1].set_title('Effect of Index Contrast')325axes[1, 1].legend(fontsize=8)326axes[1, 1].grid(True, alpha=0.3)327328plt.tight_layout()329save_plot('hr_mirror.pdf', 'High reflector mirrors: layer count, peak reflectance, stop band, and index contrast.')330331# Calculate max reflectance for results332R_max_HR = max(peak_reflectances)333\end{pycode}334335\section{Narrow-Band Filters}336\begin{pycode}337# Fabry-Perot interference filter338def fabry_perot_filter(wavelength, d_cavity, n_cavity, layers_mirror, n_sub):339"""340Fabry-Perot filter: mirror / cavity / mirror on substrate.341"""342# Build full stack: front mirror + cavity + back mirror343full_stack = layers_mirror + [(n_cavity, d_cavity)] + layers_mirror[::-1]344return multi_layer_reflectance(wavelength, full_stack, n_sub)345346# Design filter centered at 550 nm347n_cavity = 1.38 # MgF2 spacer348d_cavity = 550e-9 / (2 * n_cavity) # Half-wave spacer349350# Mirror layers351mirror_pairs = 5352mirror_layers = []353for i in range(mirror_pairs):354mirror_layers.append((n_H, d_H))355mirror_layers.append((n_L, d_L))356357# Fine wavelength grid for filter358wavelengths_fine = np.linspace(500e-9, 600e-9, 1000)359360fig, axes = plt.subplots(2, 2, figsize=(10, 8))361362# Plot 1: Narrow-band filter transmission363T_filter = []364for lam in wavelengths_fine:365R = fabry_perot_filter(lam, d_cavity, n_cavity, mirror_layers, n_glass)366T_filter.append((1 - R) * 100)367368axes[0, 0].plot(wavelengths_fine*1e9, T_filter, 'b-', linewidth=2)369axes[0, 0].set_xlabel('Wavelength (nm)')370axes[0, 0].set_ylabel('Transmittance (\\%)')371axes[0, 0].set_title('Narrow-Band Filter (5 pairs each mirror)')372axes[0, 0].grid(True, alpha=0.3)373374# Plot 2: Filter with different mirror reflectivity375mirror_pair_counts = [3, 5, 7, 9]376colors_filter = ['blue', 'green', 'orange', 'red']377378for pairs, color in zip(mirror_pair_counts, colors_filter):379m_layers = []380for i in range(pairs):381m_layers.append((n_H, d_H))382m_layers.append((n_L, d_L))383384T = []385for lam in wavelengths_fine:386R = fabry_perot_filter(lam, d_cavity, n_cavity, m_layers, n_glass)387T.append((1 - R) * 100)388389axes[0, 1].plot(wavelengths_fine*1e9, T, color=color, linewidth=2, label=f'{pairs} pairs')390391axes[0, 1].set_xlabel('Wavelength (nm)')392axes[0, 1].set_ylabel('Transmittance (\\%)')393axes[0, 1].set_title('Filter Bandwidth vs Mirror Reflectivity')394axes[0, 1].legend(fontsize=8)395axes[0, 1].grid(True, alpha=0.3)396397# Plot 3: Edge filter (long-pass)398def edge_filter(wavelength, layers, n_sub, n_ambient=1.0):399"""Edge filter design."""400return multi_layer_reflectance(wavelength, layers, n_sub, n_ambient)401402# Design long-pass at 500 nm403lambda_edge = 500e-9404d_H_edge = lambda_edge / (4 * n_H)405d_L_edge = lambda_edge / (4 * n_L)406407edge_pairs = 20408edge_layers = []409for i in range(edge_pairs):410edge_layers.append((n_H, d_H_edge))411edge_layers.append((n_L, d_L_edge))412413T_edge = []414wavelengths_edge = np.linspace(400e-9, 700e-9, 500)415for lam in wavelengths_edge:416R = edge_filter(lam, edge_layers, n_glass)417T_edge.append((1 - R) * 100)418419axes[1, 0].plot(wavelengths_edge*1e9, T_edge, 'b-', linewidth=2)420axes[1, 0].axvline(x=500, color='red', linestyle='--', alpha=0.7, label='Design $\\lambda$')421axes[1, 0].set_xlabel('Wavelength (nm)')422axes[1, 0].set_ylabel('Transmittance (\\%)')423axes[1, 0].set_title('Edge Filter (Long-Pass)')424axes[1, 0].legend()425axes[1, 0].grid(True, alpha=0.3)426427# Plot 4: Bandpass filter using two edge filters428lambda_short = 450e-9 # Short-pass edge429lambda_long = 550e-9 # Long-pass edge430431# Short-pass432d_H_short = lambda_short / (4 * n_H)433d_L_short = lambda_short / (4 * n_L)434short_layers = []435for i in range(15):436short_layers.append((n_H, d_H_short))437short_layers.append((n_L, d_L_short))438439# Long-pass440d_H_long = lambda_long / (4 * n_H)441d_L_long = lambda_long / (4 * n_L)442long_layers = []443for i in range(15):444long_layers.append((n_H, d_H_long))445long_layers.append((n_L, d_L_long))446447T_bandpass = []448for lam in wavelengths_edge:449R_short = edge_filter(lam, short_layers, n_glass)450R_long = edge_filter(lam, long_layers, n_glass)451# Combined transmission452T = (1 - R_short) * (1 - R_long) * 100453T_bandpass.append(T)454455axes[1, 1].plot(wavelengths_edge*1e9, T_bandpass, 'purple', linewidth=2)456axes[1, 1].axvline(x=450, color='blue', linestyle='--', alpha=0.5)457axes[1, 1].axvline(x=550, color='red', linestyle='--', alpha=0.5)458axes[1, 1].set_xlabel('Wavelength (nm)')459axes[1, 1].set_ylabel('Transmittance (\\%)')460axes[1, 1].set_title('Bandpass Filter (Edge Filter Combination)')461axes[1, 1].grid(True, alpha=0.3)462463plt.tight_layout()464save_plot('narrow_band.pdf', 'Narrow-band filters: Fabry-P\\'erot, bandwidth control, and bandpass design.')465\end{pycode}466467\section{Angle Dependence and Polarization}468\begin{pycode}469# Oblique incidence calculations470def snell_angle(n1, n2, theta1):471"""Calculate refracted angle using Snell's law."""472sin_theta2 = n1 * np.sin(theta1) / n2473if np.abs(sin_theta2) > 1:474return np.pi/2 # Total internal reflection475return np.arcsin(sin_theta2)476477def oblique_layer_matrix(n, d, wavelength, theta, polarization='s'):478"""Transfer matrix for layer at oblique incidence."""479theta_in_layer = snell_angle(1.0, n, theta)480delta = 2 * np.pi * n * d * np.cos(theta_in_layer) / wavelength481482if polarization == 's':483eta = n * np.cos(theta_in_layer)484else: # p-polarization485eta = n / np.cos(theta_in_layer)486487M = np.array([488[np.cos(delta), 1j * np.sin(delta) / eta],489[1j * eta * np.sin(delta), np.cos(delta)]490])491return M492493def oblique_multilayer_reflectance(wavelength, layers, n_sub, theta, polarization='s'):494"""Reflectance at oblique incidence."""495M = np.eye(2, dtype=complex)496497for n, d in layers:498M_layer = oblique_layer_matrix(n, d, wavelength, theta, polarization)499M = M @ M_layer500501# Calculate terminal admittances502theta_sub = snell_angle(1.0, n_sub, theta)503504if polarization == 's':505eta0 = np.cos(theta)506etas = n_sub * np.cos(theta_sub)507else:508eta0 = 1.0 / np.cos(theta)509etas = n_sub / np.cos(theta_sub)510511num = eta0 * M[0, 0] + eta0 * etas * M[0, 1] - M[1, 0] - etas * M[1, 1]512den = eta0 * M[0, 0] + eta0 * etas * M[0, 1] + M[1, 0] + etas * M[1, 1]513514r = num / den515return np.abs(r)**2516517fig, axes = plt.subplots(2, 2, figsize=(10, 8))518519# Plot 1: AR coating at different angles520angles = [0, 15, 30, 45]521colors_angle = ['blue', 'green', 'orange', 'red']522523for angle, color in zip(angles, colors_angle):524theta = np.deg2rad(angle)525R_s = []526R_p = []527for lam in wavelengths:528d_AR_eff = d_AR # Could adjust for angle529Rs = single_layer_reflectance(lam, d_AR_eff, n_air, n_MgF2, n_glass) # Simplified530R_s.append(Rs * 100)531R_p.append(Rs * 100)532533# More accurate calculation for one wavelength534layers_AR = [(n_MgF2, d_AR)]535Rs_accurate = [oblique_multilayer_reflectance(lam, layers_AR, n_glass, theta, 's') * 100 for lam in wavelengths]536537axes[0, 0].plot(wavelengths*1e9, Rs_accurate, color=color, linewidth=2, label=f'{angle}$^\\circ$')538539axes[0, 0].set_xlabel('Wavelength (nm)')540axes[0, 0].set_ylabel('Reflectance (\\%)')541axes[0, 0].set_title('AR Coating: Angle Dependence (s-pol)')542axes[0, 0].legend(fontsize=8)543axes[0, 0].grid(True, alpha=0.3)544545# Plot 2: s vs p polarization546theta_45 = np.deg2rad(45)547layers_test = [(n_MgF2, d_AR)]548549R_s_45 = [oblique_multilayer_reflectance(lam, layers_test, n_glass, theta_45, 's') * 100 for lam in wavelengths]550R_p_45 = [oblique_multilayer_reflectance(lam, layers_test, n_glass, theta_45, 'p') * 100 for lam in wavelengths]551552axes[0, 1].plot(wavelengths*1e9, R_s_45, 'b-', linewidth=2, label='s-polarization')553axes[0, 1].plot(wavelengths*1e9, R_p_45, 'r--', linewidth=2, label='p-polarization')554axes[0, 1].set_xlabel('Wavelength (nm)')555axes[0, 1].set_ylabel('Reflectance (\\%)')556axes[0, 1].set_title('Polarization Dependence at 45$^\\circ$')557axes[0, 1].legend()558axes[0, 1].grid(True, alpha=0.3)559560# Plot 3: Blue shift with angle (HR mirror)561angles_shift = np.linspace(0, 60, 50)562layers_HR_8 = []563for i in range(8):564layers_HR_8.append((n_H, d_H))565layers_HR_8.append((n_L, d_L))566567# Find peak wavelength at each angle568peak_wavelengths = []569for angle in angles_shift:570theta = np.deg2rad(angle)571# Search for peak572max_R = 0573peak_lam = lambda_0574for lam in wavelengths:575R = oblique_multilayer_reflectance(lam, layers_HR_8, n_glass, theta, 's')576if R > max_R:577max_R = R578peak_lam = lam579peak_wavelengths.append(peak_lam * 1e9)580581axes[1, 0].plot(angles_shift, peak_wavelengths, 'b-', linewidth=2)582axes[1, 0].axhline(y=550, color='gray', linestyle='--', alpha=0.5, label='Normal incidence')583axes[1, 0].set_xlabel('Angle of incidence (degrees)')584axes[1, 0].set_ylabel('Peak wavelength (nm)')585axes[1, 0].set_title('Blue Shift of HR Mirror with Angle')586axes[1, 0].legend()587axes[1, 0].grid(True, alpha=0.3)588589# Plot 4: Polarizing beam splitter concept590# At Brewster angle, p-pol has minimum reflection591angles_brewster = np.linspace(0, 85, 100)592R_glass_s = []593R_glass_p = []594595for angle in angles_brewster:596theta = np.deg2rad(angle)597theta_t = snell_angle(1.0, n_glass, theta)598599# Fresnel coefficients600rs = (np.cos(theta) - n_glass * np.cos(theta_t)) / (np.cos(theta) + n_glass * np.cos(theta_t))601rp = (n_glass * np.cos(theta) - np.cos(theta_t)) / (n_glass * np.cos(theta) + np.cos(theta_t))602603R_glass_s.append(np.abs(rs)**2 * 100)604R_glass_p.append(np.abs(rp)**2 * 100)605606axes[1, 1].plot(angles_brewster, R_glass_s, 'b-', linewidth=2, label='s-polarization')607axes[1, 1].plot(angles_brewster, R_glass_p, 'r-', linewidth=2, label='p-polarization')608609# Brewster angle610theta_B = np.rad2deg(np.arctan(n_glass))611axes[1, 1].axvline(x=theta_B, color='green', linestyle='--', alpha=0.7, label=f'Brewster: {theta_B:.1f}$^\\circ$')612613axes[1, 1].set_xlabel('Angle of incidence (degrees)')614axes[1, 1].set_ylabel('Reflectance (\\%)')615axes[1, 1].set_title('Glass Surface: s vs p Polarization')616axes[1, 1].legend(fontsize=8)617axes[1, 1].grid(True, alpha=0.3)618619plt.tight_layout()620save_plot('angle_dependence.pdf', 'Angle and polarization effects: AR coating, s/p splitting, blue shift, and Brewster angle.')621\end{pycode}622623\section{Soap Bubbles and Natural Thin Films}624\begin{pycode}625# Soap bubble (thin water film in air)626n_water = 1.33627d_bubble = 300e-9628R_bubble = [single_layer_reflectance(lam, d_bubble, n_air, n_water, n_air) for lam in wavelengths]629630# Oil slick on water631n_oil = 1.5632d_oil = 400e-9633R_oil = [single_layer_reflectance(lam, d_oil, n_air, n_oil, n_water) for lam in wavelengths]634635fig, axes = plt.subplots(2, 2, figsize=(10, 8))636637# Plot 1: Soap bubble colors638axes[0, 0].plot(wavelengths*1e9, np.array(R_bubble)*100, 'b-', linewidth=2)639axes[0, 0].set_xlabel('Wavelength (nm)')640axes[0, 0].set_ylabel('Reflectance (\\%)')641axes[0, 0].set_title(f'Soap Bubble ($d = {d_bubble*1e9:.0f}$ nm)')642axes[0, 0].grid(True, alpha=0.3)643644# Plot 2: Different bubble thicknesses645bubble_thicknesses = [100e-9, 200e-9, 300e-9, 400e-9, 500e-9]646colors_bubble = plt.cm.rainbow(np.linspace(0, 1, len(bubble_thicknesses)))647648for d, color in zip(bubble_thicknesses, colors_bubble):649R = [single_layer_reflectance(lam, d, n_air, n_water, n_air) for lam in wavelengths]650axes[0, 1].plot(wavelengths*1e9, np.array(R)*100, color=color, linewidth=2,651label=f'{d*1e9:.0f} nm')652653axes[0, 1].set_xlabel('Wavelength (nm)')654axes[0, 1].set_ylabel('Reflectance (\\%)')655axes[0, 1].set_title('Soap Bubble Thickness Variation')656axes[0, 1].legend(fontsize=8)657axes[0, 1].grid(True, alpha=0.3)658659# Plot 3: Oil slick colors660axes[1, 0].plot(wavelengths*1e9, np.array(R_oil)*100, 'purple', linewidth=2)661axes[1, 0].set_xlabel('Wavelength (nm)')662axes[1, 0].set_ylabel('Reflectance (\\%)')663axes[1, 0].set_title(f'Oil Slick on Water ($d = {d_oil*1e9:.0f}$ nm)')664axes[1, 0].grid(True, alpha=0.3)665666# Plot 4: Color map vs thickness667thicknesses_color = np.linspace(50e-9, 600e-9, 100)668wavelengths_vis = np.linspace(400e-9, 700e-9, 100)669670T_color = np.zeros((len(thicknesses_color), len(wavelengths_vis)))671672for i, d in enumerate(thicknesses_color):673for j, lam in enumerate(wavelengths_vis):674R = single_layer_reflectance(lam, d, n_air, n_water, n_air)675T_color[i, j] = R676677im = axes[1, 1].pcolormesh(wavelengths_vis*1e9, thicknesses_color*1e9, T_color,678cmap='hot', shading='auto')679axes[1, 1].set_xlabel('Wavelength (nm)')680axes[1, 1].set_ylabel('Film thickness (nm)')681axes[1, 1].set_title('Reflectance Color Map (Soap Film)')682plt.colorbar(im, ax=axes[1, 1], label='Reflectance')683684plt.tight_layout()685save_plot('natural_films.pdf', 'Natural thin films: soap bubbles, oil slicks, and thickness-dependent colors.')686\end{pycode}687688\section{Results Summary}689\begin{pycode}690# Generate results table691print(r'\begin{table}[htbp]')692print(r'\centering')693print(r'\caption{Summary of Thin Film Parameters}')694print(r'\begin{tabular}{lll}')695print(r'\toprule')696print(r'Configuration & Parameter & Value \\')697print(r'\midrule')698print(r'\multicolumn{3}{c}{\textit{Anti-Reflection Coating}} \\')699print(f'Uncoated glass & Reflectance & {R_glass[0]*100:.2f}\\% \\\\')700print(f'MgF$_2$ coating & Min reflectance & {R_min*100:.3f}\\% \\\\')701print(f'QW thickness & $d$ & {d_AR*1e9:.1f} nm \\\\')702print(f'Optimal $n$ & $\\sqrt{{n_{{sub}}}}$ & {n_optimal:.2f} \\\\')703print(r'\midrule')704print(r'\multicolumn{3}{c}{\textit{High Reflector}} \\')705print(f'Peak reflectance & 16 pairs & {R_max_HR:.1f}\\% \\\\')706print(f'TiO$_2$ index & $n_H$ & {n_H:.2f} \\\\')707print(f'MgF$_2$ index & $n_L$ & {n_L:.2f} \\\\')708print(f'Stop band & Theory & {bandwidth_theory*100:.0f}\\% of $\\lambda_0$ \\\\')709print(r'\midrule')710print(r'\multicolumn{3}{c}{\textit{Brewster Angle}} \\')711print(f'Glass & $\\theta_B$ & {theta_B:.1f}$^\\circ$ \\\\')712print(r'\bottomrule')713print(r'\end{tabular}')714print(r'\end{table}')715\end{pycode}716717\section{Statistical Summary}718\begin{itemize}719\item \textbf{Uncoated glass reflectance}: \py{f"{R_glass[0]*100:.2f}"}\%720\item \textbf{Minimum AR coating reflectance}: \py{f"{R_min*100:.3f}"}\%721\item \textbf{Quarter-wave AR thickness}: \py{f"{d_AR*1e9:.1f}"} nm722\item \textbf{Optimal coating index}: $n = \sqrt{n_{glass}} = $ \py{f"{n_optimal:.3f}"}723\item \textbf{High reflector peak (16 pairs)}: \py{f"{R_max_HR:.1f}"}\%724\item \textbf{Stop band width}: $\approx$ \py{f"{bandwidth_theory*100:.0f}"}\% of $\lambda_0$725\item \textbf{Brewster angle (glass)}: \py{f"{theta_B:.1f}"}$^\circ$726\end{itemize}727728\section{Conclusion}729Thin film interference enables precise control of optical properties through constructive and destructive interference. A single quarter-wave MgF$_2$ layer reduces glass reflectance from 4\% to below 1\% at the design wavelength. Multi-layer stacks can achieve reflectances exceeding 99.99\% for high-power laser mirrors. The transfer matrix method provides an efficient algorithm for analyzing arbitrary layer structures. Angular dependence causes blue-shifting of spectral features and polarization splitting, exploited in polarizing beam splitters. These principles underpin technologies from camera lenses to laser mirrors, interference filters, and optical telecommunications components.730731\end{document}732733734