layout: post
title: GROMACS编写轨迹分析工具的示例代码(gmx2018.2)
categories:
- 原始文档
- 2018-07-09 20:17:46 翻译: 苏耿; 校对: 包磊
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::IOptionsContainer
,basicoptions.h
和gmx::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. 在源文件中,在未命名的命名空间之外添加这些项的定义(替换Template
,AnalysisTemplate
和具有正确值的字符串):
<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评论将自己记录为文件的作者。