基于 workbuddy.md / claude.md / codex.md 三份原始分析 及 claude_v1.md / codex_v1.md / workbuddy_v1.md 三份综合 版本,输出最终优化方案。 核心改进: - 三工具角色定位框架(QA/Perf/Architect) - Bug 三级分类(真 Bug / 已修复 / 疑似误报) - 三阶段方案(修正确性→做性能→做治理) - 自我评价与修正(v1→v2 改进表) - 10 场景工具选择速查表
20 KiB
Dynamics 项目优化方案综合报告
整合日期:2026-06-12
来源文档:claude.md(Claude Sonnet 4.6 分析)、workbuddy.md(WorkBuddy 分析)
项目路径:D:\Share\Data\aliyun-gitea\dynamics
第一部分:两份分析报告的评价与对比
1.1 WorkBuddy 分析报告评价(workbuddy.md)
优势
① 架构视野宏观、分层清晰
WorkBuddy 从整体软件工程角度出发,明确指出 compute.py 的职责混乱问题(1618 行同时承担物理引擎、文件 I/O、参数加载、外部引擎管理五种职责),并给出了完整的模块拆分方案:
compute/
├── core.py # 物理引擎
├── io.py # 文件 I/O
├── params.py # 参数加载
├── runner.py # 运行管理
├── engine_helper.py # 外部引擎
└── main.py # 主入口
这是一个系统性重构方向,有助于长期维护。
② 关注工程规范与测试
WorkBuddy 专门开辟了"测试与质量"一节,强调了缺少单元测试的风险,并给出了类型标注的示例代码——这两点是 claude.md 未涉及的。对于一个教学/研究用项目,有测试才能安全重构。
③ 配置管理建议具体
提出了将 ball_color_r/g/b 三个键合并为 ball_color: [r,g,b] 数组的配置统一方案,以及6个案例 input.txt 格式不统一的问题(如 save_trajectory、camera_* 字段缺失),并给出了统一模板,实操性强。
④ 引擎一致性梳理全面
清晰列出了 C/C++/Fortran 三引擎在 save_trajectory、box_a 默认值等参数上的差异表格,对用户切换引擎有直接指导价值。
⑤ 区分"已完成"与"待做"
文档中标注了"✅ 已修复"、"✅ 已完成"条目,帮助读者快速定位当前状态,避免重复分析。
劣势
① 缺少具体 Bug 定位
WorkBuddy 的分析主要停留在架构和规范层面,对于已经导致运行崩溃的 Bug(如 plot_wave.py 格式不匹配、run_simulation 中 config 未定义)没有识别和标注。用户按此文档操作时,可能先花时间做重构,但基础功能仍然崩溃。
② 性能优化建议较抽象
对 Python 引擎性能仅建议"使用 Numba JIT"或"numpy.vectorize",缺少具体的向量化代码示例。文档注明"Python vs C 慢约 6-8 倍",但未分析哪个函数是瓶颈(实际上是弹簧力的 Python for 循环)。
③ 部分建议颗粒度不足
"消除全局变量"建议封装为 SimulationState 数据类,但未说明:封装后接口如何调整、外部引擎的 param.json 协议是否需要同步更新、draw.py 的全局变量是否属于同一重构范围。
④ 优先级排列有待商榷
将"全局变量封装"列为 P0(与 Fortran 引擎修复同级),但封装全局变量是一项重构工作(3-4h),而 Fortran 引擎崩溃是已存在的功能阻断性问题,两者紧迫性不同。
1.2 Claude 分析报告评价(claude.md)
优势
① Bug 定位精准,附有复现条件
每个 Bug 都说明了触发条件("只要使用 engine: python"、"step_plot_wave: 1 时"),以及为什么当前没有爆发("当前所有案例恰好设置 step_plot: 0")。这对于开发者复现和验证问题极有价值。
② 代码级修复方案完整
每个 Bug 都给出了完整可运行的修复代码,包括需要修改的行号、修改前后的对比。特别是 B3(plot_wave.py 格式不匹配)不仅指出问题,还解释了为何修复较复杂(atom_masses 等物理量在新格式中缺失,需要同步扩展 header 字段),给出了分层的修复路径(快速修复 vs 彻底修复)。
③ 弹簧力向量化方案具体可行
给出了完整的 NumPy 向量化代码,包括处理重复索引的 np.add.at 用法,以及链状体系可进一步优化为直接索引的提示。预期加速 5-15 倍的估算也有根据(119 键 × 100,000 步的量级分析)。
④ 引擎差异的量化说明
指出了 engines/cpp/param.json 中 save_trajectory 默认值为 1(与 C 引擎的 0 不一致),并说明了实际影响(切换引擎时可能意外生成大文件)。
劣势
① 覆盖面刻意与 workbuddy.md 错开,造成视野盲区
claude.md 开篇声明"不与 workbuddy.md 重复",因此主动跳过了架构、测试、配置管理等方向。但这导致文档读者如果只看 claude.md,会误以为架构是没问题的。两份文档需要结合阅读,割裂感较强。
② 部分建议依赖全局变量方案
claude.md 提出的 Bug B1 修复方案(将 camera_distance 等加入全局变量)是在全局变量模式下的补丁式修复,与 workbuddy.md 建议的"封装全局变量为类"方向相反。若未来执行 workbuddy.md 的重构,B1 的修复需要再次调整。
③ 某些结论尚未验证
文档末尾注明"基于代码静态分析,未实际运行测试验证"。例如 B3(plot_wave.py 格式不匹配)的结论基于函数调用链分析,如果 load_text_data 内部有容错逻辑,实际情况可能不同。
④ 缺乏对 draw.py 的深入分析
draw.py 是用户交互最频繁的模块(3D 动画播放),但 claude.md 仅指出了一处裸 except(B5),未分析动画帧率优化、大原子数渲染性能等用户体验层面的问题。
1.3 两份报告对比总结
| 维度 | WorkBuddy | Claude |
|---|---|---|
| Bug 识别 | ❌ 未发现运行时 Bug | ✅ 识别 5 个确认 Bug,附触发条件 |
| 性能优化 | 🟡 方向正确但缺代码 | ✅ 完整向量化代码示例 |
| 架构设计 | ✅ 完整模块拆分方案 | ❌ 刻意回避,未覆盖 |
| 测试建议 | ✅ pytest 方案具体 | ❌ 未涉及 |
| 配置管理 | ✅ 6 案例统一模板 | ❌ 未涉及 |
| 引擎一致性 | ✅ 参数差异表格 | ✅ 补充了势能归属约定 |
| 修复代码 | 🟡 仅有架构示意 | ✅ 逐条给出可运行代码 |
| 优先级合理性 | 🟡 P0 安排有误 | ✅ 按紧迫性分层清晰 |
| 可独立阅读 | ✅ 自成体系 | ❌ 依赖读者已读 workbuddy.md |
| 当前状态标注 | ✅ 已完成项有标注 | ❌ 未区分 |
结论:WorkBuddy 擅长系统性架构分析和工程规范,Claude 擅长 Bug 精确定位和具体修复代码。两份报告互补,单独使用任何一份都存在明显盲区。
第二部分:综合优化方案
原则
基于以上分析,综合方案遵循以下原则:
- 先止血再手术:运行时 Bug 优先于架构重构,不能让教学演示功能处于崩溃状态
- 快速收益优先:同等重要性时,工作量小的先做
- 向量化先于重构:Python 引擎提速不依赖架构改动,可独立进行
- 渐进式重构:全局变量封装等重构分阶段进行,避免一次性大改引入新 Bug
阶段一:紧急修复(总工作量约 4 小时)
目标:恢复所有已有功能到正常可用状态
1. 修复 plot_wave.py 格式不匹配(B3)— 1.5h
根本原因:display.txt 格式从 JSON 迁移到新文本格式时,plot_wave.py 未同步更新。
修复步骤:
① 修改加载函数(plot_wave.py:22-27):
def load_disp_data(output_dir):
disp_path = os.path.join(output_dir, "display.txt")
if not os.path.exists(disp_path):
raise FileNotFoundError(f"找不到 {disp_path}")
return compute.load_display_txt(disp_path) # 改为新格式加载
② 在 save_display_txt(compute.py:217)的 header 中补充 atom_masses 字段:
# 在 header_fields 中添加
"atom_masses": ",".join(str(m) for m in ATOM_MASSES),
③ 修改 plot_wave.py 中的字段访问:
# 旧 → 新
data["n_frames"] → disp_data["frames_x"].shape[0]
data["disp_all_x"] → disp_data["frames_x"]
data["atom_masses"] → np.array([float(x) for x in h.get("atom_masses","").split(",") if x])
data["atom_ids"] → disp_data["atom_ids"]
data["bond_pairs"] → [] # display.txt 不含成键信息,能量计算中弹性势能项跳过
验证:运行 case05(step_plot_wave: 1),确认生成 wave_animation.gif。
2. 修复 run_simulation() 中的 config 未定义(B1)— 15min
在 compute.py 模块顶部(约第 68 行,紧随 camera_keyframes_raw)添加全局变量:
camera_keyframes_raw = ""
camera_distance = 40.0 # 新增
camera_elevation = 0 # 新增
camera_azimuth = 0 # 新增
在 run_from_config(约第 756 行,use_marker 赋值附近)添加:
use_marker = int(config.get("use_marker", 0))
camera_distance = float(config.get("camera_distance", 40.0)) # 新增
camera_elevation = float(config.get("camera_elevation", 0)) # 新增
camera_azimuth = float(config.get("camera_azimuth", 0)) # 新增
在 run_from_config 的 global 声明行追加这三个变量:
global use_marker, camera_keyframes_raw, camera_distance, camera_elevation, camera_azimuth
在 run_simulation(第 1548-1550 行)改为读全局变量:
"camera_distance": str(camera_distance),
"camera_elevation": str(camera_elevation),
"camera_azimuth": str(camera_azimuth),
验证:用 engine: python 运行 case01,确认生成正确的 display.txt。
3. 修复 dynamics.py 绘图块死代码(B2)— 30min
step_plot 功能依赖完整轨迹数据,但当前架构中 run_from_config 不再在主流程中保留这些数据。
快速修复:添加 "功能暂不可用" 保护,避免用户误开后崩溃:
# dynamics.py 约 322 行
if not no_plot and config.get("step_plot", 1):
print("[run] 注意:step_plot 绘图功能需要 save_trajectory=1 时的完整轨迹,")
print("[run] 当前版本暂未支持,已跳过。如需绘图请联系开发者。")
长期修复(阶段三再做):从 display.txt 读取抽帧数据重建绘图逻辑。
4. 修复 Fortran 引擎输出格式(E3)— 2h
Fortran 引擎总是输出 JSON 格式 trajectory.txt,与 Python 侧期待的 display.txt 不兼容,
使用 engine: fortran 时必然崩溃。
参照 C 引擎的 write_display_txt 函数(engines/c/main.c)在 Fortran 中实现:
- 读取
param.json中的save_trajectory参数 - 新增
write_display_txt_f90子程序,按 NSTEP 抽帧写文本格式 - 根据
save_trajectory决定是否额外写trajectory.txt
同时:将 engines/cpp/param.json 中的 "save_trajectory": 1 改为 0,与 C 保持一致。
5. 其他 1-5 分钟的小修复
# B4: examples/case06/run_dynamics.py:34
description="运行 Dynamics 示例案例 case06" # 改 case01 → case06
# B5: draw.py:97
except (ValueError, AttributeError): # 改裸 except
# Q1: dynamics.py 顶部
from compute import _load_camera_motion as _load_camera_kf # 删除重复实现
# E2: engines/cpp/param.json
"save_trajectory": 0 # 统一默认值
阶段二:性能优化(总工作量约 3 小时)
目标:Python 引擎提速 5-10 倍,外部引擎减少启动开销
6. 弹簧力向量化(P1)— 1h
将 compute.py:1253-1277 的 Python for 循环替换为 NumPy 向量化版本(见 claude.md §P1 完整代码)。
链状体系(case01-06)的进一步优化:
由于链状体系每根键的两端没有重复(原子 i 和 j 各只出现一次),可以用更快的直接索引替代 np.add.at:
# 比 np.add.at 快约 3 倍(无冲突索引时)
fx[i_arr] += fx_bond
fx[j_arr] -= fx_bond
fy[i_arr] += fy_bond
fy[j_arr] -= fy_bond
fz[i_arr] += fz_bond
fz[j_arr] -= fz_bond
对于有分叉键的复杂体系仍需用 np.add.at,可根据 BOND_PAIRS 检测是否有重复端点自动选择路径。
7. apply_fixed_constraints 预计算掩码(P2)— 30min
将 compute.py:1408-1418 的 column_stack 方案替换为预计算 bool 掩码,见 claude.md §P2 完整代码。
对 case06(120 原子,x/y 全固定)每步节省 2 次 (120, 3) 数组分配。
8. 外部引擎校准缓存(E4)— 1h
在 compute.py:924 校准逻辑前添加缓存命中检查:
import hashlib, json as _json
def _calib_cache_key(engine, n_atoms, method, dt):
s = f"{engine}:{n_atoms}:{method}:{dt}"
return hashlib.md5(s.encode()).hexdigest()[:8]
_calib_cache_path = os.path.join(script_dir, "engines", engine, "_calib_cache.json")
_calib_key = _calib_cache_key(engine, len(ATOM_IDS), config.get("method","leapfrog"), float(config["DT"]))
_cached = {}
if os.path.exists(_calib_cache_path):
with open(_calib_cache_path) as _cf:
_cached = _json.load(_cf)
if _cached.get("key") == _calib_key and time.time() - _cached.get("ts", 0) < 86400:
# 缓存命中(1天内有效),跳过校准
_step_time = _cached["step_time"]
_overhead = _cached["overhead"]
else:
# 执行校准(现有逻辑)...
# 校准完成后写缓存
with open(_calib_cache_path, "w") as _cf:
_json.dump({"key": _calib_key, "ts": time.time(),
"step_time": _step_time, "overhead": _overhead}, _cf)
预期收益:case06 第二次运行节省约 10 秒(校准 10,000 步)。
9. 其他微优化(P3/P4)— 30min
# P3: run_simulation 中删除 frame_indices 列表
# 删除: frame_indices = [] 和 frame_indices.append(step)
# 替换: n_frames_actual = record_steps // NSTEP (直接计算)
# P4: apply_driving_force 中删除 t_vec 数组创建
t_vec = np.array([t, t, t], dtype=np.float64) # 删除此行
pos_drive = d["amp"] * np.cos(2.0 * np.pi * d["freq"] * t + d["phi"]) # 直接用标量 t
阶段三:架构重构(总工作量约 15-20 小时)
目标:提升长期可维护性,支持并发模拟、单元测试
重要说明:此阶段是 workbuddy.md 建议的核心,以下是对其建议的细化和补充。
建议在阶段一、二完成并通过全案例验证后再开始。
10. 全局变量封装为 SimulationState(workbuddy.md §3.1)— 3-4h
封装后,阶段一中 B1 的补丁修复(新增三个全局变量)可以一并纳入 SimulationState,不需要额外保留。
封装时注意:外部引擎路径(run_engine)通过 param.json 传参,不受全局变量影响,可独立进行。
11. compute.py 模块拆分(workbuddy.md §1.1)— 4-6h
拆分时建议先拆 io.py(文件读写),因为它与物理算法完全解耦,风险最低。
拆分顺序建议:io.py → params.py → core.py → runner.py。
补充:同步删除已废弃的 load_parameters() 函数(102 行)和已被 dynamics.py 取代的 compute.main()(25 行),见 claude.md §Q2。
12. 添加单元测试(workbuddy.md §5.1)— 3-5h
优先覆盖以下三类:
- 物理算法正确性:以简谐振子解析解验证 leapfrog、midpoint 方法(参照
tools/NumericalMethods.py已有实现) - 文件 I/O 往返一致性:
save_display_txt → load_display_txt数值不变 - 参数验证边界:质量为 0、键长为负、NT 为 0 等异常输入
13. 案例 input.txt 格式统一(workbuddy.md §4.1)— 1-2h
将 case01-05 的 input.txt 补充缺失字段(save_trajectory、camera_*、move_camera),
统一 step_sample: 0(新版引擎已内置抽帧),清理旧格式注释。
14. dynamics.py 绘图功能完整重建(B2 长期修复)— 2h
从 display.txt 的抽帧数据重建绘图逻辑(替代依赖完整轨迹的旧实现):
disp_data = compute.load_display_txt(disp_path)
frames_x = disp_data["frames_x"] # (n_frames, n_atoms)
# 对每帧计算能量,绘制时序图
能量计算需要质量等物理量,依赖阶段一 B3 修复中在 header 补充的 atom_masses 字段。
三个引擎的综合评价
C 引擎(engines/c/main.c)
| 维度 | 评价 |
|---|---|
| 功能完整性 | ✅ 最完整,支持 display.txt 直接输出,save_trajectory 控制 |
| 性能 | ✅ 基准引擎,比 Python 快 6-8 倍 |
| 一致性 | ✅ save_trajectory 默认值为 0,与 Python 行为一致 |
| 代码质量 | 🟡 JSON 解析为自行实现(非 cJSON 库),维护成本高 |
| 建议 | 作为参考实现,其他引擎向 C 引擎看齐 |
C++ 引擎(engines/cpp/main.cpp)
| 维度 | 评价 |
|---|---|
| 功能完整性 | ✅ 功能与 C 引擎相当 |
| 性能 | 🟡 编译产物 3.3 MB(远大于 C 的 504 KB),有优化空间 |
| 一致性 | ⚠️ save_trajectory 默认值为 1(与 C 不一致),已在阶段一修复 |
| 代码质量 | 🟡 物理算法与 C 引擎重复,应提取到公共头文件 |
| 建议 | 统一 param.json 默认值;考虑与 C 引擎共用物理算法头文件 |
Fortran 引擎(engines/fortran/main.f90)
| 维度 | 评价 |
|---|---|
| 功能完整性 | ❌ 不支持 display.txt 直接输出,使用时必然崩溃 |
| 性能 | ✅ 编译性能与 C 相当,适合大规模计算 |
| 一致性 | ❌ 输出 JSON 格式 trajectory.txt,与其他引擎行为完全不同 |
| 代码质量 | 🟡 代码结构清晰,但与 C/C++ 引擎物理算法重复 |
| 建议 | 阶段一 E3 修复为最高优先级;修复后可成为高性能替代引擎 |
综合优先级总表
| 阶段 | 编号 | 问题 | 来源 | 优先级 | 预估工时 |
|---|---|---|---|---|---|
| 一 | B3 | plot_wave.py 格式不匹配(波形动画失效) |
Claude | 🔴 P0 | 1.5h |
| 一 | E3 | Fortran 引擎不支持 display.txt | 两者 | 🔴 P0 | 2h |
| 一 | B1 | run_simulation 内 config 未定义 |
Claude | 🔴 P0 | 15min |
| 一 | B2 | dynamics.py 绘图块死代码 |
Claude | 🔴 P0 | 30min |
| 一 | B4/B5 | 小错误修复(case06 文字、裸 except 等) | Claude | 🟡 P1 | 15min |
| 一 | Q1/E2 | 重复函数去除、C++ 默认值统一 | Claude | 🟡 P1 | 20min |
| 二 | P1 | 弹簧力向量化(Python 引擎 5-15x) | Claude | 🟢 P1 | 1h |
| 二 | E4 | 外部引擎校准缓存 | 两者 | 🟢 P1 | 1h |
| 二 | P2-P4 | apply_fixed_constraints 等微优化 | Claude | 🟢 P2 | 1h |
| 三 | 架构 | 全局变量封装为 SimulationState | WorkBuddy | 🔵 P2 | 3-4h |
| 三 | 架构 | compute.py 模块拆分 | WorkBuddy | 🔵 P2 | 4-6h |
| 三 | 测试 | 添加 pytest 单元测试 | WorkBuddy | 🔵 P2 | 3-5h |
| 三 | 配置 | 6 案例 input.txt 格式统一 | WorkBuddy | 🔵 P3 | 1-2h |
| 三 | 绘图 | dynamics.py 绘图功能重建 | Claude | 🔵 P3 | 2h |
| 三 | 引擎 | C/C++/Fortran 共用物理算法头文件 | WorkBuddy | 🔵 P3 | 2-4h |
附录:快速执行清单
今天可以完成(总计约 30 分钟的小修复)
[ ] examples/case06/run_dynamics.py:34 "case01" → "case06"
[ ] draw.py:97 裸 except → except (ValueError, AttributeError):
[ ] dynamics.py 删除 _load_camera_kf(),改为从 compute 导入
[ ] engines/cpp/param.json "save_trajectory": 1 → 0
[ ] compute.py:1548-1550 config.get → 全局变量(B1 修复)
本周可以完成(核心功能恢复)
[ ] plot_wave.py 格式适配(B3)
[ ] dynamics.py 绘图块保护(B2 临时修复)
[ ] Fortran 引擎 display.txt 支持(E3)
[ ] 弹簧力向量化(P1)
[ ] 引擎校准缓存(E4)
下阶段规划(架构改善)
[ ] 全局变量 → SimulationState 类
[ ] compute.py 拆分为子模块
[ ] pytest 单元测试套件
[ ] 案例 input.txt 格式统一
本文档综合 Claude Sonnet 4.6(claude.md)与 WorkBuddy(workbuddy.md)的分析报告生成。
所有代码改动建议均基于静态分析,实施前请先在独立分支验证。