概念

算例是一个能够创建新数据的例程 (详情参见 例程章节 ) 。 当前,有两种算例的实现,分别是:

前者是简单的将一个python函数转变为一个例程(process),这适用于计算量不大且易于实现的python函数。对于复杂的计算任务,通常需要外部的计算软件参与,这些代码通常运行在远端的集群上,因此使用算例任务更为合适。

下列章节将分别介绍两者,但是这里只是简单介绍概念而不深入详细的实现和使用细节。详细的内容请参考相应的进阶章节 算例函数算例任务

算例函数(Calculation functions)

以下列计算任务为例:

给定三个整数,将前两者求和后与第三者相乘。

常规的python代码如下:

# -*- coding: utf-8 -*-
def add(x, y):
    return x + y

def multiply(x, y):
    return x * y

result = multiply(add(1, 2), 3)

这些代码会直接得到想要的结果,但是,并没有关于运算过程的可验证性。结果与输入之间并没有保留运算关系。 为保留运算过程,我们使用 calcfunction()。 AiiDA的 calcfunction 是一个 装饰器 它将一个函数装饰成为一个算例例程,这使得其可以自动将得到结果的计算过程储存为一个 可验证性图 。. 使用 calcfunction 装饰器更性上面代码片段后为:

# -*- coding: utf-8 -*-
from aiida.engine import calcfunction

@calcfunction
def add(x, y):
    return x + y

@calcfunction
def multiply(x, y):
    return x * y

result = multiply(add(1, 2), 3)

我们只需要简单的在每一个函数前增加 @calcfunction 装饰器。 增加的装饰器使得 AiiDA 知晓被装饰的函数在执行后的可验证性需要被储存进可验证性图中 。 也就是执行函数后将输入和输出与一个算例节点连接。 新的代码块还涉及将输入和输出储存进可验证性图。之前的代码中,输入和输出都是普通的python整型数据结构,是无法作为节点自动存入可验证性图中的。为实现该特性,用户只需要将输入输出封装成 Int 类即可,这样他们就能够被储存进入数据库中了。

# -*- coding: utf-8 -*-
from aiida.engine import calcfunction
from aiida.orm import Int

@calcfunction
def add(x, y):
    return x + y

@calcfunction
def multiply(x, y):
    return x * y

result = multiply(add(Int(1), Int(2)), Int(3))

前后函数中的的代码差异就只是输入被封装成了 Int 类。 同样返回的结果也被封装成 Int 包含计算结果的节点,可以储存进可验证性图中。

注解

因为函数 addmultiply 中的 xy 都已经是 Int 类的实例,因为所有的运算都已经重载,无需类型转换便可以用于AiiDA类( Int, Float, 等),他们等价于python本身的类型。需要清楚的记住只有 Node 的实例,或是其子类的实例可以被储存进数据库。有关如何从例程函数中返回可储存的结果,请参考 进阶章节

在这样简单的修改后,函数运行并产生结果的整个可验证性就被保留下来,如下所示:

../../_images/add_multiply_calcfunction_data.png

图 13 算例例子生成的可验证性图

上面的例子展示了如何运行一个算例:简单的调用并运行。算例返回的结果就是对应函数的结果。然而用户可能还会想要得到有关算例节点的信息。下列代码块展示了如何用另外两个函数在运行的同时获得例程的 pk 和节点信息。

# -*- coding: utf-8 -*-
from aiida.engine import calcfunction, run, run_get_node, run_get_pk
from aiida.orm import Int

@calcfunction
def add(x, y):
    return x + y

x = Int(1)
y = Int(2)

result = run(add, x, y)
result, node = run_get_node(add, x, y)
result, pk = run_get_pk(add, x, y)

这是一个关于算例函数的一个简单概述。请从以下链接 启动例程 <topics:processes:usage:launching>` 中获得有关如可启动例程的详细细节。如果你想要知晓有关如何实现一个算例函数并找到更为详细的示例,请参考章节 算例函数的编写

算例任务(Calculation jobs)

在前面章节 算例函数 ,我们展示了如何将简单的python函数转换成一个例程,以使其在启动执行后,其结果能够自动记录在可验证性图中。然而并非所有计算过程都能够通过简单的python函数实现,而是通常涉及外部代码来实现。为了实现外部代码与内部引擎的接口 AiiDA 引入了 CalcJob process 类 。 可以在 后续章节 中找到关于如何实现与外部代码接口的细节和示例。这里,只关注算例任务的整体特征和使用场景,即如何通过算例任务来驱动外部代码的运行。

为了展示算例任务是如何工作的,我们首先需要一份外部代码样例。我们假设一个外部代码是一个bash脚本,它读入一个包含两个整数的文件,并将他们求和后返回结果,如:

#!/bin/bash
# Read two integers from file 'aiida.in' and echo their sum
x=$(cat aiida.in | awk '{print $1}')
y=$(cat aiida.in | awk '{print $2}')
echo $(( $x + $y ))

运行后,该脚本会从名为 aiida.in 的文件中读取数据,数据需要是两整数。脚本会将这两个整数解析成变量 xy ,并打印求和后的结果。当你要用AiiDA运行这段代码,你需要告诉AiiDA如何运行这段代码。 ArithmeticAddCalculation 就是一个实现与上述脚本接口的算例任务。 一个 CalcJob 就是一个关于特定代码如何运行的接口,通常作为一个计算插件调用,它的功能主要是告知AiiDA如何运行这样一份外部代码。这包括了如何从输入产生需要的输入文件,如何运行外部代码,以及当外部代码运行完成后如何取回运行结果文件。之所以需要 ‘取出’ 运行结果,是因为算例任务可能不仅仅在本地运行,还会通过SSH在远端已经 配置入AiiDA配置文件 的集群上运行。

因为 CalcJob 和前面所述的 算例函数 一样是一个例程,所以他们通过相同的方式运行。

# -*- coding: utf-8 -*-
from aiida.engine import run
from aiida.orm import load_code, Int
from aiida.plugins import CalculationFactory

ArithmeticAddCalculation = CalculationFactory('arithmetic.add')

inputs = {
    'code': load_code('add@localhost'),
    'x': Int(1),
    'y': Int(2),
}

run(ArithmeticAddCalculation, **inputs)

通过运行算例任务产生的可验证性图如下所示:

../../_images/arithmetic_add.png

图 14 算例任务例子生成的可验证性图

算例任务的执行在可验证性图中表示为一个例程节点,如图所示,图 图 14 中粉色正方形标注 C:sub:`1` 的节点。输入参数 xy 以及使用的 code 都与例程节点连接。 code 作为输入参数节点在 所有的 算例任务中都存在,用以表示真实执行的外部代码。 节点是 Code 类的实例,该类是 Data 类的子类。因此该节点同样是一个数据节点。该节点的作用是记录外部可执行代码以及代码相关的信息 这写信息是在 计算代码(code) 设置 中提供的。

这个算例任务生成两个输出,一个是包含了 xy 求和结果的整数数据节点,和一个数据类型为 FolderData 的节点,该节点包含了存放计算结果的文件夹。需要注意到的是,(除了 retrieved 节点)所有的算例任务的计算结果都不是由其自身产生的,而是通过 Parser 类解析产生的。原则上解析的步骤不是必须的因此算例任务可以没有输出,但一定会有 retrieved` 数据节点。有关算例任务中结果解析器概念的详细内容请参考 这里

传输任务

AiiDA内部引擎执行了一系列工作,以实现可验证性图 图 14 。 当一个算例任务启动时,AiiDA内部引擎执行下列步骤:

  • 上传 : 算例任务会从输入节点得到输入文件,再将输入文件上传到目标计算机器的工作目录中

  • 提交 : 提交一个作业到作业调度工具到配置文件中 code 所在的计算机。

  • 更新: 内部引擎会不断查询作业系统以返回算例任务的状态。

  • 获取: 一旦作业结束,引擎会取回输出文件,依据算例插件中定义的方式将输出结果储存在可验证性图的节点中。

以上的所有任务都需要AiiDA内部引擎与计算资源间的交互。因为将 Code 配置为与特定计算资源:py:class:~aiida.orm.computers.Computer`关联并作为算例任务的输入,引擎会知晓如何运行这些任务。 ``CalcJob` 的实现是独立的,因此在内部代码中不依赖 code 和计算资源信息。因此要在另外的计算资源中运行这个算例任务,用户只需要改变对特定计算资源配置的 code 输入即可。之前提到,算例任务不但可以运行在AiiDA引擎运行的机器上,还能运行在远端的机器上。通过输入参数 code 就能指定要运行的机器,因为计算代码(code)中包含了运行代码的特定机器的信息。如果机器不是在本地,AiiDA引擎需要连接远端机器以执行上面四个任务。引擎连接远端计算资源的操作称作 transport (传输),因此完成该操作的任务称作 传输任务

指数退让机制

当在远端机器执行算例的过程中,AiiDA引擎需要为每一个传输任务建立一条链接。当连接远端服务器时,所有服务器端的潜在问题都可能发生,这会导致算例任务失败。比如,远端计算资源可能宕机而导致结果无法获得,或者引擎所在的本地端可能无法连接网络。然而,这些问题都是不确定的。为了防止算例任务异常或一直无法连接,AiiDA使用了 指数退让策略 。当引擎执行了一个传输任务时遇到异常,算例任务不会直接失败,而是提交一个相同的任务后续重新执行。任务会自动重新加入作业系统直到其顺利结束。在连续5次尝试后,若任务还是失败,引擎会单纯的暂停算例任务而不是重新提交。verdi process list 能够输出有关任务失败的更多信息:

  PK  Created     State           Process label                 Process status
----  ----------  ------------    --------------------------    ---------------------------------------------------------------------------------------
 151  1h ago      ⏸ Waiting       ArithmeticAddCalculation      Pausing after failed transport task: retrieve_calculation failed 5 times consecutively


Total results: 1

当算例任务因为传输任务失败后且反复重试后依旧无法连接而暂停后,用户可以查看问题。如果发现问题是临时发生的,且问题被解决后,用户可以重新使用 verdi process play 来恢复暂停的任务。AiiDA引擎会自动重新提交之前失败的任务,且暂停的算例任务会继续在停止的地方恢复执行。

该指数退让机制是的AiiDA内部引擎对于算例任务的稳健性提高,将临时问题发生所导致的计算资源浪费减少到最小。

注解

当前有关该策略的设置参数如重试间隔延时和最大重试次数都不能够修改,但在未来可能会支持修改。

解析器

前面章节阐述了 CalcJob 类函数接口连接AiiDA引擎和外部运行代码。算例任务插件还会告知引擎 传输任务 该如何结束。如前所述,任务在输出文件获取后停止,这时引擎会在算例任务节点后增加标记为 retrievedFolderData 节点。这样算例任务就顺利完成了。然而用户通常还会想要将输出文件解析为特定的输出并表示为可验证性图中的节点。这可以通过 Parser 类来实现并在算例任务的输入中指定。这样,引擎就会在任务成功结束并创建输出文件后调用解析器。解析器通过以下方式实现,在获取文件后解析并转换成输出节点。有关解析器的实现细节以及如何在算例任务输入中指定解析器,请参考 解析器详情章节