Program Listing for File average.cpp

Return to documentation for file (src_qcm/average.cpp)

#include <fstream>
#include "lattice_model_instance.hpp"
#include "integrate.hpp"
#include "parser.hpp"
#include "QCM.hpp"

double tr_sigma_inf(0.0);

vector<shared_ptr<lattice_operator>> ops = {};

extern vector<double> grid_freqs; // optional imaginary frequency grid for discrete integrals
extern vector<double> grid_weights; // weights associated with grid_freqs

//==============================================================================
vector<pair<string,double>> lattice_model_instance::averages(const vector<string> &_ops)
{
  if(average_solved) return ave;
  if(_ops.size() == 0) ops = model->one_body_ops;
  else{
    ops.clear();
    ops.reserve(_ops.size());
    for(int i=0; i<_ops.size(); i++){
      if(model->term[_ops[i]]->is_interaction == false) ops.push_back(model->term[_ops[i]]);
    }
  }
  if(!gf_solved) Green_function_solve();
  double accur_OP = global_double("accur_OP");
  bool periodized_averages = global_bool("periodized_averages");


    vector<double> Iv(ops.size());
  vector<double> Iw(ops.size());

  if(model->hybrid != nullptr){ // frequency-momentum sum
      vector<double> I(ops.size());
    for(int iw=0; iw < model->hybrid->nw; iw++){
      to_zero(Iw);
      for(int ik=0; ik < model->hybrid->nk; ik++){
        average_integrand(iw, ik, I);
        Iw += I;
      }
      Iw *= model->hybrid->weight[iw]/model->hybrid->nk;
      Iv += Iw;
    }
  }
    else{ // frequency-momentum integral
    // lambda function
    if(periodized_averages){
      auto F = [this] (Complex w, vector3D<double> &k, const int *nv, double *I) mutable {average_integrand_per(w, k, nv, I);};
      QCM::wk_integral(model->spatial_dimension, F, Iv, accur_OP, global_bool("verb_integrals"));
    }
    else if(grid_freqs.size() > 0){
      // Frequency-momentum grid. The cluster Green function depends only on w,
      // so build it once per frequency (sequential), then parallelize only over k
      // via k_integral_grid. The lambda captures G_up (and G_dn) by value: the
      // captured copies live in the lambda object, are read-only inside the parallel
      // section, and contain no shared mutable state.
      int nkx, nky, nkz;
      QCM::get_wavevector_grid(nkx, nky, nkz);
      cout << "computing integrals from a grid of " << grid_freqs.size() << " frequencies and "
           << nkx << "x" << nky << "x" << nkz << " k-points (dim=" << model->spatial_dimension << ")" << endl;
      bool has_dn = (model->mixing == HS_mixing::up_down);
      for(int iw = 0; iw < (int)grid_freqs.size(); iw++){
        Complex wc(0, grid_freqs[iw]);
        Green_function G_up = cluster_Green_function(wc, false, false);
        Green_function G_dn;
        if(has_dn) G_dn = cluster_Green_function(wc, false, true);

        auto F_k = [this, G_up, G_dn, has_dn] (vector3D<double> &k, const int *nv, double *I) mutable {
          average_integrand_k(G_up, has_dn ? &G_dn : nullptr, k, nv, I);
        };
        to_zero(Iw);
        QCM::k_integral_grid(model->spatial_dimension, nkx, nky, nkz, F_k, Iw);
        Iv += Iw*grid_weights[iw];
      }
    }
    else{
      auto F = [this] (Complex w, vector3D<double> &k, const int *nv, double *I) mutable {average_integrand(w, k, nv, I);};
      QCM::wk_integral(model->spatial_dimension, F, Iv, accur_OP, global_bool("verb_integrals"));
    }
  }


  size_t i = 0;
  ave.resize(ops.size());
    for(auto& op : ops){
    if(periodized_averages) Iv[i] *= model->Lc;
    if(model->mixing == HS_mixing::full) {
      Iv[i] += op->nambu_correction_full;
      Iv[i] *= 0.5;
    }
    else if((model->mixing&HS_mixing::anomalous)  == HS_mixing::anomalous) Iv[i] += op->nambu_correction;
    ave[i] = {op->name, Iv[i]*op->norm};
        i++;
    }
  average_solved = true;

  // computing the kinetic energy
  E_kin = 0.0;
  i = 0;
  for(auto& op : ops){
    if(op->name != "mu") E_kin += Iv[i]*params.at(op->name);
    i++;
  }
  E_kin /= model->Lc;

    return ave;
}




//==============================================================================
void lattice_model_instance::average_integrand(Complex w, vector3D<double> &k, const int *nv, double *I)
{
  check_signals();
  Green_function G_up = cluster_Green_function(w, false, false);
  if(model->mixing == HS_mixing::up_down){
    Green_function G_dn = cluster_Green_function(w, false, true);
    average_integrand_k(G_up, &G_dn, k, nv, I);
  }
  else average_integrand_k(G_up, nullptr, k, nv, I);
}


//==============================================================================
void lattice_model_instance::average_integrand_k(Green_function &G_up, Green_function *G_down, vector3D<double> &k, const int *nv, double *I)
{
  Complex w = G_up.w;
  const double w_offset = 2.0;
  Complex G_pole = 1.0/(w - w_offset);

  Green_function_k K(G_up,k);
  set_Gcpt(K);
  K.Gcpt.add(-G_pole); // regulates the Green function at high frequency

  size_t i = 0;
  for(auto& op : ops){
    Complex z(0.0);
    for(auto &x : op->GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
    for(auto &x : op->IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n];
    I[i++] = real<double>(z);
  }

  if(G_down){
    Green_function_k K_dn(*G_down,k);
    set_Gcpt(K_dn);
    K_dn.Gcpt.add(-G_pole);
    i = 0;
    for(auto& op : ops){
      Complex z(0.0);
      for(auto &x : op->GF_elem_down) z += K_dn.Gcpt(x.c,x.r)*x.v;
      for(auto &x : op->IGF_elem_down) z += K_dn.Gcpt(x.c,x.r)*x.v*K_dn.phase[x.n];
      I[i++] += real<double>(z);
    }
  }
  if(model->mixing == HS_mixing::normal) for(size_t i=0; i<ops.size(); i++) I[i] *= 2;
}



//==============================================================================
void lattice_model_instance::average_integrand(int iw, int ik, vector<double> &I)
{

  Complex w(0,model->hybrid->w[iw]);
  const double w_offset = 2.0;
  Complex G_pole = 1.0/(w - w_offset);

  Green_function G = cluster_Green_function(w, false, false);
  G.iw = iw;
    Green_function_k K(G, model->hybrid->k[ik], ik);
    set_Gcpt(K);
    K.Gcpt.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)

    size_t i = 0;
    for(auto& op : ops){
        Complex z(0.0);
        for(auto &x : op->GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
        for(auto &x : op->IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n];

        I[i++] = real<double>(z);
    }
    i = 0;
    if(model->mixing == HS_mixing::up_down){
    Green_function G = cluster_Green_function(w, false, true);
    Green_function_k K(G, model->hybrid->k[ik], ik);
    set_Gcpt(K);
    K.Gcpt.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
        for(auto& op : ops){
            Complex z(0.0);
            for(auto &x : op->GF_elem_down) z += K.Gcpt(x.c,x.r)*x.v;
            for(auto &x : op->IGF_elem_down) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n];
            I[i++] += real<double>(z);
        }
    }
  if(model->mixing == HS_mixing::normal) for(size_t i=0; i<ops.size(); i++) I[i] *= 2;
}

//==============================================================================
void lattice_model_instance::average_integrand_per(Complex w, vector3D<double> &k, const int *nv, double *I)
{

  const double w_offset = 2.0;
  Complex G_pole = 1.0/(w - w_offset);

  Green_function G = cluster_Green_function(w, false, false);
  vector3D<double> k_red = model->superdual.to(model->physdual.from(k));

  Green_function_k K(G,k_red);
  periodized_Green_function(K);
  K.g.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)

  size_t i = 0;
  for(auto& op : ops){
    matrix<Complex> S(G.G.r);
    // build the operator matrix
    for(auto &x : op->GF_elem) S(x.r, x.c) += x.v;
    for(auto &x : op->IGF_elem) S(x.r, x.c) += x.v*K.phase[x.n];
    matrix<Complex> S_per = model->periodize(k_red, S);
    I[i++] = realpart(K.g.trace_product(S_per));
  }
  i = 0;
  if(model->mixing == HS_mixing::up_down){
    Green_function G = cluster_Green_function(w,false,true);
    Green_function_k K(G,k_red);
    periodized_Green_function(K);
    K.g.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
    for(auto& op : ops){
      matrix<Complex> S(G.G.r);
      // build the operator matrix
      for(auto &x : op->GF_elem_down) S(x.r, x.c) += x.v;
      for(auto &x : op->IGF_elem_down) S(x.r, x.c) += x.v*K.phase[x.n];
      matrix<Complex> S_per = model->periodize(k_red, S);
      I[i++] += realpart(K.g.trace_product(S_per));
    }
  }
  if(model->mixing == HS_mixing::normal) for(size_t i=0; i<ops.size(); i++) I[i] *= 2;
}





//==============================================================================
vector<double> lattice_model_instance::dos(const complex<double> w, bool use_grid)
{
  double accur_OP = global_double("accur_OP");
  if(!gf_solved) Green_function_solve();

  size_t D_dim = model->n_band;
  size_t d = D_dim;
  if(model->mixing&3) d *= 2;
  vector<double> D(2*D_dim);

  Green_function G = cluster_Green_function(w, false, false);

  auto F = [this, G] (vector3D<double> &k, const int *nv, double *I) mutable {
    for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
    Green_function_k K(G,k);
    set_Gcpt(K);
    for(size_t i=0; i<model->dim_GF; i++) I[model->reduced_Green_index[i]] += K.Gcpt(i,i).imag();
  };

  vector<double> Iv(model->dim_reduced_GF,0.0);
  if(use_grid){
    int nkx, nky, nkz;
    QCM::get_wavevector_grid(nkx, nky, nkz);
    QCM::k_integral_grid(model->spatial_dimension, nkx, nky, nkz, F, Iv);
  }
  else QCM::k_integral(model->spatial_dimension, F, Iv, accur_OP, global_bool("verb_integrals"));
  for(size_t i=0; i<d; i++) D[i] = -M_1_PI*Iv[i]/model->Lc;

  if(model->mixing == HS_mixing::up_down){
    Green_function G_down = cluster_Green_function(w, false, true);

    auto F_down = [this, G_down] (vector3D<double> &k, const int *nv, double *I) mutable {
      for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
      Green_function_k K(G_down,k);
      set_Gcpt(K);
      for(size_t i=0; i<model->dim_GF; i++) I[model->reduced_Green_index[i]] += K.Gcpt(i,i).imag();
    };

    to_zero(Iv);
    if(use_grid){
      int nkx, nky, nkz;
      QCM::get_wavevector_grid(nkx, nky, nkz);
      QCM::k_integral_grid(model->spatial_dimension, nkx, nky, nkz, F_down, Iv);
    }
    else QCM::k_integral(model->spatial_dimension, F_down, Iv, accur_OP, global_bool("verb_integrals"));
    for(size_t i=0; i<D_dim; i++) D[i+D_dim] = -M_1_PI*Iv[i]/model->Lc;
  }
  else if(model->mixing == HS_mixing::normal){
    for(size_t i=0; i<D_dim; i++) D[i+D_dim] = D[i];
  }
  return D;
}


//==============================================================================
double lattice_model_instance::spectral_average(const string& name, const complex<double> w)
{
  double accur_OP = global_double("accur_OP");
  if(model->term.find(name) == model->term.end()) qcm_throw("the operator "+name+" does not exist");
  size_t n_clus = model->clusters.size();

  lattice_operator op = *model->term.at(name);

  if(!gf_solved) Green_function_solve();

  Green_function G = cluster_Green_function(w, false, false);


  auto F = [this, op, G] (vector3D<double> &k, const int *nv, double *I) mutable {
    for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
    Green_function_k K(G,k);
    set_Gcpt(K);
    Complex z(0.0);
    for(auto &x : op.GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
    for(auto &x : op.IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n] + K.Gcpt(x.r,x.c)*conjugate(x.v*K.phase[x.n]);
    I[0] = imag(z);
  };
  vector<double> Iv(1,0.0);
  QCM::k_integral(model->spatial_dimension, F, Iv, accur_OP, global_bool("verb_integrals"));

  double result = -M_1_PI*Iv[0]/model->Lc;

  if(model->mixing == HS_mixing::up_down){
    Green_function G_down = cluster_Green_function(w, false, true);
    G_down.w = w;
    G_down.spin_down = true;
    G_down.G.block.assign(n_clus, matrix<Complex>());
    for(size_t i = 0; i<n_clus; i++){
      if(model->clusters[i].conj){
        G_down.G.block[i] = matrix<Complex>(model->GF_dims[i], ED::Green_function(conjugate(w), true, n_clus*label+model->clusters[i].ref, false));
        G_down.G.block[i].cconjugate();
      }
      else
        G_down.G.block[i] = matrix<Complex>(model->GF_dims[i], ED::Green_function(w, true, n_clus*label+model->clusters[i].ref, false));
    }
    G_down.G.set_size();

    auto F_down = [this, G_down, op] (vector3D<double> &k, const int *nv, double *I) mutable {
      for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
      Green_function_k K(G_down,k);
      set_Gcpt(K);
      Complex z(0.0);
      for(auto &x : op.GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
      for(auto &x : op.IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n] + K.Gcpt(x.r,x.c)*conjugate(x.v*K.phase[x.n]);
      I[0] = imag(z);
    };

    to_zero(Iv);
    QCM::k_integral(model->spatial_dimension, F_down, Iv, accur_OP, global_bool("verb_integrals"));
    result += -M_1_PI*Iv[0]/model->Lc;
  }
  if(model->mixing == HS_mixing::normal) result *= 2;

  return result;
}



//==============================================================================
vector<double> lattice_model_instance::momentum_profile(const lattice_operator& op, const vector<vector3D<double>> &k_set)
{
  double accur_OP = global_double("accur_OP");
  auto Fmp = [this, k_set, op] (Complex w, vector3D<double> &ki, const int *nv, double *I) mutable {
    const double w_offset = 2.0;
    Complex G_pole = 1.0/(w - w_offset);

    Green_function G = cluster_Green_function(w, false, false);
    size_t i = 0;
    for(auto& k : k_set){
      Green_function_k K(G,k);
      set_Gcpt(K);
      K.Gcpt.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
      Complex z(0.0);
      for(auto &x : op.GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
      for(auto &x : op.IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n];
      I[i++] = real<double>(z);
    }
    if(model->mixing == HS_mixing::up_down){
      Green_function G_dw = cluster_Green_function(w,false,true);
      i = 0;
      for(auto& k : k_set){
        Green_function_k K(G_dw,k);
        set_Gcpt(K);
        K.Gcpt.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
        Complex z(0.0);
        for(auto &x : op.GF_elem) z += K.Gcpt(x.c,x.r)*x.v;
        for(auto &x : op.IGF_elem) z += K.Gcpt(x.c,x.r)*x.v*K.phase[x.n];
        I[i++] += real<double>(z);
      }
    }
  };
  vector<double> A(k_set.size(), 0.0);
  QCM::wk_integral(0, Fmp, A, accur_OP, global_bool("verb_integrals"));
  if(model->mixing == HS_mixing::normal) A *= 2;
  else if((model->mixing&HS_mixing::anomalous)  == HS_mixing::anomalous) A += op.nambu_correction;
  else if(model->mixing == HS_mixing::full) {A += op.nambu_correction_full; A *= 0.5;}
  A *= op.norm;
  return A;
}

//==============================================================================
vector<double> lattice_model_instance::momentum_profile_per(const lattice_operator& op, const vector<vector3D<double>> &k_set)
{
  double accur_OP = global_double("accur_OP");
  auto Fmp = [this, k_set, op] (Complex w, vector3D<double> &ki, const int *nv, double *I) mutable {
    const double w_offset = 2.0;
    Complex G_pole = 1.0/(w - w_offset);

    Green_function G = cluster_Green_function(w, false, false);
    size_t i = 0;
    for(auto& k : k_set){
      vector3D<double> k_red = model->superdual.to(model->physdual.from(k));
      Green_function_k K(G,k_red);
      periodized_Green_function(K);
      K.g.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
      matrix<Complex> S(G.G.r);
      // build the operator matrix
      for(auto &x : op.GF_elem) S(x.r, x.c) += x.v;
      for(auto &x : op.IGF_elem) S(x.r, x.c) += x.v*K.phase[x.n];
      matrix<Complex> S_per = model->periodize(k_red, S);
      I[i++] = realpart(K.g.trace_product(S_per));
    }
    if(model->mixing == HS_mixing::up_down){
      Green_function G_dw = cluster_Green_function(w,false,true);
      i = 0;
      for(auto& k : k_set){
        vector3D<double> k_red = model->superdual.to(model->physdual.from(k));
        Green_function_k K(G_dw,k_red);
        periodized_Green_function(K);
        K.g.add(-G_pole); // regulates the Green function at high frequency (subtracts G_pole times the identity matrix)
        matrix<Complex> S(G.G.r);
        // build the operator matrix
        for(auto &x : op.GF_elem) S(x.r, x.c) += x.v;
        for(auto &x : op.IGF_elem) S(x.r, x.c) += x.v*K.phase[x.n];
        matrix<Complex> S_per = model->periodize(k_red, S);
        I[i++] = realpart(K.g.trace_product(S_per));
      }
    }
  };
  vector<double> A(k_set.size(), 0.0);
  QCM::wk_integral(0, Fmp, A, accur_OP, global_bool("verb_integrals"));
  if(model->mixing == HS_mixing::normal) A *= 2;
  else if((model->mixing&HS_mixing::anomalous)  == HS_mixing::anomalous) A += op.nambu_correction;
  else if(model->mixing == HS_mixing::full) {A += op.nambu_correction_full; A *= 0.5;}
  A *= op.norm;
  return A;
}


#define WINF 1e5
//==============================================================================
double lattice_model_instance::potential_energy()
{
  if(PE_solved) return E_pot;
  PE_solved = true;

  double accur_OP = global_double("accur_OP");
  if(!gf_solved) Green_function_solve();

  // computing the infinite frequency limit
  double prev_cutoff = global_double("cutoff_scale");
  set_global_double("cutoff_scale", WINF); // important for convergence


  tr_sigma_inf = 0.0;
  for(int i=0; i<model->clusters.size(); i++){
    tr_sigma_inf += ED::tr_sigma_inf(i + label*model->clusters.size());
  }

  // frequency integral
  vector<double> I(1);
  I[0] = 0.0;

  if(grid_freqs.size() > 0){
    // ---- Discrete (omega, k) grid path ----
    // Mirrors the averages()/Potthoff_functional() pattern: build the cluster
    // Green function (and its self-energy) once per frequency, then parallelize
    // the k-sum via k_integral_grid. The cluster GF is captured by value into
    // the lambda and read concurrently inside the OpenMP region (no shared
    // mutable state).
    int nkx, nky, nkz;
    QCM::get_wavevector_grid(nkx, nky, nkz);
    if(global_bool("verb_integrals"))
      cout << "computing potential energy from a grid of " << grid_freqs.size() << " frequencies and "
           << nkx << "x" << nky << "x" << nkz << " k-points (dim=" << model->spatial_dimension << ")" << endl;
    bool has_dn = (model->mixing == HS_mixing::up_down);
    vector<double> Iw(1);
    for(int iw = 0; iw < (int)grid_freqs.size(); iw++){
      Complex wc(0, grid_freqs[iw]);
      Green_function G_up = cluster_Green_function(wc, true, false);
      Green_function G_dn;
      if(has_dn) G_dn = cluster_Green_function(wc, true, true);

      auto F_k = [this, G_up, G_dn, has_dn] (vector3D<double> &k, const int *nv, double *I) mutable {
        potential_energy_integrand_k(G_up, has_dn ? &G_dn : nullptr, k, nv, I);
      };
      Iw[0] = 0.0;
      QCM::k_integral_grid(model->spatial_dimension, nkx, nky, nkz, F_k, Iw);
      I[0] += Iw[0] * grid_weights[iw];
    }
  }
  else{
    // ---- Adaptive cubature in (omega, k) ----
    auto F = [this] (Complex w, vector3D<double> &k, const int *nv, double *I) mutable {potential_energy_integrand(w, k, nv, I);};
    QCM::wk_integral(model->spatial_dimension, F, I, 0.1*accur_OP, global_bool("verb_integrals"));
  }

  set_global_double("cutoff_scale", prev_cutoff); // restores to previous value
  if(model->mixing == HS_mixing::full) I[0] *= 0.5; // full Nambu doubling overestimates by a factor of 2
  E_pot = 0.5*I[0]/model->Lc;
  return E_pot;
}




//==============================================================================
complex<double> lattice_model_instance::TrSigmaG(Complex w, vector3D<double> &k, bool spin_down)
{
  complex<double> z(0.0);
  Green_function G = cluster_Green_function(w, false, spin_down);
  Green_function_k K(G,k);
  set_Gcpt(K);
  cluster_self_energy(G);
  z = G.sigma.build_matrix().trace_product(K.Gcpt);
  return z;
}

//==============================================================================
void lattice_model_instance::potential_energy_integrand(Complex w, vector3D<double> &k, const int *nv, double *I)
{
  check_signals();

  const double w_offset = 2.0;

  Complex e = TrSigmaG(w, k, false);
  if(model->mixing == HS_mixing::up_down) e += TrSigmaG(w, k, true);
  else if(model->mixing == HS_mixing::normal) e *= 2;

  e -= tr_sigma_inf/(w-w_offset);
  I[0] = real<double>(e);
}


//==============================================================================
void lattice_model_instance::potential_energy_integrand_k(Green_function &G_up, Green_function *G_down, vector3D<double> &k, const int *nv, double *I)
{
  const double w_offset = 2.0;
  Complex w = G_up.w;

  Green_function_k K(G_up, k);
  set_Gcpt(K);
  Complex e = G_up.sigma.build_matrix().trace_product(K.Gcpt);

  if(G_down){
    Green_function_k K_dn(*G_down, k);
    set_Gcpt(K_dn);
    e += G_down->sigma.build_matrix().trace_product(K_dn.Gcpt);
  }
  else if(model->mixing == HS_mixing::normal) e *= 2;

  e -= tr_sigma_inf/(w-w_offset);
  I[0] = real<double>(e);
}


//==============================================================================
matrix<complex<double>> lattice_model_instance::Green_integral(bool spin_down, int clus)
{
  size_t dim;
  if(clus) dim = model->GF_dims[clus-1];
  else dim = model->dim_GF;
  double accur_OP = global_double("accur_OP");
  auto Fmp = [this, dim, spin_down] (Complex w, vector3D<double> &ki, const int *nv, double *I) mutable {
    const double w_offset = 2.0;
    Complex G_pole = 1.0/(w - w_offset);
    G_pole += conjugate(G_pole);
      matrix<Complex> Gloc = projected_Green_function(w, spin_down);
    Gloc.symmetrize();
    Gloc.v *= 2.0;
    Gloc.add(-G_pole);
    for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
    hermitian_matrix_to_real_vector(Gloc, I);
  };
  auto Fmp_clus = [this, dim, spin_down] (Complex w, vector3D<double> &ki, const int *nv, double *I) mutable {
    const double w_offset = 2.0;
    Complex G_pole = 1.0/(w - w_offset);
    G_pole += conjugate(G_pole);
      matrix<Complex> Gloc = cluster_Green_function(0, w, spin_down, false);
    Gloc.symmetrize();
    Gloc.v *= 2.0;
    Gloc.add(-G_pole);
    for(size_t i = 0 ; i<*nv; i++) I[i] = 0.0;
    hermitian_matrix_to_real_vector(Gloc, I);
  };

  vector<double> A(dim*dim, 0.0);
  if(clus) QCM::wk_integral(0, Fmp_clus, A, accur_OP, global_bool("verb_integrals"));
  else QCM::wk_integral(0, Fmp, A, accur_OP, global_bool("verb_integrals"));
  if(model->mixing == HS_mixing::normal) A *= 2;
  else if(model->mixing == HS_mixing::full) {A *= 0.5;}
  return hermitian_matrix_from_real_vector(dim, A);
}