8. 轨迹变换(“即时”变换)

在MDAnalysis中,一个 变换 是一个函数/类似函数的类,用于修改当前 Timestep 并返回 Timestep 。例如,一些分析和可视化通常需要进行坐标变换,如PBC校正和分子拟合。变换函数 (transformation_1transformation_2 在下面的示例中)可以由用户针对任何给定的 Timestep 在弹道上,

u = MDAnalysis.Universe(topology, trajectory)

for ts in u.trajectory:
   ts = transformation_2(transformation_1(ts))

在那里他们改变时间步长的坐标 ts 就位了。这些转换没有什么特别之处,只是它们必须以这样一种方式编写,即它们可以更改 Timestep 就位了。

如下所述 工作流程 ,可以将多个变换组合在一起并与轨迹相关联,从而使该轨迹 transformed on-the-fly 也就是说,从轨迹文件读取的数据将在例如 AtomGroup.positions 属性。

子模块 MDAnalysis.transformations 包含转换的集合(请参见 MDAnalysis中的转换 ),可以立即使用,但用户始终可以编写自定义转换(请参见 正在创建转换 )。

8.1. 工作流程

与手动应用转换相比,将整个 工作流程 具有轨迹的变换,并自动调用变换。

工作流是将按此顺序应用的转换函数的序列(元组或列表)。例如,

workflow = [transformation_1, transformation_2]

将有效地导致

ts = transformation_2(transformation_1(ts))

在轨道上的每一步。

用户可以使用添加的 Universe.trajectory.add_transformations 轨迹的方法(其中列表 workflow 取自上面的示例),

u.trajectory.add_transformations(*workflow)

或在 Universe 使用关键字参数创建 transformations

u = MDAnalysis.Universe(topology, trajectory, transformations=workflow)

请注意,在这两种情况下,工作流在添加后不能更改。

8.2. 正在创建转换

一个简单的 变换 也可以是一个以 Timestep 作为输入,修改它,然后返回它。如果它不需要其他参数,只需要 Timestep 可以定义为以下示例:

def up_by_2(ts):
    """
    Translate all coordinates by 2 angstroms up along the Z dimension.
    """
    ts.positions = ts.positions + np.array([0, 0, 2], dtype=np.float32)
    return ts

如果转换需要除 Timestep ,可以使用以下两种方法来创建此类转换:

8.2.1. 创建复杂的转换类

它是通过继承 MDAnalysis.transformations.base.TransformationBase ,它定义了 __call__() 对于转换类,可以直接应用于 Timestep_transform() 必须进行定义,并包括对 MDAnalysis.coordinates.base.Timestep

因此,转换类可以大致定义如下:

from MDAnalysis.transformations import TransformationBase

class up_by_x_class(TransformationBase):
    def __init__(self, distance):
        self.distance = distance

    def _transform(self, ts):
        ts.positions = ts.positions + np.array([0, 0, self.distance], dtype=np.float32)
        return ts

它是中的默认构造方法 MDAnalysis.transformations 从2.0.0版开始,因为它可以可靠地序列化。看见 MDAnalysis.transformations.translate 举个简单的例子。

8.2.2. 创建复杂的变换闭包函数

转换也可以是包装函数,该函数采用 Timestep 对象作为参数。因此,在这种情况下,变换函数(闭包)可以大致定义如下:

def up_by_x_func(distance):
    """
    Creates a transformation that will translate all coordinates by a given amount along the Z dimension.
    """
    def wrapped(ts):
        ts.positions = ts.positions + np.array([0, 0, distance], dtype=np.float32)
        return ts
    return wrapped

使用包装函数的另一种方法是使用 functools 。上面的函数可以写成:

import functools

def up_by_x(ts, distance):
    ts.positions = ts.positions + np.array([0, 0, distance], dtype=np.float32)
    return ts

up_by_2 = functools.partial(up_by_x, distance=2)

尽管函数(闭包)用作转换,但从2.0.0版开始,在MDAnalysis中不使用它们,因为它们不能可靠地序列化,因此 Universe 这样的变换不能与常见的并行化方案(例如,基于 multiprocessing )。有关如何编写闭包样式转换的详细描述,请参考MDAnalys1.x文档。

8.3. MDAnalysis中的转换

该模块 MDAnalysis.transformations 包含可立即在您自己的 workflows 。为了使用这些转换中的任何一种,必须首先导入模块:

import MDAnalysis.transformations

然后,可以如上所述将工作流添加到轨迹。值得注意的是,参数 max_threads 可以在创建转换实例时定义,以限制最大线程数。(请参阅 MDAnalysis.transformations.base.TransformationBase 有关更多详细信息)可以通过检查特定转换是否可以与并行分析一起使用 parallelizable 属性。

看见 当前实施的转换 中有关现有转换的更多信息 MDAnalysis.transformations

8.4. 如何实现转型

转换单个帧的坐标(尽管通常会将转换添加到 workflow ,如后续示例中所示):

u = MDAnalysis.Universe(topology, trajectory)
new_ts = MDAnalysis.transformations.translate([1,1,1])(u.trajectory.ts)

创建工作流并将其添加到轨迹:

u = MDAnalysis.Universe(topology, trajectory)
workflow = [MDAnalysis.transformations.translate([1,1,1]),
            MDAnalysis.transformations.translate([1,2,3])]
u.trajectory.add_transformations(*workflow)

在定义领域时,将工作流作为关键字参数:

workflow = [MDAnalysis.transformations.translate([1,1,1]),
            MDAnalysis.transformations.translate([1,2,3])]
u = MDAnalysis.Universe(topology, trajectory, transformations=workflow)

8.5. 转换类的构建块

转换通常利用NumPy的能力来获得更好的数组操作性能。然而,当涉及到并行性时,NumPy有时会通过超线程(当它使用OpenBlas后端时)或通过与其他并行引擎(例如DASK)一起工作来超额订阅线程。

在MDAnalysis中,我们使用 threadpoolctl 内部 TransformationBase 以控制转换的最大线程数。

还可以通过设置外部环境变量来应用全局线程限制,例如 OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 BLIS_NUM_THREADS=1 python script.py 。阅读有关并行性和资源管理的更多信息 scikit-learn documentations

建议用户对代码进行基准测试,因为在默认情况下,不同库之间的交互可能会导致性能不佳。

8.6. 当前实施的转换