import gradio as gr import numpy as np import matplotlib.pyplot as plt # --- 核心計算與繪圖函數 --- def plot_seismic_refraction(v1, v2, h, x_max): """ 根據輸入的地層參數,計算並繪製折射震測的走時曲線。 圖表內的標籤已改為英文。 """ # 物理條件檢查:折射必須 V2 > V1 if v2 <= v1: fig, ax = plt.subplots(figsize=(10, 6)) ax.text(0.5, 0.5, 'Error: V2 must be greater than V1 for critical refraction to occur.', ha='center', va='center', fontsize=12, color='red') ax.set_xlabel("Distance (m)") ax.set_ylabel("Travel Time (s)") ax.grid(True) ax.set_title("Travel-Time Curve") return fig, "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1)。" # 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)) # 2. 準備繪圖數據 x = np.linspace(0, x_max, 500) t_direct = x / v1 t_refracted = (x / v2) + ti 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_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') ax.plot(0, ti, 'mo', markersize=8, label=f'Intercept Time = {ti*1000:.1f} ms') # --- 英文圖表/座標軸標題修改處 --- ax.set_title("Interactive Seismic Refraction T-X Plot", 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) ax.set_ylim(0, max(t_direct) * 1.1) plt.tight_layout() # 4. 準備輸出的說明文字 (這部分維持中文) results_md = f""" ### 🔬 分析結果 根據您設定的參數,我們計算出以下關鍵物理量: - **臨界角 (Critical Angle, θc)**: - 公式: `θc = arcsin(V1 / V2)` - 計算: `arcsin({v1:.0f} / {v2:.0f})` = **{theta_c_deg:.2f}°** - *這是產生「抄捷徑」折射波的關鍵入射角度。* - **截時 (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`, 觀察右側的「走時-距離圖」如何即時變化,並從中學習折射震測法的核心原理! """ ) 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_seismic_refraction, inputs=[v1_slider, v2_slider, h_slider, xmax_slider], outputs=[plot_output, results_output] ) gr.Markdown( """ --- ### 學習重點 1. **觀察斜率**: 直達波的斜率是 `1/V1`,折射波的斜率是 `1/V2`。試著調整 `V1` 和 `V2`,看看線條的陡峭程度如何變化?(速度越快,線越陡,斜率越小)。 2. **觀察截時 (Intercept Time)**: 試著只增加厚度 `h`,看看 Y 軸上的截距有什麼變化?(厚度越厚,截時越大)。 3. **觀察交越距離 (Crossover Distance)**: 試著增加 `h` 或減小 `V2` 與 `V1` 的速度差,看看交叉點如何向右移動? 4. **必要條件**: 試著將 `V2` 調整到比 `V1` 小,看看會發生什麼事?你會發現,臨界折射的現象消失了! """ ) if __name__ == "__main__": demo.launch()