AKFReader

The AKFReader Python class provides a convenient way to read annotated data from the binary KF files generated by AMS.

Below is an example demonstrating some of the AKFReader class’s functionality.

#!/usr/bin/env amspython

from scm.akfreader import AKFReader

# Load a kf file.
akf = AKFReader("test.results/dftb.rkf")


# Print a summary of the content in the kf file:
print(akf.table_of_content())

# Also print comments and extra info for all variables
print(akf.table_of_content(verbose=True))


# Read a specific variable from file.
coordinates = akf.read("Molecule%Coords")
print("Coordinates:")
print(coordinates)

# Coordinates:
# [[-1.65367945e+00  4.59827802e+00 -9.59515212e-09]
#  ...
#  [-2.21256221e+00  5.05007997e+00 -1.93285786e+00]]


# Retrieve the description, units, and additional information for a variable:
description_dict = akf.description("Molecule%Coords")
print("'Molecule%Coords' description:")
print(description_dict)

# 'Molecule%Coords' description:
# {'_type': 'float_array',
#  '_shape': [3, 'nAtoms'],
#  '_comment': 'Coordinates of the nuclei (x,y,z)',
#  '_unit': 'bohr'}


# Read another variable, and do a unit conversion:
energy_in_eV = akf.read("AMSResults%Energy", units="eV")
print(f"Energy: {energy_in_eV} [eV]")


# Read all variables matching the pattern:
history_energies = akf.read("History%Energy(#)")
print(history_energies)

# [('History%Energy(1)', -5.758015498298242),
#  ('History%Energy(2)', -5.765039556660788),
#  ('History%Energy(3)', -5.766261662994689),
#  ('History%Energy(4)', -5.766288141072665)]

Note

The KFFile Python class can also be used to read KF files. Key differences between the AKFReader and the KFFile:

  • The AKFReader can provide extra information about the content of the KF files: it can give descriptions of sections, variables, units, and automatically reshapes matrices to the proper size/shape.

  • The KFFile can also be used to write KF files (while the AKFReader can only read).

See also

A command-line utility $AMSBIN/akf (described here) offers an alternative way of using the AKFReader tool.

See also

The list of possible variables for the various kf files can be found in the respective user manuals: ams.rkf, adf.rkf, band.rkf, dftb.rkf

API documentation:

class AKFReader(path: str | PathLike[str] | KFFile, kf_def_path=None, strict=False, verbose=False)

A python class for reading AMS binary KF files.

Parameters:
  • path (str, os.PathLike or KFFile): the KF file (.rkf) to read, either its path, or an already open scm.base.KFFile, which the reader then uses as it is. (The other optional constructor parameters are expert options and are best left untouched.)

Example:

from scm.akfreader import AKFReader

akf = AKFReader("test.results/dftb.rkf") # <- path to ams .rkf result file

print(akf.table_of_content())

coordinates = akf.read("Molecule%Coords")
energy = akf.read("AMSResults%Energy", units="eV")
read(name: str, units=None) → Any

Read and return the value of a variable on file.

Parameters:
  • name (str): The name of the variable to read. This should be of the form “SectionName%VariableName”, so for example akf.read(“Molecule%Coords”) means “read the variable ‘Coords’ from Section ‘Molecule’)

  • units (str): the desired output units. See the :Units: class for more info on avaiable units.

Returns:
  • The content of the variable. The return type depends on the variable you are reading. So, if the variable is an integer, and int will be return. If it’s an array, a numpy array will be returned.

Example:

>>> energy = akf.read("AMSResults%Energy")
>>> energy_in_eV = akf.read("AMSResults%Energy", units="eV")

Note: in case the variable is a “pattern” (i.e. the variable name contains “#” or “*” characters, the result will be a list of tuples of all variables matching the pattern. E.g.: ‘foo(#)’ will return [(‘foo(1)’, value1), (‘foo(2)’, value2)]

find(name: str) → list

Given the name of a variable with patterns, return the list of actual variables that match the query.

E.g. if name=’History%Energy(#)’, this might return: [‘History%Energy(1)’,’History%Energy(2)’]

table_of_content(verbose=False) → str

Returns the table of content of the KF file (i.e. the list of all sections and variables on file) in text format.

Parameters:
  • verbose (bool): if verbose is True, the full description of all variables will be included

Example:

>>> print(akf.table_of_content())

Content for KF file: /test/dftb.rkf

== AMSResults ==

    - AMSResults%Energy
    - AMSResults%Gradients
    - AMSResults%DipoleMoment
    ...

>>> print(akf.table_of_content(verbose=True))

Content for KF file: /test/dftb.rkf

== AMSResults ==

    comment   : Generic results of the DFTB evaluation.
    type      : section

    - AMSResults%Energy
        comment   : The energy computed by the engine.
        type      : float
        unit      : hartree

    - AMSResults%Gradients
        comment   : The nuclear gradients.
        shape     : [3, 'Molecule%nAtoms']
        type      : float_array
        unit      : hartree/bohr

    - AMSResults%DipoleMoment
        comment   : Dipole moment vector (x,y,z)
        shape     : [3]
        type      : float_array
        unit      : e*bohr
    ...
variables(include_hidden=False) → List[str]

Returns the list of all variables on the KF file (in the form “section%variable”).

Example:

>>> all_variables = akf.variables()
>>> print(all_variables)

['AMSResults%Charges',
'AMSResults%DipoleMoment',
...
...
'Orbitals%Energies(#)'
'Orbitals%Occupations(#)']
sections() → List[str]

Returns the list of all sections on the KF file.

Example:

>>> print(akf.sections())

['General', 'AMSResults', 'Molecule', 'Orbitals', ...]
variables_in_section(section: str) → List[str]

Returns the list of all variables in the specified section.

Example:

>>> print(akf.variables_in_section("Molecule"))

['Molecule%AtomMasses', 'Molecule%Coords', 'Molecule%AtomicNumbers', ...]

>>> for v in akf.variables_in_section("Molecule"):
>>>     print(f"{v}: {akf.read(v)}")
__contains__(name: str)

Whether or not ‘name’ is present in the KF file. This can either be a section (e.g. “Molecule”) or a variable (e.g. “Molecule%Coords”) Example:

if "Molecule%Coords" in akf:
    coords = akf.read("Molecule%Coords")
else:
    print("Variable 'Molecule%Coords' not present of file")
    print("Is the 'Molecule' section present? ", "Molecule" in akf)
description(name: str) → Dict[str, Any]

Return the description (meta-data) of the variable as a dictionary.

Parameters:
  • name (str): The name of the variable (this should be in the form “Section%Variable”)

Returns:
  • dictionary containing the available meta-data for the entry. Note that not meta-data entries are defined for all variables. For example, if a variable does not have a unit, the “_unit” key will not be present in the resulting dictionary

Example:

>>> d = akf.description("Molecule%Coords")
>>> print(type(d))

<class 'dict'>

>>> print(d)
{'_type': 'float_array',
 '_shape': [3, 'nAtoms'],
 '_comment': 'Coordinates of the nuclei (x,y,z)',
 '_unit': 'bohr'}
str_description(name: str) → str

Return the description of the variable as a string.

Example:

>>> print(akf.str_description("Molecule%Coords"))

comment   : Coordinates of the nuclei (x,y,z)
shape     : [3, 'nAtoms']
type      : float_array
unit      : bohr
units(name: str) → str | None

Return the unit of measurement of the variable if specified, otherwise returns None.

read_int(name: str) → int

Read a single integer, e.g. akf.read_int("Molecule%nAtoms").

Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not a single integer.

read_real(name: str, units: str | None = None) → float

Read a single real number, e.g. akf.read_real("AMSResults%Energy", units="eV").

units converts the value from the units in its KF definition. Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not a single real number.

read_logical(name: str) → bool

Read a single logical, e.g. akf.read_logical("NEB%climbing").

Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not a single logical.

read_string(name: str) → str

Read a single string, e.g. akf.read_string("General%program").

Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not a single string.

read_ints(name: str, *, ndim: Literal[1] = 1) → np.ndarray[Tuple[int], np.dtype[np.int32]]
read_ints(name: str, *, ndim: Literal[2]) → np.ndarray[Tuple[int, int], np.dtype[np.int32]]
read_ints(name: str, *, ndim: Literal[3]) → np.ndarray[Tuple[int, int, int], np.dtype[np.int32]]
read_ints(name: str, *, ndim: Literal[4]) → np.ndarray[Tuple[int, int, int, int], np.dtype[np.int32]]

Read an array of integers, as a numpy array of int32, the type in which KF files store them.

The array has the shape in its KF definition, which must have ndim dimensions. Variables without a shape in their definition are one-dimensional. Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not an array of integers with ndim dimensions.

Example: akf.read_ints("Molecule%AtomicNumbers")

read_reals(name: str, *, ndim: Literal[1] = 1, units: str | None = None) → np.ndarray[Tuple[int], np.dtype[np.float64]]
read_reals(name: str, *, ndim: Literal[2], units: str | None = None) → np.ndarray[Tuple[int, int], np.dtype[np.float64]]
read_reals(name: str, *, ndim: Literal[3], units: str | None = None) → np.ndarray[Tuple[int, int, int], np.dtype[np.float64]]
read_reals(name: str, *, ndim: Literal[4], units: str | None = None) → np.ndarray[Tuple[int, int, int, int], np.dtype[np.float64]]

Read an array of real numbers, as a numpy array of float64.

The array has the shape in its KF definition, which must have ndim dimensions. Variables without a shape in their definition are one-dimensional. units converts the values from the units in the KF definition. Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not an array of real numbers with ndim dimensions.

Example: akf.read_reals("Molecule%Coords", ndim=2, units="angstrom"), an nAtoms x 3 array

read_logicals(name: str, *, ndim: Literal[1] = 1) → np.ndarray[Tuple[int], np.dtype[np.bool_]]
read_logicals(name: str, *, ndim: Literal[2]) → np.ndarray[Tuple[int, int], np.dtype[np.bool_]]
read_logicals(name: str, *, ndim: Literal[3]) → np.ndarray[Tuple[int, int, int], np.dtype[np.bool_]]
read_logicals(name: str, *, ndim: Literal[4]) → np.ndarray[Tuple[int, int, int, int], np.dtype[np.bool_]]

Read an array of logicals, as a numpy array of bool.

The array has the shape in its KF definition, which must have ndim dimensions. Variables without a shape in their definition are one-dimensional. Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not an array of logicals with ndim dimensions.

Example: akf.read_logicals("NEB%fixed")

read_strings(name: str) → List[str]

Read an array of strings as a list, e.g. akf.read_strings("Vibrations%IrReps").

Raises a KeyError if the variable is not on the file, and an AKFTypeError if it is not an array of strings.

Typed reading methods:

The generic read method returns whatever a variable holds, and can also read all variables matching a pattern. The typed read methods return exactly the type in their name: Python scalars, numpy arrays of int32, float64 or bool with the shape from the variable’s definition, or lists of strings. If a variable holds anything else, they raise an AKFTypeError.

exception AKFTypeError

A variable on a KF file does not have the type of value that was asked for.

When using the typed readers for arrays, the arrays rank needs to be specified with the ndim argument. This is necessary because the rank of the return type is deduced by static Python checkers from the value of the ndim argument, since numpy arrays of different ranks are considered separate types.

coordinates = akf.read_reals("Molecule%Coords", ndim=2)
print("Coordinates:")
print(coordinates)