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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -82
app.py CHANGED
@@ -3,104 +3,112 @@ import numpy as np
3
  import matplotlib.pyplot as plt
4
 
5
  # --- 核心計算與繪圖函數 ---
6
- def plot_seismic_exploration(v1, v2, h, x_max):
7
  """
8
- 根據輸入的地層參數,計算並繪製震測的走時曲線。
9
- *** 新增了反射波的模擬 ***
10
- 圖表內的標籤為英文。
11
  """
12
- # 物理條件檢查:折射必須 V2 > V1
 
13
  if v2 <= v1:
14
- fig, ax = plt.subplots(figsize=(10, 6))
15
- ax.text(0.5, 0.5, 'Error: V2 must be greater than V1 for critical refraction to occur.',
16
- ha='center', va='center', fontsize=12, color='red')
17
- ax.set_xlabel("Distance (m)")
18
- ax.set_ylabel("Travel Time (s)")
19
- ax.grid(True)
20
- ax.set_title("Travel-Time Curve")
21
- return fig, "### 參數錯誤\n請確保第二層速度 (V2) 大於第一層速度 (V1),否則無法產生臨界折射現象。"
22
 
23
- # 1. 計算關鍵物理量
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
28
  xc = 2 * h * np.sqrt((v2 + v1) / (v2 - v1))
29
-
30
- # 反射波相關
31
  t0 = (2 * h) / v1
32
 
33
- # 2. 準備繪圖數據
34
- x = np.linspace(0, x_max, 500)
35
- # 直達波
36
- t_direct = x / v1
37
- # 折射波
38
- t_refracted = (x / v2) + ti
39
- # 反射波 (新增)
40
- t_reflected = np.sqrt(t0**2 + (x / v1)**2)
41
- # 初達波
42
- t_first_arrival = np.minimum(t_direct, t_refracted)
43
-
44
- # 3. 使用 Matplotlib 繪圖
45
- fig, ax = plt.subplots(figsize=(10, 6))
46
 
47
- # 繪製各波線
48
- ax.plot(x, t_direct, 'b--', label=f'Direct Wave (Slope 1/{v1:.0f})')
49
- ax.plot(x, t_refracted, 'g--', label=f'Refracted Wave (Slope 1/{v2:.0f})')
50
- ax.plot(x, t_reflected, 'm:', linewidth=2, label='Reflected Wave (Hyperbola)') # 新增反射波
51
- ax.plot(x, t_first_arrival, 'r-', linewidth=3, label='First Arrival (Refraction)')
52
 
53
- # 標示交越距離
54
  if xc < x_max:
55
- ax.axvline(x=xc, color='k', linestyle=':', label=f'Crossover Distance = {xc:.1f} m')
56
- ax.plot(xc, xc/v1, 'ko')
 
57
 
58
- # 標示截時
59
- ax.plot(0, ti, 'go', markersize=8, label=f'Refraction Intercept = {ti*1000:.1f} ms')
60
- # 標示雙程走時 (新增)
61
- ax.plot(0, t0, 'mo', markersize=8, label=f'Reflection Two-Way Time = {t0*1000:.1f} ms')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- # 設定圖表樣式
64
- ax.set_title("Interactive Seismic Exploration T-X Plot", fontsize=16)
65
- ax.set_xlabel("Distance (m)", fontsize=12)
66
- ax.set_ylabel("Travel Time (s)", fontsize=12)
 
 
67
 
68
- ax.legend()
69
- ax.grid(True)
70
- ax.set_xlim(0, x_max)
71
- # 自動調整 Y 軸,確保能看到所有曲線
72
- y_max = max(np.max(t_direct), np.max(t_reflected))
73
- ax.set_ylim(0, y_max * 1.1)
74
  plt.tight_layout()
 
75
 
76
- # 4. 準備輸出的說明文字
77
  results_md = f"""
78
  ### 🔬 分析結果
79
 
80
  根據您設計的地層模型,我們計算出以下關鍵物理量:
81
 
82
  #### 折射波 (Refracted Wave)
83
- - **臨界角 (Critical Angle, θc)**:
84
- - 公式: `θc = arcsin(V1 / V2)`
85
- - 計算: `arcsin({v1:.0f} / {v2:.0f})` = **{theta_c_deg:.2f}°**
86
- - *這是產生「抄捷徑」折射波的關鍵入射角度。*
87
- - **截時 (Intercept Time, tᵢ)**:
88
- - 公式: `tᵢ = (2 * h * cos(θc)) / V1`
89
- - 計算: `(2 * {h:.0f} * cos({theta_c_deg:.2f}°)) / {v1:.0f}` = **{ti*1000:.1f} ms**
90
- - *折射波走時線在時間軸上的截距,隱含了第一層的厚度資訊。*
91
- - **交越距離 (Crossover Distance, Xc)**:
92
- - 公式: `Xc = 2 * h * sqrt((V2 + V1) / (V2 - V1))`
93
- - 計算: `2 * {h:.0f} * sqrt(({v2:.0f} + {v1:.0f}) / ({v2:.0f} - {v1:.0f}))` = **{xc:.1f} m**
94
- - *在此距離之後,折射波會比直達波先抵達。*
95
 
96
  ---
97
  #### 反射波 (Reflected Wave)
98
- - **雙程走時 (Two-Way Time, t₀)**:
99
- - 公式: `t₀ = 2 * h / V1`
100
- - 計算: `2 * {h:.0f} / {v1:.0f}` = **{t0*1000:.1f} ms**
101
- - *這是震波垂直向下傳播並返回地表所需的時間,也是反射波雙曲線的頂點。*
102
  """
103
- return fig, results_md
 
104
 
105
  # --- Gradio 介面設定 ---
106
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
@@ -109,24 +117,27 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
109
  # 地心震波奇幻之旅:地球物理遊樂場 🌍
110
  > 創意的發揮是一種學習,過程中,每個人同時是學生也是老師。
111
 
112
- 這個實驗室就是你的遊樂場。透過親手調整地層的參數,你將不只是在學習地球物理,更是在**創造和發現**地底下的物理法則。
113
- 你既是設計模型的老師,也是觀察結果的學生。動動手,看看你能發現什麼秘密!
114
  """
115
  )
116
 
117
  with gr.Row():
118
  with gr.Column(scale=1):
119
- gr.Markdown("### ⚙️ 設計你的地層模型")
120
  v1_slider = gr.Slider(label="V1: 第一層速度 (m/s)", minimum=300, maximum=3000, value=800, step=50)
121
  v2_slider = gr.Slider(label="V2: 第二層速度 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
122
  h_slider = gr.Slider(label="h: 第一層厚度 (m)", minimum=5, maximum=100, value=20, step=1)
 
 
123
  xmax_slider = gr.Slider(label="最大觀測距離 (m)", minimum=100, maximum=500, value=250, step=10)
 
124
 
125
  submit_btn = gr.Button("🚀 開始探勘!", variant="primary")
126
 
127
  with gr.Column(scale=2):
128
- gr.Markdown("### 📊 觀測走時-距離圖 (T-X Plot)")
129
- plot_output = gr.Plot()
 
130
 
131
  with gr.Row():
132
  results_output = gr.Markdown("### 🔬 分析結果\n請設計你的地層模型並點擊「開始探勘!」以顯示計算結果。")
@@ -134,19 +145,23 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
134
  # --- 事件監聽 ---
135
  submit_btn.click(
136
  fn=plot_seismic_exploration,
137
- inputs=[v1_slider, v2_slider, h_slider, xmax_slider],
138
- outputs=[plot_output, results_output]
139
  )
140
 
141
  gr.Markdown(
142
  """
143
  ---
 
 
 
 
 
 
144
  ### 🚀 探索與發現 (Exploration & Discovery)
145
- 試著回答下面的問題,你就是自己的老師!
146
- 1. **折射 vs. 反射**: 在近距離 (X 接近 0) 時,哪一種波(直達波、反射波)最先抵達?為什麼?
147
- 2. **速度的影響**: 如果把 V1 和 V2 的速度差距拉得更大,折射波的走時線(綠色)會變得更陡還是更平緩?這對交越距離又有什麼影響?
148
- 3. **厚度的秘密**: 如果你只增加地層厚度 `h`,反射波的雙曲線(紫色)和折射波的截時 `tᵢ` 會如何變化?它們之間有什麼數學關聯嗎?
149
- 4. **消失的折射波**: 在什麼條件下,折射波(綠色線)會完全消失,或是永遠比直達波(藍色線)還慢?
150
  """
151
  )
152
 
 
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
26
  xc = 2 * h * np.sqrt((v2 + v1) / (v2 - v1))
 
 
27
  t0 = (2 * h) / v1
28
 
29
+ # === PART 2: 繪製 T-X 走時曲線圖 (Plot 1) ===
30
+ x_continuous = np.linspace(0, x_max, 500)
31
+ t_direct = x_continuous / v1
32
+ t_refracted = (x_continuous / v2) + ti
33
+ t_reflected = np.sqrt(t0**2 + (x_continuous / v1)**2)
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)")
50
+ ax1.legend()
51
+ ax1.grid(True)
52
+ ax1.set_xlim(0, x_max)
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:
 
117
  # 地心震波奇幻之旅:地球物理遊樂場 🌍
118
  > 創意的發揮是一種學習,過程中,每個人同時是學生也是老師。
119
 
120
+ 這個實驗室就是你的遊樂場。透過親手設計地層模型、佈放虛擬測站,你將不只是學習,更是在**創造和發現**地底下的物理法則。
 
121
  """
122
  )
123
 
124
  with gr.Row():
125
  with gr.Column(scale=1):
126
+ gr.Markdown("### ⚙️ 1. 設計你的地層模型")
127
  v1_slider = gr.Slider(label="V1: 第一層速度 (m/s)", minimum=300, maximum=3000, value=800, step=50)
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請設計你的地層模型並點擊「開始探勘!」以顯示計算結果。")
 
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