|
| 1 | +###################################################################### |
| 2 | +# Example 1 |
| 3 | +# Finite temperature calculations for the half-filled Hubbard model |
| 4 | +# on the Bethe lattice. Storing total energy and entropy per site |
| 5 | +###################################################################### |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +from gem.fragment import Fragment |
| 9 | +from gem.lattice import Lattice |
| 10 | +from gem.solvers.simple_ed import SimpleED |
| 11 | + |
| 12 | +# Parameters that determine the physical dimentions |
| 13 | +# 1 orbital with 2 spins, B=3 bath per orbital, total 8 |
| 14 | +B = 3 |
| 15 | +nimp = 2 |
| 16 | +nbath = nimp*B |
| 17 | +ntot = nimp+nbath |
| 18 | + |
| 19 | +# Physical Parameters |
| 20 | +U = 2.0 |
| 21 | +Tlist = np.hstack( (np.array([0.0]), np.logspace(np.log10(1e-2), np.log10(1.0), 41)) ) |
| 22 | +mu = 0.0 |
| 23 | + |
| 24 | +# Self-consistency Parameters |
| 25 | +itmax = 300 |
| 26 | +mix = 0.1 |
| 27 | +tol = 1e-4 |
| 28 | +spin_pen = 0.0 |
| 29 | +Tsmearing = 1e-4 |
| 30 | + |
| 31 | +# Non-interacting density of states and lattice object |
| 32 | +e_list = np.linspace(-1, 1, 5001) |
| 33 | +wks = np.sqrt(1 - e_list**2) |
| 34 | +wks /= np.sum(wks) |
| 35 | +eks = e_list[:, None, None] * np.eye(2, dtype=np.complex128) |
| 36 | + |
| 37 | +lattice = Lattice(eks, wk_list=wks) |
| 38 | + |
| 39 | +# Local Hamiltonian |
| 40 | +eloc = np.zeros((nimp, nimp)) |
| 41 | +eloc[0,0] = -U/2. |
| 42 | +eloc[1,1] = -U/2. |
| 43 | +# Interaction tensor of the embedded space |
| 44 | +Utensor = np.zeros((nimp, nimp, nimp, nimp)) |
| 45 | +Utensor[0,0,1,1] = U |
| 46 | +Utensor[1,1,0,0] = U |
| 47 | + |
| 48 | +# SimpleED solver initialization |
| 49 | +edsolver = SimpleED(ntot, use_Ntot=True, use_Sz=True, |
| 50 | + N_sector=None, Sz_sector=None, dtype=np.float64, |
| 51 | + solver_params={'spin_pen': spin_pen}) |
| 52 | + |
| 53 | +# Fragment initialization |
| 54 | +Lambda0 = None; R0 = None |
| 55 | +fragment = Fragment(nimp, nbath, eloc, Utensor, edsolver, Lambda=Lambda0, R=R0, verbose=2) |
| 56 | + |
| 57 | +def check_convergence(R_new,L_new, R_old,L_old): |
| 58 | + # Only 1 spin and gauge invariant difference |
| 59 | + L_eval_new, UL_new = np.linalg.eigh(L_new[::2,::2]) |
| 60 | + L_eval_old, UL_old = np.linalg.eigh(L_old[::2,::2]) |
| 61 | + diff_R = np.abs(np.abs(UL_old @ R_old[::2,::2]) - np.abs(UL_new @ R_new[::2,::2])).max() |
| 62 | + diff_Lambda = np.abs(L_eval_new - L_eval_old).max() |
| 63 | + diff = max(diff_R, diff_Lambda) |
| 64 | + return diff |
| 65 | + |
| 66 | +Elist = [] |
| 67 | +Slist = [] |
| 68 | +docclist = [] |
| 69 | + |
| 70 | +for T in Tlist: |
| 71 | + print('--------------------------------------') |
| 72 | + print(f'GEM loop started with U={U} and T={T}') |
| 73 | + |
| 74 | + # Self consistency loop |
| 75 | + for it in range(itmax): |
| 76 | + print(f"----- ghost-RISB iteration {it} / {itmax} -----") |
| 77 | + |
| 78 | + lattice.solve_qp([fragment], T=T, Tsmearing=Tsmearing) |
| 79 | + |
| 80 | + fragment.update_hybridization(T=T, use_Sz=True) |
| 81 | + |
| 82 | + fragment.solve_impurity(mu, T=T) |
| 83 | + |
| 84 | + Lambda_old = fragment.Lambda.copy() |
| 85 | + R_old = fragment.R.copy() |
| 86 | + |
| 87 | + fragment.update_self_energy(T=T, use_Sz=True) |
| 88 | + |
| 89 | + Lambda_new = fragment.Lambda |
| 90 | + R_new = fragment.R |
| 91 | + |
| 92 | + diff = check_convergence(R_new,Lambda_new, R_old,Lambda_old) |
| 93 | + print(f"iteration: {it} diff={diff}") |
| 94 | + |
| 95 | + fragment.Lambda = (1 - mix) * Lambda_new + mix * Lambda_old |
| 96 | + fragment.R = (1 - mix) * R_new + mix * R_old |
| 97 | + |
| 98 | + |
| 99 | + if (diff < tol and it > 2) or it == itmax - 1: |
| 100 | + print(f"----- Exiting loop with diff={diff} after {it} iterations (max={itmax}) -----") |
| 101 | + break |
| 102 | + |
| 103 | + |
| 104 | + docc = fragment.E2loc/U |
| 105 | + docclist.append(docc) |
| 106 | + ekin = lattice.compute_ekin([fragment],T=T,Tsmearing=Tsmearing) |
| 107 | + eimp = fragment.compute_energy() |
| 108 | + etot = (eimp+ekin).real |
| 109 | + # Free-energy functional |
| 110 | + L = lattice.compute_functional([fragment],T=T, Tsmearing=Tsmearing).real |
| 111 | + S = (etot-L)/T if T > 0 else 0.0 |
| 112 | + |
| 113 | + Elist.append(etot) |
| 114 | + Slist.append(S) |
| 115 | + |
| 116 | + print('--------------------------------------') |
| 117 | + print(f'GEM loop ended with U={U} and T={T}') |
| 118 | + print(f'returning docc={docc}, Etot={etot} and S={S}') |
| 119 | + print('--------------------------------------') |
| 120 | + |
| 121 | + |
| 122 | +np.savetxt('Tlist.dat',Tlist) |
| 123 | +np.savetxt('Elist.dat',Elist) |
| 124 | +np.savetxt('Slist.dat',Slist) |
| 125 | +np.savetxt('docclist.dat',docclist) |
0 commit comments