费米子严格对角化以及松原格林函数计算

思路

/* Generic tips:
 * The calculation is done by computing a set of objects in the following order:
 * Lattice -> IndexClassification -> IndexHamiltonian -> Symmetrizer ->
 * -> StatesClassification -> Hamiltonian -> FieldOperator
 ;
 * (for thermal objects, such as GFs in Matsubara domain)
 * -> DensityMatrix -> Greens Function
 *                  -> TwoParticle GF   -> Vertex4
 * The detailed explanation of each class is given below.
 */

  

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();
    // Truncate blocks that have only small contributions
    // rho.truncateBlocks(1e-15);

    /* 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;
    /** 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();
    std::vector<boost::tuple<ComplexType, ComplexType, ComplexType> > freqs_2pgf;
    Chi.compute(false, freqs_2pgf, 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));
                };
    };

    /* We define a quadratic operator, O_{ij} = c_i^+ c_j, to compute its ensemble average
     * and its fluctuations (dynamical susceptibility).
     * QuadraticOperator is the class which defines O_{ij}.
     */

    // define a quadratic operator, O = c^+_{up} c_{up}
    print_section("Quadratic oparator");
    QuadraticOperator N_up(IndexInfo, S, H, up_index, up_index);
    N_up.prepare();
    N_up.compute();

    // compute an ensemble average, <O>
    print_section("Ensemble average");
    EnsembleAverage EA(S,H, N_up, rho);
    EA.prepare();
    RealType occup_up = real(EA.getResult());
    INFO("Occupation number of up spin is " << occup_up);

    /* The dynamical susceptibility is computed by Susceptibility class.
     * One can obtain either F[ <A(tau)B> ] or F[ <A(tau)B> - <A><B> ],
     * where F denotes Fourier transform from tau to Matsubara frequency.
     * To choose the latter quantity, call subtractDisconnected() method.
     *
     * There are 3 variants of subtractDisconnected() method.
     *  1. <A> and <B> are computed in Susceptibility class
     *  2. use precomputed <A> and <B>
     *  3. use predefined QuadraticOperator instances, A and B
     */
    print_section("Dynamical susceptibility");
    Susceptibility Sus(S,H, N_up, N_up, rho);
    Sus.prepare();
    Sus.compute();
    // subtract <n_up><n_uo>
    Sus.subtractDisconnected();  // 1
    //Sus.subtractDisconnected(occup_up, occup_up);  // 2
    //Sus.subtractDisconnected(N_up, N_up);  // 3
    for(int n=0; n<10; n++){
        INFO(n << " " << Sus(n));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值