Source code for fireballpy.outputs.orbitals

from __future__ import annotations
import numpy as np
from numpy.typing import NDArray

from fireballpy._errors import type_check


[docs] class OrbitalVector: """Object to manipulate the Eigenvectors in a computation. This object adds the ability to select atoms and their orbitals with 2 index expressions. When accessing elements of the matrix one may use a ``[iatom, iorb]`` syntax. * ``iatom``: Refers to the index/indices of the atom. * ``iorb``: Refers to the orbital index/indices of the atom. Following :class:`np.ndarray` indexing, it is also valid to use expressions as ``[iatom]``, ``[iatom, iorb]``, where the absence of an index is assumed as a full selection. Slicing is supported in both the orbitals and atoms (when orbitals are not specified). Parameters ---------- vector : NDArray Matrix where each row/column represents an orbital. orbitals : NDArray[int] Array with the number of orbitals each atom has. Attributes ---------- vector : NDArray Matrix where each row/column represents an orbital. orbitals : NDArray[int] Array with the number of orbitals each atom has. """ def __init__(self, *, vector: NDArray, orbitals: NDArray[np.int64]) -> None: type_check(vector, np.ndarray, 'NDArray') type_check(orbitals, np.ndarray, 'NDArray') self.vector = np.ascontiguousarray(vector, dtype=vector.dtype) if len(self.vector.shape) != 1: raise ValueError("Parameter ``vector`` is not a vector.") self.orbitals = np.ascontiguousarray(orbitals, dtype=np.int64) # Compute the slices for the different atoms self.slices = [slice(0, self.orbitals[0])] for i in range(1, self.orbitals.size): start = self.slices[-1].stop self.slices.append(slice(start, start + self.orbitals[i])) def __repr__(self) -> str: return self.vector.__repr__() def __str__(self) -> str: return self.vector.__str__() def __getitem__(self, key): # Only one index provided if not hasattr(key, '__len__'): key0 = [] sls = self.slices[key] if isinstance(sls, slice): sls = [sls] for sl in sls: start = sl.start if sl.start else 0 stop = sl.stop if sl.stop else self.vector.size step = sl.step if sl.step else 1 key0.extend([i for i in range(start, stop, step)]) return self.vector[key0] # Handle error if len(key) > 2: raise KeyError("Cannot interpret 3 or more indices.") # Atoms and orbitals if isinstance(key[0], slice): raise KeyError("Slice atom selection with orbital selection not supported. " "To access concrete vector elements use the attribute ``vector``.") start0 = self.slices[key[0]].start if isinstance(key[1], slice): beg0 = key[1].start if key[1].start else 0 end0 = key[1].stop if key[1].stop else self.orbitals[key[0]] key0 = slice(start0 + beg0, start0 + end0, key[1].step) else: key0 = start0 + key[1] return self.vector[key0]
[docs] class OrbitalMatrix: """Object to manipulate the Hamiltonian and Overlap matrix in a computation. This object adds the ability to select atoms and their orbitals with 4 index expressions. When accessing elements of the matrix one may use a ``[iatom, jatom, iorb, jorb]`` syntax. * ``iatom``: Refers to the index/indices of the first atom (row). * ``jatom``: Refers to the index/indices of the second atom (column). * ``iorb``: Refers to the orbital index/indices of the first atom. * ``jorb``: Refers to the orbital index/indices of the second atom. Following :class:`np.ndarray` indexing, it is also valid to use expressions as ``[iatom]``, ``[iatom, jatom]``, ``[iatom, jatom, iorb]``, where the absence of an index is assumed as a full selection. Slicing is supported in both the orbitals and atoms (when orbitals are not specified). Parameters ---------- matrix : NDArray Matrix where each row/column represents an orbital. orbitals : NDArray[int] Array with the number of orbitals each atom has. Attributes ---------- matrix : NDArray Matrix where each row/column represents an orbital. orbitals : NDArray[int] Array with the number of orbitals each atom has. """ def __init__(self, *, matrix: NDArray, orbitals: NDArray[np.int64]) -> None: type_check(matrix, np.ndarray, 'NDArray') type_check(orbitals, np.ndarray, 'NDArray') self.matrix = np.ascontiguousarray(matrix, dtype=matrix.dtype) if len(self.matrix.shape) != 2 or (matrix.shape[0] != matrix.shape[1]): raise ValueError("Parameter ``matrix`` is not a matrix.") self.orbitals = np.ascontiguousarray(orbitals, dtype=np.int64) # Compute the slices for the different atoms self.slices = [slice(0, self.orbitals[0])] for i in range(1, self.orbitals.size): start = self.slices[-1].stop self.slices.append(slice(start, start + self.orbitals[i])) def __repr__(self) -> str: return self.matrix.__repr__() def __str__(self) -> str: return self.matrix.__str__() def __getitem__(self, key): # Only one index provided if not hasattr(key, '__len__'): key0 = [] sls = self.slices[key] if isinstance(sls, slice): sls = [sls] for sl in sls: start = sl.start if sl.start else 0 stop = sl.stop if sl.stop else self.matrix.shape[0] step = sl.step if sl.step else 1 key0.extend([i for i in range(start, stop, step)]) return self.matrix[key0, :] # Handle error if len(key) > 4: raise KeyError("Cannot interpret 5 or more indices.") # Only atoms if len(key) == 2: key0 = [] sls = self.slices[key[0]] if isinstance(sls, slice): sls = [sls] for sl in sls: start = sl.start if sl.start else 0 stop = sl.stop if sl.stop else self.matrix.shape[0] step = sl.step if sl.step else 1 key0.extend([[i] for i in range(start, stop, step)]) key1 = [] sls = self.slices[key[1]] if isinstance(sls, slice): sls = [sls] for sl in sls: start = sl.start if sl.start else 0 stop = sl.stop if sl.stop else self.matrix.shape[0] step = sl.step if sl.step else 1 key1.extend([i for i in range(start, stop, step)]) return self.matrix[key0, key1] # Atoms and orbitals if isinstance(key[0], slice) or isinstance(key[1], slice): raise KeyError("Slice atom selection with orbital selection not supported. " "To access concrete matrix elements use the attribute ``matrix``.") start0 = self.slices[key[0]].start start1 = self.slices[key[1]].start if isinstance(key[2], slice): beg0 = key[2].start if key[2].start else 0 end0 = key[2].stop if key[2].stop else self.orbitals[key[0]] step0 = key[2].step if key[2].step else 1 key0 = slice(start0 + beg0, start0 + end0, step0) else: key0 = start0 + key[2] if len(key) == 4: if isinstance(key[3], slice): beg1 = key[3].start if key[3].start else 0 end1 = key[3].stop if key[3].stop else self.orbitals[key[1]] step1 = key[3].step if key[3].step else 1 key1 = slice(start1 + beg1, start1 + end1, step1) else: key1 = start1 + key[3] else: key1 = self.slices[key[1]] return self.matrix[key0, key1]