int main(int argc, char* argv[]) { boost::mpi::environment env(argc,argv); boost::mpi::communicator comm; print_section("Hubbard nxn"); int size_x, size_y, wn; RealType t, mu, U, beta, reduce_tol, coeff_tol; bool calc_gf, calc_2pgf; int wf_max, wb_max; double eta, hbw, step; // for evaluation of GF on real axis try { // command line parser TCLAP::CmdLine cmd("Hubbard nxn diag", ' ', ""); TCLAP::ValueArg<RealType> U_arg("U","U","Value of U",true,10.0,"RealType",cmd); TCLAP::ValueArg<RealType> mu_arg("","mu","Global chemical potential",false,0.0,"RealType",cmd); TCLAP::ValueArg<RealType> t_arg("t","t","Value of t",false,1.0,"RealType",cmd); TCLAP::ValueArg<RealType> beta_arg("b","beta","Inverse temperature",true,100.,"RealType"); TCLAP::ValueArg<RealType> T_arg("T","T","Temperature",true,0.01,"RealType"); cmd.xorAdd(beta_arg,T_arg); TCLAP::ValueArg<size_t> x_arg("x","x","Size over x",false,2,"int",cmd); TCLAP::ValueArg<size_t> y_arg("y","y","Size over y",false,2,"int",cmd); TCLAP::ValueArg<size_t> wn_arg("","wf","Number of positive fermionic Matsubara Freqs",false,64,"int",cmd); TCLAP::ValueArg<size_t> wb_arg("","wb","Number of positive bosonic Matsubara Freqs",false,1,"int",cmd); TCLAP::SwitchArg gf_arg("","calcgf","Calculate Green's functions",cmd, false); TCLAP::SwitchArg twopgf_arg("","calc2pgf","Calculate 2-particle Green's functions",cmd, false); TCLAP::ValueArg<RealType> reduce_tol_arg("","reducetol","Energy resonance resolution in 2pgf",false,1e-5,"RealType",cmd); TCLAP::ValueArg<RealType> coeff_tol_arg("","coefftol","Total weight tolerance",false,1e-12,"RealType",cmd); TCLAP::ValueArg<RealType> eta_arg("","eta","Offset from the real axis for Green's function calculation",false,0.05,"RealType",cmd); TCLAP::ValueArg<RealType> hbw_arg("D","hbw","Half-bandwidth. Default = U",false,0.0,"RealType",cmd); TCLAP::ValueArg<RealType> step_arg("","step","Step on a real axis. Default : 0.01",false,0.01,"RealType",cmd); cmd.parse( argc, argv ); U = U_arg.getValue(); mu = (mu_arg.isSet()?mu_arg.getValue():U/2); boost::tie(t, beta, calc_gf, calc_2pgf, reduce_tol, coeff_tol) = boost::make_tuple( t_arg.getValue(), beta_arg.getValue(), gf_arg.getValue(), twopgf_arg.getValue(), reduce_tol_arg.getValue(), coeff_tol_arg.getValue()); boost::tie(size_x, size_y) = boost::make_tuple(x_arg.getValue(), y_arg.getValue()); boost::tie(wf_max, wb_max) = boost::make_tuple(wn_arg.getValue(), wb_arg.getValue()); boost::tie(eta, hbw, step) = boost::make_tuple(eta_arg.getValue(), (hbw_arg.isSet()?hbw_arg.getValue():2.*U), step_arg.getValue()); calc_gf = calc_gf || calc_2pgf; calc_gf = calc_gf || calc_2pgf; } catch (TCLAP::ArgException &e) // catch parsing exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; exit(1);} int L = size_x*size_y; INFO("Diagonalization of " << L << "=" << size_x << "*" << size_y << " sites"); Lattice Lat; /* Add sites */ std::vector<std::string> names(L); for (size_t y=0; y<size_y; y++) for (size_t x=0; x<size_x; x++) { size_t i = SiteIndexF(size_x, x, y); std::stringstream s; s << i; names[i]="S"+s.str(); Lat.addSite(new Lattice::Site(names[i],1,2)); }; INFO("Sites"); Lat.printSites(); /* Add interaction on each site*/ MelemType U_complex; U_complex = std::complex<double> (U,0); MelemType mu_complex; mu_complex = std::complex<double> (mu,0); for (size_t i=0; i<L; i++) LatticePresets::addCoulombS(&Lat, names[i], U_complex, -mu_complex); /* Add hopping */ for (size_t y=0; y<size_y; y++) { for (size_t x=0; x<size_x; x++) { size_t pos = SiteIndexF(size_x, x,y); size_t pos_right = SiteIndexF(size_x, (x+1)%size_x,y); /*if (x == size_x - 1) pos_right = SiteIndexF(0,y); */ size_t pos_up = SiteIndexF(size_x, x,(y+1)%size_y); if (size_x > 1) LatticePresets::addHopping(&Lat, std::min(names[pos], names[pos_right]), std::max(names[pos], names[pos_right]), -t); if (size_y > 1) LatticePresets::addHopping(&Lat, std::min(names[pos], names[pos_up]), std::max(names[pos], names[pos_up]), -t); }; }; int rank = comm.rank(); if (!rank) { INFO("Terms with 2 operators"); Lat.printTerms(2); INFO("Terms with 4 operators"); Lat.printTerms(4); }; IndexClassification IndexInfo(Lat.getSiteMap()); IndexInfo.prepare(false); // Create index space if (!rank) { print_section("Indices"); IndexInfo.printIndices(); }; int index_size = IndexInfo.getIndexSize(); print_section("Matrix element storage"); IndexHamiltonian Storage(&Lat,IndexInfo); Storage.prepare(); // Write down the Hamiltonian as a symbolic formula print_section("Terms"); if (!rank) INFO(Storage); Symmetrizer Symm(IndexInfo, Storage); Symm.compute(); // Find symmetries of the problem StatesClassification S(IndexInfo,Symm); // Introduce Fock space and classify states to blocks S.compute(); Hamiltonian H(IndexInfo, Storage, S); // Hamiltonian in the basis of Fock Space H.prepare(); // enter the Hamiltonian matrices H.compute(); // compute eigenvalues and eigenvectors RealVectorType evals (H.getEigenValues()); std::sort(evals.data(), evals.data() + H.getEigenValues().size()); savetxt("spectrum.dat", evals); // dump eigenvalues DensityMatrix rho(S,H,beta); // create Density Matrix rho.prepare(); rho.compute(); // evaluate thermal weights with respect to ground energy, i.e exp(-beta(e-e_0))/Z INFO("<N> = " << rho.getAverageOccupancy()); // get average total particle number savetxt("N_T.dat",rho.getAverageOccupancy()); // Green's function calculation starts here FieldOperatorContainer Operators(IndexInfo, S, H); // Create a container for c and c^+ in the eigenstate basis if (calc_gf) { INFO("1-particle Green's functions calc"); std::set<ParticleIndex> f; std::set<IndexCombination2> indices2; ParticleIndex d0 = IndexInfo.getIndex("S0", 0, down); ParticleIndex u0 = IndexInfo.getIndex("S0", 0, up); f.insert(u0); f.insert(d0); for (size_t x = 0; x < size_x; x++) { ParticleIndex ind = IndexInfo.getIndex(names[SiteIndexF(size_x, x, 0)], 0, down); f.insert(ind); indices2.insert(IndexCombination2(d0, ind)); }; Operators.prepareAll(f); Operators.computeAll(); // evaluate c, c^+ for chosen indices GFContainer G(IndexInfo, S, H, rho, Operators); G.prepareAll(indices2); // identify all non-vanishing block connections in the Green's function G.computeAll(); // Evaluate all GF terms, i.e. resonances and weights of expressions in Lehmans representation of the Green's function if (!comm.rank()) { // dump gf into a file std::set<IndexCombination2>::iterator ind2; for (ind2 = indices2.begin(); ind2 != indices2.end(); ++ind2) { // loops over all components (pairs of indices) of the Green's function // Save Matsubara GF from pi/beta to pi/beta*(4*wf_max + 1) std::cout << "Saving imfreq G" << *ind2 << " on " << 4 * wf_max << " Matsubara freqs. " << std::endl; std::stringstream fname; fname << "gw_imag" << (*ind2).Index1 << (*ind2).Index2 << ".dat"; std::ofstream gw_im(fname.str().c_str()); const GreensFunction &GF = G(*ind2); for (int wn = 0; wn < wf_max * 4; wn++) { ComplexType val = GF( I * FMatsubara(wn, beta)); // this comes from Pomerol - see GreensFunction::operator() gw_im << std::scientific << std::setprecision(12) << FMatsubara(wn, beta) << " " << real(val) << " " << imag(val) << std::endl; }; gw_im.close(); } } } return 0; }
int main(int argc, char* argv[]) { boost::mpi::environment env(argc,argv); boost::mpi::communicator world; Lattice L; L.addSite(new Lattice::Site("A",1,2)); LatticePresets::addCoulombS(&L, "A", U, -mu); L.addSite(new Lattice::Site("B",1,2)); LatticePresets::addCoulombS(&L, "B", U, -mu); LatticePresets::addHopping(&L, "A","B", -1.0); INFO("Sites"); L.printSites(); INFO("Terms"); L.printTerms(2); INFO("Terms with 4 operators"); L.printTerms(4); IndexClassification IndexInfo(L.getSiteMap()); IndexInfo.prepare(); print_section("Indices"); IndexInfo.printIndices(); ParticleIndex NModes = IndexInfo.getIndexSize(); std::vector<ParticleIndex> SpinUpIndices; for (ParticleIndex i=0; i<NModes; i++) if (IndexInfo.getInfo(i).Spin) SpinUpIndices.push_back(i); print_section("Matrix element storage"); IndexHamiltonian Storage(&L,IndexInfo); Storage.prepare(); INFO("Terms"); INFO(Storage); ParticleIndex IndexSize = IndexInfo.getIndexSize(); OperatorPresets::Sz Sz(IndexSize, SpinUpIndices); INFO("Sz terms"); Sz.printAllTerms(); OperatorPresets::N N(IndexSize); INFO("N terms"); N.printAllTerms(); if (!(Sz.commutes(N))) return EXIT_FAILURE; if (!(Storage.commutes(N))) return EXIT_FAILURE; INFO("H commutes with N"); if (!(Storage.commutes(Sz))) return EXIT_FAILURE; INFO("H commutes with Sz"); Symmetrizer Symm(IndexInfo, Storage); Symm.compute(); StatesClassification S(IndexInfo,Symm); S.compute(); Hamiltonian H(IndexInfo, Storage, S); H.prepare(); H.getPart(BlockNumber(4)).print_to_screen(); H.getPart(BlockNumber(5)).print_to_screen(); H.compute(world); H.getPart(BlockNumber(4)).print_to_screen(); H.getPart(BlockNumber(5)).print_to_screen(); INFO("The value of ground energy is " << H.getGroundEnergy()); RealType beta = 10.0; DensityMatrix rho(S,H,beta); rho.prepare(); rho.compute(); for (QuantumState i=0; i<S.getNumberOfStates(); ++i) INFO(rho.getWeight(i)); FieldOperatorContainer Operators(IndexInfo, S, H); Operators.prepareAll(); Operators.computeAll(); ParticleIndex down_index = IndexInfo.getIndex("A",0,down); FieldOperator::BlocksBimap c_map = Operators.getCreationOperator(0).getBlockMapping(); for (FieldOperator::BlocksBimap::right_const_iterator c_map_it=c_map.right.begin(); c_map_it!=c_map.right.end(); c_map_it++) { INFO(c_map_it->first << "->" << c_map_it->second); } GreensFunction GF(S,H,Operators.getAnnihilationOperator(0), Operators.getCreationOperator(0), rho); GF.prepare(); GF.compute(); ComplexVectorType G_ref(10); G_ref << -2.53021005e-01*I, -4.62090702e-01*I, -4.32482782e-01*I, -3.65598615e-01*I, -3.07785174e-01*I, -2.62894141e-01*I, -2.28274316e-01*I, -2.01170772e-01*I, -1.79539602e-01*I, -1.61950993e-01*I; bool result = true; for(int n = 0; n<10; ++n) { INFO(GF(n) << " == " << G_ref(n)); result = (result && compare(GF(n),G_ref(n))); } if (!result) return EXIT_FAILURE; return EXIT_SUCCESS; }
int main(int argc, char* argv[]) { boost::mpi::environment MpiEnv(argc, argv); world = boost::mpi::communicator(); /* As pomerol is an ED code, it requires a finite-size lattice to be * provided. Here is an example of a lattice of 2 sites. */ // First, we construct an empty lattice Lattice L; // Add a site with a name "A", that has 1 orbitals and 2 spins. L.addSite(new Lattice::Site("A",1,2)); // Add one more site with a name "B". It also has 1 orbitals and 2 spins. L.addSite(new Lattice::Site("B",1,2)); // Let us now connect two sites with a hopping term, with matrix element -1. RealType t=1.0; LatticePresets::addHopping(&L, "A","B", -t); /* Now add interaction. In order to provide some custom interaction, one can * give any custom term of 4,6 operators. This is done via * Lattice.addTerm(Lattice::Term) method. * We will use Hubbard-type n_{up}n_{down} interaction. For this and some * other typical interactions, such as SzSz or SS couplings a shortcut is * provided in the LatticePresets class. */ RealType U = 2.0; RealType mu = 1.0; // LatticePresets::addCoulombS adds Un_{up}n_{down} for 1 orbital and 2 spins. LatticePresets::addCoulombS(&L, "A", U, -mu); LatticePresets::addCoulombS(&L, "B", U, -mu); // Let us now print which sites and terms are defined. if (!world.rank()) { INFO("Sites"); // equivalent to std::cout << "Sites" << std::endl; L.printSites(); INFO("Terms"); L.printTerms(2); INFO("Terms with 4 operators"); L.printTerms(4); print_section("Indices"); }; /* In order to go further, we need to introduce the index space. An index * is a number that uniquely identifies a combination of (site,orbital,spin). * The object that takes care of handling indices is called * IndexClassification. */ // Construct IndexClassification IndexClassification IndexInfo(L.getSiteMap()); /* Important remark 1! * Most of the objects that are defined within Pomerol library have the * following semantics. They can be constructed, prepared and computed. * This means * - constructed: No operations are done except from initializing links to * other objects that current class depends on. * - prepared: Typically, this is when all memory allocation takes place. * - computed: The actual computation. This is the most costly operation. * When no heavy computation is done, it can be done during * preparation stage. */ // IndexClassification does not require much computing time, so everything // is calculated during prepare() method. IndexInfo.prepare(); // Print which indices we have IndexInfo.printIndices(); // Save the total number of indices. ParticleIndex IndexSize = IndexInfo.getIndexSize(); print_section("Matrix element storage"); /* We now need to construct the actual Hamiltonian. First, we need to create * it in the index space, e.g. write down a formula with all terms. It is * useful for a subsequent symmetry analysis. The corresponding class is called * an IndexHamiltonian. It is inherited from Operator class, which is designed * to represent a generic second-quantized fermionic operator and fully model * the algebra of fermionic operators. This means one can multiply, add, * subtract it, and also calculate the commutator of two operators, etc. */ // First construct the IndexHamiltonian object. IndexHamiltonian Storage(&L,IndexInfo); // Then prepare it. As previously no long computation required, so everything // is done in the prepare() method. Storage.prepare(); // Print out the Hamiltonian. INFO("Terms"); INFO(Storage); // Let us make a test, that our Hamiltonian commutes with an operator, that // represents the total number of particles. The preset for such operator is // defined in OperatorPresets.h and .cpp files. OperatorPresets::N N(IndexSize); INFO("N terms"); N.printAllTerms(); if ((Storage.commutes(N))) INFO("H commutes with N"); /* The Hamiltonian that is set now, has a set of symmetries. One can identify * them by checking that the IndexHamiltonian commutes with the corresponding * symmetry operator. The class that does it is called Symmetrizer. */ // Construct Symmetrizer Symmetrizer Symm(IndexInfo, Storage); /* At this stage Symmetrizer checks symmetries with some predefined operators, * such as number-of-particles and sz operators. */ Symm.compute(); /* A custom check for a symmetry can be done using * Symmetrizer.checkSymmetry(Operator). * After checking all the symmetry operations, Symmetrizer defines a set of * quantum numbers, which uniquely identifies the closed region in the * phase space. * Calling Symmetrizer::compute(true) will skip all symmetry operations and * result in a single Hamiltonian block in the end. */ /* We shall proceed now with obtaining the spectrum of the Hamiltonian. * First, introduce a basis of Fock states and classify them into blocks * that correspond to a set of quantum numbers. This is done in the * StatesClassification class. It provides all the information about Blocks * and FockStates. */ StatesClassification S(IndexInfo,Symm); S.compute(); /* We now convert the IndexHamiltonian into the basis of Fock States. The * Hamiltonian class is the one, which does it. */ Hamiltonian H(IndexInfo, Storage, S); // Enter all blocks of the Hamiltonian. H.prepare(); // Diagonalize them. H.compute(world); // Get ground energy. INFO("The value of ground energy is " << H.getGroundEnergy()); /* Important remark 2! * All the calculations done in the pomerol code are done with respect to the * block structure of the Hamiltonian. All objects that operate in Fock * space and all thermal objects, such as Green's functions are in fact a * set of pieces (called "parts") that operate on a certain block or set of * blocks. As such all actual computations are done within this parts and * grand objects like Green's functions or Hamiltonian basically just loops * over parts and tells them to run prepare() or compute() methods. */ /* At this stage the Hamiltonian, defined in Fock Space is * entered and diagonalized and it's spectrum and eigenfunctions can be * directly accessed to calculate some observables. * * We shall now proceed to the calculations of thermal quantities, e.g. * assume that our finite-size system was adiabatically connected to a thermal * reservoir, that sets certain temperature (in fact, inverse temperature * beta). This means, that the observables in the systems should be calculated * with a density-matrix exp(-\beta H), rather than by averaging with the * ground state. In the eigenbasis of the Hamiltonian the calculation of * a density matrix is straightforward - it is just \exp(-\beta (E_i - E_0)), * where E_i is an energy of the excited state, and E_0 is the ground energy. * The procedure is done as following: */ // Define inverse temperature RealType beta = 10.0; // Create the Density Matrix. DensityMatrix rho(S,H,beta); // Allocate all internal parts of density matrix. rho.prepare(); // Actually compute the density matrix. rho.compute(); /* Lehmanns representation of the Green's function required creation and * annihilation operators, calculated in the basis of eigenstates of the * Hamiltonian. Creation/AnnihilationOperator are the classes that do it. */ // Let us create c^+_{"A",up} and c_{"A",up} ParticleIndex up_index = IndexInfo.getIndex("A",0,up); CreationOperator CX(IndexInfo,S,H,up_index); CX.prepare(); CX.compute(); AnnihilationOperator C(IndexInfo,S,H,up_index); C.prepare(); C.compute(); // The local Greens function in the Matsubara domain G_{"A",up}(i\omega_n) GreensFunction GF(S,H,C,CX, rho); GF.prepare(); /* Calculate the GF and cache values for 10 Matsubaras. * These values will be fast to retrieve. The other values are also * accessible, but will require a short calculation to be done. */ GF.compute(); for(int n = 0; n<10; ++n) { INFO(n << " | " << GF(n)); } /* The two particle GF is constructed in analogy to the single-particle GF, * it requires 4 operators to be provided though. */ TwoParticleGF Chi(S,H,C,C,CX,CX,rho); /* Some knobs to make calc faster - the larger the values of tolerances, the faster is calc, but rounding errors may show. Typically these errors are less then 10^{-3} of the value. Here are some knobs that give very high-precision. If you want to make things faster, and when many values on different frequencies required - change ReduceResonanceTolerance to something like 10^-4. */ /** A difference in energies with magnitude less than this value is treated as zero. */ Chi.ReduceResonanceTolerance = 1e-8; /** Minimal magnitude of the coefficient of a term to take it into account. */ Chi.CoefficientTolerance = 1e-16; /** Knob that controls the caching frequency. */ Chi.ReduceInvocationThreshold = 1e5; /** Minimal magnitude of the coefficient of a term to take it into account with respect to amount of terms. */ Chi.MultiTermCoefficientTolerance = 1e-6; Chi.prepare(); Chi.compute(world); if (world.rank()==0) { int nm = 2; for(int n1 = -nm; n1<nm; ++n1) for(int n2 = -nm; n2<nm; ++n2) for(int n3 = -nm; n3<nm; ++n3){ INFO(n1 << " " << n2 << " " << n3 << "|" << Chi(n1,n2,n3)); }; }; }