Files
SystemSimulationApp/docs/standard/system-numeric-ir-v2.md
T

265 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 全系统数值中间表示(System Numeric IR)规范 v2.0
> 2026-09-10 集成说明:保留本规范、schema、签名与静态校验。依赖旧 `GenericFluidSystem` 的 Python 对象导出器已随旧数值内核退役,调用 `compile_system_ir()` 会明确报错。当前可执行 C 路径使用 `native_codegen.compiler.compile_native_program(network)`,尚未按本 v2 结构输出执行计划;不能把当前 C 支持等同于本规范的 native 能力声明。原导出结果固化在 `tests/data/system-ir-v2-reference.json.gz`,继续验证 schema 与复杂计划的错误检查。
状态:C-01 已实现并冻结 v2.0 数据合同;当前编译结果为 `reference_only`,原生 kernel 与执行器属于 C-02/C-03 及后续工作。
适用范围:后端 `GenericFluidSystem` 编译后的整个仿真系统。
机器可读定义:`schemas/system-numeric-ir-v2.schema.json`。
## 1. 定位与边界
System Numeric IR(以下简称 IR)描述“一个已经解析并编译好的系统,数值求解时需要哪些数据、按什么关系执行”。它是系统级合同,不是单个部件的文件。
```text
XML / 建模 JSON
→ 模型解析和连接检查
→ GenericFluidSystem 对象图
→ System IR v2.0
→ 参考执行器 / 未来原生执行器
```
XML 保存用户建立了哪些元件、参数和连线;IR 在此基础上补充求解器真正需要的槽位编号、状态降维、方程块、执行阶段、闭合范围、事务回滚、事件、Jacobian 和输出投影。因此二者看起来相似,但用途和层级不同。
本版本只完成“完整、确定、可校验的数据合同”和从现有系统生成该合同的编译器。它没有替换当前默认 Python 求解路径,不改变现有仿真结果。IR 中禁止保存 Python 函数、闭包、模型对象、对象地址和运行期临时状态。
## 2. 三个权威来源
三份实现共同定义 v2.0:
- `app/simulation/ir/schema.py`:Python 不可变数据类型、枚举、规范序列化和签名算法;
- `schemas/system-numeric-ir-v2.schema.json`:跨语言 JSON 结构合同;
- `app/simulation/ir/validation.py`:仅靠 JSON Schema 无法表达的引用、覆盖、拓扑和数值语义校验。
生产者必须同时满足机器 Schema 和语义校验。字段有增删时必须同步修改三处以及合同测试,不能只更新文档。
## 3. 版本、兼容性和严格读取
版本不是顶层整数,而是 `version` 对象:
```json
{
"$type": "schema_version",
"schema_id": "system-numeric-ir",
"major": 2,
"minor": 0
}
```
- `major` 改变表示不兼容的字段或执行语义变化;读取方必须拒绝未知主版本。
- `minor` 用于同一主版本内向前演进;当前读取方拒绝负数和高于自身能力的次版本。
- JSON Schema 对所有合同对象使用 `additionalProperties: false`,v2.0 读取方不会静默忽略未知字段或枚举值。
- 原生二进制 ABI 不写入 `SystemIR`,而由独立的 `IRNativeBuildIdentity.abi_version` 管理,当前值为 `1`。
- 压力流量中的 `causal_plans.source_schema_version == 1` 只表示其来源是既有 causal IR v1;它不是完整系统 IR 的版本,也不能携带 v1 的 Python 回调。
## 4. 线格式和顶层结构
每个 dataclass 序列化后都带有稳定的 `$type`;操作对象还带有 `opcode`。顶层 `$type` 为 `system_ir`,其字段完整集合如下:
| 字段 | 含义 |
| --- | --- |
| `version` | IR schema 身份与版本 |
| `model_id` / `model_version` | 输入系统的稳定身份和调用方提供的模型版本 |
| `compiler_id` / `compiler_version` | 产生 IR 的编译器身份,当前为 `generic-fluid-system` / `2.0.0` |
| `numeric_dtype` | 主数值类型,v2.0 只接受 `float64` |
| `buffers` / `values` | 连续缓冲区及每一个数值槽位的元数据 |
| `kernels` / `components` | kernel 声明与元件实例绑定 |
| `mediums` | 介质实现、介质常量和使用该介质的元件 |
| `ports` / `connections` | 端口变量和系统拓扑 |
| `state_reducer` | 求解器状态与元件局部状态/导数的线性映射 |
| `causal_plans` / `pressure_flow` | 因果子计划和完整压力流量方程计划 |
| `stream_plans` / `thermofluid` | stream SCC/DAG 与热流体外层闭合计划 |
| `stages` / `blocks` | 无回调操作阶段与复合/迭代执行块 |
| `entry_points` | `rhs`、`events`、`jacobian`、`outputs` 四个入口 |
| `transaction` | 试算快照、流量恢复和参考缓存诊断 |
| `modes` / `events` | 离散模式、根函数、reset 与缓存失效 |
| `jacobian` | 固定 CSR 结构、着色和局部有限差分计划 |
| `outputs` | 结果元数据和投影 |
| `capabilities` | 系统和元件的原生可执行能力及缺口 |
| `required_features` | 读取/执行该程序必须理解的功能 ID |
所有列表的顺序都是合同的一部分。引用统一采用数组索引或稳定 ID,不能依赖哈希表遍历顺序。
## 5. 规范序列化与内容签名
`canonical_json_bytes()` 是跨平台唯一线表示:
- UTF-8,ASCII 转义开启,JSON 键排序,无无意义空白;
- 字符串先做 Unicode NFC 规范化;
- tuple 写成 JSON array,不接受 list、dict、set 或任意对象;
- 浮点数写成 IEEE-754 binary64 大端十六进制对象,例如 `{"$float64":"3ff0000000000000"}`;
- `-0.0` 统一为 `+0.0`,NaN 和正负无穷直接拒绝;
- 枚举写成规范字符串,整数和布尔值保持其 JSON 类型。
`SystemIR.structural_signature` 是上述完整 `SystemIR` 内容字节的 SHA-256 小写十六进制值。它准确回答“这份 IR 内容是否完全相同”,包含参数值和所有计划,因此不声称不同表达形式的数学系统会得到同一签名。操作系统、机器路径、构建时间、编译器和 native flags 不进入这个签名。
## 6. 缓冲区、槽位和值
槽位引用的格式为 `{"$type":"slot_ref","buffer":"...","index":N}`。v2.0 恰好声明以下 16 类缓冲区,每类一次:
| dtype | 缓冲区 |
| --- | --- |
| `float64` | `time`、`state_input`、`derivative_output`、`local_state`、`local_derivative`、`algebraic`、`signal`、`parameter`、`constant`、`work_float`、`event_output`、`jacobian_value`、`result_output`、`runtime_input` |
| `int32` | `mode`、`work_int` |
`time` 的长度必须为 1;`int32` 初值必须在有符号 32 位范围内。每个缓冲区内索引为 `[0, size)`,而且每一个实际槽位必须恰好有一个 `IRValueSpec`。值描述包含稳定 ID、语义、角色、物理量、单位、缩放、可选上下界和可选所属元件。缩放必须为正有限数,边界必须有序且有限。
缓冲区是执行器唯一的数值寻址合同。名称用于诊断,不允许执行器重新用名称查找取代槽位访问。
## 7. Kernel 与元件绑定
`IRKernelSpec` 声明模型类型、模型版本、实现版本、能力、支持的 phase 以及参数/状态/mode/workspace 数量。phase 枚举为:
`primal`、`residual`、`derivative`、`property`、`event`、`reset`、`jacobian`、`output`。
phase 在 C-01 中只是稳定的功能标签。不同模型在同一 phase 下可能有不同输入输出数量,所以不能在 phase 上填写虚假的统一 arity。当前每个 `IRKernelCallOperation` 自身的有序 `read_slots`、`write_slots` 和 `equation_indices` 才是该次调用的权威依赖合同。C-02 将在此基础上冻结每个 `kernel_id + phase` 的纯数值调用签名并验证所有调用实例一致。
`IRComponentInstance` 把一个元件实例绑定到 kernel,并明确列出参数、局部状态、局部导数、mode、端口、输出和两类 workspace。绑定数量必须与 kernel 声明一致;端口和输出必须与其反向所属关系精确一致。
当前编译器把所有既有 Python kernel 标记为 `reference_only`。为便于结构审计,reference kernel 调用声明了保守读集合:可能多读,但不能漏掉模型对象当前可见的数值输入。Python 内部隐藏缓存仍不是原生槽位,因而任何含这类依赖的程序都不得宣称 `native`。
## 8. 介质、端口和连接
介质记录稳定 `medium_id`、名称、实现及其版本、常量槽位和使用它的元件索引。介质参数必须位于 `constant` 缓冲区。
端口分为 `physical` 与 `signal`:
- 物理端口必须声明正流方向,当前统一为 `intoComponent`;
- signal 端口不得声明物理流向;
- 变量角色与连接规则固定对应:`effort → equal`、`flow → sumToZero`、`stream → streamMix`、`signal → directed`。
连接必须引用两个已声明、不同、同 kind/同 domain 的端口,并精确覆盖两个端口的全部同名变量合同。物理端口和 signal 输入最多被一条连接占用;signal 输出允许扇出到多个输入。禁止重复端点对和悬空索引。
## 9. 状态降维与导数汇总
`IRStateReducer` 使求解器的 `state_input` 与各元件 `local_state` 分离。`state_reducer.initial_state` 是状态初值的语义描述,必须与 `state_input` 缓冲区的初值逐项完全相同,避免消费者面对两个不同初值:
- `state_scatter` 用 CSR 矩阵把求解器状态散射到有序局部状态槽位;
- `derivative_gather` 把有序局部导数汇总为 `derivative_output`;
- `initial_state` 与 `absolute_tolerances` 按求解器状态顺序定义。
这能显式表达共享机械坐标和气动储能状态的降维关系。例如同一气动储能状态可以按体积权重散射到多个局部状态,而不是由执行器临时按对象身份猜测。两个矩阵必须满足 CSR 不变量、维度和值数量合同,并覆盖全部组件状态/导数绑定。
## 10. 压力—流量计划与因果元数据
`IRPressureFlowPlan` 包含:
- `unknowns`:未知量的元件、端口、变量角色、槽位、缩放和边界;
- `equations`:元件或连接拥有的方程、关系、涉及槽位、残差槽位和缩放;
- `blocks`:未知量/方程的方块分解及每块 Jacobian CSR 结构;
- `scopes`:全网、敏感物理岛或方程块作用域及求解限制;
- `global_scope_index` 和 `secondary_scope_indices`:第一次全网求解与后续局部重算范围;
- `pressure_lower_bound`:全局压力下界。
全局 scope 必须覆盖完整网络;每个 scope 的未知量和方程必须等于它包含的 blocks 之并集;secondary scope 唯一且不能包含 global scope。每条方程必须拥有唯一的 residual 槽位,防止两个残差互相覆盖。`sparse_pattern_trusted=false` 时必须给出回退原因,可信结构则不得携带回退原因。
`IRCausalPlan` 保存当前 causal IR v1 编译得到的无回调元数据,包括作用域、规范/兼容/重置槽位、外部 effort、effort 阶段和 flow 阶段。它只作为 v2 压力流量计划的一部分,不代替完整系统计划。
## 11. 操作、阶段、执行块和四个入口
v2.0 的无回调 opcode 为:
`fill`、`copy`、`scatter`、`linear_combination`、`state_map`、`kernel_call`、`effort_broadcast`、`flow_assign`、`check_finite`。
`IRStage` 给出 stage kind、操作序列以及声明的读/写集合;声明集合必须与操作读写并集完全一致。`IRExecutionBlock` 可以按顺序引用 stage 或其他 block;引用图必须无环。`fixed_point` 和 `stream_scc` block 必须声明监控槽位、绝对/相对容差、最大迭代、松弛、回滚槽位和失败策略,其他 block 禁止携带收敛合同。
四个入口必须恰好各一个,且入口输入统一按 `time`、完整 `state_input`、完整 `runtime_input` 排列:
| 入口 | 必须到达的结果阶段 | 精确输出缓冲区 | 不允许夹带 |
| --- | --- | --- | --- |
| `rhs` | `derivative_reduce` | 全部 `derivative_output` | event、Jacobian、output、reset |
| `events` | `event` | 按事件顺序的全部 `event_output` 根槽位 | derivative reduce、Jacobian、output、reset |
| `jacobian` | `jacobian` | 按 CSR 顺序的全部 `jacobian_value` | event、output、reset |
| `outputs` | `output` | 按结果顺序的全部 `result_output` | event、Jacobian、reset |
入口可以复用前置 primal 阶段,但不能把四个入口合并成“每次 RHS 都把事件、Jacobian 和全部输出计算一遍”。入口执行不得写入 `state_input`、`parameter`、`constant` 或 `mode` 等持久输入。
## 12. Stream、热流体闭合和事务
`IRStreamPlan` 显式列出 stream 节点、强连通分量(SCC)、SCC 间缩点 DAG 和拓扑顺序。每个 SCC 对应一个 `stream_scc` block,循环只在 SCC 内迭代;监控槽位必须覆盖该 SCC 节点。
`IRThermofluidPlan` 覆盖全部物理端口,关联 stream plan、全局元件集合、敏感元件、secondary 压力 scope、最大迭代和流量相对容差。是否使用保守全网求解及原因必须成对出现,避免执行器静默扩大作用域。
`IRTransactionPlan` 冻结一次试探计算需要快照和恢复的端口变量,并单列实际活动气动端口上的 `m_flow`。当前目标系统中的这部分数量为 232;机械模型对象里没有作为活动端口变量出现的隐藏 `m_flow` 字段不会被误算进该集合。所有失败试算必须恢复快照;正常成功返回即为隐式提交,不另设可被误排序的 commit opcode。
`cache_component_indices`、`cache_attribute_ids` 和 `diagnostic_owner_ids` 只记录当前 Python 参考路径中仍需关注的隐藏副作用,供 C-02/C-03 清除和 Shadow 诊断;它们不是原生内存布局。存在 opaque Python cache 属性的系统不能标记为 `native`。
## 13. 模式、事件和 Reset
每个 `IRModeSpec` 记录 int32 mode 槽位、所属元件、合法值及初值。模式槽位必须全部且只被一个 mode 说明,组件 mode 绑定与 owner 关系必须双向覆盖。
每个 `IREventSpec` 记录稳定事件 ID、事件类型、owner、根槽位、触发方向、终止性、优先级、mode guard、reset steps、失效缓存种类以及是否重启积分器。reset 只能引用 `reset` stage,事件根槽位必须精确覆盖 `event_output` 缓冲区。
当前 IR 已能表达现有元件暴露的事件和模式结构;仍隐藏在 Python 信号求解或机械密集输出逻辑中的行为属于 `reference_only` 能力缺口,必须在 C-02/C-08 显式化后才能原生执行。
## 14. Jacobian 合同
`IRJacobianPlan` 包含固定 CSR pattern、与非零项一一对应的 `value_slots`、颜色组、填充值步骤、解析 value 索引和局部有限差分列。
CSR 必须满足:`row_pointers` 长度为行数加一、首项为 0、单调不减、末项等于非零项数量;每行列索引递增、唯一且在范围内。颜色组中的列不能共享同一潜在非零行,列不能重复着色。解析项和有限差分项不得重复或越界;每个有限差分列只能填写该列在 CSR 中确实存在的 value 索引,步长必须为正有限数。Jacobian 入口的执行步骤必须与 `fill_steps` 完全一致。
结构可以保守地多报潜在非零项,但不能漏报可能依赖。结构、模式布局或 kernel 实现改变会自然改变整份 IR 内容签名。
## 15. 输出合同
每个 `IROutputSpec` 包含稳定 output ID、所属元件、scope、可选端口名、内部名、显示标签、类别、物理量、单位、局部顺序、来源槽位、结果槽位以及线性 scale/offset。
`output_id` 和 `result_output` 槽位在全系统唯一。`order` 只在 `(component_index, scope, port_name)` 内排序,因此不同元件出现相同 `order` 是合法的;全局最终列顺序由 `outputs` 数组顺序确定。组件的 `output_indices` 必须精确反向覆盖其所有输出。
## 16. 能力报告与拒绝规则
能力级别只有:
- `native`:所有 kernel phase、状态、事件、事务和缓存都满足原生合同;
- `reference_only`:数学/结构已描述,但至少一个阶段仍依赖 Python 参考实现;
- `unsupported`:当前 IR 无法安全表达或执行,必须带 error 级能力问题。
每个元件必须恰好有一条 capability,列出支持 phase 与缺失 feature。系统为 `native` 时所有元件和 kernels 都必须是 native,且不能依赖 opaque Python cache;系统含任意 reference-only 元件时不能伪装为 native。能力问题具有 code、severity、scope ID 和消息,相同 code/scope 不得重复。
当前 `compile_system_ir()` 的输出明确为 `reference_only`,原因是 C-02 的纯数值 kernel 签名与 C-03 的扁平参考执行器尚未完成。这不是 IR 编译失败,也不允许 native loader 越过能力报告运行。标为 native 的系统还必须覆盖所有实际调用 phase,并且不得要求 `reference_kernel_dispatch`。
## 17. 原生构建产物键
二进制缓存身份与 IR 内容签名严格分离。`native_artifact_key(program, build)` 对以下信息再次做规范序列化和 SHA-256:
- `program.structural_signature`;
- native ABI 版本;
- target triple;
- 编译器 ID 与版本;
- 有序编译 flags;
- 浮点策略;
- kernel 库 SHA-256 签名。
ABI 必须等于当前支持值,字符串不能为空,flags 不能含空项,kernel 库签名必须为 64 位小写十六进制。这样相同 IR 在 Windows/Linux 上具有相同内容签名,但得到不同且安全的 native artifact key。
## 18. 编译、校验和消费流程
当前公开入口为:
```python
from app.simulation.ir import compile_system_ir, require_valid_system_ir
program = compile_system_ir(system, model_version="...")
require_valid_system_ir(program)
payload = program.canonical_json_bytes()
signature = program.structural_signature
```
`compile_system_ir()` 接收已完成解析和系统构建的 `GenericFluidSystem`,不直接解析 XML。消费者必须先验证,再根据 `capabilities.system_level` 选择参考路径或未来原生路径;不得把“JSON Schema 能读取”误当成“具备 native 执行能力”。
静态校验采用 fail-closed 策略,覆盖:版本与 required feature、全部槽位、数值范围、组件/kernel arity、端口/连接、介质、状态映射、压力流量方程与 scope、阶段读写、block 无环、四入口切片、stream/热流体、事务、mode/event/reset、Jacobian、输出及能力一致性。`require_valid_system_ir()` 聚合错误后拒绝程序。
## 19. C-01 验收边界与后续工作
C-01 的完成标准是:
- 能从当前目标复杂模型和历史 0.81 s 模型生成完整系统级结构;
- 同一系统重复编译得到字节完全相同的 canonical JSON 和签名;
- 换行方式、Python 哈希种子和目标平台不会污染 IR 内容身份;
- 故意破坏引用、覆盖、CSR、事务、入口或能力合同会被拒绝;
- IR 中没有 callback、对象地址或任意 Python 对象;
- 默认 Python 仿真路径保持不变。
C-01 不等于已经拥有可运行的 C 后端。下一步 C-02 要冻结每个 kernel 的纯数值签名、隐藏缓存和错误码;C-03 要用扁平 Python 执行器逐槽 Shadow 对照;完成这两项后,才可以建立 C ABI、原生执行器并逐步把 capability 从 `reference_only` 提升为 `native`。