TRIQS/triqs_modest 3.3.0
Modular Electronic Structure Toolkit
Loading...
Searching...
No Matches
obe_tb.cpp
Go to the documentation of this file.
1// Copyright (c) 2025--present, The Simons Foundation
2// This file is part of TRIQS/modest and is licensed under the terms of GPLv3 or later.
3// SPDX-License-Identifier: GPL-3.0-or-later
4// See LICENSE in the root of this distribution for details.
5
6#include <nda/layout/range.hpp>
7#include <nda/matrix_functions.hpp>
8#include <triqs/gfs.hpp>
9#include <triqs/utility/exceptions.hpp>
10#include <type_traits>
11#include "./local_space.hpp"
12#include <triqs/tb/wannier_loader.hpp>
13#include "downfolding.hpp"
14#include <stdexcept>
15#include "./obe_tb.hpp"
17
18// DANGEROUS : no way to check that wannier hamiltonian has atoms in the same order if user specifies their positions/
19// the atomic shell info. We are relying on them to get this right somehow, so there should be some print check
20
21namespace triqs::modest {
22
23 one_body_elements_tb one_body_elements_from_wannier90(std::string const &wannier_file_path, spin_kind_e spin_kind,
24 std::vector<atomic_orbs> atomic_shells) {
25 if (spin_kind == spin_kind_e::Polarized) {
26 throw std::runtime_error("If performing a spin-polarized calculation, you need to supply two Wannier file paths for up and down channels.\n");
27 }
28 // call the wannier90 loader and set up tb_hamiltonian
29 // FIXME check if tb or hr exists -- write a lambda that checks which exists and returns just one of the two
30 auto [R, HR, _] = read_wannier90_tb_data(wannier_file_path);
31 std::vector<tb_hamiltonian> tb_H;
32 tb_H.emplace_back(R, HR);
33 // put the same H a second time for two spin channels
34 if (spin_kind == spin_kind_e::NonPolarized) { tb_H.emplace_back(R, HR); }
35
36 return make_obe_from_tb(std::move(tb_H), spin_kind, std::move(atomic_shells));
37 };
38
39 one_body_elements_tb one_body_elements_from_model(std::vector<std::array<long, 3>> const &Rs, std::vector<nda::array<dcomplex, 2>> const &HR,
40 spin_kind_e spin_kind, std::vector<atomic_orbs> atomic_shells) {
41 if (spin_kind != spin_kind_e::NonPolarized) {
42 throw std::runtime_error("Tight-binding model can only be used for non-spin-polarized calculations.\n");
43 }
44 std::vector<tb_hamiltonian> tb_H = {tb_hamiltonian(Rs, HR), tb_hamiltonian(Rs, HR)};
45 return make_obe_from_tb(std::move(tb_H), spin_kind, std::move(atomic_shells));
46 }
47
48 one_body_elements_tb one_body_elements_from_wannier90(std::string const &wannier_file_path_up, std::string const &wannier_file_path_dn,
49 spin_kind_e spin_kind, std::vector<atomic_orbs> atomic_shells) {
50 if (spin_kind != spin_kind_e::Polarized) {
51 throw std::runtime_error("For a non-spin polarized calculation, you should specify only one Wannier Hamiltonian.\n");
52 }
53
54 // call the wannier90 loader and set up tb_hamiltonian list
55 std::vector<tb_hamiltonian> tb_H;
56 for (auto file : {wannier_file_path_up, wannier_file_path_dn}) {
57 auto [R, HR, _] = read_wannier90_tb_data(file);
58 tb_H.emplace_back(R, HR);
59 }
60 if (tb_H[0].n_orbitals() != tb_H[1].n_orbitals()) {
61 throw std::runtime_error(
62 "Cannot construct a one_body_elements "
63 "using up and down H_k that have a different number of orbtials.");
64 }
65 return make_obe_from_tb(std::move(tb_H), spin_kind, std::move(atomic_shells));
66 }
67
68 // -----------------------------------------------------------------------
69
70 nda::array<nda::matrix<dcomplex>, 2> Hloc(std::vector<tb_hamiltonian> const &H_sigma, std::vector<atomic_orbs> const &atomic_shells) {
71
72 // group the shells into atom indices
73 // makes a vector containing the dim of each atomic shell...
74 //auto shell_decomposition = atomic_shells | stdv::transform([](auto const &s) { return s.dim; });
75
76 // return Hloc with shape [n_atoms, nsigma]
77 nda::array<nda::matrix<dcomplex>, 2> Hloc_result(atomic_shells.size(), H_sigma.size());
78
79 for (auto [isigma, H] : enumerate(H_sigma)) {
80
81 // FIXME : REFACTOR decomposition from atomic shells
82 // check that HR and atomic shells list have the same total size
83 long n_orb = 0;
84 for (auto shell : atomic_shells) { n_orb += shell.dim; }
85 if (H.n_orbitals() != n_orb) {
86 throw std::runtime_error("Wannier Hamiltonian does not have the same number of orbitals as the provided atomic shells: HR "
87 + std::to_string(H.n_orbitals()) + " , atomic_shells total " + std::to_string(n_orb));
88 }
89
90 // find the home cell of the TB file to get H0
91 auto iR0 = H.get_R_idx(std::array<long, 3>{0, 0, 0});
92
93 // Hloc needs to have dimensions [n_atomic_shells, nspin].
94 // NOTE: we cannot use enumerated_subslice here because we do not have a C_space!
95 // this function computes Hloc so we can determine the C_space in the first place.
96 long start_orb = 0;
97 for (auto &&[ishell, shell] : enumerate(atomic_shells)) {
98 auto Hloc0_ab = nda::zeros<dcomplex>(shell.dim, shell.dim);
99 // need to create a matrix of dim x dim for each
100 for (auto iorb : nda::range(shell.dim)) {
101 for (auto jorb : nda::range(shell.dim)) { Hloc0_ab(iorb, jorb) = H.hoppings()[iR0](start_orb + iorb, start_orb + jorb); }
102 start_orb += shell.dim;
103 }
104 Hloc_result(ishell, isigma) = Hloc0_ab;
105 }
106 }
107 return Hloc_result;
108 }
109
110 nda::array<nda::matrix<dcomplex>, 2> impurity_levels(one_body_elements_tb const &obe) {
111 nda::array<nda::matrix<dcomplex>, 2> Hloc_result(1, obe.C_space.n_sigma());
112 for (auto sigma : range(obe.C_space.n_sigma())) { Hloc_result(0, sigma) = obe.H[sigma].hoppings()[obe.H[sigma].get_R_idx({0, 0, 0})]; }
113 return Hloc_result;
114 }
115
116 // -----------------------------------------------------------------------
117
118 one_body_elements_tb make_obe_from_tb(std::vector<tb_hamiltonian> H_sigma, spin_kind_e spin_kind, std::vector<atomic_orbs> atomic_shells) {
119
120 // calculate Hloc using helper function -- Hloc here is dim [nshells, nsigma]
121 nda::array<nda::matrix<dcomplex>, 2> hloc = Hloc(H_sigma, atomic_shells);
122
123 // construct block structure using Hloc
124 double block_threshold = 1e-6;
125 bool diagonalize_hloc = false; // CHECK this would mean looking at band energies at k = 0?
126 auto [decomposition, U] = discover_symmetries(hloc, atomic_shells, block_threshold, diagonalize_hloc);
127
128 // call constructor for local space, for now ignoring rotation_from_spherical_to_dft_basis
129 auto LS = local_space(spin_kind, atomic_shells, decomposition, U, {});
130
131 // construct and return obe_tb
132 return one_body_elements_tb{.C_space = std::move(LS), .H = std::move(H_sigma)};
133 }
134
135 // -----------------------------------------------------------------------
136
137 one_body_elements_tb fold(tb::superlattice const &sl, one_body_elements_tb const &obe) {
138 auto new_H = obe.H | stdv::transform([&](auto x) { return fold(sl, x); }) | tl::to<std::vector>();
139 auto sh = obe.C_space.atomic_shells();
140 decltype(sh) new_atomic_shells;
141 auto const &dec = obe.C_space.atoms_block_decomposition();
142 nda::array<std::vector<long>, 2> new_dec(dec.extent(0) * sl.n_cluster_sites(), dec.extent(1));
143 for (auto i : nda::range(sl.n_cluster_sites())) {
144 for (auto &&[j, shell] : enumerate(sh)) {
145 new_atomic_shells.emplace_back(shell);
146 new_dec(i * sh.size() + j, r_all) = dec(j, r_all);
147 }
148 }
149 return {.C_space = local_space{obe.C_space.spin_kind(), std::move(new_atomic_shells), new_dec, {}, {}}, .H = std::move(new_H)};
150 }
151
152 // -----------------------------------------------------------------------
153
154 one_body_elements_tb rotate(one_body_elements_tb const &obe, nda::matrix<dcomplex> const &U) {
155
156 auto rotate = [](tb_hamiltonian const &H, nda::matrix<dcomplex> const &U) {
157 if (U.extent(0) != H.n_orbitals()) {
158 throw std::runtime_error(
159 "Cannot rotate a tb_hamiltonian with a unitary matrix that has a different number of rows than the number of orbitals in the Hamiltonian.");
160 }
161
162 auto const &Rs = H.get_R_list();
163 std::vector<nda::array<dcomplex, 2>> rotated_hoppings;
164 for (auto const &tR : H.hoppings()) { rotated_hoppings.emplace_back(U * nda::matrix<dcomplex>(tR) * dagger(U)); }
165 return tb_hamiltonian{Rs, std::move(rotated_hoppings)};
166 };
167
168 auto new_H = obe.H | stdv::transform([&](auto x) { return rotate(x, U); }) | tl::to<std::vector>();
169
170 return {.C_space = obe.C_space, .H = std::move(new_H)};
171 }
172
173} // namespace triqs::modest
Describe the atomic orbitals within downfolded space.
spin_kind_e spin_kind() const
Spin kind of index.
long n_sigma() const
Dimension of the index.
nda::array< std::vector< long >, 2 > const & atoms_block_decomposition() const
2-dim array of all blocks spanning space -> atoms_block_decomposition.
std::vector< atomic_orbs > const & atomic_shells() const
List of all atomic shells spanning the space.
nda::array< nda::matrix< dcomplex >, 2 > impurity_levels(one_body_elements_on_grid const &obe)
Compute the local impurity levels from the single-particle dispersion.
one_body_elements_tb one_body_elements_from_wannier90(std::string const &wannier_file_path, spin_kind_e spin_kind, std::vector< atomic_orbs > atomic_shells)
Construct a one-body elements TB object from Wannier90 in the case of a single spin index.
Definition obe_tb.cpp:23
one_body_elements_tb make_obe_from_tb(std::vector< tb_hamiltonian > H_sigma, spin_kind_e spin_kind, std::vector< atomic_orbs > atomic_shells)
Helper to contruct and return an OBE_tb object given a list of tb_Hamiltonians of length n_sigma.
Definition obe_tb.cpp:118
one_body_elements_tb rotate(one_body_elements_tb const &obe, nda::matrix< dcomplex > const &U)
Rotate a tight-binding Hamiltonian by a unitary matrix .
Definition obe_tb.cpp:154
one_body_elements_tb fold(tb::superlattice const &sl, one_body_elements_tb const &obe)
Definition obe_tb.cpp:137
spin_kind_e
Kind of σ index.
std::pair< nda::array< std::vector< long >, 2 >, nda::array< nda::matrix< dcomplex >, 2 > > discover_symmetries(nda::array< nda::matrix< dcomplex >, 2 > const &Hloc0, std::vector< atomic_orbs > const &atomic_shells, double block_threshold, bool diagonalize_hloc)
Find symmetries of the component of a Hamiltonian to determine a GF block structure.
Definition loaders.cpp:44
nda::array< nda::matrix< dcomplex >, 2 > Hloc(std::vector< tb_hamiltonian > const &H_sigma, std::vector< atomic_orbs > const &atomic_shells)
Compute given tight binding Hamiltonians.
Definition obe_tb.cpp:70
one_body_elements_tb one_body_elements_from_model(std::vector< std::array< long, 3 > > const &Rs, std::vector< nda::array< dcomplex, 2 > > const &HR, spin_kind_e spin_kind, std::vector< atomic_orbs > atomic_shells)
Definition obe_tb.cpp:39
static constexpr auto r_all
Definition defs.hpp:40
A one-body elements using a tight-binding Hamiltonian.
Definition obe_tb.hpp:29
std::vector< tb_hamiltonian > H
List of TB Hamiltonians.
Definition obe_tb.hpp:31
local_space C_space
Local space.
Definition obe_tb.hpp:30