
自己编译过PyRosetta的人都知道这个过程远没有“pip install”那么轻松。它本质上包含两条线一条是Rosetta主程序本身的C编译另一条是把这些C功能封装成Python可调用模块的绑定层编译。任何一个环节踩坑第二天的时间基本就交代了。我前前后后在几台不同配置的Linux机器上折腾过编译安装也帮别人排查过各种报错。这篇文章不打算复述官方文档而是把我实际操作的流程、环境准备、关键参数含义、遇到过的故障和排查方法串起来作为一份可以“抄作业”的记录也顺便说清楚每一步背后的原因。如果你正准备在Linux平台编译安装PyRosetta或者编译到一半莫名崩溃、找不到头绪这篇应该能帮上忙。1. 为什么要自己编译PyRosetta1.1 PyRosetta到底是什么和Rosetta是什么关系先理清概念。Rosetta是一套用C实现的蛋白质结构预测与设计框架从2000年前后发展到现在已经不只是单蛋白结构预测而是覆盖了蛋白设计、抗体建模、酶设计、RNA建模、对接、自由能计算等一批模块。它原本的使用方式是命令行调用relax、fixbb、docking_protocol等编译好的可执行程序输入PDB文件和一批参数完成计算。PyRosetta则是Rosetta的Python绑定层。它用类似pybind11的机制把Rosetta C库里的核心类和方法暴露成Python接口让你可以直接在Python脚本里创建Pose、操作残基、计算能量、调用打分函数。举个例子import pyrosetta pyrosetta.init(-mute all) pose pyrosetta.pose_from_pdb(/path/to/input.pdb) scorefxn pyrosetta.create_score_function(ref2015) score scorefxn(pose) print(pose.pdb_info().name(), score)这段代码干的事情本质上就是让Python进程链接Rosetta编译出来的层库通过绑定层把C对象变成Python对象来操作。所以编译PyRosetta不是只编一个独立的小插件而是把整套C核心编译出来再把绑定层接上去。很多人低估了这部分工作量以为和普通Python包一样几分钟完事实际上首次编译一两个小时很正常。1.2 什么样的场景需要“绕远路”编译如果你只是想跑一些常规模拟官方预编译的PyRosetta包其实更方便解压即用不需要面对编译问题。但以下场景你会发现必须走编译这条路你手上有一个需要复现或改造的Rosetta本地分支已经改过C源代码必须从这版源码重新编译绑定层。你需要在特殊CPU架构或者特定Linux环境上运行而官方预编译包里没有对应兼容版本。你需要MPI并行支持想在Python端启动多个进程做大规模Rosetta计算预编译包往往不包含MPI版本。你希望针对当前机器CPU做指令集优化用-marchnative级别的方式彻底榨干性能。你需要在调试模式下编译从Python端追踪C层的问题预编译包帮不了你。还有一点比较隐蔽官方预编译包用到的是它自己要求的底层系统库版本和某些Linux发行版自带的版本容易冲突。表现就是import成功一旦初始化就报“符号找不到”的错或者直接段错误。自己编译能让所有动态库用当前机器同一套工具链生成兼容性反而更容易把控。1.3 编译安装与预编译包的取舍我给的判断标准很简单如果是急着出结果、机器上也没什么特殊要求直接用预编译包如果打算长期在这台集群上做科研、跑流水线或者要基于源码二次开发那值得花半天时间把环境一次理顺。编译的代价主要是时间和环境复杂度但收益也很实际。编译过程会让你把Rosetta工程结构、构建系统和依赖关系都过一遍之后遇到运行时问题的时候你能自己通过查询环境定位而不是拿着一堆预编译文件干瞪眼。从“会用”到“能调”的转变基本就是在这一次编译工程中发生的。2. 编译前的环境准备与依赖梳理2.1 硬件配置与磁盘规划先说硬件。编译Rosetta不是小事它包含几千个源文件编译任务并行度又高对CPU、内存、磁盘都有实打实的要求。我建议的底线配置CPU8核以上16核体验最佳。内存16G起步32G更稳。 很多人第一次编译时只给4G或8G编译到一半进程被系统OOM杀掉终端就显示一个孤零零的Killed。磁盘源码包解压后可能几个G但编译中间产物和缓存会暴涨建议至少预留50G。放在/tmp这种临时目录的风险很大编译中途清理临时文件可能导致重来。系统主流的Linux发行版都行但工具链版本要达到要求。GCC建议7以上新版本Rosetta建议9/10/11。我自己的实测大概是这样16核机器release模式编译完整二进制大概花一个半小时8核机器要三小时上下。如果并行开太多导致资源紧张时间反而会拉长。所以编译前先确认机器负载避免一边跑大型任务一边编译。2.2 依赖工具链安装清单我在不同Linux发行版上装的依赖不完全一样但有几个是通用且必须的C编译器、Python开发头文件、pybind11、Boost系列库、zlib、libjpeg、libpng、hdf5、sqlite3。在Debian系发行版上我习惯这样装sudo apt update sudo apt install -y gcc g gfortran sudo apt install -y python3 python3-dev python3-venv python3-pip sudo apt install -y build-essential cmake scons sudo apt install -y libboost-dev libboost-program-options-dev sudo apt install -y zlib1g-dev libjpeg-dev libpng-dev sudo apt install -y libhdf5-dev libsqlite3-dev在Red Hat系发行版上对应的包名略有差异建议先用包管理器搜索确认再装sudo dnf install -y gcc-c gcc-gfortran python3-devel cmake scons sudo dnf install -y boost-devel zlib-devel libjpeg-turbo-devel libpng-devel sudo dnf install -y hdf5-devel sqlite-devel这里最容易被忽略的是python3-dev或python3-devel。编译Python扩展模块时必须找到Python.h缺了这个头文件编译会在很早期就报“找不到Python.h”并停止。如果你发现系统的/usr/include/python3.x目录不存在基本就是没装开发包。另外一个重点是版本一致性。后面绑定层编译的时候用的Python和运行时导入的Python必须是同一个版本包括小版本号。最省心的做法就是一开始就建好虚拟环境所有编译操作都在虚拟环境内部进行避免和系统Python混在一起。2.3 获取Rosetta源码与目录规划Rosetta源码需要从官方渠道获取学术授权和非商业授权的下载入口不同商业用途需要申请商业授权。每个人的源码包文件名不一样但内部结构大体一致解压后通常会看到main、tools、tests、demos等目录。拿到源码包后第一件事是规划路径。我强烈建议放到一个全英文、没有空格、没有特殊符号的路径下例如export ROSETTA_ROOT/home/yourname/software/rosetta_src tar -xjf rosetta_src_release_xxx.tar.bz2 -C /home/yourname/software/之前有位同行把源码放在带中文目录的路径下结果编译过程中的脚本拼接路径时把文件名打乱了报错信息看起来像是编译器问题折腾半天才发现是路径编码问题。这种低级坑最好在开始前就避免。进入源码根目录后先确认构建方式。老版本一般在main/source下有scons.py新版本支持CMake构建根目录或main/source下面会有CMakeLists.txt。这一步决定了后半段用什么命令别凭经验硬套。ls $ROSETTA_ROOT/main/source/scons.py ls $ROSETTA_ROOT/main/source/CMakeLists.txt把环境变量先配好后面很多脚本都会引用export ROSETTA3$ROSETTA_ROOT/main/source export PYROSETTA_DIR$ROSETTA3/src/python/PyRosetta3. 完整编译流程与关键参数3.1 第一步编译Rosetta底层引擎不管最后用CMake还是老式构建脚本第一步都是先把Rosetta底层C编译出来。这一步生成的库文件是Python绑定层的链接基础也是整个编译过程中耗时最长、最容易出问题的一环。老版本构建方式cd $ROSETTA3 ./scons.py -j 16 moderelease bin这里逐个解释参数和背后原因moderelease表示编译release版本。release会开编译器优化产出速度快但debug信息少。如果之后要调试Rosetta或PyRosetta的C代码可以改成modedebug但编译时间更长、运行更慢日常计算不建议用。bin表示目标为构建全部可执行程序。bin目标依赖了核心库、应用层库和所有协议的编译相当于把Rosetta整个主干的依赖树都走一遍。-j并行编译任务数。不要盲目填满CPU核数我一般填核数减2。例如16核填14避免系统其他服务被饿死内存紧张时再降到8或4。新版本CMake构建方式cd $ROSETTA3 mkdir -p build cd build cmake .. -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX$ROSETTA3/build/bin cmake --build . --target bin -j 16CMAKE_BUILD_TYPERelease对应开启优化CMAKE_INSTALL_PREFIX指定安装路径后面--target bin和scons版本里的bin目标作用一致。这里有一个常见疑问我只是想用Python调PyRosetta为什么要把一堆命令行可执行程序也编出来直接编库不行吗原因是Rosetta的构建系统并不是为“只编绑定层”设计的。它整个依赖图围绕最终可执行程序组织Python绑定层的构建脚本会引用中间产物、库路径和编译期生成的配置文件。直接单独编一个绑定层目标除非你对构建系统非常熟悉否则会碰到大量缺失中间文件的问题。先跑一遍bin等于把依赖关系全部打通后面流程顺畅很多。3.2 第二步构建Python绑定模块底层引擎编译完成后进入PyRosetta目录开始绑定层编译。cd $PYROSETTA_DIR python setup.py build_ext --inplacebuild_ext是setuptools里构建扩展模块的标准命令--inplace表示把生成出来的.so模块直接放在源码树下方便未安装时导入测试。这一步会启动pybind11的代码生成和编译实际上又调用了一轮C编译器。不过因为底层库已经编好了它更多是在做胶水层和链接工作耗时相对可控。编译过程中会生成类似pyrosetta目录下的动态库文件例如带release标识的.so文件。如果磁盘紧张可以把编译临时目录指到别处python setup.py build_ext --inplace --build-temp /home/yourname/tmp/pyrosetta_build这里说一个容易踩的细节这一步执行时当前终端里的Python解释器版本必须和之后运行环境完全一致。最稳妥的做法是在准备阶段就建好虚拟环境之后切到虚拟环境里执行编译和安装。python3 -m venv /home/yourname/pyrosetta_env source /home/yourname/pyrosetta_env/bin/activate cd $PYROSETTA_DIR python setup.py build_ext --inplace3.3 第三步安装到虚拟环境并配置环境变量build_ext --inplace生成的模块已经能用了但为了不污染源码目录最好还是在虚拟环境里正式安装一遍cd $PYROSETTA_DIR pip install .如果你不想安装而是临时用也可以设置PYTHONPATH指向源码目录export PYTHONPATH$PYROSETTA_DIR:$PYTHONPATH不过这种方式在源码目录移动、重新编译或者切换分支时会非常混乱。我建议直接用虚拟环境安装保持环境清爽也让后面pip freeze能正确记录依赖。安装完成之后还需要确保Rosetta数据文件路径被正确识别。PyRosetta初始化时会加载打分函数参数、数据库文件等它查找路径的方式依赖环境变量。检查一下当前环境是否包含了ROSETTA3和常见的数据路径echo $ROSETTA3 echo $PYROSETTA_DIR如果发现环境变量消失了可以在虚拟环境的激活脚本里自动设置echo export ROSETTA3$ROSETTA3 $VIRTUAL_ENV/bin/activate echo export PYROSETTA_DIR$PYROSETTA_DIR $VIRTUAL_ENV/bin/activate以后激活虚拟环境就自动带上路径少掉很多不必要的烦恼。4. 安装后的验证与典型用法4.1 验证导入与基础初始化编译安装最刺激的时刻就是第一次import。我建议先用最小命令验证python -c import pyrosetta; pyrosetta.init(-mute all -run:version)如果没有任何报错说明Python扩展模块、底层库、数据文件路径这三个环节基本正常。这里-run:version会让初始化时打印出当前Rosetta版本信息方便确认你用的是哪套源码。如果看到“段错误”或者“ImportError”先别急着怀疑绑层代码。大部分这类问题本质上还是环境不一致比如虚拟环境没激活、用错了Python版本、动态库路径不对。下面第5节会详细讲排查方法。4.2 跑通一个简单的结构任务验证Phase通过后跑一个最简单的人小流程来确认打分函数和Pose操作正常import pyrosetta pyrosetta.init(-mute all) pdb_filename 1ubq.pdb pose pyrosetta.pose_from_pdb(pdb_filename) print(Total residues:, pose.total_residue()) scorefxn pyrosetta.create_score_function(ref2015) total_score scorefxn(pose) print(Total score:, total_score)手上没有PDB文件的话可以先直接构造一个空Posepose pyrosetta.Pose() sequence AAAAAAAAAAAAAAAAAAAA pyrosetta.toolbox.peptidesequence_from_sequence(sequence)不管用哪种方式只要能计算出分数就说明打分函数的数据表加载正常主流程已经可用。4.3 性能调优与多线程配置自己编译的一个主要优势是能做针对性优化。CMake构建时如果确定只在当前机器跑可以加上本地指令集优化-DCMAKE_CXX_FLAGS-marchnative-marchnative会让编译器根据当前CPU支持的指令集自动启用优化能提升一部分单线程性能。但这个选项生成的二进制不可移植拷到别的机器上可能直接非法指令崩溃所以只有确认环境固定时再用。使用层面上PyRosetta的多线程逻辑和Rosetta命令行的逻辑不完全一样。你如果开了MPI编译初始化时还需要指定对应的进程组import pyrosetta pyrosetta.init(-mpi_tracer_to_file ./log_)没有MPI需求就不要在编译时随意加上MPI选项否则初始化时可能要求额外的MPI运行时环境平白给部署找麻烦。我通常把MPI版本的编译做成独立环境和普通版本分开管理两边互不干扰。5. 高频问题与排查实录5.1 编译中途被Killed这是概率最高的编译事故。表现是编译进程跑到某一个链接或大文件编译阶段终端直接没了有时只有一个Killed字样。用dmesg | tail查看系统日志往往能看到内核的OOM killer痕迹。解决办法分三层第一降低并行度比如从-j 16改到-j 4。编译任务的内存峰值通常出现在链接大目标文件时并行任务越多峰值叠加越大。第二关闭图形界面、浏览器等大内存进程保证编译期间有充足内存。第三增加交换空间临时缓解短期峰值。比如加一个16G的swapfilesudo fallocate -l 16G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile但这只是缓兵之计长期使用应该在物理内存上做规划。5.2 第三方库版本冲突Rosetta源码树里经常自带一批第三方库比如boost和若干头文件库。如果系统也装了版本不同的同名库编译期可能出现模板报错、命名空间冲突甚至运行时符号错乱。我处理这类冲突的原则是尽量采取走通的方式。编译Rosetta主程序和Python绑定时优先使用Rosetta捆绑的库不要手动指定外部库路径如果系统库版本明显过新导致编译报错就通过环境变量或CMake选项强制指向源码自带库。排查的方法是注意看报错中引用的路径。报错里指向/usr/include/boost和/opt/rosetta/main/source/external/boost对应的是完全不同的处理思路前者是系统库后者是自带库。确定冲突来源后再决定改哪个路径不要乱加CFLAGS或CPLUS_INCLUDE_PATH硬怼。5.3 Python版本不匹配引发的问题我遇到过在编译时用系统Python 3.8运行时却切到conda环境Python 3.9的情况。结果import成功初始化阶段直接段错误。这个错误非常隐蔽因为不是明确的“版本不匹配”报错而是进程崩溃。排查方式是先确认当前Python是什么版本which python python -V再看编译时用了哪个Python。如果两个解释器路径不同在虚拟环境里重新编译一遍绑定层问题基本就消失了。我的经验是所有编译和安装动作开局先激活目标虚拟环境后面每一步都不换解释器就不会踩到这个深坑。5.4 动态库加载失败编译成功但import时报libxxx.so找不到或者直接报“cannot open shared object file”。这通常和动态库搜索路径有关用以下命令查看实际依赖ldd $PYROSETTA_DIR/pyrosetta/*.so如果输出里某行显示not found找到这个库本来在哪个目录下find /usr /lib /usr/local -name libxxx.so* 2/dev/null确认路径后把它加入动态库搜索路径export LD_LIBRARY_PATH/path/to/libdir:$LD_LIBRARY_PATH注意这是一种权宜之计更推荐的方式是通过虚拟环境配置或者包管理器把对应依赖装到系统标准目录免得每次启动终端都要先设置环境。5.5 数据文件路径找不到如果你编译、import、初始化都没有报错但一执行打分函数就提示找不到ref2015或某些参数文件多半是环境变量没配好。Rosetta启动时需要定位数据库目录而数据库目录路径来自编译期的默认值或环境变量。优先检查echo $ROSETTA3 pyrosetta.init_environment()确认数据库确实存在。如果不希望每次都设置环境变量可以在Python脚本里显式指定import os os.environ[ROSETTA3] /home/yourname/software/rosetta_src/main/source import pyrosetta pyrosetta.init(-mute all)5.6 编译器版本过旧新版本Rosetta大量使用C11之后的特性GCC版本太老会在编译早期报一堆头文件错误例如std::filesystem找不到之类。排查方式是确认编译器版本g --version如果版本明显太老用module或update-alternatives切换到新版本编译器。有些集群上默认的g是老版本但系统里其实安装了新版本只是没设置默认。切换后重编即可不需要改代码。6. 一些实操体会与建议最后分享几个我个人累积下来的操作习惯对降低编译难度帮助很大。第一个建议是不要在源码目录里硬编。编译产生的中间文件、缓存、临时对象会大量占用源码目录而且反复重试时容易和新源码混在一起。我用固定目录存放源码编译临时文件放到独立目录必要时直接删除重建不影响主源码。第二个建议是准备一个编译日志文件。编译输出几千上万行终端刷屏后你想回溯最早的错误极其痛苦。简单的方法是把日志同时落地./scons.py -j 16 moderelease bin 21 | tee build.log如果中途失败直接查日志开头和相关报错尾部效率高很多。第三个建议是尽量保留一个干净环境。不要一会儿在conda环境编一会儿切到系统Python编不要今天GCC 7明天GCC 11。任何一次环境切换都可能在某个隐蔽依赖上埋下雷。环境的一致性是整个编译工程里性价比最高的保障。第四个建议是初次编译先用小并行度跑通流程成功后再考虑开大并行做完整构建。很多人上来就-j 32结果资源耗尽被Killed反复几次心态先崩了。我先用-j 2或-j 4完整验证一遍构建链路没问题再用高并行重跑整体时间反而更短。编译安装PyRosetta这件事说白了就是一次对工具链、依赖、构建系统和Rosetta工程结构的全面摸底。前期准备做足了后半程会顺利很多准备不足每一个报错都可能是新的时间黑洞。希望这篇记录能帮你少走一点弯路把时间节省下来放到真正的研究和任务上。