Files
SystemSimulationApp/docs/system-xml-v2.md
T

13 KiB
Raw Blame History

System XML v2 协议

System XML v2 是 ReactFlow 建模前端与 app.simulation 仿真层之间的交换格式。v2 将物理端口与信号端口分开,并移除了物理连接中的方向语义。

基本约定

  • 根元素 System 的 schemaVersion 固定为 2,unitSystem 固定为 SI。
  • 新导出的介质引用语义使用可选根属性 mediumReferenceVersion="1"。缺少该属性的文档按旧版介质索引规则读取。
  • 子元素顺序固定为 Simulation、Components、Connections。
  • Component.id 是稳定实例 ID,name 是用户可编辑名称。
  • 参数以 SI 基准值保存;显示单位不改变 XML 中的数值含义。
  • 物理端口统一规定 m_flow > 0 表示流入组件,m_flow < 0 表示流出组件。
  • nominalRole 只表示设计意图和展示语义,不限制实际流向。
  • v2 文档不携带仿真结果;端口实际流向、流量幅值和累计质量由运行结果接口返回。

完整示例

<?xml version="1.0" encoding="UTF-8"?>
<System name="transfer-system" schemaVersion="2" unitSystem="SI" mediumReferenceVersion="1">
  <Simulation tStart="0" tStop="2" step="0.1" maxStep="0.005" method="BDF"/>
  <Components>
    <Component id="cylinder_1" name="cylinder_1" type="cylinder" componentType="cylinder" x="90" y="180" rotation="0" mirrored="false">
      <Port name="port_b" kind="physical" domain="pneumatic" nominalRole="outlet" positiveFlowDirection="intoComponent" side="right"/>
      <Parameter name="volume" value="0.01"/>
      <Parameter name="p0" value="35000000"/>
      <Parameter name="T0" value="300"/>
    </Component>
    <Component id="tank_1" name="tank_1" type="tank" componentType="tank" x="420" y="180" rotation="0" mirrored="false">
      <Port name="port_a" kind="physical" domain="pneumatic" nominalRole="inlet" positiveFlowDirection="intoComponent" side="left"/>
      <Parameter name="volume" value="0.1"/>
      <Parameter name="p0" value="100000"/>
      <Parameter name="T0" value="300"/>
    </Component>
  </Components>
  <Connections>
    <Connection id="edge-1" kind="physical" domain="pneumatic">
      <Endpoint component="cylinder_1" port="port_b"/>
      <Endpoint component="tank_1" port="port_a"/>
    </Connection>
  </Connections>
</System>

Port

组件的 rotation 只能为 0/90/180/270,mirrored 表示水平镜像。它们只用于恢复画布布局和端口显示位置,不参与物理方程或流向判断。

属性 含义
name 组件模型中的稳定端口名
kind physical 或 signal
domain 端口物理域,当前流体组件使用 pneumatic
nominalRole 物理端口使用 inlet/outlet/bidirectional;信号端口使用 input/output
positiveFlowDirection 物理端口固定为 intoComponent;信号端口省略
side 前端图标上的 left/right 布局位置,不参与物理求解

物理端口的流向由求解结果决定。一个名义出口的 m_flow > 0 表示该端口发生实际流入,可在结果层标记为倒流。

三通的三个端口当前保留仿真模型已有名称 port_in/port_out1/port_out2,但全部声明为 bidirectional,名称不构成方向约束。

Connection

物理连接包含两个无序 Endpoint。第一个端点不代表上游,第二个端点也不代表下游;交换二者顺序不得改变仿真结果。

信号连接也使用两个 Endpoint,但必须分别携带 role="source" 和 role="target"。信号端口只允许 output 与 input 相连。

连接生成前必须验证:

  • 组件和端口存在。
  • 两端 kind 相同。
  • 两端 domain 相同。
  • 信号连接一端为 output,另一端为 input。

v1 迁移

  • v1 的字符串端口在加载时按组件注册表迁移成 v2 端口对象。
  • v1 的 source/sourcePort/target/targetPort 在导出 v2 时转换为两个 Endpoint。
  • 物理连接不继承 v1 的 source/target 方向。
  • v1 文件仍由原 XSD 描述;新生成文件只输出 v2。

XSD 负责结构和基础枚举校验,端口注册、拓扑完整性与可求解性由模型校验层负责。

AMESim 介质定义与索引兼容

AMESim 介质定义继续使用普通的零端口 Component,不增加新的 XML 层级。其介质索引和物性选择仍以数值 Parameter 保存,例如:

<Component id="air_1" name="Air 1"
           type="amesim_ideal_air_medium"
           componentType="amesim_ideal_air_medium"
           x="40" y="40">
  <Parameter name="gi" value="1"/>
  <Parameter name="property_model" value="0"/>
</Component>

氦气 Peng-Robinson 定义使用相同结构:

<Component id="helium_1" name="Helium 1"
           type="amesim_helium_medium"
           componentType="amesim_helium_medium"
           x="40" y="140">
  <Parameter name="gi" value="2"/>
  <Parameter name="property_model" value="0"/>
</Component>

这里的 property_model=0 是氦气组件内部的 Peng-Robinson 选项编号;编译时 同时保留源 AMESim fluidType=12/eosType=6 元数据,不能把两个编号体系混用。

气动组件的 gi=0 表示内置的空气理想气体;正整数引用画布中的显式介质 定义。介质定义的 property_model 是该介质内部的物性计算模型编号,当前 空气定义中的 0 表示理想气体。界面中的 gi 下拉栏只显示索引数值, property_model 下拉栏的名称和可选值则来自组件目录。

解析旧文档时,只有同时满足以下条件才执行旧 gi 兼容转换:

  • 根元素没有 mediumReferenceVersion;
  • 所有组件类型均可由当前注册表识别;
  • 画布中不存在目录角色为 amesimGasMediumDefinition 的组件。

在这种可证明没有显式介质定义的旧文档中,缺失的 gi 会补为 0,旧 gi=1 会映射为 0,并返回语义层警告。带有 mediumReferenceVersion="1" 的新文档不会重解释正整数索引。

在物性计算模型下拉接口加入之前保存的介质定义可能只有 gi。无论是否存在 mediumReferenceVersion,解析器都会为缺失的 property_model 注入组件目录 声明的默认值,并返回 AMESIM_GAS_PROPERTY_MODEL_DEFAULTED 警告;重新保存后 该参数会显式写入 XML。

完成兼容转换后,解析器统一按介质引用版本 1 执行以下语义校验:

  • 介质定义组件的 gi 必须是 1..99 的整数,且工程内不得重复。
  • 气动组件的介质引用必须是 0..99 的整数。
  • gi=0 始终引用内置空气理想气体;所有正索引必须存在对应介质定义。
  • 同一个气动连通分量内只能使用一个 gi,不同的独立气动网络可以选择不同 介质。

相应错误码为 AMESIM_GAS_MEDIUM_INDEX_INVALID、 AMESIM_GAS_MEDIUM_INDEX_DUPLICATE、 AMESIM_GAS_REFERENCE_INDEX_INVALID、AMESIM_GAS_REFERENCE_UNDEFINED 和 AMESIM_GAS_REFERENCE_CONFLICT。XML 解析得到的规范化工程 JSON 总是输出 数值字段 "mediumReferenceVersion": 1;因此旧文档一经解析并重新保存,便 不再依赖旧版启发式迁移。

仿真模型编译接口

POST /api/reactflow/compile-model 接收与工程保存、XML 导出相同的 ReactFlow 工程 JSON。它会执行以下操作:

  1. 按 node.data.modelType 创建 app.simulation 组件实例,并写入 SI 参数。
  2. 将前端端口声明与组件注册端口逐项比对。
  3. 按画布实际 edges 创建无方向物理连接,而不是按组件类型或拖入顺序推断拓扑。
  4. 检查端口存在性、物理域兼容性、重复连接和未连接端口。

成功响应中的物理连接只包含两个 endpoints,不包含 source/target:

{
  "success": true,
  "name": "transfer-system",
  "components": [
    {
      "id": "cylinder_1",
      "type": "cylinder",
      "ports": [
        {
          "name": "port_b",
          "kind": "physical",
          "domain": "pneumatic",
          "nominalRole": "outlet",
          "positiveFlowDirection": "intoComponent",
          "variables": [
            {"name": "p", "role": "effort", "connectionRule": "equal"},
            {"name": "m_flow", "role": "flow", "connectionRule": "sumToZero"},
            {"name": "h_outflow", "role": "stream", "connectionRule": "streamMix"}
          ]
        }
      ]
    }
  ],
  "connections": [
    {
      "id": "edge-1",
      "kind": "physical",
      "domain": "pneumatic",
      "endpoints": [
        {"component": "cylinder_1", "port": "port_b"},
        {"component": "tank_1", "port": "port_a"}
      ]
    }
  ],
  "unconnectedPorts": []
}

一个物理端口当前只允许一条连接;需要分支时必须显式放置 Tee 等结点组件。这样拓扑不会通过“一个端口连多条线”隐式产生结点方程。

此接口完成模型实例化、端口契约校验、拓扑编译和压力-流量方程结构组装。编译结果中的 pressureFlowSystem 包含未知量、方程、数量及 isSquare 状态;方阵只表示结构数量平衡,不代表方程一定可解。

当前组件已提供可执行残差:气瓶和贮箱提供状态-压力约束,孔板提供流量守恒和压差-流量本构关系,三通提供等压零结点和流量守恒。XML 注册表中的管段使用准稳态 Darcy 阻性模型,同时提供流量守恒和双向压降关系。连接层根据端口契约生成 p 相等及 m_flow 代数和为零的残差。

/api/reactflow/simulate-testmodel 继续保留固定 TestModel 和动态管段,用于已有基线对比。XML 驱动仿真使用独立的通用半显式求解链路,不调用固定 TestModel 闭合器。

第二阶段:XML 解析与校验

第二阶段已经实现从 System XML v2 回到仿真网络的完整入口。解析过程固定分为三层:

层级 layer 负责内容
XML xml 文档大小、XML 语法、禁止 DTD 和实体声明
XSD schema v2 版本、元素顺序、必填属性、枚举、基础数值类型
模型语义 semantic 组件注册、端口契约、参数集合和范围、端点引用、物理域、连接占用及仿真设置

校验诊断统一包含:

{
  "severity": "error",
  "layer": "semantic",
  "code": "ENDPOINT_PORT_UNKNOWN",
  "message": "Connection edge-1 references unknown port tank_1.port_x.",
  "path": "/System/Connections/Connection[1]/Endpoint[2]",
  "line": 18
}

未连接端口使用 PORT_UNCONNECTED 警告,不会阻止解析和网络编译;结构错误、接口不一致、参数错误和非法拓扑会使 valid=false。

API

四个接口均直接接收 Content-Type: application/xml 的原始 XML 请求体:

  • POST /api/system-xml/validate:无论成功与否都返回校验报告,便于编辑器实时显示问题。
  • POST /api/system-xml/parse:成功时返回规范化工程 JSON;失败时返回 HTTP 422 和结构化诊断。
  • POST /api/system-xml/compile-model:成功时返回仿真网络、仿真设置和校验报告;失败时返回 HTTP 422。
  • POST /api/system-xml/simulate:完成校验、编译、仿真准备、代数闭合、stream 传播和时间积分;成功时返回组件与端口时间序列,失败时返回 HTTP 422 和仿真层诊断。

示例:

Invoke-RestMethod `
  -Method Post `
  -Uri http://127.0.0.1:8000/api/system-xml/validate `
  -ContentType application/xml `
  -InFile .\test\system.xml

组件参数和端口定义集中在 app/simulation/registry.py。ReactFlow JSON 编译和 XML 语义校验共用该注册表,新增组件时必须先在这里登记参数范围、默认值和端口契约。

第三阶段:XML 驱动仿真 MVP

第三阶段当前已经打通:

  1. XML 中的组件、参数和无方向物理连接编译成 app.simulation 网络。
  2. 仿真准备层检查未连接端口、方程数量、无储能代数孤岛和无阻力储能直连。
  3. SciPy 非线性最小二乘求解每个时刻的端口压力与质量流量。
  4. 根据求解后的实际流向迭代传播 h_outflow,并在三通处执行质量流量加权混合。
  5. 动态组件自动拼装质量及内能导数,使用 XML 的 tStart/tStop/step/maxStep/method 开展积分。
  6. 结果包含动态组件的 m/U/p/T/rho/u/h,以及全部物理端口的 p/m_flow/h_outflow 时间序列。

当前限制:

  • 只支持注册表中的气动物理组件,不支持信号端口仿真。
  • 所有物理端口在运行前必须完成连接;分支必须显式使用三通。
  • 每个独立物理网络必须包含至少一个气瓶或贮箱作为压力和焓的储能锚点。
  • 两个储能组件不能通过理想连接或纯三通直接耦合,必须在中间放置孔板或管段。
  • XML 管段当前是准稳态阻性元件,p0/T0 用于名义密度和初始代数猜测,不包含管内储气动态。
  • 当前 stream 混合是适合 MVP 的正则化近似,还不是 Modelica inStream/actualStream 的严格复刻。
  • 当前是半显式 ODE/代数求解链路,不支持一般高指数 DAE 和事件系统。

运行示例:

Invoke-RestMethod `
  -Method Post `
  -Uri http://127.0.0.1:8000/api/system-xml/simulate `
  -ContentType application/xml `
  -InFile .\test\system.xml