Source code for aiida_quantumespresso.utils.validation.trajectory
# -*- coding: utf-8 -*-
"""Utilities for the validation of `TrajectoryData` content."""
from typing import List, Optional
from aiida.orm import TrajectoryData
import numpy
[docs]def verify_convergence_trajectory(
trajectory: TrajectoryData,
index: int = -1,
threshold_forces: Optional[float] = None,
threshold_stress: Optional[float] = None,
reference_pressure: float = 0,
fixed_coords: Optional[List[List[bool]]] = None
) -> bool:
"""Verify that the data of the given ``TrajectoryData`` is converged with respect to the given thresholds.
Two are properties checked for convergence: forces and stress. If no threshold is specified for a property, the
check is skipped. If all thresholds are successfully met, `True` is returned. If at least one of them fails, `False`
is returned.
The stress is converged if the absolute difference of its corresponding pressure and the reference pressure is
within the given threshold.
For calculations with fixed coordinates using the ``fixed_coords`` setting, the fixed coordinates can be provided.
The forces that correspond to a fixed coordinate are ignored when comparing with the threshold.
:param trajectory: the `TrajectoryData`
:param index: the frame index of the trajectory data to check, default is `-1` meaning the last frame
:param threshold_forces: the force threshold in Ry / bohr
:param threshold_stress: the stress threshold in kbar
:param reference_pressure: reference pressure in kbar
:param fixed_coords: list of fixed coordinates in the calculation for each site
:return: `True` if all thresholds are valid, `False` otherwise
:raises ValueError: if any of the arrays or indices don't exist
"""
if threshold_forces is not None:
converged_forces = verify_convergence_forces(trajectory, index, threshold_forces, fixed_coords)
else:
converged_forces = True
if threshold_stress is not None:
converged_stress = verify_convergence_stress(trajectory, index, threshold_stress, reference_pressure)
else:
converged_stress = True
return converged_forces and converged_stress
[docs]def verify_convergence_forces(
trajectory: TrajectoryData,
index: int = -1,
threshold: Optional[float] = None,
fixed_coords: Optional[List[List[bool]]] = None
) -> bool:
"""Verify that the `forces` of the given `TrajectoryData` are converged with respect to given threshold.
For calculations with fixed coordinates using the ``fixed_coords`` setting, the fixed coordinates can be provided.
The forces that correspond to a fixed coordinate are ignored when comparing with the threshold.
:param trajectory: the `TrajectoryData`
:param index: the frame index of the trajectory data to check, default is `-1` meaning the last frame
:param threshold: the force threshold in Ry / bohr
:param fixed_coords: list of fixed coordinates in the calculation for each site
:return: `True` if threshold is valid, `False` otherwise
:raises ValueError: if the `forces` array or given index does not exist
"""
from qe_tools import CONSTANTS
if threshold is None:
return None
threshold *= CONSTANTS.ry_to_ev / CONSTANTS.bohr_to_ang # Convert to eV / Å
try:
abs_forces = numpy.abs(trajectory.get_array('forces')[index])
except (KeyError, IndexError) as exception:
raise ValueError('the `forces` array does not exist or the given index exceeds the length.') from exception
if fixed_coords is not None:
fixed_coords = numpy.array(fixed_coords)
# Set the forces corresponding to fixed coordinates to zero, as they should not be checked versus threshold
# Since `fixed_coords` is a list of lists of booleans, where `True` indicates that a coordinate is fixed, we can
# invert this array and multiply it with the forces to set all fixed coordinates to zero.
abs_forces *= numpy.invert(fixed_coords)
return numpy.all(abs_forces < threshold)
[docs]def verify_convergence_stress(
trajectory: TrajectoryData,
index: int = -1,
threshold: Optional[float] = None,
reference_pressure: float = 0,
) -> bool:
"""Verify that the `stress` of the given `TrajectoryData` are converged with respect to given threshold.
The stress is converged if the absolute difference of its corresponding pressure and the reference pressure is
within the given threshold.
:param trajectory: the `TrajectoryData`
:param index: the frame index of the trajectory data to check, default is `-1` meaning the last frame
:param threshold: the stress threshold in kbar
:param reference_pressure: reference pressure in kbar
:return: `True` if threshold is valid, `False` otherwise
:raises ValueError: if the `stress` array or given index does not exist
"""
if threshold is None:
return None
threshold /= 10. # Convert to GPa
reference_pressure /= 10. # Convert to GPa
try:
stress = trajectory.get_array('stress')[index]
except (KeyError, IndexError) as exception:
raise ValueError('the `stress` array does not exist or the given index exceeds the length.') from exception
pressure = numpy.trace(stress) / 3.
return abs(pressure - reference_pressure) < threshold