如何处理数据

导入数据

AiiDA允许用户将数据从数据库导出到归档文件,归档文件可以导入到任何其他AiiDA数据库中。如果你有一个想要导入的AiiDA导出存档,你可以使用 verdi archive import 命令(详见 参考章节 )。

注解

有关通过AiiDA档案导出和导入数据的信息,请参见 如何共享数据

相反,如果您拥有尚未成为AiiDA导出归档的一部分的现成数据,例如文件、文件夹、表格数据、数组或任何其他类型的数据,此操作指南将向您展示如何将它们导入AiiDA。

要在AiiDA中存储任何数据,需要将其包装在 Data 节点。可验证性图这样它可以在 可验证性图 中表示。这个``Data``类有不同的变种或子类,它们适用于不同类型的数据。AiiDA提供了许多内置数据类型。你可以使用 verdi plugin 命令列出这些插件。执行 verdi plugin list aiida.data 应显示如下内容:

Registered entry points for aiida.data:
* array
* bool
* code
* dict
* float
* folder
* list
* singlefile

Info: Pass the entry point as an argument to display detailed information

如输出所示,您可以通过在命令后面附加名称来获得关于每种类型的更多信息,例如, verdi plugin list aiida.data singlefile:

Description:

The ``singlefile`` data type is designed to store a single file in its entirety.
A ``singlefile`` node can be created from an existing file on the local filesystem in two ways.
By passing the absolute path of the file:

    singlefile = SinglefileData(file='/absolute/path/to/file.txt')

or by passing a filelike object:

    with open('/absolute/path/to/file.txt', 'rb') as handle:
        singlefile = SinglefileData(file=handle)

The filename of the resulting file in the database will be based on the filename passed in the ``file`` argument.
This default can be overridden by passing an explicit name for the ``filename`` argument to the constructor.

如所见, singlefile 类型对应于 SinglefileData`类,被用于包装存储在本地文件系统上的单个文件。如果你有这样一个单独的文件,想将其存储在AiiDA中,你可以使用``verdi shell` 创建它:

SinglefileData = DataFactory('singlefile')
singlefile = SinglefileData(file='/absolute/path/to/file.txt')
singlefile.store()

第一步是加载与数据类型对应的类,这是通过将名称(由``verdi plugin list aiida.data``列出)传递给:DataFactory 来完成的。然后构造该类的一个实例,将感兴趣的文件作为参数传递。

注解

构造任何特定数据类型实例的确切方式取决于类型。使用``verdi plugin list aiida.data <ENTRY_POINT>``。命令获取任何特定类型的更多信息。

注意,在构造之后,您将获得一个 未存储的 节点。这意味着此时您的数据还没有存储在数据库中,您可以首先检查它并有自定义地修改它。如果您对结果满意,您可以通过调用 store() 来永久存储新数据。每个节点在创建时都分配了一个通用唯一标识符(UUID),存储后还分配了一个主键(PK),可以通过UUID和PK检索主键。您可以使用这些标识符来引用或检索节点。查找和检索以前导入的数据的方法在 如何查找数据 一节中描述。

如果 verdi plugin list 列出的类型中没有你可用的数据类型,适合你的需要,你也可以创建自己的自定义类型。详细信息请参考下一节 如何添加自定义数据类型的支持

查找和查询数据

一旦您成功地完成了项目的一系列工作流,或者导入了您感兴趣的数据集,您就会希望快速找到与您的分析相关的数据。AiiDA数据库中的数据以连接的图的形式存储,可以通过:class:~aiida.orm.querybuilder.QueryBuilder 轻松地 查询

The QueryBuilder lets you query your AiiDA database independently of the backend used under the hood. Before starting to write a query, it helps to:

  • 知道你想要查询什么。在数据库语言中,你需要告诉后端你正在寻找什么 实体 ,以及你想要*投影*它的哪个属性。例如,您可能对「算例」的标签和其所有输出的PK感兴趣。

  • 知道你感兴趣的数据实体之间的关系。AiiDA图中的节点(顶点,即vertices)是由链接连结的(边,即edges)。一个 节点 可以作为另一个 节点 的输入或输出,也可以是始祖或者后代。

  • 知道您希望如何过滤查询结果。

一旦你清楚了你想要什么和你如何能得到它, QueryBuilder 将为您构建一个sql查询。

有两种使用 QueryBuilder 的方法:

  1. 追加查询条件 的方法中,使用 QueryBuilder.append()` 方法逐步构造查询。

  2. queryhelp 方法中,你构造一个字典来定义你的查询,并将它传递给 QueryBuilder

这两个api提供相同的功能- 追加条件的方法可能更适合交互式使用,例如,在 verdi shell,而queryhelp方法在脚本中更常用。在本节中,我们将重点介绍追加条件方法的基础知识。有关高级查询或queryhelp的更多详细信息,请参见 高级查询部分

选择查询实体

使用 QueryBuilder 的``append()`` 方法,你可以查询你感兴趣的实体。假设您想查询数据库中的「算例」作业节点:

from aiida.orm import QueryBuilder
qb = QueryBuilder()       # Instantiating instance. One instance -> one query
qb.append(CalcJobNode)    # Setting first vertex of path

如果您对不同类的实例感兴趣,您也可以传递一个类的可迭代对象。但是,它们必须是相同的ORM类型(例如,必须是 Node 的子类):

qb = QueryBuilder()       # Instantiating instance. One instance -> one query
qb.append([CalcJobNode, WorkChainNode]) # Setting first vertices of path, either WorkChainNode or Job.

注解

「列程」都有一个运行时:class:~aiida.engine.processes.process.Process 用以执行进程和一个 Node 其将数据存储在数据库中(详细说明请参见 相应的主题部分)。:类 QueryBuilder 允许你传递:class:~aiida.orm.nodes.node.Node 类(例如:class:~aiida.orm.nodes.process.calculation.calcjob.CalcJobNode )或:Process 类(例如:class:~aiida.engine.processes.calcjobs.calcjob.CalcJob),它将自动为查询选择正确的数据实体。使用:class:~aiida.orm.nodes.process.calculation.calcjob.CalcJobNodeCalcJob 将产生相同的查询结果。

结果检索

一旦你将你想要查询的实体 缀加QueryBuilder,下一个问题是如何得到结果。有几种方法可以从查询中获取数据:

qb = QueryBuilder()                 # Instantiating instance
qb.append(CalcJobNode)              # Setting first vertices of path

first_row = qb.first()              # Returns a list (!) of the results of the first row

all_results_d = qb.dict()           # Returns all results as a list of dictionaries

all_results_l = qb.all()            # Returns a list of lists

如果你正在处理一个大型数据集,你可以返回你的查询作为一个生成器:

all_res_d_gen = qb.iterdict()       # Return a generator of dictionaries
                                    # of all results
all_res_l_gen = qb.iterall()        # Returns a generator of lists

这将批量检索数据,您可以在查询完全完成之前开始处理数据。例如,你可以在For循环中遍历你的查询结果:

for entry in qb.iterall():
    # do something with a single entry in the query result

过滤器

通常您不希望查询某个类的 所有 数据实体,而是根据某些属性*过滤*结果。假设您不需要所有:class:~aiida.orm.nodes.process.calculation.calcjob.CalcJobNode ,而只有那些是 finished 状态的数据:

qb = QueryBuilder()                 # Initialize a QueryBuilder instance
qb.append(
    CalcJobNode,                    # Append a CalcJobNode
    filters={                       # Specify the filters:
        'attributes.process_state': 'finished',  # the process is finished
    },
)

可以对查询中的一个实体应用多个筛选器。假设你对数据库中所有已经 完成 的并且有 exit_status == 0 的计算作业感兴趣,:

qb = QueryBuilder()                 # Initialize a QueryBuilder instance
qb.append(
    CalcJobNode,                    # Append a CalcJobNode
    filters={                       # Specify the filters:
        'attributes.process_state': 'finished',     # the process is finished AND
        'attributes.exit_status': 0                 # has exit_status == 0
    },
)

如果你想查询只要满足其中一个条件的「算例」作业,你可以使用 or 操作符:

qb = QueryBuilder()
qb.append(
    CalcJobNode,
    filters={
        'or':[
            {'attributes.process_state': 'finished'},
            {'attributes.exit_status': 0}
        ]
    },
)

如果我们在上面的例子中写了``and``而不是 or,我们将执行与前一个完全相同的查询,因为 and``是默认的行为,如果你在字典中为'``filters``参数提供几个键值对的过滤器。如果你想要所有状态为``finished``或``excepted``的计算作业,你也可以使用 ``in 操作符:

qb = QueryBuilder()
qb.append(
    CalcJobNode,
    filters={
        'attributes.process_state': {'in': ['finished', 'excepted']}
    },
)

可以通过在运算符前添加感叹号来否定过滤器。因此,要查询所有状态不为``finished`` 或 ``excepted``的计算作业:

qb = QueryBuilder()
qb.append(
    CalcJobNode,
    filters={
        'attributes.process_state': {'!in': ['finished', 'excepted']}
    },
)

注解

上述规则适用于所有操作符。例如,你可以用``!==`` 来作不等价操作,因为这是带有否定前缀的相等操作符(==)。

所有可用运算符的完整列表可以在:ref:`高级查询部分<topics:database:advancedquery:tables:operators>`中找到。

关系查找

可以根据数据与数据库中另一个实体的关系来查询数据。假设您对算例作业本身不感兴趣,而是对它们创建的一个输出感兴趣。你可以对所有 CalcJobNode 建立你的初始查询,在数据库中使用输出到查询的第一步的关系:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob')

在第一个``append``调用中,我们查询所有:class:~aiida.orm.nodes.process.calculation.calcjob.CalcJobNode,并 标记 这一步与 唯一 标识符 'calcjob' 。接下来,我们寻找:CalcJobNode’ 输出的所有 Int 节点。使用 with_incoming 关系参数。Int 节点是由 CalcJobNode 创建的并与之有一个 incoming 创建类型的链接。

在我们的查询上下文中,我们构建了一个由*顶点*(即我们要查询的实体)组成的 路径 ,这些顶点由 (由它们之间的关系定义)连接。你可以使用query查询所有可能的关系的完整集合,以及它们连接到的实体,更多相关指令可以在 :ref:`进阶查找章节 <topics:database:advancedquery:tables:relationships>`中找到。

注解

 tag 标识符可以是任何字母数字字符串,它只是一个标签,用于在定义关系时引用查询路径上的前一个顶点。

投影

默认情况下,QueryBuilder 返回与查询路径的最终附加项相对应的实体实例。例如:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob')

上面的代码片段将返回所有``Int``节点,它们是 CalcJobNode 的输出。但是,你也可以通过在对应的``append()`` 调用中添加``project=’*’``投影 路径中的其他实体:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob', project='*')
qb.append(Int, with_incoming='calcjob')

这将返回所有,有一个 Int 输出节点的:class:`~aiida.orm.nodes.process.calculation.calcjob.CalcJob

然而,在许多情况下,我们对实体本身并不感兴趣,而是对它们的PK、UUID、 属性 或实体存储的其他一些信息感兴趣。这可以通过向``project``关键字参数提供相应的 来实现:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob', project='id')

在上面的例子中,执行查询返回``Int``节点的所有:class:~aiida.orm.nodes.process.calculation.calcjob.CalcJobNode 类的 主键PK ,此外,你可以通过提供一个列表来为一个顶点投射多个信息:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob', project=['id', 'attributes.value'])

对于上面的查询,qb.all() 将返回一个列表的列表,其中每个元素对应于一个实体并包含两项: Int 节点的PK及其值。最后,你可以沿着查询路径投影多个顶点的信息:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob', project='*')
qb.append(Int, with_incoming='calcjob', project=['id', 'attributes.value'])

所有投影必须以数据库中实体的 之一开始,或者使用``’*’`` 投影实例本身。到目前为止,我们遇到的列的例子是``id``, uuidattributes。如果列是一个字典,您可以使用点号展开字典值,就像我们在前面的示例中所做的那样,以获得``attributes.value``。这还可以用于投影嵌套字典的值。

注解

请注意,为了一致性,QueryBuilder.all() / iterall() 总是返回列表的列表,即使您只投射单个实体的一个属性。在本例中,使用 QueryBuilder.all(flat=True) 以平面列表的形式返回查询结果。

正如前面提到的,本节只提供了对:class:~aiida.orm.querybuilder.QueryBuilder`的简要介绍。要了解更多高级查询,请参见:ref:`相应的主题部分<topics:database:advancedquery>

组织数据

如何对节点进行分组

AiiDA的数据库非常适合自动存储您的所有数据,但有时浏览这样一个平面数据存储可能比较棘手。为了在大量数据中创建一些顺序,您可以将节点集 分组 在一起,就像您在文件系统中的文件夹中处理文件一样。在这个类比中,文件夹由 Group 表示。每个组实例可以容纳任意数量的节点,任意节点可以包含在任意数量的组中。一个典型的用例是在一个组中存储共享公共属性的所有节点。

下面我们将展示如何对组执行一组可执行的典型操作。

创建新组

使用命令行接口:

$ verdi group create test_group

使用python接口:

In [1]: group = Group(label='test_group')

In [2]: group.store()
Out[2]: <Group: "test_group" [type core], of user xxx@xx.com>

*列出所有可用组

例如:

$ verdi group list

组有不同的类型,由它们的类型字符串表示。默认情况下 verdi group list 只显示类型为 core 的组。如果你想显示另一种类型的组,请使用 -T/--type-string 选项。如果要显示所有类型的组,请使用 -a/--all-types 选项。

例如,列出类型为``core.auto`` 的组,使用:

$ verdi group list -T core.auto

类似地,我们可以使用``QueryBuilder`` 的 type_string 键来’筛选组:

In [1]: QueryBuilder().append(Group, filters={'type_string': 'core'}).all(flat=True)
Out[1]:
[<Group: "another_group" [type core], of user xxx@xx.com>,
<Group: "old_group" [type core], of user xxx@xx.com>,
<Group: "new_group" [type core], of user xxx@xx.com>]

将节点加入组

一旦创建了``test_group`` ,我们就可以向它添加节点。例如,要向组中添加一个``pk=1`` 的节点,我们可以使用命令行:

$ verdi group add-nodes -G test_group 1
Do you really want to add 1 nodes to Group<test_group>? [y/N]: y

或者Python接口:

In [1]: group.add_nodes(load_node(pk=1))

显示关于组的信息

使用命令行接口:

$ verdi group show test_group

-----------------  ----------------
Group label        test_group
Group type_string  user
Group description  <no description>
-----------------  ----------------
# Nodes:
PK    Type    Created
----  ------  ---------------
 1    Code    26D:21h:45m ago

从组中移除节点

使用命令行接口:

$ verdi group remove-nodes -G test_group 1
Do you really want to remove 1 nodes from Group<test_group>? [y/N]: y

使用python接口:

In [1]: group = load_group(label='test_group')

In [2]: group.remove_nodes([load_node(1)])

或者,您可能希望从组中删除 所有 节点。在命令行中你只需要添加 -c/--clear 选项到 verdi group remove-nodes ..

$ verdi group remove-nodes -c -G test_group
Do you really want to remove ALL the nodes from Group<test_group>? [y/N]:

在Python接口中,你可以使用 .clear() 方法来实现相同的目标:

In [1]: group = load_group(label='test_group')

In [2]: group.clear()

重命名组

使用命令行接口:

$ verdi group relabel test_group old_group
Success: Label changed to old_group

使用python接口:

In [1]: group = load_group(label='old_group')

In [2]: group.label = 'another_group'

删除组

使用命令行接口:

$ verdi group delete another_group
Are you sure to delete Group<another_group>? [y/N]: y
Success: Group<another_group> deleted.

默认情况下,任何与组相关的删除操作都不会影响节点本身。例如,如果您删除一个组,属于该组的节点将保留在数据库中。如果从组中删除节点,也会发生同样的情况——这些节点将保留在数据库中,但不再属于组。

如果你也想删除节点,在删除组时,使用 ``–delete-nodes``选项:

$ verdi group delete another_group --delete-nodes

将一个组拷贝为其他组

此操作将把原来的组的内容复制到目标组中。此外,如果目标组不存在,将自动创建它。

使用命令行接口:

$ verdi group copy source_group dest_group
Success: Nodes copied from group<source_group> to group<dest_group>

使用python接口:

In [1]: src_group = Group.objects.get(label='source_group')

In [2]: dest_group = Group(label='destination_group').store()

In [3]: dest_group.add_nodes(list(src_group.nodes))

使用组的例子

在本节中,我们将提供一些实际示例,说明如何使用组来组织和结构化数据库中的节点。

使用类似属性对晶体结构进行分组

假设,我们想把所有计算出的带隙大于 ``1.0 eV``的结构分组到一个名为``promising_structures``的组中,可以使用以下方法:

# Finding the structures with the bandgap > 1.0.
qb = QueryBuilder()
qb.append(StructureData,  tag='structure', project='*') # Here we are projecting the entire structure object
qb.append(CalcJobNode, with_incoming='structure', tag='calculation')
qb.append(Dict, with_incoming='calculation', filters={'attributes.bandgap': {'>': 1.0}})

# Adding the structures in 'promising_structures' group.
group = load_group(label='promising_structures')
group.add_nodes(q.all(flat=True))

注解

任何节点只能被包含在一个组中一次,如果再次添加,它将被忽略。这意味着可以安全地多次调用add_nodes,并且只添加不属于该组的节点。

使用分组数据进行进一步处理

这里我们演示了如何提交所有属于名为``promising_structures``组的结构的计算:

# Querying the structures that belong to the 'promising_structures' group.
qb = QueryBuilder()
qb.append(Group, filters={'label': 'promising_structures'}, tag='group')
qb.append(StructureData, with_group='group')

# Submitting the simulations.
for structure in qb.all(flat=True):
    builder = SomeWorkChain.get_builder()
    builder.structure = structure
    ...
    submit(builder)

但是要注意,我们也可以使用 group.nodes 来访问组中的节点。要达到上述相同的结果,你需要做以下事情:

group = load_group(label='promising_structures')

# Here make sure to include only structures, as group can contain any nodes.
structures = [s for s in group.nodes if isinstance(nodes, StructureData)]
for structure in structures:
    builder = SomeWorkChain.get_builder()
    builder.structure = structure
    ...
    submit(builder)

要找到属性``property_a`` 值小于 1 且属于``promising_structures`` 组的所有结构,可以构建如下查询:

qb = QueryBuilder()
qb.append(Group, filters={'label': 'promising_structures'}, tag='group')
qb.append(StructureData, with_group='group', tag='structure', project='*')
qb.append(SomeWorkChain, with_incoming='structure', tag='calculation')
qb.append(Dict, with_incoming='calculation', filters={'attributes.property_a': {'<': 1}})

qb.all(flat=True) 的返回值将包含符合上述条件的所有结构。

按层级关系组织组

AiiDA中的组本质上是“扁平的”,即组可能只包含节点而不包含其他组。但是,使用 GroupPath 工具可以基于分隔标签构造 虚拟 组层次结构

GroupPath 的工作方式与Python的 pathlib.Path 很相似。因此路径在组标签中由正斜杠 ‘/’ 表示。

例如,我们有这样的组:

$ verdi group list

PK    Label                    Type string    User
----  -----------------        -------------  --------------
1     base1/sub_group1         core           user@email.com
2     base1/sub_group2         core           user@email.com
3     base2/other/sub_group3   core           user@email.com

我们也可以从命令行访问它们:

$ verdi group path ls -l
Path         Sub-Groups
---------  ------------
base1                 2
base2                 1
$ verdi group path ls base1
base1/sub_group1
base1/sub_group2

或使用python接口:

In [1]: from aiida.tools.groups import GroupPath
In [2]: path = GroupPath("base1")
In [3]: print(list(path.children))
Out[3]: [GroupPath('base1/sub_group2', cls='<class 'aiida.orm.groups.Group'>'),
         GroupPath('base1/sub_group1', cls='<class 'aiida.orm.groups.Group'>')]

The GroupPath can be constructed using indexing or “divisors”:

In [4]: path = GroupPath()
In [5]: path["base1"] == path / "base1"
Out[5]: True

使用 browse() 属性,也可以将路径构造为前面的属性。这在交互式环境中很有用,可用路径将显示在tab补全中:

In [6]: path.browse.base1.sub_group2()
Out[6]: GroupPath('base1/sub_group2', cls='<class 'aiida.orm.groups.Group'>')

检查path元素是否存在:

In [7]: "base1" in path
Out[7]: True

一个组可以是“虚拟的”,在这种情况下,它的标签并不直接与一个组相关,或者可以通过 get_group() 方法来检索这个组。

In [8]: path.is_virtual
Out[8]: True
In [9]: path.get_group() is None
Out[9]: True
In [10]: path["base1/sub_group1"].is_virtual
Out[10]: False
In [11]: path["base1/sub_group1"].get_group()
Out[11]: <Group: "base1/sub_group1" [type core], of user user@email.com>

组可以创建和销毁:

In [12]: path["base1/sub_group1"].delete_group()
In [13]: path["base1/sub_group1"].is_virtual
Out[13]: True
In [14]: path["base1/sub_group1"].get_or_create_group()
Out[14]: (<Group: "base1/sub_group1" [type core], of user user@email.com>, True)
In [15]: path["base1/sub_group1"].is_virtual
Out[15]: False

要遍历路径,请使用 children() 。对于递归遍历,使用 walk():

In [16]: for subpath in path.walk(return_virtual=False):
    ...:     print(subpath)
    ...:
GroupPath('base1/sub_group1', cls='<class 'aiida.orm.groups.Group'>')
GroupPath('base1/sub_group2', cls='<class 'aiida.orm.groups.Group'>')
GroupPath('base2/other/sub_group3', cls='<class 'aiida.orm.groups.Group'>')

你也可以直接遍历路径的节点,可选按节点类和 构建查询器 允许的任何其他筛选方式:

In [17]: from aiida.orm import Data
In [18]: data = Data()
In [19]: data.set_extra("key", "value")
In [20]: data.store()
Out[20]: <Data: uuid: 0adb5224-585d-4fd4-99ae-20a071972ddd (pk: 1)>
In [21]: path["base1/sub_group1"].get_group().add_nodes(data)
In [21]: next(path.walk_nodes(node_class=Data, filters={"extras.key": "value"}))
Out[21]: WalkNodeResult(group_path=GroupPath('base1/sub_group1', cls='<class 'aiida.orm.groups.Group'>'),
node=<Data: uuid: 0adb5224-585d-4fd4-99ae-20a071972ddd (pk: 1)>)

最后,你也可以指定 Group 子类(如上所述):

In [22]: from aiida.orm import UpfFamily
In [23]: path2 = GroupPath(cls=UpfFamily)
In [24]: path2["base1"].get_or_create_group()
Out[24]: (<UpfFamily: "base1" [type core.upf], of user user@email.com>, True)

重要

GroupPath 实例只会识别实例化的 cls 类型的组。默认 clsaiida.orm.Group:

In [25]: orm.UpfFamily(label="a").store()
Out[25]: <UpfFamily: "a" [type core.upf], of user user@email.com>
In [26]: GroupPath("a").is_virtual
Out[26]: True
In [27]: GroupPath("a", cls=orm.UpfFamily).is_virtual
Out[27]: False

删除数据

默认情况下,每当您运行或提交一个新的计算时,AiiDA将在数据库中为您创建新的节点,并且永远不会替换或删除数据。但是,在某些情况下,删除不再有用的节点可能是有用的,例如测试运行或不正确/错误的数据和计算。对于这种情况,AiiDA提供了 verdi node delete 命令和 delete_nodes() 函数,从可验证性图删除节点。

警告

一旦数据被删除,就没有办法恢复它了(除非您做了备份)。

重要的是,请注意,即使您要求只删除一个节点, verdi node delete 通常会删除许多附加的链接节点,以保持可验证性图的一致状态。例如,如果您删除一个算例的输入,AiiDA也将删除计算本身(否则您将在可验证性图中有效地更改该计算的输入)。完整的一致性规则将在 这里 详细解释。

因此:总是检查 verdi node delete 的输出,以确保它所删除的内容没有超出您的预期。您还可以使用 verdi node delete--dry-run 标志来查看该命令将做什么,而不执行任何实际操作。

此外,还有许多附加规则,它们不是强制性的,但可以由用户进行切换。例如 ,如果在删除计算时,您也想删除它产生的数据,您可以设置 --create-forward (使用 --no-create-forward 将只删除计算,保留输出数据: 注意,这有效地删除了输出数据的来源信息)。这些标志的完整列表可以从帮助命令 verdi node delete -h 中获得。

from aiida.tools import delete_nodes
pks_to_be_deleted = delete_nodes(
    [1, 2, 3], dry_run=True, create_forward=True, call_calc_forward=True, call_work_forward=True
)

删除计算机

要删除一台计算机,你可以使用 verdi computer delete 。如果在创建计算机之后,您发现出现了一个错误并想要删除它,那么这个命令非常有用。特别要注意的是,如果计算机已经被至少一个节点使用过, verdi computer delete 将无法执行。在这种情况下,您需要使用 verdi node delete 先删除相应的节点。

删除可变数据

AiiDA中的数据子集在存储节点之后也是可变的,用户可以方便地对数据进行标记/分组/注释。这些数据可以在任何时候安全地删除。这包括:

  • 节点额外属性:可以使用 delete_extra()delete_extra_many() 方法删除。

  • 节点注释:可以使用 remove_comment() 删除这些注释。

  • :可以使用 :py:meth:`Group.objects.delete() ` 删除。该命令将只删除组,而不删除组中包含的节点。

完全删除AiiDA的某个配置文件

如果您不想有选择地删除一些节点,而是想删除整个AiiDA配置文件,请使用 verdi profile delete 命令。这个命令将删除文件存储库和数据库。

危险

除非先前备份过,否则不可能恢复已删除的配置文件!

传输数据

1.6.0 新版功能.

危险

这个特性仍然是测试版,它的API在不久的将来可能会改变。因此,不建议您在公共/生产工作流中依赖它。

此外,我们非常感谢对其进行反馈(请登录https://github.com/aiidateam/aiida-core/issues/4811)。

当启动一个计算作业时,AiiDA将创建一个 RemoteData 节点,其将作为输出节点附加到带有标签 remote_folder 的计算节点。由 CalcJob 插件生成的输入文件被复制到这个远程文件夹中,因为作业也是在那里执行的,代码也会在同一个远程文件夹中生成它的输出文件。因为 RemoteData 节点只显式地在远程计算机上存储文件路径,而不是它的实际内容,它的功能或多或少像一个符号链接。这意味着如果远程文件夹被删除,将无法检索其内容。因此, CalcJob 插件可以指定一些文件,这些文件应该是可 检索 的,并存储 FolderData 节点,并且它作为带有标签 retrieved_folder 的输出附加到计算节点。

尽管 检索列表 允许指定在本地检索什么输出文件,但这必须在 提交计算之前 完成。为了提供更多的灵活性来决定,即使在它终止之后,已完成的计算作业的哪些文件要存储在本地,AiiDA还附带了 TransferCalculation 插件。这个计算插件可以从远程计算机检索文件,并将它们保存在本地 FolderData 中。复制内容的规范是通过类型输入提供的

In [1]: instructions_cont = {}
    ... instructions_cont['retrieve_files'] = True
    ... instructions_cont['symlink_files'] = [
    ...     ('node_keyname', 'source/path/filename', 'target/path/filename'),
    ... ]
    ... instructions_node = orm.Dict(dict=instructions_cont)

The 'source/path/filename' and 'target/path/filename' are both relative paths (to their respective folders). The node_keyname is a string that will be used when providing the source RemoteData node to the calculation. You also need to provide the computer between which the transfer will occur:

In [2]: transfer_builder = CalculationFactory('core.transfer').get_builder()
    ... transfer_builder.instructions = instructions_node
    ... transfer_builder.source_nodes = {'node_keyname': source_node}
    ... transfer_builder.metadata.computer = source_node.computer

这里的变量 source_node 对应于需要检索其内容的 RemoteData 节点。最后,你只是运行或提交计算,就像你做任何其他操作:

In [2]: from aiida.engine import submit
    ... submit(transfer_builder)

你也可以用它将本地文件复制到一个新的 RemoteData 文件夹中。为此,你首先必须调整指令,将 'retrieve_files' 设置为 False ,并使用 'local_files' 列表,而不是 'symlink_files'

In [1]: instructions_cont = {}
    ... instructions_cont['retrieve_files'] = False
    ... instructions_cont['local_files'] = [
    ...     ('node_keyname', 'source/path/filename', 'target/path/filename'),
    ... ]
    ... instructions_node = orm.Dict(dict=instructions_cont)

还需要注意的是,在本例中, source_node 的类型为 FolderData 所以你必须手动选择你想要复制文件的电脑。你可以通过运行 verdi computer list 查看可用计算机,并使用如 load_computer() 下所示的标签来加载:

In [2]: transfer_builder.metadata.computer = load_computer('some-computer-label')

无论是上传还是检索,你都可以复制多个文件,方法是将它们分别添加到指令输入中的 local_filessymlink_files 键的列表中。通过提供多个 source_node,每个``source_node`` 都有一个不同的 'node_keyname'。也可以从任意数量的节点复制文件,而目标节点总是一个(因此您可以在单个调用中 “收集” 文件,但不能 “分发” 文件)。