恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
PharmaPy 干法制粒二次开发教程(13):组装完整干法制粒 flowsheet——加料→辊压→粉碎→分级(闭路返料)端到端求解
首页
资讯中心
/
PharmaPy 干法制粒二次开发教程(13):组装完整干法制粒 flowsheet——加料→辊压→粉碎→分级(闭路返料)端到端求解
PharmaPy 干法制粒二次开发教程(13):组装完整干法制粒 flowsheet——加料→辊压→粉碎→分级(闭路返料)端到端求解
发布时间:2026/10/16 2:52:18
PharmaPy 干法制粒二次开发教程13组装完整干法制粒 flowsheet——加料→辊压→粉碎→分级闭路返料端到端求解版本声明块工具/软件本项目二次开发的三件套单元RollerCompactor/Comminutor/Classifier均为本系列二次开发示例非官方 API 官方SimulationExec语言/环境Python 3.9 numpy/scipy PharmaPy 框架本文目标把前 12 篇攒下的三块积木真正拼成一条可求解的连续流程并让 PSD 从进料一路贯通到合格品产出。一句话结论用SimulationExec(path_phys, flowsheetFEED -- RC -- MILL -- CLS -- OUT)定义干法制粒拓扑sim.Node挂上本项目三件套RollerCompactor/Comminutor/Classifier均为示例类闭路时让分级粗流经返料边回灌粉碎机SolveFlowsheet(kwargs_run...)端到端求解即可在DynamicResult.time/结果字段里读出进料 PSD → ribbon PSD → 颗粒 PSD → 分级后合格品 PSD的完整贯通。〇、本篇要解决的认知问题我手上已经有单机版的三件套为什么还要专门谈组装 flowsheet拼在哪里最容易翻车--拓扑字符串怎么和三件套的挂载契约对齐SolvFlowsheet对每个单元分别要什么runtime粒子的流在三件套之间到底传什么——是质量流m、PSD还是要同时传 PSD 流率闭路返料在 flowsheet 里是再加一条边那么简单吗循环物流怎么处理初值/收敛端到端跑完后我如何从结果里把CQA 初值PSD/粗粒/细粉/强度系统性地拎出来一、机制解析为什么组装不是把三块积木排排站单机演示篇 8/10/12每块都是独立跑、独立断言非常干净。但真实的流水线是三块之间互传物流进料量、粒径网格、密度、真密度这些量在模块间必须自洽——尤其干法制粒的特殊性在于同一份粒径网格要贯穿全流程进料/辊压/粉碎/分级都在这组x_gr上。一旦某一节私自换了网格或单位中间流束就会接不上出现辊压产出在粉碎端读不到这类阴坑。所以组装的本质是定义一份全流程共享的状态契约网格、单位、质量流率再让三个单元都守这份契约。拓扑字符串 挂载契约照样画官方 guide回顾篇 3 官方 guide底账 A5拓扑串R01 -- HOLD01 -- CR01 -- F01每个节点用sim.Name 单元实例挂上去再逐字段注入Phases/Kinetics/Inlet/Utility。我们的三件套在篇 8 里已声明了self.Phases/Kinetics/Inlet/Utility None做挂载契约所以流程组装与官方 guide语义一致只是单元类变了。SimulationExec.__init__收path_phys物性路径flowsheetSolveFlowsheet(kwargs_run...)对每个节点给独立runtime与sundials_opts。流束在单元间传什么状态官方SlurryStream/SolidPhase承载的是质量流率 组分分数 PSDdistrib。对干法制粒我们约定的流束契约是mass_flowkg/sx_gr网格distrib每档质量份额或数量密度.三件套按此互传加料Feeder给mass_flow与一套x_gr/distribRollerCompactor把进料 PSD 收缩成 ribbon PSD流率不变篇 8Comminutor粉碎把 ribbon 按S_i/B_ij破碎产出的 PSD 右移/封顶质量守恒Classifier按E(x)分粗/细流粗流回流闭路或弃用开路细流出料。传PSD 流率而不只是质量是干法制粒与普通反应流程最大的区别——CQA粒径是流程的核心状态不是事后指标。闭路返料循环物流的初值与收敛SimulationExec的SolveFlowsheet对每个节点按拓扑顺序推进BatchReactor 一类的时序。循环物流闭路返料在动态仿真里通常先给返料一个初值常取回调料每轮迭代后用新算的粗流刷新返料流直到粗流质量稳定不动点。这不是官方框架的自动能力而是二次开发时要自己加的一层外循环归到完整项目篇 20。本篇先给一个数学上可收敛的简化闭路返料比例由 Tromp 的E(x)直接算出并循环到|Δ返料|tol用循环计数器保护迭代。端到端的 CQA 初值跑通后CQA 不是一个数值而是一串沿流程的分布ribbon SF/密度RollerCompactor输出篇 8颗粒 PSDComminutor端到端产出——d10/d50/d90/span篇 14 详算细粉占比125µm质量份额篇 16 详做分级后的合格品 PSDClassifier细流。本篇先把怎么把全流程的 PSD 端到端取出这一机制讲清具体 CQA 计算放 14-16。二、完整代码与逐行剖析下面是一个结构完整、可运行的三件套串联示例三件为示例类正式动力学可复用篇 8/10/11定义RollerCompactor/Comminutor/Classifier三个最小单元类挂进FEED--RC--MILL--CLS--OUT闭路把CLS粗流R累积回流给MILL再SolveFlowsheet以循环迭代代替框架自动循环并读全流程 PSD。importnumpyasnpfrompharmapy.SimExecimportSimulationExec# 官方执行器# ---- 网格全流程共享的一份铁律5----x_lo,x_hi,numbins10.0,2000.0,40ratio(x_hi/x_lo)**(1.0/(numbins-1))x_grnp.geomspace(x_lo,x_hi,numbins)x_cnp.sqrt(x_gr[:-1]*x_gr[1:])nbnumbins-1volx_c**3.0# ---- 三个最小单元复用前篇动力学此处聚焦串联骨架----classRollerCompactor:# 篇8 简化ribbon SF 收缩PSDdef__init__(self,SCF5.0):self.SCFSCF;self.SF_ribbonNone;self.PhasesNonedefstep(self,x_gr,distrib,mass_flow):SF0.620.02*(self.SCF-4.0)# 示意趋势(以文献/实测为准)self.SF_ribbonSF shiftSF**(1.0/3.0)# 线收缩d_ribnp.interp(x_gr*shift,x_gr,distrib)# 拦截收缩后的分布d_ribd_rib/np.sum(d_rib)returnd_rib,mass_flowclassComminutor:# 篇10/11 简化选择破碎守恒def__init__(self,S_ref0.5,q1.0,mesh1000.0):self.SS_ref*(x_c/500.0)**q self.Snp.where(x_cmesh,10.0,self.S)Bnp.zeros((nb,nb))forjinrange(nb):imax(1,min(nb-1,int(np.searchsorted(vol,0.5*vol[j]))))w(vol[i]-0.5*vol[j])/(vol[i]-vol[i-1])ifinbelse1.0B[i-1,j]w;B[i,j](1.0-w)ifinbelse0.0self.BBdefstep(self,distrib):# 快速破碎到准稳态(示意)returnnp.dot(self.B,self.S*distrib)/(1.0self.S*distrib.sum()*0.)classClassifier:# 篇12Tromp 分流def__init__(self,d50300.0,s3.0):self.d50d50;self.ssdefstep(self,distrib):E1.0/(1.0(self.d50/x_c)**self.s)returndistrib*E,distrib*(1.0-E)# (粗流, 细流)# ---- 组装 端到端求解 ----simSimulationExec(path/to/thermo,flowsheetFEED -- RC -- MILL -- CLS -- OUT)sim.FEED.mass_flow0.1# 进料 kg/ssim.FEED.x_gr,sim.FEED.distribx_gr,np.where(x_c600,1.0,0.0)sim.RCRollerCompactor(SCF5.0)sim.MILLComminutor()sim.CLSClassifier(d50300.0)# 闭路外循环由分级粗流回流推动到返料稳定为止distribsim.FEED.distrib.copy()rec0.0foritinrange(20):d_rib,mfsim.RC.step(x_gr,distrib,sim.FEED.mass_flow)d_millsim.MILL.step(d_rib)d_coarse,d_finesim.CLS.step(d_mill)rec_newnp.sum(d_coarse)# 粗流(回收)份额ifabs(rec_new-rec)1e-6:# 返料不动点print(f闭路收敛于迭代{it1}返料率{rec_new:.4f})breakdistribd_coarse;recrec_new# 回流刷新print(合格品(细流) d50 占比峰值档 ≈,x_c[np.argmax(d_fine)],µm)逐行要点全流程共享x_gr进料/辊压/粉碎/分级全用同一x_gr/vol这是状态契约的物理落点——任何模块偷改网格或单位链条就会断篇 8 的 ingrect 收缩回到同一网格。RollerCompactor.step用np.interp(x_gr*shift, x_gr, distrib)拦截收缩后的分布再归一——把篇 8 的ribbon PSD 占位补成了明确产出shiftSF^(1/3)是工程近似以文献/实测为准。Comminutor.step复用篇 10-11 的S/B一行np.dot(B, S*distrib)完成出生-死亡骨架演示完整动力学见篇 10-11。Classifier.step直接返回(distrib*E, distrib*(1-E))两流coarsefinedistrib守恒篇 12。闭路外循环for it in range(20)里用rec_new检测返料不动点1e-6即收敛——这是SimulationExec不替你做的循环物流收敛层正是篇 20 完整项目里要工程化的关键块。it20上限防止死循环。SimulationExec在此只作拓扑容器承托三件套与flowsheet语义真正的解在step()链上推进DynamicResult/字段读取在真项目里由完整单元类的solve产出篇 8 已有。三、常见报错与排查某一节擅自改网格/单位链条在中间断。现象MILL收到的distrib长度与x_gr不匹配或S/Bindex 越界。根因篇 8 的 ribbon 收缩没有落回原网格而是把distrib长度改了。解法所有单元统一np.interp回到x_gr维护全流程共享网格的契约打印每个节点出口distrib.size验证等于nb。闭路迭代不收敛振荡/发散。现象rec_new在两个值间来回跳或冲向 1。根因分流E(x)与破碎的增益耦合粗流回流把过多粗粒又喂回去形成正反馈初始distrib过粗也会加剧。解法对回流做部分松弛distrib(1−α)*oldα*new如α0.5并限制每轮破碎只允许有限粒径范围进出把d50调大让粗流更钝再试。分级E在x→0让distrib*E数值下溢rec丢失。现象rec_new突然变成 0 或 NaN。根因E1/(1(d50/x)^s)当x极小时(d50/x)^s爆炸E→0但乘上极小的distrib数值精度崩。解法给distrib设下限如np.where(distrib1e-12,0,distrib)或把E用np.nextafter安全计算。SimulationExec收不到三件套报节点不存在。现象构造后SolveFlowsheet找不到RC/MILL/CLS。根因拓扑字符串里的名字必须和你赋的sim.Name逐字一致且这些名字不能与框架保留关键字冲突。解法先print(sim.state_names)列出已登记节点再把RC/MILL/CLS同名赋上三件套要求显式声明Phases等挂载属性篇 8 已写。端到端跑得很慢。现象闭路每轮都重算破碎/分流循环多轮后卡顿。根因破碎S/B一级np.dot不算贵但np.interp与argmax每轮重复外加循环无上限保护。解法把S/B/E等只依赖网格不依赖流的量在构造时算一次缓存如存self.B循环只更新distribfor it in range(20)明确上限。四、动手练习端到端贯通验收跑上面代码确认闭路收敛并打印进料→ribbon→破碎→细流各站点的d50或峰值档。判定三站d50或峰值档单调合理——进料含粗粒、ribbon 略缩、破碎后粗粒减少、细流明显偏细且drift1e-6。闭路 vs 开路对比把distribd_coarse回流改为distribd_fine不回流重跑看细流d50与返料率。判定闭路细流更细、返料率开路且开路无循环开销——用数据讲清返料保上限但耗能。网格切换陷阱故意在RollerCompactor.step里把shift弄到x_gr*2后不interp回原网格观察链条断裂。判定报维度/守恒错误或 PSD 错乱——亲历全流程共享网格契约被破坏的代价记住篇 13 的铁律。五、小结与下一篇预告本篇终于把三块积木拼成一条可解流程FEED--RC--MILL--CLS--OUT端到端挂入SimulationExec闭路返料以外循环收敛并读出了沿流程贯通的 PSD。你掌握了二次开发型 flowheet 组装的精髓——全流程共享一份网格/单位/流率的状态契约循环物流自己加收敛层。PSD 算是跑出来了但怎么变成 CQA 报告还差一步。下一篇篇 14《PSD 报告层》——把离散 bin 的 PSD 提炼成工程师真正要看的指标d10/d50/d90、span、细粉占比并用 lognormal/GGS/RRB 三种拟合把离散数据还原成连续曲线。本篇认知问题回显FAQQ1三块单机都能跑为什么组装还会翻车A1因为真实流水线三块之间互传物流——进料量和 PSD 要贯穿全流程。干法制粒最特殊的是同一份粒径网格跨越加料/辊压/粉碎/分级任何模块偷改网格或单位中间流束就接不上。组装的本质是定义一份共享状态契约网格、单位、质量流率三个单元都守它。Q2--拓扑怎么和三件套对齐SolveFlowsheet要什么A2沿用官方 guideflowsheetFEED -- RC -- MILL -- CLS -- OUT每个节点sim.Name单元实例挂载注入Phases/Kinetics/Inlet/Utility三件套在篇 8 已声明这些挂载默认None。SolveFlowsheet(kwargs_run...)对每个节点给独立runtime/sundials_opts。拓扑串里的名字必须与赋值的属性名逐字一致。Q3流束在单元间到底传什么A3传质量流率mass_flowkg/s 共享x_gr网格 每档distrib。只传质量不行——因为CQA粒径分布本身就是流程的核心状态不是事后指标。加料给量PSD辊压收缩成 ribbon PSD粉碎破成更细 PSD分级按E(x)分粗/细流。Q4闭路返料是再加一条边那么简单吗A4不是。SimulationExec不替你处理循环物流收敛二次开发要自己加一层外循环给返料初值每轮用新算的粗流刷新返料流直到|Δ返料|tol不动点。本篇用for循环 rec_new检测收敛it20上限防死循环完整的工程化闭环加松弛、初始稳定在篇 20。Q5端到端跑完怎么把 CQA 拎出来mulationExec不替你处理循环物流收敛二次开发要自己加一层**外循环**给返料初值每轮用新算的粗流刷新返料流直到|Δ返料|tol不动点。本篇用for循环 rec_new检测收敛it20 上限防死循环完整的工程化闭环加松弛、初始稳定在篇 20。Q5端到端跑完怎么把 CQA 拎出来A5CQA 不是单个数而是一串沿流程的分布RollerCompactor给 ribbon SF/密度Comminutor给颗粒 PSDd10/d50/d90/spanClassifier细流给合格品 PSD125µm份额即细粉占比。本篇只负责把它从各站step出口系统性取出具体怎么算这些指标放篇 14-16。