Source code for aiida_phonopy.data.force_constants
# -*- coding: utf-8 -*-
"""Module defining the class for force constants data."""
from __future__ import annotations
from typing import Union
from aiida.orm import StructureData
import numpy as np
from phonopy.structure.cells import PhonopyAtoms
from .raw import RawData
[docs]class ForceConstantsData(RawData): # pylint: disable=too-many-ancestors
"""Self-contained class for force constants data and non-analytical constants.
It stores also the structure information (unitcell, supercell, ...), for a
complete transferable data type.
"""
def __init__(
self,
structure: Union[StructureData, None] = None,
phonopy_atoms: Union[PhonopyAtoms, None] = None,
supercell_matrix: list | None = None,
primitive_matrix: list | None = None,
symprec: float = 1e-05,
is_symmetry: bool = True,
distinguish_kinds: bool = True,
**kwargs
):
"""Instantiate the class.
The minimal input is to define either the `structure` or the `phonopy_atoms` input.
They cannot be specified at the same time.
:param structure: a :class:`~aiida.orm.StructureData` node
:param phononpy_atoms: a :class:`~phonopy.structure.cells.PhonopyAtoms` instance
:param supercell_matrix: a (3,3) shape array describing the supercell transformation
:param primitive_matrix: a (3,3) shape array describing the primite transformation
:param symprec: precision tollerance for symmetry analysis
:param is_symmetry: whether using symmetries
:distinguish_kinds: it stores a mapping between kinds and chemical symbols;
by default Phonopy does not support kind,
thus useful if in the input `structure` kinds are defined
:paramm kwargs: for internal use
"""
kwargs['structure'] = structure
kwargs['phonopy_atoms'] = phonopy_atoms
kwargs['supercell_matrix'] = supercell_matrix
kwargs['primitive_matrix'] = primitive_matrix
kwargs['symprec'] = symprec
kwargs['is_symmetry'] = is_symmetry
kwargs['distinguish_kinds'] = distinguish_kinds
super().__init__(**kwargs)
[docs] def get_phonopy_instance(self, **kwargs):
"""Return a :class:`~phonopy.Phonopy` object with force and nac parameters (if set).
:param kwargs: see :func:`aiida_phonopy.data.preprocess.PreProcessData.get_phonopy_instance`
* symmetrize_nac: whether or not to symmetrize the nac parameters
using point group symmetry; bool, defaults to self.is_symmetry
* factor_nac: factor for non-analytical corrections;
float, defaults to Hartree*Bohr
"""
ph_instance = super().get_phonopy_instance(**kwargs)
if self.force_constants is not None:
ph_instance.force_constants = self.force_constants
return ph_instance
@property
[docs] def force_constants(self) -> np.ndarray:
"""Get force force constants matrix."""
try:
the_forces = self.get_array('force_constants')
except KeyError:
the_forces = None
return the_forces
[docs] def set_force_constants(self, force_constants: list | np.ndarray):
"""Set force constants matrix.
:param force_constants: array of force constants matrix in compact or full format
:param type: (n_patom = atoms in primitive cell, n_satom = atoms in supercell)
* Compact format: (n_patom, n_satom, 3, 3)
* Full format: (n_satom, n_satom, 3, 3)
:raises:
* TypeError: if the format is not of the correct type
* ValueError: if the format is not compatible
* RuntimeError: if the displacement dataset was not initialize in input
"""
self._if_can_modify()
if not isinstance(force_constants, (list, np.ndarray)):
raise TypeError('the input is not of the correct type')
n_satoms = len(self.get_supercell().sites)
n_patoms = len(self.get_primitive_cell().sites)
fc = np.array(force_constants)
if fc.shape in [(n_patoms, n_satoms, 3, 3), (n_satoms, n_satoms, 3, 3)]:
self.set_array('force_constants', fc)
else:
raise ValueError('the array is not of the correct shape')