# AGENTS.md ## 项目目标 本仓库要构建一个**离线、以物理正确性为优先的数值相对论时空视频渲染器**。最终目标是把双黑洞(BBH)并合等动态数值相对论(NR)演化输出的真实四维时空渲染为 4K 视频。 渲染器必须直接消费时间依赖的 4D metric,对每条相机光线进行向过去的 null geodesic tracing;它不是仅渲染静态快照、波形或标量诊断的工具。第一阶段只处理两种 ray 终态:被黑洞捕获,或逃逸到无穷远天球。局域物质辐射、吸积盘、流体和等离子体均不在当前范围内。 设计依据是 [`nr_spacetime_movie_renderer_design.md`](nr_spacetime_movie_renderer_design.md)。实现前应先阅读与修改内容相关的章节;该文档是架构和物理取舍的权威来源。 ## 不要偏离的方向 - 不以实时性能、游戏式视觉近似或 ShaderToy 式效果为目标。 - frozen snapshot 只能作为数值演化代码内的廉价诊断,不能替代最终的动态 4D 渲染。 - 不把恒星背景预烘焙成 RGB 天球纹理;恒星是带方向、温度和振幅的点源 catalog。 - 相机不是固定三维坐标,而是 worldline 加 tetrad 的预生成轨迹。 - 第一版只做 CPU;先验证物理与数据流,再考虑 GPU。 - 不为复杂 OO/class hierarchy 增加抽象。首选 C 的 `struct`、明确 ownership、函数表和连续数组。 ## 核心架构约束 ### 时间组织 所有帧的 ray 必须按 coordinate time 汇总,沿时间从新到旧,通过可装入内存的 metric time slab 推进。不要按 frame 各自重复加载所需的 4D 数据。 每个 refinement pass 只追踪新请求的 samples:完整追踪后才能判断 image-plane triangle 是否需要细分,而早期 slab 此时已释放。不能在同一次 slab sweep 中临时创建新 ray 并回到相机时间重新追踪。 ### Ray 与并行 - 以 coordinate time `t` 作为 geodesic ODE 自变量,使 ray 演化与 slab 边界自然对齐。 - 批量 ray 状态采用 SoA `RayPool`,避免 per-ray allocation;支持 activation、终止和 stream compaction。 - 用 OpenMP 对 active ray pool 做粗粒度循环并行。不要为单条 ray 或 triangle 创建 task,也不要引入细粒度 mutex。 - metric evaluator 的可变缓存和插值 scratch space 必须是 thread-local,不能共享可变 cache。 - 优先 bulk arrays、顺序 metric I/O、time-slab streaming 和 coarse-grained parallelism。 ### 镜头映射与点源渲染 每帧在 image plane 上使用 adaptive triangle mesh。每个顶点保存 film 坐标、ray 状态、逃逸方向 `n_inf` 与频移信息。根据真实 mapping 与局部插值的误差、orientation consistency 和 Jacobian 奇异性进行局部细分。 最终从局部可逆 triangle 构造 inverse lens map:`sky direction -> image position`。同一恒星被多个局部 patch 覆盖时自然形成多像;不要依赖全局“第几阶像”分类。 catalog 内部数据保留 `(direction, temperature, amplitude)`,而非 RGB。对每个像计算局部 magnification 与频率比 `g`,用 `T_obs = g * T_emit` 处理黑体频移,并以保留亚像素位置的 PSF 直接 splat 到 HDR framebuffer。 ## Backend 与模块边界 顶层 movie scheduler 不得依赖具体 metric 来源。时空 backend 至少应可替换为:Minkowski、解析 Schwarzschild,以及 nmesh 4D 数值时空;未来可加入 Kerr/BBH。 - `observer`:worldline/tetrad 读取、插值和正交化;不实现 geodesic 或渲染。 - `movie` / `frame`:帧时间表、adaptive mesh、refinement 请求与 ray 结果回填。 - `ray`:批量 ray 状态、初始化、生命周期和 compaction。 - `geodesic`:3+1 null geodesic RHS、频移、ODE stepper 和 slab 内推进;不处理磁盘 I/O 或 frame mesh。 - `spacetime`:backend 统一接口、slab、metric/导数插值以及 capture/infinity classification。 - `nmesh_backend`:读取原生 DG node 输出、AMR lookup、Lagrange 空间插值及导数、时间插值和 puncture trajectories。 - `catalog`:Gaia/2MASS 等 catalog 的读取、内部转换和天球索引。 - `optics` / `psf`:黑体频移、linear RGB、flux、PSF 和 HDR 累积。 - `output`:HDR、曝光、tone mapping、PNG/EXR 与视频编码接口。 对 nmesh 数据,直接在原生 DG nodes 上用 Lagrange basis 插值及求空间导数;不要先重采样到 Cartesian uniform grid,也不要求相邻时间 slice 的 AMR tree 拓扑对应。对固定全局点,应分别在各 slice 的空间 mesh 上求值,再进行时间插值。 优先从 3+1 identities、已知 gauge RHS 或 temporal interpolant 的解析导数获得时间导数;不要为已有插值量另行做低阶 finite difference,也不要在 geodesic RHS 中计算不会使用的量。 ## 黑洞终止与暗终态 过去向光线不使用 horizon 内位置 cutoff、AH-calibrated puncture 小球或 armed/re-entry 状态机判定正常物理捕获。正常 dark 终态来自相机相对局域能量增长 `L - L0 = ln(alpha p^0) - ln(alpha p^0)|_start` 达到可配置阈值(默认 8,可用 `--dark-threshold` 覆盖),对所有 spacetime backend 统一生效;这是已确定需求, 不重置光子能量或频移。不同 dark reason 不制造 mesh seam。无法可靠推进的 积分必须报告具体数值失败,不得改写成 capture。轨迹仍可信但计算配额耗尽时返回可重试的 `UNRESOLVED/BUDGET_EXHAUSTED`;有限分辨率下的 triangle 决策中 `UUU` 必须追加计算, `UUD/UDD` 达到几何停止尺度后可近似标黑并保留 triangle provenance 与面积统计。 不假定每次生产演化都会运行昂贵的 AH finder,也不依赖 capture sidecar。跨 chart、 跨 region 或穿越视界本身不是暗终态。 ## 开发与验证顺序 按以下层级实现和回归验证,后续阶段不得绕过前一阶段的基准: 1. Minkowski + 星表:验证 observer/camera projection、天球方向、catalog、温度/振幅、PSF 和 mesh。 2. 解析 Schwarzschild:验证 capture、Einstein ring、多像、refinement、局部 inverse map、放大率和频移。 3. 数值单 Schwarzschild:验证动态 4D slab streaming,并与解析解比较逃逸方向、频移和捕获分类。 4. FOZ4c BBH:在上层架构不重设计的前提下接入成熟的 BBH 演化。 新增物理、插值或优化时,优先添加能与前一阶段比较的收敛测试或 regression test。尚未由 prototype/convergence test 决定的参数(如时间输出 cadence、时间插值阶数、slab 大小、refinement 阈值、PSF、ODE stepper)不要伪装成既定事实。 性能 benchmark 记录必须保留完整、可复制的命令及原始终端输出,不能只记录汇总耗时或吞吐量;输出中的 build/cache、输入加载、工作线程、处理数量与 fallback 等统计是后续正确归因性能变化的证据。 ## 仓库卫生与短期产物 只有对本项目有长期记录价值、且值得进入 public repo 的测试与 benchmark 才纳入 git。 - 短期性能对照、一次性 A/B 原始日志、临时测量脚本、ad-hoc probe 与中间数据放到仓库根目录的 `local/`;该目录已被 `.gitignore` 排除,不进入版本库。 - 追踪文件必须可复现:命令把输出、原始日志写到 `/tmp` 或 `local/` 是正常的,这些仓库外/被忽略的数据无需跟踪;但复现所需的输入、脚本、fixture 必须一并纳入 git(放在 `scripts/` 或 `benchmarks//`),不得依赖未跟踪的脚本或数据。 - `benchmarks/` 只保留自包含、可复现的长期记录;`tests/` 只保留针对生产代码的回归测试,不保留为单次实验服务的一次性探针。 ## 修改原则 - 改动应保持 backend、observer、geodesic、movie mesh 与 optics 的职责分离。 - 性能优化不得破坏 time-dependent tracing、局部可逆映射或点源的亚像素 PSF 渲染。 - 新增缓存先明确 ownership、生命周期和线程归属;高开销可变缓存默认 thread-local。 - 未在设计文档中明确的物理或数值取舍,应通过小型 prototype、基准或收敛实验确定,并同步更新设计文档。 ## Git 提交消息 - Git 提交消息必须使用英文,包括标题和正文。 - 提交消息必须以 category 开头,格式为 `Category: 简洁说明`。 - 常用 category 包括 `Doc:`、`Fix:`、`Feat:`、`Makefile:` 等。 - 对不属于修复或功能的普通小更新,使用所涉及的模块作为 category,例如 `Catalog:`、`Geodesic:`、`Spacetime:` 或 `Output:`。