PharmaPy 干法制粒二次开发教程(3):对象模型与 flowsheet 机制——SimulationExec、流程拓扑串与单元装配

发布时间:2026/10/16 12:05:20

PharmaPy 干法制粒二次开发教程(3):对象模型与 flowsheet 机制——SimulationExec、流程拓扑串与单元装配 PharmaPy 干法制粒二次开发教程3对象模型与 flowsheet 机制——SimulationExec、流程拓扑串与单元装配版本声明块工具/软件CryPTSys/PharmaPySimulationExec执行器语言/环境Python 3.9 numpy2 scipy1.14 assimulo3.4.3本文目标让你把一个单元 一个类实例再升格为一条流程 一串拓扑 一套属性注入跑通官方 guide 的 R01→HOLD01→CR01→F01 四单元流程并看清今后如何把干法制粒单元插进同一套插座。一句话结论PharmaPy 用SimulationExec(path_phys, flowsheetR01 -- HOLD01 -- CR01 -- F01)的--字符串定义流程拓扑单元以类实例挂到sim并注入Utility/Kinetics/Inlet/Phases属性完成装配SolveFlowsheet(kwargs_run...)按每单元runtime/sundials_opts求解结果经DynamicResult.time/result.字段与GetStreamTable(basismass|mole)读取。〇、本篇要解决的认知问题学完前两篇你已经会搭建环境、跑通官方测试能确认一个批式反应器能求解。但跑单元和跑流程之间隔着一层框架的装配心智。本篇要回答四个问题PharmaPy 为什么用对象 拓扑字符串来描述流程而不是一个写死的大模型输入文件一个单元在sim上到底是怎么被喂饱的——Utility、Kinetics、Inlet、Phases四个属性分别装什么、各自的赋值时机flowsheetR01 -- HOLD01 -- CR01 -- F01这条串在SimulationExec里会被解释成什么缓冲罐、结晶器、过滤器在串中的角色有何不同求解器和结果到底长什么样——SolveFlowsheet(kwargs_run...)怎么传每单元的求解参数DynamicResult与GetStreamTable如何取数这四个问题一旦通透篇 5 你要新增的RollerCompactor/Comminution/Classifier就只是再造一个类 改一行拓扑串的工作。一、机制解析为什么是对象 拓扑串PharmaPy 的定位是pythonic——官方仓库 README 原话是让用户能simulate the dynamics of standalone, drug substance unit operations也能setting up and simulating pharmaceutical flowsheets, i.e., interconnected unit operations。它的立身哲学不是喂一个总装脚本而是每个单元操作就是一个普通 Python 类挂在你创建的仿真执行器对象上再用一个字符串描述它们之间的连接。这套设计有三个现实收益增量构建。你可以先单独构造一个单元、只喂必要属性求解看行为满意后再把下一个单元挂上去。单元的独立性和连接性被解耦。二次开发时我们新写的RollerCompactor可以先脱离流程单独跑再接入拓扑串——这正是调试的最小闭环。属性注入赶走构造函数地狱。一个BatchCryst的全参数签名有十几项target_comp、method、scale、vol_tank、controls、adiabatic、h_conv……但真正随时间变化的边界条件冷却水、进料流股、动力学、初始相并不宜全塞进构造函数。PharmaPy 用sim.单元名.Utility ...这种赋值即注入的方式把过程对象和配置属性分开类型是sim上挂的类配置是后面逐项点的属性。想让单元换一种水浴改一行赋值即可不碰构造函数。拓扑可读、可审计。R01 -- HOLD01 -- CR01 -- F01一行就把链条画完设备和物流的上下游关系一目了然且F01这类带别名Its Process的单元还能在同一流程里做替代对比。对干法制粒的三段式辊压→粉碎→分级这样一行拓扑就是自文档。上图中的Feeder → Roller Compactor → Mill → Classifier → 颗粒产品正是我们要在 PharmaPy 上用同类拓扑串FEED -- RC -- MILL -- CLASS -- OUT二次开发出来的流程——先把三段式画在拓扑上再逐段填动力学模型篇 6~12。价值锚点本篇顺完官方 guide 的反应器→缓冲罐→结晶器→过滤器链条就等于把框架默认的插座摸清了。干法制粒所有单元——无论新增多少——最终都插进这同一个插座先建模成类再sim.Xxx 类(...)再点属性再进拓扑串再求解。形式不变变的只是动力学。属性注入模式的价值看官方 guide 的写法sim.R01建好后并不是一条config字典而是连续四行挂属性sim.R01PlugFlowReactor(diam_in,num_discr50,isothermalFalse)sim.R01.Utilitycw# 冷媒冷却水sim.R01.Kineticskinetics# 反应动力学sim.R01.Inletliquid_in# 进料流股动态边界sim.R01.Phasesliquid_init# 初始相罐内初始状态这四条属性对应单元操作的四类输入来源它们合起来才是单元的完整装配Utility公用工程把过程参数温度设定、流速从物料动力学里剥离出来。干法制粒中这正是辊压机辊温/冷却回路的位置。Kinetics微观速率决定反应或结晶动力学。干法制粒里对应压缩性指数、破碎选择函数/破碎函数——我们篇 6~11 落地的东西最终都从这里塞进去。Inlet动态进料边界。Phases初始相含固相 PSD。这是唯一与相有关的输入也是干法制粒数字孪生最需要精耕的字段——SolidPhase的distrib/x_distrib就是 PBM 状态向量。判定法则凡是这个单元在求解开始时需要先知道的状态塞Phases求解过程中持续驱动的外部量塞Utility/Inlet/Kinetics。理解这一点篇 4 讲SolidPhase的矩/离散双表示时你就不会迷路。二、完整代码与逐行剖析下面给出官方 guidePharmaPy_guide.html风格的最小可运行 flowsheet一列管式反应器R01、一个动态缓冲罐HOLD01、一个1D-FVM结晶器CR01、一个过滤器F01。path_phys指向官方 data 目录中的物性数据。API 签名逐字摘自官方 guide 与 readthedocs。importnumpyasnpfrompharmapy.SimExecimportSimulationExecfrompharmapy.ReactorsimportPlugFlowReactorfrompharmapy.CrystallizersimportBatchCrystfrompharmapy.PhasesimportLiquidPhase,SolidPhasefrompharmapy.StreamsimportLiquidStreamfrompharmapy.KineticsimportRxnKineticsfrompharmapy.UtilitiesimportCoolingWaterfrompharmapy.SolidLiquidSepimportDynamicCollector,Filter# ---------- 0. 物性数据目录data/ 下 JSON ----------path_physpath/to/PharmaPy/data# ---------- 1. 定义流程拓扑串 ----------graphR01 -- HOLD01 -- CR01 -- F01# 先建执行器再往 sim 上挂单元simSimulationExec(path_phys,flowsheetgraph)# ---------- 2. 通用工程 / 动力学 / 流股 / 相 ----------# 冷却水R01 恒温外套水源cwCoolingWater(mass_flow5.0,temp_in298.15)# 一级不可逆反应动力学A - CkineticsRxnKinetics(rate_constants{k1:1e-4})# 反应器进料以摩尔浓度给出liquid_inLiquidStream(path_phys,temp_in298.15,mole_conc{A:2.0,C:0.0})# 反应器初始相批充料liquid_initLiquidPhase(path_phys,init_temp298.15,mass_frac{A:1.0},vol1e-3)# ---------- 3. 逐单元装配 ----------sim.R01PlugFlowReactor(diam_in0.05,num_discr50,isothermalFalse)sim.R01.Utilitycw sim.R01.Kineticskinetics sim.R01.Inletliquid_in sim.R01.Phasesliquid_init sim.HOLD01DynamicCollector()# 缓冲罐无需额外属性# 结晶器1D-FVM PBMscale1e-9 配合 µm 级固相网格x_grnp.geomspace(1,1500,num35)# 对数等距 35 bin[1, 1500] µmdistrib_initnp.zeros(35);distrib_init[10]1e9# 一个种子 binsolid_crySolidPhase(path_phys,x_distribx_gr,distribdistrib_init,mass_frac[0,0,1,0,0])sim.CR01BatchCryst(target_compC,method1D-FVM,scale1e-9,controls{temp:298.15})sim.CR01.Phasessolid_cry# 过滤器直径 / 阻挡系数 / 膜阻sim.F01Filter(diam0.1,alpha1.0,Rm1e12)# ---------- 4. 逐单元求解参数 ----------runtime_reactor3600.0# sruntime_cryst7200.0# srun_kwargs{R01:{runtime:runtime_reactor},HOLD01:{runtime:runtime_reactor},CR01:{runtime:runtime_cryst,sundials_opts:{maxh:60}},F01:{runtime:runtime_cryst},}sim.SolveFlowsheet(kwargs_runrun_kwargs)# ---------- 5. 取结果 ----------res_crysim.CR01# 该单元自带 resultplotsim.CR01.plot_profiles()# 矩 μ0..μ3、温度、浓度/过饱和度图tres_cry.result.time# 时间向量mu0res_cry.result.moments[:,0]# 0 阶矩总粒子数密度演化stream_tablesim.GetStreamTable(basismass)# 全流程流股表pandasstream_table.head()逐段剖析步骤 0-1SimulationExec(path_phys, flowsheetgraph)是流程的总指挥。graph里的--即物流指向SimulationExec会在求解时按此构建连接关系。步骤 2所有输入材料在挂单元之前先构造好。注意CoolingWater(mass_flow..., temp_in...)、LiquidStream(path_phys, ...)都带temp级参数——这些是实时边界和步骤 3 里谁用它们完全解耦。步骤 3装配点。sim.R01 PlugFlowReactor(diam_in..., num_discr50, isothermalFalse)先建空槽再用Utility/Kinetics/Inlet/Phases逐项点属性填满。sim.HOLD01 DynamicCollector()是缓冲罐只需要挂上即可。结晶器BatchCryst(target_compC, method1D-FVM, scale1e-9, ...)其Phases装的是SolidPhase——这个scale1e-9与x_gr np.geomspace(1, 1500, num35)的 µm 级网格配合PharmaPy 内部把 µm 基准矩换算成米基准体积分数时乘以(1e-6)**conv_exp。sim.F01 Filter(diam, alpha, Rm)则是管线式单元。步骤 4run_kwargs的键名 拓扑串里的单元名每个值为该单元的求解参数字典runtime是仿真时长sundials_opts{maxh: 60}限定了吃紧单元的最大步长结晶这类刚性系统常需要。步骤 5剖面型结果sim.CR01.plot_profiles()直接出图result.time给时间向量result.moments之类的字段按绝对值取状态GetStreamTable(basismass)汇总全流程所有流股的 pandas 表格temp/pres/mass/moles/vol/mass_frac/vol_flowbasis可换mole。关键sim.CR01既是执行器上的一个槽求解后它自己同时携带result对象。结果对象在框架里由DynamicResult.__dict__.update(**results)填充result.time与result.field都能直取。所以你要取某个单元的剖面就sim.单元名.result.字段。三、常见报错与排查报错 1拓扑串格式写错——sim SimulationExec(path_phys, flowsheetR01--CR01)现象解析期即报流程解读失败。根因flowsheet需要把节点名用空格包好的箭头--分隔裸--或不带空格往往无法正确切分出节点。解法统一写R01 -- HOLD01 -- CR01 -- F01节点名与箭头之间存在空格并核对每个节点名与后续run_kwargs键、sim.Xxx赋值名三者完全一致大小写敏感。报错 2某单元漏注入Kinetics/Phases——sim.R01.Kinetics未赋值就求解现象SolveFlowsheet执行到该单元时抛 “attribute not found” 或盒模型无初值报错。根因官方引导下RxnKinetics、初始相必须由用户显式点属性漏一个单元在运行时才开始报告。解法装配后将官方 guide 的四条属性逐一核对——反应器要Utility/Kinetics/Inlet/Phases四件套齐结晶器至少要喂Phases里的SolidPhase。排查时可先只解单单元缩小范围。报错 3scale/distrib维度或量级不匹配——固相网格与scale冲突现象矩或体积累计出现 NaN / 负质量 / 数量级离谱。根因scale1e-9与x_distrib的 µm 量级网格必须是配套的同一约定SolidPhase的distrib长度必须与x_distrib的 bin 数一致如上面都是num35。解法网格np.geomspace(1, 1500, num35)与scale成组绑定改其中一个就要同步另一个先跑官方结晶示例自测plot_profiles()的 μ0 是否单调不炸再改参数。报错 4sundials_opts缺失导致刚性系统解不动现象结晶/反应系统求解步长不断缩短、报积分器步长过小或收敛失败。根因1D-FVMPBM 与多相耦合是典型刚性默认 SUNDIALS 设置不足以追踪快速反应。解法在run_kwargs对应单元补sundials_opts: {maxh: 60}限制最大步长必要时调小maxh或调整容差确认重刚assimulo3.4.3已随 conda-forge 装好。报错 5plot_profiles()出空图 / “no data”现象图框架出来了但曲线为空。根因该单元没解到终点或result未填充往往是因为run_kwargs漏了它的键导致该单元被跳过。解法确认run_kwargs里确实给该单元传了runtime再确认sim.单元名.result非空后再plot_profiles()。四、动手练习练习 1必做逐字抄下 §二 的完整代码并运行把path_phys指向你本机PharmaPy/data目录必要时借官方reactor_tests.py里的物性文件名。判定标准脚本无报错sim.CR01.plot_profiles()出图stream_table sim.GetStreamTable(basismass)返回的是一个 pandas DataFrame可print(stream_table.columns)。练习 2把basis从mass换成mole对比GetStreamTable返回的流股表在质量项 vs 摩尔项上的差异。再在run_kwargs中给CR01加maxh从 60 改到 20观察积分是否更稳。判定标准数值量纲正确、变更前后物理趋势不变。练习 3进阶预告新增一个纯占位单元sim.M01 Xxx(...)并把它插进拓扑串末尾但先不往run_kwargs里加它的键。运行观察错误现象对照 §三「报错 5」。判定标准你能说出报错与漏装配/漏求解参数的因果关系并补上正确键使其通过。五、小结与下一篇预告本篇完成了一次认知跃迁单元类 装配属性注入 拓扑字符串三件套构成 PharmaPy flowsheet 的完整装配。SimulationExec(path_phys, flowsheetgraph)定义框架Utility/Kinetics/Inlet/Phases决定每个单元吃什么、怎么动、初始什么状态SolveFlowsheet(kwargs_run...)按每单元runtime/sundials_opts推进DynamicResult.time/result.字段与GetStreamTable(basis...)承接结果。今后所有干法制粒单元——RollerCompactor、Comminution、Classifier——都会套用这套插座只是把Kinetics换成压缩性/破碎动力学把Phases换成SolidPhase承载 PSD。下一篇篇 4固相/PBM 基础设施将深入本篇反复出现的SolidPhasex_distrib/distrib的离散物流 vs 矩表示moments/num_mom/getMoments、1D-FVM与moments双方法的选择依据。理解了 PSD 怎么在SolidPhase里被表示、被取矩你才真正握住干法制粒数字孪生的状态中枢。本篇认知问题回显FAQQ1PharmaPy 为什么用对象 拓扑字符串而不是一个大配置文件因为它要同时支持独立单单元和互联 flowsheet两种用法。每个单元操作是一个普通类可脱离流程单独构造调试流程连接用一个可读的--字符串描述。增量构建、属性注入、拓扑可审计三方面的收益让二次开发时先单测单元、再进流程成为可能。Q2Utility、Kinetics、Inlet、Phases四个属性各自装什么、何时赋值Utility装公用工程如CoolingWater过程设定温度/流速Kinetics装微观速率反应动力学干法处对应压缩性/破碎动力学Inlet装动态进料流股Phases装初始相含SolidPhase的 PSD。赋值时机在单元类挂到sim之后、SolveFlowsheet之前用赋值即注入完成装配与构造函数解耦。Q3flowsheetR01 -- HOLD01 -- CR01 -- F01会被怎么解释缓冲罐、结晶器、过滤器角色有何不同SimulationExec读字符串把--理解为物流指向据此构建上下游连接。HOLD01 DynamicCollector()只需挂上、角色存中继CR01 BatchCryst(...)是执行 PBM 的核心单元需喂PhasesF01 Filter(diam, alpha, Rm)是管线式分离单元。三者都在拓扑串中占一个节点但所需装配的复杂度不同。Q4SolveFlowsheet(kwargs_run...)怎么传参DynamicResult与GetStreamTable如何取数BatchCryst(…)是执行 PBM 的核心单元需喂PhasesF01 Filter(diam, alpha, Rm) 是管线式分离单元。三者都在拓扑串中占一个节点但所需装配的复杂度不同。Q4SolveFlowsheet(kwargs_run...)怎么传参DynamicResult与GetStreamTable如何取数run_kwargs的键 拓扑串中的单元名每值是一个求解参数字典常用runtime时长与sundials_opts{maxh: 60}求解器限步。结果经sim.单元名.result.time取时间向量、result.字段取状态量sim.CR01.plot_profiles()出剖面图sim.GetStreamTable(basismass|mole)返回全流程流股的 pandas 表格。
延伸阅读

更多相关文章

2026/10/16 12:00:18

YOLOv10实时目标检测实战:从环境配置到摄像头检测

简介:这是一份基于YOLOv10的实时目标检测系统开发案例教程,面向计算机视觉初学者与希望快速上手YOLO系列算法的开发者,解决从零搭建检测模型到视频流实时推理的完整流程问题。教程以单个doc文档呈现,压缩包共1个文件,大…

2026/10/16 17:23:14

MATLAB m_map地理绘图:离线投影、海岸线与坐标映射实战指南

简介:MATLAB的m_map包是面向地球科学、气象与环境研究者的专业地理绘图工具箱,专为解决高精度地图投影、地理坐标转换及多源空间数据可视化等核心问题而设计。资源包含97个文件,以55个MATLAB函数(.m)为主,支…

2026/10/16 17:23:14

自媒体矩阵运营效率低?蚁小二这类工具怎么帮创作者减负

做自媒体矩阵最头疼的,不是写内容、拍视频,而是重复的后台操作。多账号分发、跨平台数据统计、团队账号权限管理,账号一多,每天光是登录十几个平台后台,就要消耗大量创作时间。不少创作者开始借助矩阵工具,…

2026/10/16 17:23:14

C++类继承实战:学生成绩计算题中的虚函数与构造函数避坑指南

PTA刷到“7-3 学生CPP成绩计算”这道题时,很多人第一反应是:又是算成绩的,十几行代码就能搞定。结果真把代码交上去,编译错误站起来一大片,改来改去最后卡在“派生类构造函数怎么给基类传参”这种最基础的位置。这道15…

2026/10/16 17:18:13

OpenCV 2.4.13使用指南:老版本兼容、编译配置与避坑实战

简介:OpenCV 2.4.13.3 是计算机视觉领域经典库 OpenCV 的一个长期维护版本,在传统图像处理、特征提取与旧项目迁移中仍被频繁使用。这份 zip 压缩包提供该版本的核心运行与开发文件,整体大小约 90.16MB,适合需要离线获取库文件的开…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/16 0:01:11

Cephadm实战指南:Linux系统工程师的生产级Ceph集群部署与排障

简介:本资源是一份完整的Ceph分布式存储系统中文技术文档合集,面向Linux系统运维工程师、云平台架构师及存储方向开发者,帮助读者系统掌握Ceph集群的部署、配置与运维核心能力。文档覆盖对象存储(RGW)、块设备&#xf…

2026/10/16 0:01:11

ITK中SpatialObject几何体系解析:坐标系、变换与管道集成

1. 空间对象的定位:为什么 ITK 需要一套几何描述体系很多刚开始接触 ITK 的朋友,包括我自己,最初都是被它的图像滤波、配准、分割这些“正统”功能吸引进来的。等真正上手做点实际项目,比如把分割出来的器官边界做量化分析、给血管…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑