自己写流程模拟软件后,我开始重新理解“算对了”

最近,我在继续开发自己的流程模拟项目 RadishFlow。这一轮最想分享的进展,是一个很小的流程:两股进料进入混合器,再送进闪蒸罐。

学过化工原理的朋友,大概一眼就能看懂这张图。但当我真的把它做成可以编辑、运行、保存、重开的软件,才发现,屏幕上出现几个温度、压力和流量,只完成了其中一部分工作。

一个结果属于哪次输入?参数改了一半能不能保存?连接断了以后,软件能不能把人带到问题现场?这些看似外围的问题,会直接影响我们能否正确理解一次模拟。

今天就借这个已经跑通的小案例,聊聊化工计算、软件状态,以及它们和 AI Agent 的关系。本文对应截至 2026 年 9 月 14 日的开发进展,界面截图来自当前 Studio 实机。

先交代模型边界:目前 RadishFlow 使用演示物性和简化单元模型。本文的算例用于讨论已经实现的算法、数据传递和用户操作,不作为真实装置的设计计算依据。这一点会影响后面每个数字应该怎样解读。

当前已经打通的流程包括进料接闪蒸、进料经加热器、冷却器或阀门再接闪蒸,以及这次重点验证的双进料混合流程。Studio 已经有独立物性页、流程图画布、参数检查器、模块结果和运行诊断区域。

这次 Mixer 案例从普通空白项目开始:选择二元烃 Lite 演示物性包和甲烷、乙烷组分,放置两个 Feed、一个 Mixer 和一个 Flash Drum,再明确提交参数、接受连接建议。当前连接仍是受控建议方式,不能把它理解成任意自由连线已经完成。

在建模过程中,连接建议也有自己的条件。只有一股来源时,软件不会替双入口混合器猜出另一股物料;补齐两个来源后,再给出对应两个入口的建议。连接既决定图上画了什么,也决定求解器会消费哪些输入,所以不能只追求画线顺手,而忽略了连接本身的含义。

两股进料分别设为:第一股 300 K、150000 Pa、2 mol/s,甲烷和乙烷摩尔分率各为 0.5;第二股 330 K、120000 Pa、1 mol/s,甲烷为 0.8,乙烷为 0.2。混合器出口压力显式设为 90000 Pa。

运行后,混合出口总流量为 3 mol/s,甲烷摩尔分率为 0.6。这两个数很容易手算:甲烷进入量是 2×0.5+1×0.8=1.8 mol/s,除以总流量 3 mol/s,便得到 0.6;乙烷相应为 0.4。

温度显示为 310 K,对应当前实现的摩尔流量加权:T=(2×300+1×330)÷3。这里要特别留意模型含义:目前 Mixer 尚未通过总焓平衡反求出口温度。当组成、热容或相态发生变化时,不能直接把这种加权结果解释为严格的绝热混合温度。

对这个小流程而言,我更关心另一个细节:Mixer 产生的出口状态,是否就是下游 Flash 实际拿到的入口状态?如果右侧模块结果显示一组值,底部流股表显示另一组值,即使它们各自看起来合理,这条软件链路也出了问题。

现在的实现会让下游计算、模块结果和结果输出沿用同一份求解数据。回归测试核对 Mixer 的产出与 Flash 的消费引用;实窗导出的混合流股同样是 310 K、90000 Pa、3 mol/s,组成也保持一致。

把第一股流量从 2 mol/s 改成 1 mol/s 后,软件还要处理更完整的一串变化。对应自动化回归中,保存、重开并重新运行后,总流量变为 2 mol/s,甲烷摩尔分率变为 0.65,按当前近似得到的温度变为 315 K。

这里既有物料衡算,也有软件状态验证:输入有没有保存,旧结果有没有失效,新结果有没有送到正确的界面。相同的几个数字,背后其实对应着不同层次的正确性。

我现在判断“算对了”,会先看结果对应的是不是当前输入。

假设一个流程已经运行成功,然后把加热器出口温度从 360 K 改成 365 K。参数已经变了,求解器还没有重跑,此时屏幕里原来的温度和焓值,仍然只属于上一次计算。

RadishFlow 为已提交的项目变更维护文档修订号,并让求解快照关联对应修订。项目继续编辑后,原来的快照会被标记为过期;结果审阅和复制、导出入口不能继续把它当成当前答案。

这件事还延伸到了保存对话框。导出操作开始时有当前结果,不代表选完文件路径、确认覆盖时仍然如此。因此输出执行过程中还会重新核对快照身份和文档修订,拒绝已经过时的请求。

对于操作者来说,它减少了“改完参数却复制了旧答案”的机会。对于程序来说,它把“当前结果”变成了可以检查的条件,而不是界面上的一句提示。每次讨论计算结果,都有了一个明确的输入版本作为起点。

输入框里的内容,也需要有自己的身份。

写界面以前,很容易把参数编辑想成一件简单的事:输入一个数字,更新模型。但真正使用时,我们经常会删掉旧值、输入一半、切到另一个对象,或者临时试一个明显不合理的数。

当前实现区分编辑草稿和正式参数。非法温度草稿不会直接进入文档;尚未提交的内容不会因为点击保存就自动生效。最近补齐的 macOS 撤销与重做,也区分了输入框文本历史和已经提交的文档操作。

阀门压力暴露过一个很具体的问题:草稿是否有效,可能取决于另一个对象。比如出口压力草稿为 95000 Pa,入口压力降低到 85000 Pa 后,它就不符合当前单元约束;入口升到 120000 Pa 后,同一段草稿又可以提交。

这个联动已经修复。上游参数变化后,保留的草稿会重新校验,同时保留用户原来的输入,不自动替用户提交。这种边界很细,却能避免提示已经更新、按钮状态仍然停在旧条件下的混乱。

Mixer 也有同类约束:当前出口压力不能高于两个入口压力的较低值。案例中两股入口分别是 150000 Pa 和 120000 Pa,130000 Pa 的出口压力草稿会被拒绝。这是当前模型明确实现的约束,并不表示复杂管网压降已经求解。

一次运行失败,最好能把人带回可操作的位置。

在双进料验收中,我们主动断开了第二股进料的目标端。重新运行后,诊断指向 Mixer 缺少绑定的入口 inlet_b,恢复入口可以聚焦混合器。随后选择原来的流股,显式重连,再运行,流程恢复成功。

这里的“恢复”有必要说准确。定位到错误对象,并不会让模型自动变正确。软件需要区分只是导航的动作,以及真正修改模型、可以撤销的动作;用户完成修复以后,还需要重跑才能获得当前结果。

目前顶部恢复入口、画布选择、检查器和运行面板已经围绕同一套诊断目标联动。对我而言,这让错误信息变得有用:看到哪个单元、哪个入口存在问题,知道下一步该检查什么,修复以后还能继续原来的工作。

保存与重开也在这条操作链里。当前项目保存的是已经提交的输入;单元位置和视口布局由独立布局文件恢复。Mixer 实窗案例保存后重新打开,会明确显示缺少求解快照,再次运行才形成新的当前结果。

目前结果页可以复制或导出轻量文本,内容包含快照标识、文档修订、流股、单元、步骤和诊断,数值标注 SI 单位。macOS 的对应原生输出路径已经完成实窗验证。这还不是完整报表系统,但已经能把一次计算带出界面进行核对。

这些行为落到代码里,也需要分清职责。当前项目用 Rust 承载对象模型、简化计算和 Studio 界面;对象模型描述流股、单元与端口,求解层负责图校验后的执行与步骤结果。CAPE-OPEN / COM 相关语义则留在 .NET 10 适配层,Rust Core 不直接处理 COM 类型。

对于这个无回路的小流程,求解器按连接关系安排单元执行:先得到两股进料,再计算混合出口,最后进入闪蒸。当前遇到环路会报错,循环物流的收敛算法还没有实现。因此,这里可以讨论的是已经跑通的顺序执行与数据传递,不能由此推导复杂循环流程也已支持。

做完这些,再回头看 AI Agent,我关注的问题也更具体了。

如果将来让 Agent 修改温度,它怎样知道自己改的是草稿,还是已经提交的参数?如果得到一份结果,它怎样判断这份结果属于哪个版本?如果运行失败,它拿到的是一句模糊报错,还是一个可以定位的对象?

这些问题已经能在当前项目的实现中找到一部分基础:明确的文档操作、输入校验、结果修订关系、结构化诊断和恢复目标。它们首先服务普通用户,也为自动化调用提供更清楚的行为边界。

但这里需要把进展说实在:统一变量与动作浏览树、公共 API、操作录制与回放,以及让 Agent 自主完成建模,仍属于后续方向。当前不能宣称已经实现了“对话一句话,自动完成流程模拟”。

这次开发让我形成的判断是,Agent 能否可靠参与模拟,和软件自身能否清楚表达输入、操作、结果与错误有关。一个动作有明确后果、一个结果有明确来源,后续自动化才更容易验证。这个判断来自眼前这些已经完成的小功能。

同样,“已收敛”也要有明确含义。当前热力学实现包含 Antoine 关联式、理想 K 值、Rachford–Rice TP Flash 和常热容显热近似,但仍缺少完整焓参考态、相变潜热和真实 EOS 等能力。

阀门目前保持入口温度并降压,还不是等焓节流的 PH Flash;闪蒸罐在指定温压下进行相分配,也不能据此推导设备满足绝热能量平衡。测试通过可以证明既定算法和软件链路符合预期,工程准确性还需要独立物性来源、适用工况与基准验证。

我也越来越重视验证场景之间的区别。自动化测试适合反复核对参数、连接和结果之间的关系;真实窗口则会暴露入口找不到、对话框没出现、焦点不正确等问题。一个层面的通过,不能自动覆盖另一个层面。这次文章里的数值既有代码和回归依据,也有实际运行画面,二者承担不同的证明任务。

这个阶段的 RadishFlow,已经能让人从空白项目搭起一个小流程,明确输入,找到连接问题,运行查看结果,再保存重开继续计算。我愿意把这些具体进展写出来,因为它们让“自己做一个流程模拟软件”变成了可以逐项检查的工作。

接下来继续增加单元和物性模型时,我也会沿用这个检查顺序:参数到底是什么,计算采用什么假设,结果从哪里来,失败以后怎样继续。把这些问题回答清楚,画布上的每一个设备,才算真正成为软件中可以使用的一部分。