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 <triqs/experimental/lattice/wannier_loader.hpp>
12#include <stdexcept>
13#include "./obe_tb.hpp"
15
16namespace triqs::modest {
17
18 // -----------------------------------------------------------------------
19 // Functions to construct using wannier90 input directly
20
21 // NOTE : there is no way to check that wannier hamiltonian has atoms in the same order if user specifies their positions/
22 // the atomic shell info. We are relying on them to get this right.
23
24 one_body_elements_tb one_body_elements_from_wannier90(std::string const &wannier_file_path, spin_kind_e spin_kind,
25 std::vector<atomic_orbs> atomic_shells) {
26 if (spin_kind == spin_kind_e::Polarized) {
27 throw std::runtime_error("If performing a spin-polarized calculation, you need to supply two Wannier file paths for up and down channels.\n");
28 }
29 // call the wannier90 loader and set up tb_hk
30 auto [R, HR, r_op, lvs] = read_wannier90_tb_data(wannier_file_path);
31 std::vector<tb_hk> 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 one_body_elements_tb(std::move(tb_H), spin_kind, std::move(atomic_shells));
37 }
38
39 one_body_elements_tb one_body_elements_from_wannier90(std::string const &wannier_file_path_up, std::string const &wannier_file_path_dn,
40 spin_kind_e spin_kind, std::vector<atomic_orbs> atomic_shells) {
41
42 if (spin_kind != spin_kind_e::Polarized) {
43 throw std::runtime_error("For a non-spin polarized calculation, you should specify only one Wannier Hamiltonian.\n");
44 }
45
46 // call the wannier90 loader and set up tb_hk list
47 std::vector<tb_hk> tb_H;
48 for (auto file : {wannier_file_path_up, wannier_file_path_dn}) {
49 auto [R, HR, r_op, lvs] = read_wannier90_tb_data(file);
50 tb_H.emplace_back(R, HR);
51 }
52 return one_body_elements_tb(std::move(tb_H), spin_kind, std::move(atomic_shells));
53 }
54
55 // -----------------------------------------------------------------------
56 // constructor
57
58 one_body_elements_tb::one_body_elements_tb(std::vector<tb_hk> H_sigma, spin_kind_e spin_kind, std::vector<atomic_orbs> atomic_shells)
59 : H{std::move(H_sigma)} {
60
61 // check that appropriate Hamiltonian information was provided
62 if (H[0].n_orbitals() != H[1].n_orbitals()) {
63 throw std::runtime_error(
64 "Cannot construct a one_body_elements "
65 "using up and down H_k that have a different number of orbitals.");
66 }
67
68 // calculate Hloc using helper function -- Hloc here is dim [nshells, nsigma]
69 nda::array<nda::matrix<dcomplex>, 2> hloc = Hloc(H, atomic_shells);
70
71 // construct block structure using Hloc
72 double block_threshold = 1e-6;
73 bool diagonalize_hloc = false; // CHECK this would mean looking at band energies at k = 0?
74 auto [decomposition, U] = discover_symmetries(hloc, atomic_shells, block_threshold, diagonalize_hloc);
75
76 // call constructor for local space, for now ignoring rotation_from_spherical_to_dft_basis
77 C_space = local_space(spin_kind, atomic_shells, decomposition, U, {});
78 }
79
80 // -----------------------------------------------------------------------
81
82 nda::array<nda::matrix<dcomplex>, 2> Hloc(std::vector<tb_hk> const &H_sigma, std::vector<atomic_orbs> const &atomic_shells) {
83
84 // return Hloc with shape [n_atoms, nsigma]
85 nda::array<nda::matrix<dcomplex>, 2> Hloc_result(atomic_shells.size(), H_sigma.size());
86
87 for (auto [isigma, H] : enumerate(H_sigma)) {
88
89 // check that HR and atomic shells list have the same total size
90 long n_orb = 0;
91 for (auto shell : atomic_shells) { n_orb += shell.dim; }
92 if (H.n_orbitals() != n_orb) {
93 throw std::runtime_error("TB Hamiltonian does not have the same number of orbitals as the provided atomic shells: HR "
94 + std::to_string(H.n_orbitals()) + " , atomic_shells total " + std::to_string(n_orb));
95 }
96
97 // find the home cell of the TB file to get H0
98 auto iR0 = H.get_R_idx(std::array<long, 3>{0, 0, 0});
99
100 // Hloc needs to have dimensions [n_atomic_shells, nspin].
101 // NOTE: we cannot use enumerated_subslice here because we do not have a C_space!
102 // this function computes Hloc so we can determine the C_space in the first place.
103 long start_orb = 0;
104 for (auto &&[ishell, shell] : enumerate(atomic_shells)) {
105 auto Hloc0_ab = nda::zeros<dcomplex>(shell.dim, shell.dim);
106 // need to create a matrix of dim x dim for each
107 for (auto iorb : nda::range(shell.dim)) {
108 for (auto jorb : nda::range(shell.dim)) { Hloc0_ab(iorb, jorb) = H[iR0](start_orb + iorb, start_orb + jorb); }
109 start_orb += shell.dim;
110 }
111 Hloc_result(ishell, isigma) = Hloc0_ab;
112 }
113 }
114 return Hloc_result;
115 }
116
117 nda::array<nda::matrix<dcomplex>, 2> impurity_levels(one_body_elements_tb const &obe) {
118 nda::array<nda::matrix<dcomplex>, 2> Hloc_result(1, obe.C_space.n_sigma());
119 for (auto sigma : range(obe.C_space.n_sigma())) { Hloc_result(0, sigma) = obe.H[sigma][{0, 0, 0}]; }
120 return Hloc_result;
121 }
122
123 // -----------------------------------------------------------------------
124
125 one_body_elements_tb fold(superlattice const &sl, one_body_elements_tb const &obe) {
126 auto new_H = obe.H | stdv::transform([&](auto x) { return fold(sl, x); }) | tl::to<std::vector>();
127 auto sh = obe.C_space.atomic_shells();
128 decltype(sh) new_atomic_shells;
129 auto const &dec = obe.C_space.atoms_block_decomposition();
130 nda::array<std::vector<long>, 2> new_dec(dec.extent(0) * sl.n_cluster_sites(), dec.extent(1));
131 for (auto i : nda::range(sl.n_cluster_sites())) {
132 for (auto &&[j, shell] : enumerate(sh)) {
133 new_atomic_shells.emplace_back(shell);
134 new_dec(i * sh.size() + j, r_all) = dec(j, r_all);
135 }
136 }
137 return one_body_elements_tb(std::move(new_H), local_space{obe.C_space.spin_kind(), std::move(new_atomic_shells), new_dec, {}, {}});
138 }
139
140 // -----------------------------------------------------------------------
141
142 one_body_elements_tb rotate(one_body_elements_tb const &obe, nda::matrix<dcomplex> const &U) {
143
144 auto new_H = obe.H;
145 for (auto &h : new_H) {
146 if (U.extent(0) != h.n_orbitals()) {
147 throw std::runtime_error(
148 "Cannot rotate a tb_hk with a unitary matrix that has a different number of rows than the number of orbitals in the Hamiltonian.");
149 }
150 for (auto i : nda::range(long(h.get_R_list().size()))) {
151 auto tR = nda::matrix<dcomplex>(h[i]);
152 h[i] = U * tR * dagger(U);
153 }
154 }
155
156 return one_body_elements_tb(std::move(new_H), obe.C_space);
157 }
158
159 // -----------------------------------------------------------------------
160
162
164 throw std::runtime_error("Can only extend to spin a non-spin-polarized one_body_elements_tb.");
165 }
166
167 auto const &Rs = obe.H[0].get_R_list();
168 auto n_orb = obe.C_space.dim();
169 auto new_orb = 2 * n_orb;
170
171 auto extend_matrix = [&new_orb, &n_orb](auto const &mat) {
172 auto ext_mat = nda::array<dcomplex, 2>(new_orb, new_orb);
173 for (auto const &i : nda::range(n_orb)) {
174 for (auto const &j : nda::range(n_orb)) {
175 ext_mat(i, j) = mat(i, j); // up-up
176 ext_mat(i + n_orb, j + n_orb) = mat(i, j); // down-down
177 }
178 }
179 return ext_mat;
180 };
181
182 std::vector<nda::array<dcomplex, 2>> new_hoppings;
183 for (auto const &tR : obe.H[0].hoppings()) new_hoppings.emplace_back(extend_matrix(tR));
184 auto new_tb_H = std::vector<tb_hk>{{Rs, std::move(new_hoppings)}};
185
186 auto const &sh = obe.C_space.atomic_shells();
187 auto new_atomic_shells = sh
188 | stdv::transform([](auto const &s) { return atomic_orbs{.dim = 2 * s.dim, .l = s.l, .cls_idx = s.cls_idx, .dft_idx = s.dft_idx}; })
189 | tl::to<std::vector>();
190
191 return one_body_elements_tb(std::move(new_tb_H), spin_kind_e::NonColinear, std::move(new_atomic_shells));
192 }
193
194 // -----------------------------------------------------------------------
195
196 one_body_elements_tb add_local_term(one_body_elements_tb const &obe, nda::matrix<dcomplex> const &local_term) {
197 if (local_term.extent(0) != obe.C_space.dim()) {
198 throw std::runtime_error("Cannot add a local term with a different dimension than the one_body_elements_tb.");
199 }
200
201 auto new_tb_H = obe.H;
202 for (auto &h : new_tb_H) { h[{0, 0, 0}] += local_term; }
203
204 auto spin_kind = obe.C_space.spin_kind();
205 auto shells = obe.C_space.atomic_shells();
206
207 return one_body_elements_tb(std::move(new_tb_H), spin_kind, std::move(shells));
208 }
209} // 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.
long dim() const
Dimension of the correlated space.
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(std::vector< tb_hk > H_sigma, local_space ls)
Construct a one-body elements TB object from a list of tb_hk objects.
Definition obe_tb.hpp:43
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:24
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:142
spin_kind_e
Kind of σ index.
nda::array< nda::matrix< dcomplex >, 2 > Hloc(std::vector< tb_hk > const &H_sigma, std::vector< atomic_orbs > const &atomic_shells)
Compute given tight binding Hamiltonians.
Definition obe_tb.cpp:82
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
one_body_elements_tb extend_to_spin(one_body_elements_tb const &obe)
Definition obe_tb.cpp:161
one_body_elements_tb add_local_term(one_body_elements_tb const &obe, nda::matrix< dcomplex > const &local_term)
Definition obe_tb.cpp:196
one_body_elements_tb fold(superlattice const &sl, one_body_elements_tb const &obe)
Convert a tight binding Hamiltonian to its superlattice equivalent.
Definition obe_tb.cpp:125
static constexpr auto r_all
Definition defs.hpp:33
Info on an atomic shell.
A one-body elements using a tight-binding Hamiltonian.
Definition obe_tb.hpp:29
std::vector< tb_hk > H
List of TB Hamiltonians.
Definition obe_tb.hpp:31
local_space C_space
Local space.
Definition obe_tb.hpp:30