数据类型#

AiiDA 已经提供了许多有用的数据类型。本节将详细介绍最常见的数据类型,以及使用这些数据类型的一些方便的特性/功能。

例如,在 verdi shell 中工作时,可以通过 DataFactory() 函数(也从 aiida.plugins 中公开),将相应的 entry point 作为参数传递,从而访问不同的数据类型:

In [1]: ArrayData = DataFactory('core.array')

重要

本节中的许多示例都假设您在 verdi shell 中工作。如果不是这种情况,则必须首先加载 DataFactory() 等函数:

from aiida.plugins import DataFactory

ArrayData = DataFactory('core.array')

运行 verdi plugin list aiida.data 命令可获得所有数据 entry point 的列表。

对于所有数据类型,你可以在 API 参考中找到相应数据类的链接,以阅读更多关于类及其方法的信息。我们还详细说明了哪些数据存储在数据库中(主要作为属性,因此信息可以很容易地查询,例如使用 QueryBuilder ),哪些数据作为原始文件存储在 AiiDA 文件库中(提供对文件内容的访问,但不能有效查询:这对于不需要查询的大数据文件等非常有用)。

如果您需要处理某些特定类型的数据,请先查看下面的数据类型/插件列表,如果没有找到您需要的,请查看 Adding support for custom data types

核心数据类型#

以下是 AiiDA 已提供的核心数据类型列表,以及它们的 entry point 和数据在 node 存储到 AiiDA 数据库后的存储位置。

Entry point

储存在数据库中

存放在储存库

Int

core.int

整数值

-

Float

core.float

浮点数值

-

Str

core.str

字符串

-

Bool

core.bool

布尔值

-

List

core.list

完整列表

-

Dict

core.dict

完整词典

-

EnumData

core.enum

值、名称和类标识符

-

JsonableData

core.jsonable

JSON 数据和类标识符

-

ArrayData

core.array

数组名称和相应的形状

.npy 格式的数组数据

XyData

core.array.xy

数组名称和相应的形状

.npy 格式的数组数据

SinglefileData

core.singlefile

文件名

文件

FolderData

core.folder

-

所有文件和文件夹

RemoteData

core.remote

计算机和文件夹的绝对路径

所有文件和文件夹

AbstractCode

-

默认插件,附加/预处理文本

-

Code

core.code

计算机和可执行路径

所有文件和文件夹

InstalledCode

core.code.installed

计算机和可执行路径

-

PortableCode

core.code.portable

可执行文件的相对路径

代码的所有文件和文件夹

ContainerizedCode

core.code.containerized

计算机、图像和可执行路径

-

基地类型#

有许多有用的类封装了基本 Python 数据类型( IntFloatStrBool ),因此它们可以存储在 provenance 中。这些数据类型会自动与 verdi shell 一起加载,也会直接从 aiida.orm 暴露出来。当您需要为 workfunction 等提供单个参数时,它们尤其有用。

这些类的使用方式通常与相应的基本类型类似:

In [1]: total = Int(2) + Int(3)

如果需要访问裸值而不是整个 AiiDA 类,请使用 .value 属性:

In [2]: total.value
Out[2]: 5

警告

如果你需要做简单的操作,比如两个数字相乘,这是很方便的,但要非常小心,不要把这样的 nodes 而不是相应的 Python 值传递给用它们执行繁重计算的库。事实上,对数值的任何操作都会被创建新的 AiiDA nodes 的操作所取代,但速度会慢很多(参见 GitHub 上的 this discussion )。在这种情况下,请记住将 node.value 传递给数学函数。

AiiDA 还为 Python 的两个基本迭代器实现了数据类: ListDict 。它们可以存储任何列表或字典,其中的元素可以是基本 Python 类型(字符串、浮点数、整数、布尔、无类型):

In [1]: l = List(list=[1, 'a', False])

请注意关键字参数 list 的使用,这是 List 类的构造函数所必需的。您还可以在迭代器中存储任意深度级别的列表或字典。例如,您可以创建一个字典,其中的值是一个字典列表:

In [2]: d = Dict(dict={'k': 0.1, 'l': [{'m': 0.2}, {'n': 0.3}]})

要从 ListDict 实例获取 Python listdictionary ,必须使用 get_list()get_dict() 方法:

In [3]: l.get_list()
Out[3]: [1, 'a', False]

In [4]: d.get_dict()
Out[4]: {'k': 0.1, 'l': [{'m': 0.2}, {'n': 0.3}]}

不过,您也可以使用列表索引或字典键来提取特定值:

In [5]: l[1]
Out[5]: 'a'

In [6]: d['k']
Out[6]: 0.1

例如,您还可以使用相应 Python 基本类型的许多方法:

In [7]: l.append({'b': True})

In [8]: l.get_list()
Out[8]: [1, 'a', False, {'b': True}]

使用 store() 方法存储 node 后,所有基础数据类型的值都会存储在数据库的属性列中。

警告

The List and Dict only store the Python base types, not the corresponding AiiDA data type. These will be converted to their corresponding Python base type when storing the List or Dict node in the database.

枚举数据#

Enum 成员由 EnumData 类中的三个属性表示:

  • name :成员姓名

  • value :成员值

  • identifier :枚举标识符的字符串表示形式

In [1]: from enum import Enum
   ...: class Color(Enum):
   ...: RED = 1
   ...: GREEN = 2

In [2]: from aiida.orm import EnumData
   ...: color = EnumData(Color.RED)

In [3]: color.name
Out[3]: 'RED'

In [4]: color.value
Out[4]: 1

In [5]: color.get_member()
Out[5]: <Color.RED: 1>

JsonableData#

JsonableData is a data plugin that allows one to easily wrap existing objects that are JSON-able.

任何实现了 as_dict 方法的类都可以被该数据插件封装和存储,该方法返回的字典是对象的 JSON 可序列化表示。要进行反序列化,还应实现一个 from_dict 方法,该方法将字典作为输入并返回对象。

In [1]: from aiida.orm import JsonableData
   ...: class MyClass:
   ...:     def __init__(self, a: int, b: int):
   ...:         self.a = a
   ...:         self.b = b
   ...:     def __str__(self):
   ...:         return f'MyClass({self.a}, {self.b})'
   ...:     def as_dict(self) -> dict:
   ...:         return {'a': self.a, 'b': self.b}
   ...:     @classmethod
   ...:     def from_dict(cls, d: dict):
   ...:         return cls(d['a'], d['b'])
   ...:
   ...: my_object = MyClass(1, 2)
   ...: my_jsonable = JsonableData(my_object)
   ...: str(my_jsonable.obj)
Out[1]: 'MyClass(1, 2)'

数组数据#

The ArrayData class can be used to represent numpy arrays in the provenance. Each array is assigned to a name specified by the user using the set_array() method:

In [1]: ArrayData = DataFactory('core.array'); import numpy as np

In [2]: array = ArrayData()

In [3]: array.set_array('matrix', np.array([[1, 2], [3, 4]]))

请注意,一个 ArrayData 实例可以存储多个不同名称的数组:

In [4]: array.set_array('vector', np.array([[1, 2, 3, 4]]))

要查看存储在 ArrayData 实例中的数组名称列表,可以使用 get_arraynames() 方法:

In [5]: array.get_arraynames()
Out[5]: ['matrix', 'vector']

如果需要某个名称对应的数组,只需向 get_array() 方法提供该名称即可:

In [6]: array.get_array('matrix')
Out[6]:
array([[1, 2],
      [3, 4]])

与所有 node 一样,您可以使用 store() 方法存储 ArrayData node。不过,只有数组的名称和形状会存储到数据库中,数组的内容会存储到 numpy format ( .npy ) 的存储库中。

XyData#

如果要处理的数组之间存在关系,例如 yx 的函数,则可以使用 XyData 类:

In [1]: XyData = DataFactory('core.array.xy'); import numpy as np

In [2]: xy = XyData()

该类专门为 xy 值配备了 setter 和 getter 方法,并负责一些验证工作(例如,检查它们是否具有相同的形状)。用户还必须指定 xy 的单位:

In [3]: xy.set_x(np.array([10, 20, 30, 40]), 'Temperate', 'Celsius')

In [4]: xy.set_y(np.array([1, 2, 3, 4]), 'Volume Expansion', '%')

请注意,您可以设置多个与 x 网格相对应的 y 值。与 ArrayData 相同,数组的名称和形状存储在数据库中,数组的内容存储在 numpy format ( .npy ) 的存储库中。

单文件数据#

要在 provenance 中包含单个文件,可以使用 SinglefileData 类。该类可通过所要存储文件的 绝对 路径进行初始化:

In [1]: SinglefileData = DataFactory('core.singlefile')

In [2]: single_file = SinglefileData('/absolute/path/to/file')

存储 node 时,文件名存储在数据库中,文件本身复制到存储库中。可以使用 get_content() 方法获取字符串格式的文件内容:

In [3]: single_file.get_content()
Out[3]: 'The file content'

对于大文件,使用 get_object_content() 将整个内容读入内存可能并不可取。相反,可以为存储库中的文件打开一个类似文件的句柄,用来以流的形式读取文件内容。例如,这对于从存储库中复制一个大文件到磁盘上的文件非常有用,而无需将其全部加载到内存中:

In [4]: import shutil
        with single_file.open(mode='rb') as source:
            with open('copy.txt', mode='wb') as target:
                shutil.copyfileobj(source, target)

备注

为确保文件复制过来的内容相同(不存在编码问题),文件以 ``binary`` 模式打开,在 mode 参数中加入 b 字符。

为了提高效率,版本库接口只通过类文件对象或字符串来访问对象内容。然而,在某些用例中,对象内容_需要_在本地文件系统中以文件形式提供。例如, numpy.loadtxt 方法只接受文件路径,而不接受类文件对象。在这种情况下,可以使用 as_path() 上下文管理器在本地文件系统中提供文件内容:

In [5]: with single_file.as_path() as filepath:
            numpy.loadtxt(filepath)

生成值 filepathpathlib.Path 的实例,指向本地文件系统中包含文件内容的位置。退出上下文管理器后,本地文件系统上的临时副本会自动清理。

备注

复制内容的临时目录使用标准库的 tempfile.TemporaryDirectory() 函数创建。临时目录的位置可以从一个与平台相关的列表中选择,也可以通过 TMPDIR 环境变量来控制(详见 the official documentation )。

警告

The as_path() context manager will copy the file content to a temporary folder on the local file system. For large files this can be an expensive operation and it is inefficient since it requires an additional read and write operation. Therefore, if it is possible to use file-like objects or read the content into memory, the get_object_content() and open() methods should be preferred.

文件夹数据#

The FolderData class stores sets of files and folders (including its subfolders). To store a complete directory, simply use the tree keyword:

In [1]: FolderData = DataFactory('core.folder')

In [2]: folder = FolderData(tree='/absolute/path/to/directory')

或者,也可以先构建 node,然后使用各种存储库方法从目录和文件路径添加对象:

In [1]: folder = FolderData()

In [2]: folder.put_object_from_tree('/absolute/path/to/directory')

In [3]: folder.put_object_from_file('/absolute/path/to/file1.txt', path='file1.txt')

或从 file-like objects 获取:

In [4]: folder.put_object_from_filelike(filelike_object, path='file2.txt')

反之,可以使用 get_object_content() 方法访问存储在 FolderData node 中的文件内容:

In [5]: folder.get_object_content('file1.txt')
Out[5]: 'File 1 content\n'

要查看存储在 FolderData 中的文件,可以使用 list_object_names() 方法:

In [6]: folder.list_object_names()
Out[6]: ['subdir', 'file1.txt', 'file2.txt']

在本例中, subdir/absolute/path/to/directory 的子目录,其内容已添加到上面。要列出 subdir 目录的内容,可以将其路径传递给 list_object_names() 方法:

In [7]: folder.list_object_names('subdir')
Out[7]: ['file3.txt', 'module.py']

通过传递正确的路径,使用 get_object_content() 方法可以再次显示内容:

In [8]: folder.get_object_content('subdir/file3.txt')
Out[8]: 'File 3 content\n'

由于 FolderData node 只是一个文件集合,因此它只需将这些文件存储在存储库中。

对于大文件,使用 get_object_content() 将整个内容读入内存可能并不可取。相反,可以为存储库中的文件打开一个类似文件的句柄,用来以流的形式读取文件内容。例如,这对于从存储库中复制一个大文件到磁盘上的文件非常有用,而无需将其全部加载到内存中:

In [9]: import shutil
        with folder.open('subdir/file3.txt', mode='rb') as source:
            with open('copy.txt', mode='wb') as target:
                shutil.copyfileobj(source, target)

备注

为确保文件复制过来的内容相同(不存在编码问题),文件以 ``binary`` 模式打开,在 mode 参数中加入 b 字符。

为了提高效率,版本库接口只通过类文件对象或字符串来访问对象内容。然而,在某些用例中,对象内容_需要_在本地文件系统中以文件形式提供。例如, numpy.loadtxt 方法只接受文件路径,而不接受类文件对象。在这种情况下,可以使用 as_path() 上下文管理器在本地文件系统中提供 node 资源库的内容:

In [10]: with folder.as_path() as filepath:
             print(list(filepath.iterdir()))
Out[10]: ['subdir', 'file1.txt', 'file2.txt']

生成值 dirpathpathlib.Path 的一个实例,指向本地文件系统中包含存储库完整内容的位置。退出上下文管理器后,本地文件系统上的临时副本会自动清理。

备注

复制内容的临时目录使用标准库的 tempfile.TemporaryDirectory() 函数创建。临时目录的位置可以从一个与平台相关的列表中选择,也可以通过 TMPDIR 环境变量来控制(详见 the official documentation )。

可选择指定一个显式对象:

In [11]: with folder.as_path('some_data_file.dat') as filepath:
             numpy.loadtxt(filepath)

如果 path 处的对象是一个目录,返回值将指向包含其内容的目录。如果是文件,返回值将指向包含对象内容的文件。

警告

The as_path() context manager will copy the content to a temporary folder on the local file system. For large repositories this can be an expensive operation and it is inefficient since it requires an additional read and write operation. Therefore, if it is possible to use file-like objects or read the content into memory, the get_object_content() and open() methods should be preferred.

远程数据#

The RemoteData node represents a “symbolic link” to a specific folder on a remote computer. Its main use is to allow users to persist the provenance when e.g. a calculation produces data in a raw/scratch folder, and the whole folder needs to be provided to restart/continue. To create a RemoteData instance, simply pass the remote path to the folder and the computer on which it is stored:

In [1]: RemoteData = DataFactory('core.remote')

In [2]: computer = load_computer(label='computer_label')

In [3]: remote = RemoteData(remote_path='/absolute/path/to/remote/directory' computer=local)

使用 listdir() 方法可以查看远程文件夹的内容:

In [4]: remote.listdir()
Out[4]: ['file2.txt', 'file1.txt', 'subdir']

要查看子目录的内容,请向 listdir() 方法传递相对路径:

In [5]: remote.listdir('subdir')
Out[5]: ['file3.txt', 'module.py']

警告

使用 listdir() 方法或任何从远程计算机检索信息的方法,都会使用其传输类型打开与远程计算机的连接。在编写脚本和/或 workflow 时,强烈不建议使用这些方法。

抽象代码#

在 2.1 版本加入.

The aiida.orm.nodes.data.code.abstract.AbstractCode class provides the abstract class for objects that represent a “code” that can be executed through a aiida.engine.processes.calcjobs.calcjob.CalcJob plugin. There are currently four implementations of this abstract class:

代码#

自 2.1 版本弃用.

在历史上,只有一种代码实现方式,即 Code ,它实现了两种不同类型的代码:

  • 计算机中预装的可执行文件,用 Computer 表示。

  • 包含所有代码文件(包括可执行文件)的目录,该目录将上载到

这两种类型被称为``remote``和``local``代码。然而,这种命名法会导致混淆,因为 ``remote`` 代码也可以指本地主机上的可执行文件,即 AiiDA 本身运行的机器。此外,由一个类实现两个不同的概念会导致界面不直观。因此, Code 类在 aiida-core==2.1 中被弃用,分别由 InstallCodeInstallCode 取代。 Code 类现已废弃,将在 aiida-core==3.0 中删除。

安装代码#

在 2.1 版本加入.

The InstalledCode class is an implementation of the AbstractCode class that represents an executable code on a remote computer. This plugin should be used if an executable is pre-installed on a computer. The InstalledCode represents the code by storing the filepath of the relevant executable and the computer on which it is installed. The computer is represented by an instance of Computer. Each time a CalcJob is run using an InstalledCode, it will run its executable on the associated computer. Example of creating an InstalledCode:

from aiida.orm import InstalledCode
code = InstalledCode(
    label='some-label',
    computer=load_computer('localhost'),
    filepath_executable='/usr/bin/bash'
)

在 2.3 版本发生变更: filepath_executable 不再要求是绝对路径,而可以是可执行文件的名称。

可移植代码#

在 2.1 版本加入.

The PortableCode class is an implementation of the AbstractCode class that represents an executable code stored in AiiDA’s storage. This plugin should be used for executables that are not already installed on the target computer, but instead are available on the machine where AiiDA is running. The plugin assumes that the code is self-contained by a single directory containing all the necessary files, including a main executable. When constructing a PortableCode, passing the absolute filepath as filepath_files will make sure that all the files contained within are uploaded to AiiDA’s storage. The filepath_executable should indicate the filename of the executable within that directory. Each time a CalcJob is run using a PortableCode, the uploaded files will be automatically copied to the working directory on the selected computer and the executable will be run there. Example of creating an PortableCode:

from pathlib import Path
from aiida.orm import PortableCode
code = PortableCode(
    label='some-label',
    filepath_files=Path('/some/path/code'),
    filepath_executable='executable.exe'
)

容器化代码#

在 2.1 版本加入.

The ContainerizedCode class allows running an executable within a container image on a target computer. The data plugin stores the following information in the database:

  • image_name : 容器映像的名称(如 docker://alpine:3 这样的 URI 或 /path/to/image.sif 这样的绝对文件路径)。

  • filepath_executable :容器内可执行文件的文件路径(如 /usr/bin/bash )。

  • engine_command :调用容器映像的 bash 命令(如 singularity exec --bind $PWD:$PWD {image_name} )。该命令的具体形式取决于所使用的容器化技术。

  • computer :要在其上运行容器的 Computer

备注

如果目标计算机上还没有容器映像,大多数容器 engine 会在首次使用时从注册表中提取映像。如果映像较大,这可能需要一段时间。

重要

如果 engine_command 包含变量(如 singularity exec --bind $PWD:$PWD {image_name} 示例),则 Computer 必须将 use_double_quotes 设置为 True 。默认情况下, Computer 将使用单引号来转义命令行参数,因此 $PWD 不会被展开。如果在创建 Computer 时没有这样定义,可以通过 API 进行更改:

computer = load_computer('some-computer')
computer.set_use_double_quotes(True)

设置#

可以通过 CLI 和 API 创建 ContainerizedCode 。以下示例展示了如何通过 Singularity 在基础 Docker 容器中设置运行 bash ,以便在名为 some-computerComputer 上运行:

verdi code create core.code.containerized \
    --non-interactive \
    --label containerized-code \
    --default-calc-job-plugin core.arithmetic.add \
    --computer some-computer \
    --filepath-executable "/bin/sh" \
    --image-name "docker://alpine:3" \
    --engine-command "singularity exec --bind $PWD:$PWD {image_name}"
from aiida.orm import ContainerizedCode, load_computer

code = ContainerizedCode(
    computer=load_computer('some-computer')
    filepath_executable='/bin/sh'
    image_name='docker://alpine:3',
    engine_command='singularity exec --bind $PWD:$PWD {image_name}'
).store()

有关每个容器化解决方案的概述和具体设置说明,请参阅 supported container technologies 部分。

运行#

与其他代码一样, ContainerizedCode 用于启动计算。如果定义了默认计算作业插件,则可使用 get_builder 获得流程生成器:

from aiida.engine import submit
from aiida.orm import load_code

code = load_code('containerized-code')
builder = code.get_builder()
# Define the rest of the inputs
submit(builder)

重要

如果容器化代码用于启用 MPI 的计算(请参阅 Controlling MPI ),则 MPI 命令行参数会放在容器运行时的前面。例如,当运行启用了 MPI 的 Singularity 时,提交脚本中的运行行将被写为

"mpirun" "-np" "1" "singularity" "exec" "--bind" "$PWD:$PWD" "ubuntu" '/bin/bash' '--version' '-c' < "aiida.in" > "aiida.out" 2> "aiida.err"

这意味着容器化程序是作为普通 MPI 程序启动的,因此它需要支持将执行上下文转发给容器应用程序。目前还无法在容器运行时内部调用 MPI。

支持的容器技术#

ContainerizedCode 兼容多种容器技术:

要使用 Docker aiida-core==2.3.0 或更高版本,才能设置 wrap_cmdline_params = True 。为 Docker 容器设置代码时,请使用以下 engine_command 设置代码:

docker run -i -v $PWD:/workdir:rw -w /workdir {image_name} sh -c

备注

目前尚不支持使用 MPI 运行,因为它需要在容器内调用,而这在目前是不可能的。相关计算机还应配置 use_double_quotes = False 设置。可以通过 Python API 使用 load_computer('idenfitier').set_use_double_quotes(False) 进行设置。

以下配置提供了一个示例,用于设置量子 ESPRESSO 的 pw.x 由 Docker 在本地主机上运行

label: qe-pw-on-docker
computer: localhost
engine_command: docker run -i -v $PWD:/workdir:rw -w /workdir {image_name} sh -c
image_name: haya4kun/quantum_espresso
filepath_executable: pw.x
default_calc_job_plugin: quantumespresso.pw
use_double_quotes: false
wrap_cmdline_params: true

将配置保存到 code.yml ,然后使用 verdi CLI 创建代码:

verdi code create core.code.containerized -n --config=code.yml

要使用 Singularity ,请在设置代码时使用下面的 engine_command

singularity exec --bind $PWD:$PWD {image_name}

要使用 Sarus ,请在设置代码时使用 engine_command

sarus run --mount=src=$PWD,dst=/workdir,type=bind --workdir=/workdir {image_name}

材料科学数据类型#

由于 AiiDA 最早是在计算材料科学界开发的,aiida-core 仍然包含了几种该领域特有的数据类型。本节列出了这些数据类型,并提供了一些 important 使用示例。

Entry point

储存在数据库中

存放在储存库

StructureData

structure

晶胞、周期性边界条件、原子位置、物种和种类。

\-

TrajectoryData

array.trajectory

结构种类以及单元、步长和位置阵列的形状。

数组数据的 numpy 格式。

UpfData

upf

UPF 的 MD5 和伪势元素。

伪势文件。

KpointsData

array.kpoints

(作为网格)网格和偏移量。

(作为列表)``kpoints`` 数组的形状、标签及其索引。

\-

数组数据的 numpy 格式。

BandsData

array.bands

单位、标签及其编号,以及波段和 kpoints 阵列的形状。

数组数据的 numpy 格式。

结构数据#

The StructureData data type represents a structure, i.e. a collection of sites defined in a cell. The boundary conditions are periodic by default, but can be set to non-periodic in any direction.

例如,您想为 bcc Li 创建一个 StructureData 实例。首先,让我们通过定义单元格来创建实例:

In [1]: StructureData = DataFactory('core.structure')

In [2]: unit_cell = [[3.0, 0.0, 0.0], [0.0, 3.0, 0.0], [0.0, 0.0, 3.0]]

In [3]: structure = StructureData(cell=unit_cell)

备注

AiiDA 中晶体结构单元和原子坐标的默认单位是 Å(Ångström)。

接下来,您可以使用 append_atom() 方法将 Li 原子添加到结构中:

In [4]: structure.append_atom(position=(0.0, 0.0, 0.0), symbols="Li")

In [5]: structure.append_atom(position=(1.5, 1.5, 1.5), symbols="Li")

您可以通过检查 cellsites 属性来检查小区和站点是否已正确设置:

In [6]: structure.cell
Out[6]: [[3.5, 0.0, 0.0], [0.0, 3.5, 0.0], [0.0, 0.0, 3.5]]

In [7]: structure.sites
Out[7]: [<Site: kind name 'Li' @ 0.0,0.0,0.0>, <Site: kind name 'Li' @ 1.5,1.5,1.5>]

StructureData node 中,您还可以获得著名材料科学 Python 库的格式,如 the Atomic Simulation Environment (ASE) 和 pymatgen

In [8]: structure.get_ase()
Out[8]: Atoms(symbols='Li2', pbc=True, cell=[3.5, 3.5, 3.5], masses=...)

In [9]: structure.get_pymatgen()
Out[9]:
Structure Summary
Lattice
    abc : 3.5 3.5 3.5
angles : 90.0 90.0 90.0
volume : 42.875
      A : 3.5 0.0 0.0
      B : 0.0 3.5 0.0
      C : 0.0 0.0 3.5
PeriodicSite: Li (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000]
PeriodicSite: Li (1.5000, 1.5000, 1.5000) [0.4286, 0.4286, 0.4286]

参见

JsonableData, which can store any other Pymatgen class.

出口#

The following export formats are available for StructureData:

  • xsf (XCrySDen 等可视化软件支持的格式;支持周期性单元格)

  • xyz (经典 xyz 格式,通常不支持周期单元格(即使单元格已在注释行中标明)

  • cif (导出为 CIF 格式,不降低对称性,即始终以 P1 对称性存储结构)

例如,可以使用 verdi CLI 导出 node:

$ verdi data core.structure export --format xsf <IDENTIFIER> > Li.xsf

其中 <IDENTIFIER> 是 node 可能的标识符之一,例如 PK 或 UUID。这将以 xsf 格式输出结构并将其写入文件。

轨迹数据#

The TrajectoryData data type represents a sequences of StructureData objects, where the number of atomic kinds and sites does not change over time. Beside the coordinates, it can also optionally store velocities. If you have a list of StructureData instances called structure_list that represent the trajectory of your system, you can create a TrajectoryData instance from this list:

In [1]: TrajectoryData = DataFactory('core.array.trajectory')

In [2]: trajectory = TrajectoryData(structure_list)

请注意,与 StructureData 数据类型相反,单元和原子位置是以 numpy 数组形式存储在存储库中,而不是数据库中。

出口#

您可以使用 verdi data core.trajectory export 导出 TrajectoryData node,它接受多种格式,包括 xsfcif ,以及 --step NUM (选择只导出特定轨迹步)等附加参数。

可使用以下导出格式:

  • xsf (XCrySDen 等可视化软件支持的格式;支持周期性单元格)

  • cif (导出为 CIF 格式,不降低对称性,即始终以 P1 对称性存储结构)

UpfData#

The UpfData data type represents a pseudopotential in the .UPF format (e.g. used by Quantum ESPRESSO - see also the AiiDA Quantum ESPRESSO plugin). Usually these will be installed as part of a pseudopotential family, for example via the aiida-pseudo package.

要查看已安装在 AiiDA 配置文件中的假势族,可以使用 verdi CLI:

$ verdi data core.upf listfamilies
Success: * SSSP_v1.1_precision_PBE [85 pseudos]
Success: * SSSP_v1.1_efficiency_PBE [85 pseudos]

KpointsData#

The KpointsData data type represents either a grid of k-points (in reciprocal space, for crystal structures), or explicit list of k-points (optionally with a weight associated to each one).

要创建一个 KpointsData 实例来描述由 k 个点组成的规则(2 x 2 x 2)网格,请在 verdi shell 中执行以下命令集:

In [1]: KpointsData = DataFactory('core.array.kpoints')
   ...: kpoints_mesh = KpointsData()
   ...: kpoints_mesh.set_kpoints_mesh([2, 2, 2])

这将创建一个以伽马点为中心的 (2 x 2 x 2) 网格(即无偏移)。

或者,也可以使用 set_kpoints() 方法,从 k 点列表中定义 KpointsData node:

In [2]: kpoints_list = KpointsData()
   ...: kpoints_list.set_kpoints([[0, 0, 0], [0.5, 0.5, 0.5]])

在这种情况下,您还可以为(部分)点关联标签,这对于生成带状结构图(并将其存储在 BandsData 实例中)非常有用:

In [3]: kpoints_list.labels = [[0, "G"]]

In [4]: kpoints_list.labels
Out[4]: [(0, 'G')]

自动计算 k 点路径#

AiiDA 提供了许多工具和包装器,用于自动计算给定晶胞或晶体结构的 k 点路径。

主界面由 aiida.tools.data.array.kpoints.main.get_kpoints_path()aiida.tools.data.array.kpoints.main.get_explicit_kpoints_path() 这两个方法提供。

这些方法也可以直接导出为 aiida.tools.get_kpoints_path 等,非常方便。

这两种方法的区别如下:

  • get_kpoints_path() returns a dictionary of k-point coordinates (e.g. {'GAMMA': [0. ,0. ,0. ], 'X': [0.5, 0., 0.], 'L': [0.5, 0.5, 0.5]}, and then a list of tuples of endpoints of each segment, e.g. [('GAMMA', 'X'), ('X', 'L'), ('L', 'GAMMA')] for the \(\Gamma-X-L-\Gamma\) path.

  • get_explicit_kpoints_path(), instead, returns a list of kpoints that follow that path, with some predefined (but user-customizable) distance between points, e.g. something like [[0., 0., 0.], [0.05, 0., 0.], [0.1, 0., 0.], ...].

根据底层代码的工作方式,一种方法可能优于另一种方法。

方法的文档说明了预期参数。一般接口总是需要一个 StructureData 作为第一个参数 structure ,以及一个要使用的方法字符串(默认情况下是 seekpath ,但也可以使用 AiiDA 早期版本中实现的 legacy 方法;参见下面的说明)。

附加参数以 kwargs 的形式传递给底层实现,底层实现通常接受不同数量的参数。

Seekpath 实现#

当指定 method='seekpath' 时,将使用 seekpath 库生成路径。请注意,这需要安装 seekpath (默认情况下不可用,以减少 AiiDA 核心的依赖性,但可以使用 pip install seekpath 轻松安装)。

有关可接受参数的完整描述,请参阅基础方法 aiida.tools.data.array.kpoints.seekpath.get_explicit_kpoints_path()aiida.tools.data.array.kpoints.seekpath.get_kpoints_path() 的文档,更多一般信息请参阅 seekpath documentation

如果使用此实现,请引用 Hinuma paper ::

Y. Hinuma, G. Pizzi, Y. Kumagai, F. Oba, I. Tanaka,
Band structure diagram paths based on crystallography,
Comp. Mat. Sci. 128, 140 (2017)
DOI: 10.1016/j.commatsci.2016.10.015
传统实施

这是指从 AiiDA 早期版本开始就有的实现方式。

备注

在三维情况下(所有三个方向都有周期性边界条件),本实现希望结构已经根据 Setyawan 论文(见下文期刊参考文献)标准化。如果不是这种情况,返回的 k 点和带状结构将是不正确的。程序库唯一能正确处理的情况是轴对调,程序库在分配 k 点标签和坐标时会正确考虑这种对调/旋转。

因此,我们建议您使用 seekpath 实现,它能够自动正确识别 Hinuma paper 中描述的标准化晶体结构(原始晶体结构和常规晶体结构)。

有关可接受参数的完整描述,请参阅基础方法 aiida.tools.data.array.kpoints.legacy.get_explicit_kpoints_path()aiida.tools.data.array.kpoints.legacy.get_kpoints_path() 的 docstring,更多一般信息请参阅 seekpath documentation

如果您使用这种实现方式,请从以下参考文献中引用正确的参考文献:

  • 3D 实现基于 Setyawan paper ::

    W. Setyawan, S. Curtarolo,
    High-throughput electronic band structure calculations: Challenges and tools,
    Comp. Mat. Sci. 49, 299 (2010)
    DOI: 10.1016/j.commatsci.2010.05.010
    
  • 2D 实现基于 Ramirez paper ::

    R. Ramirez and M. C. Bohm,
    Simple geometric generation of special points in brillouin-zone integrations. Two-dimensional bravais lattices
    Int. J. Quant. Chem., XXX, 391-411 (1986)
    DOI: 10.1002/qua.560300306
    

乐队数据#

The BandsData data type is dedicated to store band structures of different types (electronic bands, phonons, or any other band-structure-like quantity that is a function of the k-points in the Brillouin zone). In this section we describe the usage of the BandsData to store the electronic band structure of silicon and some logic behind its methods. The dropdown panels below explain some expanded use cases on how to create a BandsData node and plot the band structure.

手动创建 BandsData 实例

要开始使用 BandsData 数据类型,我们应该使用 DataFactory import 并创建一个 BandsData 类型的对象:

from aiida.plugins import DataFactory
BandsData = DataFactory('core.array.bands')
bands_data = BandsData()

为了 import 波段,我们需要确保有两个数组:一个包含 kpoints,另一个包含波段。kpoints 对象的形状应为 nkpoints * 3 ,而波段的形状应为 nkpoints * nstates 。假设 kpoints 的数量为 12,而状态的数量为 5,那么 kpoints 和波段数组将如下所示:

import numpy as np
kpoints = np.array(
      [[0.    , 0.    , 0.    ], # array shape is 12 * 3
      [0.1   , 0.    , 0.1   ],
      [0.2   , 0.    , 0.2   ],
      [0.3   , 0.    , 0.3   ],
      [0.4   , 0.    , 0.4   ],
      [0.5   , 0.    , 0.5   ],
      [0.5   , 0.    , 0.5   ],
      [0.525 , 0.05  , 0.525 ],
      [0.55  , 0.1   , 0.55  ],
      [0.575 , 0.15  , 0.575 ],
      [0.6   , 0.2   , 0.6   ],
      [0.625 , 0.25  , 0.625 ]])

bands = np.array(
  [[-5.64024889,  6.66929678,  6.66929678,  6.66929678,  8.91047649], # array shape is 12 * 5, where 12 is the size of the kpoints mesh
  [-5.46976726,  5.76113772,  5.97844699,  5.97844699,  8.48186734],  # and 5 is the numbe of states
  [-4.93870761,  4.06179965,  4.97235487,  4.97235488,  7.68276008],
  [-4.05318686,  2.21579935,  4.18048674,  4.18048675,  7.04145185],
  [-2.83974972,  0.37738276,  3.69024464,  3.69024465,  6.75053465],
  [-1.34041116, -1.34041115,  3.52500177,  3.52500178,  6.92381041],
  [-1.34041116, -1.34041115,  3.52500177,  3.52500178,  6.92381041],
  [-1.34599146, -1.31663872,  3.34867603,  3.54390139,  6.93928289],
  [-1.36769345, -1.24523403,  2.94149041,  3.6004033 ,  6.98809593],
  [-1.42050683, -1.12604118,  2.48497007,  3.69389815,  7.07537154],
  [-1.52788845, -0.95900776,  2.09104321,  3.82330632,  7.20537566],
  [-1.71354964, -0.74425095,  1.82242466,  3.98697455,  7.37979746]])

要在 bands_data 对象中插入 k 点和波段,我们应该使用 set_kpoints()set_bands() 方法:

bands_data.set_kpoints(kpoints)
bands_data.set_bands(bands, units='eV')
绘制带状结构图

接下来,我们要将带状结构可视化。在此之前,我们可能需要添加 k 点标签阵列:

labels = [(0, 'GAMMA'),
          (5, 'X'),
          (6, 'X'),
          (11, 'U')]

bands_data.labels = labels
bands_data.show_mpl() # to visualize the bands

由此产生的带状结构如下

../_images/bands.png

警告

与任何 AiiDA node 一样,一旦 bands_data 对象被存储( bands_data.store() ),它将不接受任何修改。

您可能会注意到,根据您分配 kpoints 标签的方式, show_mpl() 方法的输出结果看起来有所不同。请比较一下:

bands_data.labels = [(0, 'GAMMA'),
          (5, 'X'),
          (6, 'Y'),
          (11, 'U')]
bands_data.show_mpl()

bands_data.labels = [(0, 'GAMMA'),
          (5, 'X'),
          (7, 'Y'),
          (11, 'U')]
bands_data.show_mpl()

在第一种情况下,标签分别为 XY 的两个相邻 k 点看起来就像 X|Y ,而在第二种情况下,它们之间会相隔一定的距离。造成这种差异的原因如下。在第一种情况下,绘制方法会发现两个相邻的 k 点,并假定它们是带状结构中的不连续点(例如 Gamma-X|Y-U)。在第二种情况下,标为 XY 的 k 点不再是相邻点,因此在绘制时它们之间有一定的距离。X 轴上 k 点之间的间隔与它们之间的直线距离成正比。

处理旋转

BandsData 对象还可以处理自旋极化计算的结果。如果要为两种不同的自旋提供不同的波段,您只需将它们合并到一个数组中,然后使用 set_bands() 方法再次进行 import:

bands_spins = [bands, bands-0.3] # to distinguish the bands of different spins we subtract 0.3 from the second band structure
bands_data.set_bands(bands_spins, units='eV')
bands_data.show_mpl()

现在,频带阵列的形状变为 nspins * nkpoints * nstates

出口

The BandsData data type can be exported with verdi data core.bands export, which accepts a number of formats including (see also below) and additional parameters like --prettify-format FORMATNAME, see valid formats below, or --y-min-lim, --y-max-lim to specify the y-axis limits.

可使用以下导出格式:

  • agr :导出带有波段图的 Xmgrace .agr 文件

  • agr_batch :将 Xmgrace 批次文件与独立的 .dat 文件一起导出

  • dat_blocks :导出一个 .dat 文件,其中每一行都有一个数据点(xy),带以空行分隔成块。

  • dat_multicolumn :导出一个 .dat 文件,其中每一行都包含给定 x 坐标的所有数值: x y1 y2 y3 y4 ... (x``为沿带状路径的线性坐标, ``yN 为带状能量)。

  • gnuplot :导出 gnuplot 文件和 .dat 文件。

  • json :导出一个 json 文件,其中将波段划分为多个区段。

  • mpl_singlefile :导出一个 python 文件,执行该文件时将显示使用 matplotlib 模块绘制的曲线图。所有数据都以 json 格式多行字符串的形式包含在同一个 python 文件中。

  • mpl_withjson :同上,但 json 数据单独存储在不同的文件中。

  • mpl_pdf :同上,但在创建.py 文件后,运行该文件将带状结构导出为 PDF 文件(矢量)。 注意 :需要安装 python matplotlib 模块。如果 use_latex 为真,则需要在系统中安装 LaTeX 来排版标签以及 dvipng 二进制文件。

  • mpl_png :同上,但在创建.py 文件后,运行该文件将带状结构导出为 PDF 文件(矢量)。 注意 :此格式与上述 mpl_pdf 格式具有相同的依赖关系。

AiiDA 提供了一些函数来 ``prettify`` 频带结构的标签(如果标签存在于数据 node),例如,用 \(\Gamma\) 替换 GAMMA ,或用 \(K_{1}\) 替换 K_1 。这对某些输出格式(如 Xmgrace、Gnuplot、matplotlib)是有意义的。

预置函数被定义为 Prettifier 类的方法,可通过调用 Prettifier.get_prettifiers() 获取。

应根据两个方面来选择预处理剂:

  1. 原始标签在数据库中的存储方式。目前有两种类型: seekpath 模块中使用的 seekpath 格式,其中明确写入希腊字母(如 GAMMA ),下划线用于表示下标( K_1 );以及 ``old`` simple 格式,其中 \(\Gamma\)G 表示,没有下划线符号)。

  2. 视输出格式而定:xmgrace 对希腊字母和下标有特定的语法,matplotlib 使用 LaTeX 语法等。

大多数导出格式已经决定了最好使用哪种预置符,但如果需要更改,可以将 prettify_format 参数传递给 export() 方法。有效的预置符包括

  • agr_seekpath :Xmgrace 格式,使用 seekpath 原始标签语法。

  • agr_simple :Xmgrace 格式,使用 simple 原始标签语法。

  • latex_simple :LaTeX 格式(包括美元符号),使用 seekpath 原始标签语法。

  • latex_seekpath :LaTeX 格式(包括美元符号),使用 simple 原始标签语法。

  • gnuplot_simple :GNUPlot 格式(希腊字母使用 Unicode,下划线使用 LaTeX 语法 without 美元符号),使用 seekpath 原始标签语法。

  • gnuplot_seekpath :GNUPlot 格式(希腊字母使用 Unicode,下划线使用 LaTeX 语法 without 美元符号),使用 simple 原始标签语法。

  • pass :无操作预置符:使所有字符串保持原始值不变。

导出数据 nodes#

除上述 CLI 命令外,每个数据 node 都有一个 export() 方法,可将给定的数据 node 以各种可用格式导出到文件中,例如将其传递给可视化软件。

The export() method asks for a filename, and it will write to file the result. It is possible that more than one file is written (for example, if you produce a gnuplot script, the data will typically be in a different .dat file). The return value of the function is a list of files that have been created.

导出格式列表取决于特定的数据插件。导出格式通常由文件扩展名推断,但如果无法推断(或想指定特定格式),可向 export() 传递一个额外的 fileformat 参数。所有有效导出格式的列表可通过 Data.get_export_formats() 方法获取,该方法会返回包含所有有效格式的字符串列表。

如果不想直接导出到文件,而只想获得文件内容的字符串,则可以调用 _exportcontent() 方法,同时传递一个 fileformat 参数。返回值是一个长度为 2 的元组:第一个元素是一个字符串,包含 ``main`` 文件的内容;第二个元素是一个字典(可能为空),包含应创建/需要的附加文件列表:键是文件名,值是文件内容。

增加对自定义数据类型的支持#

The nodes in the provenance graph that are the inputs and outputs of processes are referred to as data and are represented by Data nodes. Since data can come in all shapes and forms, the Data class can be sub classed. AiiDA ships with some basic data types such as the Int which represents a simple integer and the Dict, representing a dictionary of key-value pairs. There are also more complex data types such as the ArrayData which can store multidimensional arrays of numbers. These basic data types serve most needs for the majority of applications, but more specific solutions may be useful or even necessary. In the next sections, we will explain how a new data type can be created and what guidelines should ideally be observed during the design process.

创建数据插件#

创建一个新的数据类型就像为基础 Data 类创建一个新的子类一样简单。

from aiida.orm import Data

class NewData(Data):
    """A new data type that wraps a single value."""

备注

要使用新的 Data 插件,必须使用 entry point 注册。详见 What is an entry point?

在这一点上,我们的新数据类型没有什么特别之处。通常情况下,创建新数据类型是为了表示特定类型的数据。在本例中,我们假设 NewData 数据类型的目标是存储一个数值。例如,我们可以用所需的 value 构建一个新的 NewData 数据 node:

node = NewData(value=5)

我们需要允许将该值传递给 node 类的构造函数。因此,我们必须覆盖构造函数 __init__()

from aiida.orm import Data

class NewData(Data):
    """A new data type that wraps a single value."""

    def __init__(self, **kwargs):
        value = kwargs.pop('value')
        super().__init__(**kwargs)
        self.base.attributes.set('value', value)

警告

要使类正常运行,构造函数的签名 不能更改 ,并且必须调用父类的构造函数**。还要注意的是,之后从数据库加载 node 时,构造函数***不会被调用。因此,不应该依赖于在 __init__ 本身内部初始化实例属性(这里的 ``attributes`` 并不是指存储在数据库中的数据,而是指 Python 对类实例属性的正常理解)。

在调用基类的构造函数之前,我们必须从关键字参数 kwargs 中删除 value 关键字,因为基类不会期望它出现,如果关键字参数中留有该关键字,就会引发异常。最后一步是实际*存储构造函数调用者传递的值。一个新的 node 有两个位置可以永久存储其任何属性:

  • 数据库

  • 文件库

有关 design guidelines 的章节将详细介绍每种选项的优缺点以及何时使用。目前,由于我们只存储单个值,最简单也是最好的选择就是使用数据库。每个 node 都有 属性 ,可以存储任何键值对,只要值是可序列化的 JSON 即可。将值添加到 node 的属性中后,一旦存储了 NewData node 的实例,就可以在数据库中查询这些值。

node = NewData(value=5)   # Creating new node instance in memory
node.base.attributes.set('value', 6)  # While in memory, node attributes can be changed
node.store()  # Storing node instance in the database

在数据库中存储 node 实例后,其属性将被冻结, node.base.attributes.set('value', 7) 将失效。通过在 node 实例的属性中存储 value ,我们可以确保即使在以后重新加载 node 时也能检索到 value

除了确保数据 node 的内容存储在数据库或文件存储库中,数据类型类还可以为用户提供有用的方法来检索这些数据。例如,根据 NewData 类的当前状态,要检索已存储的 NewData node 的 value 内容,需要执行以下操作

node = load_node(<IDENTIFIER>)
node.base.attributes.get('value')

换句话说, NewData 类的用户需要知道, value 被存储为名称为 value``的属性。这并不容易记住,因此对用户也不是很友好。既然 ``NewData 类型是一个类,我们就可以为它提供有用的方法。让我们引入一个方法来返回为其存储的值:

from aiida.orm import Data

class NewData(Data):
    """A new data type that wraps a single value."""

    ...

    @property
    def value(self):
        """Return the value stored for this instance."""
        return self.base.attributes.get('value')

增加实例属性 value 后,检索 NewData node 的值就容易多了:

node = load_node(<IDENTIFIER)
value = node.value

如前所述,除了属性之外,数据类型还可以在文件存储库中存储其属性。下面是一个需要封装单个文本文件的自定义数据类型的示例:

import os
from aiida.orm import Data


class TextFileData(Data):
    """Data class that can be used to wrap a single text file by storing it in its file repository."""

    def __init__(self, filepath, **kwargs):
        """Construct a new instance and set the contents to that of the file.

        :param file: an absolute filepath of the file to wrap
        """
        super().__init__(**kwargs)

        filename = os.path.basename(filepath)  # Get the filename from the absolute path
        self.put_object_from_file(filepath, filename)  # Store the file in the repository under the given filename
        self.base.attributes.set('filename', filename)  # Store in the attributes what the filename is

    def get_content(self):
        """Return the content of the single file stored for this data node.

        :return: the content of the file as a string
        """
        filename = self.base.attributes.get('filename')
        return self.get_object_content(filename)

创建该数据类型的新实例并获取其内容:

node = TextFileData(filepath='/some/absolute/path/to/file.txt')
node.get_content()  # This will return the content of the file

本示例是 aiida-core 附带的 SinglefileData 数据类的简化版本。如果这恰好是您的用例(或非常接近用例),当然最好使用该类,或者您可以对其进行子类化,并在需要时对其进行调整。

刚刚介绍的新数据类型示例当然是微不足道的,但概念始终是相同的,可以很容易地扩展到更复杂的自定义数据类型。下一节将就如何优化设计新数据类型提供有用的指导。

数据库还是存储库?#

在决定在哪里存储数据类型的属性时,用户可以在数据库和文件库之间进行选择。所有存储在数据库中的 node 属性(如属性)都可以作为数据库查询的一部分直接搜索,而存储在文件库中的数据则无法查询。这意味着,例如,可以搜索数据库存储的某一整数属性在某一数值范围内的所有 node,但存储在文件存储库内的文件中的相同数值却不能以这种方式直接搜索。然而,在数据库中存储大量数据的代价是降低数据库查询的速度。因此,内容不一定需要查询的大数据(即大文件)最好存储在文件库中。数据类型可以安全地同时并行使用数据库和文件存储库来存储单个属性。存储在数据库中的属性是作为 node 的 属性 存储的。node 类有各种方法来设置这些属性,如 set()set_many()

Fields#

在 2.6 版本加入.

The attributes of new data types may be exposed to end users by explicitly defining each attribute field under the __qb_fields__ class attribute of the new data class.

from aiida.orm.fields import add_field

class NewData(Data):
    """A new data type."""

    __qb_fields__ = [
        add_field(
          key='frontend_key',
          alias='backend_key',  # optional mapping to a backend key, if different (only allowed for attribute fields)
          dtype=str,
          is_attribute=True,  # signalling if field is an attribute field (default is `True`)
          is_subscriptable=False,  # signalling subscriptability for dictionary fields
          doc='An example field',
        )
    ]

The internal mechanics of aiida.orm.fields will dynamically add frontend_key to the fields attribute of the new data type. The construction of fields follows the rules of inheritance, such that other than its own fields, NewData.fields will also inherit the fields of its parents, following the inheritance tree up to the root Entity ancestor. This enhances the usability of the new data type, for example, allowing the end user to programmatically define filters and projections when using AiiDA’s QueryBuilder.

备注

add_field() will return the flavor of QbField associated with the type of the field defined by the dtype argument, which can be given as a primitive type or a typing-module type hint, e.g. Dict[str, Any]. When using the data class in queries, the logical operators available to the user will depend on the flavor of QbField assigned to the field.