Source code for aiida_quantumespresso.tools.calculations.pw

# -*- coding: utf-8 -*-
"""Tools for nodes created by running the `PwCalculation` class."""
from aiida.common import AttributeDict, exceptions
from aiida.tools.calculations.base import CalculationTools

from aiida_quantumespresso.calculations.functions.create_magnetic_configuration import create_magnetic_configuration


[docs]class PwCalculationTools(CalculationTools): """Calculation tools for `PwCalculation`. Methods implemented here are available on any `CalcJobNode` produced by the `PwCalculation class through the `tools` attribute. """ # pylint: disable=too-few-public-methods
[docs] def get_scf_accuracy(self, index=0): """Return the array of SCF accuracy values for a given SCF cycle. :param index: the zero-based index of the desired SCF cycle :return: a list of SCF accuracy values of a certain SCF cycle. :raises ValueError: if the node does not have the `output_trajectory` output :raises ValueError: if `output_trajectory` does not have the `scf_accuracy` or `scf_iterations` arrays :raises IndexError: if the `index` is out of range """ try: trajectory = self._node.outputs.output_trajectory except exceptions.NotExistent as exc: raise ValueError(f'{self._node} does not have the `output_trajectory` output node') from exc try: scf_accuracy = trajectory.get_array('scf_accuracy') except KeyError as exc: raise ValueError(f'{trajectory} does not contain the required `scf_accuracy` array') from exc try: scf_iterations = trajectory.get_array('scf_iterations') except KeyError as exc: raise ValueError(f'{trajectory} does not contain the required `scf_iterations` array') from exc number_of_frames = len(scf_iterations) if index < -number_of_frames or index >= number_of_frames: raise IndexError(f'invalid index {index}, must be between {-number_of_frames} and {number_of_frames - 1}') # building an scf_accuracy_index for easier manipulation scf_accuracy_index = [0] for i in scf_iterations: scf_accuracy_index.append(scf_accuracy_index[-1] + i) if index < 0: return scf_accuracy[scf_accuracy_index[index - 1]:scf_accuracy_index[index]] return scf_accuracy[scf_accuracy_index[index]:scf_accuracy_index[index + 1]]
[docs] def get_magnetic_configuration(self, atol: float = 0.5, ztol: float = 0.05) -> AttributeDict: """Get the final magnetic configuration of a ``pw.x`` calculation.""" try: structure = self._node.outputs.output_structure except AttributeError: structure = self._node.inputs.structure try: magnetic_moments = self._node.outputs.output_trajectory.get_array('atomic_magnetic_moments')[-1].tolist() except KeyError as exc: raise KeyError('Could not find the `atomic_magnetic_moments` in the `output_trajectory`.') from exc results = create_magnetic_configuration( structure, magnetic_moments, atol=atol, ztol=ztol, metadata={'store_provenance': False}, ) structure_kindname_position = [(site.kind_name, site.position) for site in structure.sites] allo_kindname_position = [(site.kind_name, site.position) for site in results['structure'].sites] structure_kindnames_sorted = sorted(structure_kindname_position, key=lambda l: l[1]) allo_kindnames_sorted = sorted(allo_kindname_position, key=lambda l: l[1]) requires_new_kinds = not structure_kindnames_sorted == allo_kindnames_sorted non_magnetic = all(abs(magn) < ztol for magn in results['magnetic_moments'].get_dict().values()) if requires_new_kinds: results = create_magnetic_configuration( structure, magnetic_moments, atol=atol, ztol=ztol, metadata={'store_provenance': False}, ) structure = results['results'] return AttributeDict({ 'structure': structure, 'magnetic_moments': None if non_magnetic else results['magnetic_moments'] })