Source code for dgamore.dmft_interface

# SPDX-FileCopyrightText: 2025-2026 Julian Peil <julian.peil@tuwien.ac.at>
# SPDX-License-Identifier: MIT
#
# DGAmore — Multi-Orbital Ladder Dynamical Vertex Approximation (LDGA) &
#           Eliashberg Equation Solver for Strongly Correlated Electron Systems
"""
DMFT input interface. :class:`DMFTInterface` is the abstract contract for reading the quantities a DGA run needs
from a converged DMFT calculation (inverse temperature, chemical potential, fillings, interaction parameters,
the 1-particle Green's function/self-energy and the 2-particle Green's function). :class:`W2dynInterface`
implements it for w2dynamics HDF5 output (handling multiple inequivalent atoms); :class:`TriqsInterface` is a
placeholder. The 2-particle Green's function is expected in the w2dynamics frequency convention.
"""

import os
from abc import ABC

import h5py
import numpy as np

import dgamore.config as config
import dgamore.symmetrize_new as sym
from dgamore.greens_function import GreensFunction
from dgamore.local_four_point import LocalFourPoint
from dgamore.n_point_base import SpinChannel
from dgamore.self_energy import SelfEnergy


[docs] class DMFTInterface(ABC): """ Abstract interface for DMFT calculations. Reads the quantities needed for a DGA calculation from the output files. """
[docs] def get_beta(self) -> float: r""" Returns the inverse temperature from the DMFT calculation. :return: The inverse temperature :math:`\beta` from the DMFT calculation. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_mu(self) -> float: r""" Returns the chemical potential from the DMFT calculation. :return: The chemical potential :math:`\mu` from the DMFT calculation. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_nd(self, ineq: int = 1) -> int: """ Returns the number of interacting d-orbitals from the DMFT calculation. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The number of interacting d-orbitals. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_totdens(self) -> float: """ Returns the total electron density from the DMFT calculation. :return: The total electron density from the DMFT calculation. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_occ(self, ineq: int = 1) -> np.ndarray: """ Returns the orbital-resolved occupation from the DMFT calculation. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The orbital-resolved occupation. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_udd(self, ineq: int = 1) -> float: r""" Returns the density-density interaction :math:`U` for the interacting d-orbitals (used both for plain density-density and Kanamori interactions). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The interaction :math:`U_{dd}`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_jdd(self, ineq: int = 1) -> float: r""" Returns the Hund's coupling :math:`J` for the interacting d-orbitals (nonzero only for Kanamori interactions). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The Hund's coupling :math:`J_{dd}`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_vdd(self, ineq: int = 1) -> float: r""" Returns the inter-orbital repulsion :math:`V` (often :math:`U'`) for the interacting d-orbitals (nonzero only for Kanamori interactions). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The inter-orbital repulsion :math:`V_{dd}`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_dc(self, ineq: int = 1) -> float: """ Returns the double-counting correction for the self-energy from the DMFT calculation. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The double-counting correction for the self-energy. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_giw(self, ineq: int = 1) -> GreensFunction: """ Returns the one-particle Green's function from DMFT. It must be returned with an array of shape ``[nbands, nbands, 2*niv_dmft]``. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The local DMFT :class:`GreensFunction`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_siw(self, ineq: int = 1) -> SelfEnergy: """ Returns the one-particle self-energy from DMFT, already including the double-counting correction. It must be returned with an array of shape ``[1, 1, 1, nbands, nbands, 2*niv_dmft]``. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The local DMFT :class:`SelfEnergy`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] def get_g2iw(self, channel: SpinChannel, ineq: int = 1) -> LocalFourPoint: r""" Returns the two-particle Green's function from DMFT. The input must follow the w2dynamics frequency convention: :math:`\nu_1 = \nu`, :math:`\nu_2 = \nu-\omega`, :math:`\nu_3 = \nu'-\omega`, :math:`\nu_4 = \nu'`. :param channel: The spin channel of the two-particle quantity (density or magnetic). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The two-particle Green's function as a :class:`LocalFourPoint`. :raises NotImplementedError: In the abstract base class. """ raise NotImplementedError()
[docs] class W2dynInterface(DMFTInterface): """ Interface for w2dynamics output files. """ def __init__(self): """ Opens the w2dynamics 1- and 2-particle HDF5 files (from :mod:`dgamore.config`). """ self.file_1p = None self.file_2p = None self._open()
[docs] def get_beta(self) -> float: r""" Reads the inverse temperature from the w2dynamics config. :return: The inverse temperature :math:`\beta` from the DMFT calculation. """ return self.file_1p[".config"].attrs["general.beta"]
[docs] def get_mu(self, dmft_iter: str = "dmft-last") -> float: r""" Reads the chemical potential from the w2dynamics output. :param dmft_iter: The DMFT iteration to read from. :return: The chemical potential :math:`\mu`. """ return self.file_1p[dmft_iter + "/mu/value"][()]
[docs] def get_nd(self, ineq: int = 1) -> int: """ Reads the number of interacting d-orbitals from the w2dynamics config. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The number of interacting d-orbitals. """ return self._from_ineq_config("nd", ineq=ineq)
[docs] def get_totdens(self, dmft_iter: str = "dmft-last") -> float: """ Reads the total electron density from the w2dynamics config. :param dmft_iter: The DMFT iteration to read from. :return: The total electron density. """ return self.file_1p[".config"].attrs["general.totdens"]
[docs] def get_occ(self, ineq: int = 1, dmft_iter: str = "dmft-last") -> np.ndarray: """ Reads and spin-sums the orbital-resolved occupation from the w2dynamics output. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :param dmft_iter: The DMFT iteration to read from. :return: The orbital-resolved occupation (spin-summed). """ rho1 = self.file_1p[self._ineq_group(ineq, dmft_iter) + "/rho1/value"][()] return 2 * np.mean(rho1, axis=(1, 3))
[docs] def get_udd(self, ineq: int = 1) -> float: r""" Reads the density-density interaction from the w2dynamics config. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The density-density interaction :math:`U_{dd}` (used for plain and Kanamori interactions). """ return self._from_ineq_config("udd", ineq=ineq)
[docs] def get_jdd(self, ineq: int = 1) -> float: r""" Reads the Hund's coupling from the w2dynamics config. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The Hund's coupling :math:`J_{dd}` (nonzero only for Kanamori interactions). """ return self._from_ineq_config("jdd", ineq=ineq)
[docs] def get_vdd(self, ineq: int = 1) -> float: r""" Reads the inter-orbital repulsion from the w2dynamics config. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The inter-orbital repulsion :math:`V_{dd}` (often :math:`U'`; nonzero only for Kanamori interactions). """ return self._from_ineq_config("vdd", ineq=ineq)
[docs] def get_dc(self, ineq: int = 1, dmft_iter: str = "dmft-last") -> float: """ Reads the double-counting correction from the w2dynamics output. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :param dmft_iter: The DMFT iteration to read from. :return: The double-counting correction for the self-energy. """ return self.file_1p[self._ineq_group(ineq, dmft_iter) + "/dc/value"][()]
[docs] def get_giw(self, ineq: int = 1, dmft_iter: str = "dmft-last") -> GreensFunction: """ Returns the spin-averaged one-particle Green's function from DMFT, extended to a diagonal orbital matrix of shape ``[nbands, nbands, 2*niv_dmft]``. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :param dmft_iter: The DMFT iteration to read from. :return: The local DMFT :class:`GreensFunction`. """ giw = self.file_1p[self._ineq_group(ineq, dmft_iter) + "/giw/value"][()] # [band, spin, niv] giw = np.mean(giw, axis=1) # mean over spin return GreensFunction(self._extend_orbital(giw), nk=config.lattice.nk, beta=config.sys.beta)
[docs] def get_siw(self, ineq: int = 1, dmft_iter: str = "dmft-last") -> SelfEnergy: """ Returns the spin-averaged one-particle self-energy from DMFT, with the double-counting correction added, as an array of shape ``[1, 1, 1, nbands, nbands, 2*niv_dmft]``. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :param dmft_iter: The DMFT iteration to read from. :return: The local DMFT :class:`SelfEnergy` including the double-counting correction. """ siw = self.file_1p[self._ineq_group(ineq, dmft_iter) + "/siw/value"][()] # [band, spin, niv] siw = np.mean(siw, axis=1) # mean over spin siw = self._extend_orbital(siw)[None, None, None, ...] siw_dc = np.mean(self.get_dc(ineq, dmft_iter), axis=-1) # from [band, spin] to spin-mean siw_dc = self._extend_orbital(siw_dc)[None, None, None, ..., None] return SelfEnergy(siw, estimate_niv_core=True, beta=config.sys.beta) + siw_dc
[docs] def get_g2iw(self, channel: SpinChannel, ineq: int = 1) -> LocalFourPoint: """ Reads the two-particle Green's function from the w2dynamics 2-particle file, assembling the orbital/frequency array from the per-bosonic-frequency, per-orbital-component groups. :param channel: The spin channel of the two-particle quantity (density or magnetic). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The two-particle Green's function as a :class:`LocalFourPoint`. :raises ValueError: If ``channel`` is neither density nor magnetic. """ if channel not in (SpinChannel.DENS, SpinChannel.MAGN): raise ValueError( "The two-particle Green's function can only be retrieved for the density and magnetic spin channel." ) # the next lines determine the size of g2, i.e. niw and niv channel_group_string = f"/ineq-{ineq:03}/{channel.value}" niw_full = len(self.file_2p[channel_group_string].keys()) # 00000 is the first element. If it does not exist, there are no bosonic frequencies in the G2 and that would be weird first_index = int(next(iter(self.file_2p[f"{channel_group_string}/00000"]))) niv_full = len( self.file_2p[f"{channel_group_string}/00000/{first_index:05}/value"][()] ) # extract niv from the size of the array n_bands = self.get_nd(ineq) g2 = np.zeros((n_bands,) * 4 + (niw_full,) + 2 * (niv_full,), dtype=np.complex128) for wn in range(niw_full): wn_group_string = f"{channel_group_string}/{wn:05}" for ind in self.file_2p[wn_group_string].keys(): bands = sym.index2component_band(n_bands, 4, int(ind)) val = self.file_2p[f"{wn_group_string}/{ind}/value"][()].T g2[bands[0], bands[1], bands[2], bands[3], wn, ...] = val return LocalFourPoint(g2, channel)
def _extend_orbital(self, obj: np.ndarray) -> np.ndarray: """ Promotes a band-resolved array to a diagonal orbital matrix (values placed on the orbital diagonal). :param obj: The array whose leading axis is the band index. :return: The array with the band axis expanded to a diagonal ``[i, j, ...]`` orbital pair. """ return np.einsum("i...,ij->ij...", obj, np.eye(obj.shape[0])) def _ineq_group(self, ineq: int = 1, dmft_iter: str = "dmft-last") -> str: """ Builds the HDF5 group path for a given DMFT iteration and inequivalent atom. :param ineq: The index of the inequivalent atom (for multi-site DMFT). :param dmft_iter: The DMFT iteration to read from. :return: The HDF5 group path string. """ return dmft_iter + f"/ineq-{ineq:03}" def _from_ineq_config(self, key: str, ineq: int = 1): """ Reads an attribute from the ``.config`` group for a given inequivalent atom. :param key: The atom-config attribute name (without the ``atoms.<ineq>.`` prefix). :param ineq: The index of the inequivalent atom (for multi-site DMFT). :return: The attribute value. """ return self.file_1p[".config"].attrs[f"atoms.{ineq:1}.{key}"] def _open(self) -> None: """ Opens the w2dynamics 1- and 2-particle output files in read mode. :return: None. """ self.file_1p = h5py.File(os.path.join(config.dmft.input_path, config.dmft.fname_1p), "r") self.file_2p = h5py.File(os.path.join(config.dmft.input_path, config.dmft.fname_2p), "r") def _close(self) -> None: """ Closes the w2dynamics output files. :return: None. """ self.file_1p.close() self.file_2p.close() def __enter__(self): """ Context-manager entry; opens the w2dynamics output files (see :meth:`_open`). """ self._open() def __exit__(self, exc_type, exc_val, exc_tb): """ Context-manager exit; closes the w2dynamics output files (see :meth:`_close`). :param exc_type: Exception type if one was raised in the ``with`` block, else None. :param exc_val: Exception instance if one was raised, else None. :param exc_tb: Traceback if an exception was raised, else None. """ self._close() def __del__(self): """ Destructor; closes the w2dynamics output files (see :meth:`_close`). """ self._close()
[docs] class TriqsInterface(DMFTInterface): """ Interface for TRIQS output files. """ def __init__(self): """ Placeholder constructor for the (not yet implemented) TRIQS interface. :raises NotImplementedError: The TRIQS interface is not yet implemented. """ raise NotImplementedError()
if __name__ == "__main__": string = "test" if string == "w2dyn": interface = W2dynInterface() elif string == "triqs": interface = TriqsInterface() else: raise ValueError("Unknown interface.") g = interface.get_giw()