基础教程

欢迎来到AiiDA教程!这篇教程的目的是告诉你AiiDA是如何帮助你执行数据驱动的工作流的。在该教程的接为,你将会知道:

  • 如何将数据储存进入数据库中,以及后续如何检索它们。

  • 对Python函数使用装饰器,这样其输入和输出会自动被记录。

  • 运行和监视计算例程的状态。

  • 查看可验证性图。

重要

如果你在你自己的机器上使用AiiDA,接下来的教程将会假设你已经在当前的环境中成功安装配置好AiiDA,并能正确运行了。如果没有,请参考 准备开始篇

可验证性(Provenance)

在我们正式开始前,有必要简短地介绍AiiDA的一个最重要的概念: 可验证性(provenance)。AiiDA的数据库不仅仅包含了你计算的结果,还包含了计算输入和输出间每一步执行的步骤。所有的这些信息都被储存在 (无环图)directed acyclic graph (DAG)中。 图 1 展现了这样一个可验证性图。

../_images/workchain_graph.png

图 1 一个基本的AiiDA工作链的可验证性图

在可验证性图中,你可以看到各种不同类型的*节点*,它们有不同的形状。绿色椭圆形的是 Data (数据)节点,蓝色椭圆形的是 Code 计算代码节点,长方形则表示 processes (计算例程),比如你的 工作流 中的算例。

通过可验证性图,你不仅可以看到你所拥有的数据,还可以看到数据是如何产生的。在这一教程中,我们将使用AiiDA一步步产生可验证性图 图 1

数据节点

在运行计算前,我们先创建并储存一个 数据节点。 AiiDA提供了一个可交互的 IPython shell,它包含了许多预加载的基础AiiDA类型。通过下面指令开启IPython shell:

$ verdi shell

AiiDA重实现了最常用的一些数据类型如(int, float, str等),如果你有需要,你可以拓展的创建特殊的组合数据类型。在该教程中,简单起见,我们初始化一个 Int 节点,并赋值给 node 变量:

In [1]: node = Int(2)

我们可以这样查看 node 变量的内容:

In [2]: node
Out[2]: <Int: uuid: eac48d2b-ae20-438b-aeab-2d02b69eb6a8 (unstored) value: 2>

将返回这一刚刚创建的节点的一些信息:

  • 数据节点的类型是 Int

  • 该节点的 通用唯一标识符 (UUID) eac48d2b-ae20-438b-aeab-2d02b69eb6a8

  • 该节点当前还没有被存入数据库中 (unstored)

  • 节点的值为整数 2

我们把节点存入数据库:

In [3]: node.store()
Out[3]: <Int: uuid: eac48d2b-ae20-438b-aeab-2d02b69eb6a8 (pk: 1) value: 2>

如您所见,数据节点现在已经被分配了一个 主键 (PK),这个数字标识数据库中的节点 (pk: 1)。PK和UUID都引用节点,唯一的区别是PK是唯一的 仅适用于您的本地数据库,而UUID是全局唯一标识符,因此可以在 *不同的*数据库之间使用。仅当您在单个数据库中工作时才使用PK,即在交互式会话中,在所有其他情况下使用UUID。

重要

本教程中的PK值是假设你从一个全空的数据库开始增加数据节点产生的。因此,教程中的数据节点的PK值可能会和你的数据库中的不同!

UUID是随机产生的,因此确定它们是不同的。

接下来,我们键入 exit() 来退出IPyhon shell并回车。回到终端后,使用 verdi 命令行接口(CLI)来查看刚刚你创建的数据节点:

$ verdi node show 1

这会打印如下信息:

Property     Value
-----------  ------------------------------------
type         Int
pk           1
uuid         eac48d2b-ae20-438b-aeab-2d02b69eb6a8
label
description
ctime        2020-05-13 08:58:15.193421+00:00
mtime        2020-05-13 08:58:40.976821+00:00

同样,我们可以看到该节点的类型为 Int ,其PK = 1, UUID = eac48d2b-ae20-438b-aeab-2d02b69eb6a8 。除了这些信息外, verdi node show 命令还显示了(empty) label and description ,以及节点被创建的时间( ctime )和最后修改的时间( mtime )。

注解

AiiDA已经提供了许多的标准数据类型,但你也可以 创建你自己的数据类型

算例函数(Calculation functions)

这样你的数据就被储存在数据库中了,你可以用它来进行计算任务。比如,你想要将两个整型 Int 数据节点相乘。有如下Python函数:

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

当应用到两个 Int 节点时,将给出想要的结果,但计算不会存储在可验证性图中。但是,我们可以使用AiiDA提供的 `Python装饰器`_ 自动使它成为可验证性图的一部分。再次使用 verdi shell 启动AiiDA IPython shell,并执行以下代码片段:

In [1]: from aiida.engine import calcfunction
   ...:
   ...: @calcfunction
   ...: def multiply(x, y):
   ...:     return x * y

这将 multiply 函数转换为AiIDA 算例函数,AiIDA中最基本的执行单元。接下来,使用 load_node 函数和数据节点的PK加载上一节中创建的 Int 节点:

In [2]: x = load_node(pk=1)

当然,我们需要另一个整数来和第一个相乘。让我们创建一个新的 Int 数据节点,并将其赋给变量 y:

In [3]: y = Int(3)

现在,我们可以将两个数相乘了!

In [4]: multiply(x, y)
Out[4]: <Int: uuid: 42541d38-1fb3-4f60-8122-ab8b3e723c2e (pk: 4) value: 6>

成功!``calcfunction`` -修饰 multiply 函数将两个 Int 数据节点相乘,并返回一个新的 Int 数据节点,其值为两个输入节点的乘积。注意,通过执行 multiply 函数,所有的输入和输出节点都会自动存储在数据库中:

In [5]: y
Out[5]: <Int: uuid: 7865c8ff-f243-4443-9233-dd303a9be3c5 (pk: 2) value: 3>

我们还没有存储分配给 y 变量的数据节点,但是通过将其作为 multiply 函数的输入参数,它将自动存储为PK = 2。类似地,返回值为6的 Int 节点存储在PK = 4中。

我们再次键入 exit() 离开IPython shell,并使用 verdi CLI来查看我们刚刚运行的计算例程:

$ verdi process list

返回的列表为空?别着急!默认情况下, verdi process list 只会返回正在运行的 active 的计算例程。如果你希望看到 所有的 计算例程(比如已经 结束的 计算例程),你只需要简单的在命令中加上 -a 选项:

$ verdi process list -a

你现在就可以看到如下的输出了:

  PK  Created    Process label    Process State    Process status
----  ---------  ---------------  ---------------  ----------------
   3  1m ago     multiply         ⏹ Finished [0]

Total results: 1

Info: last time an entry changed state: 1m ago (at 09:01:05 on 2020-05-13)

我们可以看到 multiply 算例在一分钟之前创建,并被赋值了主键PK 3,且完成执行 Finished

最后一步,让我们看看这个简单计算的可验证性。可验证性图可以使用verdi 命令行自动生成。让我们为刚刚运行的PK = 3的 multiply 计算函数生成出处图:

$ verdi node graph generate 3

该命令将出处图写入一个 .pdf 文件。使用您最喜欢的PDF查看器来查看。它应该类似于 图 2 中显示的图形。

../_images/calcfun_graph.png

图 2 multiply 算例函数的可验证性图。

注解

请注意, CalcJob 的主键值可以于你数据库中的不同。

CalcJobs (算例任务)

当运行需要外部代码或在远程机器上运行的计算时,一个简单的计算函数不再足够。为此,AiiDA提供了``CalcJob`` 例程类。

要运行一个 CalcJob,你需要设置两个东西:一个 code 用于实现所需的计算和一个 computer 用于实际计算运行。

如果您在Quantum Mobile VM或Binder上运行本教程,那么这些都已为您预先配置好了。如果您在自己的机器上运行,您可以遵循下面面板中的说明。

参见

关于如何 执行外部代码.

计算机的一个典型的现实例子是远程超级计算设施。代码可以是任何东西,从Python脚本到强大的 从头算 计算材料学代码(如Quantum Espresso)或机器学习工具(如Tensorflow)。让我们来看看可用的代码:

$ verdi code list
# List of configured codes:
# (use 'verdi code show CODEID' to see the details)
* pk 5 - add@tutor

您可以在打印的列表中看到一个计算代码 add@tutor,即PK = 5。这个计算代码允许我们将两个整数相加。 add@tutor 标识符表明带有标签 add 的代码在带有标签 tutor 的计算机上运行。要查看计算机的详细信息,可以使用以下 verdi 命令:

$ verdi computer show tutor
Computer name:     tutor
 * PK:             1
 * UUID:           b9ecb07c-d084-41d7-b862-a2b1f02722c5
 * Description:
 * Hostname:       localhost
 * Transport type: local
 * Scheduler type: direct
 * Work directory: /Users/mbercx/epfl/tutorials/my_tutor/work
 * Shebang:        #!/bin/bash
 * mpirun command: mpirun -np {tot_num_mpiprocs}
 * prepend text:
 # No prepend text.
 * append text:
 # No append text.

我们可以看到, Work目录 已经被设置为当前目录的 Work 子目录。这是在 tutor 计算机上运行的计算将被执行的目录。

注解

您可能已经注意到, tutor 计算机的PK是1,与我们在本教程开始时创建的 Int 节点相同。这是因为不同的实体(如节点、计算机和组)存储在数据库的不同表中。因此,每种实体类型的PK对于每个数据库都是惟一的,但是不同类型的实体可以在一个数据库中拥有相同的PK。

现在让我们再次启动 verdi shell ,并使用它的标签加载 add@tutor 代码:

In [1]: code = load_code(label='add')

每个代码都有一个方便的工具来设置所需的输入,称为 builder (构建器)。它可以通过 get_builder 方法获得:

In [2]: builder = code.get_builder()

使用构建器,您可以通过直接提供输入参数来轻松地设置计算。让我们使用由前面的``calcfunction``创建的``Int``节点作为一个输入,并使用一个新节点作为第二个输入:

In [3]: builder.x = load_node(pk=4)
   ...: builder.y = Int(5)

如果您的节点的PK不同,并且您不记得上一次计算的输出节点的PK,请检查您之前生成的起源图,并使用输出节点的UUID:

In [3]: builder.x = load_node(uuid='42541d38')
   ...: builder.y = Int(5)

注意,您不必提供整个UUID来加载节点。只要UUID的第一部分在您的数据库中是惟一的,AiiDA就会找到您要查找的节点。

注解

该构建器的一个出色特性是能够对输入使用tab补全。尝试在verdi shell中键入 builder. + <TAB>

要执行 CalcJob ,我们使用AiiDA引擎提供的 run 函数:

In [4]: from aiida.engine import run
   ...: run(builder)

等待流程完成。一旦完成,它将返回一个带有输出节点的字典:

Out[4]:
{'sum': <Int: uuid: 7d5d781e-8f17-498a-b3d5-dbbd3488b935 (pk: 8) value: 11>,
'remote_folder': <RemoteData: uuid: 888d654a-65fb-4da0-b3bc-d63f0374f274 (pk: 9)>,
'retrieved': <FolderData: uuid: 4733aa78-2e2f-4aeb-8e09-c5cfb58553db (pk: 10s)>}

除了两个 Int 节点的和之外,计算函数还返回另外两个输出:一个类型为 RemoteData ,另一个类型为 FolderData 。更多细节请参见 算例作业部分。现在,退出IPython shell,再次检查 所有 进程:

$ verdi process list -a

您现在应该看到列表中有两个进程。一个是你之前运行的 multiply 算例函数,第二个是你刚刚运行的 ArithmeticAddCalculation 算例任务。获取 ArithmeticAddCalculation 的PK,并生成可验证性图。结果应如 图 3 所示。

$ verdi node graph generate 7
../_images/calcjob_graph.png

图 3 ArithmeticAddCalculation 算例任务的可验证性图,由 multiply 算例函数的输出作为其一个输入。

你可以使用verdi shell来查看更为详细的包含输入输出的计算例程的信息:

$ verdi process show 7

提交到后台

当我们在前一节中使用 run 命令时,IPython shell在等待 CalcJob 完成时被阻塞。当我们简单地把两个数相加时,这不是一个问题,但如果我们想要运行多个计算,需要数小时或数天,这就不再实用了。相反,我们将把 CalcJob 提交给AiiDA 守护进程。守护进程是一个在后台运行的程序,管理提交的计算,直到守护进程本身被 终止 为止。让我们首先使用 verdi 命令行检查守护进程的状态:

$ verdi daemon status

如果后台守护进程正在运行,则会有如下输出:

Profile: tutorial
Daemon is running as PID 96447 since 2020-05-22 18:04:39
Active workers [1]:
  PID    MEM %    CPU %  started
-----  -------  -------  -------------------
96448    0.507        0  2020-05-22 18:04:39
Use verdi daemon [incr | decr] [num] to increase / decrease the amount of workers

此时,我们将其停止:

$ verdi daemon stop

接下来,让我们 提交 前面运行的 CalcJob 。启动 verdi shell 并执行下面的Python代码片段。它遵循了我们之前所做的所有步骤,但是现在使用 submit 函数而不是 run:

In [1]: from aiida.engine import submit
   ...:
   ...: code = load_code(label='add')
   ...: builder = code.get_builder()
   ...: builder.x = load_node(pk=4)
   ...: builder.y = Int(5)
   ...:
   ...: submit(builder)

当使用 submit 时,计算作业不会在本地解释器中运行,而是被发送给守护进程,你可以立即获得控制权。它返回的不是计算的 结果 ,而是刚刚提交的 CalcJob 的节点:

Out[1]: <CalcJobNode: uuid: e221cf69-5027-4bb4-a3c9-e649b435393b (pk: 12) (aiida.calculations:arithmetic.add)>

退出IPython shell并查看计算例程列表:

$ verdi process list

你会看到你刚刚提交的算例任务 CalcJob ,其状态为 Created

  PK  Created    Process label             Process State    Process status
----  ---------  ------------------------  ---------------  ----------------
  12  13s ago    ArithmeticAddCalculation  ⏹ Created

Total results: 1

Info: last time an entry changed state: 13s ago (at 09:06:57 on 2020-05-13)

CalcJob 进程现在正在等待被守护进程运行程序拾取,但是守护进程目前是禁用的。让我们(再)启动它:

$ verdi daemon start

现在你可以使用 verdi process list 来跟踪 CalcJob 的执行,或者 watch 它的进度:

$ verdi process watch 12

让我们等待 CalcJob 完成,然后使用 verdi process list -a 查看到目前为止我们已经运行的所有进程:

  PK  Created    Process label             Process State    Process status
----  ---------  ------------------------  ---------------  ----------------
   3  6m ago     multiply                  ⏹ Finished [0]
   7  2m ago     ArithmeticAddCalculation  ⏹ Finished [0]
  12  1m ago     ArithmeticAddCalculation  ⏹ Finished [0]

Total results: 3

Info: last time an entry changed state: 14s ago (at 09:07:45 on 2020-05-13)

工作流

到目前为止,我们已经手动执行了每个流程。AiiDA允许我们通过将它们链接到一个 工作流 中来自动化这些步骤,其可验证性被存储以确保复现性。在本教程中,我们准备了一个基本的 WorkChain ,它已经在 aiida-core 中实现了。你可以看到下面的代码:

注解

除了工作链,工作流也可以作为 工作函数 来实现。对于计算量不是很大且可以很容易地在Python函数中实现的工作流来说,这是理想的。

让我们运行上面的 工作链 !启动 verdi shell 并使用 WorkflowFactory 加载 MultiplyAddWorkChain:

In [1]: MultiplyAddWorkChain = WorkflowFactory('arithmetic.multiply_add')

WorkflowFactory 是一个有用的工具,用于基于工作流的 接入点 加载工作流,例如本例中的 'arithmetic.multiply_add' 。类似于 CalcJobWorkChain 输入可以使用构建器进行设置:

In [2]: builder = MultiplyAddWorkChain.get_builder()
   ...: builder.code = load_code(label='add')
   ...: builder.x = Int(2)
   ...: builder.y = Int(3)
   ...: builder.z = Int(5)

一旦 WorkChain 输入设置好了,我们就使用AiiDA引擎的 submit 函数将它提交给守护进程:

In [3]: from aiida.engine import submit
   ...: submit(builder)

退出IPython shell并查看计算例程列表:

$ verdi process list -a

你将会看到如下的输出,当然输出略有不同,取决于工作流运行到了哪里:

  PK  Created    Process label             Process State    Process status
----  ---------  ------------------------  ---------------  ------------------------------------
   3  7m ago     multiply                  ⏹ Finished [0]
   7  3m ago     ArithmeticAddCalculation  ⏹ Finished [0]
  12  2m ago     ArithmeticAddCalculation  ⏹ Finished [0]
  19  16s ago    MultiplyAddWorkChain      ⏵ Waiting        Waiting for child processes: 22
  20  16s ago    multiply                  ⏹ Finished [0]
  22  15s ago    ArithmeticAddCalculation  ⏵ Waiting        Waiting for transport task: retrieve

Total results: 6

Info: last time an entry changed state: 0s ago (at 09:08:59 on 2020-05-13)

我们可以看到 MultiplyAddWorkChain 目前正在等待它的 子进程 ArithmeticAddCalculation 的完成。再次检查 所有 过程列表(你现在应该知道怎么做了!)大约半分钟后,所有进程都应该处于 Finished 状态。verdi process status 命令可打印工作链调用的进程的 层次 概述:

$ verdi process status 19
MultiplyAddWorkChain<19> Finished [0] [3:result]
    ├── multiply<20> Finished [0]
    └── ArithmeticAddCalculation<22> Finished [0]

括号 [3:result] 显示当前步骤的概述 MultiplyAddWorkChain (步骤3,名称 result)。process status 对于调试复杂的工作链特别有用,因为它有助于查明问题发生的位置。

我们现在可以用以下方法为 WorkChain 生成完整的可验证性图:

$ verdi node graph generate 19

看起来熟悉吗?可验证性图应该类似于我们在本教程开始时显示的(图 4)。

../_images/workchain_graph.png

图 4 基础AiIDA教程后生成的可验证性图

下一步

恭喜!你成功完成了我们的初级试炼,成为AiiDA专家指日可待。

我们还准备了非常实用的详细操作篇,对于下面的使用场景非常有用: