Spaces:
Sleeping
Sleeping
File size: 9,887 Bytes
e971733 1ecc7cc 28681db 1ecc7cc e971733 28681db c4e778c 28681db c4e778c 28681db c4e778c 28681db c4e778c 28681db c4e778c ea8c4dd 28681db c4e778c 28681db e971733 ea8c4dd c4e778c 28681db c4e778c 28681db c4e778c 28681db ea8c4dd c4e778c 28681db ea8c4dd c4e778c 1ecc7cc a00010c c4e778c 28681db 1ecc7cc c4e778c 1ecc7cc ea8c4dd a00010c c4e778c ea8c4dd c4e778c 576e90d c4e778c 28681db c4e778c 210db34 28681db c4e778c a00010c c4e778c a00010c c4e778c ea8c4dd e971733 c4e778c e971733 c4e778c ea8c4dd c4e778c e971733 c4e778c e971733 c4e778c e971733 a00010c c4e778c e971733 c4e778c 28681db e971733 9f4f09a 28681db c4e778c e971733 c4e778c e971733 9f4f09a c4e778c 9f4f09a e971733 4a31c97 |
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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt
# --- 輔助函數:產生 Ricker 震波 ---
def ricker_wavelet(t, f=25.0):
""" 產生一個 Ricker 震波 (墨西哥帽函數) """
t = t - 2.0 / f # 將震波峰值對齊時間點
p = (np.pi * f * t) ** 2
return (1 - 2 * p) * np.exp(-p)
# --- 核心計算與繪圖函數 ---
# 【*** FIX 1: Add scenario_name_val as a function parameter ***】
def plot_seismic_exploration(scenario_name_val, v1, v2, v3, h1, h2, x_max, num_receivers, gain):
"""
根據輸入的地層參數,計算並繪製所有探勘圖表。
"""
# === PART 1: 物理計算 (升級至三層模型) ===
valid_model = True
error_msg = ""
if v2 <= v1 or v3 <= v2:
valid_model = False
error_msg = "### ⚠️ 模型警告\n速度必須隨深度增加 (V3 > V2 > V1),折射波分析可能無效。"
# 計算關鍵物理量 (第一層介面)
t0_1 = (2 * h1) / v1
# 計算關鍵物理量 (第二層介面)
t0_2 = (2 * h1 / v1) + (2 * h2 / v2)
# === PART 2: 繪製地質模型圖 (新圖表) ===
fig0, ax0 = plt.subplots(figsize=(10, 2))
ax0.set_xlim(0, x_max)
ax0.set_ylim(-(h1 + h2) * 1.5, 5)
ax0.axhline(0, color='saddlebrown', linewidth=3)
ax0.axhline(-h1, color='gray', linestyle='--')
ax0.axhline(-(h1+h2), color='darkgray', linestyle='--')
ax0.fill_between([0, x_max], 0, -h1, color='sandybrown', alpha=0.6)
ax0.fill_between([0, x_max], -h1, -(h1+h2), color='darkkhaki', alpha=0.6)
ax0.fill_between([0, x_max], -(h1+h2), -(h1 + h2) * 1.5, color='dimgray', alpha=0.6)
ax0.text(x_max/2, -h1/2, f'Layer 1\nV1 = {v1:.0f} m/s\nh1 = {h1:.0f} m', ha='center', va='center', fontsize=9, color='black')
ax0.text(x_max/2, -h1-h2/2, f'Layer 2\nV2 = {v2:.0f} m/s\nh2 = {h2:.0f} m', ha='center', va='center', fontsize=9, color='black')
ax0.text(x_max/2, -(h1+h2)*1.25, f'Layer 3 (Basement)\nV3 = {v3:.0f} m/s', ha='center', va='center', fontsize=9, color='white')
ax0.set_title("Geological Model Cross-section")
ax0.set_ylabel("Depth (m)")
ax0.set_yticks([0, -h1, -(h1+h2)])
ax0.set_xticks([])
fig0.tight_layout(pad=1.1)
# === PART 3: 繪製 T-X 走時曲線圖 ===
x_continuous = np.linspace(0, x_max, 500)
# 反射波 (使用RMS速度近似)
v_rms_2 = np.sqrt((v1**2 * 2*h1/v1 + v2**2 * 2*h2/v2) / (2*h1/v1 + 2*h2/v2))
t_refl_1 = np.sqrt(t0_1**2 + (x_continuous / v1)**2)
t_refl_2 = np.sqrt(t0_2**2 + (x_continuous / v_rms_2)**2)
fig1, ax1 = plt.subplots(figsize=(10, 6))
ax1.plot(x_continuous, t_refl_1, 'm:', linewidth=2, label='Reflection 1 (from Layer 2)')
ax1.plot(x_continuous, t_refl_2, 'c:', linewidth=2, label='Reflection 2 (from Layer 3)')
# 折射波
if valid_model:
theta_c12 = np.arcsin(v1 / v2)
ti_12 = (2 * h1 * np.cos(theta_c12)) / v1
t_refr_12 = (x_continuous / v2) + ti_12
ax1.plot(x_continuous, t_refr_12, 'g--', label='Refraction (from Layer 2)')
ti_23 = 2 * h1 * np.sqrt(v3**2 - v1**2)/(v1*v3) + 2 * h2 * np.sqrt(v3**2 - v2**2)/(v2*v3)
t_refr_23 = (x_continuous / v3) + ti_23
ax1.plot(x_continuous, t_refr_23, 'y--', label='Refraction (from Layer 3)')
ax1.set_title("1. Travel-Time (T-X) Curve")
ax1.legend(fontsize='small')
ax1.grid(True)
ax1.set_xlim(0, x_max)
y_max = np.max(t_refl_2) * 1.1
ax1.set_ylim(0, y_max)
fig1.tight_layout(pad=1.1)
# === PART 4: 繪製視覺化震測剖面圖 ===
fig2, ax2 = plt.subplots(figsize=(10, 5))
receiver_x = np.linspace(0, x_max, int(num_receivers))
# 反射波到時
t_refl_1_rx = np.sqrt(t0_1**2 + (receiver_x / v1)**2)
t_refl_2_rx = np.sqrt(t0_2**2 + (receiver_x / v_rms_2)**2)
wavelet_duration = y_max / 10
wavelet_t = np.linspace(0, wavelet_duration, 100)
for i in range(int(num_receivers)):
# 繪製第一層反射
wavelet_amp_1 = ricker_wavelet(wavelet_t, f=40) * gain
x_trace_1 = receiver_x[i] + wavelet_amp_1
y_trace_1 = t_refl_1_rx[i] - wavelet_duration/2 + wavelet_t
ax2.plot(x_trace_1, y_trace_1, 'k-', linewidth=0.8)
ax2.fill_betweenx(y_trace_1, receiver_x[i], x_trace_1, where=(x_trace_1 > receiver_x[i]), color='black')
# 繪製第二層反射
wavelet_amp_2 = ricker_wavelet(wavelet_t, f=30) * gain * 0.8 # Deeper reflections are weaker
x_trace_2 = receiver_x[i] + wavelet_amp_2
y_trace_2 = t_refl_2_rx[i] - wavelet_duration/2 + wavelet_t
ax2.plot(x_trace_2, y_trace_2, 'b-', linewidth=0.8)
ax2.fill_betweenx(y_trace_2, receiver_x[i], x_trace_2, where=(x_trace_2 > receiver_x[i]), color='blue')
ax2.set_title(f"2. Visualized Seismic Profile ({int(num_receivers)} Traces)")
ax2.set_ylim(y_max, -y_max*0.05)
ax2.set_xlim(-x_max * 0.05, x_max * 1.05)
fig2.subplots_adjust(left=0.1, right=0.98, top=0.9, bottom=0.15)
# === PART 5: 準備探勘日誌 ===
log_md = f"""
### 📝 現場探勘日誌
**任務目標**: {scenario_name_val}
**儀器設定**: {int(num_receivers)} 個測站, 測線長度 {x_max} 公尺。
**初步分析**:
- **第一介面反射 (黑色震波)**: 雙程走時 (TWT) 約 **{t0_1*1000:.1f} ms**。
- **第二介面反射 (藍色震波)**: 雙程走時 (TWT) 約 **{t0_2*1000:.1f} ms**。
{error_msg}
"""
return fig0, fig1, fig2, log_md
# --- Gradio 介面與任務設定 ---
scenarios = {
"自訂模式 (Custom Mode)": {"v1": 800, "v2": 2500, "v3": 4500, "h1": 20, "h2": 50},
"尋找淺層地下水 (Find Groundwater)": {"v1": 500, "v2": 2200, "v3": 3500, "h1": 15, "h2": 40},
"桃園台地工程鑽探 (Taoyuan Engineering)": {"v1": 600, "v2": 1800, "v3": 3000, "h1": 10, "h2": 30},
"油氣田探勘 (Oil & Gas Prospecting)": {"v1": 1500, "v2": 2800, "v3": 4200, "h1": 100, "h2": 250},
}
def update_sliders(scenario_key):
params = scenarios[scenario_key]
return params['v1'], params['v2'], params['v3'], params['h1'], params['h2']
with gr.Blocks(theme=gr.themes.Soft()) as demo:
scenario_name = gr.State("自訂模式 (Custom Mode)")
gr.Markdown("# 地球物理探勘總部 🛰️")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 🎯 1. 選擇探勘任務")
scenario_dropdown = gr.Dropdown(list(scenarios.keys()), label="Select Mission", value="自訂模式 (Custom Mode)")
gr.Markdown("### ⚙️ 2. 微調地層參數")
v1_slider = gr.Slider(label="V1 (m/s)", minimum=300, maximum=5000, value=800, step=50)
h1_slider = gr.Slider(label="h1 (m)", minimum=5, maximum=500, value=20, step=5)
v2_slider = gr.Slider(label="V2 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
h2_slider = gr.Slider(label="h2 (m)", minimum=10, maximum=1000, value=50, step=10)
v3_slider = gr.Slider(label="V3 (m/s)", minimum=1000, maximum=8000, value=4500, step=50)
gr.Markdown("### 📡 3. 設定儀器")
xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=2000, value=500, step=50)
receivers_slider = gr.Slider(label="測站數量", minimum=10, maximum=200, value=50, step=5)
gain_slider = gr.Slider(label="剖面增益", minimum=1, maximum=20, value=4, step=1)
submit_btn = gr.Button("🚀 發射震波!", variant="primary")
with gr.Column(scale=2):
gr.Markdown("### 🗺️ 地質模型")
plot_output0 = gr.Plot(label="Geological Model")
gr.Markdown("### 📊 探勘數據")
plot_output1 = gr.Plot(label="走時-距離圖")
plot_output2 = gr.Plot(label="視覺化震測剖面圖")
with gr.Row():
log_output = gr.Markdown("### 📝 現場探勘日誌\n請選擇任務或調整參數,然後點擊「發射震波!」")
# --- 事件監聽 ---
scenario_dropdown.change(
fn=update_sliders,
inputs=scenario_dropdown,
outputs=[v1_slider, v2_slider, v3_slider, h1_slider, h2_slider]
)
scenario_dropdown.change(lambda x: x, inputs=scenario_dropdown, outputs=scenario_name)
# 【*** FIX 2: Add scenario_name to the inputs list ***】
submit_btn.click(
fn=plot_seismic_exploration,
inputs=[scenario_name, v1_slider, v2_slider, v3_slider, h1_slider, h2_slider, xmax_slider, receivers_slider, gain_slider],
outputs=[plot_output0, plot_output1, plot_output2, log_output]
)
gr.Markdown(
"""
---
### 🧠 總工程師的挑戰
1. **看見儲油構造**: 在「油氣田探勘」任務中,來自第二介面(藍色震波)的反射同相軸呈現一個向上彎曲的「背斜」形狀,這正是油氣最喜歡聚集的地方!你能透過微調 `h1` 和 `h2` 讓這個構造更明顯嗎?
2. **折射的極限**: 試著在自訂模式中,將 `V2` 調得比 `V1` 慢,看看走時圖和日誌會出現什麼警告?這在真實地質中稱為「低速帶」,是折射法的一大挑戰。
3. **解析度問題**: 將「測站數量」調到最低,再慢慢增加。你需要多少個測站,才能清楚地分辨出剖面圖中來自兩個不同介面的反射波?這就是探勘的「解析度」概念。
"""
)
gr.HTML("""
<footer style="text-align:center; margin-top: 30px; color:grey;">
<p>「創意的發揮是一種學習,過程中,每個人同時是學生也是老師。」</p>
<p>地球物理探勘總部 © 2025 - 由 Gemini 根據課程文件與靈感生成</p>
</footer>
""")
if __name__ == "__main__":
demo.launch() |