TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
tight_binding.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2013-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You may obtain a copy of the License at
16// https://www.gnu.org/licenses/gpl-3.0.txt
17//
18// Authors: Thomas Ayral, Michel Ferrero, Alexander Hampel, Olivier Parcollet, Nils Wentzell
19
24
25#include "./tight_binding.hpp"
26#include "./grid_generator.hpp"
28
29#include <nda/algorithms.hpp>
30#include <nda/linalg/eigh.hpp>
31#include <nda/nda.hpp>
32
33#include <tuple>
34#include <utility>
35#include <vector>
36
37namespace triqs::lattice {
38
39 using namespace arrays;
40
41 void tight_binding::check_hoppings() {
42 // checking displacements and hopping matrices
43 if (displ_vec_.size() != overlap_mat_vec_.size()) TRIQS_RUNTIME_ERROR << " Number of displacements != Number of matrices";
44 for (int i = 0; i < displ_vec_.size(); ++i) {
45 if (displ_vec_[i].size() != bl_.ndim())
46 TRIQS_RUNTIME_ERROR << "displacement of incorrect size : got " << displ_vec_[i].size() << "instead of " << bl_.ndim();
47 if (first_dim(overlap_mat_vec_[i]) != n_orbitals())
48 TRIQS_RUNTIME_ERROR << "the first dim matrix is of size " << first_dim(overlap_mat_vec_[i]) << " instead of " << n_orbitals();
49 if (second_dim(overlap_mat_vec_[i]) != n_orbitals())
50 TRIQS_RUNTIME_ERROR << "the second dim matrix is of size " << second_dim(overlap_mat_vec_[i]) << " instead of " << n_orbitals();
51
52 // check hermiticity of hoppings: Hij(+R)= Hji(-R)* by looping of all displacements again
53 bool found = false;
54 for (int j = 0; j < displ_vec_.size(); ++j) {
55 if (displ_vec_[i] == -displ_vec_[j]) {
56 found = true;
57 if (max_element(abs(overlap_mat_vec_[i] - dagger(overlap_mat_vec_[j]))) > 1.e-12)
58 TRIQS_RUNTIME_ERROR << "hopping matrix of displacement " << displ_vec_[i] << overlap_mat_vec_[i]
59 << "\nis not hermitian conjugate of matrix for displacement " << displ_vec_[j] << overlap_mat_vec_[j] << "\n";
60 break;
61 }
62 }
63 if (not found) TRIQS_RUNTIME_ERROR << "opposite hopping vector of " << displ_vec_[i] << " cannot be found";
64 }
65 }
66
67 //------------------------------------------------------
68
69 tight_binding::tight_binding(bravais_lattice bl, std::vector<nda::vector<long>> displ_vec, std::vector<nda::matrix<dcomplex>> overlap_mat_vec)
70 : bl_(std::move(bl)), displ_vec_(std::move(displ_vec)), overlap_mat_vec_(std::move(overlap_mat_vec)) {
71 check_hoppings();
72 }
73
74 //------------------------------------------------------
75
77 : tight_binding(std::move(bl), std::move(hoppings.displ_vec), std::move(hoppings.overlap_mat_vec)) {}
78
79 //------------------------------------------------------
80
81 std::pair<array<double, 1>, array<double, 2>> dos(tight_binding const &TB, int nkpts, int neps) {
82
83 // loop on the BZ
84 int ndim = TB.lattice().ndim();
85 int norb = static_cast<int>(TB.lattice().n_orbitals());
86 grid_generator grid(ndim, nkpts);
87 array<double, 1> tempeval(norb);
88 array<dcomplex, 3> evec(norb, norb, grid.size());
89 array<double, 2> eval(norb, grid.size());
90 if (norb == 1)
91 for (; grid; ++grid) {
92 double ee = real(TB.fourier((*grid)(range(ndim)))(0, 0));
93 eval(0, grid.index()) = ee;
94 evec(0, 0, grid.index()) = 1;
95 }
96 else
97 for (; grid; ++grid) {
98 // cerr<<" index = "<<grid.index()<<endl;
99 array_view<double, 1> eval_sl = eval(range::all, grid.index());
100 array_view<dcomplex, 2> evec_sl = evec(range::all, range::all, grid.index());
101 std::tie(eval_sl, evec_sl) = nda::linalg::eigh(TB.fourier((*grid)(range(ndim))));
102 // cerr<< " point "<< *grid << " value "<< eval_sl<< endl; //" "<< (*grid) (range(ndim)) << endl;
103 }
104
105 // define the epsilon mesh, etc.
106 array<double, 1> epsilon(neps);
107 double epsmax = max_element(eval);
108 double epsmin = min_element(eval);
109 double deps = (epsmax - epsmin) / neps;
110 // for (int i =0; i< neps; ++i) epsilon(i)= epsmin+i/(neps-1.0)*(epsmax-epsmin);
111 for (int i = 0; i < neps; ++i) epsilon(i) = epsmin + (i + 0.5) * deps;
112
113 // bin the eigenvalues according to their energy
114 // NOTE: a is defined as an integer. it is the index for the DOS.
115 // REPORT <<"Starting Binning ...."<<endl;
116 array<double, 2> rho(neps, norb);
117 rho() = 0;
118 for (int l = 0; l < norb; l++) {
119 for (int j = 0; j < grid.size(); j++) {
120 int a = int((eval(l, j) - epsmin) / deps);
121 if (a == int(neps)) a = a - 1;
122 for (int k = 0; k < norb; k++) { rho(a, k) += real(conj(evec(k, l, j)) * evec(k, l, j)); }
123 }
124 }
125 rho /= grid.size() * deps;
126 return std::make_pair(epsilon, rho);
127 }
128
129 //----------------------------------------------------------------------------------
130
131 std::pair<array<double, 1>, array<double, 1>> dos_patch(tight_binding const &TB, const array<double, 2> &triangles, int neps, int ndiv) {
132 // WARNING: This version only works for a single band Hamiltonian in 2 dimensions!!!!
133 // triangles is an array of points defining the triangles of the patch
134 // neps in the number of bins in energy
135 // ndiv in the number of divisions used to divide the triangles
136
137 // int ndim=TB.lattice().dim();
138 // int norb=TB.lattice().n_orbitals();
139 int ntri = static_cast<int>(triangles.shape(0)) / 3;
140 array<double, 1> dos(neps);
141
142 // Check consistency
143 int ndim = TB.lattice().ndim();
144 // int norb=TB.lattice().n_orbitals();
145 if (ndim != 2) TRIQS_RUNTIME_ERROR << "dos_patch : dimension 2 only !";
146 if (triangles.shape(1) != ndim) TRIQS_RUNTIME_ERROR << "dos_patch : the second dimension of the 'triangle' array in not " << ndim;
147
148 // Every triangle has ndiv*ndiv k points
149 int nk = ntri * ndiv * ndiv;
150 int k_index = 0;
151 double epsmax = -100000, epsmin = 100000;
152 array<dcomplex, 2> thop(1, 1);
153 array<double, 1> energ(nk), weight(nk);
154
155 // a, b, c are the corners of the triangle
156 // g the center of gravity taken from a
157 array<double, 1> a(ndim), b(ndim), c(ndim), g(ndim), rv(ndim);
158 int pt = 0;
159 double s{}, t{};
160
161 // loop over the triangles
162 for (int tri = 0; tri < ntri; tri++) {
163 a = triangles(pt, range::all);
164 pt++;
165 b = triangles(pt, range::all);
166 pt++;
167 c = triangles(pt, range::all);
168 pt++;
169 g = ((a + b + c) / 3.0 - a) / double(ndiv);
170
171 // the area around a k point might be different from one triangle to the other
172 // so I use it to weight the sum in the dos
173 double area = abs(0.5 * ((b(0) - a(0)) * (c(1) - a(1)) - (b(1) - a(1)) * (c(0) - a(0))) / (ndiv * ndiv));
174
175 for (int i = 0; i < ndiv; i++) {
176 s = i / double(ndiv);
177 for (int j = 0; j < ndiv - i; j++) {
178 t = j / double(ndiv);
179 for (int k = 0; k < 2; k++) {
180
181 rv = a + s * (b - a) + t * (c - a) + (k + 1.0) * g;
182
183 if (k == 0 || j < ndiv - i - 1) {
184
185 energ(k_index) = real(TB.fourier(rv)(0, 0));
186 // compute(rv);
187 // energ(k_index) = real(tk_for_eval(1,1)); //tk_for_eval is Fortran array
188 weight(k_index) = area;
189 if (energ(k_index) > epsmax) epsmax = energ(k_index);
190 if (energ(k_index) < epsmin) epsmin = energ(k_index);
191 k_index++;
192 }
193 }
194 }
195 }
196 }
197 // check consistency
198 assert(k_index == nk);
199
200 // define the epsilon mesh, etc.
201 array<double, 1> epsilon(neps);
202 double deps = (epsmax - epsmin) / neps;
203 for (int i = 0; i < neps; ++i) epsilon(i) = epsmin + i / (neps - 1.0) * (epsmax - epsmin);
204
205 // bin the eigenvalues according to their energy
206 int ind{};
207 dos() = 0.0;
208 for (int j = 0; j < nk; j++) {
209 ind = int((energ(j) - epsmin) / deps);
210 if (ind == int(neps)) ind--;
211 dos(ind) += weight(j);
212 }
213 dos /= deps; // Normalize the DOS
214 return {std::move(epsilon), std::move(dos)};
215 }
216
217 //------------------------------------------------------
218 array<dcomplex, 3> hopping_stack(tight_binding const &TB, nda::array_const_view<double, 2> k_stack) {
219 array<dcomplex, 3> res(TB.n_orbitals(), TB.n_orbitals(), k_stack.shape(1));
220 for (int i = 0; i < k_stack.shape(1); ++i) res(range::all, range::all, i) = TB.fourier(k_stack(range::all, i));
221 return res;
222 }
223
224 //------------------------------------------------------
225 array<double, 2> energies_on_bz_path(tight_binding const &TB, k_t const &K1, k_t const &K2, int n_pts) {
226 int norb = static_cast<int>(TB.lattice().n_orbitals());
227 int ndim = TB.lattice().ndim();
228 array<double, 2> eval(norb, n_pts);
229 k_t dk = (K2 - K1) / double(n_pts), k = K1;
230 for (int i = 0; i < n_pts; ++i, k += dk) { eval(range::all, i) = nda::linalg::eigvalsh(TB.fourier(k(range(ndim)))()); }
231 return eval;
232 }
233
234 //------------------------------------------------------
235 array<dcomplex, 3> energy_matrix_on_bz_path(tight_binding const &TB, k_t const &K1, k_t const &K2, int n_pts) {
236 int norb = static_cast<int>(TB.lattice().n_orbitals());
237 int ndim = TB.lattice().ndim();
238 array<dcomplex, 3> eval(norb, norb, n_pts);
239 k_t dk = (K2 - K1) / double(n_pts), k = K1;
240 for (int i = 0; i < n_pts; ++i, k += dk) { eval(range::all, range::all, i) = TB.fourier(k(range(ndim)))(); }
241 return eval;
242 }
243
244 //------------------------------------------------------
245 array<double, 2> energies_on_bz_grid(tight_binding const &TB, int n_pts) {
246
247 int norb = static_cast<int>(TB.lattice().n_orbitals());
248 int ndim = TB.lattice().ndim();
249 grid_generator grid(ndim, n_pts);
250 array<double, 2> eval(norb, grid.size());
251 for (; grid; ++grid) { eval(range::all, grid.index()) = nda::linalg::eigvalsh(TB.fourier((*grid)(range(ndim)))()); }
252 return eval;
253 }
254
255} // namespace triqs::lattice
int size() const
Get the total number of blocks.
int ndim() const
Get the number of dimensions of the Bravais lattice.
Forward iterator generating uniformly-spaced points in the d-dimensional unit cube .
int size() const
Total number of grid points, equal to (trailing factors are 1 for dim < 3).
int index() const
Linear index of the current grid point in the iteration order (x fastest, then y, then z).
Tight-binding Hamiltonian on a Bravais lattice with fully localised orbitals.
auto fourier(K const &k) const
Compute the Fourier transform for a given momentum vector (or array of momentum vectors).
long n_orbitals() const
Number of orbitals (also the size of the Bloch Hamiltonian matrix ).
bravais_lattice const & lattice() const
Get the underlying Bravais lattice.
tight_binding(bravais_lattice bl, std::vector< nda::vector< long > > displ_vec, std::vector< nda::matrix< dcomplex > > overlap_mat_vec)
Construct a tight-binding Hamiltonian on a given Bravais lattice from explicit displacement and overl...
auto const & displ_vec() const
Get the list of displacement vectors, in units of the lattice basis vectors.
auto const & overlap_mat_vec() const
Get the list of overlap (hopping) matrices, aligned with the displacement vectors.
TRIQS exception hierarchy and related macros.
Provides a forward iterator generating a regular grid of points in a cuboid.
G::regular_type::real_t real(G const &g)
Take the real part of a Green's function (no check), returning a new Green's function with a real tar...
nda::vector< double > k_t
Reciprocal space vector type.
std::pair< array< double, 1 >, array< double, 2 > > dos(tight_binding const &TB, int nkpts, int neps)
Compute the density of states of a tight-binding Hamiltonian on a regular k-grid.
many_body_operator_generic< T > c(IndexTypes... indices)
Create an annihilation operator .
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
Ordered dictionary mapping lattice displacements to hopping (overlap) matrices.
Provides a tight-binding Hamiltonian class for Bravais lattices and associated utilities.