262 lines
12 KiB
Python
262 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Generate work-summary PPT styled like 组会/yh -散射边界条件.pptx
|
||
Run: python generate_summary_ppt.py
|
||
"""
|
||
from pathlib import Path
|
||
import subprocess
|
||
import sys
|
||
|
||
from pptx import Presentation
|
||
from pptx.util import Pt
|
||
from pptx.enum.text import PP_ALIGN
|
||
|
||
ROOT = Path(__file__).resolve().parent
|
||
ASSETS = ROOT / "ppt_assets"
|
||
REF_PPT = Path(r"e:\组会\yh -散射边界条件.pptx")
|
||
REF_IMGS = Path(r"c:\Users\Administrator\Desktop\ppt_assets_ref")
|
||
OUT_PATH = ROOT / "OpticsFEM_2D_to_3D_Summary.pptx"
|
||
LAYOUT_TITLE_CONTENT = 11 # 5_标题和内容 — same as reference deck
|
||
|
||
|
||
def delete_all_slides(prs):
|
||
while len(prs.slides) > 0:
|
||
sld_id = prs.slides._sldIdLst[0]
|
||
r_id = sld_id.rId
|
||
prs.part.drop_rel(r_id)
|
||
del prs.slides._sldIdLst[0]
|
||
|
||
|
||
def set_title(slide, text):
|
||
if slide.shapes.title:
|
||
slide.shapes.title.text = text
|
||
|
||
|
||
def add_textbox(slide, left, top, width, height, text, size=14, bold=False):
|
||
box = slide.shapes.add_textbox(left, top, width, height)
|
||
tf = box.text_frame
|
||
tf.word_wrap = True
|
||
p = tf.paragraphs[0]
|
||
p.text = text
|
||
p.font.size = Pt(size)
|
||
p.font.bold = bold
|
||
return box
|
||
|
||
|
||
def add_bullets(slide, left, top, width, height, items, size=13):
|
||
box = slide.shapes.add_textbox(left, top, width, height)
|
||
tf = box.text_frame
|
||
tf.word_wrap = True
|
||
for i, item in enumerate(items):
|
||
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
|
||
p.text = item
|
||
p.font.size = Pt(size)
|
||
p.space_after = Pt(6)
|
||
|
||
|
||
def add_picture(slide, path, left, top, width=None, height=None):
|
||
path = Path(path)
|
||
if not path.exists():
|
||
print(f" [warn] missing image: {path}")
|
||
return None
|
||
if width and height:
|
||
return slide.shapes.add_picture(str(path), left, top, width=width, height=height)
|
||
if width:
|
||
return slide.shapes.add_picture(str(path), left, top, width=width)
|
||
if height:
|
||
return slide.shapes.add_picture(str(path), left, top, height=height)
|
||
return slide.shapes.add_picture(str(path), left, top)
|
||
|
||
|
||
def ensure_assets():
|
||
ASSETS.mkdir(parents=True, exist_ok=True)
|
||
# charts
|
||
subprocess.run([sys.executable, str(ASSETS / "make_charts.py")], check=True, cwd=ROOT)
|
||
# normE slices if not present
|
||
mesh = ROOT / "scat3D/Release/SBCmesh.dat"
|
||
outdir = ROOT / "scat3D/Release/OutFile"
|
||
plot_script = ROOT / "tools/plot_normE_slice.py"
|
||
if mesh.exists() and (outdir / "normE").exists() and plot_script.exists():
|
||
for plane, coord, name in [("z", 0.5, "normE_z050.png"), ("z", 0, "normE_z000.png"), ("y", 0.5, "normE_y050.png")]:
|
||
out = ASSETS / name
|
||
if not out.exists():
|
||
subprocess.run([
|
||
sys.executable, str(plot_script),
|
||
"--mesh", str(mesh), "--outdir", str(outdir),
|
||
"--plane", plane, "--coord", str(coord),
|
||
"--output", str(out),
|
||
], cwd=ROOT, check=False)
|
||
|
||
|
||
def build(prs):
|
||
delete_all_slides(prs)
|
||
# emu helpers — reference deck is 13.33" wide
|
||
from pptx.util import Inches
|
||
W = prs.slide_width
|
||
|
||
# --- 1 封面(仿参考第1页布局)---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "组会汇报")
|
||
add_textbox(
|
||
slide, Inches(0), Inches(2.9), W, Inches(1.6),
|
||
"OpticsFEM 二维→三维扩展工作总结\n"
|
||
"一阶 Nedelec 边元 · 散射边界条件与本征频率问题\n"
|
||
"汇报人: 时间:2026年6月",
|
||
size=22, bold=False,
|
||
)
|
||
add_picture(slide, REF_IMGS / "image5.png", Inches(7.2), Inches(1.5), width=Inches(5.5))
|
||
|
||
# --- 2 背景与目标 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "项目背景与目标")
|
||
add_bullets(slide, Inches(0.35), Inches(1.35), Inches(5.8), Inches(5.5), [
|
||
"原有 OpticsFEM:2D 三角网格 + 一阶边元(FemType 0–3)",
|
||
"目标:扩展至 3D 四面体网格,对标 MATLAB 参考代码",
|
||
"算例:SBC 立方体模型,εr=1.0/1.5,λ₀=0.8 m",
|
||
"散射:assembly_equ + out + inc → Ax=b",
|
||
"本征:assembly_equ → A·x=λ·B·x",
|
||
"交付:C++ 散射全流程 + 本征矩阵与 MATLAB 对齐",
|
||
])
|
||
add_picture(slide, REF_IMGS / "image6.png", Inches(6.3), Inches(1.6), width=Inches(6.5))
|
||
|
||
# --- 3 SBC 物理(仿参考“散射边界条件模型”)---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "散射边界条件(SBC)物理模型")
|
||
add_textbox(slide, Inches(0.35), Inches(1.8), Inches(5.5), Inches(4.5),
|
||
"散射边界条件(Scattering Boundary Condition)\n\n"
|
||
"• 出射边界:模拟只有沿外法向传播的出射波\n"
|
||
"• 入射激励:平面波 Einc=(1,0,0)\n"
|
||
"• 体积分:curl-curl − k₀²ε\n"
|
||
"• C++ 实现:Assemble_SBC + assembly_inc/out\n"
|
||
"• 与 MATLAB main.m 流程一致", size=14)
|
||
add_picture(slide, REF_IMGS / "image8.png", Inches(1.5), Inches(4.8), width=Inches(3.5))
|
||
add_picture(slide, REF_IMGS / "image17.png", Inches(6.0), Inches(1.5), width=Inches(6.8))
|
||
|
||
# --- 4 2D vs 3D ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "二维与三维扩展对照")
|
||
add_picture(slide, ASSETS / "status_table.png", Inches(0.4), Inches(1.4), width=Inches(12.0))
|
||
add_textbox(slide, Inches(0.5), Inches(5.0), Inches(12), Inches(1.2),
|
||
"新增 FemType 4(3D 散射)与 5(3D 本征);共用 Mesh_3D + SBCmesh.dat",
|
||
size=13)
|
||
|
||
# --- 5 软件架构 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "软件整体架构")
|
||
add_picture(slide, ASSETS / "workflow.png", Inches(0.5), Inches(1.35), width=Inches(12.2))
|
||
add_bullets(slide, Inches(0.5), Inches(5.2), Inches(12), Inches(1.5), [
|
||
"模块:mesh · material · phy · kernel · solver · post · Interface",
|
||
"构建:scat3D/(散射)与 eigen3D/(本征)独立 CMake 目录",
|
||
], size=12)
|
||
|
||
# --- 6 散射方程与流程(仿参考“方程”页)---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "三维散射问题(FemType = 4)")
|
||
add_picture(slide, REF_IMGS / "image3.png", Inches(0.4), Inches(1.5), width=Inches(5.5))
|
||
add_picture(slide, REF_IMGS / "image4.png", Inches(6.2), Inches(1.5), width=Inches(6.5))
|
||
add_textbox(slide, Inches(0.5), Inches(5.5), Inches(12), Inches(1.2),
|
||
"方程 A·x=b | 配置 sbc3d.json | 返回码 4 | complexsolver SparseLU",
|
||
size=13, bold=True)
|
||
|
||
# --- 7 散射数值结果 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "散射仿真结果 |E| 分布")
|
||
add_picture(slide, ASSETS / "normE_z050.png", Inches(0.4), Inches(1.35), width=Inches(6.0))
|
||
add_picture(slide, ASSETS / "metrics.png", Inches(6.6), Inches(1.35), width=Inches(6.2))
|
||
add_textbox(slide, Inches(0.5), Inches(5.6), Inches(12), Inches(0.8),
|
||
"z=0.5 m 切面 | |E| min=0.72 max=1.36 V/m | 残差 |r|≈6.2×10⁻¹³",
|
||
size=13)
|
||
|
||
# --- 8 多切面对比 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "散射场多切面 |E|(C++ Post 输出)")
|
||
add_picture(slide, ASSETS / "normE_z000.png", Inches(0.3), Inches(1.4), width=Inches(4.0))
|
||
add_picture(slide, ASSETS / "normE_z050.png", Inches(4.5), Inches(1.4), width=Inches(4.0))
|
||
add_picture(slide, ASSETS / "normE_y050.png", Inches(8.7), Inches(1.4), width=Inches(4.0))
|
||
add_textbox(slide, Inches(0.3), Inches(1.2), Inches(4), Inches(0.3), "z = 0", size=11, bold=True)
|
||
add_textbox(slide, Inches(4.5), Inches(1.2), Inches(4), Inches(0.3), "z = 0.5 m", size=11, bold=True)
|
||
add_textbox(slide, Inches(8.7), Inches(1.2), Inches(4), Inches(0.3), "y = 0.5 m", size=11, bold=True)
|
||
add_textbox(slide, Inches(0.5), Inches(5.8), Inches(12), Inches(0.6),
|
||
"可与 COMSOL 切面对比:compare/compare_normE_zslice.m", size=12)
|
||
|
||
# --- 9 本征问题 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "三维本征频率问题(FemType = 5)")
|
||
add_bullets(slide, Inches(0.35), Inches(1.4), Inches(5.5), Inches(5.5), [
|
||
"广义本征:A·x = λ·B·x",
|
||
"A = curl-curl(刚度),B = ε 质量矩阵",
|
||
"边界:全 PMC(与 MATLAB 3D一阶本征问题2 一致)",
|
||
"C++:Assemble + Test_OutputMatrix",
|
||
"导出:Ai/Aj/Av、Bi/Bj/Bv(COO,0-based)",
|
||
"求解:MATLAB eigs(A,B) + get_ele 绘图",
|
||
"待接:C++ Run/Post",
|
||
])
|
||
add_picture(slide, REF_IMGS / "image27.png", Inches(6.0), Inches(1.5), width=Inches(6.8))
|
||
|
||
# --- 10 本征矩阵验证 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "本征矩阵组装验证(C++ vs MATLAB)")
|
||
add_picture(slide, ASSETS / "metrics.png", Inches(0.4), Inches(1.4), width=Inches(6.5))
|
||
add_bullets(slide, Inches(6.8), Inches(1.5), Inches(6), Inches(5), [
|
||
"Av.txt 约 1.29×10⁶ 行非零元",
|
||
"C++ OutFile 与 MATLAB OutFile 逐行一致",
|
||
"Bi/Bj/Bv 约 43632 非零(质量矩阵)",
|
||
"FemType=5 返回码:5",
|
||
"下一步:接入 Solver_EigenFreq",
|
||
], size=14)
|
||
|
||
# --- 11 求解器 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "求解器体系")
|
||
add_picture(slide, REF_IMGS / "image12.png", Inches(0.4), Inches(1.5), width=Inches(5.5))
|
||
add_bullets(slide, Inches(6.2), Inches(1.5), Inches(6.5), Inches(5), [
|
||
"solver/:接口层(编进 OpticsFEM.exe)",
|
||
"散射:interface.cpp → complexsolver.exe",
|
||
"complexsolver:Eigen SparseLU / BiCGSTAB",
|
||
"路径:Release/complex/complexsolver.exe",
|
||
"本征 3D:当前 MATLAB eigs 验证",
|
||
"优势:无需 PETSc,CMake 一键编译",
|
||
], size=13)
|
||
|
||
# --- 12 总结 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "总结与下一步")
|
||
add_bullets(slide, Inches(0.4), Inches(1.4), Inches(6), Inches(5.5), [
|
||
"【已完成】2D→3D 一阶 Nedelec 框架扩展",
|
||
"【已完成】3D SBC 散射端到端可运行、数值可靠",
|
||
"【已完成】3D 本征 A/B 组装与 MATLAB 严格对齐",
|
||
"【待完成】本征 C++ 求解与后处理接入",
|
||
"【待完成】PBC、二阶基等扩展(MATLAB 已有)",
|
||
"【待完成】Test_Main 支持命令行指定 JSON",
|
||
], size=14)
|
||
add_picture(slide, ASSETS / "normE_z050.png", Inches(6.5), Inches(1.5), width=Inches(6.0))
|
||
|
||
# --- 13 致谢 ---
|
||
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
|
||
set_title(slide, "下一步工作")
|
||
add_textbox(
|
||
slide, Inches(1.5), Inches(2.5), Inches(10), Inches(2.5),
|
||
"1. COMSOL 数值对比出图(compare 脚本)\n"
|
||
"2. FemType=5 接入 Run/Post,完成 C++ 本征闭环\n"
|
||
"3. 扩展 PBC / 二阶基边界条件\n"
|
||
"4. 算例自动化与文档完善\n\n"
|
||
"谢谢!",
|
||
size=22, bold=False,
|
||
)
|
||
|
||
|
||
def main():
|
||
print("Preparing assets...")
|
||
ensure_assets()
|
||
if not REF_PPT.exists():
|
||
raise FileNotFoundError(f"Reference PPT not found: {REF_PPT}")
|
||
print(f"Loading template: {REF_PPT}")
|
||
prs = Presentation(str(REF_PPT))
|
||
build(prs)
|
||
prs.save(str(OUT_PATH))
|
||
print(f"Saved: {OUT_PATH}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|