Program Listing for File Chern.cpp¶
↰ Return to documentation for file (src_qcm/Chern.cpp)
#include <iostream>
#include <fstream>
#define MAX_K_SIDE 5000
#ifdef _OPENMP
#include <omp.h>
#endif
#include "lattice_model_instance.hpp"
#include "integrate.hpp"
#include "parser.hpp"
bool recursive=false;
const double threshold = 0.1*M_PI;
vector3D<double> increment(double d, int no, int dir);
template <typename T>
void gauge_field(matrix<T> &A, matrix<T> &B, vector<T> &u){
for(int i=0; i<A.c; ++i){
u[i] = 0.0;
for(int j=0; j<A.r; ++j){
u[i] += conjugate(A(j,i))*B(j,i);
}
}
}
void lattice_model_instance::Green_eigensystem(Green_function &G, const vector3D<double> &k0, vector<double> &e, matrix<Complex> &U, int opt)
{
vector3D<double> k = k0;
if(opt&1) k = model->superdual.to(model->dual.from(k));
else if(opt&2){
// do nothing : leave k as is
}
else k = model->superdual.to(model->physdual.from(k));
Green_function_k K(G,k);
if(opt&2){
set_Gcpt(K);
K.Gcpt.eigensystem(e, U);
}
else{
periodized_Green_function(K);
K.g.eigensystem(e, U);
}
}
vector3D<double> increment(double d, int no, int dir)
{
switch(dir){
case 3:
switch(no){
case 0: return { d, 0.0, 0.0};
case 1: return { 0.0, d, 0.0};
case 2: return {-d, 0.0, 0.0};
}
case -3:
switch(no){
case 0: return { d, 0.0, 0.0};
case 1: return { 0.0,-d, 0.0};
case 2: return {-d, 0.0, 0.0};
}
case 2:
switch(no){
case 0: return {-d, 0.0, 0.0};
case 1: return { 0.0, 0.0, d};
case 2: return { d, 0.0, 0.0};
}
case -2:
switch(no){
case 0: return {-d, 0.0, 0.0};
case 1: return { 0.0, 0.0, -d};
case 2: return { d, 0.0, 0.0};
}
case 1:
switch(no){
case 0: return {0.0, d, 0.0};
case 1: return {0.0, 0.0, d};
case 2: return {0.0, -d, 0.0};
}
case -1:
switch(no){
case 0: return {0.0, d, 0.0};
case 1: return {0.0, 0.0,-d};
case 2: return {0.0,-d, 0.0};
}
}
return {0.0, 0.0, 0.0};
}
double lattice_model_instance::Berry_flux(const vector<vector3D<double>> &k, int orb, bool spin_down)
{
check_signals();
double eta = global_double("eta");
Green_function G = cluster_Green_function(Complex(0, eta), false, spin_down);
size_t ng = model->dim_reduced_GF;
if(orb > ng) qcm_throw("the orbital number specified in Berry curvature computations is beyond the number of orbitals in the lattice model");
matrix<Complex> U(ng), U0(ng), U1(ng);
vector<Complex> u(ng);
vector<double> e(ng), e0(ng), e1(ng);
// loop over the wavevectors of the path
Green_eigensystem(G, k[0], e1, U1, 0); for(auto& x : e0) x = -1.0/x;
U0 = U1;
e0 = e1;
Complex z(1.0);
for(int i=1; i<k.size(); i++){
Green_eigensystem(G, k[i], e, U, 0); for(auto& x : e) x = -1.0/x;
gauge_field(U0, U, u);
if (orb==0){
for(int b=0; b<ng; b++) if(e[b] < 0.0) z *= u[b];
}
else z *= u[orb-1];
U0 = U;
e0 = e;
}
gauge_field(U0, U1, u);
if (orb==0){
for(int b=0; b<ng; b++) if(e0[b]+e[b] < 0.0) z *= u[b];
}
else z *= u[orb-1];
double flux = -arg(z)/(2*M_PI);
if(model->mixing == HS_mixing::normal) flux *= 2.0;
if(model->mixing == HS_mixing::up_down and spin_down == false) flux += Berry_flux(k, true, 0);
return flux;
}
double lattice_model_instance::Berry_plaquette(Green_function &G, const vector3D<double> &k1, const double deltax, const double deltay, const int opt, int dir, int orb)
{
check_signals();
size_t ng;
if(opt&2) ng = model->dim_GF;
else ng = model->dim_reduced_GF;
if(orb > ng) qcm_throw("the orbital label specified in Berry curvature computations is beyond the number of orbitals in the lattice model");
matrix<Complex> U1(ng), U2(ng), U3(ng), U4(ng);
vector<Complex> u1(ng), u2(ng), u3(ng), u4(ng);
vector<double> e1(ng), e2(ng), e3(ng), e4(ng);
vector3D<double> k(k1);
Green_eigensystem(G, k, e1, U1, opt);
for(auto& x : e1) x = -1.0/x;
k += increment(deltax, 0, dir);
Green_eigensystem(G, k, e2, U2, opt);
for(auto& x : e2) x = -1.0/x;
k += increment(deltay, 1, dir);
Green_eigensystem(G, k, e3, U3, opt);
for(auto& x : e3) x = -1.0/x;
k += increment(deltax, 2, dir);
Green_eigensystem(G, k, e4, U4, opt);
for(auto& x : e4) x = -1.0/x;
gauge_field(U1, U2, u1);
gauge_field(U2, U3, u2);
gauge_field(U3, U4, u3);
gauge_field(U4, U1, u4);
Complex z(1.0);
if (orb==0){
for(int b=0; b<ng; b++){
if(e1[b]+e2[b]+e3[b]+e4[b] < 0.0) z *= u1[b]*u2[b]*u3[b]*u4[b];
}
}
else{
int b = orb-1;
z *= u1[b]*u2[b]*u3[b]*u4[b];
}
double phase = arg(z);
if(abs(phase) > threshold && recursive){
phase = 0.0;
double dx = deltax/2;
double dy = deltay/2;
k = k1;
phase += Berry_plaquette(G, k, dx, dy, opt, dir, orb);
k += increment(dx, 0, dir);
phase += Berry_plaquette(G, k, dx, dy, opt, dir, orb);
k += increment(dy, 1, dir);
phase += Berry_plaquette(G, k, dx, dy, opt, dir, orb);
k += increment(dx, 2, dir);
phase += Berry_plaquette(G, k, dx, dy, opt, dir, orb);
}
return phase;
}
vector<double> lattice_model_instance::Berry_curvature(vector3D<double>& k1, vector3D<double>& k2, int nk, int orb, bool rec, int dir)
{
recursive = rec;
if(model->spatial_dimension < 2) qcm_throw("'Berry_curvature' can only be applied to a 2D or 3D Brillouin zone!");
if(nk > MAX_K_SIDE) qcm_throw("The wavevector grid has too many points. nk should be <= "+to_string<int>(MAX_K_SIDE));
if(nk < 10) qcm_throw("The wavevector grid has too few points. nk should be >= 10");
int opt=0;
if(global_bool("dual_basis")) opt += 1;
if(global_char("periodization") == 'N') opt += 2;
vector<double> B(nk*nk, 0.0);
vector3D<double> delta1;
vector3D<double> delta2;
double d1, d2;
if(dir==1){
if(fabs(k1.x-k2.x)>1e-10) qcm_throw("in Berry curvature, k1 and k2 must have the same 3rd component");
d1 = (k2.y-k1.y)/nk;
d2 = (k2.z-k1.z)/nk;
delta1.y = d1;
delta2.z = d2;
}
else if(dir==2){
if(fabs(k1.y-k2.y)>1e-10) qcm_throw("in Berry curvature, k1 and k2 must have the same 3rd component");
d1 = (k2.x-k1.x)/nk;
d2 = (k2.z-k1.z)/nk;
delta1.x = d1;
delta2.z = d2;
}
else{
if(fabs(k1.z-k2.z)>1e-10) qcm_throw("in Berry curvature, k1 and k2 must have the same 3rd component");
d1 = (k2.x-k1.x)/nk;
d2 = (k2.y-k1.y)/nk;
delta1.x = d1;
delta2.y = d2;
}
double eta = global_double("eta");
Green_function G = cluster_Green_function(Complex(0, eta), false, false);
// loop over the plaquettes
// #pragma omp parallel for
for(int j=0; j<nk; j++){
for(int i=0; i<nk; i++){
B[i+nk*j] = Berry_plaquette(G, k1 + i*delta1 + j*delta2, d1, d2, opt, dir, orb);
}
}
if(model->mixing == HS_mixing::up_down){
G = cluster_Green_function(Complex(0, 1e-8), false, true);
for(int j=0; j<nk; j++){
for(int i=0; i<nk; i++){
B[i+nk*j] = Berry_plaquette(G, k1 + i*delta1 + j*delta2, d1, d2, opt, dir, orb);
}
}
}
else if(model->mixing == HS_mixing::normal) B *= 2.0;
// else if(model->mixing == HS_mixing::full) B *= 0.5;
B *= -1.0/(2*M_PI*d1*d2);
return B;
}
double lattice_model_instance::monopole_part(vector3D<double>& k, double a, int nk, int orb, bool rec, int dir, bool spin_down)
{
recursive = rec;
int opt=0;
double charge = 0.0;
double d = 2*a/nk;
double eta = global_double("eta");
// double eta = 1e-6;
Green_function G = cluster_Green_function(Complex(0, eta), false, spin_down);
// loop over the plaquettes
// #pragma omp parallel for
for(int j=0; j<nk; j++){
for(int i=0; i<nk; i++){
vector3D<double>kb = k;
switch(dir){
case 1 :
kb.x += a;
kb.y += i*d - a;
kb.z += j*d - a;
break;
case -1 :
kb.x -= a;
kb.y -= i*d - a;
kb.z -= j*d - a;
break;
case 2 :
kb.y += a;
kb.z += i*d - a;
kb.x += j*d - a;
break;
case -2 :
kb.y -= a;
kb.z -= i*d - a;
kb.x -= j*d - a;
break;
case 3 :
kb.z += a;
kb.x += i*d - a;
kb.y += j*d - a;
break;
case -3 :
kb.z -= a;
kb.x -= i*d - a;
kb.y -= j*d - a;
break;
}
charge += Berry_plaquette(G, kb, d, d, opt, dir, orb);
}
}
return charge;
}
double lattice_model_instance::monopole(vector3D<double>& k, double a, int nk, int orb, bool rec)
{
recursive = rec;
if(model->spatial_dimension != 3) qcm_throw("'monopole' can only be applied to a 3D model!");
if(nk > 1000) qcm_throw("The wavevector grid has too many points. nk should be <= 1000");
if(nk < 2) qcm_throw("The wavevector grid has too few points. nk should be >= 2");
double charge = 0.0;
charge += monopole_part(k, a, nk, orb, rec, 1, false);
charge += monopole_part(k, a, nk, orb, rec,-1, false);
charge += monopole_part(k, a, nk, orb, rec, 2, false);
charge += monopole_part(k, a, nk, orb, rec,-2, false);
charge += monopole_part(k, a, nk, orb, rec, 3, false);
charge += monopole_part(k, a, nk, orb, rec,-3, false);
if(model->mixing == HS_mixing::up_down){
charge += monopole_part(k, a, nk, orb, rec, 1, true);
charge += monopole_part(k, a, nk, orb, rec,-1, true);
charge += monopole_part(k, a, nk, orb, rec, 2, true);
charge += monopole_part(k, a, nk, orb, rec,-2, true);
charge += monopole_part(k, a, nk, orb, rec, 3, true);
charge += monopole_part(k, a, nk, orb, rec,-3, true);
}
else if(model->mixing == HS_mixing::normal) charge *= 2.0;
charge *= -1.0/(2*M_PI);
return charge;
}