!TODO: Make sure everything has an ierr argument when allocating, and that everything is deallocated at end !TODO: Add symmetry information by using singles excitation generators (store excitations)? (Not urgent) !Module to non-iteratively calculate the RPA energy under the quasi-boson approximation module RPA_Mod use SystemData, only: nel, nBasis, Arr, Brr, G1, tReltvy use excitation_types, only: Excite_2_t use sltcnd_mod, only: sltcnd_excit use constants, only: dp, int64, n_int, maxExcit, stdout use SymExcit3, only: GenExcitations3 use SymExcit4, only: GenExcitations4, ExcitGenSessionType use Determinants, only: get_helement, GetH0Element3 use DeterminantData, only: fDet use bit_rep_data, only: NIfTot use DetBitops, only: EncodeBitDet use Integrals_neci, only: get_umat_el use UMatCache, only: GTID use util_mod, only: get_free_unit, stop_all, neci_flush implicit none !input options logical :: tStabilityAnalysis = .true. logical :: tDirectRPA !global variables integer :: ov_space integer :: virt_start !global arrays real(dp), allocatable :: A_mat(:, :) real(dp), allocatable :: B_mat(:, :) real(dp), allocatable :: OccNumbers(:) contains subroutine RunRPA_QBA(Weight, Energy) implicit none integer :: ierr, i, j, m, n, ex(2, maxExcit), ex2(2, maxExcit), mi_ind, nj_ind integer :: StabilitySize, lWork, info, i_p, m_p, v, mp_ip_ind, ic integer :: nJ(NEl), exflag, mu, id(2, maxExcit), a, i_ind, iunit, ia_ind integer(n_int) :: iLutHF(0:NIfTot) real(dp), intent(out) :: Weight, Energy real(dp) :: Energy_stab, Temp_real, norm, Energy2, H0tmp, Fii real(dp) :: X_norm, Y_norm HElement_t(dp) :: HDiagTemp, hel, hel1, hel2 logical :: tAllExcitsFound, tParity real(dp), allocatable :: Stability(:, :), temp2(:, :), W2(:) real(dp), allocatable :: W(:), Work(:), S_half(:, :), temp(:, :) real(dp), allocatable :: X_stab(:, :), Y_stab(:, :), StabilityCopy(:, :) real(dp), allocatable :: X_chol(:, :), Y_chol(:, :) real(dp), allocatable :: AminB(:, :), AplusB(:, :), temp3(:, :) character(len=*), parameter :: t_r = "RunRPA_QBA" type(ExcitGenSessionType) :: session write(stdout, "(A)") write(stdout, "(A)") "**************************************" if (tDirectRPA) then write(stdout, "(A)") "* Entering DIRECT RPA calculation *" else write(stdout, "(A)") "* Entering FULL RPA calculation *" end if write(stdout, "(A)") "**************************************" write(stdout, "(A)") call neci_flush(stdout) HDiagTemp = get_helement(fDet, fDet, 0) Energy = real(HDiagTemp, dp) write(stdout, "(A,G25.10)") "Reference energy is: ", Energy !Quickly find correlation energy from MP2 for comparison. Temp_real = 0.0_dp tAllExcitsFound = .false. exflag = 3 ex(:, :) = 0 call EncodeBitDet(FDet, iLutHF) HDiagTemp = GetH0Element3(FDet) Fii = real(HDiagTemp, dp) do while (.true.) if (tReltvy) then call GenExcitations4(session, FDet, nJ, exFlag, Ex, tParity, tAllExcitsFound, .false.) else call GenExcitations3(FDet, iLutHF, nJ, exflag, Ex, tParity, tAllExcitsFound, .false.) end if if (tAllExcitsFound) exit !All excits found if (Ex(1, 2) == 0) then ic = 1 else ic = 2 end if hel = get_helement(FDet, nJ, ic, Ex, tParity) H0tmp = getH0Element3(nJ) H0tmp = Fii - H0tmp Temp_real = Temp_real + (hel**2) / H0tmp end do write(stdout, "(A,G25.10)") "For comparison, MP2 correlation energy is: ", Temp_real Weight = (0.0_dp) Energy = (0.0_dp) ov_space = (nBasis - NEl) * NEl virt_start = NEl + 1 write(stdout, "(A,I8)") "1p-1h space is: ", ov_space allocate(A_mat(ov_space, ov_space), stat=ierr) allocate(B_mat(ov_space, ov_space), stat=ierr) if (ierr /= 0) call stop_all(t_r, "alloc Err") A_mat(:, :) = 0.0_dp B_mat(:, :) = 0.0_dp !First, construct A and B, which correspond to: ! <HF| [a*_i a_m [H, a*_n a_j]] |HF> = A ! -<HF| [a*_i a_m [H, a*_j a_n]] |HF> = B do j = 1, nel ex(1, 2) = Brr(j) !Second index in integral ex2(2, 2) = Brr(j) do n = virt_start, nBasis nj_ind = ov_space_ind(n, j) ex(2, 2) = Brr(n) !fourth index in integral ex2(1, 2) = Brr(n) do i = 1, nel ex(2, 1) = Brr(i) !Third index in integral ex2(2, 1) = Brr(i) do m = virt_start, nBasis mi_ind = ov_space_ind(m, i) ex(1, 1) = Brr(m) !First index in integral ex2(1, 1) = Brr(m) if (tDirectRPA) then !No exchange interactions ! Obtain spatial rather than spin indices if required id = gtID(ex) ! Only non-zero contributions if Ms preserved in each term (consider ! physical notation). if ((G1(ex(1, 1))%Ms == G1(ex(2, 1))%Ms) .and. & (G1(ex(1, 2))%Ms == G1(ex(2, 2))%Ms)) then hel1 = get_umat_el(id(1, 1), id(1, 2), id(2, 1), & id(2, 2)) else hel1 = (0) end if A_mat(mi_ind, nj_ind) = real(hel1, dp) !Now for B matrix id = gtID(ex2) ! Only non-zero contributions if Ms preserved in each term (consider ! physical notation). if ((G1(ex2(1, 1))%Ms == G1(ex2(2, 1))%Ms) .and. & (G1(ex2(1, 2))%Ms == G1(ex2(2, 2))%Ms)) then hel1 = get_umat_el(id(1, 1), id(1, 2), id(2, 1), & id(2, 2)) else hel1 = (0) end if B_mat(mi_ind, nj_ind) = real(hel1, dp) else !Full antisymmetrized integrals HEl1 = sltcnd_excit(nJ, Excite_2_t(ex), .false.) HEl2 = sltcnd_excit(nJ, Excite_2_t(ex2), .false.) A_mat(mi_ind, nj_ind) = real(HEl1, dp) B_mat(mi_ind, nj_ind) = real(HEl2, dp) end if end do end do end do end do !Now add the diagonal part to A do i = 1, nel do m = virt_start, nBasis mi_ind = ov_space_ind(m, i) A_mat(mi_ind, mi_ind) = A_mat(mi_ind, mi_ind) + (Arr(Brr(m), 2) - Arr(Brr(i), 2)) ! write(stdout,*) Arr(Brr(m),2)-Arr(Brr(i),2) end do end do !Check that A is hermitian and B is symmetric do i = 1, ov_space do j = 1, ov_space if (abs(B_mat(i, j) - B_mat(j, i)) > 1.0e-7_dp) then write(stdout, *) i, j, B_mat(i, j), B_mat(j, i), abs(B_mat(i, j) - B_mat(j, i)) call stop_all(t_r, "B not symmetric") end if if (abs(A_mat(i, j) - A_mat(j, i)) > 1.0e-7_dp) then write(stdout, *) i, j, A_mat(i, j), A_mat(j, i), abs(A_mat(i, j) - A_mat(j, i)) call stop_all(t_r, "A not hermitian") end if end do end do if (tStabilityAnalysis) then !Calculate stability analysis of HF solution, and from this, calculate RPA amplitudes !This should give identical results to other method, will be quite a bit slower, but also !gives information about the stability of the HF solution in all directions. write(stdout, "(A)") write(stdout, "(A)") "Calculating RPA from stability matrix..." #ifdef CMPLX_ call stop_all(t_r, "Not coded up for complex integrals. Bug ghb24") #endif !Stability = ( A B ) ! ( B* A* ) StabilitySize = 2 * ov_space allocate(Stability(StabilitySize, StabilitySize), stat=ierr) Stability(:, :) = 0.0_dp Stability(1:ov_space, 1:ov_space) = A_mat(1:ov_space, 1:ov_space) !Assume all integrals real to start with ! Stability(ov_space+1:StabilitySize,1:ov_space)=conjg(B(1:ov_space,1:ov_space)) Stability(ov_space + 1:StabilitySize, 1:ov_space) = B_mat(1:ov_space, 1:ov_space) Stability(1:ov_space, ov_space + 1:StabilitySize) = B_mat(1:ov_space, 1:ov_space) !Assume all integrals real to start with ! Stability(ov_space+1:StabilitySize,ov_space+1:StabilitySize)=conjg(A_mat(1:ov_space,1:ov_space)) Stability(ov_space + 1:StabilitySize, ov_space + 1:StabilitySize) = A_mat(1:ov_space, 1:ov_space) ! call writematrix(Stability,'Stability matrix') !Now diagonalize !Find optimal space allocate(StabilityCopy(StabilitySize, StabilitySize)) StabilityCopy(:, :) = Stability(:, :) allocate(W(StabilitySize), stat=ierr) !Eigenvalues of stability matrix allocate(Work(1)) if (ierr /= 0) call stop_all(t_r, "alloc err") W(:) = 0.0_dp lWork = -1 info = 0 call dsyev('V', 'U', StabilitySize, Stability, StabilitySize, W, Work, lWork, info) if (info /= 0) call stop_all(t_r, 'workspace query failed') lwork = int(work(1)) + 1 deallocate(work) allocate(work(lwork)) call dsyev('V', 'U', StabilitySize, Stability, StabilitySize, W, Work, lwork, info) if (info /= 0) call stop_all(t_r, "Diag failed") deallocate(work) do i = 1, StabilitySize if (W(i) < 0.0_dp) then write(stdout, *) i, W(i) call stop_all(t_r, "HF solution not stable. Not local minimum. Recompute HF.") end if end do if (.not. tdirectRPA) then write(stdout, "(A)") "Stability matrix positive definite. HF solution is minimum. RPA stable" end if !Now compute S^(1/2), and transform into original basis allocate(S_half(StabilitySize, StabilitySize)) S_half(:, :) = 0.0_dp do i = 1, StabilitySize S_half(i, i) = sqrt(W(i)) end do allocate(temp(StabilitySize, StabilitySize)) call dgemm('n', 'n', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, Stability, StabilitySize, & S_half, StabilitySize, 0.0_dp, temp, StabilitySize) call dgemm('n', 't', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, temp, StabilitySize, Stability, & StabilitySize, 0.0_dp, S_half, StabilitySize) !S_half is now S^1/2 in the original basis !Check this by squaring it. call dgemm('n', 't', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, S_half, StabilitySize, S_half, & StabilitySize, 0.0_dp, temp, StabilitySize) do i = 1, StabilitySize do j = 1, StabilitySize if (abs(StabilityCopy(i, j) - temp(i, j)) > 1.0e-7_dp) then call stop_all(t_r, 'S^1/2 not calculated correctly in original basis') end if end do end do temp(:, :) = 0.0_dp do i = 1, ov_space temp(i, i) = 1.0_dp end do do i = ov_space + 1, StabilitySize temp(i, i) = -1.0_dp end do allocate(temp2(StabilitySize, StabilitySize)) call dgemm('n', 'n', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, S_half, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) call dgemm('n', 'n', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, temp2, StabilitySize, S_half, & StabilitySize, 0.0_dp, temp, StabilitySize) !Now diagonalize temp = S^(1/2) (1 0 \\ 0 -1 ) S^(1/2) lWork = -1 allocate(W2(StabilitySize)) allocate(Work(1)) W2(:) = 0.0_dp call dsyev('V', 'U', StabilitySize, temp, StabilitySize, W2, Work, lWork, info) if (info /= 0) call stop_all(t_r, 'workspace query failed') lwork = int(work(1)) + 1 deallocate(work) allocate(work(lwork)) call dsyev('V', 'U', StabilitySize, temp, StabilitySize, W2, Work, lwork, info) if (info /= 0) call stop_all(t_r, "Diag failed") deallocate(work) ! call writevector(W2,'Excitation energies') ! temp now holds the eigenvectors X~ Y~ ! W2 runs over StabilitySize eigenvalues (ov_space*2). Therefor we expect redundant pairs of +-W2, corresponding ! to pairs of eigenvectors (X^v Y^v) and (X^v* Y^v*) (Same in real spaces). do i = 1, ov_space !This they are listed in order of increasing eigenvalue, we should be able to easily check that they pair up if (abs(W2(i) + W2(StabilitySize - i + 1)) > 1.0e-7_dp) then write(stdout, *) i, StabilitySize - i + 1, W2(i), W2(StabilitySize - i + 1), abs(W2(i) - W2(StabilitySize - i + 1)) call stop_all(t_r, "Excitation energy eigenvalues do not pair") end if end do !We actually have everything we need for the energy already now. However, calculate X and Y too. !Now construct (X Y) = S^(-1/2) (X~ Y~) !First get S^(-1/2) in the original basis S_half(:, :) = 0.0_dp do i = 1, StabilitySize S_half(i, i) = -sqrt(W(i)) end do call dgemm('n', 'n', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, Stability, StabilitySize, S_half, & StabilitySize, 0.0_dp, temp2, StabilitySize) call dgemm('n', 't', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, temp2, StabilitySize, Stability, & StabilitySize, 0.0_dp, S_half, StabilitySize) !S_half is now S^(-1/2) in the original basis !Now multiply S^(-1/2) (X~ y~) call dgemm('n', 'n', StabilitySize, StabilitySize, StabilitySize, 1.0_dp, S_half, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) !Check that eigenvectors are also paired. !Rotations among degenerate sets will screw this up though ! do i=1,ov_space ! write(stdout,*) "Eigenvectors: ",i,StabilitySize-i+1,W2(i),W2(StabilitySize-i+1) ! do j=1,StabilitySize ! write(stdout,*) j,temp2(j,i),temp2(j,StabilitySize-i+1) ! end do ! end do ! call writematrix(temp2,'X Y // Y X',.true.) !temp2 should now be a matrix of (Y X) ! (X Y) ! This is the other way round to normal, but due to the fact that our eigenvalues are ordered -ve -> +ve ! TODO: Are the signs of this matrix correct? allocate(X_stab(ov_space, ov_space)) !First index is (m,i) compound index. Second is the eigenvector index. allocate(Y_stab(ov_space, ov_space)) X_stab(:, :) = 0.0_dp Y_stab(:, :) = 0.0_dp !Put the eigenvectors corresponding to *positive* eigenvalues into the X_stab and Y_stab arrays. X_stab(1:ov_space, 1:ov_space) = temp2(1:ov_space, ov_space + 1:StabilitySize) Y_stab(1:ov_space, 1:ov_space) = -temp2(ov_space + 1:StabilitySize, ov_space + 1:StabilitySize) deallocate(temp2) !Normalize the eigenvectors appropriately do mu = 1, ov_space norm = 0.0_dp Y_norm = 0.0_dp X_norm = 0.0_dp do i = 1, ov_space norm = norm + X_stab(i, mu) * X_stab(i, mu) - Y_stab(i, mu) * Y_stab(i, mu) Y_norm = Y_norm + Y_stab(i, mu) * Y_stab(i, mu) X_norm = X_norm + X_stab(i, mu) * X_stab(i, mu) end do if (norm <= 0.0_dp) then write(stdout, *) "Norm^2 for vector ", mu, " is: ", norm call stop_all(t_r, 'norm undefined') end if norm = sqrt(norm) do i = 1, ov_space X_stab(i, mu) = X_stab(i, mu) / norm Y_stab(i, mu) = Y_stab(i, mu) / norm end do if (Y_norm > X_norm / 2.0_dp) then write(stdout, *) "Warning: hole amplitudes large for excitation: ", mu, & " Quasi-boson approximation breaking down." write(stdout, *) "Norm of X component: ", X_norm write(stdout, *) "Norm of Y component: ", Y_norm end if end do ! call writematrix(X_stab,'X',.true.) !Now check orthogonality call Check_XY_orthogonality(X_stab, Y_stab) ! call writevector(W2,'Stab_eigenvalues') !Now check that we satisfy the original RPA equations !For the *positive* eigenvalue space (since we have extracted eigenvectors corresponding to this), check that: ! ( A B ) (X) = E_v(X ) ! ( B A ) (Y) (-Y) deallocate(temp) allocate(temp(StabilitySize, ov_space)) temp(1:ov_space, 1:ov_space) = X_stab(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = Y_stab(:, :) allocate(temp2(StabilitySize, ov_space)) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, StabilityCopy, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (W2(i + ov_space) * X_stab(j, i))) > 1.0e-6_dp) then write(stdout, *) i, j, temp2(j, i), (W2(i + ov_space) * X_stab(j, i)), W2(i + ov_space) call stop_all(t_r, "RPA equations not satisfied for positive frequencies in X matrix") end if end do end do do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j + ov_space, i) - (-W2(i + ov_space) * Y_stab(j, i))) > 1.0e-6_dp) then write(stdout, *) i, j, temp2(j + ov_space, i), (-W2(i + ov_space) * Y_stab(j, i)), -W2(i + ov_space) call stop_all(t_r, "RPA equations not satisfied for positive frequencies in Y matrix") end if end do end do deallocate(temp, temp2) !Is is also satisfied the other way around? !Check that we also satisfy (still for the *positive* eigenvalues): ! ( A B ) (Y) = -E_v(Y ) ! ( B A ) (X) (-X) allocate(temp(StabilitySize, ov_space)) temp(1:ov_space, 1:ov_space) = Y_stab(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = X_stab(:, :) allocate(temp2(StabilitySize, ov_space)) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, StabilityCopy, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (-W2(i + ov_space) * Y_stab(j, i))) > 1.0e-6_dp) then call stop_all(t_r, "RPA equations not satisfied for negative frequencies in X matrix") end if end do end do do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j + ov_space, i) - (W2(i + ov_space) * X_stab(j, i))) > 1.0e-6_dp) then call stop_all(t_r, "RPA equations not satisfied for negative frequencies in Y matrix") end if end do end do deallocate(temp, temp2) !TODO: Finally, check that we satisfy eq. 1 in the Scuseria paper for X and Y defined for positive eigenvalues... do i = ov_space + 1, StabilitySize do j = 1, StabilitySize StabilityCopy(i, j) = -StabilityCopy(i, j) end do end do !Stability copy is now (A B // -B -A) allocate(temp(StabilitySize, ov_space)) allocate(temp2(StabilitySize, ov_space)) temp = 0.0_dp temp(1:ov_space, 1:ov_space) = X_stab(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = Y_stab(:, :) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, StabilityCopy, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (W2(i + ov_space) * X_stab(j, i))) > 1.0e-7_dp) then call stop_all(t_r, "RPA equations not satisfied for X") end if end do end do do i = 1, ov_space do j = ov_space + 1, StabilitySize if (abs(temp2(j, i) - (W2(i + ov_space) * Y_stab(j - ov_space, i))) > 1.0e-7_dp) then call stop_all(t_r, "RPA equations not satisfied for Y") end if end do end do deallocate(temp, temp2) !Now calculate energy, in two different ways: !1. -1/2 Tr[A] + 1/2 sum_v E_v(positive) Energy_stab = 0.0_dp do i = 1, ov_space Energy_stab = Energy_stab + W2(ov_space + i) - A_mat(i, i) end do Energy_stab = Energy_stab / 2.0_dp if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy from stability analysis (plasmonic RPA-TDA excitation energies): ", & Energy_stab else write(stdout, "(A,G25.10)") "Full RPA energy from stability analysis (plasmonic RPA-TDA excitation energies): ", & Energy_stab end if Energy_stab = 0.0_dp !E = 0.25 * Tr[BZ] where Z = Y X^-1 allocate(temp2(ov_space, ov_space)) temp2(:, :) = 0.0_dp !Find X^-1 call d_inv(X_stab, temp2) ! call writematrix(temp2,'X^-1',.true.) allocate(temp(ov_space, ov_space)) !Find Z (temp) call dgemm('n', 'n', ov_space, ov_space, ov_space, 1.0_dp, Y_stab, ov_space, temp2, ov_space, 0.0_dp, temp, ov_space) !Find BZ (temp2) call dgemm('n', 'n', ov_space, ov_space, ov_space, 1.0_dp, B_mat, ov_space, temp, ov_space, 0.0_dp, temp2, ov_space) !Take trace of BZ do i = 1, ov_space Energy_stab = Energy_stab + temp2(i, i) end do Energy_stab = Energy_stab / 2.0_dp if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy from stability analysis (Ring-CCD: 1/2 Tr[BZ]): ", Energy_stab else write(stdout, "(A,G25.10)") "Full RPA energy from stability analysis (Ring-CCD: 1/2 Tr[BZ]): ", Energy_stab end if Energy_stab = 0.0_dp do i = 1, ov_space Y_norm = 0.0_dp do j = 1, ov_space Y_norm = Y_norm + Y_stab(j, i)**2 end do Energy_stab = Energy_stab - W2(i + ov_space) * Y_norm end do if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy from stability analysis (Y-matrix): ", Energy_stab else write(stdout, "(A,G25.10)") "Full RPA energy from stability analysis (Y-matrix): ", Energy_stab end if deallocate(W2, W, temp, temp2, StabilityCopy, Stability) end if write(stdout, *) write(stdout, "(A)") "Calculating RPA via Cholesky decomposition" !Now, we calculate the RPA amplitudes via consideration of the boson operators. !This will generally be quicker, and should give the same result. !Calculate A-B allocate(AminB(ov_space, ov_space)) do i = 1, ov_space do j = 1, ov_space AminB(j, i) = A_mat(j, i) - B_mat(j, i) end do end do if (.true.) then !Optional !Check that AminB is positive definite - required for Cholesky decomposition allocate(temp(ov_space, ov_space)) temp(:, :) = AminB(:, :) !Also check it is symmetric! do i = 1, ov_space do j = 1, ov_space if (abs(AminB(i, j) - AminB(j, i)) > 1.0e-7_dp) then call stop_all(t_r, "A - B matrix is not symmetric") end if end do end do !Now diagonalise lWork = -1 allocate(W(ov_space)) allocate(Work(1)) W(:) = 0.0_dp call dsyev('V', 'U', ov_space, temp, ov_space, W, Work, lWork, info) if (info /= 0) call stop_all(t_r, 'workspace query failed') lwork = int(work(1)) + 1 deallocate(work) allocate(work(lwork)) call dsyev('V', 'U', ov_space, temp, ov_space, W, Work, lWork, info) if (info /= 0) call stop_all(t_r, "Diag failed") deallocate(work) do i = 1, ov_space if (W(i) < 0.0_dp) then call writevector(W, 'AminB eigenvalues') call stop_all(t_r, "A-B not positive definite - cannot do cholesky decomposition") end if end do deallocate(temp, W) end if !Construct triangular matrices via cholesky decomposition call dpotrf('U', ov_space, AminB, ov_space, info) if (info /= 0) call stop_all(t_r, 'Cholesky decomposition failed.') !Now set lower triangular part to zero do i = 1, ov_space do j = 1, i - 1 AminB(i, j) = 0.0_dp end do end do if (.true.) then !Optional ! call writematrix(AminB,'T') !Check cholesky decomposition successful allocate(temp(ov_space, ov_space)) call dgemm('T', 'N', ov_space, ov_space, ov_space, 1.0_dp, AminB, ov_space, AminB, ov_space, 0.0_dp, temp, ov_space) do i = 1, ov_space do j = 1, ov_space if (abs(temp(i, j) - (A_mat(i, j) - B_mat(i, j))) > 1.0e-7_dp) then call stop_all(t_r, 'Cholesky decomposition not as expected') end if end do end do deallocate(temp) end if allocate(AplusB(ov_space, ov_space), stat=ierr) do i = 1, ov_space do j = 1, ov_space AplusB(j, i) = A_mat(j, i) + B_mat(j, i) end do end do allocate(temp(ov_space, ov_space)) call dgemm('N', 'N', ov_space, ov_space, ov_space, 1.0_dp, AminB, ov_space, AplusB, ov_space, 0.0_dp, temp, ov_space) call dgemm('N', 'T', ov_space, ov_space, ov_space, 1.0_dp, temp, ov_space, AminB, ov_space, 0.0_dp, AplusB, ov_space) deallocate(temp) !Check T (A + B) T^T is actually symmetric do i = 1, ov_space do j = 1, ov_space if (abs(AplusB(i, j) - AplusB(j, i)) > 1.0e-7_dp) then call stop_all(t_r, 'Not symmetric') end if end do end do !AplusB is now actually T (A + B) T^T !Diagonalize this lWork = -1 allocate(W(ov_space)) allocate(Work(1)) W(:) = 0.0_dp call dsyev('V', 'U', ov_space, AplusB, ov_space, W, Work, lWork, info) if (info /= 0) call stop_all(t_r, 'workspace query failed') lwork = int(work(1)) + 1 deallocate(work) allocate(work(lwork)) call dsyev('V', 'U', ov_space, AplusB, ov_space, W, Work, lWork, info) if (info /= 0) call stop_all(t_r, "Diag failed") deallocate(work) do i = 1, ov_space if (W(i) < 0.0_dp) then call stop_all(t_r, "Excitation energies^2 not positive. Error in diagonalization.") end if end do ! call writevector(sqrt(W),'Eigenvalues') !Excitation energies are now the +- sqrt of eigenvalues !Construct W^(-1/2) T^T R (temp) !and W^(1/2) T^-1 R (temp2) allocate(temp(ov_space, ov_space)) call dgemm('T', 'N', ov_space, ov_space, ov_space, 1.0_dp, AminB, ov_space, AplusB, ov_space, 0.0_dp, temp, ov_space) do i = 1, ov_space !Mulitply each column by corresponding eigenvalue^(-1/2). Only deal with positive eigenvalues here do j = 1, ov_space temp(j, i) = temp(j, i) * (1.0_dp / (sqrt(sqrt(W(i))))) end do end do allocate(temp2(ov_space, ov_space)) allocate(temp3(ov_space, ov_space)) !Temp3 will hold the inverse of the T matrix temporarily. call d_inv(AminB, temp3) call dgemm('N', 'N', ov_space, ov_space, ov_space, 1.0_dp, temp3, ov_space, AplusB, ov_space, 0.0_dp, temp2, ov_space) deallocate(temp3) do i = 1, ov_space !Mulitply each column by corresponding eigenvalue^(1/2). Only deal with positive eigenvalues here do j = 1, ov_space temp2(j, i) = temp2(j, i) * (sqrt(sqrt(W(i)))) end do end do allocate(X_Chol(ov_space, ov_space)) allocate(Y_Chol(ov_space, ov_space)) do i = 1, ov_space do j = 1, ov_space X_Chol(j, i) = 0.5_dp * (temp(j, i) + temp2(j, i)) Y_Chol(j, i) = 0.5_dp * (temp(j, i) - temp2(j, i)) end do end do ! do i=1,ov_space ! do j=1,ov_space ! if(abs(Y_Chol(i,j)).gt.1.0e-7) then ! write(stdout,*) "Found non-zero Y matrix value..." ! end if ! end do ! end do !Y_Chol and X_Chol now cover all eigenvectors. !Eigensystem can be represented as: ! ( X ) val=sqrt(W) and ( Y* ) val=-sqrt(W) ! ( Y ) ( X* ) call Check_XY_orthogonality(X_Chol, Y_Chol) !Are X and Y vectors the same as from the stability matrix? !I guess there will be different rotation among degenerate sets, so not a well defined comparison. ! if(tStabilityAnalysis) then ! do i=1,ov_space ! do j=1,ov_space ! if(abs(X_Chol(j,i)-X_Stab(j,i)).gt.1.0e-7) then ! write(stdout,*) j,i,X_Chol(j,i),X_Stab(j,i) !! call stop_all(t_r,"Calculation of X matrix not the same as via stability matrix") ! end if ! if(abs(Y_Chol(j,i)-Y_Stab(j,i)).gt.1.0e-7) then ! write(stdout,*) j,i,Y_Chol(j,i),Y_Stab(j,i) !! call stop_all(t_r,"Calculation of Y matrix not the same as via stability matrix") ! end if ! end do ! end do ! end if !Now check that we satisfy the original RPA equations !For the *positive* eigenvalue space (since we have extracted eigenvectors corresponding to this), check that: ! ( A B ) (X) = E_v(X ) ! ( B A ) (Y) (-Y) deallocate(temp, temp2) allocate(Stability(StabilitySize, StabilitySize)) Stability(:, :) = 0.0_dp Stability(1:ov_space, 1:ov_space) = A_mat(1:ov_space, 1:ov_space) Stability(ov_space + 1:StabilitySize, 1:ov_space) = B_mat(1:ov_space, 1:ov_space) Stability(1:ov_space, ov_space + 1:StabilitySize) = B_mat(1:ov_space, 1:ov_space) Stability(ov_space + 1:StabilitySize, ov_space + 1:StabilitySize) = A_mat(1:ov_space, 1:ov_space) allocate(temp(StabilitySize, ov_space)) temp(1:ov_space, 1:ov_space) = X_Chol(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = Y_Chol(:, :) allocate(temp2(StabilitySize, ov_space)) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, Stability, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (sqrt(W(i)) * X_Chol(j, i))) > 1.0e-6_dp) then write(stdout, *) i, j, temp2(j, i), (sqrt(W(i)) * X_Chol(j, i)), sqrt(W(i)) call stop_all(t_r, "RPA equations not satisfied for positive frequencies in X matrix") end if end do end do do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j + ov_space, i) - (-sqrt(W(i)) * Y_Chol(j, i))) > 1.0e-6_dp) then write(stdout, *) i, j, temp2(j + ov_space, i), (-sqrt(W(i)) * Y_Chol(j, i)), -sqrt(W(i)) call stop_all(t_r, "RPA equations not satisfied for positive frequencies in Y matrix") end if end do end do deallocate(temp, temp2) !Is is also satisfied the other way around? !Check that we also satisfy (still for the *positive* eigenvalues): ! ( A B ) (Y) = -E_v(Y ) ! ( B A ) (X) (-X) allocate(temp(StabilitySize, ov_space)) temp(1:ov_space, 1:ov_space) = Y_Chol(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = X_Chol(:, :) allocate(temp2(StabilitySize, ov_space)) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, Stability, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (-sqrt(W(i)) * Y_Chol(j, i))) > 1.0e-6_dp) then call stop_all(t_r, "RPA equations not satisfied for negative frequencies in X matrix") end if end do end do do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j + ov_space, i) - (sqrt(W(i)) * X_Chol(j, i))) > 1.0e-6_dp) then call stop_all(t_r, "RPA equations not satisfied for negative frequencies in Y matrix") end if end do end do deallocate(temp, temp2) !TODO: Finally, check that we satisfy eq. 1 in the Scuseria paper for X and Y defined for positive eigenvalues... do i = ov_space + 1, StabilitySize do j = 1, StabilitySize Stability(i, j) = -Stability(i, j) end do end do !Stability copy is now (A B // -B -A) allocate(temp(StabilitySize, ov_space)) allocate(temp2(StabilitySize, ov_space)) temp = 0.0_dp temp(1:ov_space, 1:ov_space) = X_Chol(:, :) temp(ov_space + 1:StabilitySize, 1:ov_space) = Y_Chol(:, :) call dgemm('n', 'n', StabilitySize, ov_space, StabilitySize, 1.0_dp, Stability, StabilitySize, temp, & StabilitySize, 0.0_dp, temp2, StabilitySize) do i = 1, ov_space do j = 1, ov_space if (abs(temp2(j, i) - (sqrt(W(i)) * X_Chol(j, i))) > 1.0e-7_dp) then call stop_all(t_r, "RPA equations not satisfied for X") end if end do end do do i = 1, ov_space do j = ov_space + 1, StabilitySize if (abs(temp2(j, i) - (sqrt(W(i)) * Y_Chol(j - ov_space, i))) > 1.0e-7_dp) then call stop_all(t_r, "RPA equations not satisfied for Y") end if end do end do deallocate(temp, temp2, Stability) Energy2 = real(HDiagTemp, dp) norm = 0.0_dp do i = 1, ov_space norm = norm + A_mat(i, i) end do Energy2 = Energy2 - norm / 2.0_dp norm = 0.0_dp do i = 1, ov_space !Run over positive eigenvalue pairs norm = norm + sqrt(W(i)) end do Energy2 = Energy2 + norm / 2.0_dp if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy (plasmonic RPA-TDA excitation energies): ", & Energy2 - real(HDiagTemp, dp) else write(stdout, "(A,G25.10)") "Full RPA energy (plasmonic RPA-TDA excitation energies): ", & Energy2 - real(HDiagTemp, dp) end if Energy = 0.0_dp allocate(temp(ov_space, ov_space)) allocate(temp2(ov_space, ov_space)) temp(:, :) = 0.0_dp call d_inv(X_Chol, temp) call dgemm('n', 'n', ov_space, ov_space, ov_space, 1.0_dp, Y_Chol, ov_space, temp, ov_space, 0.0_dp, temp2, ov_space) call dgemm('n', 'n', ov_space, ov_space, ov_space, 1.0_dp, B_Mat, ov_space, temp2, ov_space, 0.0_dp, temp, ov_space) do i = 1, ov_space Energy = Energy + temp(i, i) end do Energy = Energy / 2.0_dp if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy (Ring-CCD: 1/2 Tr[BZ]): ", Energy else write(stdout, "(A,G25.10)") "Full RPA energy (Ring-CCD: 1/2 Tr[BZ]): ", Energy end if deallocate(temp, temp2) HDiagTemp = get_helement(fDet, fDet, 0) Energy = real(HDiagTemp, dp) do v = 1, ov_space norm = 0.0_dp do i = 1, ov_space norm = norm + (abs(Y_Chol(i, v)))**2.0_dp end do Energy = Energy - norm * sqrt(W(v)) end do if (tDirectRPA) then write(stdout, "(A,G25.10)") "Direct RPA energy (Y matrix): ", Energy - real(HDiagTemp, dp) else write(stdout, "(A,G25.10)") "Full RPA energy (Y matrix): ", Energy - real(HDiagTemp, dp) end if write(stdout, "(A)") write(stdout, "(A)") "RPA calculation completed successfully" write(stdout, "(A)") write(stdout, "(A)") "Calculating 1RDM..." allocate(OccNumbers(nBasis)) !This is the GS 1RDM, which is diagonal in RPA OccNumbers(:) = 0.0_dp !For occupied orbital: Gamma(i,i) = 1 - 1/2 \sum_{mu,a} |Y^{mu}_ia|^2 !For virtual orbital: Gamma(a,a) = 1/2 \sum_{mu,i} |Y^{mu}_ia|^2 do i = 1, nel do mu = 1, ov_space do a = virt_start, nBasis ia_ind = ov_space_ind(a, i) OccNumbers(i) = OccNumbers(i) + 0.5_dp * abs(Y_Chol(ia_ind, mu))**2.0_dp end do end do OccNumbers(i) = 1.0_dp - OccNumbers(i) end do do a = virt_start, nBasis do mu = 1, ov_space do i = 1, nel ia_ind = ov_space_ind(a, i) OccNumbers(a) = OccNumbers(a) + 0.5_dp * abs(Y_Chol(ia_ind, mu))**2.0_dp end do end do end do write(stdout, "(A)") "Writing RPA occupation numbers to file: RPA_OccNumbers" write(stdout, "(A)") "" iunit = get_free_unit() open(iunit, file='RPA_OccNumbers', status='unknown') write(iunit, "(A)") "# Orbital(Energy_order) Orbital Fock eigenvalue RPA_Occupation" do i = 1, nBasis i_ind = Brr(i) write(iunit, "(2I9,2G25.10)") i, i_ind, Arr(i_ind, 2), OccNumbers(i) end do close(iunit) deallocate(A_mat, B_mat, X_Chol, Y_Chol, OccNumbers) end subroutine RunRPA_QBA SUBROUTINE d_inv(mat, matinv) implicit none real(dp), INTENT(IN) :: mat(:, :) real(dp), dimension(size(mat, 1), size(mat, 2)), intent(out) :: matinv real(dp), dimension(size(mat, 1), size(mat, 2)) :: matdum integer, dimension(size(mat, 1)) :: ipiv integer :: nsize, msize, i, info msize = size(mat, 1) nsize = size(mat, 2) matdum = mat matinv = 0.0_dp do i = 1, msize matinv(i, i) = 1.0_dp end do info = 0 call dGETRF(msize, nsize, matdum, nsize, ipiv, info) ! IF (INFO /= 0) STOP 'Error with d_inv 1' if (info /= 0) then write(stdout, *) 'Warning from d_inv 1', info call stop_all("d_inv", "Warning from d_inv 1") end if call dGETRS('n', msize, nsize, matdum, nsize, IPIV, matinv, msize, info) if (info /= 0) call stop_all("d_inv", 'Error with d_inv 2') END SUBROUTINE d_inv subroutine Check_XY_orthogonality(X, Y) implicit none real(dp), intent(in) :: X(ov_space, ov_space), Y(ov_space, ov_space) integer :: i, i_p, m, m_p, v, mi_ind, mp_ip_ind, mu, mu_p, j real(dp) :: temp character(len=*), parameter :: t_r = "Check_XY_orthogonality" !Eigenvectors orthonormal do mu = 1, ov_space do mu_p = 1, ov_space temp = 0.0_dp do i = 1, ov_space temp = temp + X(i, mu) * X(i, mu_p) - Y(i, mu) * Y(i, mu_p) end do if ((mu == mu_p) .and. ((abs(temp) - 1.0_dp) > 1.0e-6_dp)) then write(stdout, *) mu, mu_p, temp call writevector(X(:, mu), 'X(:,mu)') call writevector(Y(:, mu), 'Y(:,mu)') call stop_all(t_r, 'X/Y not normalized') else if ((mu /= mu_p) .and. (abs(temp) > 1.0e-6_dp)) then write(stdout, *) mu, mu_p call stop_all(t_r, 'X/Y not orthogonal') end if end do end do !Rows orthonormal too do i = 1, ov_space do j = 1, ov_space temp = 0.0_dp do mu = 1, ov_space temp = temp + X(i, mu) * X(j, mu) - Y(i, mu) * Y(j, mu) end do if ((i /= j) .and. (abs(temp) > 1.0e-7_dp)) then write(stdout, *) i, j, temp call stop_all(t_r, 'X/Y rows not orthogonal') else if ((i == j) .and. (abs(temp) - 1.0_dp) > 1.0e-7_dp) then write(stdout, *) i, j, temp call stop_all(t_r, 'X/Y rows not normalized') end if end do end do end subroutine Check_XY_orthogonality !a is fast integer function ov_space_ind(a, i) implicit none integer, intent(in) :: i, a ov_space_ind = (i - 1) * (nBasis - NEl) + a - NEl end function ov_space_ind subroutine WriteMatrix(mat, matname, tOneLine) implicit none real(dp), intent(in) :: mat(:, :) character(len=*), intent(in) :: matname integer :: i, j logical :: tOneLine write(stdout, *) "Writing out matrix: ", trim(matname) write(stdout, "(A,I7,A,I7)") "Size: ", size(mat, 1), " by ", size(mat, 2) do i = 1, size(mat, 1) do j = 1, size(mat, 2) if (tOneLine) then write(stdout, "(G25.10)", advance='no') mat(i, j) else write(stdout, "(2I6,G25.10)") i, j, mat(i, j) end if end do write(stdout, *) end do end subroutine WriteMatrix subroutine WriteVector(vec, vecname) implicit none real(dp), intent(in) :: vec(:) character(len=*), intent(in) :: vecname integer :: i write(stdout, *) "Writing out vector: ", trim(vecname) write(stdout, "(A,I7,A,I7)") "Size: ", size(vec, 1) do i = 1, size(vec, 1) ! write(stdout,"(G25.10)",advance='no') vec(i) write(stdout, "(G25.10)") vec(i) end do write(stdout, *) end subroutine WriteVector end module RPA_Mod