Source code for aiida_quantumespresso.data.hubbard_structure

# -*- coding: utf-8 -*-
"""Data plugin that represents a crystal structure with Hubbard parameters."""
import json
from typing import List, Tuple, Union

from aiida.orm import StructureData
import numpy as np
from pymatgen.core import Lattice, PeriodicSite

from aiida_quantumespresso.common.hubbard import Hubbard, HubbardParameters

__all__ = ('HubbardStructureData',)


[docs]class HubbardStructureData(StructureData): """Structure data containing code agnostic info on Hubbard parameters."""
[docs] _hubbard_filename = 'hubbard.json'
def __init__( self, cell: List[List[float]], sites: List[Tuple[str, str, Tuple[float, float, float]]], pbc: Tuple[bool, bool, bool] = (True, True, True), hubbard: Hubbard = None, **kwargs, ): """Set a ``HubbardStructureData`` instance. :param cell: (3,3) shape list of floats :param pbc: (3,) shape list in bools :param sites: list of lists, each of the shape [symbol, name, position], where position is a (3,) shape list of floats :param hubbard: a :class:`~aiida_quantumespresso.common.hubbard.Hubbard` istance """ super().__init__(cell=cell, **kwargs) self.sites = sites self.pbc = pbc self.hubbard = Hubbard(parameters=[]) if hubbard is None else hubbard @property
[docs] def sites(self): """Return the :attr:`~aiida.orm.nodes.data.structure.StructureData.sites`.""" return super().sites
@sites.setter def sites(self, values: List[Tuple[str, str, Tuple[float, float, float]]]): """Set the :attr:`~aiida.orm.nodes.data.structure.StructureData.sites`. :param values: list of sites, each as [symbol, name, (3,) shape list of positions] """ self.clear_sites() for simbols, kind_name, position in values: self.append_atom(symbols=simbols, name=kind_name, position=position) @property
[docs] def hubbard(self) -> Hubbard: """Get the `Hubbard` instance. :returns: a :class:`~aiida_quantumespresso.common.hubbard.Hubbard` instance. """ # pylint: disable=not-context-manager with self.base.repository.open(self._hubbard_filename, mode='rb') as handle: return Hubbard.model_validate_json(json.load(handle))
@hubbard.setter def hubbard(self, hubbard: Hubbard): """Set the full Hubbard information.""" if not isinstance(hubbard, Hubbard): raise ValueError('the input is not of type `Hubbard`') serialized = json.dumps(hubbard.model_dump_json()) self.base.repository.put_object_from_bytes(serialized.encode('utf-8'), self._hubbard_filename) @staticmethod
[docs] def from_structure( structure: StructureData, hubbard: Union[Hubbard, None] = None, ): """Return an instance of ``HubbardStructureData`` from a ``StructureData`` node. :param structure: :class:`aiida.orm.StructureData` instance :param hubbard: :class:`~aiida_quantumespresso.common.hubbard.Hubbard` instance :returns: ``HubbardStructureData`` instance """ sites = [[structure.get_kind(site.kind_name).symbol, site.kind_name, site.position] for site in structure.sites] cell = structure.cell pbc = structure.pbc return HubbardStructureData(cell=cell, pbc=pbc, sites=sites, hubbard=hubbard)
[docs] def append_hubbard_parameter( self, atom_index: int, atom_manifold: str, neighbour_index: int, neighbour_manifold: str, value: float, translation: Tuple[int, int, int] = None, hubbard_type: str = 'Ueff', ): """Append a :class:`~aiida_quantumespresso.common.hubbard.HubbardParameters`. :param atom_index: atom index in unitcell :param atom_manifold: atomic manifold (e.g. 3d, 3d-2p) :param neighbour_index: neighbouring atom index in unitcell :param neighbour_manifold: neighbour manifold (e.g. 3d, 3d-2p) :param value: value of the Hubbard parameter, in eV :param translation: (3,) list of ints, describing the translation vector associated with the neighbour atom, defaults to None :param hubbard_type: hubbard type (U, V, J, ...), defaults to 'Ueff' (see :class:`~aiida_quantumespresso.common.hubbard.Hubbard` for full allowed values) """ sites = [ PeriodicSite( species=site.species, coords=site.coords, lattice=Lattice(self.cell, pbc=self.pbc), coords_are_cartesian=True ) for site in self.get_pymatgen().sites ] if any((atom_index > len(sites) - 1, neighbour_index > len(sites) - 1)): raise ValueError( 'atom_index and neighbour_index must be within the range of the number of sites in the structure' ) if translation is None: _, translation = sites[atom_index].distance_and_image(sites[neighbour_index]) translation = np.array(translation, dtype=np.int64).tolist() hp_tuple = (atom_index, atom_manifold, neighbour_index, neighbour_manifold, value, translation, hubbard_type) parameters = HubbardParameters.from_tuple(hp_tuple) hubbard = self.hubbard if parameters not in hubbard.parameters: hubbard.parameters.append(parameters) self.hubbard = hubbard
[docs] def pop_hubbard_parameters(self, index: int): """Pop Hubbard parameters in the list. :param index: index of the Hubbard parameters to pop """ hubbard = self.hubbard hubbard.parameters.pop(index) self.hubbard = hubbard
[docs] def clear_hubbard_parameters(self): """Clear all the Hubbard parameters.""" hubbard = self.hubbard hubbard.parameters = [] self.hubbard = hubbard
[docs] def initialize_intersites_hubbard( self, atom_name: str, atom_manifold: str, neighbour_name: str, neighbour_manifold: str, value: float = 1e-8, hubbard_type: str = 'V', use_kinds: bool = True, ): """Initialize and append intersite Hubbard values between an atom and its neighbour(s). .. note:: this only initialize the value between the first neighbour. In case `use_kinds` is False, all the possible combination of couples having kind name equal to symbol are initialized. :param atom_name: atom name in unitcell :param atom_manifold: atomic manifold (e.g. 3d, 3d-2p) :param neighbour_index: neighbouring atom name in unitcell :param neighbour_manifold: neighbour manifold (e.g. 3d, 3d-2p) :param value: value of the Hubbard parameter, in eV :param hubbard_type: hubbard type (U, V, J, ...), defaults to 'V' (see :class:`~aiida_quantumespresso.common.hubbard.Hubbard` for full allowed values) :param use_kinds: whether to use kinds for initializing the parameters; when False, it initializes all the ``Kinds`` matching the ``atom_name`` """ sites = self.get_pymatgen_structure().sites function = self._get_one_kind_index if use_kinds else self._get_symbol_indices atom_indices = function(atom_name) neigh_indices = function(neighbour_name) if atom_indices is None or neigh_indices is None: raise ValueError('species or kind names not in structure') for atom_index in atom_indices: for neighbour_index in neigh_indices: _, translation = sites[atom_index].distance_and_image(sites[neighbour_index]) translation = np.array(translation, dtype=np.int64).tolist() args = ( atom_index, atom_manifold, neighbour_index, neighbour_manifold, value, translation, hubbard_type ) self.append_hubbard_parameter(*args)
[docs] def initialize_onsites_hubbard( self, atom_name: str, atom_manifold: str, value: float = 1e-8, hubbard_type: str = 'Ueff', use_kinds: bool = True, ): """Initialize and append onsite Hubbard values of atoms with specific name. :param atom_name: atom name in unitcell :param atom_manifold: atomic manifold (e.g. 3d, 3d-2p) :param value: value of the Hubbard parameter, in eV :param hubbard_type: hubbard type (U, J, ...), defaults to 'Ueff' (see :class:`~aiida_quantumespresso.common.hubbard.Hubbard` for full allowed values) :param use_kinds: whether to use kinds for initializing the parameters; when False, it initializes all the ``Kinds`` matching the ``atom_name`` """ function = self._get_one_kind_index if use_kinds else self._get_symbol_indices atom_indices = function(atom_name) if atom_indices is None: raise ValueError('species or kind names not in structure') for atom_index in atom_indices: args = (atom_index, atom_manifold, atom_index, atom_manifold, value, [0, 0, 0], hubbard_type) self.append_hubbard_parameter(*args)
[docs] def _get_one_kind_index(self, kind_name: str) -> List[int]: """Return the first site index matching with `kind_name`.""" for i, site in enumerate(self.sites): if site.kind_name == kind_name: return [i]
[docs] def _get_symbol_indices(self, symbol: str) -> List[int]: """Return one site index for each kind name matching symbol.""" site_kindnames = self.get_site_kindnames() matching_kinds = [kind.name for kind in self.kinds if symbol in kind.symbol] return [site_kindnames.index(kind) for kind in matching_kinds]