afem/outlook/README.md

470 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/qq=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²(ε_r1)·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 = +1teacher 独有、0一致1physics 独有)
**安全过滤**
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-AUCGNN 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_idseed=42
- 训练图1513 个验证图375 个
- 正样本比例3.0%teacher_mark top-3%
- 训练 100 epoch最佳 epoch 57val_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.4403.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 在远 OODk=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. **极端 OODk=80两者都差**:误差超过 100%,说明 20000 个单元完全不够分辨 k=80 的短波结构λ_eff≈0.028,需要 ~70000+ 单元)
5. **k=20/30 时 hybrid 反而更好**:接近训练分布时 physics 先验有价值hybrid 通过混合两种信号获得更稳定的标记