# Dynamics 项目优化建议 > 分析日期:2026-06-12 > 版本:main (43 commits) --- ## 1. 架构优化 ### 1.1 compute.py 拆分(高优先级) **现状**:`compute.py` 1618 行,混合了以下职责: - 物理引擎(数值积分方法 ~10 个函数) - 文件 I/O(JSON/text 读写) - 外部引擎管理(subprocess 调用) - 参数加载与解析 - 主入口 main() **建议**:按职责拆分为独立模块 ``` compute/ ├── __init__.py # 导出公共 API ├── core.py # 物理引擎: 数值积分方法, 力的计算 (~200 行) ├── io.py # 文件 I/O: save/load display/trajectory (~300 行) ├── params.py # 参数加载: load_coord_file, load_bond 等 (~150 行) ├── runner.py # 运行管理: run_simulation, run_engine, run_from_config (~200 行) ├── engine_helper.py # 外部引擎: 校准, subprocess, 进度监控 (~150 行) └── main.py # 主入口: main(), if __name__ == "__main__" (~30 行) ``` **收益**:可单独测试物理算法、文件读写、引擎调用 ### 1.2 外部引擎代码重构(中优先级) **现状**:C/C++/Fortran 引擎各自实现了一套: - JSON 读取/写入(3 套独立实现) - 物理算法(复制粘贴) - display.txt / trajectory.txt 输出 **建议**: - C 和 C++ 引擎共用头文件/函数库 - 物理算法提取为公共库(`engines/common/`) - Fortran 更新支持 `save_trajectory` 兼容 --- ## 2. 性能优化 ### 2.1 外部引擎校准耗时(高) **现状**:`run_engine()` 每次执行前先跑 1000 步校准测速(`_calib_nt = min(1000, max(100, total_steps // 10))`),对短时运行(~1000 步)校准占比高达 50%。 **建议**: - 缓存校准结果:按 `(engine, NT, DT, NSTEP, n_atoms)` 哈希缓存 - 或对短运行 (<5000 步) 跳过校准 - 或只在校准目录为空时执行 ```python # 建议改动 _calib_cache = os.path.join(script_dir, "engines", engine, "_calib_cache.json") if os.path.exists(_calib_cache): with open(_calib_cache) as f: _calib_data = json.load(f) if _calib_data.get("key") == _calib_key: _step_time = _calib_data["step_time"] _overhead = _calib_data["overhead"] skip_calibration() ``` ### 2.2 display.txt 读取加速(已完成) **现状**:已从逐行 `split()+float()` 改为 `np.genfromtxt()` 批量解析,200帧×120原子 加载仅 0.087s(测试已通过)。 ### 2.3 Python 引擎性能(低) **现状**:Python 引擎比 C 引擎慢约 6-8 倍(43s vs 7s for 10 万步)。对 50 万步以上的长时间模拟不适用。 **建议**: - 使用 Numba JIT 加速力的计算 - 或使用 `numpy.vectorize` 替代 Python 循环(力的计算中已部分使用 numpy 数组操作) - 文档中注明 Python 引擎仅适用于小规模测试 ### 2.4 校准数据的进度条(已修复) 校准后进度条使用实时帧号更新,不再使用时间估算,避免了 `0% → 100%` 跳变。但注意校准本身不显示进度。 --- ## 3. 代码质量 ### 3.1 消除全局变量(高优先级) **现状**:`compute.py` 定义了 40+ 个模块级全局变量: ```python box_a = None alpha = None ATOM_IDS = None ... ``` 所有算法函数直接依赖这些全局变量,导致: - 无法并发运行两个模拟 - 无法单元测试单个函数 - 函数签名无法自文档化 **建议**:将全局状态封装为 `SimulationState` 类: ```python @dataclass class SimulationState: box_a: float = 10.0 atom_ids: np.ndarray = None atom_masses: np.ndarray = None ... def leapfrog(state: SimulationState, ...): ... ``` ### 3.2 draw.py 全局变量(中优先级) **现状**:类似的问题,`draw.py` 用了 ~30 个模块级变量: ```python DISPLAY_X = ... DISP_ALL_X = ... N_FRAMES = ... frame_idx = 0 ``` **建议**:封装为 `AnimationData` 和 `CameraState` 类 ### 3.3 废弃文件清理(低优先级) 以下文件可能已废弃或重复: | 文件 | 行数 | 判断 | |------|------|------| | `sample.py` | 184 | 已不再被调用(抽帧已移至引擎内部) | | `build_release_zip.py` | - | 打包脚本,非核心功能 | | `export_web_data.py` | - | 导出脚本,非核心功能 | | `migrate_npz_outputs.py` | - | 迁移脚本,一次性使用 | | `tools/NumericalMethods.py` | - | 可能已过时 | | `build/` (CMake 相关) | - | 与独立引擎编译无关 | --- ## 4. 配置管理 ### 4.1 input.txt 格式统一(高优先级) **现状**:6 个案例的 `input.txt` 格式不统一: | 字段 | case01 | case06 | |------|--------|--------| | `save_trajectory` | ❌ 缺失 | ✅ 有 | | `camera_distance/elevation/azimuth` | ❌ 缺失 | ✅ 有 | | `move_camera` | ❌ 缺失 | ✅ 有 | | `step_sample` | 仍然写 1 | 已改为 0 | | `step_plot`配置 | 旧注释格式 | 新注释格式 | **建议**: ```yaml # 统一模板 flow: step_simulate: 1 step_sample: 0 # 引擎已内置抽帧 step_animation: 1 save_trajectory: 0 # 默认不保留 physics: box_a: 10.0 G: [0, 0, -9.8] method: leapfrog T_total: 10.0 NSTEP: 100 DT: 0.001 render: use_marker: 0 alpha: [0.0]*6 ball_color: [0.9, 0.2, 0.2] box_color: [0.8, 0.8, 0.85] camera: distance: 40.0 elevation: 0 azimuth: 0 move: false ``` ### 4.2 YAML 结构优化(中优先级) **现状**:`ball_color_r/g/b` 三个独立的键,YAML 列表更自然: ```yaml # 当前 ball_color_r: 0.90 ball_color_g: 0.20 ball_color_b: 0.20 # 建议 ball_color: [0.90, 0.20, 0.20] ``` --- ## 5. 测试与质量 ### 5.1 缺少测试(高优先级) **现状**:无单元测试、无集成测试。每次修改只能手动跑案例。 **建议**: - 为 `compute.py` 的物理算法(Leapfrog、Euler 等)添加 pytest 测试 - 为文件 I/O(`save_display_txt` / `load_display_txt` 读写一致性)添加测试 - 添加一个 `examples/case00` 最小验证案例(2 个原子,10 步) - CI:`pytest` + 简单的集成测试脚本 ### 5.2 缺少类型标注(中优先级) ```python # 现状 def leapfrog(x, y, z, vx, vy, vz, dt, m, g, b): # 建议 def leapfrog( x: np.ndarray, y: np.ndarray, z: np.ndarray, vx: np.ndarray, vy: np.ndarray, vz: np.ndarray, dt: float, m: np.ndarray, g: np.ndarray, b: np.ndarray ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: ``` --- ## 6. 引擎一致性 ### 6.1 Fortran 引擎更新(高) **现状**:Fortran 引擎**不支持** `save_trajectory=0` 模式,总是写 JSON 格式的 `trajectory.txt`。需要: 1. 读取 `save_trajectory` 参数 2. 计算时按 NSTEP 采样 3. 写 display.txt 新格式 4. 按条件跳过 trajectory.txt 输出 参考 C 引擎的 `write_display_txt` 实现(约 40 行代码)。 ### 6.2 参数默认值一致性(中) 各引擎的 `SimParams` 默认值不一致: | 参数 | C | C++ | Fortran | |------|---|---|---------| | `box_a` | 无默认 | 10.0 | 10.0 (代码中) | | `method` | leapfrog | leapfrog | leapfrog | | `save_trajectory` | 0 | 1 | 不支持 | --- ## 7. 用户体验 ### 7.1 进度条改进(低) - 外部引擎校准阶段不显示进度(建议加 "校准中…" 文本) - Fortran 引擎支持时,增加进度消息 ### 7.2 缓存一致性(已修复) 已修复:`display.txt` 在 `save_trajectory=0` 时不再保留 `trajectory.txt`,外部引擎路径总是从 `trajectory.txt` 重新抽帧。 ### 7.3 move_camera.txt 实时生效(已修复) `draw.py` 直接读取 `move_camera.txt`,修改后重启动画即可生效,无需重新跑模拟。 --- ## 8. 实施优先级 | 优先级 | 内容 | 预估工作量 | |--------|------|-----------| | P0 | Fortran 引擎支持 save_trajectory=0 | 2-3h | | P0 | 全局变量封装为 SimulationState | 3-4h | | P1 | compute.py 模块拆分 | 4-6h | | P1 | 添加单元测试 (pytest) | 3-5h | | P1 | input.txt 格式统一 (6个案例) | 1-2h | | P2 | 校准缓存加速 | 1-2h | | P2 | 废弃文件清理 | 0.5h | | P3 | draw.py 封装 AnimationData | 2-3h | | P3 | 类型标注 | 2-3h | --- ## 9. 快速 wins(半小时内可完成) 1. ✅ 更新 Fortran 引擎(参考 C 的 `write_display_txt` 迁移) 2. ✅ 删除 `sample.py`(已废弃) 3. ✅ 统一 6 个案例的 `input.txt` 格式 4. ✅ 添加 `save_trajectory` 到 case01-05 的 default 配置 5. ✅ 统一 `ball_color_r/g/b` 为 `ball_color` 列表