Source code for dmft_tools.interaction_hamiltonian

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
################################################################################
#
# solid_dmft - A versatile python wrapper to perform DFT+DMFT calculations
#              utilizing the TRIQS software library
#
# Copyright (C) 2018-2020, ETH Zurich
# Copyright (C) 2021, The Simons Foundation
#      authors: A. Hampel, M. Merkel, and S. Beck
#
# solid_dmft is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# solid_dmft is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.

# You should have received a copy of the GNU General Public License along with
# solid_dmft (in the file COPYING.txt in this directory). If not, see
# <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Contains all functions related to constructing the interaction Hamiltonian.
"""

# system
import os
import numpy as np
from itertools import product

# triqs
from h5 import HDFArchive
import triqs.utility.mpi as mpi
from triqs.gf import make_gf_imfreq
from triqs.operators import util, n, c, c_dag, Operator
from solid_dmft.dmft_tools import common


try:
    import forktps as ftps
except ImportError:
    pass

def _load_crpa_interaction_matrix(sum_k, general_params, gw_params, filename='UIJKL'):
    """
    Loads  dynamic interaction data to use as an interaction Hamiltonian.
    """
    def _round_to_int(data):
        return (np.array(data) + .5).astype(int)

    if gw_params['code'] == 'Vasp':
    # Loads data from VASP cRPA file
        print('Loading Vasp cRPA matrix from file: '+str(filename))
        data = np.loadtxt(filename, unpack=True)
        u_matrix_four_indices = np.zeros(_round_to_int(np.max(data[:4], axis=1)), dtype=complex)
        for entry in data.T:
            # VASP switches the order of the indices, ijkl -> ikjl
            i, k, j, l = _round_to_int(entry[:4])-1
            u_matrix_four_indices[i, j, k, l] = entry[4] + 1j * entry[5]

        # Slices up the four index U-matrix, separating shells
        u_matrix_four_indices_per_shell = [None] * sum_k.n_inequiv_shells
        first_index_shell = 0
        for ish in range(sum_k.n_corr_shells):
            icrsh = sum_k.corr_to_inequiv[ish]
            n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']
            u_matrix_temp = u_matrix_four_indices[first_index_shell:first_index_shell+n_orb,
                                                  first_index_shell:first_index_shell+n_orb,
                                                  first_index_shell:first_index_shell+n_orb,
                                                  first_index_shell:first_index_shell+n_orb]
            # I think for now we should stick with real interactions make real
            u_matrix_temp.imag = 0.0

            if ish == icrsh:
                u_matrix_four_indices_per_shell[icrsh] = u_matrix_temp
            elif not np.allclose(u_matrix_four_indices_per_shell[icrsh], u_matrix_temp, atol=1e-6, rtol=0):
                # TODO: for some reason, some entries in the matrices differ by a sign. Check that
                # mpi.report(np.allclose(np.abs(u_matrix_four_indices_per_shell[icrsh]), np.abs(u_matrix_temp),
                # atol=1e-6, rtol=0))
                mpi.report('Warning: cRPA matrix for impurity {} '.format(icrsh)
                           + 'differs for shells {} and {}'.format(sum_k.inequiv_to_corr[icrsh], ish))

            first_index_shell += n_orb

        if not np.allclose(u_matrix_four_indices.shape, first_index_shell):
            print('Warning: different number of orbitals in cRPA matrix than in calculation.')

    elif gw_params['code'] == 'aimbes':
        from solid_dmft.gw_embedding.bdft_converter import convert_gw_output
        u_matrix_four_indices_per_shell = []
        # lad GW input from h5 file
        if mpi.is_master_node():
            if 'Uloc_dlr' not in gw_params:
                gw_data, ir_kernel = convert_gw_output(
                    general_params['jobname'] + '/' + general_params['seedname'] + '.h5',
                    gw_params['h5_file'],
                    it_1e = gw_params['it_1e'],
                    it_2e = gw_params['it_2e'],
                    ha_ev_conv = True
                )
                gw_params.update(gw_data)
            for icrsh in range(sum_k.n_inequiv_shells):
                # for now we assume that up / down are equal
                if general_params['h_int_type'][icrsh] in  ('crpa', 'crpa_density_density'):
                    Uloc_0 = make_gf_imfreq(gw_params['Uloc_dlr'][icrsh]['up'],1)
                    u_matrix_four_indices_per_shell.append(Uloc_0.data[0,:,:,:,:] + gw_params['Vloc'][icrsh]['up'])
                else:
                    u_matrix_four_indices_per_shell.append(gw_params['Vloc'][icrsh]['up'])

                u_matrix_four_indices_per_shell[icrsh] = u_matrix_four_indices_per_shell[icrsh]
        mpi.barrier()
        u_matrix_four_indices_per_shell = mpi.bcast(u_matrix_four_indices_per_shell)
        gw_params = mpi.bcast(gw_params)
    else:
        raise ValueError('Unknown code for reading cRPA results: {}'.format(gw_params['code']))

    return u_matrix_four_indices_per_shell, gw_params


# def _adapt_U_2index_for_SO(Umat, Upmat):
#     """
#     Changes the two-index U matrices such that for a system consisting of a
#     single block 'ud' with the entries (1, up), (1, down), (2, up), (2, down),
#     ... the matrices are consistent with the case without spin-orbit coupling.

#     Parameters
#     ----------
#     Umat : numpy array
#         The two-index interaction matrix for parallel spins without SO.
#     Upmat : numpy array
#         The two-index interaction matrix for antiparallel spins without SO.

#     Returns
#     -------
#     Umat_SO : numpy array
#         The two-index interaction matrix for parallel spins. Because in SO all
#         entries have nominal spin 'ud', this matrix now contains the original
#         Umat and Upmat.
#     Upmat_SO : numpy array
#         The two-index interaction matrix for antiparallel spins. Unused because
#         in SO, all spins have the same nominal spin 'ud'.
#     """

#     Umat_SO = np.zeros(np.array(Umat.shape)*2, dtype=Umat.dtype)
#     Umat_SO[::2, ::2] = Umat_SO[1::2, 1::2] = Umat
#     Umat_SO[::2, 1::2] = Umat_SO[1::2, ::2] = Upmat
#     Upmat_SO = None

#     return Umat_SO, Upmat_SO


def _adapt_U_4index_for_SO(Umat_full):
    """
    Changes the four-index U matrix such that for a system consisting of a
    single block 'ud' with the entries (1, up), (1, down), (2, up), (2, down),
    ... the matrix is consistent with the case without spin-orbit coupling.
    This can be derived directly from the definition of the Slater Hamiltonian.

    Parameters
    ----------
    Umat_full : numpy array
       The four-index interaction matrix without SO.

    Returns
    -------
    Umat_full_SO : numpy array
        The four-index interaction matrix with SO. For a matrix U_ijkl, the
        indices i, k correspond to spin sigma, and indices j, l to sigma'.
    """

    Umat_full_SO = np.zeros(np.array(Umat_full.shape)*2, dtype=Umat_full.dtype)
    for spin, spin_prime in ((0, 0), (0, 1), (1, 0), (1, 1)):
        Umat_full_SO[spin::2, spin_prime::2, spin::2, spin_prime::2] = Umat_full

    return Umat_full_SO


def _construct_kanamori(sum_k, general_params, solver_type_per_imp, icrsh, den_den=False):
    """
    Constructs the Kanamori interaction Hamiltonian. Only Kanamori does not
    need the full four-index matrix. Therefore, we can construct it directly
    from the parameters U and J.
    """

    n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']
    if sum_k.SO == 1:
        assert n_orb % 2 == 0
        n_orb = n_orb // 2

    if n_orb not in (2, 3):
        mpi.report('warning: are you sure you want to use the kanamori hamiltonian '
                   + 'outside the t2g or eg manifold?')

    # check if Uprime has been specified manually
    if general_params['U_prime'][icrsh] is None:
        U_prime = general_params['U'][icrsh] - 2.0 * general_params['J'][icrsh]
    else:
        U_prime = general_params['U_prime'][icrsh]
    mpi.report('U = {:.2f}, U\' = {:.2f}, J = {:.2f}\n'.format(general_params['U'][icrsh], U_prime, general_params['J'][icrsh]))

    if solver_type_per_imp[icrsh] == 'ftps':
        # 1-band modell requires J and U' equals zero
        if n_orb == 1:
            up, j = 0.0, 0.0
        else:
            up = U_prime
            j = general_params['J'][icrsh]
        h_int = ftps.solver_core.HInt(u=general_params['U'][icrsh], j=j, up=up, dd=den_den)
    elif sum_k.SO == 0:
        # Constructs U matrix
        Umat, Upmat = util.U_matrix_kanamori(n_orb=n_orb, U_int=general_params['U'][icrsh],
                                             J_hund=general_params['J'][icrsh],
                                             Up_int=U_prime)
        if den_den:
            h_int = util.h_int_density(sum_k.spin_block_names[sum_k.SO], n_orb,
                                       map_operator_structure=sum_k.sumk_to_solver[icrsh],
                                       U=Umat, Uprime=Upmat,
                                       H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))
        else:
            h_int = util.h_int_kanamori(sum_k.spin_block_names[sum_k.SO], n_orb,
                                        map_operator_structure=sum_k.sumk_to_solver[icrsh],
                                        U=Umat, Uprime=Upmat, J_hund=general_params['J'][icrsh],
                                        H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))
    else:
        h_int = _construct_kanamori_soc(general_params['U'][icrsh], general_params['J'][icrsh],
                                        n_orb, sum_k.sumk_to_solver[icrsh],
                                        os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))
    return h_int


def _construct_kanamori_soc(U_int, J_hund, n_orb, map_operator_structure, H_dump=None):
    r"""
    Adapted from triqs.operators.util.hamiltonians.h_int_kanamori. Assumes
    that spin_names == ['ud'] and that map_operator_structure is given.
    """

    orb_names = list(range(n_orb))

    if H_dump:
        H_dump_file = open(H_dump, 'w')
        H_dump_file.write("Kanamori Hamiltonian:" + '\n')

    H = Operator()
    mkind = util.op_struct.get_mkind(None, map_operator_structure)

    s = 'ud'

    # density terms:
    # TODO: reformulate in terms of Umat and Upmat for consistency with triqs?
    if H_dump:
        H_dump_file.write("Density-density terms:" + '\n')
    for a1, a2 in product(orb_names, orb_names):
        if a1 == a2:  # same spin and orbital
            continue

        if a1 // 2 == a2 // 2:  # same orbital (, different spins)
            U_val = U_int
        elif a1 % 2 != a2 % 2:  # different spins (, different orbitals)
            U_val = U_int - 2*J_hund
        else:  # same spins (, different orbitals)
            U_val = U_int - 3*J_hund

        H_term = 0.5 * U_val * n(*mkind(s, a1)) * n(*mkind(s, a2))
        H += H_term

        # Dump terms of H
        if H_dump and not H_term.is_zero():
            H_dump_file.write('%s' % (mkind(s, a1), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a2), ) + '\t')
            H_dump_file.write(str(U_val) + '\n')

    # spin-flip terms:
    if H_dump:
        H_dump_file.write("Spin-flip terms:" + '\n')
    for a1, a2, a3, a4 in product(orb_names, orb_names, orb_names, orb_names):
        if a1 == a2 or a1 == a3 or a1 == a4 or a2 == a3 or a2 == a4 or a3 == a4:
            continue

        if not (a1//2 == a2//2 and a3//2 == a4//2 and a1//2 != a3//2 and a1 % 2 != a3 % 2):
            continue

        H_term = -0.5 * J_hund * c_dag(*mkind(s, a1)) * c(*mkind(s, a2)) * c_dag(*mkind(s, a3)) * c(*mkind(s, a4))
        H += H_term

        # Dump terms of H
        if H_dump and not H_term.is_zero():
            H_dump_file.write('%s' % (mkind(s, a1), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a2), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a3), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a4), ) + '\t')
            H_dump_file.write(str(-J_hund) + '\n')

    # pair-hopping terms:
    if H_dump:
        H_dump_file.write("Pair-hopping terms:" + '\n')
    for a1, a2, a3, a4 in product(orb_names, orb_names, orb_names, orb_names):
        if a1 == a2 or a1 == a3 or a1 == a4 or a2 == a3 or a2 == a4 or a3 == a4:
            continue

        if not (a1//2 == a2//2 and a3//2 == a4//2 and a1//2 != a3//2 and a1 % 2 != a3 % 2):
            continue

        H_term = 0.5 * J_hund * c_dag(*mkind(s, a1)) * c_dag(*mkind(s, a2)) * c(*mkind(s, a4)) * c(*mkind(s, a3))
        H += H_term

        # Dump terms of H
        if H_dump and not H_term.is_zero():
            H_dump_file.write('%s' % (mkind(s, a1), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a2), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a3), ) + '\t')
            H_dump_file.write('%s' % (mkind(s, a4), ) + '\t')
            H_dump_file.write(str(-J_hund) + '\n')

    return H


def _construct_dynamic(sum_k, general_params, icrsh):
    """
    Constructs the interaction Hamiltonian for a frequency-dependent interaction.
    Works only without spin-orbit coupling and only for one orbital.
    """

    mpi.report('###### Dynamic U calculation ######, load parameters from input archive.')
    U_onsite = None
    if mpi.is_master_node():
        with HDFArchive(general_params['jobname']+'/'+general_params['seedname']+'.h5', 'r') as ar:
            U_onsite = ar['dynamic_U']['U_scr']
    U_onsite = mpi.bcast(U_onsite)

    n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']
    if sum_k.SO == 1:
        raise ValueError('dynamic U not implemented for SO!=0')
    if n_orb > 1:
        raise ValueError('dynamic U not implemented for more than one orbital')

    mpi.report('onsite interaction value for imp {}: {:.3f}'.format(icrsh, U_onsite[icrsh]))
    h_int = util.h_int_density(sum_k.spin_block_names[sum_k.SO], n_orb,
                               map_operator_structure=sum_k.sumk_to_solver[icrsh],
                               U=np.array([[0]]), Uprime=np.array([[U_onsite[icrsh]]]), H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))

    return h_int


def _generate_four_index_u_matrix(sum_k, general_params, icrsh):
    """
    Generates the four-index interaction matrix per impurity with the interaction
    parameters U and J (and ratio_F4_F2 for the d shell).
    """

    # ish points to the shell representative of the current group
    ish = sum_k.inequiv_to_corr[icrsh]
    n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']
    if sum_k.SO == 1:
        assert n_orb % 2 == 0
        n_orb = n_orb // 2

    l = sum_k.corr_shells[ish]['l']
    if l != 2:
        slater_integrals = util.U_J_to_radial_integrals(l=l, U_int=general_params['U'][icrsh],
                                                        J_hund=general_params['J'][icrsh])
    else:
        # Implements parameter R=F4/F2. For R=0.63 equivalent to util.U_J_to_radial_integrals
        U = general_params['U'][icrsh]
        J = general_params['J'][icrsh]
        R = general_params['ratio_F4_F2'][icrsh]
        R = 0.63 if R is None else R
        slater_integrals = np.array([U, 14*J/(1+R), 14*J*R/(1+R)])

    mpi.report('\nImpurity {}: The corresponding slater integrals are'.format(icrsh))
    formatted_slater_integrals = [y for x in list(zip([2*x for x in range(len(slater_integrals))], slater_integrals)) for y in x]
    mpi.report(('F{:d} = {:.2f}, '*len(slater_integrals)).format(*formatted_slater_integrals))

    # Constructs U matrix
    # construct full spherical symmetric U matrix and transform to cubic basis
    # the order for the cubic orbitals is given by the convention. The TRIQS
    # convention is as follows ("xy","yz","z^2","xz","x^2-y^2")
    # this is consistent with the order of orbitals in the VASP interface
    # but not necessarily with wannier90, qe, and wien2k!
    # This is also true for the f-shell.
    Umat_full = util.U_matrix_slater(l=l,
                              radial_integrals=slater_integrals, basis='spherical')
    Umat_full = util.transform_U_matrix(Umat_full,
                                        util.spherical_to_cubic(l=l, convention=general_params['h_int_basis']))

    if l == 2 and n_orb == 2:
        Umat_full = util.eg_submatrix(Umat_full)
        mpi.report('Using eg subspace of interaction Hamiltonian')
    elif l == 2 and n_orb == 3:
        Umat_full = util.t2g_submatrix(Umat_full)
        mpi.report('Using t2g subspace of interaction Hamiltonian')
    elif n_orb != 2*l+1:
        raise ValueError(f'Imp {icrsh}: for the Slater Hamiltonian, please use either '
                         f'the full shell with 2l+1={2*l+1} orbitals '
                         'or the t2g or eg subspace of the d shell with 3 or 2 orbitals.')

    return Umat_full


def _rotate_four_index_matrix(sum_k, general_params, Umat_full, icrsh):
    """ Rotates the four index matrix into the local frame. """

    ish = sum_k.inequiv_to_corr[icrsh]
    # Transposes rotation matrix here because TRIQS has a slightly different definition
    Umat_full_rotated = util.transform_U_matrix(Umat_full, sum_k.rot_mat[ish].T)

    if general_params['h_int_type'][icrsh] in ('density_density', 'crpa_density_density', 'dyn_density_density'):
        if not np.allclose(Umat_full_rotated, Umat_full):
            mpi.report('WARNING: applying a rotation matrix changes the dens-dens Hamiltonian.\n'
                       + 'This changes the definition of the ignored spin flip and pair hopping.')
    elif general_params['h_int_type'][icrsh] in ('full_slater', 'crpa', 'dyn_full'):
        if not np.allclose(Umat_full_rotated, Umat_full):
            mpi.report('WARNING: applying a rotation matrix changes the interaction Hamiltonian.\n'
                       + 'Please ensure that the rotation is correct!')

    return Umat_full_rotated


def _construct_density_density(sum_k, general_params, Umat_full_rotated, icrsh):
    """
    Constructs the density-density Slater-Hamiltonian from the four-index
    interaction matrix.
    """

    # Constructs Hamiltonian from Umat_full_rotated
    n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']

    Umat, Upmat = util.reduce_4index_to_2index(Umat_full_rotated)
    h_int = util.h_int_density(sum_k.spin_block_names[sum_k.SO], n_orb,
                               map_operator_structure=sum_k.sumk_to_solver[icrsh],
                               U=Umat, Uprime=Upmat, H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))

    return h_int


def _construct_slater(sum_k, general_params, Umat_full_rotated, icrsh):
    """
    Constructs the full Slater-Hamiltonian from the four-index interaction
    matrix.
    """

    n_orb = common.get_n_orbitals(sum_k)[icrsh]['up']

    h_int = util.h_int_slater(sum_k.spin_block_names[sum_k.SO], n_orb,
                              map_operator_structure=sum_k.sumk_to_solver[icrsh],
                              U_matrix=Umat_full_rotated,
                              H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt'))

    return h_int

[docs] def h_int_simple_intra(spin_names,n_orb,U,off_diag=None,map_operator_structure=None,H_dump=None): r""" Create a simple intra orbital density-density Hamiltonian. (no inter orbital terms) .. math:: H = \frac{1}{2} \sum_{i \sigma \neq \sigma')} U_{i i}^{\sigma \sigma'} n_{i \sigma} n_{i \sigma'}. Parameters ---------- spin_names : list of strings Names of the spins, e.g. ['up','down']. n_orb : int Number of orbitals. U : float U value off_diag : boolean Do we have (orbital) off-diagonal elements? If yes, the operators and blocks are denoted by ('spin', 'orbital'), otherwise by ('spin_orbital',0). map_operator_structure : dict Mapping of names of GF blocks names from one convention to another, e.g. {('up', 0): ('up_0', 0), ('down', 0): ('down_0',0)}. If provided, the operators and blocks are denoted by the mapping of ``('spin', 'orbital')``. H_dump : string Name of the file to which the Hamiltonian should be written. Returns ------- H : Operator The Hamiltonian. """ from triqs.operators.util.op_struct import get_mkind if H_dump: H_dump_file = open(H_dump,'w') H_dump_file.write("Density-density Hamiltonian:" + '\n') H = Operator() mkind = get_mkind(off_diag,map_operator_structure) if H_dump: H_dump_file.write("Density-density terms:" + '\n') for s1, s2 in product(spin_names,spin_names): if (s1 is not s2): for a1 in range(n_orb): H_term = 0.5 * U * n(*mkind(s1,a1)) * n(*mkind(s2,a1)) H += H_term # Dump terms of H if H_dump and not H_term.is_zero(): H_dump_file.write('%s'%(mkind(s1,a1),) + '\t') H_dump_file.write('%s'%(mkind(s2,a1),) + '\t') H_dump_file.write(str(U) + '\n') return H
[docs] def construct(sum_k, general_params, solver_type_per_imp, gw_params=None): """ Constructs the interaction Hamiltonian. Currently implemented are the Kanamori Hamiltonian (usually for 2 or 3 orbitals), the density-density and the full Slater Hamiltonian (for 2, 3, or 5 orbitals). If sum_k.rot_mat is non-identity, we have to consider rotating the interaction Hamiltonian: the Kanamori Hamiltonian does not change because it is invariant under orbital mixing but all the other Hamiltonians are at most invariant under rotations in space. Therefore, sum_k.rot_mat has to be correct before calling this method. The parameters U and J will be interpreted differently depending on the type of the interaction Hamiltonian: it is either the Kanamori parameters for the Kanamori Hamiltonian or the orbital-averaged parameters (consistent with DFT+U, https://cms.mpi.univie.ac.at/wiki/index.php/LDAUTYPE ) for all other Hamiltonians. Note also that for all Hamiltonians except Kanamori, the order of the orbitals matters. The correct order is specified here: triqs.github.io/triqs/unstable/documentation/python_api/triqs.operators.util.U_matrix.spherical_to_cubic.html """ # Extracts U and J mpi.report('*** interaction parameters ***') # Constructs the interaction Hamiltonian. Needs to come after setting sum_k.rot_mat mpi.report('\nConstructing the interaction Hamiltonians') h_int = [None] * sum_k.n_inequiv_shells for icrsh in range(sum_k.n_inequiv_shells): mpi.report('\nImpurity {}: constructing a {} type interaction Hamiltonian'.format(icrsh, general_params['h_int_type'][icrsh])) # Kanamori if general_params['h_int_type'][icrsh] == 'kanamori': h_int[icrsh] = _construct_kanamori(sum_k, general_params, solver_type_per_imp, icrsh, den_den=False) continue if general_params['h_int_type'][icrsh] == 'kanamori_den_den': h_int[icrsh] = _construct_kanamori(sum_k, general_params, solver_type_per_imp, icrsh, den_den=True) continue # for density density or full slater get full four-index U matrix if general_params['h_int_type'][icrsh] in ('density_density', 'full_slater'): mpi.report('\nNote: The input parameters U and J here are orbital-averaged parameters.') mpi.report('Note: The order of the orbitals is important. See also the doc string of this method.') # Checks that all entries are l == 2 or R is None if (sum_k.corr_shells[sum_k.inequiv_to_corr[icrsh]]['l'] != 2 and general_params['ratio_F4_F2'][icrsh] is not None): raise ValueError('Ratio F4/F2 only implemented for d-shells ' + 'but set in impurity {}'.format(icrsh)) if general_params['h_int_type'][icrsh] == 'density_density' and solver_type_per_imp[icrsh] == 'ftps': # TODO: implement raise NotImplementedError('Note: Density-density not implemented for ftps.') Umat_full = _generate_four_index_u_matrix(sum_k, general_params, icrsh) if sum_k.SO == 1: Umat_full = _adapt_U_4index_for_SO(Umat_full) # Rotates the interaction matrix Umat_full_rotated = _rotate_four_index_matrix(sum_k, general_params, Umat_full, icrsh) # construct slater / density density from U tensor if general_params['h_int_type'][icrsh] == 'full_slater': h_int[icrsh] = _construct_slater(sum_k, general_params, Umat_full_rotated, icrsh) else: h_int[icrsh] = _construct_density_density(sum_k, general_params, Umat_full_rotated, icrsh) continue # simple total impurity occupation interation: U/2 (Ntot^2 - Ntot) if general_params['h_int_type'][icrsh] == 'ntot': n_tot_op = Operator() for block, n_orb in sum_k.gf_struct_solver[icrsh].items(): n_tot_op += sum(n(block, orb) for orb in range(n_orb)) h_int[icrsh] = general_params['U'][icrsh]/2.0 * (n_tot_op*n_tot_op - n_tot_op) continue if general_params['h_int_type'][icrsh] == 'simple_intra': h_int[icrsh] = h_int_simple_intra(sum_k.spin_block_names[sum_k.SO], common.get_n_orbitals(sum_k)[icrsh]['up'], map_operator_structure=sum_k.sumk_to_solver[icrsh], U=general_params['U'][icrsh], H_dump=os.path.join(general_params['jobname'], f'H_imp{icrsh}.txt')) continue # read from file options if general_params['h_int_type'][icrsh] in ('crpa', 'crpa_density_density', 'dyn_density_density', 'dyn_full'): Umat_full, gw_params = _load_crpa_interaction_matrix(sum_k, general_params, gw_params) if sum_k.SO == 1: Umat_full[icrsh] = _adapt_U_4index_for_SO(Umat_full[icrsh]) # Rotates the interaction matrix if sum_k.use_rotations: Umat_full[icrsh] = _rotate_four_index_matrix(sum_k, general_params, Umat_full[icrsh], icrsh) Umat_full[icrsh][np.abs(Umat_full[icrsh]) < general_params['U_crpa_threshold']] = 0.0 gw_params['U_matrix_rot']= Umat_full # construct slater / density density from U tensor if general_params['h_int_type'][icrsh] in ('crpa', 'dyn_full'): h_int[icrsh] = _construct_slater(sum_k, general_params, Umat_full[icrsh].real, icrsh) else: h_int[icrsh] = _construct_density_density(sum_k, general_params, Umat_full[icrsh].real, icrsh) continue raise NotImplementedError('Error when constructing the interaction Hamiltonian.') return h_int, gw_params