cwadayi commited on
Commit
1ecc7cc
·
verified ·
1 Parent(s): ea8c4dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -80
app.py CHANGED
@@ -2,24 +2,26 @@ import gradio as gr
2
  import numpy as np
3
  import matplotlib.pyplot as plt
4
 
 
 
 
 
 
 
 
5
  # --- 核心計算與繪圖函數 ---
6
- def plot_seismic_exploration(v1, v2, h, x_max, num_receivers):
7
  """
8
- 根據輸入的地層參數,計算並繪製震測的走時曲線與震測剖面概念圖。
9
  """
10
- # === PART 1: 物理計算 (與之前相同) ===
11
- # 物理條件檢查
12
  if v2 <= v1:
13
- # 產生兩個空的錯誤圖表
14
  fig1, ax1 = plt.subplots(figsize=(10, 6))
15
  ax1.text(0.5, 0.5, 'Error: V2 must be greater than V1', ha='center', va='center', color='red')
16
- ax1.set_title("Travel-Time Curve")
17
- fig2, ax2 = plt.subplots(figsize=(10, 4))
18
- ax2.text(0.5, 0.5, 'Please ensure V2 > V1 to generate a valid model.', ha='center', va='center', color='red')
19
- ax2.set_title("Seismic Profile Concept")
20
- return fig1, fig2, "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1),否則無法產生臨界折射現象。"
21
 
22
- # 計算關鍵物理量
23
  theta_c_rad = np.arcsin(v1 / v2)
24
  theta_c_deg = np.rad2deg(theta_c_rad)
25
  ti = (2 * h * np.cos(theta_c_rad)) / v1
@@ -34,16 +36,12 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers):
34
  t_first_arrival_continuous = np.minimum(t_direct, t_refracted)
35
 
36
  fig1, ax1 = plt.subplots(figsize=(10, 6))
37
- ax1.plot(x_continuous, t_direct, 'b--', label=f'Direct Wave (Slope 1/{v1:.0f})')
38
- ax1.plot(x_continuous, t_refracted, 'g--', label=f'Refracted Wave (Slope 1/{v2:.0f})')
39
- ax1.plot(x_continuous, t_reflected, 'm:', linewidth=2, label='Reflected Wave (Hyperbola)')
40
- ax1.plot(x_continuous, t_first_arrival_continuous, 'r-', linewidth=3, label='First Arrival (Refraction)')
41
-
42
  if xc < x_max:
43
  ax1.axvline(x=xc, color='k', linestyle=':', label=f'Crossover = {xc:.1f} m')
44
- ax1.plot(0, ti, 'go', markersize=8, label=f'Refraction Intercept = {ti*1000:.1f} ms')
45
- ax1.plot(0, t0, 'mo', markersize=8, label=f'Reflection TWT = {t0*1000:.1f} ms')
46
-
47
  ax1.set_title("1. Travel-Time (T-X) Curve", fontsize=16, loc='left')
48
  ax1.set_xlabel("Distance (m)")
49
  ax1.set_ylabel("Travel Time (s)")
@@ -53,73 +51,57 @@ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers):
53
  y_max = max(np.max(t_direct), np.max(t_reflected))
54
  ax1.set_ylim(0, y_max * 1.1)
55
 
56
- # === PART 3: 繪製震測剖面概念圖 (Plot 2) ===
57
- fig2, ax2 = plt.subplots(figsize=(10, 4))
58
-
59
- # 產生離散的測站位置
60
  receiver_x = np.linspace(0, x_max, num_receivers)
61
 
62
  # 計算每個測站的抵達時間
63
  t_direct_rx = receiver_x / v1
64
- t_refracted_rx = (receiver_x / v2) + ti
65
  t_reflected_rx = np.sqrt(t0**2 + (receiver_x / v1)**2)
66
- t_first_arrival_rx = np.minimum(t_direct_rx, t_refracted_rx)
 
 
 
 
67
 
68
- # 繪製每一條震波線和標記
69
  for i in range(num_receivers):
70
- # 畫代表震波線的灰色虛線
71
- ax2.plot([receiver_x[i], receiver_x[i]], [0, y_max * 1.1], 'k--', alpha=0.2)
72
- # 標示各種波的抵達時間
73
- ax2.plot(receiver_x[i], t_direct_rx[i], 'bo', markersize=5, alpha=0.6) # 直達波
74
- ax2.plot(receiver_x[i], t_reflected_rx[i], 'mo', markersize=5, alpha=0.6) # 反射波
75
- ax2.plot(receiver_x[i], t_first_arrival_rx[i], 'ro', markersize=8) # 初達波 (最重要)
76
-
77
- # 繪製地表
 
 
 
 
 
 
 
78
  ax2.axhline(0, color='brown', linewidth=2)
79
- # 繪製震源
80
- ax2.plot(0, 0, 'r*', markersize=20, label='Source (震源)')
81
- # 繪製測站
82
- ax2.plot(receiver_x, np.zeros_like(receiver_x), 'kv', markersize=8, label='Receivers (測站)')
83
 
84
- ax2.set_title(f"2. Seismic Profile Concept ({num_receivers} Traces)", fontsize=16, loc='left')
85
  ax2.set_xlabel("Distance (m)")
86
- ax2.set_ylabel("Travel Time (s)")
87
  ax2.set_xlim(-x_max * 0.05, x_max * 1.05)
88
- ax2.set_ylim(y_max * 1.1, -y_max*0.05) # 時間軸向下為正
89
  ax2.legend(loc='lower left')
90
 
91
- # 清理圖表外觀
92
  plt.tight_layout()
93
  fig1.tight_layout()
94
 
95
  # === PART 4: 準備輸出的說明文字 ===
96
- results_md = f"""
97
- ### 🔬 分析結果
98
-
99
- 根據您設計的地層模型,我們計算出以下關鍵物理量:
100
-
101
- #### 折射波 (Refracted Wave)
102
- - **臨界角 (Critical Angle, θc)**: `arcsin({v1:.0f} / {v2:.0f})` = **{theta_c_deg:.2f}°**
103
- - **截時 (Intercept Time, tᵢ)**: `(2 * {h:.0f} * cos({theta_c_deg:.2f}°)) / {v1:.0f}` = **{ti*1000:.1f} ms**
104
- - **交越距離 (Crossover Distance, Xc)**: `2 * {h:.0f} * sqrt(...)` = **{xc:.1f} m**
105
-
106
- ---
107
- #### 反射波 (Reflected Wave)
108
- - **雙程走時 (Two-Way Time, t₀)**: `2 * {h:.0f} / {v1:.0f}` = **{t0*1000:.1f} ms**
109
- """
110
  return fig1, fig2, results_md
111
 
112
-
113
  # --- Gradio 介面設定 ---
114
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
115
- gr.Markdown(
116
- """
117
- # 地心震波奇幻之旅:地球物理遊樂場 🌍
118
- > 創意的發揮是一種學習,過程中,每個人同時是學生也是老師。
119
-
120
- 這個實驗室就是你的遊樂場。透過親手設計地層模型、佈放虛擬測站,你將不只是學習,更是在**創造和發現**地底下的物理法則。
121
- """
122
- )
123
 
124
  with gr.Row():
125
  with gr.Column(scale=1):
@@ -128,40 +110,42 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
128
  v2_slider = gr.Slider(label="V2: 第二層速度 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
129
  h_slider = gr.Slider(label="h: 第一層厚度 (m)", minimum=5, maximum=100, value=20, step=1)
130
 
131
- gr.Markdown("### ⚙️ 2. 佈放你的觀測儀器")
132
  xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=500, value=250, step=10)
133
- receivers_slider = gr.Slider(label="測站數量 (Number of Receivers)", minimum=5, maximum=100, value=25, step=1) # 新增滑桿
 
134
 
135
  submit_btn = gr.Button("🚀 開始探勘!", variant="primary")
136
 
137
  with gr.Column(scale=2):
138
  gr.Markdown("### 📊 觀測結果")
139
  plot_output1 = gr.Plot(label="走時-距離圖 (T-X Plot)")
140
- plot_output2 = gr.Plot(label="震測剖面概念圖 (Seismic Profile Concept)") # 新增第二個圖表區
141
 
142
- with gr.Row():
143
- results_output = gr.Markdown("### 🔬 分析結果\n請設計你的地層模型並點擊「開始探勘!」以顯示計算結果。")
144
 
145
  # --- 事件監聽 ---
146
  submit_btn.click(
147
  fn=plot_seismic_exploration,
148
- inputs=[v1_slider, v2_slider, h_slider, xmax_slider, receivers_slider],
149
- outputs=[plot_output1, plot_output2, results_output] # 更新輸出目標
150
  )
151
 
152
  gr.Markdown(
153
  """
154
  ---
155
- ### 📖 剖面圖是如何誕生的?
156
- 上方的 **T-X 圖** 是一個抽象的數學關係圖,展現了震波抵達時間與距離的完美曲線。
157
- 下方的 **剖面概念圖** 則模擬了真實的探勘情境:我們在地表放置了許多**測站 (黑色三角形)**,每個測站都會記錄到屬於自己的**震波線 (灰色虛線)**。圖中的彩色圓點,就是該測站記錄到的「到時」。
 
 
158
 
159
- 當我們把成千上萬條震波線並排陳列,就形成了一幅壯觀的**震測剖面圖**,它就像是地球的超音波照片!
160
 
161
- ### 🚀 探索與發現 (Exploration & Discovery)
162
- 1. **數據密度**: 試著將「測站數量」從 5 慢慢增加到 100,觀察下方的剖面概念圖。當測站越密集時,你是否能越清晰地「看見」上方 T-X 圖中的那些曲線?
163
- 2. **剖面盲區**: 在近距離時,反射波(紫色點)和直達波(藍色點)幾乎混在一起。這在真實的資料處理中稱作「Muting Zone」。你能透過調整 V1 和 h,讓這兩種波的抵達時間分得更開嗎?
164
- 3. **交越點的意義**: 在剖面概念圖上,找到初達波標記(紅色點)從跟隨藍色點轉為跟隨(不存在的)綠色點趨勢的位置。這個位置對應到上方 T-X 圖的什麼特徵?
165
  """
166
  )
167
 
 
2
  import numpy as np
3
  import matplotlib.pyplot as plt
4
 
5
+ # --- 輔助函數:產生 Ricker 震波 ---
6
+ def ricker_wavelet(t, f=25.0):
7
+ """ 產生一個 Ricker 震波 (墨西哥帽函數) """
8
+ t = t - 2.0 / f # 將震波峰值對齊時間點
9
+ p = (np.pi * f * t) ** 2
10
+ return (1 - 2 * p) * np.exp(-p)
11
+
12
  # --- 核心計算與繪圖函數 ---
13
+ def plot_seismic_exploration(v1, v2, h, x_max, num_receivers, gain):
14
  """
15
+ 根據輸入的地層參數,計算並繪製震測的走時曲線與視覺化震測剖面圖。
16
  """
17
+ # === PART 1: 物理計算 ===
 
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)
26
  theta_c_deg = np.rad2deg(theta_c_rad)
27
  ti = (2 * h * np.cos(theta_c_rad)) / v1
 
36
  t_first_arrival_continuous = np.minimum(t_direct, t_refracted)
37
 
38
  fig1, ax1 = plt.subplots(figsize=(10, 6))
39
+ ax1.plot(x_continuous, t_direct, 'b--', label='Direct Wave')
40
+ ax1.plot(x_continuous, t_refracted, 'g--', label='Refracted Wave')
41
+ ax1.plot(x_continuous, t_reflected, 'm:', linewidth=2, label='Reflected Wave')
42
+ ax1.plot(x_continuous, t_first_arrival_continuous, 'r-', linewidth=3, label='First Arrival')
 
43
  if xc < x_max:
44
  ax1.axvline(x=xc, color='k', linestyle=':', label=f'Crossover = {xc:.1f} m')
 
 
 
45
  ax1.set_title("1. Travel-Time (T-X) Curve", fontsize=16, loc='left')
46
  ax1.set_xlabel("Distance (m)")
47
  ax1.set_ylabel("Travel Time (s)")
 
51
  y_max = max(np.max(t_direct), np.max(t_reflected))
52
  ax1.set_ylim(0, y_max * 1.1)
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
 
60
  t_reflected_rx = np.sqrt(t0**2 + (receiver_x / v1)**2)
61
+ t_first_arrival_rx = np.minimum(receiver_x / v1, (receiver_x / v2) + ti)
62
+
63
+ # 繪製每一條帶有震波的震波線
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)
80
+ ax2.fill_betweenx(y_trace_first, receiver_x[i], x_trace_first, where=(x_trace_first > receiver_x[i]), color='red', alpha=0.8)
81
+
82
+ # 繪製地表、震源與測站
83
  ax2.axhline(0, color='brown', linewidth=2)
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)
91
+ ax2.set_ylim(y_max * 1.1, -y_max*0.05)
92
  ax2.legend(loc='lower left')
93
 
 
94
  plt.tight_layout()
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):
 
110
  v2_slider = gr.Slider(label="V2: 第二層速度 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
111
  h_slider = gr.Slider(label="h: 第一層厚度 (m)", minimum=5, maximum=100, value=20, step=1)
112
 
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
 
120
  with gr.Column(scale=2):
121
  gr.Markdown("### 📊 觀測結果")
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], # 加入 gain_slider
131
+ outputs=[plot_output1, plot_output2, demo.outputs[-1]] # 確保結果正確輸出
132
  )
133
 
134
  gr.Markdown(
135
  """
136
  ---
137
+ ### 📖 剖面圖是如何誕生的? (全新解說)
138
+ 下方的 **視覺化震測剖面圖** 完美模擬了真實的探勘情境。每一條黑色的垂直線代表一個**測站 (Receiver)**,它記錄到的訊號就是一條帶有**震波 (Wiggle)** 的**震波線 (Trace)**。
139
+
140
+ - **紅色震波** 代表 **初達波 (First Arrival)**,是能量最早抵達的波。
141
+ - **黑色震波** 代表 **反射波 (Reflected Wave)**,它們來自地下介面的反射。
142
 
143
+ 地球物理學家最重要的工作,就是在成千上萬條震波線中,**尋找並追蹤這些連續排列的震波(稱為「同相軸」)**。例如,圖中那條優美的黑色雙曲線同相軸,就清楚地標示出了地下第一層介面的位置!
144
 
145
+ ### 🚀 探索與發現 (全新挑戰)
146
+ 1. **增益的效果**: 試著調整「剖面增益」,看看震波的振幅如何變化。在真實資料中,深層的反射信號很微弱,就需要提高增益才能看清楚。
147
+ 2. **看見雙曲線**: 專注觀察剖面圖中的黑色震波。當你增加「測站數量」時,是不是能更清楚地「描繪」出那條對應到上方 T-X 圖的紫色雙曲線?
148
+ 3. **初達波的威力**: 紅色的初達波在剖面圖中形成了一條明顯的分界線。觀察它的轉折點,思考一下這個轉折點(交越距離)告訴了我們關於地下速度結構的什麼資訊?
149
  """
150
  )
151