Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,8 +18,10 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
|
|
| 18 |
if v2 <= v1:
|
| 19 |
fig1, ax1 = plt.subplots(figsize=(10, 6))
|
| 20 |
ax1.text(0.5, 0.5, 'Error: V2 must be greater than V1', ha='center', va='center', color='red')
|
|
|
|
| 21 |
fig2, ax2 = plt.subplots(figsize=(10, 5))
|
| 22 |
-
ax2.text(0.5, 0.5, 'Please ensure V2 > V1', ha='center', va='center', color='red')
|
|
|
|
| 23 |
return fig1, fig2, "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1)。"
|
| 24 |
|
| 25 |
theta_c_rad = np.arcsin(v1 / v2)
|
|
@@ -53,7 +55,7 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
|
|
| 53 |
|
| 54 |
# === PART 3: 繪製視覺化震測剖面圖 (Plot 2) ===
|
| 55 |
fig2, ax2 = plt.subplots(figsize=(10, 5))
|
| 56 |
-
receiver_x = np.linspace(0, x_max, num_receivers)
|
| 57 |
|
| 58 |
# 計算每個測站的抵達時間
|
| 59 |
t_direct_rx = receiver_x / v1
|
|
@@ -64,16 +66,16 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
|
|
| 64 |
wavelet_duration = 0.08
|
| 65 |
wavelet_t = np.linspace(0, wavelet_duration, 100)
|
| 66 |
|
| 67 |
-
for i in range(num_receivers):
|
| 68 |
-
# 繪製反射波震波
|
| 69 |
wavelet_amp_refl = ricker_wavelet(wavelet_t, f=40) * gain
|
| 70 |
x_trace_refl = receiver_x[i] + wavelet_amp_refl
|
| 71 |
y_trace_refl = t_reflected_rx[i] - wavelet_duration/2 + wavelet_t
|
| 72 |
ax2.plot(x_trace_refl, y_trace_refl, 'k-', linewidth=1)
|
| 73 |
ax2.fill_betweenx(y_trace_refl, receiver_x[i], x_trace_refl, where=(x_trace_refl > receiver_x[i]), color='black')
|
| 74 |
|
| 75 |
-
# 繪製初達波震波
|
| 76 |
-
wavelet_amp_first = ricker_wavelet(wavelet_t, f=30) * gain * 1.2
|
| 77 |
x_trace_first = receiver_x[i] + wavelet_amp_first
|
| 78 |
y_trace_first = t_first_arrival_rx[i] - wavelet_duration/2 + wavelet_t
|
| 79 |
ax2.plot(x_trace_first, y_trace_first, 'r-', linewidth=1.5)
|
|
@@ -84,7 +86,7 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
|
|
| 84 |
ax2.plot(0, 0, 'r*', markersize=20, label='Source')
|
| 85 |
ax2.plot(receiver_x, np.zeros_like(receiver_x), 'kv', markersize=8, label='Receivers')
|
| 86 |
|
| 87 |
-
ax2.set_title(f"2. Visualized Seismic Profile ({num_receivers} Traces)", fontsize=16, loc='left')
|
| 88 |
ax2.set_xlabel("Distance (m)")
|
| 89 |
ax2.set_ylabel("Two-Way Time (s)")
|
| 90 |
ax2.set_xlim(-x_max * 0.05, x_max * 1.05)
|
|
@@ -95,13 +97,32 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
|
|
| 95 |
fig1.tight_layout()
|
| 96 |
|
| 97 |
# === PART 4: 準備輸出的說明文字 ===
|
| 98 |
-
results_md = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
return fig1, fig2, results_md
|
| 100 |
|
| 101 |
# --- Gradio 介面設定 ---
|
| 102 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 103 |
-
gr.Markdown(
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
with gr.Row():
|
| 107 |
with gr.Column(scale=1):
|
|
@@ -113,7 +134,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 113 |
gr.Markdown("### ⚙️ 2. 佈放儀器與顯示設定")
|
| 114 |
xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=500, value=250, step=10)
|
| 115 |
receivers_slider = gr.Slider(label="測站數量", minimum=5, maximum=100, value=40, step=1)
|
| 116 |
-
gain_slider = gr.Slider(label="剖面增益 (Display Gain)", minimum=1, maximum=20, value=5, step=1)
|
| 117 |
|
| 118 |
submit_btn = gr.Button("🚀 開始探勘!", variant="primary")
|
| 119 |
|
|
@@ -122,19 +143,22 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 122 |
plot_output1 = gr.Plot(label="走時-距離圖 (T-X Plot)")
|
| 123 |
plot_output2 = gr.Plot(label="視覺化震測剖面圖 (Visualized Seismic Profile)")
|
| 124 |
|
| 125 |
-
|
|
|
|
| 126 |
|
| 127 |
# --- 事件監聽 ---
|
|
|
|
|
|
|
| 128 |
submit_btn.click(
|
| 129 |
fn=plot_seismic_exploration,
|
| 130 |
-
inputs=[v1_slider, v2_slider, h_slider, xmax_slider, receivers_slider, gain_slider],
|
| 131 |
-
outputs=[plot_output1, plot_output2,
|
| 132 |
)
|
| 133 |
|
| 134 |
gr.Markdown(
|
| 135 |
"""
|
| 136 |
---
|
| 137 |
-
### 📖 剖面圖是如何誕生的?
|
| 138 |
下方的 **視覺化震測剖面圖** 完美模擬了真實的探勘情境。每一條黑色的垂直線代表一個**測站 (Receiver)**,它記錄到的訊號就是一條帶有**震波 (Wiggle)** 的**震波線 (Trace)**。
|
| 139 |
|
| 140 |
- **紅色震波** 代表 **初達波 (First Arrival)**,是能量最早抵達的波。
|
|
@@ -142,7 +166,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 142 |
|
| 143 |
地球物理學家最重要的工作,就是在成千上萬條震波線中,**尋找並追蹤這些連續排列的震波(稱為「同相軸」)**。例如,圖中那條優美的黑色雙曲線同相軸,就清楚地標示出了地下第一層介面的位置!
|
| 144 |
|
| 145 |
-
### 🚀 探索與發現
|
| 146 |
1. **增益的效果**: 試著調整「剖面增益」,看看震波的振幅如何變化。在真實資料中,深層的反射信號很微弱,就需要提高增益才能看清楚。
|
| 147 |
2. **看見雙曲線**: 專注觀察剖面圖中的黑色震波。當你增加「測站數量」時,是不是能更清楚地「描繪」出那條對應到上方 T-X 圖的紫色雙曲線?
|
| 148 |
3. **初達波的威力**: 紅色的初達波在剖面圖中形成了一條明顯的分界線。觀察它的轉折點,思考一下這個轉折點(交越距離)告訴了我們關於地下速度結構的什麼資訊?
|
|
|
|
| 18 |
if v2 <= v1:
|
| 19 |
fig1, ax1 = plt.subplots(figsize=(10, 6))
|
| 20 |
ax1.text(0.5, 0.5, 'Error: V2 must be greater than V1', ha='center', va='center', color='red')
|
| 21 |
+
ax1.set_title("Travel-Time Curve")
|
| 22 |
fig2, ax2 = plt.subplots(figsize=(10, 5))
|
| 23 |
+
ax2.text(0.5, 0.5, 'Please ensure V2 > V1 to generate a valid model.', ha='center', va='center', color='red')
|
| 24 |
+
ax2.set_title("Visualized Seismic Profile")
|
| 25 |
return fig1, fig2, "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1)。"
|
| 26 |
|
| 27 |
theta_c_rad = np.arcsin(v1 / v2)
|
|
|
|
| 55 |
|
| 56 |
# === PART 3: 繪製視覺化震測剖面圖 (Plot 2) ===
|
| 57 |
fig2, ax2 = plt.subplots(figsize=(10, 5))
|
| 58 |
+
receiver_x = np.linspace(0, x_max, int(num_receivers))
|
| 59 |
|
| 60 |
# 計算每個測站的抵達時間
|
| 61 |
t_direct_rx = receiver_x / v1
|
|
|
|
| 66 |
wavelet_duration = 0.08
|
| 67 |
wavelet_t = np.linspace(0, wavelet_duration, 100)
|
| 68 |
|
| 69 |
+
for i in range(int(num_receivers)):
|
| 70 |
+
# 繪製反射波震波
|
| 71 |
wavelet_amp_refl = ricker_wavelet(wavelet_t, f=40) * gain
|
| 72 |
x_trace_refl = receiver_x[i] + wavelet_amp_refl
|
| 73 |
y_trace_refl = t_reflected_rx[i] - wavelet_duration/2 + wavelet_t
|
| 74 |
ax2.plot(x_trace_refl, y_trace_refl, 'k-', linewidth=1)
|
| 75 |
ax2.fill_betweenx(y_trace_refl, receiver_x[i], x_trace_refl, where=(x_trace_refl > receiver_x[i]), color='black')
|
| 76 |
|
| 77 |
+
# 繪製初達波震波
|
| 78 |
+
wavelet_amp_first = ricker_wavelet(wavelet_t, f=30) * gain * 1.2
|
| 79 |
x_trace_first = receiver_x[i] + wavelet_amp_first
|
| 80 |
y_trace_first = t_first_arrival_rx[i] - wavelet_duration/2 + wavelet_t
|
| 81 |
ax2.plot(x_trace_first, y_trace_first, 'r-', linewidth=1.5)
|
|
|
|
| 86 |
ax2.plot(0, 0, 'r*', markersize=20, label='Source')
|
| 87 |
ax2.plot(receiver_x, np.zeros_like(receiver_x), 'kv', markersize=8, label='Receivers')
|
| 88 |
|
| 89 |
+
ax2.set_title(f"2. Visualized Seismic Profile ({int(num_receivers)} Traces)", fontsize=16, loc='left')
|
| 90 |
ax2.set_xlabel("Distance (m)")
|
| 91 |
ax2.set_ylabel("Two-Way Time (s)")
|
| 92 |
ax2.set_xlim(-x_max * 0.05, x_max * 1.05)
|
|
|
|
| 97 |
fig1.tight_layout()
|
| 98 |
|
| 99 |
# === PART 4: 準備輸出的說明文字 ===
|
| 100 |
+
results_md = f"""
|
| 101 |
+
### 🔬 分析結果
|
| 102 |
+
|
| 103 |
+
根據您設計的地層模型,我們計算出以下關鍵物理量:
|
| 104 |
+
|
| 105 |
+
#### 折射波 (Refracted Wave)
|
| 106 |
+
- **臨界角 (Critical Angle, θc)**: `arcsin({v1:.0f} / {v2:.0f})` = **{theta_c_deg:.2f}°**
|
| 107 |
+
- **截時 (Intercept Time, tᵢ)**: `(2 * {h:.0f} * cos({theta_c_deg:.2f}°)) / {v1:.0f}` = **{ti*1000:.1f} ms**
|
| 108 |
+
- **交越距離 (Crossover Distance, Xc)**: `2 * {h:.0f} * sqrt(...)` = **{xc:.1f} m**
|
| 109 |
+
|
| 110 |
+
---
|
| 111 |
+
#### 反射波 (Reflected Wave)
|
| 112 |
+
- **雙程走時 (Two-Way Time, t₀)**: `2 * {h:.0f} / {v1:.0f}` = **{t0*1000:.1f} ms**
|
| 113 |
+
"""
|
| 114 |
return fig1, fig2, results_md
|
| 115 |
|
| 116 |
# --- Gradio 介面設定 ---
|
| 117 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 118 |
+
gr.Markdown(
|
| 119 |
+
"""
|
| 120 |
+
# 地心震波奇幻之旅:地球物理遊樂場 🌍
|
| 121 |
+
> 創意的發揮是一種學習,過程中,每個人同時是學生也是老師。
|
| 122 |
+
|
| 123 |
+
這個實驗室就是你的遊樂場。透過親手設計地層模型、佈放虛擬測站,你將不只是學習,更是在**創造和發現**地底下的物理法則。
|
| 124 |
+
"""
|
| 125 |
+
)
|
| 126 |
|
| 127 |
with gr.Row():
|
| 128 |
with gr.Column(scale=1):
|
|
|
|
| 134 |
gr.Markdown("### ⚙️ 2. 佈放儀器與顯示設定")
|
| 135 |
xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=500, value=250, step=10)
|
| 136 |
receivers_slider = gr.Slider(label="測站數量", minimum=5, maximum=100, value=40, step=1)
|
| 137 |
+
gain_slider = gr.Slider(label="剖面增益 (Display Gain)", minimum=1, maximum=20, value=5, step=1)
|
| 138 |
|
| 139 |
submit_btn = gr.Button("🚀 開始探勘!", variant="primary")
|
| 140 |
|
|
|
|
| 143 |
plot_output1 = gr.Plot(label="走時-距離圖 (T-X Plot)")
|
| 144 |
plot_output2 = gr.Plot(label="視覺化震測剖面圖 (Visualized Seismic Profile)")
|
| 145 |
|
| 146 |
+
with gr.Row():
|
| 147 |
+
results_output = gr.Markdown("### 🔬 分析結果\n請設計你的地層模型並點擊「開始探勘!」以顯示計算結果。")
|
| 148 |
|
| 149 |
# --- 事件監聽 ---
|
| 150 |
+
# 【*** THIS IS THE FIX ***】
|
| 151 |
+
# The incorrect 'demo.outputs[-1]' has been replaced with the correct variable name 'results_output'.
|
| 152 |
submit_btn.click(
|
| 153 |
fn=plot_seismic_exploration,
|
| 154 |
+
inputs=[v1_slider, v2_slider, h_slider, xmax_slider, receivers_slider, gain_slider],
|
| 155 |
+
outputs=[plot_output1, plot_output2, results_output]
|
| 156 |
)
|
| 157 |
|
| 158 |
gr.Markdown(
|
| 159 |
"""
|
| 160 |
---
|
| 161 |
+
### 📖 剖面圖是如何誕生的?
|
| 162 |
下方的 **視覺化震測剖面圖** 完美模擬了真實的探勘情境。每一條黑色的垂直線代表一個**測站 (Receiver)**,它記錄到的訊號就是一條帶有**震波 (Wiggle)** 的**震波線 (Trace)**。
|
| 163 |
|
| 164 |
- **紅色震波** 代表 **初達波 (First Arrival)**,是能量最早抵達的波。
|
|
|
|
| 166 |
|
| 167 |
地球物理學家最重要的工作,就是在成千上萬條震波線中,**尋找並追蹤這些連續排列的震波(稱為「同相軸」)**。例如,圖中那條優美的黑色雙曲線同相軸,就清楚地標示出了地下第一層介面的位置!
|
| 168 |
|
| 169 |
+
### 🚀 探索與發現
|
| 170 |
1. **增益的效果**: 試著調整「剖面增益」,看看震波的振幅如何變化。在真實資料中,深層的反射信號很微弱,就需要提高增益才能看清楚。
|
| 171 |
2. **看見雙曲線**: 專注觀察剖面圖中的黑色震波。當你增加「測站數量」時,是不是能更清楚地「描繪」出那條對應到上方 T-X 圖的紫色雙曲線?
|
| 172 |
3. **初達波的威力**: 紅色的初達波在剖面圖中形成了一條明顯的分界線。觀察它的轉折點,思考一下這個轉折點(交越距離)告訴了我們關於地下速度結構的什麼資訊?
|