Files
SystemSimulationApp/docs/standard/component-model-authoring-spec-v1.md
T
ljz 7611f13208 修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验
相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。

- 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。
- 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。
- 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。
- 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。
- 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。
- 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。

验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
2026-09-17 23:50:13 +08:00

606 lines
32 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.
# 组件模型建模规范 v1
文档版本:1.2.0
修订日期:2026-09-12
核对代码基线:`22579e5` 加本次输入合同实现;配套工程 JSON v2,XML v3 和模型版本不变。
状态:Python 声明、C 数值实现;本版按实际注册与浏览器导入演练修订。
总流程:[新组件注册流程](component-registration-workflow-v1.md);示例:[注册示例与验证](component-registration-example-v1.md)。
适用对象:人工开发者、代码生成工具和 AI 编程助手
配套读取规范:[组件库分类、发现与读取规范 v1](component-library-spec-v1.md)
端口供需规范:[气动端口变量供需合同](port-computation-contract.md)
## 1. 文档目标
本文档规定采用 Python 元数据与 C 数值内核的元件应如何创建、修改、测试和注册。完成后的模型必须
同时满足四个使用方:
1. C 编译器能够读取模型声明、生成系统代码并调用 C 方程。
2. System XML 能够根据稳定类型找到模型。
3. React Flow 能够自动显示图标、端口和参数。
4. 结果页面能够根据结构化元数据展示变量。
本文档是模型代码的开发合同。若本文档与当前代码行为不一致,应把它视为缺陷:
先核对实际实现,再在同一次修改中同步代码、测试和文档,禁止让两套规则长期并存。
## 2. 开始前先判断任务类型
### 2.1 新增公开模型
公开模型加入启用库后能被目录和 System XML 识别;前端当前隐藏 `experimental` 库,其余有效库可见。参与原生仿真还需第 12 节的完整数值接入。必须:
- 放入某个组件库的分类目录。
- 实现完整模型契约。
- 加入该库 `library.py` 的 `models` 清单。
- 添加目录、契约、方程和最小仿真测试。
### 2.2 修改已有公开模型
必须先判断改动是否破坏已有工程:
| 改动 | 版本建议 | 兼容性要求 |
| --- | --- | --- |
| 修复数值实现但不改变契约 | 修订版本 | 若提高 `modelVersion`,既有 XML 会因精确版本不匹配而被拒绝;需明确是否真的变更合同 |
| 新增有默认值的参数或结果 | 次版本 | 新 XML 必须写全当前参数;无通用迁移,前端已有部分型号专用迁移 |
| 修改界面名称或图标 | 库修订版本 | 不修改机器标识 |
| 修改方程的物理语义 | 根据影响提高次版本或主版本 | 补充基准和变更说明 |
| 删除、改名端口或参数 | 主版本 | 当前格式直接拒绝旧端口或参数;如以后需要兼容,再单独设计迁移器 |
| 修改 `MODEL_TYPE` | 视为新模型 | 明确保留旧实现、显式迁移或拒绝旧工程;没有自动生成的迁移映射 |
### 2.3 新增内部模型
研究类可以不加入生产清单。但当前原生入口要求精确的类及版本合同,不能只新增 C 函数和白名单就直接执行未发现的类。需要端到端演练时,在隔离源码副本中建立明确的测试库、原生发现入口与方程适配,见注册示例;或设计并验证专门的内部适配入口。不要恢复旧 Python 求解路径,也不要把演练类留在正式目录。
### 2.4 新增物理域
仅新增模型类不足以支持新物理域。除了模型,还必须设计:
- `PortDefinition` 和端口变量。
- 变量角色与连接规则。
- 网络兼容性检查。
- 代数方程和 stream/signal 传播。
- XML 端口协议。
- 前端连线兼容规则。
- 最小闭合系统与求解测试。
没有完成这些基础能力时,不得仅通过修改 `domain` 字符串宣称支持新物理域。
## 3. 开发前必须读取的文件
人工或 AI 在修改模型前,应按顺序读取:
1. 本文档。
2. 目标库的 `library.py`。
3. 同分类中物理行为最接近的现有模型。
4. [`core/base.py`](../../app/simulation/core/base.py)。
5. [`core/ports.py`](../../app/simulation/core/ports.py)。
6. [`core/metadata.py`](../../app/simulation/core/metadata.py)。
7. [`core/catalog.py`](../../app/simulation/core/catalog.py)。
8. [`registry.py`](../../app/simulation/registry.py) 中的启动校验。
9. 与目标模型最接近的测试。
不要只根据文件名、前端图标或旧 XML 猜测模型语义。
## 4. 文件位置和命名
公开模型放在:
```text
app/simulation/components/<library_id>/<category_id>/<model_module>.py
```
例如:
```text
app/simulation/components/experimental/storage/cylinder.py
app/simulation/components/experimental/flow/orifice.py
app/simulation/components/experimental/junctions/tee.py
```
规则:
- 推荐每个公开模型单独成文件;注册器不按文件名发现,现有同一文件包含多个相关型号的组织方式仍有效。
- 模块名、`MODEL_TYPE`、端口名和参数名使用稳定机器标识。
- `MODEL_TYPE` 使用小写 `snake_case`。
- 参数和结果变量允许保留已有热力学惯例,如 `T0`、`T`、`U`。
- 中文名称只写入 `label`,不能代替机器标识。
- 求解器、介质和网络通用逻辑不得复制到模型文件。
## 5. 公开模型完整契约
每个公开模型类必须在自身类体中显式声明:
```python
MODEL_TYPE = "example_component"
MODEL_VERSION = "1.0.0"
PORTS = (...)
PARAMETERS = (...)
RESULT_VARIABLES = (...)
DISPLAY = ...
```
同时必须实现:
```python
@classmethod
def create(
cls,
*,
name: str,
medium: IdealGasMedium,
parameters: Mapping[str, float],
) -> Component:
...
```
注册器要求这些字段直接存在于公开模型类中。不要依赖父类隐式提供
`MODEL_TYPE`、`MODEL_VERSION`、`PORTS`、`PARAMETERS`、`RESULT_VARIABLES`、
`DISPLAY` 或 `create()`。
## 6. 基类选择
`AlgebraicComponent` 表示没有积分状态的元件;`DynamicComponent` 表示有积分状态的元件;`ThermodynamicVolumeComponent` 提供标准 `m,U,p,T,rho,u,h` 结果声明。这些基类只描述模型,不再实现数值求值方法。
`state_size` 只描述 Python 对象,不能自动生成原生状态;状态索引、初值、导数和输出须在生成路径逐项实现。
构造函数负责参数校验、几何预处理和端口注册。介质对象保存物性常量与模型选择。状态初值、流量、焓、受力和导数必须在 C 中计算。
用 `EQUATIONS` 或 `equation_definitions()` 声明结构化连接约束,返回 `EquationDefinition`,包括关系、变量和归属,不包含运行时残差值。`__MODEL__` 占位符由基类替换为实例名。该声明供网络结构展示与检查使用,不替代 C 方程或构建支持白名单。
## 7. 端口建模规范
当前气动模型使用:
```python
PortDefinition.pneumatic(
"port_a",
nominal_role="bidirectional",
computation=THERMODYNAMIC_SUPPLY,
)
```
上例适用于提供温度/压力的储气端,`THERMODYNAMIC_SUPPLY` 从 `core.port_computation` 导入。阀门或管道阻力端应按实际模型选择 `FLOW_SUPPLY`;不能给所有气动端口套用同一个供需模板。
气动端口包含:
| 变量 | 角色 | 连接规则 | SI 单位 |
| --- | --- | --- | --- |
| `p` | `effort` | `equal` | `Pa` |
| `m_flow` | `flow` | `sumToZero` | `kg/s` |
| `h_outflow` | `stream` | `streamMix` | `J/kg` |
| `volume` | `signal` | `directed` | `m3` |
| `volume_flow` | `signal` | `directed` | `m3/s` |
必须遵守:
- `m_flow > 0` 表示质量流入当前组件。
- `nominal_role` 只用于界面和默认布局,不限制实际流向。
- 物理连接的端点顺序不代表流向;具体子模型仍可规定固定的变量供需,例如 PN3NODE2 的参考口。
- 所有声明端口必须使用 `register_declared_port()` 创建。
- `DISPLAY.ports` 必须与 `PORTS` 名称集合完全一致。
- 分支连接使用三通等连接元件,不能让一个物理端口直接连接多条边。
禁止:
- 在模型内部根据画布左右方向判断流向。
- 为了前端显示另造一套端口名。
- 把 `port_a` 固定解释为真实入口、把 `port_b` 固定解释为真实出口。
- 直接绕过端口状态读写其他组件对象。
### 7.1 新元件先写逐变量接口表
新模型必须先在模型说明中列明每个端口的下列信息,再写元数据和 C 代码:
| 信息 | 必须回答的问题 |
| --- | --- |
| 物理含义、机器名、SI 单位 | 传递的是温度还是比焓?能量还是能量流率? |
| 提供方/使用方 | 该变量由本部件提供、从对端取得,还是需要连接方程共同确定? |
| 提供方式 | 来自当前状态、固定值、另一个端口的别名,还是由公式计算? |
| 计算依赖 | 计算该输出具体需要哪些输入、状态和参数?不要只写“依赖某部件”。 |
| 符号与坐标 | 正号代表流入还是流出?机械量采用什么方向?映射原模型时是否取反? |
| 必需性及默认值 | 缺少对端变量时能否用有物理依据的默认值?何时应当报错? |
| 条件变化 | 参数是否改变可用端口、输入输出关系?反向流、零流量或切换时怎样处理? |
固定参数放在 `PARAMETERS`,例如管径、长度、初始温度 `T0`;随仿真变化的端口量放在接口定义中,例如当前温度 `T`。固定开度变体可以有意用参数代替开度信号,但应使用独立模型类型并说明差异。
### 7.2 供需声明与当前实现边界
`PORTS` 是权威来源。当前 `PortComputation` 的 `inputs/outputs` 支持 `p`、`T`、`m_flow`、`H_flow` 四个气动量;`reference_port` 只表达 p/T 从另一个端口输入复制的关系。前端目录与 JSON/XML 校验从注册表恢复这些声明,工程快照不能覆盖它们。
`H_flow` 表示能量流率(W),只用于当前接口供需检查,不能当作已经存在的 C 运行时端口字段;运行时仍使用 `h_outflow` 和质量流率。两种表达的单位、符号及零流量处理必须在 C 方程中正确转换。
- `mode="fixed"` 表示该子模型的接口供需是固定要求;它不表示输出数值为常量,也不是固定积分步长。
- `mode="equation"` 表示允许连接方程联合确定变量。必须有相应 C 求解能力支持,不得为了绕过错误接线而随意改为此模式。
- 新移植部件如有明确的固定输入输出,应按原始接口声明并验证。现有非节点部件采用 `equation` 是本阶段保留已有联合求解能力的策略,不能把它当作所有 Amesim 子模型的原始接口定义。
- 新模型需要声明容积、机械量、任意输出依赖或可选输入默认值时,应先同步扩展供需数据结构、注册器、目录 schema、前后端检查与测试。当前四变量接口尚不能完整承载这些信息,不得只在注释或 JSON 中添加编译器不读取的字段。
### 7.3 从 Amesim 移植时的对照要求
以明确的 Amesim 版本和**子模型编号**为依据读取端口变量表及其实现;图标相同或端口数量相同不足以证明模型等价。每个原始变量都应有映射记录:保留、换名、单位/符号变换、由其他量导出、仅保留默认值,或明确不支持。
枚举必须按含义转换,不能按数字相等判断匹配。已核查的直接选择参数集中在 `app/simulation/components/amesim/semantics.py`;例如 UD00 的 AME `1/2` 对应公共 `0/1`。转换仅应用于外部 AME 参数,禁止再次应用于已经采用公共编码的 JSON/XML。新增选择项须补映射覆盖测试;尚未实现的活动选项必须在执行编译时明确拒绝,不能静默沿用其他模式的公式。模型仍可保存,执行能力与保存能力分别说明。
既要记录普通输入/输出,也要记录状态、固定输出、别名、取反别名、可选输入及默认值。例如 PNCH012 的容积输入和 MECMAS21 的加速度端口量,不能因为当前四变量气动模板没有对应字段就不作说明。
接口方向对齐、局部公式对齐和完整仿真曲线对齐是三项不同的验证;不能用其中一项替代其他两项。实验组件及自行简化的变体,不应宣称与 Amesim 原子模型一比一相同。
### 7.4 为计算排序准备元件步骤
元件说明和 C 接入应区分:状态/物性输出、连接量与局部代数计算、状态导数、展示输出。例如储气元件先由当前状态提供温度和压力,得到流量后再算导数,不能把整个元件当作一个不可拆分的步骤。
端口供需合同不承载任意计算步骤的依赖图。当前扩展 C 生成器使用 `Computation` 记录内置气动方程的输入、输出与 C 语句,再自动排序及划分局部循环。新模型须显式接入这些计算关系;只注册 `PORTS/PARAMETERS` 不会自动生成数值方程。参考值复制与能量汇总应拆开,多输出 C 调用必须正确列出读取参数与写出结果,见 [C 求值排序规范](native-evaluation-schedule.md)。
新增或修改接口至少验证:合法连接、输入无人提供、冲突连接、参考链及参考环、反向/零流量、可选输入默认值、单位/符号映射,以及独立解析或外部基准下的 C 结果。没有对应机制的能力必须标记为尚未支持。
## 8. 参数建模规范
所有用户可配置输入必须使用 `ParameterDefinition`:
```python
ParameterDefinition(
name="volume",
label="容积",
quantity="volume",
unit="m3",
default=0.1,
minimum=0.0,
minimum_exclusive=True,
)
```
字段含义:
| 字段 | 规则 |
| --- | --- |
| `name` | 稳定机器名,同时用于 XML、工程文件和 `create()` |
| `label` | 前端显示名称,不能为空 |
| `quantity` | 受控物理量标识 |
| `unit` | 后端 SI 基准单位 |
| `default` | 必须能够创建有效模型 |
| `minimum` / `maximum` | 必须反映方程有效范围 |
| `minimum_exclusive` | 用于直径、容积等严格大于零的量 |
当前受控单位定义在 `SI_UNIT_BY_QUANTITY`:
| quantity | SI 单位 |
| --- | --- |
| `acceleration` | `m/s2` |
| `area` | `m2` |
| `dimensionless` | 空字符串 |
| `density` | `kg/m³` |
| `flow_coefficient` | `kg/(s*Pa^0.5)` |
| `force` | `N` |
| `heat_transfer_coefficient` | `W/(m2*K)` |
| `internal_energy` | `J` |
| `length` | `m` |
| `mass` | `kg` |
| `mass_flow` | `kg/s` |
| `pressure` | `Pa` |
| `specific_enthalpy` | `J/kg` |
| `specific_internal_energy` | `J/kg` |
| `temperature` | `K` |
| `time` | `s` |
| `translational_damping` | `N/(m/s)` |
| `translational_stiffness` | `N/m` |
| `velocity` | `m/s` |
| `volume` | `m3` |
| `volume_flow` | `m3/s` |
| `windage` | `N/(m/s)^2` |
新增物理量时先扩展后端受控单位表,并核对共享参数单位表 `schemas/parameter-units.json`、结果页 `RESULT_UNIT_OPTIONS` 及三入口换算测试。目录的 `unit` 只给出基准单位,不会自动产生全部换算选项;例如当前 `time` 参数只显示 `s`,不能假设已支持 `ms`。新增编辑器需同时扩展元数据枚举、注册校验、目录解析和实际控件。禁止在单个模型中私自拼写新的同义 `quantity`。
构造函数必须调用:
```python
self.set_parameter_values(
{
"volume": volume,
"p0": p0,
"T0": T0,
}
)
```
方程和结果输出使用 SI。外部工程 JSON v2 数值、数字字符串、表达式统一使用所选参数单位;网页、HTTP、CLI 在适配层归一化,内核仅接受有限 SI 数字。旧 v1 数值为 SI,导入保留含义后再转换导出 v2。单位源表为 `schemas/parameter-units.json`,详见[参数入口合同](backend-interface-version-spec-v1.md#61-工程存储与执行入口)。
## 9. 结果变量规范
### 9.1 组件级结果
组件自身状态或派生量使用 `ResultVariableDefinition`:
```python
ResultVariableDefinition(
name="pressure_drop",
label="压降",
quantity="pressure",
unit="Pa",
category="derived",
order=10,
)
```
声明为可见的组件输出和活动端口可见输出必须在 C 生成器的输出布局中有对应值。测试应核对实际 EXE 输出键与 `result_variable_metadata()` 一致,不再实现 Python `component_result_values()`。
### 9.2 端口结果
端口结果由 `PORTS` 的端口变量自动产生,不要在 `RESULT_VARIABLES` 中重复声明
`port_a.p`、`port_a.m_flow` 等字段。
### 9.3 禁止暴露的内容
以下内容默认不能作为用户结果:
- 非线性求解器内部未知量索引。
- 缩放残差和迭代缓存。
- 仅用于调试的临时中间值。
- 可以由已有结果稳定推导、但没有明确工程用途的重复字段。
## 10. 显示声明规范
公开模型必须声明 `DISPLAY`:
```python
DISPLAY = ComponentDisplaySpec(
label="示例阻力元件",
library_id="experimental",
category_id="flow",
symbol="generic",
ports=(
PortDisplaySpec("port_a", "left", order=10),
PortDisplaySpec("port_b", "right", order=20),
),
order=90,
)
```
规则:
- `library_id` 必须等于所属库 ID。
- `category_id` 必须存在于所属库的 `categories`。
- `symbol` 是前端图形键,不是模型类型。
- 可复用已存在的图形键;也可使用新的稳定键并暂时回退通用图形,不能据此声称已实现专用图标。
- 只有确实需要专用工程图标时才修改前端图标渲染器。
- `side` 只允许 `left` 或 `right`。
- 旋转和镜像不能改变端口名或物理语义。
- `role="amesimGasMediumDefinition"` 是介质识别元数据,XML/前端和网络的介质定义基类必须配套;不是只设图形角色就能实现新介质。
- 动态端口不是通用目录能力;当前 LMECHN1 存在专用逻辑。新动态型号必须同步有效端口查询、前端布局、参数变更后旧连线处理及往返测试。
## 11. 标准创建入口
`create()` 是注册器创建模型的唯一入口:
```python
@classmethod
def create(
cls,
*,
name: str,
medium: IdealGasMedium,
parameters: Mapping[str, float],
) -> ExampleComponent:
return cls(
name=name,
medium=medium,
coefficient=parameters["coefficient"],
)
```
注册器会在调用前:
1. 补齐默认参数。
2. 拒绝未知参数。
3. 检查有限值和边界。
调用后还会检查:
1. 返回对象类型正确。
2. 实例 `model_type` 与 `MODEL_TYPE` 一致。
3. 实际端口与 `PORTS` 完全一致。
4. 实例保存的参数与规范化参数完全一致。
`create()` 不另造一套默认值或偷偷修改传入参数。构造函数仍须调用 `set_parameter_values()`,并处理必要的跨字段约束;不能以注册器已检查为由删除直接构造所需的校验。XML 要求完整参数,不能把 Python 工厂的补默认行为当成 XML 的规则。
## 12. C 方程实现要求
1. 优先复用现有内核;新公式放入 `native/components/modules/` 合适模块,公共声明放入 `native/include/kernels.h`。增加函数或模块依赖时同步 `native_codegen/modules.py::EXPORTS/DEPENDENCIES`,防止生成代码已有调用但链接遗漏实现。
2. `native/components/kernels.c` 是模块的诊断聚合入口,生产按需构建不直接编译它。新增模块核对聚合包含清单;不要同时链接聚合文件和各模块造成重复定义。
3. 在 `native_codegen/extended.py` 接入端口、介质、状态、初始化、方程、导数、输出与事件。新型号默认走支持它的路径;只有将其纳入 `compiler.py` 紧凑路径时才同步实现该路径,不能只扩充类型集合。
4. 新库还要加入 `extended.py::catalog_contracts()`;核对精确类及 `contracts.py::SUPPORTED_VERSIONS`。版本表代表已实现的能力,不能代替方程或 C 输出映射。
5. 用 `Computation/EvaluationSchedule` 声明实际计算依赖,多输出函数区分读取参数与写出指针;别名和依赖流量的汇总拆开。新增循环需要对应的求解能力,不是图排序成功就一定能求解。
6. 核对 `jacobian.py` 的表达式和状态依赖分析。新函数经审查后才纳入识别集合;分支、投影、多输出依赖必须完整。未知依赖允许保守逐列差分,禁止当常量处理来保留着色。动态模型适用时给生成的 `model`/`model.exe` 传 `--verify-jacobian` 对照;Python 包装 CLI 当前没有该参数。
7. 核对 `tolerances.py` 的量纲尺度:当前 `m/m1/m2` 为 `1e-14`,`x/v` 为 `1e-12`,其他字段默认 `1e-8`。新状态按物理量评估,不能只依名称套用默认值。
8. 当前状态与试探状态分离,无效物性、欠定连接和不收敛明确失败。气动物性复用传递 `NativePropertyCache` 上下文,不跨试算无条件复用旧值。信号跳变和限位接入运行库,不能改物理参数或放宽容差隐藏错误。
9. 保持 SI、端口符号和适用的质量/能量守恒;按需编译及缓存由内容失效机制管理,测试完整模型命中、参数变化、模块变化及遗漏导出错误。
10. C 代码、构建依赖、进程与文件操作遵守[跨平台交付约定](跨平台交付约定.md)。分别记录 Linux 与 Windows 实际结果,不把 Linux 模拟测试写成 Windows 验收。
### 12.1 分段信号的时间事件
新增阶跃、脉冲或循环分段信号时,元数据注册不会自动建立时间事件。除信号值方程外,还须在原生生成器中提供下一事件时间,汇入 `model_next_break(t, end)`。默认接入 `extended.py`;若该型号也走 `compiler.py` 紧凑路径,则两条路径都须实现。连续光滑信号没有这项分段要求;由压力、位置等状态触发的切换需要相应状态事件支持,不能仅凭时间表处理。
- 求值与事件查询必须共享同一套绝对时间边界。可复用 UD00 的 `native_signal` / `native_signal_break`;避免一边以取模判断阶段,另一边独立计算事件。仅新增现有 UD00 实例或修改参数无需再次注册事件。
- 对 `t < end`,返回严格晚于 `t` 的最近边界,超过仿真终点或没有后续事件时返回 `end`。包含开始时刻、阶段端点、周期回绕,以及模型需要定位的斜率变化;同一时刻的零时长阶段按合同合并处理。
- UD00 使用右连续阶段规则:`t < boundary` 属于前一段,`t == boundary` 属于后一段。周期终点统一使用下一周期起点。不能用任意全局 epsilon 把真实边界前的时刻提前切换。
- 求值必须支持试探步、回退和结果重放;不得在普通 `model_eval` 调用中推进全局阶段索引。UD00 当前由时间纯函数查找阶段。需要离散状态的组件,应另行实现可回退、可重放的状态及事件提交机制。
- 运行库按最早边界分段积分,在内部边界左侧结束旧段,再于边界处保留连续状态并重新启动积分。新组件通常复用该流程,不为每个型号新增求解器分支。名义采样时间仍按其实际浮点值求值;“界面显示同一小数”不等于内部时间完全相等。
- 新内核函数同步公共声明及 `native_codegen/modules.py` 导出/依赖。测试边界前一个浮点数、边界本身及后一个浮点数,并覆盖延迟开始、周期回绕、多周期、退化参数和 BDF/RK45 最小系统的结果重放。参照 `tests/test_native_signal_boundaries.py`。
### 12.2 事件输出与重放
原生输出语义 version 2 保留实际时间的规则采样,并自动保存内部时间事件的左侧相邻浮点时刻及事件时刻。已有状态重置事件在当前积分区间可用时保存左邻插值状态,再保存重置后的状态;同一实际时间以后一次接受状态为准。时间序列保持严格递增,样本数量可能超过规则网格点数。初始即时事件不倒填时间,仿真终点不额外制造时间事件。
组件接入已有时间/状态事件接口后无需自行写入输出文件。结果重放必须仅依赖保存时间、完整状态和模型参数;模式影响输出时,必须有可保存、可回退、可重放的表示,不能读取运行结束时的全局模式。接触状态接口同样需要遵守这项约束。相关运行库合同见 [native/README.md](../../native/README.md),测试见 `tests/test_native_output_semantics.py`。
外部对照不能把显示为同一小数时刻当作同一事件侧。当前八路评估以全部分段常值信号核验驱动阶段,所有变量共用同一对保存样本;保留双方真实时间及无法配对记录。相同阶段也不等于实际时间完全相同,高刚度快速过程仍需进一步核验时间差的影响。
### 12.3 LSTP 接触状态事件
LSTP00A 已在两条原生生成路径默认注册:`native_codegen/contacts.py` 发出两端机械速度/位移状态索引、间隙、刚度、阻尼、阻尼距离、力符号模式及原公式运算顺序;`native/runtime/contact_events.h` 在接受步的密集插值上定位间隙过零,并在非负力模式定位接触区内的原始力过零。新增现有 LSTP 实例或修改其参数无需额外注册。
此接口只覆盖已审查的 LSTP 状态形式,不会因新组件名称或元数据相似而自动适用。新增类似组件时,须明确事件函数及依赖、根方向、初始贴边/切触、连续状态是否重置、同时事件和防重复提交规则;生成的事件表达式应与力公式保持一致,包括浮点运算顺序。检测不能悄悄引入整模型 RHS 求值或改变已有雅可比/物性复用。MASS 弹性限位及连续释放仍为独立实验,未在本次加入默认路径。
接触事件的运行期记录只用于定位及防止重复触发,不影响力求值。若新模式需要真正的离散物理状态,应按 12.2 保存和重放,不能借用这个定位记录作为不可重放的力开关。验证应包括解析或独立参考、步长变化、接触与脱离、力截断、无事件轨迹保持和 BDF/RK45;参考 `tests/test_native_contact_events.py`。
## 13. 模型实现示例
可参照 [气瓶声明](../../app/simulation/components/experimental/storage/cylinder.py)、[气腔声明](../../app/simulation/components/amesim/storage/chambers.py)、[C 数值模块](../../native/components/modules/) 与 [系统 C 生成器](../../app/simulation/native_codegen/extended.py)。完整开发顺序见 [注册示例与验证](component-registration-example-v1.md)。
Python `create()` 只创建经校验的描述对象;C `model_init()` 生成质量、能量及机械状态,`model_eval()` 计算导数和输出。两者通过生成的状态/参数布局关联。
## 14. 注册模型
元数据发现这一步,在所属库的 `library.py` 加入类路径;它不替代第 12 节的原生接入:
```python
models=(
# 已有模型
"app.simulation.components.experimental.flow.example_restriction:ExampleRestriction",
)
```
禁止:
- 直接修改 `COMPONENT_MODEL_REGISTRY`。
- 在前端复制参数和端口定义作为正式来源。
- 递归扫描组件目录自动导入所有 `.py`。
- 同时注册两个相同 `MODEL_TYPE`。
- 把测试类、抽象基类或内部算例模型加入公开清单。
## 15. 测试要求
每个公开模型至少添加:
1. 静态契约测试。
2. 默认参数创建测试。
3. 参数边界测试。
4. 端口与显示布局一致性测试。
5. C 方程与独立解析解或冻结参考值对照。
6. 按物理适用性覆盖零流量、反向流动或信号边界;无流量的信号源不套用气动守恒验收。
7. 目录输出测试。
8. 最小 XML 编译测试。
9. 使用当前支持的 C RK45/BDF 做短时仿真,事件按模型适用性验证;XSD 中出现其他方法不代表运行库已支持。
10. 浏览器完整接线工程的导入、编辑、XML、运行、结果保存、CSV 和刷新恢复。后端单元件允许的未接端口警告,在前端可能是阻止运行的错误。
11. 编译/缓存与实际 Windows/Linux 验证;依赖着色的动态模型另有雅可比对照。
推荐先运行:
```powershell
.\.venv-win\Scripts\python.exe -m unittest `
tests.test_component_registry `
tests.test_component_catalog `
tests.test_component_metadata
```
随后按改动范围运行型号数值、网络、原生构建与浏览器测试;涉及共享合同、内核或求解行为时运行完整回归:
```powershell
.\.venv-win\Scripts\python.exe -m unittest discover -s tests
```
目录契约影响前端时还要运行:
```powershell
cd frontend
$env:Path = (Join-Path (Split-Path $PWD) '.tools\node-v24.18.0-win-x64') + ';' + $env:Path
npm.cmd run build
```
## 16. 修改已有模型的安全步骤
1. 找到 `MODEL_TYPE` 的所有 XML、工程和测试引用。
2. 记录修改前的端口、参数、结果和默认行为。
3. 判断版本级别和是否需要迁移。
4. 先增加或修改测试,明确预期物理行为。
5. 修改模型类,不在注册器和前端复制规则。
6. 检查默认实例和旧参数是否仍能创建。
7. 检查最小系统是否仍然闭合。
8. 运行针对性测试,涉及共享机制时完成相应完整回归。
9. 同步本文档或模型专属说明中的物理假设。
## 17. 人工或 AI 的任务输入卡
为了减少猜测,新增模型前建议先填写:
```text
模型中文名称:
MODEL_TYPE:
所属 library_id:
所属 category_id:
物理域:
模型用途和边界:
端口列表及含义:
参数列表、SI 单位、默认值和范围:
状态变量:
代数方程或微分方程:
正流量约定:
需要显示的组件结果:
已知参考模型或工程公式:
最小测试系统:
允许的近似:
明确不实现的能力:
```
如果关键物理信息缺失,AI 应先通过现有模型、测试或用户提供的参考补齐;不能仅凭
组件名称自行创造方程。
## 18. AI 修改协议
AI 创建或修改模型时必须遵守:
### 修改前
1. 读取第 3 节列出的文件。
2. 检查工作区已有改动,不能覆盖无关修改。
3. 明确模型是公开模型还是内部模型。
4. 明确端口物理域、状态、参数、方程和结果。
5. 找到最接近的现有模型并沿用代码风格。
### 修改中
1. 将物理契约保存在模型类中。
2. 只在库清单中登记公开模型。
3. 不修改集中注册表来加入单个模型。
4. 不为了让测试通过而放宽全局校验。
5. 不改变现有模型标识,除非任务明确要求迁移。
6. 不把前端拖拽方向当作物理流向。
7. 不把求解器失败简单隐藏为默认结果。
### 修改后
1. 展示涉及的模型、清单和测试文件。
2. 报告版本变化和兼容性影响。
3. 运行针对性测试和必要的前端构建;共享机制修改运行相应完整后端回归。
4. 检查 `GET /api/components/catalog` 中的模型、分类、端口和参数。
5. 告知用户需要重启 FastAPI 才能加载新的 Python 模块。
6. 未执行的校验必须明确说明原因。
## 19. 常见失败与处理
| 现象 | 常见原因 | 处理 |
| --- | --- | --- |
| FastAPI 启动时报模型缺少声明 | 字段继承自父类或漏写 | 在公开模型类中显式声明 |
| 模型未出现在前端 | 未加入 `library.py` 或后端未重启 | 检查清单并重启 FastAPI |
| 前端显示红色“加载失败” | `/api/components/catalog` 不可用或目录合同无效 | 悬停状态查看详情,再检查 8000 端口和接口响应 |
| 显示端口校验失败 | `DISPLAY.ports` 与 `PORTS` 不一致 | 使用相同端口名和完整集合 |
| 单位校验失败 | `quantity` 与 SI 单位不匹配 | 使用受控单位表或先扩展规范 |
| 默认模型无法注册 | 默认参数越界或构造函数未保存参数 | 修复默认值和 `set_parameter_values()` |
| XML 报不支持模型 | XML `type` 与 `MODEL_TYPE` 不一致 | 修正类型或提供迁移 |
| 模型可显示但无法仿真 | 只完成目录元数据,方程或物理域求解未实现 | 补齐方程、网络和求解测试 |
## 20. 完成定义
一个模型只有同时满足以下条件才算完成:
- 模型契约完整且启动校验通过。
- 默认参数和边界有效。
- 端口、参数和结果具有稳定物理含义。
- 方程覆盖正常、边界与适用的反向流动/事件,并有独立参考。
- 模型已加入正确库清单。
- 目录接口能自动输出模型。
- 前端无需复制参数和端口定义即可使用。
- XML 能映射到正确模型。
- 最小系统能够编译;声称可仿真的模型必须产生有限结果。
- 相应分层测试及必要的构建/回归通过,浏览器能实际运行完整接线案例。
- Windows/Linux 的验证状态明确,尚未实测的平台不能标记为通过。
- 文档记录了模型假设、适用范围和已知限制。