import gradio as gr import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle # --- 核心物理模擬函數 --- def calculate_gravity_anomalies(mountain_height, subsurface_density, subsurface_position): """ 根據地形和地下異常體,計算並繪製重力異常曲線。 (All plot labels are in English to prevent font issues on cloud platforms.) """ # 1. 建立空間座標 (模擬一條 100 公里的測線) x = np.linspace(-50, 50, 500) # 2. 建立地形 (用高斯函數模擬一座山) mountain_width = 15 # 山的寬度 (固定) topography = mountain_height * np.exp(-(x**2) / (2 * mountain_width**2)) # 確保地表最低點為 0 topography[topography < 0.01] = 0 # 3. 建立地下的「真實」重力異常訊號 (布格異常) # 同樣用高斯函數模擬一個球體造成的重力異常 body_width = 8 # 地下異常體的寬度 (固定) # 核心:布格異常只反映地下的情況 bouguer_anomaly = subsurface_density * np.exp(-((x - subsurface_position)**2) / (2 * body_width**2)) # 4. 計算布格校正 (Bouguer Correction) # 根據講義公式 BC = 0.112 * h bouguer_correction = 0.112 * topography # 5. 計算自由空間重力異常 (Free-air Anomaly) # 核心:自由空間重力異常 = 地下真實訊號 + 地形影響 # FAA = BA + BC free_air_anomaly = bouguer_anomaly + bouguer_correction # --- Matplotlib 繪圖 (English Labels) --- fig, ax1 = plt.subplots(figsize=(12, 8)) # 設定主 Y 軸 (重力異常) ax1.set_xlabel('Profile Position (km)', fontsize=14) ax1.set_ylabel('Gravity Anomaly (mGal)', color='tab:red', fontsize=14) ax1.tick_params(axis='y', labelcolor='tab:red') ax1.grid(True, linestyle='--', alpha=0.6) # 繪製重力曲線 ax1.plot(x, free_air_anomaly, 'b--', label='Free-air Anomaly ($\Delta g_{fa}$)', linewidth=2) ax1.plot(x, bouguer_anomaly, 'r-', label='Bouguer Anomaly ($\Delta g_{B}$)', linewidth=3, alpha=0.9) ax1.legend(loc='upper left', fontsize=12) ax1.set_ylim(-35, 85) # 固定 Y 軸範圍以便比較 # 設定次 Y 軸 (地形/深度) ax2 = ax1.twinx() ax2.set_ylabel('Elevation / Depth (m)', color='tab:green', fontsize=14) ax2.tick_params(axis='y', labelcolor='tab:green') # 繪製地形 ax2.fill_between(x, 0, topography, color='saddlebrown', alpha=0.4, label='Topography') # 繪製地下異常體 subsurface_depth = -200 # 固定深度 body_color = 'darkred' if subsurface_density >= 0 else 'darkblue' body_alpha = min(1.0, 0.2 + abs(subsurface_density)/50.0) # 密度越大顏色越深 circle = Circle((subsurface_position, subsurface_depth), radius=body_width*0.8, color=body_color, alpha=body_alpha) ax2.add_patch(circle) ax2.set_ylim(-500, 1500) # 固定地形/深度軸範圍 fig.suptitle('Interactive Bouguer Anomaly Lab', fontsize=20, weight='bold') plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 調整佈局以容納標題 # 關閉 matplotlib 的自動顯示,將 figure 物件交給 Gradio 處理 plt.close(fig) return fig # --- Gradio 介面設定 (UI remains in Chinese) --- with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown( """ # ⛰️ 布格異常互動實驗室 ⛏️ 歡迎來到重力探勘互動實驗室!在這裡,你可以親手操作變數,理解地球物理學家如何「看透」地表。 ### 操作指南: 1. **調整「山脈高度」滑桿:** 觀察藍色虛線 (Free-air Anomaly) 如何緊緊跟隨地形變化。 2. **調整「地下異常體密度」與「位置」:** 觀察紅色實線 (Bouguer Anomaly) 如何只反映地下物質的變化,幾乎不受地表高山的影響。 **核心思想:** 布格校正的目的,就是從充滿地形雜訊的「自由空間重力異常」中,減去地形的影響,得到能真正反映地下密度的「布格異常」。 """ ) with gr.Row(): with gr.Column(scale=1): mountain_slider = gr.Slider(minimum=0, maximum=500, value=300, step=10, label="山脈高度 (公尺, h)") density_slider = gr.Slider(minimum=-30, maximum=50, value=30, step=2, label="地下異常體密度 (mGal, 代表質量異常)") position_slider = gr.Slider(minimum=-30, maximum=30, value=10, step=1, label="地下異常體水平位置 (km)") with gr.Column(scale=3): output_plot = gr.Plot() # 將滑桿與函數連接 inputs = [mountain_slider, density_slider, position_slider] for slider in inputs: slider.change(fn=calculate_gravity_anomalies, inputs=inputs, outputs=output_plot) # 頁面載入時先執行一次 demo.load(fn=calculate_gravity_anomalies, inputs=inputs, outputs=output_plot) # 啟動應用 demo.launch()