DiagNatOrbMat Subroutine

public subroutine DiagNatOrbMat()

Arguments

None

Contents

Source Code


Source Code

    subroutine DiagNatOrbMat()

        ! The diagonalisation routine reorders the orbitals in such a way that
        ! the corresponding orbital labels are lost. In order to keep the spin
        ! and spatial symmetries, each symmetry must be fed into the
        ! diagonalisation routine separately. The best way to do this is to
        ! order the orbitals so that all the alpha orbitals follow all the beta
        ! orbitals, with the occupied orbitals first, in terms of symmetry, and
        ! the virtual second, also ordered by symmetry. This gives us
        ! flexibility w.r.t rotating only the occupied or only virtual and
        ! looking at high spin states.

        use MemoryManager, only: TagIntType

        real(dp) :: SumTrace, SumDiagTrace
        real(dp), allocatable :: WORK2(:), EvaluesSym(:), NOMSym(:, :)
        integer :: ierr, i, j, x, z, Sym, LWORK2
        integer :: SymStartInd, NoSymBlock, PrevSym, StartOccVirt, EndOccVirt, Prev, NoOcc
        integer(TagIntType) :: EvaluesSymTag, NOMSymTag, WORK2Tag
        character(len=*), parameter :: t_r = 'DiagNatOrbMat'

        DiagNatOrbMat_Time%timer_name = 'DiagNatOrb'
        call set_timer(DiagNatOrbMat_Time, 30)

        do x = 1, NoSpinCyc
            if (tSeparateOccVirt) then
                if (x == 1) then
                    if (tStoreSpinOrbs) then
                        NoOcc = nOccBeta
                    else
                        NoOcc = NEl / 2
                    end if
                    Prev = 0
                else if (x == 2) then
                    NoOcc = nOccAlpha
                    Prev = SpatOrbs
                end if
            else
                NoOcc = 0
            end if
            if (tRotateVirtOnly) then
                do i = 1, NoOcc
                    do j = 1, SpatOrbs
                        NatOrbMat(i + Prev, j + Prev) = 0.0_dp
                        NatOrbMat(j + Prev, i + Prev) = 0.0_dp
                        if (i == j) NatOrbMat(i + Prev, j + Prev) = 1.0_dp
                    end do
                    Evalues(i + Prev) = 1.0_dp
                end do
            else if (tRotateOccOnly) then
                do i = NoOcc + 1, SpatOrbs
                    do j = 1, SpatOrbs
                        NatOrbMat(i + Prev, j + Prev) = 0.0_dp
                        NatOrbMat(j + Prev, i + Prev) = 0.0_dp
                        if (i == j) NatOrbMat(i + Prev, j + Prev) = 1.0_dp
                    end do
                    Evalues(i + Prev) = 1.0_dp
                end do
            else if (tSeparateOccVirt) then
                do i = 1, NoOcc
                    do j = NoOcc + 1, SpatOrbs
                        NatOrbMat(i + Prev, j + Prev) = 0.0_dp
                        NatOrbMat(j + Prev, i + Prev) = 0.0_dp
                    end do
                end do
            end if
        end do

        ! Test that we're not breaking symmetry.
        do i = 1, NoOrbs
            do j = 1, NoOrbs
                if (tStoreSpinOrbs) then
                    if ((int(G1(SymLabelList2_rot(i))%sym%S, 4) /= int(G1(SymLabelList2_rot(j))%sym%S, 4))) then
                        if (abs(NatOrbMat(i, j)) >= 1.0e-15_dp) then
                            write(stdout, '(6A8,A20)') 'i', 'j', 'Label i', 'Label j', 'Sym i', 'Sym j', 'Matrix value'
                            write(stdout, '(6I3,F40.20)') i, j, SymLabelList2_rot(i), SymLabelList2_rot(j), &
                                int(G1(SymLabelList2_rot(i))%sym%S, 4), &
                                int(G1(SymLabelList2_rot(j))%sym%S, 4), NatOrbMat(i, j)
                            if (tUseMP2VarDenMat) then
                                write(stdout, *) '**WARNING** - There is a non-zero NatOrbMat value between " &
                                 & //"orbitals of different symmetry.'
                                write(stdout, *) 'These elements will be ignored, and the symmetry maintained " &
                                 & //"in the final transformation matrix.'
                            else
                                call stop_all(t_r, 'Non-zero NatOrbMat value between different symmetries.')
                            end if
                        end if
                        NatOrbMat(i, j) = 0.0_dp
                    end if
                else
                    if ((int(G1(SymLabelList2_rot(i) * 2)%sym%S, 4) /= int(G1(SymLabelList2_rot(j) * 2)%sym%S, 4))) then
                        if (abs(NatOrbMat(i, j)) >= 1.0e-15_dp) then
                            write(stdout, '(6A8,A20)') 'i', 'j', 'Label i', 'Label j', 'Sym i', 'Sym j', 'Matrix value'
                            write(stdout, '(6I3,F40.20)') i, j, SymLabelList2_rot(i), SymLabelList2_rot(j), &
                                int(G1(SymLabelList2_rot(i) * 2)%sym%S, 4), int(G1(SymLabelList2_rot(j) * 2)%sym%S, 4), NatOrbMat(i, j)
                            if (tUseMP2VarDenMat) then
                                write(stdout, *) '**WARNING** - There is a non-zero NatOrbMat value between orbitals of " &
                                & //"different symmetry.'
                                write(stdout, *) 'These elements will be ignored, and the symmetry maintained in the " &
                                & //"final transformation matrix.'
                            else
                                call stop_all(t_r, 'Non-zero NatOrbMat value between different symmetries.')
                            end if
                        end if
                        NatOrbMat(i, j) = 0.0_dp
                    end if
                end if
            end do
        end do

        SumTrace = 0.0_dp
        do i = 1, NoOrbs
            SumTrace = SumTrace + NatOrbMat(i, i)
        end do

        write(stdout, *) 'Calculating eigenvectors and eigenvalues of NatOrbMat'
        call neci_flush(stdout)

        ! If we are using spin orbitals, need to feed in the alpha and beta
        ! spins separately. Otherwise these jumble up and the final ordering
        ! is uncorrect. There should be no non-zero values between these, but
        ! can put a check in for this.

        do x = 1, NoSpinCyc

            ! If we want to maintain the symmetry, we cannot have all the
            ! orbitals jumbled up when the diagonaliser reorders the
            ! eigenvectors. Must instead feed each symmetry block in
            ! separately. This means that although the transformed orbitals
            ! are jumbled within the symmetry blocks, the symmetry labels are
            ! all that are relevant and these are unaffected.
            StartOccVirt = 1
            EndOccVirt = 2
            if (tRotateVirtOnly) StartOccVirt = 2
            if (tRotateOccOnly) EndOccVirt = 1

            do z = StartOccVirt, EndOccVirt
                if (x == 1) then
                    if (z == 1) PrevSym = 1
                    if (z == 2) PrevSym = 9
                else if (x == 2) then
                    if (z == 1) PrevSym = 17
                    if (z == 2) PrevSym = 25
                end if

                Sym = 0
                LWORK2 = -1

                do while (Sym <= 7)

                    NoSymBlock = SymLabelCounts2_rot(2, Sym + PrevSym)

                    ! This is one less than the index that the symmetry starts,
                    ! so that when we run through i = 1,...,  we can start at
                    ! SymStartInd+i.
                    SymStartInd = SymLabelCounts2_rot(1, Sym + PrevSym) - 1

                    if (NoSymBlock > 1) then

                        allocate(NOMSym(NoSymBlock, NoSymBlock), stat=ierr)
                        call LogMemAlloc('NOMSym', NoSymBlock**2, 8, t_r, NOMSymTag, ierr)
                        if (ierr /= 0) call stop_all(t_r, "Problem allocating NOMSym.")
                        allocate(EvaluesSym(NoSymBlock), stat=ierr)
                        call LogMemAlloc('EvaluesSym', NoSymBlock, 8, t_r, EvaluesSymTag, ierr)
                        if (ierr /= 0) call stop_all(t_r, "Problem allocating EvaluesSym.")

                        LWORK2 = 3 * NoSymBlock + 1
                        allocate(WORK2(LWORK2), stat=ierr)
                        call LogMemAlloc('WORK2', LWORK2, 8, t_r, WORK2Tag, ierr)
                        if (ierr /= 0) call stop_all(t_r, "Problem allocating WORK2.")

                        do j = 1, NoSymBlock
                            do i = 1, NoSymBlock
                                NOMSym(i, j) = NatOrbMat(SymStartInd + i, SymStartInd + j)
                            end do
                        end do

                        write(stdout, *) '*****'
                        write(stdout, *) 'Symmetry ', Sym, 'with x ', x, ' and z ', z, ' has ', NoSymBlock, ' orbitals.'
                        write(stdout, *) 'The NatOrbMat for this symmetry block is '
                        do i = 1, NoSymBlock
                            do j = 1, NoSymBlock
                                write(stdout, '(F20.10)', advance='no') NOMSym(j, i)
                            end do
                            write(stdout, *) ''
                        end do

                        ! NOMSym goes in as the original NOMSym, comes out as
                        ! the eigenvectors (Coefficients). EvaluesSym comes out
                        ! as the eigenvalues in ascending order.
                        call dsyev('V', 'L', NoSymBlock, NOMSym, NoSymBlock, EvaluesSym, WORK2, LWORK2, ierr)

                        write(stdout, *) 'After diagonalization, the e-vectors (diagonal elements) of this matrix are,'
                        do i = 1, NoSymBlock
                            write(stdout, '(F20.10)', advance='no') EvaluesSym(i)
                        end do
                        write(stdout, *) ''
                        write(stdout, *) 'These go from orbital,', SymStartInd + 1, ' to ', SymStartInd + NoSymBlock

                        do i = 1, NoSymBlock
                            Evalues(SymStartInd + i) = EvaluesSym(i)
                        end do

                        ! CAREFUL if eigenvalues are put in ascending order,
                        ! this may not be correct, with the labelling system.
                        ! Maybe better to just take coefficients and transform
                        ! TMAT2DRot in transform2elints. A check that comes out
                        ! as diagonal is a check of this routine anyway.

                        write(stdout, *) 'The eigenvectors (coefficients) for symmetry block ', Sym
                        do i = 1, NoSymBlock
                            do j = 1, NoSymBlock
                                write(stdout, '(F20.10)', advance='no') NOMSym(j, i)
                            end do
                            write(stdout, *) ''
                        end do

                        ! Directly fill the coefficient matrix with the
                        ! eigenvectors from the diagonalization.
                        do j = 1, NoSymBlock
                            do i = 1, NoSymBlock
                                NatOrbMat(SymStartInd + i, SymStartInd + j) = NOMSym(i, j)
                            end do
                        end do

                        deallocate(WORK2)
                        call LogMemDealloc(t_r, WORK2Tag)

                        deallocate(NOMSym)
                        call LogMemDealloc(t_r, NOMSymTag)

                        deallocate(EvaluesSym)
                        call LogMemDealloc(t_r, EvaluesSymTag)

                    else if (NoSymBlock == 1) then
                        ! The eigenvalue is the lone value, while the
                        ! eigenvector is 1.

                        Evalues(SymStartInd + 1) = NatOrbMat(SymStartInd + 1, SymStartInd + 1)
                        NatOrbMat(SymStartInd + 1, SymStartInd + 1) = 1.0_dp
                        write(stdout, *) '*****'
                        write(stdout, *) 'Symmetry ', Sym, ' has only one orbital.'
                        write(stdout, *) 'Copying diagonal element,', SymStartInd + 1, 'to NatOrbMat'
                    end if

                    Sym = Sym + 1
                end do
            end do
        end do

        write(stdout, *) 'Matrix diagonalised'
        call neci_flush(stdout)

        SumDiagTrace = 0.0_dp
        do i = 1, NoOrbs
            SumDiagTrace = SumDiagTrace + Evalues(i)
        end do
        if ((abs(SumDiagTrace - SumTrace)) > 10.0_dp) then
            write(stdout, *) 'Sum of diagonal NatOrbMat elements : ', SumTrace
            write(stdout, *) 'Sum of eigenvalues : ', SumDiagTrace
            write(stdout, *) 'WARNING, The trace of the 1RDM matrix before diagonalisation is not equal to that after.'
        end if

        call halt_timer(DiagNatOrbMat_Time)

    end subroutine DiagNatOrbMat