Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/optics/polarization.tex
51 views
unlisted
1
\documentclass[a4paper, 11pt]{article}
2
\usepackage[utf8]{inputenc}
3
\usepackage[T1]{fontenc}
4
\usepackage{amsmath, amssymb}
5
\usepackage{graphicx}
6
\usepackage{siunitx}
7
\usepackage{booktabs}
8
\usepackage[makestderr]{pythontex}
9
10
\title{Optics: Polarization States and Jones Calculus}
11
\author{Computational Science Templates}
12
\date{\today}
13
14
\begin{document}
15
\maketitle
16
17
\section{Introduction}
18
Polarization describes the orientation of the electric field oscillation in electromagnetic waves. This analysis explores polarization states using Jones vectors and Mueller matrices, demonstrating the effects of optical elements like polarizers, wave plates, and rotators. Applications in ellipsometry, stress analysis, and optical communications are examined.
19
20
\section{Mathematical Framework}
21
22
\subsection{Jones Vectors}
23
Jones vectors represent polarization states:
24
\begin{equation}
25
\mathbf{E} = \begin{pmatrix} E_x \\ E_y \end{pmatrix} = \begin{pmatrix} A_x e^{i\phi_x} \\ A_y e^{i\phi_y} \end{pmatrix}
26
\end{equation}
27
28
\subsection{Stokes Parameters}
29
The Stokes vector describes polarization including partial polarization:
30
\begin{equation}
31
\mathbf{S} = \begin{pmatrix} S_0 \\ S_1 \\ S_2 \\ S_3 \end{pmatrix} = \begin{pmatrix} |E_x|^2 + |E_y|^2 \\ |E_x|^2 - |E_y|^2 \\ 2\text{Re}(E_x E_y^*) \\ 2\text{Im}(E_x E_y^*) \end{pmatrix}
32
\end{equation}
33
34
\subsection{Jones Matrices}
35
Common Jones matrices:
36
\begin{itemize}
37
\item Linear polarizer at angle $\theta$: $J_P = \begin{pmatrix} \cos^2\theta & \cos\theta\sin\theta \\ \cos\theta\sin\theta & \sin^2\theta \end{pmatrix}$
38
\item Wave plate with retardance $\delta$: $J_W = \begin{pmatrix} e^{-i\delta/2} & 0 \\ 0 & e^{i\delta/2} \end{pmatrix}$
39
\end{itemize}
40
41
\section{Environment Setup}
42
\begin{pycode}
43
import numpy as np
44
import matplotlib.pyplot as plt
45
plt.rc('text', usetex=True)
46
plt.rc('font', family='serif')
47
48
def save_plot(filename, caption=""):
49
plt.savefig(filename, bbox_inches='tight', dpi=150)
50
print(r'\begin{figure}[htbp]')
51
print(r'\centering')
52
print(r'\includegraphics[width=0.95\textwidth]{' + filename + '}')
53
if caption:
54
print(r'\caption{' + caption + '}')
55
print(r'\end{figure}')
56
plt.close()
57
\end{pycode}
58
59
\section{Jones Vector Representation}
60
\begin{pycode}
61
# Jones vectors for common polarization states
62
def linear_pol(theta):
63
"""Linear polarization at angle theta."""
64
return np.array([np.cos(theta), np.sin(theta)])
65
66
def circular_pol(handedness='right'):
67
"""Circular polarization."""
68
if handedness == 'right':
69
return np.array([1, -1j]) / np.sqrt(2)
70
else:
71
return np.array([1, 1j]) / np.sqrt(2)
72
73
def elliptical_pol(a, b, theta=0, handedness='right'):
74
"""Elliptical polarization with semi-axes a, b at angle theta."""
75
sign = -1 if handedness == 'right' else 1
76
E = np.array([a, sign * 1j * b])
77
# Rotate by theta
78
c, s = np.cos(theta), np.sin(theta)
79
R = np.array([[c, -s], [s, c]])
80
return R @ E / np.sqrt(a**2 + b**2)
81
82
# Jones matrices
83
def linear_polarizer(theta):
84
"""Linear polarizer at angle theta."""
85
c, s = np.cos(theta), np.sin(theta)
86
return np.array([[c**2, c*s], [c*s, s**2]])
87
88
def wave_plate(delta, theta=0):
89
"""Wave plate with retardance delta, fast axis at angle theta."""
90
c, s = np.cos(theta), np.sin(theta)
91
R = np.array([[c, s], [-s, c]])
92
W = np.array([[np.exp(-1j*delta/2), 0], [0, np.exp(1j*delta/2)]])
93
return R.T @ W @ R
94
95
def quarter_wave(theta=0):
96
return wave_plate(np.pi/2, theta)
97
98
def half_wave(theta=0):
99
return wave_plate(np.pi, theta)
100
101
def rotator(theta):
102
"""Polarization rotator (Faraday rotator)."""
103
c, s = np.cos(theta), np.sin(theta)
104
return np.array([[c, -s], [s, c]])
105
106
# Visualization functions
107
def polarization_ellipse(jones, n_points=100):
108
"""Generate polarization ellipse from Jones vector."""
109
t = np.linspace(0, 2*np.pi, n_points)
110
Ex = np.real(jones[0] * np.exp(1j*t))
111
Ey = np.real(jones[1] * np.exp(1j*t))
112
return Ex, Ey
113
114
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
115
116
# Plot 1: Common polarization states
117
states = [
118
('Horizontal', linear_pol(0), 'blue'),
119
('Vertical', linear_pol(np.pi/2), 'red'),
120
('45$^\\circ$', linear_pol(np.pi/4), 'green'),
121
('RCP', circular_pol('right'), 'purple'),
122
('LCP', circular_pol('left'), 'orange')
123
]
124
125
for name, jones, color in states:
126
Ex, Ey = polarization_ellipse(jones)
127
axes[0, 0].plot(Ex, Ey, color=color, linewidth=2, label=name)
128
129
axes[0, 0].set_xlabel('$E_x$')
130
axes[0, 0].set_ylabel('$E_y$')
131
axes[0, 0].set_title('Polarization States')
132
axes[0, 0].legend(fontsize=8, loc='upper right')
133
axes[0, 0].set_xlim([-1.2, 1.2])
134
axes[0, 0].set_ylim([-1.2, 1.2])
135
axes[0, 0].set_aspect('equal')
136
axes[0, 0].grid(True, alpha=0.3)
137
138
# Plot 2: Elliptical polarization with different ellipticities
139
ellipticities = [0, 0.3, 0.6, 1.0]
140
colors_ell = ['blue', 'green', 'orange', 'red']
141
142
for ell, color in zip(ellipticities, colors_ell):
143
a = 1
144
b = ell
145
jones = elliptical_pol(a, b, theta=np.pi/6)
146
Ex, Ey = polarization_ellipse(jones)
147
axes[0, 1].plot(Ex, Ey, color=color, linewidth=2, label=f'$\\varepsilon = {ell:.1f}$')
148
149
axes[0, 1].set_xlabel('$E_x$')
150
axes[0, 1].set_ylabel('$E_y$')
151
axes[0, 1].set_title('Elliptical Polarization')
152
axes[0, 1].legend(fontsize=8)
153
axes[0, 1].set_aspect('equal')
154
axes[0, 1].grid(True, alpha=0.3)
155
156
# Plot 3: 3D representation of electric field
157
t = np.linspace(0, 4*np.pi, 200)
158
z = t / (2*np.pi)
159
160
# Right circular polarization
161
from mpl_toolkits.mplot3d import Axes3D
162
ax3d = axes[1, 0]
163
164
Ex_rcp = np.cos(t)
165
Ey_rcp = np.sin(t)
166
167
# Project onto 2D (simplified)
168
ax3d.plot(t / np.pi, Ex_rcp, 'b-', linewidth=1.5, label='$E_x$')
169
ax3d.plot(t / np.pi, Ey_rcp, 'r--', linewidth=1.5, label='$E_y$')
170
ax3d.set_xlabel('Phase ($\\pi$)')
171
ax3d.set_ylabel('Electric field')
172
ax3d.set_title('RCP Electric Field Components')
173
ax3d.legend()
174
ax3d.grid(True, alpha=0.3)
175
176
# Plot 4: Poincare sphere projection (Stokes parameters)
177
def jones_to_stokes(jones):
178
Ex, Ey = jones
179
S0 = np.abs(Ex)**2 + np.abs(Ey)**2
180
S1 = np.abs(Ex)**2 - np.abs(Ey)**2
181
S2 = 2 * np.real(Ex * np.conj(Ey))
182
S3 = 2 * np.imag(Ex * np.conj(Ey))
183
return np.array([S0, S1, S2, S3])
184
185
# Generate points on Poincare sphere
186
theta_p = np.linspace(0, 2*np.pi, 50)
187
phi_p = np.linspace(0, np.pi, 25)
188
THETA, PHI = np.meshgrid(theta_p, phi_p)
189
190
S1_sphere = np.sin(PHI) * np.cos(THETA)
191
S2_sphere = np.sin(PHI) * np.sin(THETA)
192
S3_sphere = np.cos(PHI)
193
194
axes[1, 1].contourf(S1_sphere, S2_sphere, S3_sphere, levels=20, cmap='coolwarm')
195
axes[1, 1].set_xlabel('$S_1/S_0$')
196
axes[1, 1].set_ylabel('$S_2/S_0$')
197
axes[1, 1].set_title('Poincar\\'e Sphere Projection')
198
axes[1, 1].set_aspect('equal')
199
200
# Mark key states
201
for name, jones, color in states[:3]:
202
S = jones_to_stokes(jones)
203
axes[1, 1].plot(S[1]/S[0], S[2]/S[0], 'o', color=color, markersize=8)
204
205
plt.tight_layout()
206
save_plot('polarization_states.pdf', 'Polarization states: Jones vectors, elliptical polarization, and Poincar\\'e sphere.')
207
\end{pycode}
208
209
\section{Malus's Law and Polarizer Chains}
210
\begin{pycode}
211
# Malus's Law demonstration
212
angles = np.linspace(0, np.pi, 100)
213
input_pol = linear_pol(0) # Horizontal
214
transmitted = []
215
for theta in angles:
216
P = linear_polarizer(theta)
217
output = P @ input_pol
218
intensity = np.abs(output[0])**2 + np.abs(output[1])**2
219
transmitted.append(intensity)
220
221
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
222
223
# Plot 1: Malus's Law
224
axes[0, 0].plot(np.rad2deg(angles), transmitted, 'b-', linewidth=2)
225
axes[0, 0].plot(np.rad2deg(angles), np.cos(angles)**2, 'r--', linewidth=1.5, alpha=0.7, label='Theory')
226
axes[0, 0].set_xlabel('Polarizer Angle (degrees)')
227
axes[0, 0].set_ylabel('Transmitted Intensity')
228
axes[0, 0].set_title("Malus's Law: $I = I_0\\cos^2\\theta$")
229
axes[0, 0].legend()
230
axes[0, 0].grid(True, alpha=0.3)
231
232
# Plot 2: Two crossed polarizers with intermediate polarizer
233
intermediate_angles = np.linspace(0, np.pi/2, 50)
234
transmission_chain = []
235
236
for theta_mid in intermediate_angles:
237
# Three polarizers: 0, theta_mid, 90 degrees
238
P1 = linear_polarizer(0)
239
P2 = linear_polarizer(theta_mid)
240
P3 = linear_polarizer(np.pi/2)
241
242
output = P3 @ P2 @ P1 @ input_pol
243
intensity = np.abs(output[0])**2 + np.abs(output[1])**2
244
transmission_chain.append(intensity)
245
246
# Theory: cos^2(theta) * cos^2(90-theta) = 0.25*sin^2(2*theta)
247
theory = 0.25 * np.sin(2 * intermediate_angles)**2
248
249
axes[0, 1].plot(np.rad2deg(intermediate_angles), transmission_chain, 'b-', linewidth=2, label='Simulation')
250
axes[0, 1].plot(np.rad2deg(intermediate_angles), theory, 'r--', linewidth=1.5, alpha=0.7, label='Theory')
251
axes[0, 1].set_xlabel('Intermediate Polarizer Angle (degrees)')
252
axes[0, 1].set_ylabel('Transmitted Intensity')
253
axes[0, 1].set_title('Crossed Polarizers with Intermediate')
254
axes[0, 1].legend()
255
axes[0, 1].grid(True, alpha=0.3)
256
257
# Plot 3: N polarizers between crossed pair
258
N_polarizers = np.arange(1, 21)
259
transmission_N = []
260
261
for N in N_polarizers:
262
if N == 0:
263
transmission_N.append(0)
264
else:
265
angles_chain = np.linspace(0, np.pi/2, N + 2)
266
jones = input_pol
267
for angle in angles_chain:
268
P = linear_polarizer(angle)
269
jones = P @ jones
270
intensity = np.abs(jones[0])**2 + np.abs(jones[1])**2
271
transmission_N.append(intensity)
272
273
# Theory: cos^(2N)(pi/(2N))
274
theory_N = np.cos(np.pi / (2 * (N_polarizers + 1)))**(2 * (N_polarizers + 1))
275
276
axes[1, 0].plot(N_polarizers, transmission_N, 'bo-', linewidth=1.5, markersize=4, label='Simulation')
277
axes[1, 0].plot(N_polarizers, theory_N, 'r--', linewidth=1.5, alpha=0.7, label='Theory')
278
axes[1, 0].set_xlabel('Number of Intermediate Polarizers')
279
axes[1, 0].set_ylabel('Transmitted Intensity')
280
axes[1, 0].set_title('Transmission Through N Polarizers')
281
axes[1, 0].legend()
282
axes[1, 0].grid(True, alpha=0.3)
283
284
# Plot 4: Extinction ratio
285
extinction_ratios = []
286
polarizer_angles = np.linspace(0, np.pi, 500)
287
288
# Perfect polarizer
289
for theta in polarizer_angles:
290
P = linear_polarizer(theta)
291
output = P @ input_pol
292
intensity = np.abs(output[0])**2 + np.abs(output[1])**2
293
extinction_ratios.append(intensity)
294
295
axes[1, 1].semilogy(np.rad2deg(polarizer_angles), extinction_ratios, 'b-', linewidth=2)
296
axes[1, 1].set_xlabel('Polarizer Angle (degrees)')
297
axes[1, 1].set_ylabel('Transmitted Intensity')
298
axes[1, 1].set_title('Extinction Ratio')
299
axes[1, 1].set_ylim([1e-6, 1.5])
300
axes[1, 1].grid(True, alpha=0.3, which='both')
301
302
plt.tight_layout()
303
save_plot('malus_law.pdf', "Malus's law, polarizer chains, and extinction ratio analysis.")
304
\end{pycode}
305
306
\section{Wave Plates and Polarization Conversion}
307
\begin{pycode}
308
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
309
310
# Plot 1: Quarter-wave plate effect
311
input_linear = linear_pol(np.pi/4)
312
qwp_angles = [0, np.pi/8, np.pi/4]
313
colors = ['blue', 'green', 'red']
314
labels = ['$\\theta = 0^\\circ$ (elliptical)', '$\\theta = 22.5^\\circ$', '$\\theta = 45^\\circ$ (circular)']
315
316
for theta, color, label in zip(qwp_angles, colors, labels):
317
QWP = quarter_wave(theta)
318
output = QWP @ input_linear
319
Ex, Ey = polarization_ellipse(output)
320
axes[0, 0].plot(Ex, Ey, color=color, linewidth=2, label=label)
321
322
axes[0, 0].set_xlabel('$E_x$')
323
axes[0, 0].set_ylabel('$E_y$')
324
axes[0, 0].set_title('Quarter-Wave Plate Effects (45$^\\circ$ input)')
325
axes[0, 0].legend(fontsize=8)
326
axes[0, 0].set_aspect('equal')
327
axes[0, 0].grid(True, alpha=0.3)
328
329
# Plot 2: Half-wave plate rotation
330
input_horizontal = linear_pol(0)
331
hwp_angles = np.linspace(0, np.pi/2, 5)
332
colors_hwp = plt.cm.viridis(np.linspace(0, 1, 5))
333
334
for theta, color in zip(hwp_angles, colors_hwp):
335
HWP = half_wave(theta)
336
output = HWP @ input_horizontal
337
Ex, Ey = polarization_ellipse(output)
338
axes[0, 1].plot(Ex, Ey, color=color, linewidth=2, label=f'$\\theta = {np.rad2deg(theta):.0f}^\\circ$')
339
340
axes[0, 1].set_xlabel('$E_x$')
341
axes[0, 1].set_ylabel('$E_y$')
342
axes[0, 1].set_title('Half-Wave Plate Rotation')
343
axes[0, 1].legend(fontsize=8)
344
axes[0, 1].set_aspect('equal')
345
axes[0, 1].grid(True, alpha=0.3)
346
347
# Plot 3: Retardance scan - output intensity through analyzer
348
retardances = np.linspace(0, 4*np.pi, 200)
349
analyzer_angles = [0, np.pi/4, np.pi/2]
350
colors_ret = ['blue', 'green', 'red']
351
352
input_45 = linear_pol(np.pi/4)
353
354
for analyzer, color in zip(analyzer_angles, colors_ret):
355
intensities = []
356
for delta in retardances:
357
WP = wave_plate(delta, theta=0)
358
A = linear_polarizer(analyzer)
359
output = A @ WP @ input_45
360
I = np.abs(output[0])**2 + np.abs(output[1])**2
361
intensities.append(I)
362
363
axes[1, 0].plot(retardances/np.pi, intensities, color=color, linewidth=2,
364
label=f'Analyzer: {np.rad2deg(analyzer):.0f}$^\\circ$')
365
366
axes[1, 0].set_xlabel('Retardance ($\\pi$)')
367
axes[1, 0].set_ylabel('Transmitted Intensity')
368
axes[1, 0].set_title('Intensity vs Retardance')
369
axes[1, 0].legend(fontsize=8)
370
axes[1, 0].grid(True, alpha=0.3)
371
372
# Plot 4: Circular to linear conversion (QWP)
373
input_rcp = circular_pol('right')
374
qwp_scan = np.linspace(0, np.pi, 100)
375
ellipticity_output = []
376
377
for theta in qwp_scan:
378
QWP = quarter_wave(theta)
379
output = QWP @ input_rcp
380
381
# Calculate ellipticity from Stokes parameters
382
S = jones_to_stokes(output)
383
ellipticity = np.arctan2(S[3], np.sqrt(S[1]**2 + S[2]**2)) / (np.pi/2)
384
ellipticity_output.append(ellipticity)
385
386
axes[1, 1].plot(np.rad2deg(qwp_scan), ellipticity_output, 'b-', linewidth=2)
387
axes[1, 1].axhline(y=0, color='gray', linestyle='--', alpha=0.7)
388
axes[1, 1].set_xlabel('QWP Fast Axis Angle (degrees)')
389
axes[1, 1].set_ylabel('Output Ellipticity')
390
axes[1, 1].set_title('RCP Through QWP')
391
axes[1, 1].grid(True, alpha=0.3)
392
393
plt.tight_layout()
394
save_plot('wave_plates.pdf', 'Wave plate effects: QWP, HWP, retardance scan, and circular to linear conversion.')
395
\end{pycode}
396
397
\section{Mueller Matrix Formalism}
398
\begin{pycode}
399
# Mueller matrices for partially polarized light
400
def jones_to_mueller(J):
401
"""Convert Jones matrix to Mueller matrix."""
402
# Using Kronecker product method
403
A = np.array([[1, 0, 0, 1],
404
[1, 0, 0, -1],
405
[0, 1, 1, 0],
406
[0, 1j, -1j, 0]])
407
A_inv = np.linalg.inv(A)
408
409
# J tensor J* is 4x4
410
J_kron = np.kron(J, np.conj(J))
411
412
M = np.real(A @ J_kron @ A_inv)
413
return M
414
415
# Mueller matrix for depolarizer
416
def depolarizer_mueller(p):
417
"""Mueller matrix for partial depolarizer with polarization degree p."""
418
return np.diag([1, p, p, p])
419
420
# Mueller matrix for linear polarizer
421
def polarizer_mueller(theta):
422
c2 = np.cos(2*theta)
423
s2 = np.sin(2*theta)
424
return 0.5 * np.array([[1, c2, s2, 0],
425
[c2, c2**2, c2*s2, 0],
426
[s2, c2*s2, s2**2, 0],
427
[0, 0, 0, 0]])
428
429
# Mueller matrix for retarder
430
def retarder_mueller(delta, theta=0):
431
c2 = np.cos(2*theta)
432
s2 = np.sin(2*theta)
433
cd = np.cos(delta)
434
sd = np.sin(delta)
435
return np.array([[1, 0, 0, 0],
436
[0, c2**2 + s2**2*cd, c2*s2*(1-cd), -s2*sd],
437
[0, c2*s2*(1-cd), s2**2 + c2**2*cd, c2*sd],
438
[0, s2*sd, -c2*sd, cd]])
439
440
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
441
442
# Plot 1: Degree of polarization through depolarizer
443
S_in = np.array([1, 1, 0, 0]) # Horizontal polarized
444
dop_values = np.linspace(0, 1, 100)
445
dop_out = []
446
447
for p in dop_values:
448
M = depolarizer_mueller(p)
449
S_out = M @ S_in
450
dop = np.sqrt(S_out[1]**2 + S_out[2]**2 + S_out[3]**2) / S_out[0]
451
dop_out.append(dop)
452
453
axes[0, 0].plot(dop_values, dop_out, 'b-', linewidth=2)
454
axes[0, 0].plot([0, 1], [0, 1], 'r--', linewidth=1.5, alpha=0.7, label='Linear')
455
axes[0, 0].set_xlabel('Depolarizer parameter $p$')
456
axes[0, 0].set_ylabel('Output DOP')
457
axes[0, 0].set_title('Degree of Polarization Through Depolarizer')
458
axes[0, 0].legend()
459
axes[0, 0].grid(True, alpha=0.3)
460
461
# Plot 2: Partially polarized light through polarizer
462
dop_in_values = [0.2, 0.5, 0.8, 1.0]
463
colors_dop = ['red', 'orange', 'green', 'blue']
464
polarizer_angles = np.linspace(0, np.pi, 100)
465
466
for dop_in, color in zip(dop_in_values, colors_dop):
467
# Partially polarized horizontal
468
S_in = np.array([1, dop_in, 0, 0])
469
intensities = []
470
471
for theta in polarizer_angles:
472
M = polarizer_mueller(theta)
473
S_out = M @ S_in
474
intensities.append(S_out[0])
475
476
axes[0, 1].plot(np.rad2deg(polarizer_angles), intensities, color=color, linewidth=2,
477
label=f'DOP = {dop_in:.1f}')
478
479
axes[0, 1].set_xlabel('Polarizer Angle (degrees)')
480
axes[0, 1].set_ylabel('Transmitted Intensity')
481
axes[0, 1].set_title('Partially Polarized Light Through Polarizer')
482
axes[0, 1].legend(fontsize=8)
483
axes[0, 1].grid(True, alpha=0.3)
484
485
# Plot 3: Mueller matrix visualization
486
M_qwp = retarder_mueller(np.pi/2, 0)
487
488
im = axes[1, 0].imshow(M_qwp, cmap='RdBu', vmin=-1, vmax=1)
489
axes[1, 0].set_xticks([0, 1, 2, 3])
490
axes[1, 0].set_yticks([0, 1, 2, 3])
491
axes[1, 0].set_xticklabels(['$S_0$', '$S_1$', '$S_2$', '$S_3$'])
492
axes[1, 0].set_yticklabels(['$S_0$', '$S_1$', '$S_2$', '$S_3$'])
493
axes[1, 0].set_title('Mueller Matrix: QWP at 0$^\\circ$')
494
495
for i in range(4):
496
for j in range(4):
497
val = M_qwp[i, j]
498
axes[1, 0].text(j, i, f'{val:.2f}', ha='center', va='center', fontsize=9)
499
500
# Plot 4: Stokes vector trajectory on Poincare sphere
501
# RCP through rotating HWP
502
hwp_rotation = np.linspace(0, np.pi, 100)
503
S_rcp = np.array([1, 0, 0, 1]) # RCP
504
505
S1_traj = []
506
S2_traj = []
507
S3_traj = []
508
509
for theta in hwp_rotation:
510
M = retarder_mueller(np.pi, theta)
511
S_out = M @ S_rcp
512
S1_traj.append(S_out[1]/S_out[0])
513
S2_traj.append(S_out[2]/S_out[0])
514
S3_traj.append(S_out[3]/S_out[0])
515
516
axes[1, 1].plot(S1_traj, S2_traj, 'b-', linewidth=2)
517
axes[1, 1].plot(S1_traj[0], S2_traj[0], 'go', markersize=8, label='Start')
518
axes[1, 1].plot(S1_traj[-1], S2_traj[-1], 'ro', markersize=8, label='End')
519
# Draw unit circle
520
theta_circle = np.linspace(0, 2*np.pi, 100)
521
axes[1, 1].plot(np.cos(theta_circle), np.sin(theta_circle), 'k--', alpha=0.3)
522
axes[1, 1].set_xlabel('$S_1/S_0$')
523
axes[1, 1].set_ylabel('$S_2/S_0$')
524
axes[1, 1].set_title('Stokes Vector Trajectory (RCP through rotating HWP)')
525
axes[1, 1].legend()
526
axes[1, 1].set_aspect('equal')
527
axes[1, 1].grid(True, alpha=0.3)
528
529
plt.tight_layout()
530
save_plot('mueller_matrix.pdf', 'Mueller matrix formalism: depolarization, partial polarization, and Stokes trajectories.')
531
\end{pycode}
532
533
\section{Optical Activity and Faraday Effect}
534
\begin{pycode}
535
# Optical activity (natural rotation)
536
def optically_active_medium(thickness, specific_rotation, concentration=1):
537
"""Jones matrix for optically active medium."""
538
rotation = specific_rotation * thickness * concentration
539
return rotator(rotation)
540
541
# Faraday rotator
542
def faraday_rotator(thickness, verdet_constant, B_field):
543
"""Jones matrix for Faraday effect."""
544
rotation = verdet_constant * B_field * thickness
545
return rotator(rotation)
546
547
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
548
549
# Plot 1: Optical rotation vs thickness
550
thicknesses = np.linspace(0, 10, 100) # cm
551
specific_rotation = 66.5 # degrees/dm for sucrose
552
concentrations = [0.1, 0.2, 0.5, 1.0] # g/mL
553
colors_conc = ['blue', 'green', 'orange', 'red']
554
555
input_h = linear_pol(0)
556
557
for conc, color in zip(concentrations, colors_conc):
558
rotations = []
559
for t in thicknesses:
560
M = optically_active_medium(t/10, np.deg2rad(specific_rotation), conc)
561
output = M @ input_h
562
# Calculate rotation angle
563
angle = np.arctan2(np.real(output[1]), np.real(output[0]))
564
rotations.append(np.rad2deg(angle))
565
566
axes[0, 0].plot(thicknesses, rotations, color=color, linewidth=2,
567
label=f'c = {conc} g/mL')
568
569
axes[0, 0].set_xlabel('Path length (cm)')
570
axes[0, 0].set_ylabel('Rotation angle (degrees)')
571
axes[0, 0].set_title('Optical Rotation (Sucrose)')
572
axes[0, 0].legend(fontsize=8)
573
axes[0, 0].grid(True, alpha=0.3)
574
575
# Plot 2: Faraday rotation vs magnetic field
576
B_fields = np.linspace(0, 1, 100) # Tesla
577
verdet_constant = 3.8 # rad/(T*m) for terbium gallium garnet
578
thickness = 0.01 # m
579
580
rotation_faraday = verdet_constant * B_fields * thickness * 180 / np.pi
581
582
axes[0, 1].plot(B_fields, rotation_faraday, 'b-', linewidth=2)
583
axes[0, 1].set_xlabel('Magnetic field (T)')
584
axes[0, 1].set_ylabel('Rotation angle (degrees)')
585
axes[0, 1].set_title('Faraday Rotation (TGG, 1 cm)')
586
axes[0, 1].grid(True, alpha=0.3)
587
588
# Plot 3: Optical isolator
589
# Forward: polarizer -> Faraday rotator (45 deg) -> analyzer (45 deg)
590
# Backward: analyzer -> Faraday rotator (45 deg) -> polarizer
591
592
rotation_45 = np.pi/4
593
FR = rotator(rotation_45)
594
P0 = linear_polarizer(0)
595
P45 = linear_polarizer(np.pi/4)
596
597
# Forward pass
598
input_forward = np.array([1, 0])
599
forward_1 = P0 @ input_forward
600
forward_2 = FR @ forward_1
601
forward_out = P45 @ forward_2
602
I_forward = np.abs(forward_out[0])**2 + np.abs(forward_out[1])**2
603
604
# Backward pass (Faraday effect is non-reciprocal)
605
input_backward = np.array([np.cos(np.pi/4), np.sin(np.pi/4)])
606
backward_1 = FR @ input_backward # Same rotation direction
607
backward_out = P0 @ backward_1
608
I_backward = np.abs(backward_out[0])**2 + np.abs(backward_out[1])**2
609
610
bar_positions = [0, 1]
611
bar_heights = [I_forward, I_backward]
612
bar_labels = ['Forward', 'Backward']
613
bar_colors = ['green', 'red']
614
615
axes[1, 0].bar(bar_positions, bar_heights, color=bar_colors, alpha=0.7)
616
axes[1, 0].set_xticks(bar_positions)
617
axes[1, 0].set_xticklabels(bar_labels)
618
axes[1, 0].set_ylabel('Transmitted Intensity')
619
axes[1, 0].set_title('Optical Isolator Performance')
620
axes[1, 0].grid(True, alpha=0.3, axis='y')
621
622
# Add isolation ratio text
623
isolation = 10 * np.log10(I_forward / I_backward) if I_backward > 0 else np.inf
624
625
# Plot 4: Polarization rotation in chiral medium
626
# Different for left and right circular polarizations
627
input_lcp = circular_pol('left')
628
input_rcp = circular_pol('right')
629
630
rotation_angles = np.linspace(0, np.pi, 100)
631
632
# Track phase difference between LCP and RCP
633
lcp_phase = []
634
rcp_phase = []
635
636
for rot in rotation_angles:
637
R = rotator(rot)
638
out_lcp = R @ input_lcp
639
out_rcp = R @ input_rcp
640
641
# Phase is argument of complex amplitude
642
lcp_phase.append(np.angle(out_lcp[0]))
643
rcp_phase.append(np.angle(out_rcp[0]))
644
645
axes[1, 1].plot(np.rad2deg(rotation_angles), np.rad2deg(lcp_phase), 'b-', linewidth=2, label='LCP')
646
axes[1, 1].plot(np.rad2deg(rotation_angles), np.rad2deg(rcp_phase), 'r-', linewidth=2, label='RCP')
647
axes[1, 1].set_xlabel('Rotator angle (degrees)')
648
axes[1, 1].set_ylabel('Output phase (degrees)')
649
axes[1, 1].set_title('Circular Polarization Through Rotator')
650
axes[1, 1].legend()
651
axes[1, 1].grid(True, alpha=0.3)
652
653
plt.tight_layout()
654
save_plot('optical_activity.pdf', 'Optical activity and Faraday effect: rotation, isolators, and circular birefringence.')
655
\end{pycode}
656
657
\section{Birefringence and Stress Analysis}
658
\begin{pycode}
659
# Photoelastic stress analysis
660
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
661
662
# Plot 1: Birefringent material between crossed polarizers
663
# Intensity depends on retardance and orientation
664
retardances = np.linspace(0, 4*np.pi, 100)
665
orientations = [0, np.pi/8, np.pi/4, 3*np.pi/8]
666
colors_orient = ['blue', 'green', 'orange', 'red']
667
668
input_h = linear_pol(0)
669
analyzer_v = linear_polarizer(np.pi/2)
670
671
for theta, color in zip(orientations, colors_orient):
672
intensities = []
673
for delta in retardances:
674
WP = wave_plate(delta, theta)
675
output = analyzer_v @ WP @ input_h
676
I = np.abs(output[0])**2 + np.abs(output[1])**2
677
intensities.append(I)
678
679
axes[0, 0].plot(retardances/np.pi, intensities, color=color, linewidth=2,
680
label=f'$\\theta = {np.rad2deg(theta):.0f}^\\circ$')
681
682
axes[0, 0].set_xlabel('Retardance ($\\pi$)')
683
axes[0, 0].set_ylabel('Transmitted Intensity')
684
axes[0, 0].set_title('Birefringent Material Between Crossed Polarizers')
685
axes[0, 0].legend(fontsize=8)
686
axes[0, 0].grid(True, alpha=0.3)
687
688
# Plot 2: Simulated photoelastic pattern
689
x = np.linspace(-5, 5, 200)
690
y = np.linspace(-5, 5, 200)
691
X, Y = np.meshgrid(x, y)
692
693
# Simulated stress field (disk under diametral compression)
694
sigma_1 = 1 / (X**2 + Y**2 + 0.5) # Radial pattern
695
sigma_2 = -0.5 * sigma_1
696
stress_diff = sigma_1 - sigma_2
697
698
# Retardance proportional to stress difference
699
C = 1 # Stress-optic coefficient
700
d = 1 # Thickness
701
delta_stress = 2 * np.pi * C * d * stress_diff
702
703
# Fast axis along principal stress
704
theta_stress = 0.5 * np.arctan2(Y, X)
705
706
# Intensity between crossed polarizers
707
I_photo = np.sin(2 * theta_stress)**2 * np.sin(delta_stress/2)**2
708
709
im = axes[0, 1].pcolormesh(X, Y, I_photo, cmap='hot', shading='auto')
710
axes[0, 1].set_xlabel('$x$')
711
axes[0, 1].set_ylabel('$y$')
712
axes[0, 1].set_title('Photoelastic Stress Pattern')
713
axes[0, 1].set_aspect('equal')
714
715
# Plot 3: Isochromatic fringes (constant retardance)
716
# Represent different orders
717
fringe_orders = np.arange(0, 5)
718
for m in fringe_orders:
719
# Find contour where delta = 2*pi*m
720
contour_level = m
721
axes[1, 0].contour(X, Y, delta_stress/(2*np.pi), levels=[m], colors='k', linewidths=1)
722
723
axes[1, 0].set_xlabel('$x$')
724
axes[1, 0].set_ylabel('$y$')
725
axes[1, 0].set_title('Isochromatic Fringes')
726
axes[1, 0].set_aspect('equal')
727
728
# Plot 4: Liquid crystal retardance
729
# Voltage-controlled birefringence
730
voltages = np.linspace(0, 10, 100) # V
731
732
# Simplified model: retardance decreases with voltage
733
V_threshold = 1 # Threshold voltage
734
delta_0 = 2 * np.pi # Maximum retardance
735
delta_lc = delta_0 * np.exp(-((voltages - V_threshold)/3)**2)
736
delta_lc[voltages < V_threshold] = delta_0
737
738
# Transmission between crossed polarizers at 45 degrees
739
T_lc = np.sin(delta_lc/2)**2
740
741
axes[1, 1].plot(voltages, T_lc, 'b-', linewidth=2)
742
axes[1, 1].set_xlabel('Applied Voltage (V)')
743
axes[1, 1].set_ylabel('Normalized Transmission')
744
axes[1, 1].set_title('Liquid Crystal Cell Response')
745
axes[1, 1].grid(True, alpha=0.3)
746
747
plt.tight_layout()
748
save_plot('birefringence.pdf', 'Birefringence applications: photoelasticity, isochromatics, and LC response.')
749
\end{pycode}
750
751
\section{Results Summary}
752
\begin{pycode}
753
# Generate results table
754
print(r'\begin{table}[htbp]')
755
print(r'\centering')
756
print(r'\caption{Summary of Polarization Parameters}')
757
print(r'\begin{tabular}{lll}')
758
print(r'\toprule')
759
print(r'Configuration & Parameter & Result \\')
760
print(r'\midrule')
761
print(r"Malus's Law & Maximum transmission & 1.0 \\")
762
print(r"Crossed polarizers & Transmission & 0 \\")
763
print(f"Three polarizers (0$^\\circ$, 45$^\\circ$, 90$^\\circ$) & Transmission & {0.25:.2f} \\\\")
764
print(r"QWP at 45$^\circ$ & Linear $\rightarrow$ circular & Yes \\")
765
print(r"HWP at $\theta$ & Rotation & $2\theta$ \\")
766
print(r'\midrule')
767
print(f"Optical isolator & Forward transmission & {I_forward:.2f} \\\\")
768
print(f"Optical isolator & Backward transmission & {I_backward:.3f} \\\\")
769
print(f"Sucrose specific rotation & $[\\alpha]_D$ & {specific_rotation:.1f}$^\\circ$/dm \\\\")
770
print(f"TGG Verdet constant & $V$ & {verdet_constant:.1f} rad/(T$\\cdot$m) \\\\")
771
print(r'\bottomrule')
772
print(r'\end{tabular}')
773
print(r'\end{table}')
774
\end{pycode}
775
776
\section{Statistical Summary}
777
\begin{itemize}
778
\item \textbf{Malus's Law}: $I = I_0\cos^2\theta$ verified
779
\item \textbf{Quarter-wave plate}: Converts linear to circular polarization at 45$^\circ$
780
\item \textbf{Half-wave plate}: Rotates polarization by $2\theta$
781
\item \textbf{Stokes parameters}: $S_0^2 = S_1^2 + S_2^2 + S_3^2$ for fully polarized light
782
\item \textbf{Three-polarizer transmission}: 25\% maximum at 45$^\circ$ intermediate
783
\item \textbf{N-polarizer limit}: Approaches 100\% as $N \to \infty$
784
\item \textbf{Optical isolator}: Non-reciprocal due to Faraday effect
785
\end{itemize}
786
787
\section{Conclusion}
788
Jones and Mueller calculus provide complete descriptions of polarization transformations for coherent and partially coherent light. Circular polarization requires a phase difference of $\pi/2$ between orthogonal components. Wave plates are essential for polarization control in optical systems, from LCD displays to optical communications. The Faraday effect enables non-reciprocal devices like optical isolators, critical for protecting laser sources. Photoelasticity exploits stress-induced birefringence for mechanical stress analysis. Understanding polarization is fundamental to many optical technologies including polarimetry, ellipsometry, and quantum key distribution.
789
790
\end{document}
791
792