Spaces:
Sleeping
Sleeping
File size: 7,635 Bytes
80df50a d390991 fd63fae 80df50a fd63fae 80df50a fd63fae 70d7494 fd63fae 70d7494 fd63fae 80df50a fd63fae d390991 fd63fae 80df50a d390991 80df50a 70d7494 80df50a fd63fae 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 70d7494 d390991 70d7494 80df50a d390991 80df50a 70d7494 fd63fae 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 fd63fae d390991 fd63fae d390991 70d7494 d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a d390991 80df50a 70d7494 80df50a d390991 80df50a fd63fae |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt
# --- 核心計算與繪圖函數 (已加入臨界距離) ---
def plot_travel_times(v1, v2, h, x_max):
"""
根據輸入的地層參數,計算並繪製直達波、反射波與折射波的走時曲線。
圖表內的標籤為英文。
"""
# 物理條件檢查:折射必須 V2 > V1
if v2 <= v1:
fig, ax = plt.subplots(figsize=(10, 6))
# 即使沒有折射,我們仍然可以顯示直達波和反射波
x = np.linspace(0, x_max, 500)
t_direct = x / v1
t_reflected = np.sqrt(x**2 + (2 * h)**2) / v1
ax.plot(x, t_direct, 'b--', label='Direct Wave')
ax.plot(x, t_reflected, 'm-.', label='Reflected Wave')
ax.text(0.5, 0.5, 'V2 <= V1: Critical refraction does not occur.\nOnly Direct and Reflected waves are shown.',
ha='center', va='center', fontsize=12, color='orange')
ax.set_xlabel("Distance (m)")
ax.set_ylabel("Travel Time (s)")
ax.grid(True)
ax.legend()
ax.set_title("Travel-Time Curve (No Refraction)")
results_md = "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1) 才能產生臨界折射現象。"
return fig, results_md
# 1. 計算關鍵物理量
theta_c_rad = np.arcsin(v1 / v2)
theta_c_deg = np.rad2deg(theta_c_rad)
ti = (2 * h * np.cos(theta_c_rad)) / v1
xc = 2 * h * np.sqrt((v2 + v1) / (v2 - v1))
xcrit = 2 * h * np.tan(theta_c_rad) # <<< 新增:臨界距離計算
# 2. 準備繪圖數據
x = np.linspace(0, x_max, 500)
t_direct = x / v1
t_refracted = (x / v2) + ti
t_reflected = np.sqrt(x**2 + (2 * h)**2) / v1
t_first_arrival = np.minimum(t_direct, t_refracted)
# 3. 使用 Matplotlib 繪圖
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, t_direct, 'b--', label=f'Direct Wave (Slope 1/{v1:.0f})')
ax.plot(x, t_refracted, 'g--', label=f'Refracted Wave (Slope 1/{v2:.0f})')
ax.plot(x, t_reflected, 'm-.', label='Reflected Wave')
ax.plot(x, t_first_arrival, 'r-', linewidth=3, label='First Arrival')
# 標示交越距離
if xc < x_max:
ax.axvline(x=xc, color='k', linestyle=':', label=f'Crossover Distance = {xc:.1f} m')
ax.plot(xc, xc/v1, 'ko')
# <<< 新增:標示臨界距離 >>>
if xcrit < x_max:
tcrit = (xcrit / v2) + ti # 計算臨界點的時間
ax.axvline(x=xcrit, color='c', linestyle=':', label=f'Critical Distance = {xcrit:.1f} m')
ax.plot(xcrit, tcrit, 'co', markersize=8) # 在臨界點加上青色圓點
ax.plot(0, ti, 'mo', markersize=8, label=f'Intercept Time = {ti*1000:.1f} ms')
ax.set_title("Interactive Seismic T-X Plot (Reflection + Refraction)", fontsize=16)
ax.set_xlabel("Distance (m)", fontsize=12)
ax.set_ylabel("Travel Time (s)", fontsize=12)
ax.legend()
ax.grid(True)
ax.set_xlim(0, x_max)
max_time = np.max([t_direct[-1], t_refracted[-1], t_reflected[-1]])
ax.set_ylim(0, max_time * 1.1)
plt.tight_layout()
# 4. 準備輸出的說明文字
results_md = f"""
### 🔬 分析結果
根據您設定的參數,我們計算出以下關鍵物理量:
- **直達波 (Direct Wave)**:
- 公式: `t = x / V1`
- *震波直接沿著地表傳遞,走時圖為一條過原點的直線。*
- **反射波 (Reflected Wave)**:
- 公式: `t = sqrt(x² + (2h)²) / V1`
- *震波向下傳至界面後反射回地表。走時圖為一條雙曲線。*
- **折射波 (Refracted Wave)**:
- **臨界角 (Critical Angle, θc)**:
- 公式: `θc = arcsin(V1 / V2)`
- 計算: `arcsin({v1:.0f} / {v2:.0f})` = **{theta_c_deg:.2f}°**
- **臨界距離 (Critical Distance, Xcrit)**:
- 公式: `Xcrit = 2 * h * tan(θc)`
- 計算: `2 * {h:.0f} * tan({theta_c_deg:.2f}°) ` = **{xcrit:.1f} m**
- *這是能夠在地表接收到第一道臨界折射波的「最短水平距離」。*
- **截時 (Intercept Time, tᵢ)**:
- 公式: `tᵢ = (2 * h * cos(θc)) / V1`
- 計算: `(2 * {h:.0f} * cos({theta_c_deg:.2f}°)) / {v1:.0f}` = **{ti*1000:.1f} ms**
- *折射波走時線在時間軸上的截距,隱含了第一層的厚度資訊。*
- **交越距離 (Crossover Distance, Xc)**:
- 公式: `Xc = 2 * h * sqrt((V2 + V1) / (V2 - V1))`
- 計算: `2 * {h:.0f} * sqrt(({v2:.0f} + {v1:.0f}) / ({v2:.0f} - {v1:.0f}))` = **{xc:.1f} m**
- *在此距離上,直達波和折射波同時抵達。超過此距離後,折射波會先到。*
"""
return fig, results_md
# --- Gradio 介面設定 (已更新標題與說明) ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 地心震波奇幻之旅:互動震測走時曲線實驗室 🌍")
gr.Markdown(
"""
歡迎來到地球物理小教室!這個互動工具可以模擬一個簡單的雙層地層模型中的震波傳遞。
您可以透過下方的滑桿來調整第一層速度 `V1`、第二層速度 `V2` 以及第一層的厚度 `h`。
觀察右側的「走時-距離圖」如何即時變化,並從中學習**直達波 (Direct)**、**反射波 (Reflected)** 與**折射波 (Refracted)** 的核心原理!
"""
)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### ⚙️ 調整地層參數")
v1_slider = gr.Slider(label="V1: 第一層速度 (m/s)", minimum=300, maximum=3000, value=800, step=50)
v2_slider = gr.Slider(label="V2: 第二層速度 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
h_slider = gr.Slider(label="h: 第一層厚度 (m)", minimum=5, maximum=100, value=20, step=1)
xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=500, value=250, step=10)
submit_btn = gr.Button("產生圖表", variant="primary")
with gr.Column(scale=2):
gr.Markdown("### 📊 走時-距離圖 (T-X Plot)")
plot_output = gr.Plot()
with gr.Row():
results_output = gr.Markdown("### 🔬 分析結果\n請調整參數並點擊按鈕以顯示計算結果。")
# --- 事件監聽 ---
submit_btn.click(
fn=plot_travel_times,
inputs=[v1_slider, v2_slider, h_slider, xmax_slider],
outputs=[plot_output, results_output]
)
gr.Markdown(
"""
---
### 學習重點
1. **觀察斜率**: 直達波的斜率是 `1/V1`,折射波的斜率是 `1/V2`。
2. **觀察截時 (Intercept Time)**: 增加厚度 `h`,觀察折射波在 Y 軸上的截距如何變大。
3. **觀察交越距離 (Crossover Distance)**: 增加 `h` 或減小 `V2` 與 `V1` 的速度差,觀察交叉點如何向右移動。
4. **觀察反射波**: 注意反射波(洋紅色點虛線)是一條雙曲線,其曲率與 `h` 和 `V1` 有關。
5. **尋找臨界點**: 觀察**臨界距離**(青色虛線)的位置。這是折射波物理上「誕生」的地方。注意看,它正好落在反射波的曲線上!
6. **必要條件**: 將 `V2` 調整到比 `V1` 小,臨界折射現象會消失,只剩下直達波與反射波!
<footer>
<p style="text-align:center; color:grey;">地球物理小教室 © 2025 - 由 Gemini 根據課程文件生成</p>
</footer>
"""
)
if __name__ == "__main__":
demo.launch()
|