记得第一次在物理课本上看到折射率公式时,总觉得那些符号和数字像是天书。直到某天我用Matplotlib画出了光线穿过不同介质的动态路径,突然就理解了为什么游泳池底看起来比实际更浅——这种"啊哈时刻"正是我想带给各位的体验。本文将带你用Python代码构建一个可交互的折射率模拟器,不仅能直观展示绝对折射率与相对折射率的区别,还能实时观察光速变化对折射角度的影响。
工欲善其事,必先利其器。我们需要以下工具链:
bash复制pip install numpy matplotlib ipywidgets
推荐使用Jupyter Notebook进行交互式实验,以下代码创建一个介质折射率对照表:
python复制import pandas as pd
materials = {
'介质': ['真空', '空气', '水', '乙醇', '玻璃(冕牌)', '钻石'],
'绝对折射率': [1.0, 1.000293, 1.333, 1.361, 1.52, 2.417],
'分子结构': ['无', '稀疏气体', '氢键网络', '有机分子', '硅氧四面体', '碳晶体']
}
df = pd.DataFrame(materials)
print(df.to_markdown(index=False))
| 介质 | 绝对折射率 | 分子结构 |
|---|---|---|
| 真空 | 1.0 | 无 |
| 空气 | 1.000293 | 稀疏气体 |
| 水 | 1.333 | 氢键网络 |
| 乙醇 | 1.361 | 有机分子 |
| 玻璃(冕牌) | 1.52 | 硅氧四面体 |
| 钻石 | 2.417 | 碳晶体 |
提示:折射率与介质密度正相关,但并非线性关系——分子极化率才是关键因素
不同波长的光在相同介质中折射率也不同(色散现象),用以下代码模拟:
python复制import numpy as np
import matplotlib.pyplot as plt
wavelengths = np.linspace(380, 780, 100) # 可见光波段(纳米)
n_water = 1.333 + 0.000014/wavelengths**2 # 水的色散公式简化版
plt.figure(figsize=(10,6))
plt.plot(wavelengths, n_water, label='水的折射率')
plt.xlabel('波长 (nm)'), plt.ylabel('折射率')
plt.title('水的色散曲线'), plt.grid(True)
plt.legend(), plt.show()
根据定义,绝对折射率n = c/v,我们创建介质光速计算函数:
python复制def calculate_speed(n_medium):
c = 3e8 # 真空光速(m/s)
return c / n_medium
speeds = [calculate_speed(n) for n in df['绝对折射率']]
df['光速(m/s)'] = speeds
print(df[['介质', '绝对折射率', '光速(m/s)']].to_markdown(index=False))
| 介质 | 绝对折射率 | 光速(m/s) |
|---|---|---|
| 真空 | 1.0 | 3.000000e+08 |
| 空气 | 1.000293 | 2.999121e+08 |
| 水 | 1.333 | 2.250563e+08 |
| 乙醇 | 1.361 | 2.204261e+08 |
| 玻璃(冕牌) | 1.52 | 1.973684e+08 |
| 钻石 | 2.417 | 1.241208e+08 |
用Snell定律实现光线追踪模拟器:
python复制def snell_law(n1, n2, theta1):
theta1_rad = np.radians(theta1)
theta2_rad = np.arcsin(n1/n2 * np.sin(theta1_rad))
return np.degrees(theta2_rad)
def plot_refraction(n1, n2, angle=30):
# 入射光线
x_incident = [-1, 0]
y_incident = [np.tan(np.radians(angle)), 0]
# 折射光线
refr_angle = snell_law(n1, n2, angle)
x_refracted = [0, 1]
y_refracted = [0, np.tan(np.radians(refr_angle))]
plt.figure(figsize=(8,6))
plt.plot(x_incident, y_incident, 'r-', linewidth=2, label=f'入射角: {angle}°')
plt.plot(x_refracted, y_refracted, 'b-', linewidth=2,
label=f'折射角: {refr_angle:.2f}°')
plt.axhline(0, color='k', linestyle='--')
plt.text(0.5, 0.2, f'n={n2}', fontsize=12, ha='center')
plt.text(-0.5, 0.2, f'n={n1}', fontsize=12, ha='center')
plt.legend(), plt.axis('equal'), plt.grid(True)
plt.title(f'折射率变化: {n1} → {n2}'), plt.show()
plot_refraction(1.0, 1.5) # 真空到玻璃
相对折射率n₁₂ = n₂/n₁ = v₁/v₂,实现代码如下:
python复制def relative_refractive_index(n1, n2):
return n2 / n1 # 等价于 calculate_speed(n1)/calculate_speed(n2)
# 示例:空气到水的相对折射率
n_air_to_water = relative_refractive_index(1.000293, 1.333)
print(f"空气到水的相对折射率: {n_air_to_water:.4f}")
模拟光线穿过多层介质的情况:
python复制def multi_layer_refraction(layers, initial_angle=30):
angles = [initial_angle]
for i in range(len(layers)-1):
new_angle = snell_law(layers[i], layers[i+1], angles[-1])
angles.append(new_angle)
# 绘制光路
plt.figure(figsize=(10,6))
y_pos = 0
for i, (n, angle) in enumerate(zip(layers, angles)):
length = 1 if i==0 else 1/(i+1) # 视觉调整
x = [i, i+length]
y = [y_pos, y_pos + length*np.tan(np.radians(angle))]
plt.plot(x, y, 'o-', label=f'层{i+1}: n={n}, θ={angle:.1f}°')
y_pos = y[-1]
plt.xlabel('介质层'), plt.ylabel('光路轨迹')
plt.title('多层介质折射光路'), plt.legend()
plt.grid(True), plt.show()
multi_layer_refraction([1.0, 1.33, 1.52, 2.4]) # 真空→水→玻璃→钻石
使用ipywidgets构建交互式模拟器:
python复制from ipywidgets import interact, FloatSlider
@interact(
n1=FloatSlider(1.0, min=1.0, max=3.0, step=0.1),
n2=FloatSlider(1.5, min=1.0, max=3.0, step=0.1),
angle=FloatSlider(30, min=0, max=89, step=1)
)
def interactive_refraction(n1, n2, angle):
plot_refraction(n1, n2, angle)
当n₁ > n₂时会发生全反射现象:
python复制def critical_angle(n1, n2):
return np.degrees(np.arcsin(n2/n1)) if n1 > n2 else 90
n_water, n_air = 1.333, 1.000293
theta_c = critical_angle(n_water, n_air)
print(f"水到空气的临界角: {theta_c:.2f}°")
# 验证全反射
plot_refraction(n_water, n_air, angle=theta_c+5) # 超过临界角
阶跃折射率光纤的模拟:
python复制def fiber_optic_simulation(core_n=1.46, cladding_n=1.44):
theta_c = critical_angle(core_n, cladding_n)
acceptance_angle = np.degrees(np.arcsin(np.sqrt(core_n**2 - cladding_n**2)))
print(f"临界角: {theta_c:.2f}°")
print(f"接收角: {acceptance_angle:.2f}°")
# 绘制光纤结构
fig, ax = plt.subplots(figsize=(8,8))
circle1 = plt.Circle((0,0), 1, color='lightblue', alpha=0.5)
circle2 = plt.Circle((0,0), 1.2, color='gray', alpha=0.2)
ax.add_artist(circle2), ax.add_artist(circle1)
# 绘制光路
for angle in np.linspace(-acceptance_angle, acceptance_angle, 5):
rad_angle = np.radians(angle)
x = [-1.5*np.sin(rad_angle), 1.5*np.sin(rad_angle)]
y = [1.5*np.cos(rad_angle), -1.5*np.cos(rad_angle)]
ax.plot(x, y, 'r-', alpha=0.7)
ax.set_xlim(-2,2), ax.set_ylim(-2,2)
ax.set_aspect('equal'), ax.set_title('阶跃折射率光纤光路')
plt.show()
fiber_optic_simulation()
设计一个简单的双凸透镜模型:
python复制def lens_design(n_lens=1.5, curvature=0.5):
# 创建透镜表面
theta = np.linspace(-np.pi/3, np.pi/3, 100)
x = curvature * np.sin(theta)
y = curvature * np.cos(theta) - curvature/2
plt.figure(figsize=(10,6))
plt.plot(x, y, 'b-', linewidth=2) # 前表面
plt.plot(-x, y, 'b-', linewidth=2) # 后表面
# 模拟平行光入射
for h in np.linspace(-0.4, 0.4, 7):
# 入射光
plt.plot([-2, x[np.argmin(np.abs(y-h))]],
[h, h], 'k--')
# 折射光(简化计算)
focal_length = curvature/(n_lens-1)/2
plt.plot([x[np.argmin(np.abs(y-h))], focal_length],
[h, 0], 'r-')
plt.xlim(-1,1), plt.ylim(-0.6,0.6)
plt.title(f'双凸透镜模拟 (n={n_lens})'), plt.grid(True)
plt.show()
lens_design(n_lens=1.8)
在完成这些实验后,我发现最有趣的是调整钻石的折射率参数时看到的光线弯曲效果——那种夸张的偏折角度瞬间解释了为什么钻石会有如此璀璨的火彩。建议读者尝试修改代码中的折射率参数,比如创建一个n=0.8的"超材料"看看会出现什么反常识现象,这种亲手打破物理常识的体验正是编程模拟的魅力所在。