Skip to content

结果契约:solve 与 simulate 返回什么 ​

一次性调用的公开面只有两个 JSON 信封:esml.solve/v1 与 esml.simulate/v1。 本文说明每个字段的含义、单位约定、成功与失败语义。客户端(Python / Julia) 只是这两个信封的薄封装;直接调命令行与走客户端拿到的是同一份契约。

机器可校验的 JSON Schema: esml-result-v1.json。

契约遵循只增不改:字段只会增加,不会改名或删除;破坏性变更才升 apiVersion。同一份源文件 + 同一 engineVersion + 同一输入,结果可复现。

稳态:esml solve --json ​

bash
esml solve library/domains/thermal/tclab.comp.esml -L library --json
json
{
  "apiVersion": "esml.solve/v1",
  "system": "Demo",
  "unknowns": 2,
  "values": [
    { "path": "tclab.T1", "value": 339.0071428571428, "unit": "K" },
    { "path": "tclab.T2", "value": 303.29285714285714, "unit": "K" }
  ],
  "meta": {
    "engineVersion": "1.9.0",
    "docHash": "612e8b852339681c6a6b62266240d1f13367dc44945c3c8a32058cfc1bdf13f3",
    "durationMs": 0.423,
    "converged": true,
    "iterations": 2,
    "residual": 1.734723475976807e-16,
    "residualTolerance": 3.400071428571428e-08,
    "unitCoverage": { "known": 2, "total": 2, "complete": true }
  }
}
字段类型含义
apiVersionstring固定 esml.solve/v1;客户端据此拒绝未知信封
systemstring求解的 system 名
unknownsint展平后原始变量数(含状态与代数变量)
values[]array按展平顺序稳定输出,每项一个变量
values[].pathstring实例路径,例如 tclab.T1、b1.net.Vm
values[].valuefloat数值;声明了单位时已换算到一致 SI 单位,未声明时保持模型原值
values[].unitstring | nullSI 量纲表达式(如 K、kg/m^3、kg*m/s^2);无量纲为 "1";模型未声明单位为 null
meta.engineVersionstring产生该结果的引擎版本
meta.docHashstring源文件逐字节的 SHA-256(64 位十六进制),可作缓存/归档键
meta.durationMsfloat引擎侧计算耗时(毫秒,含重构结果;不含进程启动与读文件)
meta.convergedbool成功信封中恒为 true;未收敛不会返回信封,而是非零退出
meta.iterationsint | null产出该结果的 Newton 迭代次数(homotopy 各步累加;失败的鲁棒性尝试不计);--component 路径为 null
meta.residualfloat | nullNewton 接受解时的残差 ∞-范数
meta.residualTolerancefloat | null该残差通过的门限(TOL·(1+‖u‖∞));residual ≤ residualTolerance;DDM / component 路径不适用时为 null
meta.unitCoverageobjectknown 个未知量有单位、total 个未知量;complete 为二者相等(simulate 按输出列计)

单位语义 ​

  • 声明了单位的变量,返回值换算到一致 SI 单位:K、Pa、kg/s、W…… 不是作者写的显示单位(°C、bar、kW)。需要显示单位时在客户端换算。
  • 未声明单位的变量保持模型原值,unit 为 null;它可能不是 SI(例如 标幺值)。不要把它当作 SI 数值直接做物理量纲运算。
  • unit 是量纲表达式,不是单位名字。声明为无量纲的量(包括标幺值、 摩尔分数)显示为 "1";未声明的仍为 null。
  • unit: null 表示模型没有声明单位,引擎不猜。此时数值仍是模型原值。 给变量或类型补上 unit= 后,同一份结果就会带单位。
  • IEEE 300 潮流算例的 PowerPort 已声明 Vm/P/Q 为标幺(unit="1")、 Va 为弧度(量纲同为 "1"),因此 4,604 个值全部带单位、 unitCoverage.complete 为 true;数值本身不变。
  • unitCoverage 让调用方一眼判断结果能否直接进入带单位的后处理;不要用 unit: null 的数值去猜物理量。

时域:esml simulate --json ​

bash
esml simulate library/domains/thermal/tclab.comp.esml \
  --tspan 0,10 --steps 2 -L library --json
json
{
  "apiVersion": "esml.simulate/v1",
  "system": "Demo",
  "steps": 2,
  "points": 3,
  "columns": ["tclab.T1", "tclab.T2"],
  "units": { "tclab.T1": "K", "tclab.T2": "K" },
  "t": [0.0, 5.0, 10.0],
  "rows": [
    [296.15, 296.15],
    [297.3636208318605, 296.15589136326145],
    [298.55347675938515, 296.16555562766]
  ],
  "meta": {
    "engineVersion": "1.9.0",
    "docHash": "612e8b852339681c6a6b62266240d1f13367dc44945c3c8a32058cfc1bdf13f3",
    "durationMs": 0.59,
    "unitCoverage": { "known": 2, "total": 2, "complete": true }
  }
}
字段类型含义
apiVersionstring固定 esml.simulate/v1
stepsint积分步数(区间数);points = steps + 1
pointsint输出点数,等于 t 与 rows 的长度
columnsstring[]每列对应的变量路径,顺序与 rows[] 一致;默认是状态变量,代数变量用 --observe 指定
unitsobject列名 → SI 量纲表达式;只列出有单位的列
tfloat[]时间网格,单位秒
rows[][]float与 columns 等长的数值行
metaobject版本、docHash、耗时、单位覆盖率;时间积分要么完成要么报错,因此没有 converged

--observe 指定的变量也会进入 columns,并在 units 中给出单位;观测常量 参数时同样返回该参数声明的单位。找不到的路径列输出 null,并在 stderr 给出 warn:。

失败语义 ​

契约只有成功信封。以下情况一律非零退出,stderr 给可操作的错误信息, stdout 不输出半个信封:

  • 解析 / 结构 / 单位检查失败;
  • 求解未收敛、奇异 Jacobian、非有限残差;
  • 学术版超出未知量上限,或调用商业版能力(DTIL session);
  • 文件不存在、库找不到、物性后端缺失。

因此客户端不需要判断“结果是否可信”:拿到信封就是成功结果,拿不到就是 带原因的失败。Python 客户端抛 EsmlError(超时抛 EsmlTimeout);Julia 客户端抛 EsmlError。

失败也是契约:esml.error/v1 ​

带 --json 的命令失败时,stdout 仍为空;stderr 先给人类可读的 error: 行, 最后一行是机器可读信封:

json
{
  "apiVersion": "esml.error/v1",
  "code": "io.read",
  "message": "read …: No such file or directory",
  "hint": "Check the path … and its permissions.",
  "docHash": null,
  "engineVersion": "1.9.0"
}
code含义典型处理
license.size学术版超过 16,384 展平未知量缩小模型或联系商业授权
license.dtil学术版调用了商业版 DTIL 会话改用一次性调用或商业版
io.read文件不存在/不可读/权限不足检查路径与权限
solve.failed求解未收敛、奇异、非有限残差读 message 的阶段与原因
model.parse解析/语法错误跑 esml check
model.structure结构分析失败读 struct[...] 诊断
usage子命令/参数用法错误修命令行
engine.error其他引擎错误(兜底)读 message/hint

message 是权威细节,code 只用于分类;不要解析 message 做分支。 未知 code 按 engine.error 处理。docHash 在源文件已读入后可用,否则为 null。Python 客户端抛 EsmlError 并带 .code / .hint / .doc_hash; Julia 客户端同名字段。会话协议的错误信封(esml.session/v1、op:"error") 同样带 code / hint;会话启动阶段的拒绝(学术版 DTIL、文件不可读)也先输出 一行这样的信封,再非零退出。

常驻会话(商业版)的对齐 ​

esml.session/v1 的 solve 响应与一次性契约对齐:

  • 每个值带 unit,并新增 units(路径 → SI 量纲);
  • meta 含 engineVersion、docHash(源文件 SHA-256)、durationMs、 converged、iterations、residual、residualTolerance、unitCoverage;
  • simulate / predict 的每个 series 带 unit,并新增 units 与 meta (无 converged);
  • open / info 带 sourceHash 与 engineVersion。

顶层数值 docHash 仍是会话快照/恢复的协议令牌;meta.docHash 是源文件的 SHA-256。Python Session.solve() 返回 SolveResult.units / .meta, simulate / predict 返回 Trajectory.units / .meta;Julia 同名。

版本与可复现性 ​

  • engineVersion 是产生结果的引擎版本;升级引擎后请重新基线化数值。
  • docHash 是源文件的 SHA-256。它不包含 --system、库文件、参数覆盖或引擎 版本:完整复现键请组合 (docHash, engineVersion, system, 输入覆盖)。
  • 同一构建、同一输入的结果是确定的;跨平台可能有末位浮点差异。

客户端字段 ​

Python 的 SolveOutput / SimulateOutput 与 Julia 的 SolveOutput / SimulateOutput 已暴露这些字段:

客户端字段对应信封
valuesvalues[].value(按路径索引)
unitsvalues[].unit(solve)或 units(simulate)
metameta
raw完整信封,保留未来新增字段

示例:

python
from esml import solve

out = solve("tclab.comp.esml", libraries=["library"])
print(out.values["tclab.T1"], out.units["tclab.T1"])   # 339.007... K
print(out.meta["converged"], out.meta["unitCoverage"])
julia
using ESML

out = solve("tclab.comp.esml"; libraries = ["library"])
println(out.values["tclab.T1"], " ", out.units["tclab.T1"])
println(out.meta["converged"], " ", out.meta["unitCoverage"])

已知边界 ​

  • 常驻会话(DTIL:session / predict / snapshot)是商业版能力,协议是 行式 esml.session/v1;结果字段已与一次性契约对齐,见上一节。
  • solve --ddm(域分解)仍在 esml.solve/v1 下,额外带 ddm 块; values[] 的 unit 与 meta 已补齐,residualTolerance 不适用于 DDM (其收敛判据由分区协调器给出)。
  • durationMs 是引擎侧耗时,不含 Python/Julia 进程启动与 JSON 解析;端到端 性能请以客户端实测为准。
  • iterations 只统计产出结果的收敛 Newton(homotopy 各步累加);失败后 换一条鲁棒性路径的尝试不计入,因此它不是“总共试了多少次”的计数器。
  • converged 在成功信封中恒为 true;若未来支持“返回未收敛迭代点”,那会 改变成功信封的含义,将作为新语义引入并升 apiVersion。

陕 ICP 备 19000424 号 -2 | 陕公网安备 61010302001034 号