ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PhysiCell多尺度仿真集成指南:从SBML到COPASI的跨尺度建模实践

PhysiCell多尺度仿真集成指南:从SBML到COPASI的跨尺度建模实践 PhysiCell这个系列写到现在已经是第十四篇。前面聊过它的细胞力学、细胞周期、微环境扩散这些单点功能但隔三差五就有读者在评论区问PhysiCell到底能不能和其他生物仿真软件配合起来用它内置了BioFVM那能不能接COPASI、Smoldyn、CompuCell3D这些生态把分子、细胞、组织整个串成一条线这问题问到了多尺度仿真真正的核心痛点。单个软件里的功能再花哨价值也有限真正难的是“跨尺度怎么接”。PhysiCell的定位是细胞级代理模型配合BioFVM能算氧气、葡萄糖这类信号的扩散和消耗但它默认不擅长完整的细胞内信号网络也不能直接读SBML、SBtab这类生信标准格式。真要搞一套带p53-MDM2反馈、NF-kB串扰、代谢重编程的多尺度模型单打独斗确实吃力。这篇来拆“集成”这件事。我把它分成四块微环境参数怎么和实验数据对齐分子网络怎么通过SBML挂进每个细胞PhysiCell和Smoldyn、CompuCell3D、VCell这类工具怎么分工以及我实际踩过的几个坑。刚入门的朋友可以照着做已经在跑多尺度模型的也能从集成路径和排错思路里找到可复用的东西。1. PhysiCell在多尺度仿真版图中的真实位置以及为什么集成绕不开1.1 PhysiCell擅长什么不擅长什么PhysiCell是一个开源的、C写的、面向大规模3D细胞群体的多尺度仿真平台。它的核心设计是Agent-Based Modeling也就是ABM。每个细胞是一个智能体带有位置、体积、粘附、机械碰撞、细胞周期、分泌吸收能力以及一系列表型决策规则。微环境这一层由内置的BioFVM负责求解反应-扩散方程模拟氧气、药物、细胞因子等底物的空间梯度。这套设计的最大优势是细胞群体在组织尺度上的行为涌现比如肿瘤球生长、免疫细胞浸润、血管新生它能跑得又快又直观。尤其适合做“细胞之间相互作用导致整体行为变化”这类问题比如CAR-T细胞进入实体瘤后为什么容易被耗竭T细胞在什么条件下才能穿过致密基质。但它的短板也很明显。第一细胞内部的分子信号网络和代谢通路不是它的主场。你可以用手写ODE的方式在细胞函数里塞进去几个分子但一旦涉及几十个物种、几百个反应没有标准化的格式来进行管理就是一场灾难。第二它默认不认SBML这类生信通用格式外面的工具导出的网络模型不能直接拖进去用。第三实验数据导入和闭环比较吃力比如需要根据病理切片上的细胞分布来初始化细胞位置就需要外部脚本做图像处理。第四再往上接药物动力学参数或者器官级模型时PhysiCell本身不提供接口只能靠我们自己搭桥。所以多尺度仿真的一个现实情况是PhysiCell适合做“细胞群体行为”这一层的主干但分子层的通路定义、微环境层的参数标定、数据层的统计可视化都需要跟别的工具做集成。1.2 集成到底分成哪几个层次我做了几年的多尺度模型越来越倾向于把集成分层来看。每一层有它自己的代表工具和要解决的问题别混在一起谈。层次代表工具在PhysiCell工作流里的角色分子通路层SBML、COPASI、BionetGen、VCell定义或转换细胞内部的生化反应网络生成ODE后挂到每个细胞的决策逻辑上微环境层BioFVM、实验氧/药物浓度数据定义底物的扩散系数、降解速率、边界条件与体外实验曲线对齐数据交换层Python、ParaView、MATLAB前处理图像分割、初始化细胞位置和后处理统计、出图、动画外部模拟层Smoldyn、CompuCell3D、VCell处理特定尺度的建模需求例如分子粒子级或需要精确细胞形状的场景判断标准其实很直接。当单个细胞的行为规则取决于细胞内某个分子的浓度、某个信号通路的开关状态时就必须接分子网络层。当仿真结果要跟体外实验曲线对比时就必须确保微环境参数一致。当数据集大到用文本挨个翻看不现实时就必须接数据交换层。1.3 集成的本质是标准格式不是硬凑接口不少人一听到“集成”就以为是要在PhysiCell里封装一个什么API。实际上绝大多数跨软件的中转站是标准格式本身。SBML负责描述生化网络CSV和VTK负责传递空间和细胞状态JSON/XML负责传配置。把这些标准化了工具之间的衔接就顺了。所以下文我会反复强调SBML和VTK这两个格式因为它们是这个生态里的通用语言。2. 最容易上手的集成入口先把微环境与实验数据对齐2.1 BioFVM在PhysiCell里的实际耦合方式PhysiCell默认内置BioFVM很多人不知道这其实就是微环境集成的一环。BioFVM是一套独立于PhysiCell的“反应-扩散方程求解器”被集成进来以后每个体素里都在解一组PDE描述各种底物的扩散、衰减和被细胞吸收/分泌的过程。氧气、葡萄糖、药物、化疗因子、细胞因子都以这种“底物”的形式存在于仿真中。在PhysiCell的配置里微环境变量在PhysiCell_settings.xml中定义。比如要加一个氧气变量大概是这样的microenvironment_setup variable nameoxygen unitsmmHg diffusion_coefficient100000 decay_rate0.1 initial_condition38/ /microenvironment_setup这里每个参数背后都有物理含义。diffusion_coefficient控制氧气在组织里的扩散能力单位通常是微米平方每分钟decay_rate是底物的自然降解速度initial_condition是初始浓度如果设了dirichlet_condition则表示边界浓度固定。调整这些参数会直接影响肿瘤球内部有没有缺氧区、坏死核心长什么样。2.2 新增一种细胞因子并把它耦合到细胞决策实际做集成的时候我们经常需要加入自定义信号分子比如TNF-α或IL-2。做法不难在XML里加一个变量然后在自定义细胞函数里找到该底物在微环境中的索引设置分泌/吸收速率。大致逻辑是这样void my_cell_model(Cell* pCell, Phenotype phenotype, double dt) { int tnf_index microenvironment.find_density_index(TNFa); // 每个细胞都可以分泌TNF-α phenotype.secretion.secretion_rates[tnf_index] 5.0; }这是最朴素的一层集成底物由细胞分泌又反过来影响细胞行为。比如当TNF-α浓度超过阈值时细胞进入凋亡程序这就形成了“微环境-细胞表型”的闭环。2.3 和实验氧分布数据对齐的具体做法微环境集成里最容易忽略的是参数标定。很多实验数据是体外培养测得的氧浓度梯度比如肿瘤球在特定深度会出现缺氧区。我们要做的是让仿真里的氧分布曲线和实验曲线尽量重合这时候就需要调diffusion_coefficient、decay_rate和细胞的氧气消耗率。我的建议是分两步。第一步先不管细胞把纯扩散的氧分布跑出来和实验的空白对照对齐。第二步再放入细胞调消耗率。如果直接一上来就调所有参数很容易过拟合而且出了问题根本定位不到源头。这算是我做过好几个项目之后的一个经验微环境参数对齐是一切上层集成的前提这层不对齐后面挂上分子网络只会更乱。3. 分子通路集成的核心通道用libSBML把生化网络挂进每个细胞3.1 为什么SBML是这个场景里的标准交换格式SBML全称Systems Biology Markup Language是系统生物学领域用来描述生化反应网络的XML标准格式。它定义了几类核心对象物种物种、反应反应、速率定律速率定律、参数参数、单位单位等。几乎所有主流分子网络工具都支持SBML的导入导出。对PhysiCell来说我们需要接的是一个“从外部工具生成网络然后进入细胞内ODE”的通道。如果不用SBML就得把几十个反应公式一个个手抄到C代码里效率低不说还特别容易抄错。用SBML等于有了一个所有工具都能认的中间语言在COPASI里把p53网络调好参数导出SBML在BionetGen里写免疫受体信号规则转成SBML甚至在VCell里做空间反应-扩散模型也能生成SBML。然后我再在PhysiCell这一侧统一解析。3.2 环境准备安装libSBML解析SBML建议直接用libSBML。它是对SBML标准最完整的官方解析库支持C、C、Java、Python等语言。PhysiCell本身是C项目所以我一般用C API来解析。Ubuntu下的安装sudo apt-get install libsbml-devmacOS下brew install libsbmlWindows下建议去SBML官网下载预编译库然后把include和lib路径配到编译器里。装好以后可以先用一个简单的C程序验证解析器是否正常。3.3 在PhysiCell项目中解析并执行SBML模型PhysiCell的自定义模块通常放在custom_modules/目录下。我的做法是单独写一个MyIntracellularModel类在初始化时调用SBMLReader把模型读进来把物种名称、初值、反应速率律都放到一张映射表里。代码结构类似这样#include sbml/SBMLTypes.h #include map #include string class MyIntracellularModel { public: std::mapstd::string, double species_values; void load_from_sbml(const std::string filename) { SBMLReader reader; SBMLDocument* doc reader.readSBML(filename); Model* model doc-getModel(); if (!model) return; // 读取物种和初值 for (unsigned int i 0; i model-getNumSpecies(); i) { Species* sp model-getSpecies(i); species_values[sp-getId()] sp-getInitialConcentration(); } // 读取反应速率律公式 for (unsigned int i 0; i model-getNumReactions(); i) { Reaction* rx model-getReaction(i); KineticLaw* kl rx-getKineticLaw(); std::string formula kl-getFormula(); // 这里需要把formula字符串解析成可执行表达式 // 可以自己写一个轻量表达式解析器或者利用libSBML的FormulaParser } } };拿到了速率定律字符串后难点在“怎么把字符串变成可计算的函数”。libSBML自带的FormulaParser能解析SBML的公式语法但如果你需要更灵活的数值积分我建议用下面两种方式之一要么自己实现一个轻量表达式解析器支持加减乘除、幂、常用的函数exp、log、sqrt然后把函数指针存起来要么在COPASI里直接把模型生成C代码再手动摘出核心的速率表达式。我在实际项目里更倾向于后者。因为SBML的速率律字符串千奇百怪自己写表达式解析器维护成本很高。COPASI导出C代码后公式已经是人类可读的标准C语言移植到PhysiCell的自定义细胞函数里就非常直接。3.4 在细胞函数里做状态更新并影响表型当分子网络解析完成接下来就是把它和细胞行为绑定。我在自定义细胞函数里的标准写法是从custom_data里读取当前分子浓度用外部解析好的速率律算一个时间步的增量然后更新状态再根据关键分子的浓度改变细胞表型。void my_cell_model(Cell* pCell, Phenotype phenotype, double dt) { double akt pCell-custom_data[akt]; double bad pCell-custom_data[bad]; // 一个非常简化的AKT-BAD网络示意 double k1 2.0; double k2 0.8; double d_akt k1 * (1.0 - akt) - k2 * akt * bad; double d_bad k2 * akt * bad - 0.5 * bad; // 欧拉积分实际建议用RK4或限制单步增量 akt d_akt * dt; bad d_bad * dt; pCell-custom_data[akt] akt; pCell-custom_data[bad] bad; // 分子状态影响表型AKT高表达时细胞倾向于增殖 if (akt 0.8) { phenotype.cycle.data.transition_rate(0, 1) 0.9; } else { phenotype.cycle.data.transition_rate(0, 1) 0.1; } }关键点在于“时间步的限制”。PhysiCell的dt通常是为了解决细胞运动、扩散而设置的分子网络的ODE尺度可能完全不一样。如果反应速率常数比较大直接用欧拉积分容易数值发散。我的经验是先把时间步切到足够细比如如果反应时间尺度在分钟量级而PhysiCell的外部时间步是0.1分钟可以接受但如果是毫秒级过程就必须在细胞函数里做子循环把dt切成更小步长或者使用隐式求解器。3.5 单位不归一化仿真一定跑飞SBML集成里最大的坑就是单位换算。SBML模型常用时间单位是秒PhysiCell内部时间是分钟SBML里浓度单位可能是mol/L或mmol/L而PhysiCell微环境的浓度单位可能是mmHg或uM空间尺度上SBML可以是基于体积单位不是基于微米网格。举个我踩过的例子。从COPASI导出的一个代谢网络速率常数是以秒为单位的直接挂进PhysiCell后每个真实秒的时间被当成一分钟来算等于所有反应速率被放大了60倍。十几个时间步之后分子浓度从1e-6直接变成了1e12一开始我还以为是模型稳定性问题排查很久才发现纯粹是单位没换。所以建议在加载SBML模型时写一个预处理脚本把单位统一换算成PhysiCell的base units时间用分钟空间用微米浓度用uM或者mmHg。libSBML里的UnitDefinition其实可以提供单位换算信息但更省力的做法是在COPASI里就把模型单位改成min和uM再导出。做完这一步再进PhysiCell能省掉很多麻烦。4. 和CompuCell3D、Smoldyn、VCell这些工具怎么分工什么时候才需要联合4.1 三种底层模拟机制的本质差异很多读者会拿PhysiCell和CompuCell3D、Smoldyn、VCell、COPASI做对比其实它们并不是同一个层面的工具底层机制完全不同。把每个工具的本质搞清楚才知道什么时候该联合、什么时候谁替代谁。工具底层模型核心尺度强项与PhysiCell的典型配合方式PhysiCellAgent-Based中心力模型细胞/组织大规模细胞群体行为、微环境扩散主干仿真CompuCell3DCellular Potts模型细胞/组织细胞形状、细胞-细胞接触、边界张力对形态敏感的问题做交叉验证Smoldyn分子粒子随机游走分子/细胞膜受体-配体相互作用、分子扩散轨迹提供膜表面信号或小范围梯度参数VCellPDE/ODE空间建模分子到细胞反应-扩散的空间模型、分子通路建模用VCell生成空间反应-扩散模型再简化为微环境参数COPASIODE/随机/代谢网络分子通路参数拟合、稳态分析、剂量响应先在这里调节SBML网络再导出PhysiCell里的细胞是“球形的带半径的粒子”通过中心力模型来处理碰撞、黏附这种机制的好处是计算快、能跑百万细胞坏处是细胞形状永远是圆的或者被挤成多边形没法精确模拟上皮细胞的极化形态。如果研究的问题高度依赖细胞形状比如集体迁移中的头尾极性、细胞重塑导致的组织折叠那CompuCell3D的Cellular Potts模型反而更合适。Smoldyn则是更底层的存在。它几乎是给分子做布朗运动模拟的每个分子都是一个粒子在空间里随机游走遇到配体就结合。用Smoldyn去模拟跨膜受体的聚集过程能得到受体在膜上的空间分布这些参数可以用来修正PhysiCell里细胞对信号分子的响应阈值。VCell是个被低估的工具。它本身就是做空间反应-扩散方程的可以用来在连续空间里模拟一个简化的组织片段里的信号梯度然后把梯度参数化成PhysiCell微环境里的边界条件或初始条件。这种方式比直接猜一个扩散系数要科学得多。4.2 一个联合工作流的实例p53-NF-kB串扰信号的跨尺度搭建拿我之前做过的“肿瘤微环境中的p53-NF-kB串扰”的例子来说一下完整的联合路径。第一步在COPASI里搭建p53和NF-kB的动态网络调好参数做一遍稳态和动态分析确认振荡行为正常。第二步把网络导出为SBML。第三步用libSBML解析SBML在PhysiCell每个细胞里实现这套ODE细胞状态变量存在custom_data里。第四步使用BioFVM在微环境层模拟TNF-α的扩散。第五步在细胞函数里读取局部的TNF-α浓度作为NF-kB通路的输入NF-kB激活后又促进细胞分泌更多细胞因子反馈回微环境。这就是一个标准的双向耦合多尺度模型分子网络影响细胞行为细胞行为改变微环境微环境回过头来调节分子网络。这种闭环是单靠一个PhysiCell或者单靠一个COPASI都跑不出来的必须做集成。4.3 可视化与后处理层面的集成ParaView PhysiCell Studio仿真做完不能只看CSV数字可视化这一步同样属于集成。PhysiCell默认会输出带细胞位置和属性的数据文件另外还可以输出VTK格式可以直接拖进ParaView里渲染。ParaView能做的事情包括显示肿瘤球的三维结构、用颜色映射表达细胞内部的p53浓度、把微环境的氧浓度用透明体渲染出来。这对于跟生物学家讨论模型结果特别有用。PhysiCell Studio是官方出的交互式可视化工具可以在仿真过程中暂停、拖动细胞、实时看指标。我做集成调试的时候一般先把输出间隔设得小一点在Studio里逐步检查确认分子浓度和表型变化符合预期再放大规模跑正式仿真。5. 实测踩坑记录单位、版本、并发、可复现性5.1 单位换算错误导致浓度指数爆炸这个前面已经提过但还是值得单独列出来。现象是仿真跑了几百个时间步后某个分子浓度直接变成1e15甚至NaN整片组织全部死掉。第一次遇到我还以为是解析器写错了后来把每个物种的初值和速率常数逐一打印出来才发现是SBML里k值的单位是1/s而PhysiCell的dt单位是min差了60倍另外浓度单位也有mol/L和uM之间的1000倍差距。解决方案非常简单写一个单位统一脚本或者干脆在COPASI导出前手动把所有单位改成min和uM。以后凡是接入新模型我第一件事就是先看单位定义不看单位直接跑就是给自己埋坑。5.2 libSBML版本冲突导致的链接错误PhysiCell本身用的编译系统比较传统如果你同时装了系统自带的和conda的libSBML很容易出现undefined reference。我遇到过的情况是系统里是libsbml5但某个工具链需要libsbml6编译时一堆符号找不到。排查思路是用ldconfig -p | grep sbml看系统库里有哪些版本用ldd看可执行文件实际链接的是哪个so。然后统一include路径和库路径尽量全部指向同一个小版本的libSBML。5.3 并行仿真里的随机性和可复现性问题PhysiCell支持用OpenMP并行但并行之后随机数怎么分配是个大问题。如果所有线程共用同一个全局随机种子那么每次运行结果都可能不同而且不同机器、不同核心数下结果都不一样。这在做科研场景下非常致命审稿人让你复现结果的时候你回一句“每次跑出来都不同”就麻烦了。我的经验是先在单线程模式下跑一条基线确定要复现的标准配置然后用显式设置随机种子的方式跑并行如果并行逻辑改变了随机数调用顺序最简单的方式是给每个线程单独初始化随机流并把线程数固定下来。PhysiCell本身支持在配置里设随机种子但如果你在细胞函数里另外用了自己的随机数生成器一定要保证它的种子也受控。5.4 输出文件直接把磁盘撑爆跑大规模3D仿真每隔0.1分钟存一次所有细胞位置和微环境场跑几千分钟输出几十个GB是常态。我第一次跑肿瘤免疫模型时一个晚上醒来发现磁盘满了所有数据都白跑。后来学乖了输出间隔设到合理范围比如每1分钟或每10分钟存一次并且只保存自己关心的一组细胞属性微环境场可以隔几帧才存或者只输出某个切面的数据。VTK的压缩选项也开着。这类细节看着小实际能救你一命。问题现象对策单位不统一浓度爆炸、NaN解析SBML前统一时间/min、浓度/uM单位libSBML版本冲突链接时undefined reference统一include和lib路径到同一个小版本并行不可复现每次运行结果不同单线程基线 固定随机种子 固定线程数输出文件过大磁盘满、跑白加大输出间隔、只存关键属性、开VTK压缩6. 再往上走实验数据闭环、机器学习决策和更大的生态6.1 用实验影像数据做初始化和验证很多真实场景需要把实验切片里的细胞分布直接搬进仿真。这时集成路径通常是这样先用ImageJ或CellProfiler对HE染色切片做细胞分割得到每个细胞的位置和形态再把坐标数据转成PhysiCell的初始细胞布局最后在仿真里复现出和切片相似的空间结构。这一步的价值在于模型不再是从完全理想化的随机构型开始跑而是从真实的组织状态出发做预测的基准就完全不一样了。6.2 把机器学习决策塞进细胞模型再进阶一点细胞的行为规则不一定要手写固定阈值。最近有不少工作在做“细胞级强化学习”。思路是把细胞当成一个决策智能体观测是局部的细胞因子浓度、氧气浓度、邻居密度动作是增殖、迁移、凋亡或者分泌某种细胞因子奖励函数由整体肿瘤控制率来定义。具体到PhysiCell可以用Python离线训练一个简单的决策树或线性策略导出成一个轻量文件在C细胞函数里加载并按输入特征判断动作。这种“Python训练-PhysiCell推理”的集成路径可以把机器学习模型嵌入到大规模ABM仿真里。实测跑下来决策树比神经网络更容易在C里嵌入而且可解释性强。如果你要上神经网络可以考虑把ONNX Runtime也编译进去但复杂度会上去不少。6.3 和PK/PD、器官级模型的串联还有一类集成是往上走比如把PhysiCell放在一个更大的药物研发平台上。上游的PK模型计算药物在血液里的浓度曲线输出给PhysiCell作为微环境里药物底物的边界条件PhysiCell算出肿瘤细胞数量变化和耐药细胞比例再反馈给药代动力学模型。这种串联能回答一些特别实际的问题比如用药方案是每周一次大剂量更好还是每天低剂量更好。PhysiCell在这类体系里就是中间的“药效动力学”模块。它不是一个封闭的黑盒只要输入输出格式标准化就能嵌入到任何一种已经跑通的药研流程里。这是我认为多尺度集成最值得投入的方向。个人经验是无论做哪一种集成都建议从最小闭环开始。先拿一个三到五个分子的SBML模型在COPASI里跑通再挂进PhysiCell确认分子浓度曲线和COPASI趋势一致然后再逐步扩大网络规模。不要一上来就拿一个大而全的代谢网络做集成否则一旦跑飞你根本分不清是解析器的问题、单位的问题还是模型本身的数值稳定性问题。最小闭环跑通了后面加模块就是重复劳动真正卡壳的地方大概率已经提前排掉了。
返回列表