  ---
  ASMR++ 奖励计算完整分析

  默认配置使用 reward_type: spatial_max + error_metric: maximum。整个奖励信号链分以下步骤：

  Step 1: 误差估计 — 精细网格参考解

  参考网格 (初始网格细化6次)
         ↓ FEM求解
  参考解 u_ref (视为"真值")
         ↓
  粗网格解 u_coarse 在每个积分点(参考网格元素中点)与 u_ref 比较
         ↓
  绝对误差 |u_ref - u_coarse| per 积分点
         ↓ scatter_max per 粗元素
  error_per_element: 每个粗网格元素内的最大误差 (num_elements, solution_dim)

  用精细网格做数值积分 (error_integrator.py:86-169)，支持三种积分方式：mean(积分平均值)、squared(积分平方误差)、maximum(元素内最大误差)。默认Poisson 是标量 PDE，solution_dim=1。

  Step 2: spatial_max 奖励计算

  核心代码在 mesh_refinement.py:657-714，以下是逐步推导：

  奖励基准 (reward_per_agent_and_dim)
    = previous_error_per_element           ← 细分前该元素的误差

  ┌─────┬─────┐
  │     │     │
  ├─────┼─────┤
  │     │     │
  └─────┴─────┘

  父元素 i (error=0.8)                子元素:  j1(0.3), j2(0.5), j3(0.6), j4(0.1)
                                                          ↓ scatter_max per agent_mapping
  max_mapped_error[i] = max(0.3, 0.5, 0.6, 0.1) = 0.6
                                                          ↓
  reward_raw[i] = 0.8 - 0.6 = +0.2  ✅ 误差最大的子元素也比父元素好

  关键：spatial_max 只奖励"所有子元素误差都下降"的情况。如果有任一子元素误差仍等于原父元素误差，reward=0。

  父元素 j (error=0.5)                子元素:  k1(0.5), k2(0.1), k3(0.2), k4(0.05)
                                                          ↓
  max_mapped_error[j] = max(0.5, 0.1, 0.2, 0.05) = 0.5
                                                          ↓
  reward_raw[j] = 0.5 - 0.5 = 0    ❌ 有一个子元素仍未改善

  对比 spatial (非 max) 模式：

  reward_raw[i] = previous_error[i] - Σ_j φ_ij * error[j]
                = 标量加法 (np.add.at) 把所有子元素误差从父元素误差中减去
  这种模式下即使部分子元素没有改善，整体仍有正奖励。

  Step 3: 归一化 + 降维到标量

  # 除以初始网格的误差 → 把误差改善量归一化到 [0, ~1] 区间
  reward_per_agent_and_dim = reward_per_agent_and_dim / initial_approximation_error

  # 多维 PDE 降维: dot product with solution_dimension_weights
  # Poisson 是标量PDE, weights=[1.0], 即恒等变换
  reward_per_agent = project_to_scalar(reward_per_agent_and_dim)
  # = np.dot(reward_per_agent_and_dim, [1.0]) = reward_per_agent_and_dim

  Step 4: 元素惩罚 (Element Penalty)

  # 统计每个父元素产生了多少子元素
  element_counts = unique(agent_mapping, return_counts=True)[1]  # 每个父元素→子元素的数量
  element_counts = element_counts - 1  # 减1因为是"新增的"子元素数

  # 默认 λ ~ 0.01 (loguniform 采样于 [1e-3, 1e-1])
  element_penalty = λ * element_counts

  ┌──────────────────────────┬────────────────┬──────────────────┐
  │           场景           │ element_counts │ penalty (λ=0.01) │
  ├──────────────────────────┼────────────────┼──────────────────┤
  │ 未细分元素               │       0        │        0         │
  ├──────────────────────────┼────────────────┼──────────────────┤
  │ 分裂为 4 个子三角        │       3        │       0.03       │
  ├──────────────────────────┼────────────────┼──────────────────┤
  │ 被波及细分 (Rivara 平滑) │      1-3       │    0.01-0.03     │
  └──────────────────────────┴────────────────┴──────────────────┘

  作用: 惩罚是正则化项，防止策略无节制细分所有元素。只在"误差改善 > 细分代价"时细分才有利。

  Step 5: 元素上限惩罚 (Element Limit Penalty)

  if num_elements > maximum_elements (20000):
      element_limit_penalty = 1000 / previous_num_elements  # ≈ 0.05~0.5 per agent
  else:
      element_limit_penalty = 0

  Step 6: 最终每 Agent 奖励

  r_i = error_improvement_i / initial_error
      - λ * new_elements_created_by_i
      - limit_penalty

  形状为 (num_agents_t,) — 每个 agent（父元素）一个标量奖励。

  Step 7: 奖励到 TD 误差 — 与论文公式 (3) 的对应

  Buffer 存储:
    r_i(s_t, a_t)      ← 父元素 i 的奖励 (num_agents_t,)
    V_i(s_t)            ← 父元素 i 的价值 (num_agents_t,)
    φ_ij = agent_mapping ← 子元素j → 父元素i 的映射
    V_j(s_{t+1})         ← 子元素的价值 (num_agents_{t+1},)

  GAE Delta 计算:
    projected_V = scatter_sum(V_j(s_{t+1}), index=φ_ij)   ← Σ_j φ_ij·V_j(s_{t+1})
    δ_i = r_i + γ * projected_V_i - V_i(s_t)

    对应论文 (3):  δ_i^t = r(s^t, a^t)_i + γ·Σ_j φ_ij^t·V_j(s^{t+1}) - V_i(s^t)

  Step 8: 混合奖励 (Mixed Return, global_weight=0.5)

  在 MixedOnPolicyBuffer 中额外计算：

  # 全局奖励 (均值)
  r_global = mean(r_i)                     # 所有agent的平均奖励

  # 全局价值 (均值)
  V_global = mean(V_i)                     # 所有agent的平均价值

  # 全局 GAE
  δ_global = r_global + γ·V_global' - V_global

  # 局部 GAE
  δ_local_i = 上述 per-agent GAE

  # 混合 Advantage
  A_i = (1 - 0.5) * A_local_i + 0.5 * A_global

  完整奖励流总结

  FEM求解 → 逐元素误差估计 (±积分 vs 参考网格)
           ↓
    spatial_max: error_before - max_error_of_children
           ↓
    归一化 (/ initial_error)
           ↓
    - λ * new_elements + limit_penalty
           ↓
    r_i (per agent) ────────────→ 局部 GAE → A_local_i
           │                                      ↓
           └→ r_global = mean(r_i) → 全局 GAE → A_global
                                                  ↓
                                     A_i = 0.5·A_local_i + 0.5·A_global
                                                  ↓
                                     送入 PPO policy_loss

  设计精巧之处：
  1. 空间奖励 + agent_mapping：每个元素独立计算误差改善，通过 agent_mapping φ_ij 追踪父→子关系
  2. spatial_max 语义：reward 表示"最差子元素的误差下降量"——驱动策略优先细分误差最大的区域
  3. 元素惩罚：防止盲目细分，精确到每个 agent 独立计算代价
  4. 混合奖励：局部信号指导细粒度决策 + 全局信号稳定整体训练