仓库源文.md),站点原文)


layout: post title: GROMACS编写轨迹分析工具的示例代码(gmx2018.2) categories:


GROMACS安装包括了一个使用轨迹分析框架编写轨迹分析工具的模板。

它可以从安装目录下的share/gromacs/template/和源代码分发中的share/template/中找到。

该文档的完整源代码也包含在本文档中:template.cpp本页的其余部分将介绍代码以解释不同的部分。

全局定义

首先包括一些通用的C++头文件:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #008800">#include</span> <span style="color: #008800; font-style: italic"><string></span><span style="color: #008800"></span> <span style="color: #008800">#include</span> <span style="color: #008800; font-style: italic"><vector></span><span style="color: #008800"></span> </pre></div>

并继续包括分析库的头文件:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #008800">#include</span> <span style="color: #008800; font-style: italic"><gromacs/trajectoryanalysis.h></span><span style="color: #008800"></span> </pre></div>

此头文件包含其他头文件,这些头文件一起定义了编写轨迹分析工具所需的所有基本数据类型。为方便起见,我们还将gmx命名空间中的所有名称导入全局范围,以避免在任何地方重复使用该名称:

<div class="highlight"><pre style="line-height:125%"><span></span>using namespace gmx; </pre></div>

工具模块类声明

然后我们定义一个实现分析工具的类:

<div class="highlight"><pre style="line-height:125%"><span></span>class <span style="color: #A0A000">AnalysisTemplate</span> : public TrajectoryAnalysisModule { <span style="color: #A0A000">public</span>: AnalysisTemplate(); virtual <span style="color: #00BB00; font-weight: bold">void</span> <span style="color: #00A000">initOptions</span>(IOptionsContainer <span style="color: #666666">*</span>options, TrajectoryAnalysisSettings <span style="color: #666666">*</span>settings); virtual <span style="color: #00BB00; font-weight: bold">void</span> <span style="color: #00A000">initAnalysis</span>(<span style="color: #AA22FF; font-weight: bold">const</span> TrajectoryAnalysisSettings <span style="color: #666666">&</span>settings, <span style="color: #AA22FF; font-weight: bold">const</span> TopologyInformation <span style="color: #666666">&</span>top); virtual <span style="color: #00BB00; font-weight: bold">void</span> <span style="color: #00A000">analyzeFrame</span>(<span style="color: #00BB00; font-weight: bold">int</span> frnr, <span style="color: #AA22FF; font-weight: bold">const</span> t_trxframe <span style="color: #666666">&</span>fr, t_pbc <span style="color: #666666">*</span>pbc, TrajectoryAnalysisModuleData <span style="color: #666666">*</span>pdata); virtual <span style="color: #00BB00; font-weight: bold">void</span> <span style="color: #00A000">finishAnalysis</span>(<span style="color: #00BB00; font-weight: bold">int</span> nframes); virtual <span style="color: #00BB00; font-weight: bold">void</span> <span style="color: #00A000">writeOutput</span>(); <span style="color: #A0A000">private</span>: class ModuleData; std<span style="color: #666666">::</span>string fnDist_; <span style="color: #00BB00; font-weight: bold">double</span> cutoff_; Selection refsel_; SelectionList sel_; AnalysisNeighborhood nb_; AnalysisData data_; AnalysisDataAverageModulePointer avem_; }; </pre></div>

分析工具类来自于gmx::TrajectoryAnalysisModule,这个接口具有一些便捷的函数用于与其他代码早期接口进行对接.下面,我们将介绍模板中实现的不同方法(请注意,模板不会实现某些虚拟方法,因为它们不太常用),讨论在更复杂的情况下可能出现的一些问题。 有关可用虚拟方法和便捷功能的完整说明,请参阅gmx::TrajectoryAnalysisModule的文档。第一个成员变量块用于包含提供给不同选项的值。它们将根据分析工具的需要而变化。AnalysisNeighborhood对象提供在分析中使用的邻域搜索。最后一个变量块用于处理输出数据。有关如何使用它们的详细信息,请参照initAnalysis()

对于模板,我们不需要任何自定义帧本地数据。 如果您认为需要一些更复杂的分析需求,请参阅gmx::TrajectoryAnalysisModuleData的文档以获取更多详细信息。 如果您不关心并行化,则无需考虑此部分。 您可以简单地在模块类中声明所有变量,在gmx::TrajectoryAnalysisModule::initAnalysis()中初始化它们,并在gmx::TrajectoryAnalysisModule::finishAnalysis()中进行任何后处理。

构造

分析模块的构造函数(和可能的析构函数)应该很简单:构造函数应该只是初始化默认值,析构函数应该释放模块管理的任何内存。对于模板,我们的类中没有需要显式释放的属性,因此我们只声明一个构造函数:

<div class="highlight"><pre style="line-height:125%"><span></span>AnalysisTemplate<span style="color: #666666">::</span>AnalysisTemplate() <span style="color: #666666">:</span> cutoff_(<span style="color: #666666">0.0</span>) { registerAnalysisDataset(<span style="color: #666666">&</span>data_, <span style="color: #BB4444">"avedist"</span>); } </pre></div>

输入选项

模块的初始化分为几个方法,其中两个在模板中使用。gmx::TrajectoryAnalysisModule::initOptions()用于设置模块理解的选项,以及通过gmx::TrajectoryAnalysisSettings设置不同的选项(有关更多详细信息,请参阅该类的文档):

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">void</span> AnalysisTemplate<span style="color: #666666">::</span>initOptions(IOptionsContainer <span style="color: #666666">*</span>options, TrajectoryAnalysisSettings <span style="color: #666666">*</span>settings) { <span style="color: #AA22FF; font-weight: bold">static</span> <span style="color: #AA22FF; font-weight: bold">const</span> <span style="color: #00BB00; font-weight: bold">char</span> <span style="color: #666666">*</span><span style="color: #AA22FF; font-weight: bold">const</span> desc[] <span style="color: #666666">=</span> { <span style="color: #BB4444">"This is a template for writing your own analysis tools for"</span>, <span style="color: #BB4444">"GROMACS. The advantage of using GROMACS for this is that you"</span>, <span style="color: #BB4444">"have access to all information in the topology, and your"</span>, <span style="color: #BB4444">"program will be able to handle all types of coordinates and"</span>, <span style="color: #BB4444">"trajectory files supported by GROMACS. In addition,"</span>, <span style="color: #BB4444">"you get a lot of functionality for free from the trajectory"</span>, <span style="color: #BB4444">"analysis library, including support for flexible dynamic"</span>, <span style="color: #BB4444">"selections. Go ahead an try it![PAR]"</span>, <span style="color: #BB4444">"To get started with implementing your own analysis program,"</span>, <span style="color: #BB4444">"follow the instructions in the README file provided."</span>, <span style="color: #BB4444">"This template implements a simple analysis programs that calculates"</span>, <span style="color: #BB4444">"average distances from a reference group to one or more"</span>, <span style="color: #BB4444">"analysis groups."</span> }; settings<span style="color: #666666">-></span>setHelpText(desc); options<span style="color: #666666">-></span>addOption(FileNameOption(<span style="color: #BB4444">"o"</span>) .filetype(eftPlot).outputFile() .store(<span style="color: #666666">&</span>fnDist_).defaultBasename(<span style="color: #BB4444">"avedist"</span>) .description(<span style="color: #BB4444">"Average distances from reference group"</span>)); options<span style="color: #666666">-></span>addOption(SelectionOption(<span style="color: #BB4444">"reference"</span>) .store(<span style="color: #666666">&</span>refsel_).required() .description(<span style="color: #BB4444">"Reference group to calculate distances from"</span>)); options<span style="color: #666666">-></span>addOption(SelectionOption(<span style="color: #BB4444">"select"</span>) .storeVector(<span style="color: #666666">&</span>sel_).required().multiValue() .description(<span style="color: #BB4444">"Groups to calculate distances to"</span>)); options<span style="color: #666666">-></span>addOption(DoubleOption(<span style="color: #BB4444">"cutoff"</span>).store(<span style="color: #666666">&</span>cutoff_) .description(<span style="color: #BB4444">"Cutoff for distance calculation (0 = no cutoff)"</span>)); settings<span style="color: #666666">-></span>setFlag(TrajectoryAnalysisSettings<span style="color: #666666">::</span>efRequireTop); } </pre></div>

对于模板,我们首先为工具设置描述文本(用于帮助文本)。然后我们声明一个选项来指定输出文件名,后跟用于设置选择的选项,最后是一个设置截止值的选项。对于cutoff,默认值将是在构造函数中设置的值,但也可以在此处显式设置它。用户为选项提供的值将存储在成员变量中。最后,我们指出该工具始终需要拓扑信息。这样做仅用于演示目的:即使没有拓扑,模板中的代码也能正常工作。

有关如何定义不同类型选项的其他文档,请参阅gmx::IOptionsContainerbasicoptions.hgmx::SelectionOption。 您只需要定义特定于分析的选项; 通用选项,例如,用于指定输入拓扑和添加轨迹框架。

要根据选项值调整设置或选择选项(例如,接受的选择数),您需要覆盖gmx::TrajectoryAnalysisModule::optionsFinished()。 为简单起见,这不是在模板中完成的。

分析初始化

实际分析在gmx::TrajectoryAnalysisModule::initAnalysis()中初始化:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">void</span> AnalysisTemplate<span style="color: #666666">::</span>initAnalysis(<span style="color: #AA22FF; font-weight: bold">const</span> TrajectoryAnalysisSettings <span style="color: #666666">&</span>settings, <span style="color: #AA22FF; font-weight: bold">const</span> TopologyInformation <span style="color: #666666">&</span> <span style="color: #008800; font-style: italic">/*top*/</span>) { nb_.setCutoff(cutoff_); data_.setColumnCount(<span style="color: #666666">0</span>, sel_.size()); avem_.reset(new AnalysisDataAverageModule()); data_.addModule(avem_); <span style="color: #AA22FF; font-weight: bold">if</span> (<span style="color: #666666">!</span>fnDist_.empty()) { AnalysisDataPlotModulePointer plotm( new AnalysisDataPlotModule(settings.plotSettings())); plotm<span style="color: #666666">-></span>setFileName(fnDist_); plotm<span style="color: #666666">-></span>setTitle(<span style="color: #BB4444">"Average distance"</span>); plotm<span style="color: #666666">-></span>setXAxisIsTime(); plotm<span style="color: #666666">-></span>setYLabel(<span style="color: #BB4444">"Distance (nm)"</span>); data_.addModule(plotm); } } </pre></div>

有关拓扑的信息作为参数传递。设置对象还可用于访问有关用户输入的信息。

此方法的主要任务之一是为它们设置适当的gmx::AnalysisData对象和模块(有关常规方法,请参阅gmx::TrajectoryAnalysisModule)。这些对象将用于处理工具的输出。 它们的主要目的是支持并行化,但即使您不关心并行性,它们仍然提供方便的构建块,例如,用于直方图和文件输出。

对于模板,我们首先设置邻域搜索的cutoff

然后,我们创建并注册一个gmx::AnalysisData对象,该对象将为每个帧包含每个输入选择的一列。这将包含工具的主要输出:参考选择与特定选择之间的最小距离。然后,我们创建并设置一个模块,该模块将计算每个选择的平均距离(请参阅writeOutput()了解其使用方式)。最后,如果提供了输出文件,我们将创建并设置一个模块,该模块将绘制到文件的每帧距离。

如果分析模块在处理帧期间需要一些临时存储(即,它使用从gmx::TrajectoryAnalysisModuleData派生的自定义类),如果并行化被支持则应该在gmx::TrajectoryAnalysisModule::startFrames()(见下文)中分配。

如果需要根据第一帧中的数据进行初始化(最常见的是,基于框大小),则需要覆盖gmx::TrajectoryAnalysisModule::initAfterFirstFrame(),但这不在模板中使用。

分析框架

还有一个初始化方法需要重写以支持自动并行化:gmx::TrajectoryAnalysisModule::startFrames()。如果您不需要自定义帧本地数据(或根本不需要并行化),则可以跳过此方法并忽略gmx::TrajectoryAnalysisModule::analyzeFrame()的最后一个参数,以使事情更简单。在模板中,此方法不是必需的。

分析的主要部分是(在大多数分析代码中)在gmx::TrajectoryAnalysisModule::analyzeFrame()方法中完成,每个帧调用一次:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">void</span> AnalysisTemplate<span style="color: #666666">::</span>analyzeFrame(<span style="color: #00BB00; font-weight: bold">int</span> frnr, <span style="color: #AA22FF; font-weight: bold">const</span> t_trxframe <span style="color: #666666">&</span>fr, t_pbc <span style="color: #666666">*</span>pbc, TrajectoryAnalysisModuleData <span style="color: #666666">*</span>pdata) { </pre></div>

frnr参数给出了当前帧的从零开始的索引(主要用于gmx::AnalysisData),pbc包含用于距离计算的当前帧的PBC信息,例如,pbc_dx(),并且pdata指向在startFrames()中创建的数据结构。虽然通常不需要(时间字段除外),但可以通过fr访问原始帧数据。在大多数情况下,应该编写分析,使其通过选择获得所有位置数据,并且不假设它们具有恒定的大小。这就是支持选择引擎的完全灵活性所需的全部内容。

对于模板,我们首先从我们的自定义数据结构中获取数据以进行速记访问(如果使用自定义数据对象,则需要static_cast):

<div class="highlight"><pre style="line-height:125%"><span></span>AnalysisDataHandle dh <span style="color: #666666">=</span> pdata<span style="color: #666666">-></span>dataHandle(data_); <span style="color: #AA22FF; font-weight: bold">const</span> Selection <span style="color: #666666">&</span>refsel <span style="color: #666666">=</span> pdata<span style="color: #666666">-></span>parallelSelection(refsel_); </pre></div>

然后我们进行简单的计算并使用AnalysisDataHandle类来设置工具的每帧输出:

<div class="highlight"><pre style="line-height:125%"><span></span>AnalysisNeighborhoodSearch nbsearch <span style="color: #666666">=</span> nb_.initSearch(pbc, refsel); dh.startFrame(frnr, fr.time); <span style="color: #AA22FF; font-weight: bold">for</span> (<span style="color: #00BB00; font-weight: bold">size_t</span> g <span style="color: #666666">=</span> <span style="color: #666666">0</span>; g <span style="color: #666666"><</span> sel_.size(); <span style="color: #666666">++</span>g) { <span style="color: #AA22FF; font-weight: bold">const</span> Selection <span style="color: #666666">&</span>sel <span style="color: #666666">=</span> pdata<span style="color: #666666">-></span>parallelSelection(sel_[g]); <span style="color: #00BB00; font-weight: bold">int</span> nr <span style="color: #666666">=</span> sel.posCount(); real frave <span style="color: #666666">=</span> <span style="color: #666666">0.0</span>; <span style="color: #AA22FF; font-weight: bold">for</span> (<span style="color: #00BB00; font-weight: bold">int</span> i <span style="color: #666666">=</span> <span style="color: #666666">0</span>; i <span style="color: #666666"><</span> nr; <span style="color: #666666">++</span>i) { SelectionPosition p <span style="color: #666666">=</span> sel.position(i); frave <span style="color: #666666">+=</span> nbsearch.minimumDistance(p.x()); } frave <span style="color: #666666">/=</span> nr; dh.setPoint(g, frave); } dh.finishFrame(); </pre></div>

处理完所有帧后,调用gmx::TrajectoryAnalysisModule::finishAnalysis()一次。这是对数据进行任何自定义后处理的地方。对于模板,我们什么都不做,因为所有必要的处理都在数据模块中完成:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">void</span> AnalysisTemplate<span style="color: #666666">::</span>finishAnalysis(<span style="color: #00BB00; font-weight: bold">int</span> <span style="color: #008800; font-style: italic">/*nframes*/</span>) { } </pre></div>

如果在gmx::TrajectoryAnalysisModule::startFrames()中创建的数据结构用于跨帧聚合数据,则需要覆盖gmx::TrajectoryAnalysisModule::finishFrames()以组合数据结构中的数据(详情请参阅方法文档)。这对于模板来说不是必需的,因为ModuleData结构仅包含在分析单个帧期间使用的数据。

输出

最后,大多数程序需要在分析完成后输出一些值。在某些情况下,这可以通过正确链接数据模块来实现,但通常需要进行一些自定义处理。所有这些活动都应该在gmx::TrajectoryAnalysisModule::writeOutput()中完成。这使得更容易在例如脚本语言中重用分析模块,其中可能不期望输出到文件中。模板只打印出每个分析组的平均距离:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">void</span> AnalysisTemplate<span style="color: #666666">::</span>writeOutput() { <span style="color: #008800; font-style: italic">// We print out the average of the mean distances for each group.</span> <span style="color: #AA22FF; font-weight: bold">for</span> (<span style="color: #00BB00; font-weight: bold">size_t</span> g <span style="color: #666666">=</span> <span style="color: #666666">0</span>; g <span style="color: #666666"><</span> sel_.size(); <span style="color: #666666">++</span>g) { fprintf(stderr, <span style="color: #BB4444">"Average mean distance for '%s': %.3f nm</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BB4444">"</span>, sel_[g].name(), avem_<span style="color: #666666">-></span>average(<span style="color: #666666">0</span>, g)); } } </pre></div>

在这里,我们使用avem_模块,我们在initAnalysis()中初始化它来聚合计算距离的平均值。

main()的定义

现在,唯一剩下的就是定义main()函数。 要实现命令行工具,它应该创建一个模块并使用gmx::TrajectoryAnalysisCommandLineRunner使用下面的样板代码运行它:

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #00BB00; font-weight: bold">int</span> <span style="color: #00A000">main</span>(<span style="color: #00BB00; font-weight: bold">int</span> argc, <span style="color: #00BB00; font-weight: bold">char</span> <span style="color: #666666">*</span>argv[]) { <span style="color: #AA22FF; font-weight: bold">return</span> gmx<span style="color: #666666">::</span>TrajectoryAnalysisCommandLineRunner<span style="color: #666666">::</span>runAsMain<span style="color: #666666"><</span>AnalysisTemplate<span style="color: #666666">></span>(argc, argv); } </pre></div>

GROMACS内置工具

使用模板实现的分析工具也可以轻松地包含在GROMACS库中。为此,请按照下列步骤操作:

1. 将您的工具源代码放入src/gromacs/trajectoryanalysis/modules/

2. 删除using namespace gmx;, 并将所有代码包含在gmx::analysismodules命名空间中,并将工具类放入其中未命名的命名空间中。

3. 创建与您的工具对应的头文件,并使用gmx::analysismodules命名空间将以下类添加到其中(将Template替换为您的工具名称):

<div class="highlight"><pre style="line-height:125%"><span></span>class TemplateInfo { <span style="color: #A0A000">public</span>: <span style="color: #AA22FF; font-weight: bold">static</span> <span style="color: #AA22FF; font-weight: bold">const</span> <span style="color: #00BB00; font-weight: bold">char</span> name[]; <span style="color: #AA22FF; font-weight: bold">static</span> <span style="color: #AA22FF; font-weight: bold">const</span> <span style="color: #00BB00; font-weight: bold">char</span> shortDescription[]; <span style="color: #AA22FF; font-weight: bold">static</span> TrajectoryAnalysisModulePointer <span style="color: #00A000">create</span>(); }; </pre></div>

4. 在源文件中,在未命名的命名空间之外添加这些项的定义(替换TemplateAnalysisTemplate和具有正确值的字符串):

<div class="highlight"><pre style="line-height:125%"><span></span><span style="color: #AA22FF; font-weight: bold">const</span> <span style="color: #00BB00; font-weight: bold">char</span> TemplateInfo<span style="color: #666666">::</span>name[] <span style="color: #666666">=</span> <span style="color: #BB4444">"template"</span>; <span style="color: #AA22FF; font-weight: bold">const</span> <span style="color: #00BB00; font-weight: bold">char</span> TemplateInfo<span style="color: #666666">::</span>shortDescription[] <span style="color: #666666">=</span> <span style="color: #BB4444">"Compute something"</span>; TrajectoryAnalysisModulePointer TemplateInfo<span style="color: #666666">::</span>create() { <span style="color: #AA22FF; font-weight: bold">return</span> TrajectoryAnalysisModulePointer(new AnalysisTemplate); } </pre></div>

5. 在src/gromacs/trajectoryanalysis/modules.cpp中注册您的模块。

6. 完成。现在可以使用您指定的名称作为gmx模板调用您的工具。

有关具体示例和文件的首选布局,请参阅src/gromacs/trajectoryanalysis/modules/中的现有工具。请使用现有文件中的Doxygen评论将自己记录为文件的作者。