#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Build the AFEM group meeting PPTX deck -- Chinese version.""" from pptx import Presentation from pptx.util import Inches, Pt, Emu, Cm from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.shapes import MSO_SHAPE from pptx.oxml.ns import qn # Color palette (Nature-style restrained) WHITE = RGBColor(0xFF, 0xFF, 0xFF) BLACK = RGBColor(0x1A, 0x1A, 0x1A) DARK_GRAY = RGBColor(0x33, 0x33, 0x33) BODY_GRAY = RGBColor(0x44, 0x44, 0x44) CAPTION = RGBColor(0x88, 0x88, 0x88) LIGHT_LINE = RGBColor(0xDD, 0xDD, 0xDD) LIGHTER_LINE = RGBColor(0xEE, 0xEE, 0xEE) ACCENT_BLUE = RGBColor(0x2C, 0x5F, 0x8A) ACCENT_TEAL = RGBColor(0x3A, 0x7B, 0x7B) ACCENT_WARM = RGBColor(0x8B, 0x45, 0x2C) ACCENT_GREEN = RGBColor(0x3A, 0x7B, 0x4F) HIGHLIGHT_BG = RGBColor(0xE8, 0xF0, 0xF8) WARN_BG = RGBColor(0xFE, 0xF3, 0xE8) TABLE_HDR = RGBColor(0xE8, 0xF0, 0xF8) TABLE_ALT = RGBColor(0xF5, 0xF7, 0xFA) SLIDE_W = Inches(13.333) SLIDE_H = Inches(7.5) TITLE_SIZE = Pt(28) SUBHEAD_SIZE = Pt(18) BODY_SIZE = Pt(14) SMALL_SIZE = Pt(12) CAPTION_SIZE = Pt(8) TAKEAWAY_SIZE = Pt(11) prs = Presentation() prs.slide_width = SLIDE_W prs.slide_height = SLIDE_H blank_layout = prs.slide_layouts[6] def add_blank_slide(): return prs.slides.add_slide(blank_layout) def set_slide_bg(slide, color=WHITE): bg = slide.background fill = bg.fill fill.solid() fill.fore_color.rgb = color def add_rect(slide, left, top, width, height, fill_color=None, line_color=None, line_width=None): shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height) shape.line.fill.background() if fill_color: shape.fill.solid() shape.fill.fore_color.rgb = fill_color else: shape.fill.background() if line_color: shape.line.color.rgb = line_color shape.line.fill.solid() if line_width: shape.line.width = line_width return shape def add_textbox(slide, left, top, width, height, text="", font_size=BODY_SIZE, font_color=BODY_GRAY, bold=False, alignment=PP_ALIGN.LEFT, font_name='Microsoft YaHei', anchor=MSO_ANCHOR.TOP, line_spacing=1.3): txbox = slide.shapes.add_textbox(left, top, width, height) txbox.text_frame.word_wrap = True tf = txbox.text_frame tf.paragraphs[0].alignment = alignment tf.paragraphs[0].space_before = Pt(0) tf.paragraphs[0].space_after = Pt(0) tf.paragraphs[0].line_spacing = line_spacing run = tf.paragraphs[0].add_run() run.text = text run.font.size = font_size run.font.color.rgb = font_color run.font.bold = bold run.font.name = font_name rPr = run._r.get_or_add_rPr() rPr.set(qn('a:eaTypeface'), font_name) return txbox def add_multiline_textbox(slide, left, top, width, height, lines, font_size=BODY_SIZE, font_color=BODY_GRAY, font_name='Microsoft YaHei', line_spacing=1.5, alignment=PP_ALIGN.LEFT): txbox = slide.shapes.add_textbox(left, top, width, height) txbox.text_frame.word_wrap = True tf = txbox.text_frame for i, line_data in enumerate(lines): if isinstance(line_data, str): text, is_bold, fs, clr = line_data, False, font_size, font_color elif len(line_data) == 2: text, is_bold = line_data fs, clr = font_size, font_color elif len(line_data) == 3: text, is_bold, fs = line_data clr = font_color else: text, is_bold, fs, clr = line_data if i == 0: p = tf.paragraphs[0] else: p = tf.add_paragraph() p.alignment = alignment p.space_before = Pt(2) p.space_after = Pt(2) p.line_spacing = line_spacing run = p.add_run() run.text = text run.font.size = fs run.font.color.rgb = clr run.font.bold = is_bold run.font.name = font_name rPr = run._r.get_or_add_rPr() rPr.set(qn('a:eaTypeface'), font_name) return txbox def add_bullet_textbox(slide, left, top, width, height, bullets, font_size=BODY_SIZE, font_color=BODY_GRAY, font_name='Microsoft YaHei', bullet_char="-", line_spacing=1.5): txbox = slide.shapes.add_textbox(left, top, width, height) txbox.text_frame.word_wrap = True tf = txbox.text_frame for i, bullet_text in enumerate(bullets): if i == 0: p = tf.paragraphs[0] else: p = tf.add_paragraph() p.alignment = PP_ALIGN.LEFT p.space_before = Pt(3) p.space_after = Pt(3) p.line_spacing = line_spacing run_marker = p.add_run() run_marker.text = f"{bullet_char} " run_marker.font.size = font_size run_marker.font.color.rgb = ACCENT_BLUE run_marker.font.name = font_name rPr = run_marker._r.get_or_add_rPr() rPr.set(qn('a:eaTypeface'), font_name) run_text = p.add_run() run_text.text = bullet_text run_text.font.size = font_size run_text.font.color.rgb = font_color run_text.font.name = font_name rPr2 = run_text._r.get_or_add_rPr() rPr2.set(qn('a:eaTypeface'), font_name) return txbox def add_top_bar(slide): add_rect(slide, Inches(0), Inches(0), SLIDE_W, Pt(3), fill_color=ACCENT_BLUE) def add_slide_number(slide, num): add_textbox(slide, Inches(11.8), Inches(7.05), Inches(1.2), Inches(0.35), text=str(num), font_size=Pt(9), font_color=CAPTION, alignment=PP_ALIGN.RIGHT) def add_source_label(slide, text, left=None, top=None): if left is None: left = Inches(0.6) if top is None: top = Inches(6.95) add_textbox(slide, left, top, Inches(6), Inches(0.35), text=text, font_size=CAPTION_SIZE, font_color=CAPTION) def add_takeaway_bar(slide, text): add_rect(slide, Inches(0.6), Inches(6.55), Inches(12.1), Inches(0.38), fill_color=HIGHLIGHT_BG) add_textbox(slide, Inches(0.75), Inches(6.55), Inches(11.85), Inches(0.38), text=f">> {text}", font_size=TAKEAWAY_SIZE, font_color=ACCENT_BLUE, bold=False, anchor=MSO_ANCHOR.MIDDLE) def add_slide_title(slide, title_text): add_top_bar(slide) add_textbox(slide, Inches(0.6), Inches(0.35), Inches(12.1), Inches(0.7), text=title_text, font_size=TITLE_SIZE, font_color=BLACK, bold=True) add_rect(slide, Inches(0.6), Inches(1.05), Inches(1.5), Pt(2), fill_color=ACCENT_BLUE) def add_kpi_box(slide, left, top, width, height, value, label, color=ACCENT_BLUE): add_rect(slide, left, top, width, height, fill_color=HIGHLIGHT_BG) add_textbox(slide, left + Inches(0.1), top + Inches(0.08), width - Inches(0.2), Inches(0.4), text=value, font_size=Pt(22), font_color=color, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, left + Inches(0.1), top + Inches(0.5), width - Inches(0.2), Inches(0.35), text=label, font_size=Pt(10), font_color=CAPTION, alignment=PP_ALIGN.CENTER) # ====================================================================== # SLIDE 1: TITLE # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_rect(slide, Inches(0), Inches(0), SLIDE_W, Inches(0.08), fill_color=ACCENT_BLUE) add_rect(slide, Inches(0), Inches(0), Inches(0.08), SLIDE_H, fill_color=ACCENT_BLUE) add_textbox(slide, Inches(1.2), Inches(1.6), Inches(10.5), Inches(1.4), text="AFEM:基于 GNN + PPO 强化学习\n的自适应网格细化方法", font_size=Pt(38), font_color=BLACK, bold=True, line_spacing=1.3) add_textbox(slide, Inches(1.2), Inches(3.2), Inches(10.5), Inches(0.9), text="二维 Helmholtz 电磁散射问题的智能网格优化 -- 算法流程与创新汇总", font_size=Pt(18), font_color=BODY_GRAY) add_rect(slide, Inches(1.2), Inches(4.2), Inches(3.0), Pt(2), fill_color=ACCENT_BLUE) meta_lines = [ ("组会汇报 | 2025 年 5 月", False, Pt(14), CAPTION), ("", False, Pt(8), CAPTION), ("物理场景:二维 Helmholtz 方程 / 圆形介质散射体 / SBC 吸收边界", False, Pt(12), CAPTION), ("方法栈:GNN (Message Passing) / PPO / 连续尺寸场 / 残差型误差估计", False, Pt(12), CAPTION), ] add_multiline_textbox(slide, Inches(1.2), Inches(4.5), Inches(10.5), Inches(1.6), meta_lines, line_spacing=1.5) add_slide_number(slide, 1) # ====================================================================== # SLIDE 2: BACKGROUND # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "研究背景:为什么自适应网格细化很重要") left_bullets = [ "Helmholtz 方程描述电磁波在介质中的散射与传播,是电磁兼容、隐身设计、天线仿真等领域的基础方程", "有限元 (FEM) 求解精度高度依赖网格质量:网格过粗导致数值色散/污染效应;网格过密浪费计算资源", "高频 (k >> 1) 下污染效应严重:kh > 0.5 时 FEM 解定性错误,后续误差指示子完全不可靠", "核心挑战:如何用最少的网格单元达到目标精度?在误差大的区域加密,误差小的区域保持稀疏", ] add_bullet_textbox(slide, Inches(0.6), Inches(1.35), Inches(6.0), Inches(3.2), left_bullets, font_size=SMALL_SIZE) add_rect(slide, Inches(7.2), Inches(1.35), Inches(5.5), Inches(3.2), fill_color=HIGHLIGHT_BG) physics_lines = [ ("物理方程", True, Pt(14), ACCENT_BLUE), ("", False, Pt(4), BODY_GRAY), ("nabla^2 u_scat + k^2 * eps_r * u_scat = k^2 * (1-eps_r) * u_inc", True, Pt(13), ACCENT_TEAL), ("", False, Pt(4), BODY_GRAY), ("入射波:沿 -x 方向的平面波 u_inc = exp(i*k*x)", False, Pt(11), BODY_GRAY), ("散射体:圆形介质柱(eps_r 随机采样)", False, Pt(11), BODY_GRAY), ("边界条件:SBC 吸收边界 du/dn = i*k*u", False, Pt(11), BODY_GRAY), ("计算域:可配矩形域 [Lx, Ly]", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(7.4), Inches(1.5), Inches(5.1), Inches(2.8), physics_lines, line_spacing=1.3) kpis = [ ("kh > 1.4", "高频下典型 kh 值\n(远超 0.5 安全线)", ACCENT_WARM), ("400 -> 20,000", "网格单元数变化范围\n(初始 -> 最大上限)", ACCENT_BLUE), ("[2, 20]", "训练波数 k 覆盖范围\n(涵盖中频到高频)", ACCENT_TEAL), ] for i, (val, label, clr) in enumerate(kpis): add_kpi_box(slide, Inches(0.6 + i * 4.2), Inches(4.95), Inches(3.8), Inches(1.1), val, label, color=clr) add_takeaway_bar(slide, "Helmholtz 高频求解的核心矛盾:精度 vs 效率。需要智能网格细化策略来平衡二者。") add_source_label(slide, "参考文献:Ainsworth & Oden, A Posteriori Error Estimation in Finite Element Analysis, 2000") add_slide_number(slide, 2) # ====================================================================== # SLIDE 3: KNOWLEDGE GAP # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "知识缺口与技术瓶颈") add_rect(slide, Inches(0.6), Inches(1.35), Inches(5.7), Inches(2.5), fill_color=None, line_color=LIGHT_LINE, line_width=Pt(1)) add_textbox(slide, Inches(0.8), Inches(1.4), Inches(5.3), Inches(0.4), text="传统自适应方法的局限", font_size=SUBHEAD_SIZE, font_color=ACCENT_WARM, bold=True) trad_bullets = [ "基于误差指示子的 h-adaptivity 细化规则完全由人工设计", "细化判据固定(如设定误差阈值),无法适应不同 PDE 的物理特征", "SOLVE-ESTIMATE-MARK-REFINE 循环不考虑长期回报(每一步仅看当前误差)", "无法学习特定问题的网格模式,无法迁移到新 PDE 配置", ] add_bullet_textbox(slide, Inches(0.8), Inches(1.85), Inches(5.3), Inches(1.8), trad_bullets, font_size=Pt(11)) add_rect(slide, Inches(7.0), Inches(1.35), Inches(5.7), Inches(2.5), fill_color=None, line_color=ACCENT_BLUE, line_width=Pt(1.5)) add_textbox(slide, Inches(7.2), Inches(1.4), Inches(5.3), Inches(0.4), text="本工作的目标", font_size=SUBHEAD_SIZE, font_color=ACCENT_BLUE, bold=True) goal_bullets = [ "用强化学习 (RL) 替代人工规则,自动发现最优细化策略", "GNN 处理变长拓扑:每个三角形单元是一个独立的 RL agent", "连续尺寸场输出 -> 概率性元素选择 -> 非均匀自适应网格", "物理预算约束 + 误差驱动奖励 -> 计算资源集中在物理关键区域", ] add_bullet_textbox(slide, Inches(7.2), Inches(1.85), Inches(5.3), Inches(1.8), goal_bullets, font_size=Pt(11), bullet_char=">") add_textbox(slide, Inches(0.6), Inches(4.2), Inches(12.1), Inches(0.4), text="本次汇报的核心创新(相较前序工作)", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) innovations = [ ("[1] 无量纲化残差误差估计", "真空波数 k 归一化残差+相位/空间特征+GVN,介质内 eta 不被压低", ACCENT_BLUE), ("[2] Score-based 连续尺寸场", "score = -x_i 纯排序 + 物理预算约束 + Reverse Dörfler 动作掩码", ACCENT_TEAL), ("[3] L2 聚合奖励设计", "sqrt(sum eta_child^2) <= eta_parent 保证 r_local >= 0,永不惩罚细化", ACCENT_GREEN), ("[4] 尺度不变性架构", "N_init x domain_area + lambda 无量纲化特征 + ln 压缩 + 前渐近区约束", ACCENT_WARM), ] for i, (title, desc, clr) in enumerate(innovations): y = Inches(4.7 + i * 0.6) add_rect(slide, Inches(0.6), y, Pt(3), Inches(0.45), fill_color=clr) add_textbox(slide, Inches(0.85), y - Inches(0.02), Inches(3.0), Inches(0.45), text=title, font_size=Pt(13), font_color=clr, bold=True) add_textbox(slide, Inches(3.8), y - Inches(0.02), Inches(8.7), Inches(0.45), text=desc, font_size=Pt(11), font_color=BODY_GRAY) add_takeaway_bar(slide, "核心思路:让网格细化的每一步都具有明确的物理语义,而非纯数据驱动的黑箱映射") add_slide_number(slide, 3) # ====================================================================== # SLIDE 4: SYSTEM OVERVIEW # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "系统架构:RL 自适应网格细化闭环管线") stages = [ ("物理问题\n采样", ACCENT_BLUE), ("初始网格\n生成", ACCENT_BLUE), ("GNN\n观测", ACCENT_TEAL), ("Actor\n动作", ACCENT_TEAL), ("尺寸场\n排序", ACCENT_WARM), ("预算\n选择", ACCENT_WARM), ("网格\n细化", ACCENT_GREEN), ("FEM\n求解", ACCENT_GREEN), ("误差\n估计", ACCENT_GREEN), ("Reward\n计算", ACCENT_GREEN), ] y_center = Inches(2.6) box_w = Inches(1.1) box_h = Inches(0.85) gap = (Inches(12.1) - box_w * 10) / 9 for i, (label, clr) in enumerate(stages): x = Inches(0.6) + i * (box_w + gap) add_rect(slide, x, y_center, box_w, box_h, fill_color=HIGHLIGHT_BG, line_color=clr, line_width=Pt(1.5)) add_textbox(slide, x, y_center + Inches(0.05), box_w, box_h - Inches(0.1), text=label, font_size=Pt(11), font_color=clr, bold=True, alignment=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) if i < len(stages) - 1: arrow_x = x + box_w add_textbox(slide, arrow_x, y_center + Inches(0.22), gap, Inches(0.35), text=">", font_size=Pt(16), font_color=LIGHT_LINE, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, Inches(6.0), Inches(3.55), Inches(1.5), Inches(0.35), text="<-- 下一轮迭代(多步 rollout)", font_size=Pt(10), font_color=ACCENT_TEAL, alignment=PP_ALIGN.CENTER) # RL modeling add_textbox(slide, Inches(0.6), Inches(4.1), Inches(6.0), Inches(0.35), text="RL 问题建模", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) rl_lines = [ ("Agent = 每个三角形单元(数量动态变化,约 400 -> 20,000)", False, Pt(11), BODY_GRAY), ("State = GNN 节点 14 维特征(几何 + PDE 残差 + 振幅 + 相位方向 + 物理参数)", False, Pt(11), BODY_GRAY), ("Action = 1 维连续标量 x_i -> score = -x_i 排序 -> top-k 选择细化单元", False, Pt(11), BODY_GRAY), ("Reward = 零和预算审查: refined 获 r_local+0.3x(eta/mu-1)-0.06; unrefined r=0", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(0.6), Inches(4.5), Inches(6.0), Inches(2.0), rl_lines, line_spacing=1.6) # PPO training add_textbox(slide, Inches(7.2), Inches(4.1), Inches(5.5), Inches(0.35), text="PPO 训练配置", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) train_lines = [ ("双 GNN 架构:Policy / Value 各自独立 MessagePassingBase", False, Pt(11), BODY_GRAY), ("2 层消息传递 + GVN 全局虚拟节点 (注意力门控广播),inner 残差 + LayerNorm,latent_dim=64", False, Pt(11), BODY_GRAY), ("DiagGaussian 连续动作分布,log_std 可学习,clamp [-4, -1]", False, Pt(11), BODY_GRAY), ("256 步 Rollout,5 Epochs,GAE lambda=0.95,lr=3e-4,梯度裁剪 0.5", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(7.2), Inches(4.5), Inches(5.5), Inches(2.0), train_lines, line_spacing=1.6) add_takeaway_bar(slide, "闭环 RL 管线:物理求解 -> GNN 感知 -> 策略决策 -> 网格操作 -> 误差反馈 -> 策略更新") add_slide_number(slide, 4) # ====================================================================== # SLIDE 5: INNOVATION 1 - Non-dimensionalized residual error # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新 [1]:无量纲化残差误差估计 -- 消除几何尺度偏差") add_textbox(slide, Inches(0.6), Inches(1.25), Inches(5.8), Inches(0.35), text="前序问题:原始残差包含 h_K、h_e 等几何尺度,不同区域不可直接比较", font_size=Pt(13), font_color=ACCENT_WARM) add_textbox(slide, Inches(0.6), Inches(1.55), Inches(5.8), Inches(0.35), text="解决方案:改用真空波数 k 归一化,介质内残差不再被 sqrt(eps_r) 压低", font_size=Pt(13), font_color=ACCENT_BLUE) formulas = [ ("内部残差 r_int", "(h_K/k) * sqrt(V) * |k^2*eps_r*u + k^2*(eps_r-1)*u_inc|_K", "单元内部 PDE 残差;真空波数 k 归一化;SBC 条件保留 k_local"), ("梯度跳变 r_jump", "sqrt(1/2 * sum_{e in dK} (h_e/k) * |[[grad u * n]]|^2_e)", "相邻单元梯度跳变;h_e/k 使细化后跳变自然衰减"), ("SBC 边界 r_sbc", "(h_bnd/k) * |du/dn - i*k_local*u|", "Sommerfeld 吸收边界残差,仅在边界单元非零"), ] for i, (name, formula, desc) in enumerate(formulas): x = Inches(0.6 + i * 4.1) add_rect(slide, x, Inches(2.0), Inches(3.85), Inches(1.65), fill_color=HIGHLIGHT_BG) add_textbox(slide, x + Inches(0.15), Inches(2.05), Inches(3.55), Inches(0.3), text=name, font_size=Pt(13), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, x + Inches(0.15), Inches(2.35), Inches(3.55), Inches(0.65), text=formula, font_size=Pt(11), font_color=BLACK) add_textbox(slide, x + Inches(0.15), Inches(3.05), Inches(3.55), Inches(0.5), text=desc, font_size=Pt(10), font_color=CAPTION) add_rect(slide, Inches(0.6), Inches(3.95), Inches(12.1), Inches(0.7), fill_color=None, line_color=ACCENT_BLUE, line_width=Pt(1.5)) add_textbox(slide, Inches(0.8), Inches(4.0), Inches(3.5), Inches(0.55), text="逐单元误差指示子", font_size=Pt(15), font_color=BLACK, bold=True, anchor=MSO_ANCHOR.MIDDLE) add_textbox(slide, Inches(4.0), Inches(4.0), Inches(3.5), Inches(0.55), text="eta_K = sqrt(r_int^2 + r_jump^2 + r_sbc^2)", font_size=Pt(15), font_color=ACCENT_BLUE, bold=True, anchor=MSO_ANCHOR.MIDDLE) add_textbox(slide, Inches(7.5), Inches(4.0), Inches(5.0), Inches(0.55), text="三项均严格无量纲\n跨介质、跨频率公平可比", font_size=Pt(13), font_color=ACCENT_GREEN, anchor=MSO_ANCHOR.MIDDLE) add_textbox(slide, Inches(0.6), Inches(4.85), Inches(12.1), Inches(0.3), text="量纲分析验证", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) da_lines = [ ("k_local ~ [L]^-1, h_e ~ [L], |jump|^2 ~ [L]^-2 => h_e/k * |jump|^2 ~ [L]^2 * [L]^-2 = 1 严格无量纲", False, Pt(11), BODY_GRAY), ("GNN 输入用 log10 压缩的特征;Reward 用原始 eta_K(不经 log 压缩),两者公式一致,物理语义对齐", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(0.6), Inches(5.15), Inches(12.1), Inches(0.8), da_lines, line_spacing=1.5) add_takeaway_bar(slide, "真空波数 k 归一化使介质内残差自然放大 ~sqrt(eps_r) 倍,为 RL agent 提供正确的介质内/外优先级信号") add_slide_number(slide, 5) # ====================================================================== # SLIDE 6: INNOVATION 2 - 12D enhanced input features # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新 [2]:14 维增强输入特征 -- 赋予 GNN 振幅与相位方向感知") add_textbox(slide, Inches(0.6), Inches(1.25), Inches(12.1), Inches(0.35), text="前序 11 维 -> 现 12 维,新增 dist_to_interface。全部尺度相关特征均以真空波长 lambda=2*pi/k 无量纲化", font_size=Pt(13), font_color=ACCENT_BLUE) # Feature table — compact layout to avoid overflow row_h = Inches(0.30) table_top = Inches(1.65) cols = [Inches(0.6), Inches(2.0), Inches(5.5), Inches(9.8)] col_w = [Inches(1.4), Inches(3.5), Inches(4.3), Inches(3.1)] headers = ["维度", "特征名称", "物理含义", "归一化"] for j, (cx, hdr, w) in enumerate(zip(cols, headers, col_w)): add_rect(slide, cx, table_top, w, row_h, fill_color=TABLE_HDR) add_textbox(slide, cx + Inches(0.06), table_top, w - Inches(0.12), row_h, text=hdr, font_size=Pt(9), font_color=BLACK, bold=True, anchor=MSO_ANCHOR.MIDDLE) features = [ ("volume", "无量纲单元面积", "volume / lambda^2"), ("internal_residual", "内部残差(k_local 无量纲化 + log10)", "--"), ("gradient_jump", "梯度跳变残差(k_local 无量纲化 + log10)", "--"), ("sbc_residual", "SBC 边界残差(k_local 无量纲化 + log10)", "--"), ("element_penalty", "单元惩罚系数 lambda", "--"), ("timestep", "当前 rollout 步数", "--"), ("wave_number", "Helmholtz 波数 k", "--"), ("k_local_sqrt_vol", "k x sqrt(eps_r) x sqrt(volume)", "--"), ("is_sbc_boundary", "是否与 SBC 边界相邻 (0/1)", "--"), ("dist_to_interface", "到介质边界的带符号距离 [新增]", "sign(d)*ln(1+|d|/lambda)"), ("epsilon_r", "单元中点介电常数(内=eps_r, 外=1.0)", "--"), ("total_solution_magnitude", "散射场复数解的振幅", "--"), ] for i, (name, meaning, norm) in enumerate(features): y = table_top + row_h + i * row_h bg = TABLE_ALT if i % 2 == 1 else WHITE is_new = "[新增]" in meaning cells = [name, meaning, norm] for j, (cx, cell_text, w) in enumerate(zip(cols, cells, col_w)): add_rect(slide, cx, y, w, row_h, fill_color=bg, line_color=LIGHTER_LINE, line_width=Pt(0.5)) clr = ACCENT_TEAL if is_new and j == 1 else BODY_GRAY bld = is_new and j == 1 add_textbox(slide, cx + Inches(0.06), y, w - Inches(0.12), row_h, text=cell_text, font_size=Pt(8), font_color=clr, bold=bld, anchor=MSO_ANCHOR.MIDDLE) # Edge feature note — positioned after table (table bottom = 1.65 + 0.30 + 12*0.30 = 5.55") add_textbox(slide, Inches(0.6), Inches(5.65), Inches(12.1), Inches(0.25), text="边特征 (1 维):euclidean_distance / lambda -- 相邻单元中点无量纲距离 | 合计:14 (节点) + 1 (边) = 15 维图特征", font_size=Pt(9), font_color=BODY_GRAY) add_takeaway_bar(slide, "全部与尺度相关的特征均以 lambda 做无量纲归一化;dist_to_interface 用 sign·ln(1+|d|) 对数压缩,近场线性、远场自然压缩,与残差 log10 风格统一") add_slide_number(slide, 6) # ====================================================================== # SLIDE 7: INNOVATION 3 - Score-based sizing field # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新 [3]:Score-based 连续尺寸场 + 物理预算约束 + 动作掩码") add_textbox(slide, Inches(0.6), Inches(1.25), Inches(5.7), Inches(0.35), text="前序方案:S_i = N_base x Softplus(x_i) / Softplus(0) x median_area", font_size=Pt(12), font_color=ACCENT_WARM) add_textbox(slide, Inches(0.6), Inches(1.55), Inches(5.7), Inches(0.35), text="--> 依赖 median_area 基准,域缩放后语义漂移 (1x1 -> 2x2 基准 x4)", font_size=Pt(10), font_color=CAPTION) add_textbox(slide, Inches(6.9), Inches(1.25), Inches(5.6), Inches(0.35), text="当前方案:score = -x_i 纯排序 + 物理预算约束", font_size=Pt(12), font_color=ACCENT_BLUE) add_textbox(slide, Inches(6.9), Inches(1.55), Inches(5.6), Inches(0.35), text="--> score 排序丢失面积语义,但获得尺度不变性", font_size=Pt(10), font_color=CAPTION) add_rect(slide, Inches(0.6), Inches(2.1), Inches(12.1), Inches(3.1), fill_color=HIGHLIGHT_BG) add_textbox(slide, Inches(0.8), Inches(2.15), Inches(5.0), Inches(0.3), text="细化选择算法", font_size=Pt(14), font_color=BLACK, bold=True) algo_steps = [ ("Step 1: 物理预算", "A_budget_i = 1/2 x (lambda_local_i / 6)^2 仅用于 N_budget 计算\nN_budget = max(N_phys, ceil(5 x N_init)) rho_min=5.0,至少 5 倍初始单元数"), ("Step 2: Score 排序", "score = -x_i (Actor 输出标量)\nx 越小 -> 优先级越高,纯排序,不设正负门槛"), ("Step 3: 双过滤器", "eligible = {i | area_i > V_min_safeguard AND i in Reverse_Dorfler_set}\narea_floor: 纯数值底线 (1e-10 x domain_area)\nReverse Dorfler: 能量尾部淘汰 (eps_noise=0.01, >=20% floor)"), ("Step 4: Top-k 选择", "num = min(|eligible|, N_current//4, remaining//3) (自适应 cap, 增速 N//4)\nselected = top-k by score -> 1-to-4 切分细化"), ] for i, (title, content) in enumerate(algo_steps): y = Inches(2.55 + i * 0.63) add_textbox(slide, Inches(0.9), y, Inches(2.0), Inches(0.55), text=title, font_size=Pt(11), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, Inches(2.9), y, Inches(9.5), Inches(0.55), text=content, font_size=Pt(10), font_color=BODY_GRAY) add_rect(slide, Inches(0.6), Inches(5.45), Inches(12.1), Inches(0.95), fill_color=None, line_color=ACCENT_BLUE, line_width=Pt(0.5)) add_textbox(slide, Inches(0.8), Inches(5.5), Inches(11.7), Inches(0.85), text="为什么用 Reverse Dörfler 而非 P95 硬阈值?P95 在重尾分布下会被奇异点推至极高,一刀切屏蔽大片中等误差区域。Reverse Dörfler 基于能量累积 (L2 范数平方和),自适应于任意分布形态,剔除确认无价值的底部噪声,保留 >=20% 单元确保 Agent 选择空间。", font_size=Pt(11), font_color=BODY_GRAY) add_takeaway_bar(slide, "Score-based 排序 + 物理预算 + Reverse Dörfler 掩码:三层保障确保细化资源只投入到物理上需要的地方") add_slide_number(slide, 7) # ====================================================================== # SLIDE 8: INNOVATION 4 - L2 aggregation reward # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新 [4]:L2 聚合奖励设计 -- 保证非负,永不惩罚细化") add_rect(slide, Inches(0.6), Inches(1.25), Inches(12.1), Inches(0.85), fill_color=HIGHLIGHT_BG) add_textbox(slide, Inches(0.8), Inches(1.3), Inches(11.7), Inches(0.75), text="核心洞察:对 1-to-4 切分,用 L2 聚合 sqrt(sum eta_child^2) <= eta_parent 天然成立 -- 因为平方后 int 项 1->1/4 而 jump/sbc 项 1->1。\n如果用 L1 sum,sum eta_child > eta_parent(因 jump/sbc 项不变),会导致「细化=惩罚」。L2 聚合从根本上避免了这一结构性负偏置。", font_size=Pt(12), font_color=BLACK) add_rect(slide, Inches(0.6), Inches(2.35), Inches(7.5), Inches(1.85), fill_color=None, line_color=ACCENT_BLUE, line_width=Pt(1.5)) add_textbox(slide, Inches(0.8), Inches(2.4), Inches(7.1), Inches(0.3), text="逐步奖励计算", font_size=Pt(14), font_color=BLACK, bold=True) reward_lines = [ ("r_local_i = log(eta_old_i + eps) - log( sqrt(sum_{j:M[j]=i} eta_new_j^2) + eps )", True, Pt(13), ACCENT_BLUE), ("", False, Pt(4), BODY_GRAY), ("- 纯 int 主导区: eta_parent^2 = int^2, sum eta_child^2 = int^2/4 -> r_local = log(2) = +0.69 (强正奖励)", False, Pt(11), BODY_GRAY), ("- 纯 jump/sbc 主导区: eta_parent^2 = jump^2, sum eta_child^2 = jump^2 -> r_local = 0 (中性)", False, Pt(11), BODY_GRAY), ("- 永不惩罚细化 -- 与 L1 sum 方案根本不同", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(0.8), Inches(2.7), Inches(7.1), Inches(1.4), reward_lines, line_spacing=1.35) add_rect(slide, Inches(8.5), Inches(2.35), Inches(4.2), Inches(1.85), fill_color=WARN_BG) add_textbox(slide, Inches(8.7), Inches(2.4), Inches(3.8), Inches(0.3), text="epsilon_dynamic 动态截断", font_size=Pt(14), font_color=BLACK, bold=True) ed_lines = [ ("eps = max(0.05 x mean(eta_new), 1e-6)", True, Pt(11), ACCENT_WARM), ("", False, Pt(4), BODY_GRAY), ("自适应钳制,切断远场", False, Pt(11), BODY_GRAY), ("低 eta 区的 reward hacking", False, Pt(11), BODY_GRAY), ("", False, Pt(4), BODY_GRAY), ("防止 log(0) 数值爆炸,", False, Pt(11), BODY_GRAY), ("锚定当前误差分布而非", False, Pt(11), BODY_GRAY), ("固定阈值", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(8.7), Inches(2.7), Inches(3.8), Inches(1.4), ed_lines, line_spacing=1.2) add_textbox(slide, Inches(0.6), Inches(4.45), Inches(6.0), Inches(0.3), text="动作惩罚与元素上限", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) pen_lines = [ ("penalty_i = lambda x (n_i-1) + (lambda_limit/N_old) x 1[达到上限], lambda=0.06, lambda_limit=10000", False, Pt(12), BODY_GRAY), ("lambda 仅为 r_local 均值的约 1/6,轻微抑制网格膨胀,不影响主要学习信号", False, Pt(11), CAPTION), ] add_multiline_textbox(slide, Inches(0.6), Inches(4.8), Inches(6.0), Inches(0.7), pen_lines, line_spacing=1.5) add_textbox(slide, Inches(7.2), Inches(4.45), Inches(5.5), Inches(0.3), text="Actor 奖励设计原则", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) glob_lines = [ ("global_bonus 被 Helmholtz 污染误差污染", False, Pt(12), BODY_GRAY), ("E_new > E_old 可发生在正确细化后", False, Pt(11), BODY_GRAY), ("惩罚 Agent 做对的事 → 策略崩塌 (x<0→0.01)", False, Pt(11), BODY_GRAY), ("修正: global_bonus 仅诊断, 不注入 Actor reward", False, Pt(11), CAPTION), ] add_multiline_textbox(slide, Inches(7.2), Inches(4.8), Inches(5.5), Inches(0.7), glob_lines, line_spacing=1.5) add_takeaway_bar(slide, "零和预算审查: 奖金 0.3*(eta/mu-1) 全场求和为零 (Doerfler 准则 RL 对偶); unrefined r=0; global_bonus 仅诊断") add_slide_number(slide, 8) # ====================================================================== # SLIDE 9: REWARD CALIBRATION # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "奖励标度校准:随机策略下各分量量级实测") add_textbox(slide, Inches(0.6), Inches(1.25), Inches(12.1), Inches(0.35), text="随机策略下 1,321 个 refined-parent 样本实测(score-based 尺寸场)", font_size=Pt(12), font_color=CAPTION) kpi_data = [ ("+0.364", "r_local (L2 聚合)", "局部误差改善,主体信号"), ("+0.045", "penalty (lambda=0.02)", "仅占 r_local 的约 1/8"), ("+0.069", "alpha x Delta_logE (alpha=0.2)", "全局改善信号,约 r_local/5"), ("+0.387", "净奖励 net reward", "r_local >> penalty [check]"), ] for i, (val, label, desc) in enumerate(kpi_data): x = Inches(0.6 + i * 3.1) add_rect(slide, x, Inches(1.7), Inches(2.85), Inches(1.2), fill_color=HIGHLIGHT_BG) add_textbox(slide, x + Inches(0.1), Inches(1.75), Inches(2.65), Inches(0.4), text=val, font_size=Pt(24), font_color=ACCENT_BLUE, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, x + Inches(0.1), Inches(2.15), Inches(2.65), Inches(0.3), text=label, font_size=Pt(11), font_color=BLACK, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, x + Inches(0.1), Inches(2.45), Inches(2.65), Inches(0.35), text=desc, font_size=Pt(9), font_color=CAPTION, alignment=PP_ALIGN.CENTER) add_textbox(slide, Inches(0.6), Inches(3.2), Inches(12.1), Inches(0.3), text="设计验证", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) design_checks = [ ("[OK] r_local >> penalty", "局部 credit assignment 不被惩罚信号淹没,agent 能清晰感知细化 -> 误差下降的因果关系"), ("[OK] alpha x Delta_logE = r_local / 5", "全局信号提供趋势引导但不主导局部决策,避免 loss of local credit assignment"), ("[OK] r_local >= 0 保证", "L2 聚合天然保证非负,网络永远不会因细化而受到惩罚"), ] for i, (check, detail) in enumerate(design_checks): add_textbox(slide, Inches(0.8), Inches(3.5 + i * 0.45), Inches(2.8), Inches(0.35), text=check, font_size=Pt(12), font_color=ACCENT_GREEN, bold=True) add_textbox(slide, Inches(3.6), Inches(3.5 + i * 0.45), Inches(9.1), Inches(0.35), text=detail, font_size=Pt(11), font_color=BODY_GRAY) add_textbox(slide, Inches(0.6), Inches(5.1), Inches(12.1), Inches(0.3), text="奖励信号链", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) flow_steps = [ ("FEM 求解", "eta_K per element", ACCENT_BLUE), ("L2 聚合", "log(eta_old / sqrt(sum_chi^2))", ACCENT_TEAL), ("+ eps_dynamic", "截断保护", ACCENT_WARM), ("- penalty", "lambda x (n-1) 防膨胀", ACCENT_WARM), ("+ global", "alpha x Delta_logE 仅细化单元", ACCENT_GREEN), ("-> r_i", "送入 PPO GAE", ACCENT_BLUE), ] for i, (step_name, step_desc, clr) in enumerate(flow_steps): x = Inches(0.6 + i * 2.05) add_rect(slide, x, Inches(5.45), Inches(1.8), Inches(0.7), fill_color=HIGHLIGHT_BG, line_color=clr, line_width=Pt(1)) add_textbox(slide, x + Inches(0.1), Inches(5.48), Inches(1.6), Inches(0.3), text=step_name, font_size=Pt(11), font_color=clr, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, x + Inches(0.1), Inches(5.78), Inches(1.6), Inches(0.3), text=step_desc, font_size=Pt(9), font_color=CAPTION, alignment=PP_ALIGN.CENTER) if i < len(flow_steps) - 1: add_textbox(slide, x + Inches(1.8), Inches(5.6), Inches(0.25), Inches(0.3), text=">", font_size=Pt(14), font_color=LIGHT_LINE, bold=True, alignment=PP_ALIGN.CENTER) add_takeaway_bar(slide, "奖励各分量量级经过标定,满足 r_local >> penalty 且 alpha x Delta_logE 适度,agent 能学到细化 = 有益的信息") add_slide_number(slide, 9) # ====================================================================== # SLIDE 10: SCALE INVARIANCE # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新 [5]:尺度不变性架构 -- 从 1x1 到 2x2 的泛化") add_textbox(slide, Inches(0.6), Inches(1.25), Inches(12.1), Inches(0.4), text="问题:1x1 域训练 -> 2x2 域测试时,中心介质处网格未加密,远场误差显著增大", font_size=Pt(14), font_color=ACCENT_WARM) add_textbox(slide, Inches(0.6), Inches(1.75), Inches(12.1), Inches(0.3), text="根因分析(双重漂移)", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) roots = [ ("N_init 不随 domain area 缩放", "4x 面积用同数量单元 -> h 2x, area 4x", "N_init *= domain_area"), ("特征绝对值漂移", "volume/edge/dist 值随 domain 线性或平方放大", "全部用 lambda 无量纲化"), ("dist 远场 OOD", "2x2 域远角 dist/lambda 可达训练域 3x", "sign·ln(1+|d|/lambda) 对数压缩"), ] for i, (problem, cause, fix) in enumerate(roots): x = Inches(0.6 + i * 4.1) add_rect(slide, x, Inches(2.1), Inches(3.85), Inches(1.3), fill_color=HIGHLIGHT_BG) add_textbox(slide, x + Inches(0.1), Inches(2.13), Inches(3.65), Inches(0.3), text=problem, font_size=Pt(13), font_color=ACCENT_WARM, bold=True) add_textbox(slide, x + Inches(0.1), Inches(2.45), Inches(3.65), Inches(0.4), text=f"原因: {cause}", font_size=Pt(10), font_color=BODY_GRAY) add_textbox(slide, x + Inches(0.1), Inches(2.85), Inches(3.65), Inches(0.4), text=f"--> {fix}", font_size=Pt(11), font_color=ACCENT_GREEN) add_rect(slide, Inches(0.6), Inches(3.65), Inches(7.5), Inches(2.05), fill_color=None, line_color=ACCENT_BLUE, line_width=Pt(1)) add_textbox(slide, Inches(0.8), Inches(3.7), Inches(7.1), Inches(0.3), text="四项联动改进 = 完整的尺度不变性", font_size=Pt(14), font_color=BLACK, bold=True) k_mesh_lines = [ ("1. N_init = N_base x (k/k_ref)^k_exponent x domain_area (exponent/k_ref 可配,保证每单位面积密度一致)", False, Pt(12), BODY_GRAY), ("2. volume -> volume / lambda^2, euclidean_distance -> euclidean_distance / lambda", False, Pt(12), BODY_GRAY), ("3. dist_to_interface -> sign(d)*ln(1+|d|/lambda) (近场线性、远场对数压缩,与 log10 残差风格一致)", False, Pt(12), BODY_GRAY), ("4. 介质区前渐近区边缘约束: 强制迭代细化至 h <= lambda_d/N (N=1.5)", False, Pt(12), BODY_GRAY), ("--> 四项联动:N_init 修 h 漂移 + lambda 归一化修特征绝对值 + tanh 修远场 OOD", False, Pt(11), CAPTION), ] add_multiline_textbox(slide, Inches(0.8), Inches(4.0), Inches(7.1), Inches(1.5), k_mesh_lines, line_spacing=1.3) add_textbox(slide, Inches(8.5), Inches(3.7), Inches(4.2), Inches(0.3), text="N_init 缩放效果示例", font_size=Pt(13), font_color=BLACK, bold=True) k_table_lines = [ ("exponent 可配: ^2 = 理论最优, ^1.5 = 工程折中", False, Pt(10), BODY_GRAY), ("N_init 始终 = COMSOL 目标的 30-50%", False, Pt(10), BODY_GRAY), ("", False, Pt(4), BODY_GRAY), ("改前: 无 domain_area 缩放", True, Pt(10), ACCENT_WARM), ("-> 换 domain size 后 N_init 不变", False, Pt(10), CAPTION), ("-> h 随 domain 缩放,特征 OOD", False, Pt(10), CAPTION), ] add_multiline_textbox(slide, Inches(8.5), Inches(4.0), Inches(4.2), Inches(1.7), k_table_lines, line_spacing=1.3) add_takeaway_bar(slide, "N_init x domain_area + lambda 无量纲化 + ln 对数压缩:三项联动使模型可物理一致地泛化到任意尺寸测试域") add_slide_number(slide, 10) # ====================================================================== # SLIDE 11: DUAL GNN ARCHITECTURE # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "双 GNN 架构与 PPO 训练细节") add_textbox(slide, Inches(0.6), Inches(1.3), Inches(12.1), Inches(0.35), text="图观测 -> MessagePassingBase (Policy/Value 各自独立) -> Actor/Critic 头", font_size=Pt(13), font_color=BLACK, bold=True) add_rect(slide, Inches(0.6), Inches(1.8), Inches(5.8), Inches(3.0), fill_color=HIGHLIGHT_BG) add_textbox(slide, Inches(0.8), Inches(1.85), Inches(5.4), Inches(0.3), text="MessagePassingBase (x2, Policy / Value 各自独立基座)", font_size=Pt(13), font_color=ACCENT_BLUE, bold=True) gnn_items = [ ("节点嵌入", "Linear(14 -> 64)"), ("边嵌入", "Linear(1 -> 64)"), ("MP Step 1", "EdgeModule: MLP([src|dst|edge_attr]) -> 64d"), ("", "NodeModule: MLP([node|scatter_mean(入边)]) -> 64d"), ("", "+ inner 残差 + LayerNorm"), ("MP Step 2", "同 Step 1,堆叠 2 层"), ("GVN 全局虚拟节点", "h_V = Σ(η_v/Ση)·h_v (η_K 加权池化)"), ("", "α = σ(W[h_v||h_V]),h_v += scale·α ⊙ W_V·h_V"), ("输出", "节点隐向量 (num_nodes, 64)"), ] for i, (label, detail) in enumerate(gnn_items): y = Inches(2.25 + i * 0.32) if label: add_textbox(slide, Inches(0.9), y, Inches(1.6), Inches(0.28), text=label, font_size=Pt(10), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, Inches(2.5), y, Inches(3.7), Inches(0.28), text=detail, font_size=Pt(10), font_color=BODY_GRAY) add_rect(slide, Inches(7.0), Inches(1.8), Inches(5.7), Inches(1.4), fill_color=None, line_color=ACCENT_TEAL, line_width=Pt(1)) add_textbox(slide, Inches(7.2), Inches(1.85), Inches(5.3), Inches(0.3), text="Actor 头(策略网络)", font_size=Pt(13), font_color=ACCENT_TEAL, bold=True) actor_items = [ ("MLP: 2 层 Tanh (64 -> 64 -> 64)", False, Pt(11), BODY_GRAY), ("Linear(64 -> 1): 输出 x_i (连续标量)", False, Pt(11), BODY_GRAY), ("log_std: 可学习参数,初始化 -2.0 (std = 0.135)", False, Pt(11), BODY_GRAY), ("DiagGaussian(mu, sigma): 每节点独立动作分布", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(7.2), Inches(2.2), Inches(5.3), Inches(0.9), actor_items, line_spacing=1.3) add_rect(slide, Inches(7.0), Inches(3.4), Inches(5.7), Inches(1.4), fill_color=None, line_color=ACCENT_GREEN, line_width=Pt(1)) add_textbox(slide, Inches(7.2), Inches(3.45), Inches(5.3), Inches(0.3), text="Critic 头(价值网络)", font_size=Pt(13), font_color=ACCENT_GREEN, bold=True) critic_items = [ ("MLP: 2 层 Tanh (64 -> 64 -> 1)", False, Pt(11), BODY_GRAY), ("输出: V_i(s) 逐节点价值 (num_agents, 1)", False, Pt(11), BODY_GRAY), ("spatial value function: 不做聚合,保持逐节点", False, Pt(11), BODY_GRAY), ("GAE 中用 scatter_add 做子->父投影,处理变长拓扑", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(7.2), Inches(3.75), Inches(5.3), Inches(0.9), critic_items, line_spacing=1.3) add_textbox(slide, Inches(0.6), Inches(5.1), Inches(12.1), Inches(0.3), text="PPO 关键设计", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) ppo_details = [ ("单路 GAE", "scatter_add 将子单元值聚合回父单元,无需多路 GAE", ACCENT_BLUE), ("log_std clamp", "每步 optimizer.step() 后 clamp 到 [-4.0, -1.0],std in [0.018, 0.368]", ACCENT_TEAL), ("熵正则", "entropy_coefficient=0.001,防止 log_std 过早收敛到下限", ACCENT_GREEN), ("梯度裁剪", "max_grad_norm=0.5,稳定训练过程", ACCENT_WARM), ] for i, (tag, desc, clr) in enumerate(ppo_details): x = Inches(0.6 + i * 3.1) add_rect(slide, x, Inches(5.45), Inches(2.85), Inches(0.85), fill_color=HIGHLIGHT_BG) add_textbox(slide, x + Inches(0.1), Inches(5.5), Inches(2.65), Inches(0.3), text=tag, font_size=Pt(13), font_color=clr, bold=True, alignment=PP_ALIGN.CENTER) add_textbox(slide, x + Inches(0.1), Inches(5.8), Inches(2.65), Inches(0.4), text=desc, font_size=Pt(10), font_color=BODY_GRAY, alignment=PP_ALIGN.CENTER) add_takeaway_bar(slide, "双 GNN 各自独立建模 + DiagGaussian 连续动作 + scatter_add 单路 GAE -> 适合变长 agent 拓扑的 RL 训练框架") add_slide_number(slide, 11) # ====================================================================== # SLIDE 12: TRAINING OBSERVATIONS # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "训练观察与诊断:奖励稀疏性与大波数泛化") add_rect(slide, Inches(0.6), Inches(1.3), Inches(5.8), Inches(2.4), fill_color=WARN_BG) add_textbox(slide, Inches(0.8), Inches(1.35), Inches(5.4), Inches(0.35), text="观察 1: 75% rollout 步骤零 reward", font_size=Pt(14), font_color=ACCENT_WARM, bold=True) obs1_lines = [ ("4 步 rollout 中,第 0 步细化后介质区已达标 (h/lambda = 13 > N=15 参考线)", False, Pt(11), BODY_GRAY), ("步 1-3 全为零 reward,75% 的 FEM 求解白白浪费", False, Pt(11), BODY_GRAY), ("原因: 1-to-4 切分太粗,一步即达标,不存在差一点的中间状态", False, Pt(11), BODY_GRAY), ("偶尔的 spike (reward ~60) 来自随机探索中极负的 x_i 触发第二步细化", False, Pt(11), BODY_GRAY), ("--> 步 0 的 reward 信号足够训练「在哪里细化」的判断,但多步策略无法学习", False, Pt(11), CAPTION), ] add_multiline_textbox(slide, Inches(0.8), Inches(1.7), Inches(5.4), Inches(1.85), obs1_lines, line_spacing=1.35) add_rect(slide, Inches(7.0), Inches(1.3), Inches(5.7), Inches(2.4), fill_color=WARN_BG) add_textbox(slide, Inches(7.2), Inches(1.35), Inches(5.3), Inches(0.35), text="观察 2: 高 k 扇形阴影区网格偏粗", font_size=Pt(14), font_color=ACCENT_WARM, bold=True) obs2_lines = [ ("k in [2,20] 训练,小 k 尚可,大 k 效果不佳", False, Pt(11), BODY_GRAY), ("介质后方 +x 方向扇形区域网格偏粗,误差较大", False, Pt(11), BODY_GRAY), ("根本原因: 污染效应 -> 初始 kh > 0.5 时 FEM 解定性错误 (GIGO)", False, Pt(11), BODY_GRAY), ("粗网格 -> 错误解 -> 不可靠 eta -> 垃圾 GNN 特征 -> 垃圾动作", False, Pt(11), BODY_GRAY), ("2 层 GNN 感受野仅约 10 个单元,网络不知道自己在介质后方", False, Pt(11), BODY_GRAY), ] add_multiline_textbox(slide, Inches(7.2), Inches(1.7), Inches(5.3), Inches(1.85), obs2_lines, line_spacing=1.35) add_textbox(slide, Inches(0.6), Inches(4.0), Inches(12.1), Inches(0.3), text="训练日志解读 (k in [2,20], 随机 PDE, 4 步 rollout)", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) log_lines = [ ("loss ~ 0.10-0.18, explained_var ~ 0.65-0.78", "Critic 对价值函数的解释力中等偏上,尚可但非极强"), ("reward 间歇性 spike (0 -> 13 -> 60 -> 0)", "随机探索 + GAE 信度传播,信号稀疏但偶尔强正奖励"), ("agent 数量在 100-3500 间大幅波动", "取决于 PDE 随机采样和细化触发情况"), ("loss/ev 趋于平台期", "可能是 k^2 与 N=15 互斥的问题(已用 k^1.5 修复)"), ] for i, (log, interpret) in enumerate(log_lines): add_textbox(slide, Inches(0.8), Inches(4.35 + i * 0.42), Inches(4.0), Inches(0.35), text=log, font_size=Pt(11), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, Inches(5.0), Inches(4.35 + i * 0.42), Inches(7.7), Inches(0.35), text=interpret, font_size=Pt(11), font_color=BODY_GRAY) add_takeaway_bar(slide, "训练瓶颈非算法设计问题,而是物理前提 (污染效应 GIGO) 和多步细化粒度 (1-to-4 太粗) 的工程限制") add_slide_number(slide, 12) # ====================================================================== # SLIDE 13: INNOVATION SUMMARY # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "创新点汇总与可复用价值") innovations = [ ("[1]", "无量纲化\n残差误差估计", "真空波数 k 归一化残差\n介质内 η 不再被压低\nGNN+Reward 统一使用 k 归一化", ACCENT_BLUE), ("[2]", "Score-based\n连续尺寸场", "score = -x_i 纯排序\n物理预算 N_budget 约束\nReverse Dörfler 双过滤器掩码", ACCENT_TEAL), ("[3]", "L2 聚合\n奖励设计", "sqrt(sum eta_child^2) <= eta_parent 天然成立\n永不惩罚细化 (r_local >= 0)\nint 主导区强正奖励约 +0.69", ACCENT_GREEN), ("[4]", "尺度不变性\n架构", "N_init x domain_area 缩放\nlambda 无量纲化全部特征\nsign·ln 对数压缩 + 前渐近区约束", ACCENT_WARM), ("[5]", "双 GNN +\n变长拓扑 RL", "Policy/Value 独立 GNN 基座\nscatter_add 单路 GAE\nDiagGaussian + log_std clamp", RGBColor(0x5B, 0x3A, 0x8B)), ] for i, (num, title, desc, clr) in enumerate(innovations): x = Inches(0.6 + i * 2.5) add_rect(slide, x, Inches(1.35), Inches(2.3), Inches(3.1), fill_color=HIGHLIGHT_BG) add_rect(slide, x, Inches(1.35), Inches(2.3), Pt(3), fill_color=clr) add_textbox(slide, x + Inches(0.15), Inches(1.5), Inches(2.0), Inches(0.7), text=title, font_size=Pt(13), font_color=clr, bold=True, alignment=PP_ALIGN.LEFT, line_spacing=1.2) add_textbox(slide, x + Inches(0.15), Inches(2.3), Inches(2.0), Inches(2.0), text=desc, font_size=Pt(10), font_color=BODY_GRAY, line_spacing=1.4) add_textbox(slide, Inches(0.6), Inches(4.7), Inches(12.1), Inches(0.3), text="可复用价值(超越本项目的通用方法贡献)", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) reuse_items = [ ("L2 聚合 + 父子映射", "适用于任何分裂型变长 agent RL 场景(网格细化、树搜索、层次化决策)"), ("真空波数 k 归一化方法", "残差归一化用 k₀ 非 k_local,介质内物理信号不再被压低"), ("Score-based + 预算约束选择", "适用于资源受限的排序-选择问题:传感器部署、计算资源分配、实验设计优化"), ("Reverse Dörfler 动作掩码", "能量尾部淘汰的思想可推广到任何需要排除低信号样本的场景"), ] for i, (tag, desc) in enumerate(reuse_items): add_textbox(slide, Inches(0.8), Inches(5.05 + i * 0.42), Inches(2.8), Inches(0.35), text=tag, font_size=Pt(11), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, Inches(3.7), Inches(5.05 + i * 0.42), Inches(9.0), Inches(0.35), text=desc, font_size=Pt(11), font_color=BODY_GRAY) add_slide_number(slide, 13) # ====================================================================== # SLIDE 14: LIMITATIONS # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_slide_title(slide, "局限性与未解决问题") limitations = [ ("污染效应 (GIGO: Garbage-In-Garbage-Out)", [ "高 k 下初始 kh > 0.5 时 FEM 解定性错误,误差指示子 eta_K 完全不可靠", "RL 无法在错误解的基础上学到正确策略 -- 这是物理前提而非算法问题", "缓解: N_init x domaine_area 使真空始终 >= 12 单元/lambda,但高 k 下余量有限", ]), ("GNN 感受野受限", [ "2 层消息传递,每个节点感受野仅约 10 个单元,无法感知全局几何结构", "介质后方扇形阴影区:GNN 不知道自己在介质背后,小 k 学到的真空不需细化被错误泛化", "需要: 更多几何上下文特征(入射波方向、与介质相对位置)或更深的 GNN", ]), ("1-to-4 切分粒度", [ "一步细化即可达标 (每波长单元数 >= N=15 参考线),多步 rollout 中 75% 步骤零 reward", "高 eps_r 介质区可能需要 2-3 步细化,但 PPO GAE 在 4 步序列中传播稀疏信号效率极低", "需要: 更细粒度的切分方案(如 1-to-2 边切分)或递减的 N_per_wavelength 目标", ]), ("泛化到更多散射体配置", [ "当前仅在单个圆形介质柱上训练;多散射体、非圆形、复杂材料的泛化未经验证", "训练波数 [2,20] 覆盖范围有限,更高 k 需要更深的初始网格和更强的特征表达", "需要: 更丰富的 PDE 问题分布、课程学习、域随机化策略", ]), ] for i, (title, points) in enumerate(limitations): x = Inches(0.6 + (i % 2) * 6.3) y = Inches(1.3 + (i // 2) * 2.8) add_rect(slide, x, y, Inches(5.9), Inches(2.45), fill_color=None, line_color=LIGHT_LINE, line_width=Pt(1)) add_rect(slide, x + Pt(1), y + Pt(1), Pt(3), Inches(0.35), fill_color=ACCENT_WARM) add_textbox(slide, x + Inches(0.2), y + Inches(0.05), Inches(5.3), Inches(0.35), text=title, font_size=Pt(14), font_color=ACCENT_WARM, bold=True) for j, point in enumerate(points): add_textbox(slide, x + Inches(0.2), y + Inches(0.45 + j * 0.45), Inches(5.3), Inches(0.4), text=f"- {point}", font_size=Pt(10), font_color=BODY_GRAY) add_slide_number(slide, 14) # ====================================================================== # SLIDE 15: SUMMARY & DISCUSSION # ====================================================================== slide = add_blank_slide() set_slide_bg(slide, WHITE) add_top_bar(slide) add_rect(slide, Inches(0.6), Inches(2.0), Pt(4), Inches(4.0), fill_color=ACCENT_BLUE) add_textbox(slide, Inches(0.85), Inches(2.0), Inches(11.5), Inches(1.0), text="总 结", font_size=Pt(36), font_color=BLACK, bold=True) summary_points = [ "提出了一套完整的 RL 自适应网格细化框架:从物理建模、误差估计、状态表征、动作空间到奖励设计的全链路创新", "真空波数 k 归一化残差使介质内 η 自然放大,Agent 获得正确的物理优先级信号", "Score-based 尺寸场 + 物理预算约束 + Reverse Dörfler 掩码实现了资源感知的细化单元选择", "L2 聚合奖励设计从数学上保证了细化奖励非负,从根本上避免了 L1 sum 的结构性负偏置", "sign(d)*ln(1+|d|/lambda) 对数压缩 + lambda 归一化全部特征实现了域尺寸的尺度不变泛化", ] for i, point in enumerate(summary_points): add_textbox(slide, Inches(0.85), Inches(3.1 + i * 0.42), Inches(0.4), Inches(0.35), text=f"{i+1}.", font_size=Pt(14), font_color=ACCENT_BLUE, bold=True) add_textbox(slide, Inches(1.25), Inches(3.1 + i * 0.42), Inches(11.2), Inches(0.35), text=point, font_size=Pt(13), font_color=BODY_GRAY) add_rect(slide, Inches(0.85), Inches(5.4), Inches(11.5), Inches(0.05), fill_color=LIGHT_LINE) add_textbox(slide, Inches(0.85), Inches(5.6), Inches(11.5), Inches(0.4), text="讨论与后续方向", font_size=SUBHEAD_SIZE, font_color=BLACK, bold=True) discussion_points = [ "如何处理污染效应 (GIGO)?-> 更高阶 FEM (p-refinement) + 显式 kh 特征 + 更深的初始网格", "如何提升多步细化效率?-> 递减的 N_per_wavelength 目标 + 更细粒度切分 (1-to-2) + 课程学习", "如何拓展到更复杂场景?-> 多散射体、三维 Helmholtz、Maxwell 方程组、时域问题", ] for i, point in enumerate(discussion_points): add_textbox(slide, Inches(0.85), Inches(6.05 + i * 0.35), Inches(0.35), Inches(0.3), text=">", font_size=Pt(12), font_color=ACCENT_TEAL, bold=True) add_textbox(slide, Inches(1.2), Inches(6.05 + i * 0.35), Inches(11.2), Inches(0.3), text=point, font_size=Pt(12), font_color=BODY_GRAY) add_textbox(slide, Inches(8.5), Inches(7.0), Inches(4.5), Inches(0.4), text="谢谢!欢迎讨论。", font_size=Pt(18), font_color=ACCENT_BLUE, bold=True, alignment=PP_ALIGN.RIGHT) add_slide_number(slide, 15) # Save output_path = "/public/home/dxw/Codes/afem/output/final_presentation_cn.pptx" prs.save(output_path) print(f"PPTX saved to {output_path}") print(f"Slides: {len(prs.slides)}")