Program Listing for File lattice_model.cpp¶
↰ Return to documentation for file (src_qcm/lattice_model.cpp)
//
// lattice_model.cpp
// qcm3
//
// Created by David Sénéchal on 16-11-21.
// Copyright © 2016 David Sénéchal. All rights reserved.
//
#include <iostream>
#include <fstream>
#include "lattice_model.hpp"
#include "qcm_ED.hpp"
#include "QCM.hpp"
#include "parser.hpp"
#include "Green_function.hpp"
#define MAX_GENERATORS 4
size_t Green_function_k::dim_GF;
size_t Green_function_k::dim_reduced_GF;
size_t lattice_index_pair::Nc;
bool lattice_model::model_consolidated = false;
//==============================================================================
lattice_model::lattice_model() : is_closed(false), bath_exists(false)
{
dim_reduced_GF=0;
dim_GF=0;
Lc = 0;
n_band = 0;
spatial_dimension = 0;
mixing = HS_mixing::normal;
}
//===============================================================================
void lattice_model::pre_operator_consolidate()
{
//..............................................................................
// defining the Pauli matrices
pauli.assign(4, vector<vector<Complex>>(2,vector<Complex>(2,0.0)));
pauli[0][0][0] = pauli[0][1][1] = 1.0;
pauli[1][0][1] = pauli[1][1][0] = 1.0;
pauli[2][0][1] = Complex(0,-1.0);
pauli[2][1][0] = Complex(0,1.0);
pauli[3][0][0] = 1.0;
pauli[3][1][1] = -1.0;
//..............................................................................
// permuting the axes so that the repeated directions are the lowest
// happens when superlattice.D = 1 and superlattice.e[0] is along the z axis
if(superlattice.D == 1 and superlattice.e[0].z != 0){
for(int i=0; i<3; ++i) superlattice.e[i].cyclic();
for(int i=0; i<3; ++i) phys.e[i].cyclic();
for(int i=0; i<3; ++i) unit_cell.e[i].cyclic();
}
//..............................................................................
// adjusting the physical basis and constructing its dual
phys.inverse();
phys.dual(physdual);
physdual.init();
superlattice.dual(superdual);
superdual.init();
unit_cell.dual(dual);
dual.init();
//..............................................................................
// building the position map (i.e., the reverse of the array 'sites')
for(size_t i=0; i< sites.size(); i++){
position_map[sites[i].position] = i;
auto [R, S] = superlattice.fold(sites[i].position); // r = R + S, R in superlattice, S in SUC
folded_position_map[S] = i;
}
//..............................................................................
// setting the lattice orbital labels
vector<vector3D<int64_t>> orb_set;
for(size_t i=0; i< sites.size(); i++){
auto [R, S] = unit_cell.fold(sites[i].position); // r = R + S, R in lattice, S in unit cell
int b=0;
for(b=0; b<orb_set.size(); ++b) if(S == orb_set[b]) break;
if(b==orb_set.size()) orb_set.push_back(S);
sites[i].orb = b;
}
n_band = orb_set.size();
lattice_index_pair::Nc = sites.size();
Lc = sites.size()/n_band;
neighbor.push_back(vector3D<int64_t>(0,0,0));
neighbor_census();
if(clusters.size() == 1) prepare_tiling_data();
spatial_dimension = (int)superlattice.D;
//..............................................................................
// computing sys_start and nsys for clusters
nsys = systems.size();
for(size_t s=0; s <nsys; s++) clusters[systems[s].clus].nsys++;
clusters[0].sys_start = 0;
for(size_t c=1; c <clusters.size(); c++) clusters[c].sys_start = clusters[c-1].sys_start + clusters[c-1].nsys;
add_chemical_potential();
}
//===============================================================================
int lattice_model::neighbor_index(vector3D<int64_t> &R){
int ni;
for(ni=0; ni < neighbor.size(); ++ni) if(R == neighbor[ni]) break;
if(ni==neighbor.size()) neighbor.push_back(R);
return ni;
}
//===============================================================================
std::optional<neighbor_match> lattice_model::find_second_site(int s1, const vector3D<int64_t>& link)
{
// folding the first site within the SUC
auto [R1, S1] = superlattice.fold(sites[s1].position); // folds r into the SUC
vector3D<int64_t> r = S1;
r += link; // adding the link to the position
// finding the second lattice site s2 and the neighbor link R
auto [R2, S2] = superlattice.fold(r); // folds r into the SUC
auto it_site2 = folded_position_map.find(S2);
if(it_site2==folded_position_map.end()) return std::nullopt; // empty site (e.g. graphene). skip.
int s2 = (int)it_site2->second;
R2 += sites[s1].position - S1 - sites[s2].position + S2; // neighbor link (corrected for shift into conventional unit cell)
int ni = neighbor_index(R2);
R2 *= -1;
int ni_opp = neighbor_index(R2);
return neighbor_match{s2, ni, ni_opp};
}
//===============================================================================
void lattice_model::neighbor_census()
{
size_t Ns = sites.size();
for(size_t i1 = 0; i1 < Ns; ++i1){
for(size_t i2 = 0; i2 < Ns; ++i2){
vector3D<int64_t> delta = sites[i2].position - sites[i1].position;
for(size_t s = 0; s < Ns; ++s){
find_second_site((int)s, delta); // neighbor_index (called inside) handles deduplication
}
}
}
}
//===============================================================================
void lattice_model::prepare_tiling_data()
{
size_t ns = clusters[0].n_sites;
// Build one group per distinct displacement d, seeding with intra-cluster pairs
map<vector3D<int64_t>, tile_group> grp_map;
for(size_t s1 = 0; s1 < ns; ++s1)
for(size_t s2 = 0; s2 < ns; ++s2)
grp_map[sites[s2].position - sites[s1].position].entries.push_back({s1, s2, 0});
// For each displacement, add inter-cluster entries (n>0) from all sites
for(auto& [d, grp] : grp_map){
grp.n_intra = grp.entries.size(); // all entries so far are intra (n=0)
for(size_t s = 0; s < ns; ++s){
auto match = find_second_site((int)s, d);
if(!match) continue; // no site at that position (non-Bravais cluster)
if(match->ni == 0) continue; // intra-cluster, already in the list
grp.entries.push_back({s, (size_t)match->s2, (size_t)match->ni});
}
}
tiling_data.reserve(grp_map.size());
for(auto& kv : grp_map)
tiling_data.push_back(std::move(kv.second));
}
//===============================================================================
matrix<Complex> lattice_model::compact_tiling(const matrix<Complex>& A, const vector3D<double>& k)
{
QCM_ASSERT(clusters.size() == 1);
size_t ns = clusters[0].n_sites;
size_t nb = dim_GF / ns; // number of spin/Nambu blocks
// phase[n] = exp(i * k * neighbor[n] * 2*pi)
vector<Complex> phase(neighbor.size());
for(size_t n = 0; n < neighbor.size(); ++n){
double z = k * neighbor[n] * 2 * M_PI;
phase[n] = Complex(cos(z), sin(z));
}
matrix<Complex> Act(dim_GF);
for(size_t b1 = 0; b1 < nb; ++b1){
for(size_t b2 = 0; b2 < nb; ++b2){
for(auto& grp : tiling_data){
// Step 1: average A over intra-cluster (n=0) entries for this displacement
Complex avg = 0.0;
double iave = 1.0/grp.n_intra;
if(global_bool("compact_tiling_per_site")) iave = 1.0/ns;
for(size_t i = 0; i < grp.n_intra; ++i){
auto& e = grp.entries[i];
avg += A(e[0] + b1*ns, e[1] + b2*ns);
}
avg *= iave;
// Step 2: add avg*phase[n] for all entries (intra: phase=1, inter: Bloch factor)
for(auto& e : grp.entries)
Act(e[0] + b1*ns, e[1] + b2*ns) += avg * phase[e[2]];
}
}
}
return Act;
}
void lattice_model::add_chemical_potential()
{
auto tmp = make_shared<lattice_operator>(*this, "mu", latt_op_type::one_body);
term[tmp->name] = tmp;
tmp->is_active = true;
for(int s=0; s<sites.size(); ++s){
tmp->elements.push_back({s,0,s,0,0,-1.0});
tmp->elements.push_back({s,1,s,1,0,-1.0});
}
}
//===============================================================================
void lattice_model::add_anomalous_elements(vector<lattice_matrix_element>& E, int s1, int s2, int ni, int ni_opp, Complex z, latt_op_type SC)
{
switch (SC) {
case latt_op_type::one_body: // useful for normal charge density waves
if(ni==0){
E.push_back({s1, 0, s2, 0, ni, z});
E.push_back({s1, 1, s2, 1, ni, z});
E.push_back({s2, 0, s1, 0, ni, conjugate(z)});
E.push_back({s2, 1, s1, 1, ni, conjugate(z)});
}
else{
E.push_back({s1, 0, s2, 0, ni, z});
E.push_back({s1, 1, s2, 1, ni, z});
E.push_back({s2, 0, s1, 0, ni_opp, conjugate(z)});
E.push_back({s2, 1, s1, 1, ni_opp, conjugate(z)});
}
break;
case latt_op_type::singlet:
if(ni==0){
E.push_back({s1, 0, s2, 1, 0, z});
E.push_back({s1, 1, s2, 0, 0, -z});
if(s1 != s2){
E.push_back({s2, 1, s1, 0, 0, -z});
E.push_back({s2, 0, s1, 1, 0, z});
}
}
else{
E.push_back({s1, 0, s2, 1, ni_opp, z});
E.push_back({s1, 1, s2, 0, ni_opp, -z});
E.push_back({s2, 1, s1, 0, ni, -z});
E.push_back({s2, 0, s1, 1, ni, z});
}
break;
case latt_op_type::dz:
E.push_back({s1, 0, s2, 1, ni_opp, -z});
E.push_back({s1, 1, s2, 0, ni_opp, -z});
E.push_back({s2, 1, s1, 0, ni, z});
E.push_back({s2, 0, s1, 1, ni, z});
break;
case latt_op_type::dy:
if(s1 == s2 and ni==0) qcm_throw("triplet SC must be based on different sites");
E.push_back({s1, 0, s2, 0, ni_opp, z*complex<double>(0,1.0)});
E.push_back({s1, 1, s2, 1, ni_opp, z*complex<double>(0,1.0)});
E.push_back({s2, 0, s1, 0, ni, -z*complex<double>(0,1.0)});
E.push_back({s2, 1, s1, 1, ni, -z*complex<double>(0,1.0)});
break;
case latt_op_type::dx:
if(s1 == s2 and ni==0) qcm_throw("triplet SC must be based on different sites");
E.push_back({s1, 0, s2, 0, ni_opp, z});
E.push_back({s1, 1, s2, 1, ni_opp, -z});
E.push_back({s2, 0, s1, 0, ni, -z});
E.push_back({s2, 1, s1, 1, ni, z});
break;
default:
qcm_throw("SC case not implemented yet.");
break;
}
}
//===============================================================================
void lattice_model::close_model(bool force)
{
if(is_closed and !force) return;
is_closed = true;
// adjusting the norms of all operators defined so that they are mulitplied by 1/L
for(auto& X : term) X.second->norm = 1.0/sites.size();
term["mu"]->norm = -1.0/sites.size();
for(auto& X : term) X.second->close();
}
//===============================================================================
void lattice_model::post_parameter_consolidate(size_t label)
{
//..............................................................................
// converting the neighbors to the superlattice basis, for future use
if(model_consolidated){
qcm_throw("the lattice model can only be consolidated once!");
}
model_consolidated = true;
for(auto& i: neighbor){
vector3D<double> ne = superlattice.to(i);
i.x = (int)floor(ne.x+0.5);
i.y = (int)floor(ne.y+0.5);
i.z = (int)floor(ne.z+0.5);
}
// indexing the opposites
opposite_neighbor.assign(neighbor.size(), 0);
for(size_t i=1; i<neighbor.size(); i++){
vector3D<int64_t> opposite = neighbor[i]*(-1);
for(size_t j=1; j<neighbor.size(); j++){
if(neighbor[j] == opposite){
opposite_neighbor[i] = j;
opposite_neighbor[j] = i;
break;
}
}
}
//..............................................................................
// building the list of NN bonds, for eventual use
bonds.reserve(3*sites.size());
for(size_t i=0; i<sites.size(); i++){
for(size_t j=0; j<i; j++){
if(sites[i].cluster != sites[j].cluster) continue;
vector3D<int64_t> iR = sites[i].position - sites[j].position;
vector3D<double> R = phys.to(vector3D<double>(iR.x, iR.y, iR.z));
if(R*R < 1.1) bonds.push_back(pair<int, int>(i,j));
}
}
//..............................................................................
// erasing the unused operators
auto it = term.begin();
while(it != term.end()){
if(it->first == "mu") it->second->is_active = true;
if(it->second->is_active == false){
term.erase(it++);
}
else ++it;
}
//..............................................................................
// filling the list of one-body or anomalous operators
one_body_ops.reserve(term.size());
for(auto& x : term){
if(x.second->is_interaction == false) one_body_ops.push_back(x.second);
}
//..............................................................................
// finds the common mixing
mixing = HS_mixing::normal;
for(size_t s=0; s<nsys; s++){
clusters[systems[s].clus].mixing |= ED::mixing(s+label*nsys);
mixing |= clusters[systems[s].clus].mixing;
}
if(mixing > 5) mixing = mixing & HS_mixing::full;
if(mixing == 5) mixing = HS_mixing::anomalous;
n_mixed = 1;
if(mixing & HS_mixing::anomalous) n_mixed *= 2;
if(mixing & HS_mixing::spin_flip) n_mixed *= 2;
//..............................................................................
// computes offsets and dimensions
dim_GF = n_mixed*sites.size();
dim_reduced_GF = n_mixed*n_band;
Green_function_k::dim_GF = dim_GF;
Green_function_k::dim_reduced_GF = dim_reduced_GF;
clusters[0].offset = 0;
for(size_t i=1; i<clusters.size(); i++) clusters[i].offset = clusters[i-1].offset + clusters[i-1].n_sites;
GF_dims.resize(clusters.size());
for(size_t i=0; i<clusters.size(); i++) GF_dims[i] = n_mixed*clusters[i].n_sites;
GF_offset.resize(clusters.size());
GF_offset[0] = 0;
for(size_t i=1; i<clusters.size(); i++) GF_offset[i] = GF_offset[i-1] + GF_dims[i-1];
// build GF matrix elements for lattice operators
for(auto& op : term) one_body_matrix(*op.second);
// builds the array reduced_Green_index
reduced_Green_index.resize(dim_GF);
Green_to_position.resize(dim_GF);
for(size_t i = 0; i<sites.size(); i++){
size_t off = GF_offset[sites[i].cluster];
size_t ic = sites[i].index_within_cluster;
size_t ns = clusters[sites[i].cluster].n_sites;
switch(mixing){
case HS_mixing::normal:
case HS_mixing::up_down:
reduced_Green_index[off+ic] = sites[i].orb;
Green_to_position[off+ic] = superlattice.to(sites[i].position);
break;
case HS_mixing::spin_flip:
case HS_mixing::anomalous:
reduced_Green_index[off+ic] = sites[i].orb;
reduced_Green_index[off+ic+ns] = sites[i].orb + n_band;
Green_to_position[off+ic] = superlattice.to(sites[i].position);
Green_to_position[off+ic+ns] = Green_to_position[off+ic];
break;
case HS_mixing::full:
reduced_Green_index[off+ic] = sites[i].orb;
reduced_Green_index[off+ic+ns] = sites[i].orb + n_band;
reduced_Green_index[off+ic+2*ns] = sites[i].orb + 2*n_band;
reduced_Green_index[off+ic+3*ns] = sites[i].orb + 3*n_band;
Green_to_position[off+ic] = superlattice.to(sites[i].position);
Green_to_position[off+ic+ns] = Green_to_position[off+ic];
Green_to_position[off+ic+2*ns] = Green_to_position[off+ic];
Green_to_position[off+ic+3*ns] = Green_to_position[off+ic];
break;
}
}
//..............................................................................
// computing is_nambu_index
is_nambu_index.assign(dim_GF,0);
for(size_t i = 0; i<sites.size(); i++){
size_t off = GF_offset[sites[i].cluster];
size_t ns = clusters[sites[i].cluster].n_sites;
switch(mixing){
case HS_mixing::normal:
case HS_mixing::up_down:
case HS_mixing::spin_flip:
break;
case HS_mixing::anomalous:
for(size_t j=0; j<ns; j++) is_nambu_index[off+j+ns] = 1;
break;
case HS_mixing::full:
for(size_t j=0; j<2*ns; j++) is_nambu_index[off+j+2*ns] = 1;
break;
}
}
//..............................................................................
// reading the external hybridization, if applicable
if(hybrid_file.empty() == false){
hybrid = make_shared<lattice_hybrid>(hybrid_file);
if(hybrid->mixing==0){
if(n_mixed*hybrid->d != dim_GF) qcm_throw("incorrect dimension of the external hybridization matrix");
}
else if(hybrid->mixing==1){
if(mixing != 1) qcm_throw("External hybridization matrix has mixing = 1, so should the lattice model!");
if(hybrid->d != dim_GF) qcm_throw("incorrect dimension of the external hybridization matrix");
}
}
}
//===============================================================================
size_t lattice_model::to_GF_index(size_t I, size_t spin, bool anomalous, bool spin_down)
{
size_t clusterI = sites[I].cluster;
size_t J = sites[I].index_within_cluster;
switch(mixing){
case 0:
break;
case HS_mixing::spin_flip:
if(spin) J += clusters[clusterI].n_sites;
break;
case HS_mixing::anomalous:
if(spin) J += clusters[clusterI].n_sites;
break;
case HS_mixing::full:
if(spin) J += clusters[clusterI].n_sites;
if(anomalous) J += 2*clusters[clusterI].n_sites;
break;
case HS_mixing::up_down:
break;
}
return GF_offset[clusterI]+J;
}
//===============================================================================
void lattice_model::one_body_matrix(lattice_operator& op)
{
if(!op.is_interaction && !(op.mixing&HS_mixing::anomalous)){ // one-body terms
switch(mixing){
case HS_mixing::normal:
for(auto& x : op.elements){
if(x.spin1) continue;
size_t I = to_GF_index(x.site1, x.spin1, false, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor) op.IGF_elem.push_back({I, J, x.neighbor, x.v});
else op.GF_elem.push_back({I, J, x.v});
}
break;
case HS_mixing::spin_flip:
for(auto& x : op.elements){
size_t I = to_GF_index(x.site1, x.spin1, false, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor) op.IGF_elem.push_back({I, J, x.neighbor, x.v});
else op.GF_elem.push_back({I, J, x.v});
}
break;
case HS_mixing::anomalous:
for(auto& x : op.elements){
if(x.spin1==0){
size_t I = to_GF_index(x.site1, x.spin1, false, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor) op.IGF_elem.push_back({I, J, x.neighbor, x.v});
else op.GF_elem.push_back({I, J, x.v});
}
else{
size_t I = to_GF_index(x.site1, x.spin1, true, false);
size_t J = to_GF_index(x.site2, x.spin2, true, false);
if(x.neighbor) op.IGF_elem.push_back({J, I, opposite_neighbor[x.neighbor], -x.v});
else op.GF_elem.push_back({J, I, -x.v});
}
}
break;
case HS_mixing::full:
for(auto& x : op.elements){
size_t I = to_GF_index(x.site1, x.spin1, false, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor) op.IGF_elem.push_back({I, J, x.neighbor, x.v});
else op.GF_elem.push_back({I, J, x.v});
}
for(auto& x : op.elements){
size_t I = to_GF_index(x.site1, x.spin1, true, false);
size_t J = to_GF_index(x.site2, x.spin2, true, false);
if(x.neighbor) op.IGF_elem.push_back({J, I, opposite_neighbor[x.neighbor], -x.v});
else op.GF_elem.push_back({J, I, -x.v});
}
break;
case HS_mixing::up_down:
for(auto& x : op.elements){
if(x.spin1) continue;
size_t I = to_GF_index(x.site1, x.spin1, false, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor) op.IGF_elem.push_back({I, J, x.neighbor, x.v});
else op.GF_elem.push_back({I, J, x.v});
}
for(auto& x : op.elements){
if(!x.spin1) continue;
size_t I = to_GF_index(x.site1, x.spin1, false, true);
size_t J = to_GF_index(x.site2, x.spin2, false, true);
if(x.neighbor) op.IGF_elem_down.push_back({I, J, x.neighbor, x.v});
else op.GF_elem_down.push_back({I, J, x.v});
}
break;
}
}
else if(op.mixing&HS_mixing::anomalous){
switch(mixing){
case HS_mixing::normal:
case HS_mixing::spin_flip:
case HS_mixing::up_down:
break;
case HS_mixing::anomalous:
for(auto& x : op.elements){
if(x.spin1==0 or x.spin2==1) continue;
size_t I = to_GF_index(x.site1, x.spin1, true, false); // first index is Nambu
size_t J = to_GF_index(x.site2, x.spin2, false, false); // second index is normal
if(x.neighbor){
op.IGF_elem.push_back({I, J, opposite_neighbor[x.neighbor], 2.0*x.v});
op.IGF_elem.push_back({J, I, x.neighbor, 2.0*conjugate(x.v)}); // hermitian conjugate
}
else{
op.GF_elem.push_back({I, J, 2.0*x.v});
op.GF_elem.push_back({J, I, 2.0*conjugate(x.v)}); // hermitian conjugate
}
}
break;
case HS_mixing::full:
for(auto& x : op.elements){
size_t I = to_GF_index(x.site1, x.spin1, true, false);
size_t J = to_GF_index(x.site2, x.spin2, false, false);
if(x.neighbor){
op.IGF_elem.push_back({I, J, opposite_neighbor[x.neighbor], x.v});
op.IGF_elem.push_back({J, I, x.neighbor, conjugate(x.v)}); // hermitian conjugate
}
else{
op.GF_elem.push_back({I, J, x.v});
op.GF_elem.push_back({J, I, conjugate(x.v)}); // hermitian conjugate
}
I = to_GF_index(x.site2, x.spin2, true, false);
J = to_GF_index(x.site1, x.spin1, false, false);
if(x.neighbor){
op.IGF_elem.push_back({I, J, x.neighbor, -x.v});
op.IGF_elem.push_back({J, I, opposite_neighbor[x.neighbor], conjugate(-x.v)}); // hermitian conjugate
}
else{
op.GF_elem.push_back({I, J, -x.v});
op.GF_elem.push_back({J, I, conjugate(-x.v)}); // hermitian conjugate
}
}
break;
}
}
else return;
}
//===============================================================================
matrix<Complex> lattice_model::periodize(const vector3D<double> &k, matrix<Complex> &mat)
{
double L1 = 1.0/Lc;
matrix<Complex> gn(dim_reduced_GF);
for(size_t i1 = 0; i1 < dim_GF; i1++){
size_t I1 = reduced_Green_index[i1];
for(size_t i2 = 0; i2 < dim_GF; i2++){
size_t I2 = reduced_Green_index[i2];
double phi = k*(Green_to_position[i2] - Green_to_position[i1])*2*M_PI;
Complex g = Complex(cos(phi), sin(phi));
gn(I1,I2) += mat(i1,i2)*g*L1;
}
}
return gn;
}
//===============================================================================
vector<Complex> lattice_model::periodize(const vector3D<double> &k, vector<Complex> &mat)
{
vector<Complex> gn(dim_reduced_GF);
for(size_t i1 = 0; i1 < dim_GF; i1++){
size_t I1 = reduced_Green_index[i1];
double phi = k*Green_to_position[i1]*2*M_PI;
Complex g = Complex(cos(phi), sin(-phi));
gn[I1] += mat[i1]*g;
}
gn *= 1.0/Lc;
return gn;
}
//===============================================================================
void lattice_model::print(ostream &fout, bool asy_operators, bool asy_labels, bool asy_orb, bool asy_neighbors, bool asy_working_basis)
{
banner('=', "clusters", fout);
fout << "No\tn_sites\tposition\tref.\tsystems\n";
for(int c=0; c<clusters.size(); c++){
cluster& C = clusters[c];
fout << c+1 << '\t' << C.n_sites << '\t' << C.position << '\t' << C.ref << '\t' << C.conj;
for(int s=0; s<C.nsys; s++) fout << systems[s+C.sys_start].name << ", ";
fout << endl;
}
banner('=', "sites", fout);
fout << "No\tcluster\tNo in cluster\torbital\tposition\n";
for(int i=0; i<sites.size(); i++){
site& s = sites[i];
fout << i+1 << '\t' << s.cluster+1 << '\t' << s.index_within_cluster+1 << '\t' << s.orb+1 << '\t' << s.position << endl;
}
banner('.', "NN bonds", fout);
fout << "No\tcluster\tNo in cluster\torbital\tposition\n";
for(auto& b : bonds){
fout << b.first+1 << '\t' << sites[b.first].position << '\t' << b.second+1 << '\t' << sites[b.second].position << endl;
}
banner('.', "bases", fout);
fout << "dual:\n" << dual << endl;
fout << "phys:\n" << phys << endl;
fout << "physdual:\n" << physdual << endl;
fout << "superdual:\n" << superdual << endl;
fout << "superlattice:\n" << superlattice << endl;
fout << "unit_cell:\n" << unit_cell << endl;
banner('.', "neighbors", fout);
for(size_t i=1; i<neighbor.size(); i++) fout << i << " : " << neighbor[i] << endl;
banner('=', "lattice operators", fout);
for(auto& x : term){
lattice_operator& h = *x.second;
fout << '\n' << h.name << '\t';
if(h.type == latt_op_type::one_body) fout << "(one-body)";
else if(h.type == latt_op_type::singlet) fout << "(singlet)";
else if(h.type == latt_op_type::dx) fout << "(dx)";
else if(h.type == latt_op_type::dy) fout << "(dy)";
else if(h.type == latt_op_type::dz) fout << "(dz)";
else if(h.type == latt_op_type::Hubbard) fout << "(Hubbard or extended Hubbard)";
else if(h.type == latt_op_type::Hund) fout << "(Hund)";
else if(h.type == latt_op_type::Heisenberg) fout << "(Heisenberg)";
else if(h.type == latt_op_type::X) fout << "(spin X)";
else if(h.type == latt_op_type::Y) fout << "(spin Y)";
else if(h.type == latt_op_type::Z) fout << "(spin Z)";
fout << "\tnorm = " << h.norm;
if(fabs(h.nambu_correction)>SMALL_VALUE) fout << "\tNambu correction = " << h.nambu_correction;
if(fabs(h.nambu_correction_full)>SMALL_VALUE) fout << "\tNambu correction (full) = " << h.nambu_correction_full;
fout << endl;
fout << "elements (primitive form):\n";
for(auto &x : h.elements) fout << x << endl;
if(h.is_interaction) continue;
fout << "elements (in Green function basis):\n";
for(auto &x : h.GF_elem){
if(fabs(x.v.imag()) < SMALL_VALUE) fout << '[' << x.r+1 << ',' << x.c+1 << "] : " << x.v.real() << endl;
else fout << '[' << x.r+1 << ',' << x.c+1 << "] : " << x.v << endl;
}
for(auto &x : h.IGF_elem){
if(fabs(x.v.imag()) < SMALL_VALUE) fout << '[' << x.r+1 << ',' << x.c+1 << ',' << neighbor[x.n] << "] : " << x.v.real() << endl;
else fout << '[' << x.r+1 << ',' << x.c+1 << ',' << neighbor[x.n] << "] : " << x.v << endl;
}
if(h.GF_elem_down.size()) fout << "elements (in Green function basis, spin down):\n";
for(auto &x : h.GF_elem_down){
if(fabs(x.v.imag()) < SMALL_VALUE) fout << '[' << x.r+1 << ',' << x.c+1 << "] : " << x.v.real() << endl;
else fout << '[' << x.r+1 << ',' << x.c+1 << "] : " << x.v << endl;
}
for(auto &x : h.IGF_elem_down){
if(fabs(x.v.imag()) < SMALL_VALUE) fout << '[' << x.r+1 << ',' << x.c+1 << ',' << neighbor[x.n] << "] : " << x.v.real() << endl;
else fout << '[' << x.r+1 << ',' << x.c+1 << ',' << neighbor[x.n] << "] : " << x.v << endl;
}
}
banner('*', "cluster models", fout);
ED::print_models(fout);
}
//===============================================================================
pair<string, int> lattice_model::name_and_label(string &S, bool cut)
{
int label = 0;
string name(S);
size_t pos = name.rfind('_');
if(pos != string::npos){
label = from_string<int>(name.substr(pos+1));
if(cut) name.erase(pos);
}
if(label > nsys) qcm_throw("system label in "+S+" is outside of range");
if(label == 0 and term.find(name) == term.end()) qcm_throw("operator "+name+" does not exist in model");
if(term.find(name) != term.end()){
if(term.at(name)->is_density_wave and label>0 and cut) name += '@' + to_string(label);
}
return {name,label};
}
//===============================================================================
void lattice_model::interaction_operator(const string &name, vector3D<int64_t> &link, double amplitude, int b1, int b2, const string &type)
{
shared_ptr<lattice_operator> tmp_op;
// static string previous_name("");
if(auto it_op = term.find(name); it_op == term.end()){
check_name(name);
tmp_op = make_shared<lattice_operator>(*this, name, latt_op_type::Hubbard);
tmp_op->is_interaction = true;
term[name] = tmp_op;
if(type == "Hund") tmp_op->type = latt_op_type::Hund;
else if(type == "Heisenberg") tmp_op->type = latt_op_type::Heisenberg;
else if(type == "X") tmp_op->type = latt_op_type::X;
else if(type == "Y") tmp_op->type = latt_op_type::Y;
else if(type == "Z") tmp_op->type = latt_op_type::Z;
}
else{
tmp_op = it_op->second;
}
// looping over sites of the super unit cell
for(int s1=0; s1 < (int)sites.size(); s1++){
if(sites[s1].orb != b1) continue;
auto match = find_second_site(s1, link);
if(!match) continue;
auto [s2, ni, ni_opp] = *match;
if(sites[s2].orb != b2) continue; // wrong orbital. skip.
switch(tmp_op->type){
case latt_op_type::Hubbard:
if(s1==s2)
tmp_op->elements.push_back({s1, 0, s2, 1, ni, amplitude});
else{
tmp_op->elements.push_back({s1, 0, s2, 0, ni, amplitude});
tmp_op->elements.push_back({s1, 1, s2, 0, ni, amplitude});
tmp_op->elements.push_back({s1, 0, s2, 1, ni, amplitude});
tmp_op->elements.push_back({s1, 1, s2, 1, ni, amplitude});
}
break;
case latt_op_type::Hund:
case latt_op_type::Heisenberg:
case latt_op_type::X:
case latt_op_type::Y:
case latt_op_type::Z:
QCM_ASSERT(s1 != s2);
tmp_op->elements.push_back({s1, 0, s2, 0, ni, amplitude});
break;
default:
break;
}
}
}
//===============================================================================
void lattice_model::hopping_operator(const string &name, vector3D<int64_t> &link, double amplitude, int b1, int b2, int tau, int sigma)
{
shared_ptr<lattice_operator> tmp_op;
// static string previous_name("");
if(auto it_op = term.find(name); it_op == term.end()){
check_name(name);
tmp_op = make_shared<lattice_operator>(*this, name, latt_op_type::one_body);
term[name] = tmp_op;
}
else{
tmp_op = it_op->second;
}
QCM_ASSERT(tau>=0 and tau<=3 and sigma >=0 and sigma <=3);
if(tau==0 and !link.is_null()) qcm_throw("Problem with the definition of " + tmp_op->name+ ". Cannot define an operator with nonzero link AND matrix tau = 0");
if(tau!=0 and link.is_null()) qcm_throw("Problem with the definition of " + tmp_op->name + ". Cannot define an operator with on-site operator AND matrix tau != 0");
if(tau==3) qcm_throw("Problem with the definition of " + tmp_op->name + ". Cannot define an operator with matrix tau = 3");
// looping over sites of the super unit cell
for(int s1=0; s1 < (int)sites.size(); s1++){
if(sites[s1].orb != b1) continue;
auto match = find_second_site(s1, link);
if(!match) continue;
auto [s2, ni, ni_opp] = *match;
if(sites[s2].orb != b2) continue; // wrong orbital. skip.
if(tau == 0){
for(int alpha=0; alpha<2; alpha++){
for(int beta=0; beta<2; beta++){
Complex ec = pauli[sigma][alpha][beta]*amplitude;
if(ec.imag() == 0.0 and ec.real() == 0.0) continue;
tmp_op->elements.push_back({s1,alpha,s1,beta,ni,ec});
}
}
}
else{
for(int i=0; i<2; i++){
int si = s1+i*(s2-s1);
for(int j=0; j<2; j++){
int sj = s1+j*(s2-s1);
for(int alpha=0; alpha<2; alpha++){
for(int beta=0; beta<2; beta++){
Complex ec = pauli[tau][i][j]*pauli[sigma][alpha][beta]*amplitude;
if(ec.imag() == 0.0 and ec.real() == 0.0) continue;
if(j<i) tmp_op->elements.push_back({si,alpha,sj,beta,ni_opp,ec});
else tmp_op->elements.push_back({si,alpha,sj,beta,ni,ec});
}
}
}
}
}
}
}
//===============================================================================
void lattice_model::current_operator(const string &name, vector3D<int64_t> &link, double amplitude, int b1, int b2, int dir, bool pau)
{
int pau_index = 2;
double pau_mult = 1.0;
if(!pau){
pau_index = 1;
pau_mult = -1.0;
}
shared_ptr<lattice_operator> tmp_op;
if(auto it_op = term.find(name); it_op == term.end()){
check_name(name);
tmp_op = make_shared<lattice_operator>(*this, name, latt_op_type::one_body);
term[name] = tmp_op;
}
else{
tmp_op = it_op->second;
}
// previous_name = name;
// looping over sites of the super unit cell
for(int s1=0; s1 < (int)sites.size(); s1++){
if(sites[s1].orb != b1) continue;
auto match = find_second_site(s1, link);
if(!match) continue;
auto [s2, ni, ni_opp] = *match;
if(sites[s2].orb != b2) continue; // wrong orbital. skip.
int V;
switch(dir){
case 0: V = link.x; break;
case 1: V = link.y; break;
case 2: V = link.z; break;
// case 0: V = sites[s1].position.x-sites[s2].position.x; break;
// case 1: V = sites[s1].position.y-sites[s2].position.y; break;
// case 2: V = sites[s1].position.z-sites[s2].position.z; break;
}
for(int i=0; i<2; i++){
int si = s1+i*(s2-s1);
for(int j=0; j<2; j++){
int sj = s1+j*(s2-s1);
for(int alpha=0; alpha<2; alpha++){
for(int beta=0; beta<2; beta++){
Complex ec = pauli[pau_index][i][j]*pauli[0][alpha][beta]*amplitude*pau_mult;
ec *= V;
if(ec.imag() == 0.0 and ec.real() == 0.0) continue;
if(j<i) tmp_op->elements.push_back({si,alpha,sj,beta,ni_opp,ec});
else tmp_op->elements.push_back({si,alpha,sj,beta,ni,ec});
}
}
}
}
}
}
//===============================================================================
void lattice_model::anomalous_operator(const string &name, vector3D<int64_t> &link, complex<double> amplitude, int b1, int b2, const string& SC_str)
{
latt_op_type SC = latt_op_type::singlet;
if(SC_str == "singlet") SC = latt_op_type::singlet;
else if(SC_str == "dz") SC = latt_op_type::dz;
else if(SC_str == "dy") SC = latt_op_type::dy;
else if(SC_str == "dx") SC = latt_op_type::dx;
else qcm_throw("SC type must be either singlet, dz, dy or dz. Error in model file");
shared_ptr<lattice_operator> tmp_op;
// static string previous_name("");
if(auto it_op = term.find(name); it_op == term.end()){
check_name(name);
tmp_op = make_shared<lattice_operator>(*this, name, SC);
tmp_op->mixing |= HS_mixing::anomalous;
term[name] = tmp_op;
}
else{
tmp_op = it_op->second;
}
// looping over sites of the super unit cell
for(int s1=0; s1 < (int)sites.size(); s1++){
if(sites[s1].orb != b1) continue;
auto match = find_second_site(s1, link);
if(!match) continue;
auto [s2, ni, ni_opp] = *match;
if(sites[s2].orb != b2) continue; // wrong orbital. skip.
add_anomalous_elements(tmp_op->elements, s1, s2, ni, ni_opp, 0.5*amplitude, SC);
}
}
//===============================================================================
void lattice_model::density_wave(const string &name, vector3D<int64_t> &cdw_link, complex<double> amplitude, int orb, vector3D<double> Q, double phase, const string& type)
{
shared_ptr<lattice_operator> tmp_op;
vector3D<int64_t> r, R, S;
// static string previous_name("");
char dw_type = 'N';
// checks whether another line has started the definition of the same operator
if(auto it_op = term.find(name); it_op == term.end()){
check_name(name);
tmp_op = make_shared<lattice_operator>(*this, name, latt_op_type::one_body);
tmp_op->is_density_wave = true;
term[name] = tmp_op;
}
else{
tmp_op = it_op->second;
}
if(type == "spin") dw_type = 'Z';
else if(type == "N") dw_type = 'N';
else if(type == "cdw") dw_type = 'N';
else if(type == "Z") dw_type = 'Z';
else if(type == "X") dw_type = 'X';
else if(type == "Y") dw_type = 'Y';
else if(type == "singlet") tmp_op->type = latt_op_type::singlet;
else if(type == "dz") tmp_op->type = latt_op_type::dz;
else if(type == "dy") tmp_op->type = latt_op_type::dy;
else if(type == "dx") tmp_op->type = latt_op_type::dx;
else qcm_throw(type+" : unknown type of density wave");
if(cdw_link != vector3D<int64_t>(0,0,0)) dw_type = 'L';
if(tmp_op->type == latt_op_type::singlet or tmp_op->type == latt_op_type::dz or tmp_op->type == latt_op_type::dy or tmp_op->type == latt_op_type::dx) tmp_op->mixing |= HS_mixing::anomalous;
Q = physdual.from(0.5*Q);
phase *= M_PI;
// checking that the wavevector Q is compatible with the superlattice
vector3D<double> Qs = superdual.to(Q); // Qs is Q in the superdual basis. Its components must be integers (or numerically close to).
if(abs(Qs.x - floor(Qs.x+1e-6)) > 1e-5 or abs(Qs.y - floor(Qs.y+1e-6)) > 1e-5 or abs(Qs.z - floor(Qs.z+1e-6)) > 1e-5){
qcm_throw("density-wave vector for " + name + " is incommensurate with the superlattice");
}
tmp_op->norm = 1.0/sites.size();
auto& E = tmp_op->elements;
// loop over sites
for(int i=0; i<sites.size(); i++){
if(orb >= 0 && sites[i].orb != orb) continue; // wrong lattice orbital
Complex z = amplitude*cos(Q*sites[i].position+phase);
if(abs(z) > 1e-8){
if(dw_type=='Z'){
E.push_back({i,0,i,0,0,z});
E.push_back({i,1,i,1,0,-z});
}
else if(dw_type=='X'){
E.push_back({i,0,i,1,0,z});
E.push_back({i,1,i,0,0,z});
}
else if(dw_type=='Y'){
E.push_back({i,0,i,1,0,z*Complex(0,-1.0)});
E.push_back({i,1,i,0,0,z*Complex(0,1.0)});
}
else if(dw_type=='N'){
E.push_back({i,0,i,0,0,z});
E.push_back({i,1,i,1,0,z});
}
}
}
// loop over sites (bond CDWs)
if(dw_type=='L' or tmp_op->mixing&HS_mixing::anomalous){
for(int s1=0; s1 < (int)sites.size(); s1++){
if(orb >= 0 && sites[s1].orb != orb) continue; // wrong lattice orbital
vector3D<int64_t>& r = sites[s1].position;
auto match = find_second_site(s1, cdw_link);
if(!match) continue;
auto [s2, ni, ni_opp] = *match;
Complex z;
z = amplitude*Complex(cos(Q*r+phase),-sin(Q*r+phase)); // sign change 2016-04-15
if(abs(z) < SMALL_VALUE) continue;
add_anomalous_elements(tmp_op->elements, s1, s2, ni, ni_opp, 0.5*z, tmp_op->type);
}
}
}
//===============================================================================
void lattice_model::explicit_operator(const string &name, const string &type, const vector<tuple<vector3D<int64_t>, vector3D<int64_t>, complex<double>>> &elem, int tau, int sigma)
{
check_name(name);
auto tmp_op = make_shared<lattice_operator>(*this, name);
term[name] = tmp_op;
tmp_op->type = lattice_operator::op_type_map.at(type);
tmp_op->is_density_wave = true;
if(tmp_op->type == latt_op_type::Hubbard
or tmp_op->type == latt_op_type::Hund
or tmp_op->type == latt_op_type::Heisenberg
or tmp_op->type == latt_op_type::X
or tmp_op->type == latt_op_type::Y
or tmp_op->type == latt_op_type::Z
) tmp_op->is_interaction = true;
if(tmp_op->type == latt_op_type::singlet or tmp_op->type == latt_op_type::dz or tmp_op->type == latt_op_type::dy or tmp_op->type == latt_op_type::dx) tmp_op->mixing |= HS_mixing::anomalous;
string opt("10");
opt[0] = char('0')+tau;
opt[1] = char('0')+sigma;
for(auto& x : elem){
tmp_op->add_matrix_element(get<0>(x), get<1>(x), get<2>(x), opt);
}
tmp_op->close();
}
//===============================================================================
matrix<Complex> lattice_model::lattice_hybridization(int iw, int ik){
if(hybrid == nullptr) qcm_throw("Lattice hybridization has not been defined");
lattice_hybrid& H = *hybrid;
matrix<Complex> gamma(H.d);
int r = H.d*(ik+H.nk*iw);
for(int i=0; i<H.d; i++){
for(int j=0; j<H.d; j++){
gamma(j,i) = complex<double>(H.R[j+H.d*(i+r)], H.I[j+H.d*(i+r)]); // note the reverse order (row order)
}
}
// upgrade depending on mixing state
if(mixing == H.mixing) return gamma;
else if(HS_mixing::anomalous and H.mixing==0){
matrix<Complex> h(2*H.d);
gamma.move_sub_matrix(H.d, H.d, 0, 0, 0, 0, h);
gamma.move_sub_matrix_HC(H.d, H.d, 0, 0, H.d, H.d, h, -1.0);
return h;
}
else if(HS_mixing::spin_flip and H.mixing==0){
matrix<Complex> h(2*H.d);
gamma.move_sub_matrix(H.d, H.d, 0, 0, 0, 0, h);
gamma.move_sub_matrix(H.d, H.d, 0, 0, H.d, H.d, h);
return h;
}
else qcm_throw("This mixing case is not currently covered in the treatment of lattice hybridizations");
}
// move_sub_matrix_HC(size_t R, size_t C, size_t i, size_t j, size_t I, size_t J, matrix<S> &A, double z = 1.0)
// template <typename S>
// void move_sub_matrix(size_t R, size_t C, size_t i, size_t j, size_t I, size_t J, matrix<S> &A, double z = 1.0)