Spaces:
Sleeping
Sleeping
File size: 8,037 Bytes
e198f4f |
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 |
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 (Sprinter)')
ax.plot(x, t_refracted, 'g--', label=f'Refracted Wave (Subway Rider)')
ax.plot(x, t_reflected, 'm-.', label='Reflected Wave (Bouncy Ball)')
ax.plot(x, t_first_arrival, 'r-', linewidth=3, label='WINNER (First Arrival)')
if xc < x_max:
ax.axvline(x=xc, color='k', linestyle=':', label=f'Crossover Point = {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 Point = {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("The Great Seismic Race: T-X Plot", fontsize=16)
ax.set_xlabel("Race 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"""
### 📣 賽況播報:認識你的選手!
一場精彩的地底競速正在上演!三位選手的表現完全取決於您設計的賽道:
- 🏃♂️ **直達波 (The Sprinter)**:
- **策略**: 硬派跑法!只沿著地表(速度V1)直線衝刺。
- **特性**: 它是最老實的選手,在短距離賽中通常保持領先。但隨著賽道變長,它可能會被下方的捷運選手超越。
- bouncing ball
- **策略**: 垂直彈跳!它會潛入地底,在第一個地層邊界(深度h)觸地反彈,然後返回地表。
- **特性**: 它的路徑像個 V 字形,因為需要向下再向上,所以它永遠無法贏得比賽(成為初達波),但它忠實地回報了地層的深度資訊!
- 🚄 **折射波 (The Subway Rider)**:
- **策略**: 抄捷徑!這位是「機會主義者」。它知道地底下有一條速度為 `V2` 的「高速捷運」。
- **臨界點 (Critical Point)**: 在 **{xcrit:.1f} m** 處,是它能進入高速捷運的最早入口。它必須以一個完美的**臨界角 ({theta_c_deg:.2f}°)** 切入才能搭上車。
- **截時 (Intercept Time)**: 搭捷運總要花時間下到月台吧?這 **{ti*1000:.1f} ms** 就是它為了走捷徑付出的「前期時間成本」。這個時間也巧妙地告訴我們月台有多深(`h`)!
---
### 🏁 勝負的交叉點:交越距離 (Crossover Distance)
在 **{xc:.1f} m** 這個神奇的位置,短跑選手被捷運選手追上了!
- **在此之前**: 直達波(短跑選手)最快。
- **在此之後**: 折射波(捷運選手)才是永遠的贏家!
這個交叉點是我們解開地層秘密最重要的線索之一。
"""
return fig, results_md
# --- Gradio 介面設定 (生動有趣版) ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 地心震波奇幻之旅:地底競速大賽 🌍🏆")
gr.Markdown(
"""
**準備好了嗎,地球探險家?** 你現在正操控著一台虛擬震測儀,就像是地球的「超音波」掃描機。我們的任務是透過發射震波,並聆聽它們的回音,來揭開地底深處的秘密。
你將扮演三位賽跑選手——**直達波**、**反射波**與**折射波**——的總教練。調整下方的賽道參數,看看誰會在這場地底競速中脫穎而出!
"""
)
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. **斜率的秘密**: 試著把 `V1` 或 `V2` 調快,看看發生了什麼?賽道變平坦了!沒錯,**速度越快,跑完相同距離的時間就越短,所以線條(斜率)就越平緩。**
2. **「截時」的詛咒**: 如果你把地層 `h` 加厚,就像把捷運月台蓋得更深。觀察折射波的綠線,是不是整條都往上平移了?這就是增加的「通勤時間」!
3. **拉開差距**: 怎麼讓短跑選手領先久一點?你可以把 `h` 加厚(起跑線拉遠),或是讓 `V1` 和 `V2` 速度更接近(捷運沒快多少),看看那個黑色的交叉點是不是就往右邊跑了?
4. **尋找捷運入口**: 觀察青色的「臨界點」。它永遠落在彈跳球選手(反射波)的路線上。這是物理定律給我們的一個有趣彩蛋!
5. **取消捷運**: 如果你讓地底捷運的速度 `V2` 比地表跑道 `V1` 還要慢呢?你會發現捷運選手直接罷工不出現了!因為這樣抄捷徑根本不划算。
<footer>
<p style="text-align:center; color:grey;">地球物理小教室 © 2025 - 由 Gemini 根據課程文件生成</p>
</footer>
"""
)
if __name__ == "__main__":
demo.launch()
|