470 lines
16 KiB
Markdown
470 lines
16 KiB
Markdown
# Outlook: GNN-Guided Adaptive Mesh Refinement for 2D Helmholtz Scattering
|
||
|
||
## 1. 问题定义
|
||
|
||
求解 2D 介质圆柱的电磁散射(散射场公式):
|
||
|
||
```
|
||
∇²u + k²·ε_r·u = −k²·(ε_r − 1)·u_inc
|
||
∂u/∂n − i·k_local·u = 0 (Sommerfeld 辐射 BC)
|
||
```
|
||
|
||
- 入射波:`u_inc = exp(i·k·x)`,参考解:Mie 解析解
|
||
- 参数空间:k∈[3,15],eps_r∈[2,8],cx/cy∈[0.2,0.8],radius∈[0.05,0.25]
|
||
- 核心目标:在参数化 Helmholtz 散射问题中,学习一个无在线求解的预算约束网格预测器,用于近似 residual-AMR 的最终加密分布,并在低预算下优于简单物理启发式网格。
|
||
|
||
---
|
||
|
||
## 2. 算法全流程
|
||
|
||
```
|
||
Step 1. 数据生成 (gen.py)
|
||
残差驱动 AMR → 每步保存 cell 状态 + 标签
|
||
│
|
||
▼
|
||
Step 2. 训练 (train_correction.py)
|
||
features(15-dim) + physics_score → GNN → sigmoid → 二分类 teacher_mark
|
||
│
|
||
▼
|
||
Step 3. 评估
|
||
3a. 离线指标 (test_correction.py) — top-k overlap / AUC
|
||
3b. Rollout 评估 (eval_correction.py) — 迭代加密 → FEM → aw_rel
|
||
│
|
||
▼
|
||
Step 4. 可视化 (viz_correction.py)
|
||
amr 模式: GNN 驱动完整 AMR → 网格 + 场 + 误差
|
||
step 模式: 单步 GNN vs teacher vs physics 标记对比
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 数据生成 (`gen.py`)
|
||
|
||
### 3.1 初始网格
|
||
|
||
物理自适应初始网格(`build_physics_safe_initial_mesh`):
|
||
|
||
- 均匀基底网格 + 迭代局部加密
|
||
- 介质外:h ≤ λ₀/q,介质内:h ≤ λ_eff/q(q=2,每波长 2 个单元)
|
||
- 加密准则:`score = max_edge / h_target > 1` 的单元被加密
|
||
- 二分搜索控制每批加密数量,不超预算
|
||
|
||
### 3.2 AMR 循环(每步保存)
|
||
|
||
```
|
||
初始 mesh → FEM solve → 残差估计器 η → teacher_mark (η top-k)
|
||
├── physics_score → physics_mark (top-k)
|
||
├── correction_label = teacher_mark − physics_mark
|
||
└── 保存 .npz → 按 η 选单元加密 → 下一步
|
||
```
|
||
|
||
**残差估计器 η**(`environment/helmholtz.py:_compute_residual_indicator`):
|
||
- 内部残差:`h_K/k · √V_K · |k²ε_r·u_h + k²(ε_r−1)·u_inc|`
|
||
- 梯度跳变:`√(½ Σ h_e/k · |[[∇u_h·n]]|²)`
|
||
- SBC 边界残差:`h_bnd/k · |∂u/∂n − i·k_local·u|`
|
||
|
||
**标记策略**:
|
||
- teacher_mark:按 η 取 top `mark_fraction`(默认 3%)
|
||
- physics_mark:按 `physics_score` 取 top `mark_fraction`
|
||
- correction_label = +1(teacher 独有)、0(一致)、−1(physics 独有)
|
||
|
||
**安全过滤**:
|
||
1. 面积过滤:排除面积 ≤ 1e-10 的退化单元
|
||
2. 反向 Dörfler:排除误差贡献最低 1% 的单元
|
||
|
||
### 3.3 输出格式
|
||
|
||
```
|
||
outlook/data_correction/
|
||
├── params_list.npz # (n_samples, 5) PDE 参数
|
||
├── sample0000_step000.npz # 逐样本逐步数据
|
||
└── summary.json
|
||
```
|
||
|
||
每个 step .npz:
|
||
|
||
| 字段 | 形状 | 说明 |
|
||
|------|------|------|
|
||
| `features` | `(n_elem, 15)` | 几何/物理特征 |
|
||
| `edge_index` | `(2, n_edges)` | 网格图结构 |
|
||
| `physics_score` | `(n_elem,)` | h/λ_eff |
|
||
| `teacher_eta` | `(n_elem,)` | 残差估计器 |
|
||
| `teacher_mark` | `(n_elem,)` | η top-k 标记 (0/1) |
|
||
| `physics_mark` | `(n_elem,)` | physics top-k 标记 (0/1) |
|
||
| `correction_label` | `(n_elem,)` | teacher − physics (−1/0/+1) |
|
||
|
||
---
|
||
|
||
## 4. 特征工程
|
||
|
||
### 4.1 15 维基础特征
|
||
|
||
| 维 | 特征 | 说明 |
|
||
|----|------|------|
|
||
| 0,1 | x, y | 单元中点坐标 |
|
||
| 2 | area | 单元面积 |
|
||
| 3 | dist_to_center | 到圆柱中心距离 |
|
||
| 4 | signed_dist | dist − radius(负=介质内) |
|
||
| 5 | inside | 是否在圆柱内 (0/1) |
|
||
| 6 | k | 波数 |
|
||
| 7 | eps_r | 介电常数 |
|
||
| 8 | radius | 半径 |
|
||
| 9,10 | cx, cy | 圆柱中心 |
|
||
| 11 | k_h | k × √area |
|
||
| 12 | k_eps_h | k × √eps_r × √area |
|
||
| 13,14 | sin(k·x), cos(k·x) | 入射波相位 |
|
||
|
||
### 4.2 Physics Score(第 16 维输入)
|
||
|
||
```python
|
||
lambda_eff = 2π / (k · √eps_r) # 介质内
|
||
或 2π / k # 介质外
|
||
physics_score = max_edge / lambda_eff # >1 = 分辨率不足
|
||
```
|
||
|
||
### 4.3 归一化
|
||
|
||
训练集 z-score 归一化,推理复用同一统计量:
|
||
```python
|
||
x_norm = (concat(features, physics_score) - mean) / scale
|
||
```
|
||
|
||
---
|
||
|
||
## 5. GNN 架构
|
||
|
||
### 5.1 CorrectionGNN
|
||
|
||
基于 DensityGNN 骨干,替换密度回归头为二分类 logit 头:
|
||
|
||
```
|
||
Input: (n_cells, 16) node features
|
||
│
|
||
├── node_embedding: Linear(16 → latent_dim)
|
||
├── edge_embedding: Linear(16 → latent_dim)
|
||
│
|
||
├── N × MessagePassingStep:
|
||
│ ├── EdgeModule: MLP([src | dst | edge_attr]) → latent_dim
|
||
│ ├── NodeModule: MLP([node | mean(incoming_edges)]) → latent_dim
|
||
│ └── LayerNorm + Residual
|
||
│
|
||
├── GlobalVirtualNode: mean_pool → attention_gate → broadcast
|
||
│
|
||
└── head: Linear(latent → hidden) → ReLU → Linear(hidden → 1 logit)
|
||
```
|
||
|
||
### 5.2 关键设计
|
||
|
||
- 边特征:`edge_attr = |x[src] - x[dst]|`(节点特征差绝对值)
|
||
- 边丢弃:训练 0.1,推理 0.0
|
||
- 损失:BCEWithLogitsLoss + per-graph `pos_weight = neg/pos`
|
||
|
||
### 5.3 训练配置
|
||
|
||
| 参数 | 值 |
|
||
|------|-----|
|
||
| latent_dim | 64 |
|
||
| num_mp_steps | 3 |
|
||
| head_hidden | 64 |
|
||
| lr | 1e-3 |
|
||
| optimizer | Adam |
|
||
| scheduler | ReduceLROnPlateau (patience=10) |
|
||
|
||
---
|
||
|
||
## 6. 评估体系
|
||
|
||
### 6.1 离线指标(test_correction.py)
|
||
|
||
- **top-k overlap**:GNN 概率最高的 k 个 cell 与 teacher_mark 的交集 / k
|
||
- **AUC**:ROC-AUC(GNN vs physics baseline)
|
||
- **gnn_beats_physics_ratio**:GNN 优于 physics 的样本比例
|
||
|
||
### 6.2 Rollout 评估(eval_correction.py)
|
||
|
||
从初始 mesh 出发,**无 teacher_eta,无残差标记,无中间 FEM solve**。每步只用分数决定加密单元,最终一次 FEM solve 计算误差。
|
||
|
||
三种方法:
|
||
|
||
| 方法 | 打分 | 说明 |
|
||
|------|------|------|
|
||
| `physics` | `physics_score` | 纯物理先验 |
|
||
| `neural` | `model(features + physics_score)` | 纯 GNN |
|
||
| `hybrid` | `α·zscore(physics) + β·zscore(neural)` | 混合(默认 α=β=0.5) |
|
||
|
||
### 6.3 误差指标
|
||
|
||
**aw_rel**(面积加权相对误差):
|
||
```
|
||
aw_rel = √( Σ err²_tri · area / Σ ref²_tri · area )
|
||
```
|
||
|
||
**max_err**(最大逐点误差):
|
||
```
|
||
max_err = max |Re(u_fem) − Re(u_mie)|
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 可视化
|
||
|
||
### 7.1 amr 模式
|
||
|
||
GNN 驱动完整 AMR,每步 FEM solve,展示网格和场演变。
|
||
|
||
输出:
|
||
- `amr_overview.png` — 所有步骤总览
|
||
- `amr_steps/step{XX}.png` — 每步 3 面板(FEM 场 / Mie 参考 / 误差)
|
||
- `ground_truth.png` — 高保真参考解(`--compare` 时)
|
||
- `compare.png` — Physics vs GNN vs Eta 对比(`--compare` 时)
|
||
|
||
### 7.2 step 模式
|
||
|
||
重建指定 AMR 步的 mesh,对比 GNN / teacher / physics 标记。
|
||
|
||
输出:
|
||
- `marks_*.png` — 2×2 对比图(teacher / GNN / physics / TP/FP/FN/TN)
|
||
- `field_gnn_*.png` — GNN 加密后 3 面板图
|
||
- `field_eta_*.png` — 传统 η 加密后 3 面板图
|
||
|
||
---
|
||
|
||
## 8. 使用方法
|
||
|
||
### 8.1 数据生成
|
||
|
||
```bash
|
||
python outlook/src/gen.py \
|
||
--n-samples 100 \
|
||
--max-elements 12000 \
|
||
--mark-fraction 0.03 \
|
||
--output-dir outlook/data_correction
|
||
```
|
||
|
||
### 8.2 训练
|
||
|
||
```bash
|
||
python outlook/src/train_correction.py \
|
||
--data-dir outlook/data_correction \
|
||
--epochs 100 \
|
||
--batch-size 32 \
|
||
--lr 1e-3 \
|
||
--device cuda \
|
||
--checkpoint-out outlook/ckpt/correction.pt
|
||
```
|
||
|
||
输出:
|
||
- `correction.pt` — 最终模型
|
||
- `correction_best.pt` — val_loss 最低 checkpoint
|
||
- `correction_train_log.json` — 逐 epoch 日志
|
||
|
||
### 8.3 离线指标评估
|
||
|
||
```bash
|
||
python outlook/src/test_correction.py \
|
||
--checkpoint outlook/ckpt/correction.pt \
|
||
--data-dir outlook/data_correction \
|
||
--device cuda \
|
||
--visualize \
|
||
--output-dir outlook/result/correction/test
|
||
```
|
||
|
||
### 8.4 Rollout 评估
|
||
|
||
```bash
|
||
python outlook/src/eval_correction.py \
|
||
--checkpoint outlook/ckpt/correction.pt \
|
||
--data-dir outlook/data_correction \
|
||
--target-elements 2000,4000,8000,12000 \
|
||
--mark-fraction 0.03 \
|
||
--max-steps 40 \
|
||
--methods physics,neural,hybrid \
|
||
--alpha 0.5 --beta 0.5 \
|
||
--device cuda \
|
||
--output-dir outlook/result/correction/rollout
|
||
```
|
||
|
||
输出:
|
||
- `eval_results.json` — 详细结果 + 聚合统计 + 改善比例
|
||
- `summary.csv` — 每行一个 (method, target) 汇总
|
||
- `aw_rel_vs_elements.png` / `max_err_vs_elements.png`
|
||
|
||
### 8.5 可视化
|
||
|
||
```bash
|
||
# 端到端 AMR
|
||
python outlook/src/viz_correction.py \
|
||
--checkpoint outlook/ckpt/correction.pt \
|
||
--data-dir outlook/data_correction \
|
||
--mode amr --sample-id 0 --max-elements 5000 \
|
||
--device cuda \
|
||
--output-dir outlook/result/correction/viz
|
||
|
||
# 单步对比
|
||
python outlook/src/viz_correction.py \
|
||
--checkpoint outlook/ckpt/correction.pt \
|
||
--data-dir outlook/data_correction \
|
||
--mode step --sample-id 0 --step 0 \
|
||
--device cuda \
|
||
--output-dir outlook/result/correction/viz
|
||
|
||
# OOD 评估(自定义物理参数,需同时指定 k, eps-r, cx, cy, radius)
|
||
python outlook/src/viz_correction.py \
|
||
--checkpoint outlook/ckpt/correction.pt \
|
||
--data-dir outlook/data_correction \
|
||
--mode amr \
|
||
--k 30 --eps-r 4 --cx 0.5 --cy 0.5 --radius 0.15 \
|
||
--max-elements 6000 --compare \
|
||
--device cuda \
|
||
--output-dir outlook/result/correction/viz
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 目录结构
|
||
|
||
```
|
||
outlook/
|
||
├── README.md # 本文档
|
||
├── train.sh # SLURM 训练脚本(可配置 batch_size,默认 1)
|
||
├── analyze_budget_teacher.py # Budget teacher 数据集分析
|
||
├── check_correction_data.py # 数据质量校验
|
||
│
|
||
├── src/
|
||
│ ├── gen.py # 数据生成
|
||
│ ├── train_correction.py # 训练
|
||
│ ├── test_correction.py # 离线指标评估
|
||
│ ├── eval_correction.py # Rollout 评估
|
||
│ ├── viz_correction.py # 可视化
|
||
│ ├── rollout.py # 统一 AMR rollout(核心循环 + 共享 refinement 工具)
|
||
│ ├── gnn.py # DensityGNN 模型
|
||
│ ├── feat.py # 特征提取
|
||
│ ├── graph.py # mesh → PyG graph(含 build_edge_index_np)
|
||
│ ├── mesh.py # score → refined mesh
|
||
│ ├── metrics.py # aw_rel / max_err
|
||
│ ├── problem.py # PDE 参数 → HelmholtzProblem
|
||
│ └── amr.py # 残差 AMR teacher
|
||
│
|
||
├── ckpt/ # checkpoint
|
||
├── data_correction/ # 训练数据
|
||
└── result/ # 评估结果
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 辅助模块
|
||
|
||
| 模块 | 职责 |
|
||
|------|------|
|
||
| `rollout.py` | 统一 AMR rollout:`run_rollout_to_budget()` 驱动完整加密循环,支持 physics/neural/hybrid/eta 四种打分方法 |
|
||
| `feat.py` | 构建 15 维基础特征 + budget_code |
|
||
| `graph.py` | mesh → PyG Data(边特征 = phase_distance) |
|
||
| `mesh.py` | score → 迭代 top-k 加密(叶子继承初始单元 score) |
|
||
| `problem.py` | 参数字典 → HelmholtzProblem 实例 |
|
||
| `amr.py` | 纯残差驱动 AMR teacher(无网络) |
|
||
| `metrics.py` | `compute_mie_error`(aw_rel + max_err) |
|
||
|
||
---
|
||
|
||
## 11. 测试结果
|
||
|
||
### 11.1 训练配置
|
||
|
||
- 数据集:100 样本,~19 步/样本,1888 个 step 文件
|
||
- 训练/验证划分:80/20(按 sample_id,seed=42)
|
||
- 训练图:1513 个,验证图:375 个
|
||
- 正样本比例:3.0%(teacher_mark top-3%)
|
||
- 训练 100 epoch,最佳 epoch 57(val_loss=0.5873)
|
||
|
||
### 11.2 离线指标(test_correction.py)
|
||
|
||
在 20 个验证样本的 375 个 step 图上评估:
|
||
|
||
| 指标 | GNN | Physics Baseline |
|
||
|------|-----|-----------------|
|
||
| **AUC** | **0.9412** | 0.0000 |
|
||
| **top-k overlap 均值** | **0.440** | 0.131 |
|
||
| GNN beats physics | **372/375 (99.2%)** | — |
|
||
|
||
> Physics AUC=0.0 是因为 physics_score 在均匀网格上只有 2 个离散值,无法区分排序。GNN 通过学习 η 的空间分布模式,top-k overlap 提升 **3.4 倍**。
|
||
|
||
### 11.3 Rollout 评估(eval_correction.py)
|
||
|
||
20 个验证样本 × 3 种方法 × 3 个目标预算,每次 rollout 最终做 1 次 FEM solve:
|
||
|
||
**aw_rel(面积加权相对误差)**:
|
||
|
||
| 方法 | target=2000 | target=4000 | target=8000 |
|
||
|------|------------|------------|------------|
|
||
| physics | 19.19% | 14.04% | 13.25% |
|
||
| neural | 15.96% | 13.72% | 12.98% |
|
||
| **hybrid** | **15.78%** | **13.57%** | **12.94%** |
|
||
|
||
**max_err(最大逐点误差)**:
|
||
|
||
| 方法 | target=2000 | target=4000 | target=8000 |
|
||
|------|------------|------------|------------|
|
||
| physics | 0.3301 | 0.2514 | 0.2418 |
|
||
| neural | 0.2741 | 0.2431 | 0.2376 |
|
||
| **hybrid** | **0.2686** | **0.2430** | **0.2383** |
|
||
|
||
**相对 physics-only 的改善**:
|
||
|
||
| 方法 | target | aw_rel 改善 | max_err 改善 |
|
||
|------|--------|-----------|-------------|
|
||
| hybrid | 2000 | **+9.8%** | **+6.9%** |
|
||
| neural | 2000 | +9.9% | +6.2% |
|
||
| hybrid | 4000 | +1.9% | +1.5% |
|
||
| neural | 4000 | +1.1% | +1.4% |
|
||
| hybrid | 8000 | +1.9% | +1.3% |
|
||
| neural | 8000 | +1.8% | +1.5% |
|
||
|
||
### 11.4 关键发现
|
||
|
||
1. **GNN 显著优于 physics baseline**:离线 top-k overlap 从 0.131 提升到 0.440(3.4×),99.2% 的验证图上 GNN 胜出
|
||
2. **低预算改善最大**:target=2000 时 aw_rel 改善 ~10%,max_err 改善 ~7%;高预算时改善收窄到 ~2%(因为预算充足时 physics 也够用)
|
||
3. **hybrid 略优于 neural**:z-score 混合策略在多数场景下比纯 GNN 更稳定
|
||
4. **GNN 推理效率**:neural 方法比 physics 方法少用 ~12% 的 refinement 步数达到相同预算(8.9 vs 9.4 步 @2000),因为 GNN 的标记更精准
|
||
|
||
### 11.5 OOD 评估(k 超出训练范围 [3,15])
|
||
|
||
固定 `eps_r=4, cx=cy=0.5, radius=0.15`,在 k=20/30/50/80 上做三方法对比(target=5000/10000/20000):
|
||
|
||
**aw_rel (%)**:
|
||
|
||
| k | 方法 | target=5000 | target=10000 | target=20000 |
|
||
|---|------|------------|-------------|-------------|
|
||
| 20 | physics | 32.17 | 24.55 | 10.61 |
|
||
| 20 | neural | 32.56 | **17.85** | 10.68 |
|
||
| 20 | hybrid | **31.73** | 19.57 | 14.03 |
|
||
| 30 | physics | 33.94 | 15.15 | 10.14 |
|
||
| 30 | neural | **23.52** | 13.40 | 8.38 |
|
||
| 30 | hybrid | 27.94 | **13.34** | **7.96** |
|
||
| 50 | physics | 94.00 | 73.56 | 33.00 |
|
||
| 50 | neural | **88.16** | **52.92** | **27.99** |
|
||
| 50 | hybrid | 90.97 | 68.47 | 29.27 |
|
||
| 80 | physics | 122.06 | 135.78 | 101.64 |
|
||
| 80 | neural | 139.84 | **127.65** | **92.67** |
|
||
| 80 | hybrid | 139.84 | 127.68 | 105.94 |
|
||
|
||
**相对 physics-only 的改善 (%)**:
|
||
|
||
| k | 方法 | target=5000 | target=10000 | target=20000 |
|
||
|---|------|------------|-------------|-------------|
|
||
| 20 | neural | −1.2 | **+27.3** | −0.7 |
|
||
| 20 | hybrid | +1.4 | +20.3 | −32.3 |
|
||
| 30 | neural | **+30.7** | +11.6 | +17.4 |
|
||
| 30 | hybrid | +17.7 | +12.0 | +21.5 |
|
||
| 50 | neural | +6.2 | **+28.1** | **+15.2** |
|
||
| 50 | hybrid | +3.2 | +6.9 | +11.3 |
|
||
| 80 | neural | −14.6 | +6.0 | **+8.8** |
|
||
| 80 | hybrid | −14.6 | +6.0 | −4.2 |
|
||
|
||
**OOD 关键发现**:
|
||
|
||
1. **neural 在远 OOD(k=50)优势最大**:target=10000 时 aw_rel 从 73.56% 降至 52.92%(改善 +28.1%),hybrid 仅改善 +6.9%
|
||
2. **hybrid 的 physics 先验在 OOD 时成为拖累**:z-score 混合中 physics_score 的 `max_edge/h_target` 在高 k 下不再准确,导致 hybrid 的标记不如纯 neural
|
||
3. **neural 的泛化来自几何特征**:GNN 学到的是"界面附近 + 介质内 → 高密度"的空间模式,这一模式在 k 超出训练范围时仍然成立;而 physics_score 依赖 `lambda_eff = 2π/(k√ε_r)` 的具体数值
|
||
4. **极端 OOD(k=80)两者都差**:误差超过 100%,说明 20000 个单元完全不够分辨 k=80 的短波结构(λ_eff≈0.028,需要 ~70000+ 单元)
|
||
5. **k=20/30 时 hybrid 反而更好**:接近训练分布时 physics 先验有价值,hybrid 通过混合两种信号获得更稳定的标记
|