18 KiB
全系统数值中间表示(System Numeric IR)规范 v2.0
状态:C-01 已实现并冻结 v2.0 数据合同;当前编译结果为 reference_only,原生 kernel 与执行器属于 C-02/C-03 及后续工作。
适用范围:后端 GenericFluidSystem 编译后的整个仿真系统。
机器可读定义:schemas/system-numeric-ir-v2.schema.json。
1. 定位与边界
System Numeric IR(以下简称 IR)描述“一个已经解析并编译好的系统,数值求解时需要哪些数据、按什么关系执行”。它是系统级合同,不是单个部件的文件。
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 对象:
{
"$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. 编译、校验和消费流程
当前公开入口为:
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。