存储库/资源库 (Repository)#

除了 database ,AiiDA还以文件形式在 资源库 中存储信息。存储库经过优化,可以存储大量文件,这使得AiiDA可以扩展到高吞吐量负载。因此,尽管文件存储在本地文件系统的某处,但不能直接使用文件系统工具访问。相反,你应该通过应用程序接口与资源库交互。

由于每个 node 都可以拥有自己的 虚拟 文件层次结构,因此 node 的存储库内容可通过 Node 类访问。层次结构是虚拟的,因为文件实际上可能不会以相同的层次结构写入磁盘。有关实现的更多技术信息,请参阅 repository internals section

写入存储库#

要将文件写入 node,可以使用以下三种方法之一:

假设本地文件系统中有一个名为 /some/path/file.txt 的文件,要将其复制到 node 中。最直接的解决方案如下:

node = Node()
node.put_object_from_file('/some/path/file.txt', 'file.txt')

请注意,第一个参数应为绝对文件路径。第二个参数是将文件写入 node 存储库的文件名。只要是相对文件名,可以是任何有效的文件名。目标文件名可以包含嵌套子目录,例如 some/relative/path/file.txt。嵌套目录不必存在。

另外,也可以通过流或类文件对象将文件写入 node。当文件内容已在内存中时,这种方法非常有用,可以避免先将文件写入本地文件系统。例如,我们可以这样做

with open('/some/path/file.txt') as handle:
    node = Node()
    node.put_object_from_filelike(handle, 'file.txt')

与上例相同,只是先在上下文管理器中打开文件,然后再传入类文件对象。 put_object_from_filelike() 方法适用于任何类文件对象,例如字节流和文本流:

import io
node = Node()
node.put_object_from_filelike(io.BytesIO(b'some content'), 'file.txt')

最后,您可以将整个目录的内容写入 node 的存储库,而不是一次写入一个文件:

node = Node()
node.put_object_from_tree('/some/directory')

整个目录的内容将递归写入 node 的版本库。您也可以选择将内容写入版本库中的子目录:

node = Node()
node.put_object_from_tree('/some/directory', 'some/sub/path')

put_object_from_file() 一样,无需首先明确创建子目录。

列出存储库内容#

要确定 node 存储库的内容,可以使用以下方法:

第一个方法将返回 node 资源库中的文件对象列表,对象可以是目录或文件:

In [1]: node.list_object_names()
Out[1]: ['sub', 'file.txt']

要确定子目录的内容,只需将路径作为参数传递即可:

In [1]: node.list_object_names('sub/directory')
Out[1]: ['nested.txt']

请注意,返回列表中的元素是简单字符串,因此无法判断它们对应的是目录还是文件。如果需要此信息,请使用 list_objects() 代替。该方法将返回一个包含 File 对象的列表。这些对象有一个 file_type()name() 属性,分别返回文件对象的类型和名称。使用示例如下

from aiida.repository.common import FileType

for obj in node.list_objects():
    if obj.file_type == FileType.DIRECTORY:
        print(f'{obj.name} is a directory.)
    elif obj.file_type == FileType.FILE:
        print(f'{obj.name} is a file.)

要检索具有特定相对路径的特定文件对象,请使用 get_object()

In [1]: node.get_object('sub/directory/nested.txt')
Out[1]: File(file_type=FileType.FILE, name='nested.txt')

最后,如果要递归遍历 node 资源库的内容,可以使用 walk() 方法。它的操作方法与 os.walk method of the Python standard library 方法完全相同:

In [1]: for root, dirnames, filenames in node.walk():
            print(root, dirnames, filenames)
Out[1]: '.', ['sub'], ['file.txt']
        'sub', ['directory'], []
        'sub/directory', [], ['nested.txt']

从 Repository 中读取#

要检索存储在 node 资源库中的文件内容,可以使用以下方法:

第一种方法的功能与 Python 的 open 内置函数完全相同:

with node.open('some/file.txt', 'r') as handle:
    content = handle.read()

如果您想直接将内容读入内存, get_object_content() 方法为这一操作提供了捷径:

content node.get_object_content('some/file.txt', 'r')

这两种方法都接受第二个参数,以确定文件是以文本模式还是二进制模式打开。有效值分别为 'r''rb' 。请注意,这些方法只能用于从版本库读取内容,因此任何其他读取模式(如 'wb' )都会导致异常。要将文件写入版本库,请使用 writing to the repository 章节中描述的方法。

从Repository复制#

如果要复制 node 资源库中的特定文件,有关 reading from the repository 的章节介绍了如何读取这些文件的内容,然后将其写入其他地方。不过,有时你想复制 node 资源库的全部内容,或其中的一个子目录。 copy_tree() 方法可以轻松实现这一目的,其使用方法如下:

node.copy_tree('/some/target/directory')

这将把 node 的整个版本库内容写入本地文件系统的 /some/target/directory 目录。如果只想复制版本库的某个子目录,可以将其作为第二个 path 参数传递:

node.copy_tree('/some/target/directory', path='sub/directory')

这种方法与 put_object_from_tree() 结合使用,可以轻松地将整个资源库内容(或子目录)从一个 node 复制到另一个 node 中:

import tempfile
node_source = load_node(<PK>)
node_target = Node()

with tempfile.TemporaryDirectory() as dirpath:
    node_source.copy_tree(dirpath)
    node_target.put_object_from_tree(dirpath)

请注意,这种方法并不是最有效的,因为文件首先要从 node_a 写入磁盘上的临时目录,然后再从内存中读取并写入 node_b 的存储库。还有一种更高效的方法,需要更多代码,它直接使用 listing repository content 章节中解释的 walk() 方法。

node_source = load_node(<PK>)
node_target = Node()

for root, dirnames, filenames in node_source.walk():
    for filename in filenames:
        filepath = root / filename
        with node_source.open(filepath) as handle:
            node_target.put_object_from_filelike(handle, filepath)

备注

在上面的示例中,只有文件被明确复制过来。任何中间嵌套目录都将在虚拟层次结构中自动创建。不过,目前还无法明确创建目录。目前还不支持空目录。