1!--------------------------------------------------------------------------------------------------!
2!  DFTB+: general package for performing fast atomistic simulations                                !
3!  Copyright (C) 2006 - 2019  DFTB+ developers group                                               !
4!                                                                                                  !
5!  See the LICENSE file for terms of usage and distribution.                                       !
6!--------------------------------------------------------------------------------------------------!
7
8#:include 'common.fypp'
9
10!> Linear response excitations and gradients with respect to atomic coordinates
11module dftbp_linrespgrad
12  use dftbp_assert
13  use dftbp_arpack
14  use dftbp_linrespcommon
15  use dftbp_commontypes
16  use dftbp_slakocont
17  use dftbp_shortgamma
18  use dftbp_accuracy
19  use dftbp_constants, only : Hartree__eV, au__Debye
20  use dftbp_nonscc, only : NonSccDiff
21  use dftbp_scc, only : TScc
22  use dftbp_blasroutines
23  use dftbp_eigensolver
24  use dftbp_message
25  use dftbp_taggedoutput, only : TTaggedWriter, tagLabels
26  use dftbp_sorting
27  use dftbp_qm
28  use dftbp_transcharges
29  implicit none
30  private
31
32  public :: LinRespGrad_old
33
34
35  !> Tolerance for ARPACK solver.
36  real(dp), parameter :: ARTOL = epsilon(1.0_rsp)
37
38
39  !> Maximal allowed iteration in the ARPACK solver.
40  integer, parameter :: MAX_AR_ITER = 300
41
42  character(lc) :: tmpStr
43
44
45  !> Names of output files
46  character(*), parameter :: arpackOut = "ARPACK.DAT"
47  character(*), parameter :: testArpackOut = "TEST_ARPACK.DAT"
48  character(*), parameter :: transitionsOut = "TRA.DAT"
49  character(*), parameter :: XplusYOut = "XplusY.DAT"
50  character(*), parameter :: excitedQOut = "XCH.DAT"
51  character(*), parameter :: excitedDipoleOut = "XREST.DAT"
52  character(*), parameter :: excitedCoefsOut = "COEF.DAT"
53  character(*), parameter :: excitationsOut = "EXC.DAT"
54  character(*), parameter :: transDipOut = "TDP.DAT"
55  character(*), parameter :: singlePartOut = "SPX.DAT"
56
57
58  !> Communication with ARPACK for progress information
59  integer :: logfil, ndigit, mgetv0
60  integer :: msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd
61  integer :: mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd
62  integer :: mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd
63
64
65  !> Common block of ARPACK variables
66  common /debug/ logfil, ndigit, mgetv0,&
67      &    msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd,&
68      &    mnaupd, mnaup2, mnaitr, mneigh, mnapps, mngets, mneupd,&
69      &    mcaupd, mcaup2, mcaitr, mceigh, mcapps, mcgets, mceupd
70
71contains
72
73
74  !> This subroutine analytically calculates excitations and gradients of excited state energies
75  !> based on Time Dependent DFRT
76  subroutine LinRespGrad_old(tSpin, natom, iAtomStart, grndEigVecs, grndEigVal, sccCalc, dq,&
77      & coord0, nexc, nstat0, symc, SSqr, filling, species0, HubbardU, spinW, rnel, iNeighbour,&
78      & img2CentCell, orb, tWriteTagged, fdTagged, taggedWriter, fdMulliken, fdCoeffs, tGrndState,&
79      & fdXplusY, fdTrans, fdSPTrans, fdTradip, tArnoldi, fdArnoldi, fdArnoldiDiagnosis, fdExc, &
80      & tEnergyWindow, energyWindow, tOscillatorWindow, oscillatorWindow, tCacheCharges, omega,&
81      & allOmega, onsMEs, shift, skHamCont, skOverCont, excgrad, derivator, rhoSqr, occNatural,&
82      & naturalOrbs)
83
84    !> spin polarized calculation
85    logical, intent(in) :: tSpin
86
87    !> number of atoms
88    integer, intent(in) :: natom
89
90    !> index vector for S and H matrices
91    integer, intent(in) :: iAtomStart(:)
92
93    !> ground state MO-coefficients
94    real(dp), intent(in) :: grndEigVecs(:,:,:)
95
96    !> ground state MO-energies
97    real(dp), intent(in) :: grndEigVal(:,:)
98
99    !> Self-consistent charge module settings
100    type(TScc), intent(in) :: sccCalc
101
102    !> converged ground state Mulliken gross charges - atomic charges
103    real(dp), intent(in) :: dq(:)
104
105    !> atomic positions
106    real(dp), intent(in) :: coord0(:,:)
107
108    !> number of excited states to solve for
109    integer, intent(in) :: nexc
110
111    !> state of interest (< 0 find brightest, 0 calculate all nexc states, > 0 that specific state)
112    integer, intent(in) :: nstat0
113
114    !> symmetry required singlet ('S'), triplet ("T") or both ("B")
115    character, intent(in) :: symc
116
117    !> square overlap matrix between basis functions, both triangles required
118    real(dp), intent(in) :: SSqr(:,:)
119
120    !> occupations for the states
121    real(dp), intent(in) :: filling(:,:)
122
123    !> chemical species of each atom
124    integer, intent(in) :: species0(:)
125
126    !> ground state Hubbard U values for each species
127    real(dp), intent(in) :: HubbardU(:)
128
129    !> ground state spin derivatives for each species
130    real(dp), intent(in) :: spinW(:)
131
132    !> real number of electrons in system
133    real(dp), intent(in) :: rnel
134
135    !> Atomic neighbour lists
136    integer, intent(in) :: iNeighbour(0:,:)
137
138    !> Mapping of atom number to central cell atom number
139    integer, intent(in) :: img2CentCell(:)
140
141    !> data type for atomic orbital information
142    type(TOrbitals), intent(in) :: orb
143
144    !> print tag information
145    logical, intent(in) :: tWriteTagged
146
147    !> file descriptor for the tagged data output
148    integer, intent(in) :: fdTagged
149
150    !> tagged writer
151    type(TTaggedWriter), intent(inout) :: taggedWriter
152
153    !> file unit for excited Mulliken populations?
154    integer, intent(in) :: fdMulliken
155
156    !> file unit if the coefficients for the excited states should be written to disc
157    integer, intent(in) :: fdCoeffs
158
159    !> Add the ground state to the excited state transition density matrix when determining the
160    !> natural orbitals
161    logical, intent(in) :: tGrndState
162
163    !> file for X+Y data
164    integer, intent(in) :: fdXplusY
165
166    !> File unit for single particle (KS) transitions if required
167    integer, intent(in) :: fdTrans
168
169    !> File unit for single particle transition dipole strengths
170    integer, intent(in) :: fdSPTrans
171
172    !> File unit for transition dipole data
173    integer, intent(in) :: fdTradip
174
175    !> write state of Arnoldi solver to disc
176    logical, intent(in) :: tArnoldi
177
178    !> file unit for Arnoldi write out
179    integer, intent(in) :: fdArnoldi
180
181    !> file unit for Arnoldi solver tests, if this is < 1 no tests are performed
182    integer, intent(in) :: fdArnoldiDiagnosis
183
184    !> file handle for excitation energies
185    integer, intent(in) :: fdExc
186
187    !> is an energy window specified
188    logical, intent(in) :: tEnergyWindow
189
190    !> energy window for transitions above energy of nexc-th single particle transtion
191    real(dp), intent(in) :: energyWindow
192
193    !> is an oscillator window specified
194    logical, intent(in) :: tOscillatorWindow
195
196    !> the window for transitions not included in nexc and energy window (if used)
197    real(dp), intent(in) :: oscillatorWindow
198
199    !> should transition charges be cached or evaluated on the fly?
200    logical, intent(in) :: tCacheCharges
201
202    !> excitation energy of state nstat0
203    real(dp), intent(out) :: omega
204
205    !> excitation energy of all states that have been solved
206    real(dp), allocatable, intent(inout) :: allOmega(:)
207
208    !> onsite corrections if in use
209    real(dp), allocatable :: onsMEs(:,:,:,:)
210
211    !> shift vector for potentials in the ground state
212    real(dp), intent(in), optional :: shift(:)
213
214    !> non-SCC hamitonian data
215    type(OSlakoCont), intent(in), optional :: skHamCont
216
217    !> overlap data
218    type(OSlakoCont), intent(in), optional :: skOverCont
219
220    !> excitation energy gradient with respect to atomic positions
221    real(dp), intent(out), optional :: excgrad(:,:)
222
223    !> Differentiator for H0 and S matrices.
224    class(NonSccDiff), intent(in), optional :: derivator
225
226    !> ground state square density matrix
227    real(dp), intent(in), optional :: rhoSqr(:,:,:)
228
229    !> Occupation numbers for natural orbitals from the excited state density matrix
230    real(dp), intent(out), optional :: occNatural(:)
231
232    !> the single particle eigenvectors themselves for the excited state density matrix.
233    real(dp), intent(out), optional :: naturalOrbs(:,:,:)
234
235    real(dp) :: Ssq(nexc)
236    real(dp), allocatable :: gammaMat(:,:), snglPartTransDip(:,:)
237    real(dp), allocatable :: stimc(:,:,:), wij(:)
238    real(dp), allocatable :: dqex(:), sposz(:), osz(:), xpy(:), xmy(:), pc(:,:)
239    real(dp), allocatable :: t(:,:), rhs(:), woo(:), wvv(:), wov(:)
240    real(dp), allocatable :: evec(:,:), eval(:), transitionDipoles(:,:)
241    integer, allocatable :: win(:), getij(:,:)
242
243    !> array from pairs of single particles states to compound index - should replace with a more
244    !> compact data structure in the cases where there are oscilator windows
245    integer, allocatable :: iatrans(:,:)
246
247    character, allocatable :: symmetries(:)
248
249    integer :: nocc, nocc_r, nvir_r, nxoo_r, nxvv_r
250    integer :: nxov, nxov_ud(2), nxov_r, nxov_d, nxov_rd
251    integer :: norb
252    integer :: i, j, iSpin, isym, iLev, nStartLev, nEndLev
253    integer :: nSpin
254    character :: sym
255
256    real(dp) :: energyThreshold
257
258    integer :: nStat
259
260    !> control variables
261    logical :: tZVector, tCoeffs, tTradip
262
263    !> printing data
264    logical :: tMulliken
265
266    !> should gradients be calculated
267    logical :: tForces
268
269    !> transition charges, either cached or evaluated on demand
270    type(TTransCharges) :: transChrg
271
272
273    ! ARPACK library variables
274    ndigit = -3
275    ! Output unit:
276    logfil = fdArnoldi
277    msgets = 0
278    msaitr = 0
279    msapps = 0
280    mseigt = 0
281    mseupd = 0
282    if(tArnoldi) then
283      msaupd = 1
284      msaup2 = 1
285    else
286      msaupd = 0
287      msaup2 = 0
288    endif
289    ! End of ARPACK communication variables
290
291    @:ASSERT(fdExc > 0)
292
293    ! work out which data files are required, based on whether they have valid file IDs (>0)
294    tMulliken = (fdMulliken > 0)
295    tCoeffs = (fdCoeffs > 0)
296    tTradip = (fdTradip > 0)
297
298    if (tMulliken) then
299      open(fdMulliken, file=excitedQOut,position="rewind", status="replace")
300      close(fdMulliken)
301      open(fdMulliken, file=excitedDipoleOut, position="rewind", status="replace")
302      close(fdMulliken)
303    end if
304
305    @:ASSERT(fdArnoldi > 0)
306    if (tArnoldi) then
307      open(fdArnoldi, file=arpackOut, position="rewind", status="replace")
308    end if
309
310    nSpin = size(grndEigVal, dim=2)
311    @:ASSERT(nSpin > 0 .and. nSpin <=2)
312
313    norb = orb%nOrb
314
315    @:ASSERT(present(excgrad) .eqv. present(shift))
316    @:ASSERT(present(excgrad) .eqv. present(skHamCont))
317    @:ASSERT(present(excgrad) .eqv. present(skOverCont))
318    @:ASSERT(present(excgrad) .eqv. present(rhoSqr))
319    @:ASSERT(present(excgrad) .eqv. present(derivator))
320
321    @:ASSERT(present(occNatural) .eqv. present(naturalOrbs))
322
323    ! count initial number of transitions from occupied to empty states
324    nxov_ud = 0
325    do iSpin = 1, nSpin
326      !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i,j) SCHEDULE(RUNTIME) REDUCTION(+:nxov_ud)
327      do i = 1, norb - 1
328        do j = i, norb
329          if (filling(i,iSpin) > filling(j,iSpin) + elecTolMax) then
330            nxov_ud(iSpin) = nxov_ud(iSpin) + 1
331          end if
332        end do
333      end do
334      !$OMP  END PARALLEL DO
335    end do
336    nxov = sum(nxov_ud)
337
338    if (nexc + 1 >= nxov) then
339      write(tmpStr,"(' Insufficent single particle excitations, ',I0,&
340          & ', for required number of excited states ',I0)")nxov, nexc
341      call error(tmpStr)
342    end if
343
344    tForces = .false.
345    ! are gradients required?
346    if (present(excgrad)) then
347      if (size(excgrad) > 0) then
348        tForces = .true.
349      end if
350    end if
351
352
353    !> is a z vector required?
354    tZVector = tForces .or. tMulliken .or. tCoeffs .or. present(naturalOrbs)
355
356    ! Sanity checks
357    nstat = nstat0
358    if (nstat < 0 .and. symc /= "S") then
359      call error("Linresp: Brightest mode only available for singlets.")
360    end if
361    if (nstat /= 0 .and. symc == "B") then
362      call error("Linresp: Both symmetries not allowed if a specific state is excited")
363    end if
364    if (tZVector .and. nexc > nxov - 1) then
365      call error("Linresp: With gradients/properties, nexc can be greater than the number of&
366          & occupied-virtual excitations")
367    end if
368
369    ! Select symmetries to process
370    if (.not. tSpin) then
371      select case (symc)
372      case ("B")
373        ALLOCATE(symmetries(2))
374        symmetries(:) = [ "T", "S" ]
375      case ("S")
376        ALLOCATE(symmetries(1))
377        symmetries(:) = [ "S" ]
378      case ("T")
379        ALLOCATE(symmetries(1))
380        symmetries(:) = [ "T" ]
381      end select
382    else
383      ! ADG: temporary solution for spin polarized case.
384      ALLOCATE(symmetries(1))
385      symmetries(:) = [ " " ]
386    end if
387
388    ! Allocation for general arrays
389    ALLOCATE(gammaMat(natom, natom))
390    ALLOCATE(snglPartTransDip(nxov, 3))
391    ALLOCATE(stimc(norb, norb, nSpin))
392    ALLOCATE(wij(nxov))
393    ALLOCATE(win(nxov))
394    ALLOCATE(eval(nexc))
395    ALLOCATE(getij(nxov, 2))
396    ALLOCATE(transitionDipoles(nxov, 3))
397    ALLOCATE(sposz(nxov))
398
399    ! Overlap times wave function coefficients - most routines in DFTB+ use lower triangle (would
400    ! remove the need to symmetrize the overlap and ground state density matrix in the main code if
401    ! this could be used everywhere in these routines)
402    do iSpin = 1, nSpin
403      call symm(stimc(:,:,iSpin), "L", SSqr, grndEigVecs(:,:,iSpin))
404    end do
405
406    ! ground state Hubbard U softened coulombic interactions
407    call sccCalc%getAtomicGammaMatrix(gammaMat, iNeighbour, img2CentCell)
408
409    ! Oscillator strengths for exited states, when needed.
410    ALLOCATE(osz(nexc))
411
412    ! Find all single particle transitions and KS energy differences for cases that go from filled
413    ! to empty states
414    call getSPExcitations(grndEigVal, filling, wij, getij)
415
416    ! put them in ascending energy order
417    if (tOscillatorWindow) then
418      ! use a stable sort so that degenerate transitions from the same single particle state are
419      ! grouped together in the results, allowing these to be selected together (since how intensity
420      ! is shared out over degenerate transitions is arbitrary between eigensolvers/platforms).
421      call merge_sort(win, wij, 1.0_dp*epsilon(1.0))
422    else
423      ! do not require stability, use the usual routine to sort, saving an O(N) workspace
424      call index_heap_sort(win, wij)
425    end if
426    wij = wij(win)
427
428    ! dipole strength of transitions between K-S states
429    call calcTransitionDipoles(coord0, win, nxov_ud(1), getij, iAtomStart, stimc, grndEigVecs,&
430        & snglPartTransDip)
431
432    ! single particle excitation oscillator strengths
433    sposz(:) = twothird * wij(:) * sum(snglPartTransDip**2, dim=2)
434
435    if (tOscillatorWindow .and. tZVector ) then
436      call error("Incompabilitity between excited state property evaluation and an oscillator&
437          & strength window at the moment.")
438    end if
439
440    if (tOscillatorWindow .or. tEnergyWindow) then
441
442      if (.not. tEnergyWindow) then
443
444        ! find transitions that are strongly dipole allowed (> oscillatorWindow)
445        call dipselect(wij, sposz, win, snglPartTransDip,nxov_rd, oscillatorWindow, grndEigVal,&
446            & getij)
447
448      else
449
450        ! energy window above the lowest nexc single particle transitions
451        energyThreshold = wij(nexc) + energyWindow
452        nxov_r = count(wij <= energyThreshold)
453
454        nxov_d = 0
455        if (tOscillatorWindow) then
456
457          ! find transitions that are strongly dipole allowed (> oscillatorWindow)
458          if (nxov_r < nxov) then
459            ! find transitions that are strongly dipole allowed (> oscillatorWindow)
460            call dipselect(wij(nxov_r+1:), sposz(nxov_r+1:), win(nxov_r+1:),&
461                & snglPartTransDip(nxov_r+1:,:),nxov_d, oscillatorWindow,&
462                & grndEigVal, getij)
463          end if
464
465        end if
466
467        nxov_rd = nxov_r + nxov_d
468
469      end if
470    else
471
472      nxov_rd = nxov
473
474    end if
475
476    ! just in case energy/dipole windows add no extra states, and is due to an arpack solver
477    ! requirement combined with the need to get at least nexc states
478    nxov_rd = max(nxov_rd,min(nexc+1,nxov))
479
480    call TTransCharges_init(transChrg, iAtomStart, stimc, grndEigVecs, nxov_rd, nxov_ud(1), getij,&
481        & win, tCacheCharges)
482
483
484    if (fdXplusY >  0) then
485      open(fdXplusY, file=XplusYOut, position="rewind", status="replace")
486    end if
487
488    if(fdTrans>0) then
489      open(fdTrans, file=transitionsOut, position="rewind", status="replace")
490      write(fdTrans,*)
491    endif
492
493    ! single particle transition dipole file
494    if (fdTradip > 0) then
495      open(fdTradip, file=transDipOut, position="rewind", status="replace")
496      write(fdTradip,*)
497      write(fdTradip,'(5x,a,5x,a,2x,a)') "#", 'w [eV]', 'Transition dipole (x,y,z) [Debye]'
498      write(fdTradip,*)
499      write(fdTradip,'(1x,57("="))')
500      write(fdTradip,*)
501    endif
502
503    ! excitation energies
504    open(fdExc, file=excitationsOut, position="rewind", status="replace")
505    write(fdExc,*)
506    if (tSpin) then
507      write(fdExc,'(5x,a,7x,a,9x,a,9x,a,6x,a,4x,a)') 'w [eV]', 'Osc.Str.', 'Transition','Weight',&
508          & 'KS [eV]','D<S*S>'
509    else
510      write(fdExc,'(5x,a,7x,a,9x,a,9x,a,6x,a,4x,a)') 'w [eV]','Osc.Str.', 'Transition','Weight',&
511          & 'KS [eV]','Sym.'
512    end if
513
514    write(fdExc,*)
515    write(fdExc,'(1x,80("="))')
516    write(fdExc,*)
517
518    ! single particle excitations (output file and tagged file if needed).  Was used for nxov_rd =
519    ! size(wij), but now for just states that are actually included in the excitation calculation.
520    call writeSPExcitations(wij, win, nxov_ud(1), getij, fdSPTrans, sposz, nxov_rd, tSpin)
521    ALLOCATE(evec(nxov_rd, nexc))
522
523    do isym = 1, size(symmetries)
524
525      sym = symmetries(isym)
526      call buildAndDiagExcMatrix(tSpin, wij(:nxov_rd), sym, win, nxov_ud(1), nxov_rd, iAtomStart,&
527          & stimc, grndEigVecs, filling, getij, gammaMat, species0, spinW, transChrg,&
528          & fdArnoldiDiagnosis, eval, evec, onsMEs, orb)
529
530      ! Excitation oscillator strengths for resulting states
531      call getOscillatorStrengths(sym, snglPartTransDip(1:nxov_rd,:), wij(:nxov_rd), eval, evec,&
532          & filling, win, nxov_ud(1), getij, nstat, osz, tTradip, transitionDipoles)
533
534      if (tSpin) then
535        call getExcSpin(Ssq, nxov_ud(1), getij, win, eval, evec, wij(:nxov_rd), filling, stimc,&
536            & grndEigVecs)
537        call writeExcitations(sym, osz, nexc, nxov_ud(1), getij, win, eval, evec, wij(:nxov_rd),&
538            & fdXplusY, fdTrans, fdTradip, transitionDipoles, tWriteTagged, fdTagged, taggedWriter,&
539            & fdExc, Ssq)
540      else
541        call writeExcitations(sym, osz, nexc, nxov_ud(1), getij, win, eval, evec, wij(:nxov_rd),&
542            & fdXplusY, fdTrans, fdTradip, transitionDipoles, tWriteTagged, fdTagged, taggedWriter,&
543            & fdExc)
544      end if
545
546      if (allocated(allOmega)) then
547        if (size(allOmega) /= size(symmetries) * nExc) then
548          deallocate(allOmega)
549        end if
550      end if
551      if (.not. allocated(allOmega)) then
552        allocate(allOmega(size(symmetries) * nExc))
553      end if
554      allOmega(1+(iSym-1)*nExc:iSym*nExc) = sqrt(eval)
555
556    end do
557
558    if (tArnoldi) then
559      close(fdArnoldi)
560    end if
561
562    if (fdTrans > 0) close(fdTrans)
563    if (fdXplusY > 0) close(fdXplusY)
564    if (fdExc > 0) close(fdExc)
565    if (fdTradip > 0) close(fdTradip)
566
567    ! Remove some un-used memory
568    deallocate(snglPartTransDip)
569    deallocate(transitionDipoles)
570    deallocate(sposz)
571
572    if (.not. tZVector) then
573      if (nstat == 0) then
574        omega = 0.0_dp
575      else
576        omega = sqrt(eval(nstat))
577      end if
578    else
579      ! calculate Furche vectors and transition density matrix for various properties
580
581      if (nstat == 0) then
582        nStartLev = 1
583        nEndLev = nexc
584
585        if (tForces) then
586          call error("Forces currently not available unless a single excited state is specified")
587        end if
588
589      else
590        nStartLev = nstat
591        nEndLev = nstat
592      end if
593
594      if (tSpin) then
595        call error("Z vector evaluation does not currently support spin polarization.")
596      end if
597
598      if (any( abs(filling) > elecTolMax .and. abs(filling-2.0_dp) > elecTolMax ) ) then
599        call error("Fractional fillings not currently possible for excited state property&
600            & calculations")
601      end if
602
603      ! redefine if needed (generalize it for spin-polarized and fractional occupancy)
604      nocc = nint(rnel) / 2
605      nocc_r = nOcc
606      nvir_r = nOrb - nOcc
607
608      ! elements in a triangle plus the diagonal of the occ-occ and virt-virt blocks
609      nxoo_r = (nocc_r * (nocc_r + 1)) / 2
610      nxvv_r = (nvir_r * (nvir_r + 1)) / 2
611
612      ! Arrays needed for Z vector
613      ALLOCATE(xpy(nxov_rd))
614      ALLOCATE(xmy(nxov_rd))
615      ALLOCATE(t(norb, norb))
616      ALLOCATE(rhs(nxov_rd))
617      ALLOCATE(woo(nxoo_r))
618      ALLOCATE(wvv(nxvv_r))
619      ALLOCATE(wov(nxov_rd))
620      ALLOCATE(iatrans(1:nocc, nocc+1:norb))
621
622      ! Arrays for gradients and Mulliken analysis
623      if (tZVector) then
624        ALLOCATE(dqex(natom))
625        ALLOCATE(pc(norb, norb))
626      end if
627
628      ! set up transition indexing
629      call rindxov_array(win, nocc, nxov, getij, iatrans)
630
631      do iLev = nStartLev, nEndLev
632        omega = sqrt(eval(iLev))
633        ! Furche terms: X+Y, X-Y
634        xpy(:nxov_rd) = evec(:nxov_rd,iLev) * sqrt(wij(:nxov_rd) / omega)
635        xmy(:nxov_rd) = evec(:nxov_rd,iLev) * sqrt(omega / wij(:nxov_rd))
636
637        ! solve for Z and W to get excited state density matrix
638        call getZVectorEqRHS(xpy, xmy, win, iAtomStart, nocc, nocc_r,&
639            & nxov_ud(1), getij, iatrans, natom, species0,grndEigVal(:,1),&
640            & stimc, grndEigVecs, gammaMat, spinW, omega, sym, rhs, t,&
641            & wov, woo, wvv, transChrg)
642        call solveZVectorPrecond(rhs, win, nxov_ud(1), getij, natom, iAtomStart,&
643            & stimc, gammaMat, wij(:nxov_rd), grndEigVecs, transChrg)
644        call calcWVectorZ(rhs, win, nocc, nocc_r, nxov_ud(1), getij, iAtomStart,&
645            & stimc, grndEigVecs, gammaMat, grndEigVal(:,1), wov, woo, wvv, transChrg)
646        call calcPMatrix(t, rhs, win, getij, pc)
647
648        call writeCoeffs(pc, grndEigVecs, filling, nocc, fdCoeffs,&
649            & tCoeffs, tGrndState, occNatural, naturalOrbs)
650
651        ! Make MO to AO transformation of the excited density matrix
652        call makeSimiliarityTrans(pc, grndEigVecs(:,:,1))
653
654        call getExcMulliken(iAtomStart, pc, SSqr, dqex)
655        if (tMulliken) then
656          call writeExcMulliken(sym, iLev, dq, dqex, coord0, fdMulliken)
657        end if
658
659        if (tForces) then
660          call addGradients(sym, nxov_rd, natom, species0, iAtomStart, norb, nocc, nocc_r,&
661              & nxov_ud(1), getij, win, grndEigVecs, pc, stimc, dq, dqex, gammaMat, HubbardU,&
662              & spinW, shift, woo, wov, wvv, transChrg, xpy, coord0, orb, skHamCont,&
663              & skOverCont, derivator, rhoSqr(:,:,1), excgrad)
664        end if
665
666      end do
667
668      if (nstat == 0) then
669        omega = 0.0_dp
670      end if
671
672    end if
673
674  end subroutine LinRespGrad_old
675
676
677  !> Builds and diagonalizes the excitation matrix via iterative technique.
678  subroutine buildAndDiagExcMatrix(tSpin, wij, sym, win, nmatup, nxov, iAtomStart, stimc,&
679      & grndEigVecs, filling, getij, gammaMat, species0, spinW, transChrg, fdArnoldiDiagnosis,&
680      & eval, evec, onsMEs, orb)
681
682    !> spin polarisation?
683    logical, intent(in) :: tSpin
684
685    !> single particle excitation energies
686    real(dp), intent(in) :: wij(:)
687
688    !> symmetry to calculate transitions
689    character, intent(in) :: sym
690
691    !> index array for single particle excitions
692    integer, intent(in) :: win(:)
693
694    !> number of same spin excitations
695    integer, intent(in) :: nmatup
696
697    !> number of occupied-virtual transitions
698    integer, intent(in) :: nxov
699
700    !> indexing array for square matrices
701    integer, intent(in) :: iAtomStart(:)
702
703    !> overlap times ground state eigenvectors
704    real(dp), intent(in) :: stimc(:,:,:)
705
706    !> ground state eigenvectors
707    real(dp), intent(in) :: grndEigVecs(:,:,:)
708
709    !> occupation numbers
710    real(dp), intent(in) :: filling(:,:)
711
712    !> electrostatic matrix
713    real(dp), intent(in) :: gammaMat(:,:)
714
715    !> index array for for single particle excitations
716    integer, intent(in) :: getij(:,:)
717
718    !> central cell chemical species
719    integer, intent(in) :: species0(:)
720
721    !> file handle for ARPACK eigenstate tests
722    integer, intent(in) :: fdArnoldiDiagnosis
723
724    !> atomic resolved spin constants
725    real(dp), intent(in) :: spinW(:)
726
727    !> machinery for transition charges between single particle levels
728    type(TTransCharges), intent(in) :: transChrg
729
730    !> resulting eigenvalues for transitions
731    real(dp), intent(out) :: eval(:)
732
733    !> eigenvectors for transitions
734    real(dp), intent(out) :: evec(:,:)
735
736    !> onsite corrections if in use
737    real(dp), allocatable :: onsMEs(:,:,:,:)
738
739    !> data type for atomic orbital information
740    type(TOrbitals), intent(in) :: orb
741
742    real(dp), allocatable :: workl(:), workd(:), resid(:), vv(:,:), qij(:)
743    real(dp) :: sigma
744    integer :: iparam(11), ipntr(11)
745    integer :: ido, ncv, lworkl, info
746    logical, allocatable :: selection(:)
747    logical :: rvec
748    integer :: nexc, natom
749
750    integer :: iState
751    real(dp), allocatable :: Hv(:), orthnorm(:,:)
752
753    nexc = size(eval)
754    natom = size(gammaMat, dim=1)
755    @:ASSERT(all(shape(evec) == [ nxov, nexc ]))
756
757    ! Three times more Lanczos vectors than desired eigenstates
758    ncv = min(3 * nexc, nxov)
759
760    lworkl = ncv * (ncv + 8)
761
762    ALLOCATE(workl(lworkl))
763    ALLOCATE(workd(3 * nxov))
764    ALLOCATE(resid(nxov))
765    ALLOCATE(selection(ncv))
766    ALLOCATE(vv(nxov, ncv))
767    ALLOCATE(qij(natom))
768
769    resid = 0.0_dp
770    workd = 0.0_dp
771
772    ! random initial vector used for dsaupd ARPACK call
773    info = 0
774    ! IDO must be zero on the first  call
775    ido = 0
776    ! restarting the iteration with a starting vector that is a linear combination of Ritz vectors
777    ! associated with the "wanted" Ritz values.
778    iparam(1) = 1
779    ! maximum iterations of solver
780    iparam(3) = MAX_AR_ITER
781    ! solve A*x = lambda*x, with A symmetric
782    iparam(7) = 1
783
784    ! loop until exit
785    do
786
787      ! call the reverse communication interface from arpack
788      call saupd (ido, "I", nxov, "SM", nexc, ARTOL, resid, ncv, vv, nxov, iparam, ipntr, workd,&
789          & workl, lworkl, info)
790
791      if (ido == 99) then
792        ! has terminated normally, exit loop
793        exit
794      end if
795
796      ! still running, test for an error return
797      if (abs(ido) /= 1) then
798        write(tmpStr,"(' Unexpected return from arpack routine saupd, IDO ',I0, ' INFO ',I0)")&
799            & ido,info
800        call error(tmpStr)
801      end if
802
803      ! Action of excitation supermatrix on supervector
804      call omegatvec(tSpin, workd(ipntr(1):ipntr(1)+nxov-1), workd(ipntr(2):ipntr(2)+nxov-1),&
805          & wij, sym, win, nmatup, iAtomStart, stimc, grndEigVecs, filling, getij, gammaMat,&
806          & species0, spinW, onsMEs, orb, transChrg)
807
808    end do
809
810    ! check returned info flag for errors
811    if (info < 0) then
812      write(tmpStr,"(' Error with ARPACK routine saupd, info = ',I0)")info
813      call error(tmpStr)
814    else if (info  ==  1) then
815      call error("Maximum number of iterations reached. Increase the number of excited states to&
816          & solve for (NrOfExcitations).")
817    else
818
819      ! now want Ritz vectors
820      rvec = .true.
821
822      ! everything after the first 6 variables are passed directly to DSEUPD following the last call
823      ! to DSAUPD.  These arguments MUST NOT BE MODIFIED between the the last call to DSAUPD and the
824      ! call to DSEUPD.
825      call seupd (rvec, "All", selection, eval, evec, nxov, sigma, "I", nxov, "SM", nexc, ARTOL,&
826          & resid, ncv, vv, nxov, iparam, ipntr, workd, workl, lworkl, info)
827
828      ! check for error on return
829      if (info  /=  0) then
830        write(tmpStr,"(' Error with ARPACK routine seupd, info = ',I0)")info
831        call error(tmpStr)
832      end if
833
834    end if
835
836    if (fdArnoldiDiagnosis > 0) then
837      ! tests for quality of returned eigenpairs
838      open(fdArnoldiDiagnosis, file=testArpackOut, position="rewind", status="replace")
839      ALLOCATE(Hv(nxov))
840      ALLOCATE(orthnorm(nxov,nxov))
841      orthnorm = matmul(transpose(evec(:,:nExc)),evec(:,:nExc))
842
843      write(fdArnoldiDiagnosis,"(A)")'State Ei deviation    Evec deviation  Norm deviation  Max&
844          & non-orthog'
845      do iState = 1, nExc
846        call omegatvec(tSpin, evec(:,iState), Hv, wij, sym, win, nmatup, iAtomStart, stimc,&
847            & grndEigVecs, filling, getij, gammaMat, species0, spinW, onsMEs, orb, transChrg)
848        write(fdArnoldiDiagnosis,"(I4,4E16.8)")iState, dot_product(Hv,evec(:,iState))-eval(iState),&
849            & sqrt(sum( (Hv-evec(:,iState)*eval(iState) )**2 )), orthnorm(iState,iState) - 1.0_dp,&
850            & max(maxval(orthnorm(:iState-1,iState)), maxval(orthnorm(iState+1:,iState)))
851      end do
852      close(fdArnoldiDiagnosis)
853    end if
854
855  end subroutine buildAndDiagExcMatrix
856
857
858  !> Calculate oscillator strength for a given excitation between KS states
859  subroutine getOscillatorStrengths(sym, snglPartTransDip, wij, eval, evec, filling, win, nmatup,&
860      & getij, istat, osz, tTradip, transitionDipoles)
861
862    !> symmetry of transition
863    character, intent(in) :: sym
864
865    !> dipole moments for single particle transtions
866    real(dp), intent(in) :: snglPartTransDip(:,:)
867
868    !> energies for single particle transitions
869    real(dp), intent(in) :: wij(:)
870
871    !> Low lying eigenvalues of Casida eqn
872    real(dp), intent(in) :: eval(:)
873
874    !> eigenvectors of Casida eqn
875    real(dp), intent(in) :: evec(:,:)
876
877    !> Single particle occupations in the ground state
878    real(dp), intent(in) :: filling(:,:)
879
880    !> index for transitions
881    integer, intent(in) :: win(:)
882
883    !> number of up spin transitions before the down spin start
884    integer, intent(in) :: nmatup
885
886    !> index from single particle excitation to specific pair of single particle states involved
887    integer, intent(in) :: getij(:,:)
888
889    !> write transition dipole
890    logical :: tTradip
891
892    !> flag wich if <-1 on entry is returned as the brightest state
893    integer, intent(inout) :: istat
894
895    !> Oscilator strengths of transitions
896    real(dp), intent(out) :: osz(:)
897
898    !> resulting transition dipoles
899    real(dp), intent(out) :: transitionDipoles(:,:)
900
901    integer :: ii, nmat, oszLoc(1)
902    real(dp) :: wnij(size(evec, dim=1))
903    logical :: tSpin
904
905    nmat = size(evec, dim=1)
906
907    if (size(filling, dim=2) == 2) then
908      tSpin = .true.
909    else
910      tSpin = .false.
911    end if
912
913    transitionDipoles = 0.0_dp
914    osz = 0.0_dp
915
916    ! Triplet oscillator strength and transition dipole is zero for
917    ! closed shell ground state
918    if ((.not. tSpin) .and. (sym == "T")) then
919      return
920    end if
921
922    call wtdn(wij, filling, win, nmatup, nmat, getij, wnij)
923
924    !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(ii) SCHEDULE(RUNTIME)
925    do ii = 1, size(evec, dim=2)
926      osz(ii) = oscillatorStrength(snglPartTransDip, wnij, evec(:,ii))
927    end do
928    !$OMP  END PARALLEL DO
929
930    if (istat < 0) then
931      ! find largest transition dipole transition
932      oszLoc = maxloc(osz)
933      istat = oszLoc(1)
934    end if
935
936    if (tTradip) then
937      call transitionDipole(snglPartTransDip, wnij, eval, evec, transitionDipoles)
938    end if
939
940  end subroutine getOscillatorStrengths
941
942
943  !> Calculate <S^2> as a measure of spin contamination (smaller magnitudes are better, 0.5 is
944  !> considered an upper threshold for reliability according to Garcia thesis)
945  subroutine getExcSpin(Ssq, nmatup, getij, win, eval, evec, wij, filling, stimc, grndEigVecs)
946
947    !> spin contamination
948    real(dp), intent(out) :: Ssq(:)
949
950    !> number of spin up excitations
951    integer, intent(in) :: nmatup
952
953    !> index for composite excitations to specific occupied and empty states
954    integer, intent(in) :: getij(:,:)
955
956    !> single particle excitation index
957    integer, intent(in) :: win(:)
958
959    !> Casida exitation energies
960    real(dp), intent(in) :: eval(:)
961
962    !> Casida excited eigenvectors
963    real(dp), intent(in) :: evec(:,:)
964
965    !> single particle excitation energies
966    real(dp), intent(in) :: wij(:)
967
968    !> occupations in ground state
969    real(dp), intent(in) :: filling(:,:)
970
971    !> Overlap times ground state eigenvectors
972    real(dp), intent(in) :: stimc(:,:,:)
973
974    !> Ground state eigenvectors
975    real(dp), intent(in) :: grndEigVecs(:,:,:)
976
977    integer:: i, k, l, m, ia, jb, ii, aa, jj, bb
978    integer:: nmat, nexc, nup, ndwn
979    real(dp) :: rsqw, TDvnorm
980    real(dp), allocatable :: TDvec(:), TDvec_sq(:)
981    integer, allocatable :: TDvin(:)
982    logical :: ud_ia, ud_jb
983    real(dp) :: s_iaja, s_iaib, s_iajb, tmp
984    real(dp) :: wnij(size(wij))
985
986    nmat = size(evec, dim=1)
987    nexc = size(Ssq)
988    nup = ceiling(sum(filling(:,1)))
989    ndwn = ceiling(sum(filling(:,2)))
990    ALLOCATE(TDvec(nmat))
991    ALLOCATE(TDvec_sq(nmat))
992    ALLOCATE(TDvin(nmat))
993
994    call wtdn(wij, filling, win, nmatup, nmat, getij, wnij)
995
996    do i = 1, nexc
997      rsqw = 1.0_dp / sqrt(sqrt(eval(i)))
998      TDvec(:) = sqrt(wnij(:)) * rsqw * evec(:,i)
999      TDvnorm = 1.0_dp / sqrt(sum(TDvec**2))
1000      TDvec(:) = TDvec(:) * TDvnorm
1001      TDvec_sq = TDvec**2
1002
1003      ! put these transition dipoles in order of descending magnitude
1004      call index_heap_sort(TDvin, TDvec_sq)
1005      TDvin = TDvin(nmat:1:-1)
1006      TDvec_sq = TDvec_sq(TDvin)
1007
1008      ! S_{ia,ja}
1009      s_iaja = 0.0_dp
1010      do k = 1, nmat
1011        ia = TDvin(k)
1012        call indxov(win, ia, getij, ii, aa)
1013        ud_ia = (win(ia) <= nmatup)
1014        do l = 1, nmat
1015          jb = TDvin(l)
1016          call indxov(win, jb, getij, jj, bb)
1017          ud_jb = (win(jb) <= nmatup)
1018
1019          if ( (bb /= aa) .or. (ud_jb .neqv. ud_ia) ) then
1020            cycle
1021          end if
1022
1023          tmp = 0.0_dp
1024          if (ud_ia) then
1025            do m = 1,ndwn
1026              tmp = tmp + MOoverlap(ii,m,stimc,grndEigVecs) * MOoverlap(jj,m,stimc,grndEigVecs)
1027            end do
1028          else
1029            do m = 1,nup
1030              tmp = tmp + MOoverlap(m,ii,stimc,grndEigVecs) * MOoverlap(m,jj,stimc,grndEigVecs)
1031            end do
1032          end if
1033
1034          s_iaja = s_iaja + TDvec(ia)*TDvec(jb)*tmp
1035
1036        end do
1037      end do
1038
1039      ! S_{ia,ib}
1040      s_iaib = 0.0_dp
1041      do k = 1, nmat
1042        ia = TDvin(k)
1043        call indxov(win, ia, getij, ii, aa)
1044        ud_ia = (win(ia) <= nmatup)
1045        do l = 1, nmat
1046          jb = TDvin(l)
1047          call indxov(win, jb, getij, jj, bb)
1048          ud_jb = (win(jb) <= nmatup)
1049
1050          if ( (ii /= jj) .or. (ud_jb .neqv. ud_ia) ) then
1051            cycle
1052          end if
1053
1054          tmp = 0.0_dp
1055          if (ud_ia) then
1056            do m = 1,ndwn
1057              tmp = tmp + MOoverlap(aa,m,stimc,grndEigVecs) * MOoverlap(bb,m,stimc,grndEigVecs)
1058            end do
1059          else
1060            do m = 1,nup
1061              tmp = tmp + MOoverlap(m,aa,stimc,grndEigVecs) * MOoverlap(m,bb,stimc,grndEigVecs)
1062            end do
1063          end if
1064
1065          s_iaib = s_iaib + TDvec(ia)*TDvec(jb)*tmp
1066        end do
1067      end do
1068
1069      ! S_{ia,jb}
1070      s_iajb = 0.0_dp
1071      do k = 1, nmat
1072        ia = TDvin(k)
1073        call indxov(win, ia, getij, ii, aa)
1074        ud_ia = (win(ia) <= nmatup)
1075        if (.not. ud_ia ) then
1076          cycle
1077        end if
1078        do l = 1, nmat
1079          jb = TDvin(l)
1080          call indxov(win, jb, getij, jj, bb)
1081          ud_jb = (win(jb) <= nmatup)
1082
1083          if ( ud_jb ) cycle
1084
1085          s_iajb = s_iajb + TDvec(ia)*TDvec(jb) * MOoverlap(aa,bb,stimc,grndEigVecs)&
1086              & * MOoverlap(ii,jj,stimc,grndEigVecs)
1087
1088        end do
1089      end do
1090
1091      Ssq(i) =  s_iaja - s_iaib - 2.0_dp*s_iajb
1092
1093    end do
1094
1095  end subroutine getExcSpin
1096
1097
1098  !> Build right hand side of the equation for the Z-vector and those parts of the W-vectors which
1099  !> do not depend on Z.
1100  subroutine getZVectorEqRHS(xpy, xmy, win, iAtomStart, homo, nocc, nmatup, getij, iatrans, natom,&
1101      & species0, grndEigVal, stimc, c, gammaMat, spinW, omega, sym, rhs, t, wov, woo, wvv,&
1102      & transChrg)
1103
1104    !> X+Y Furche term
1105    real(dp), intent(in) :: xpy(:)
1106
1107    !> X-Y Furche term
1108    real(dp), intent(in) :: xmy(:)
1109
1110    !> index array for single particle transitions
1111    integer, intent(in) :: win(:)
1112
1113    !> index vector for S and H matrices
1114    integer, intent(in) :: iAtomStart(:)
1115
1116    !> highest occupied level
1117    integer, intent(in) :: homo
1118
1119    !> number of filled states
1120    integer, intent(in) :: nocc
1121
1122    !> number of same spin excitations
1123    integer, intent(in) :: nmatup
1124
1125    !> index array between transitions in square and 1D representations
1126    integer, intent(in) :: getij(:,:)
1127
1128    !> index array from orbital pairs to compound index
1129    integer, intent(in) :: iatrans(:,homo+1:)
1130
1131    !> number of central cell atoms
1132    integer, intent(in) :: natom
1133
1134    !> central cell chemical species
1135    integer, intent(in) :: species0(:)
1136
1137    !> ground state wavefunctions
1138    real(dp), intent(in) :: grndEigVal(:)
1139
1140    !> overlap times ground state wavefunctions
1141    real(dp), intent(in) :: stimc(:,:,:)
1142
1143    !> ground state wavefunctions
1144    real(dp), intent(in) :: c(:,:,:)
1145
1146    !> softened coulomb matrix
1147    real(dp), intent(in) :: gammaMat(:,:)
1148
1149    !> ground state spin derivatives for each species
1150    real(dp), intent(in) :: spinW(:)
1151
1152    !> Excitation energies
1153    real(dp), intent(in) :: omega
1154
1155    !> Symmetry of the transitions
1156    character, intent(in) :: sym
1157
1158    !> Right hand side for the Furche solution
1159    real(dp), intent(out) :: rhs(:)
1160
1161    !> T matrix
1162    real(dp), intent(out) :: t(:,:)
1163
1164    !> W vector occupied-virtual part
1165    real(dp), intent(out) :: wov(:)
1166
1167    !> W vector occupied part
1168    real(dp), intent(out) :: woo(:)
1169
1170    !> W vector virtual part
1171    real(dp), intent(out) :: wvv(:)
1172
1173    !> machinery for transition charges between single particle levels
1174    type(TTransCharges), intent(in) :: transChrg
1175
1176    real(dp), allocatable :: xpyq(:), qij(:), gamxpyq(:), qgamxpyq(:), gamqt(:)
1177    integer :: nxov, nxoo, nxvv
1178    integer :: i, j, a, b, ia, ib, ij, ab, ja
1179    real(dp) :: tmp1, tmp2
1180    logical, parameter :: updwn = .true.
1181
1182    nxov = size(rhs)
1183    nxoo = size(woo)
1184    nxvv = size(wvv)
1185
1186    ALLOCATE(xpyq(natom))
1187    ALLOCATE(qij(natom))
1188    ALLOCATE(gamxpyq(natom))
1189    ALLOCATE(gamqt(natom))
1190    ALLOCATE(qgamxpyq(max(nxoo, nxvv)))
1191
1192    t(:,:) = 0.0_dp
1193    rhs(:) = 0.0_dp
1194    wov(:) = 0.0_dp
1195    woo(:) = 0.0_dp
1196    wvv(:) = 0.0_dp
1197
1198    ! Build t_ab = 0.5 * sum_i (X+Y)_ia (X+Y)_ib + (X-Y)_ia (X-Y)_ib
1199    ! and w_ab = Q_ab with Q_ab as in (B16) but with corrected sign.
1200    ! factor 1 / (1 + delta_ab) follows later
1201    do ia = 1, nxov
1202      call indxov(win, ia, getij, i, a)
1203
1204      ! BA: is T_aa = 0?
1205      do b = homo + 1, a
1206        ib = iatrans(i, b)
1207        call rindxvv(homo, a, b, ab)
1208        tmp1 = xpy(ia) * xpy(ib) + xmy(ia) * xmy(ib)
1209        tmp2 = omega * (xpy(ia) * xmy(ib)+ xmy(ia) * xpy(ib))
1210        t(a,b) = t(a,b) + 0.5_dp * tmp1
1211        ! to prevent double counting
1212        if (a /= b) then
1213          t(b,a) = t(b,a) + 0.5_dp * tmp1
1214        end if
1215        ! Note: diagonal elements will be multiplied by 0.5 later.
1216        wvv(ab) = wvv(ab) + grndEigVal(i) * tmp1 + tmp2
1217      end do
1218
1219      ! Build t_ij = 0.5 * sum_a (X+Y)_ia (X+Y)_ja + (X-Y)_ia (X-Y)_ja and 1 / (1 + delta_ij) Q_ij
1220      ! with Q_ij as in eq. (B9) (1st part of w_ij)
1221      do j = i, homo
1222        ja = iatrans(j,a)
1223        call rindxvv(homo-nocc, j, i, ij)
1224        tmp1 = xpy(ia) * xpy(ja) + xmy(ia) * xmy(ja)
1225        tmp2 = omega * (xpy(ia) * xmy(ja) + xmy(ia) * xpy(ja))
1226        ! Note, there is a typo in Heringer et al. J. Comp Chem 28, 2589.
1227        ! The sign must be negative see Furche, J. Chem. Phys, 117 7433 (2002).
1228        t(i,j) = t(i,j) - 0.5_dp * tmp1
1229        ! to prevent double counting
1230        if (i /= j) then
1231          t(j,i) = t(j,i) - 0.5_dp * tmp1
1232        end if
1233        woo(ij) = woo(ij) - grndEigVal(a) * tmp1 + tmp2
1234      end do
1235
1236    end do
1237
1238    ! xpyq = Q * xpy
1239    xpyq(:) = 0.0_dp
1240    call transChrg%qMatVec(iAtomStart, stimc, c, getij, win, xpy, xpyq)
1241
1242
1243    ! qgamxpyq(ab) = sum_jc K_ab,jc (X+Y)_jc
1244    if (sym == "S") then
1245      call hemv(gamxpyq, gammaMat,  xpyq)
1246      do ab = 1, nxvv
1247        call indxvv(homo, ab, a, b)
1248        qij(:) = transq(a, b, iAtomStart, updwn, stimc, c)
1249        qgamxpyq(ab) = 2.0_dp * sum(qij * gamxpyq)
1250      end do
1251    else ! triplet case
1252      do ab = 1, nxvv
1253        call indxvv(homo, ab, a, b)
1254        qij(:) = transq(a, b, iAtomStart, updwn, stimc, c)
1255        qgamxpyq(ab) = 2.0_dp * sum(qij * xpyq * spinW(species0))
1256      end do
1257    end if
1258
1259    ! rhs(ia) -= Qia = sum_b (X+Y)_ib * qgamxpyq(ab))
1260    do ia = 1, nxov
1261      call indxov(win, ia, getij, i, a)
1262      do b = homo + 1, a
1263        call rindxvv(homo, a, b, ab)
1264        ib = iatrans(i,b)
1265        rhs(ia) = rhs(ia) - 2.0_dp * xpy(ib) * qgamxpyq(ab)
1266        ! Since qgamxpyq has only upper triangle
1267        if (a /= b) then
1268          rhs(ib) = rhs(ib) - 2.0_dp * xpy(ia) * qgamxpyq(ab)
1269        end if
1270      end do
1271    end do
1272
1273    ! -rhs = -rhs - sum_j (X + Y)_ja H + _ij[X + Y]
1274    if (sym == "S") then
1275      do ij = 1, nxoo
1276        qgamxpyq(ij) = 0.0_dp
1277        call indxoo(ij, i, j)
1278        qij(:) = transq(i, j, iAtomStart, updwn, stimc, c)
1279        ! qgamxpyq(ij) = sum_kb K_ij,kb (X+Y)_kb
1280        qgamxpyq(ij) = 2.0_dp * sum(qij * gamxpyq)
1281      end do
1282    else
1283      do ij = 1, nxoo
1284        qgamxpyq(ij) = 0.0_dp
1285        call indxoo(ij, i, j)
1286        qij(:) = transq(i, j, iAtomStart, updwn, stimc, c)
1287        qgamxpyq(ij) = 2.0_dp * sum(qij * xpyq * spinW(species0))
1288      end do
1289    end if
1290
1291    ! rhs(ia) += Qai = sum_j (X+Y)_ja qgamxpyq(ij)
1292    ! add Qai to Wia as well.
1293    do ia = 1, nxov
1294      call indxov(win, ia, getij, i, a)
1295      do j = i, homo
1296        ja = iatrans(j, a)
1297        ij = i-homo+nocc + ((j-homo+nocc - 1) * (j-homo+nocc)) / 2
1298        tmp1 = 2.0_dp * xpy(ja) * qgamxpyq(ij)
1299        rhs(ia) = rhs(ia) + tmp1
1300        wov(ia) = wov(ia) + tmp1
1301        if (i /= j) then
1302          tmp2 = 2.0_dp * xpy(ia) * qgamxpyq(ij)
1303          rhs(ja) = rhs(ja) + tmp2
1304          wov(ja) = wov(ja) + tmp2
1305        end if
1306      end do
1307    end do
1308
1309    ! gamxpyq(iAt2) = sum_ij q_ij(iAt2) T_ij
1310    gamxpyq(:) = 0.0_dp
1311    do ij = 1, nxoo
1312      call indxoo(ij, i, j)
1313      qij = transq(i, j, iAtomStart, updwn, stimc, c)
1314      if (i == j) then
1315        gamxpyq(:) = gamxpyq(:) + t(i,j) * qij(:)
1316      else
1317        ! factor 2 because of symmetry of the matrix
1318        gamxpyq(:) = gamxpyq(:) + 2.0_dp  * t(i,j) * qij(:)
1319      end if
1320    end do
1321
1322    ! gamxpyq(iAt2) += sum_ab q_ab(iAt2) T_ab
1323    do ab = 1, nxvv
1324      call indxvv(homo, ab, a, b)
1325      qij(:) = transq(a, b, iAtomStart, updwn, stimc, c)
1326      if (a == b) then
1327        gamxpyq(:) = gamxpyq(:) + t(a,b) * qij(:)
1328      else
1329        ! factor 2 because of symmetry of the matrix
1330        gamxpyq(:) = gamxpyq(:) + 2.0_dp * t(a,b) * qij(:)
1331      end if
1332    end do
1333
1334    ! gamqt(iAt1) = sum_iAt2 gamma_iAt1,iAt2 gamxpyq(iAt2)
1335    call hemv(gamqt, gammaMat, gamxpyq)
1336
1337    ! rhs -= sum_q^ia(iAt1) gamxpyq(iAt1)
1338    call transChrg%qVecMat(iAtomStart, stimc, c, getij, win, -4.0_dp*gamqt, rhs)
1339
1340    ! Furche vectors
1341    do ij = 1, nxoo
1342      call indxoo(ij, i, j)
1343      qij(:) = transq(i, j, iAtomStart, updwn, stimc, c)
1344      woo(ij) = woo(ij) + 4.0_dp * sum(qij * gamqt)
1345    end do
1346
1347  end subroutine getZVectorEqRHS
1348
1349
1350  !> Solving the (A+B) Z = -R equation via diagonally preconditioned conjugate gradient
1351  subroutine solveZVectorPrecond(rhs, win, nmatup, getij, natom, iAtomStart, stimc, gammaMat, wij,&
1352      & c, transChrg)
1353
1354    !> on entry -R, on exit Z
1355    real(dp), intent(inout) :: rhs(:)
1356
1357    !> index for single particle excitations
1358    integer, intent(in) :: win(:)
1359
1360    !> number of transitions between only up states
1361    integer, intent(in) :: nmatup
1362
1363    !> index array from composite index to specific filled-empty transition
1364    integer, intent(in) :: getij(:,:)
1365
1366    !> number of atoms
1367    integer, intent(in) :: natom
1368
1369    !> index vector for S and H0 matrices
1370    integer, intent(in) :: iAtomStart(:)
1371
1372    !> overlap times ground state mo-coefficients
1373    real(dp), intent(in) :: stimc(:,:,:)
1374
1375    !> Softened coulomb matrix
1376    real(dp), intent(in) :: gammaMat(:,:)
1377
1378    !> single particle excitation energies
1379    real(dp), intent(in) :: wij(:)
1380
1381    !> ground state mo-coefficients
1382    real(dp), intent(in) :: c(:,:,:)
1383
1384    !> machinery for transition charges between single particle levels
1385    type(TTransCharges), intent(in) :: transChrg
1386
1387    integer :: nxov
1388    integer :: ia, i, a, k
1389    real(dp) :: rhs2(size(rhs)), rkm1(size(rhs)), zkm1(size(rhs)), pkm1(size(rhs)), apk(size(rhs))
1390    real(dp) :: qTmp(nAtom), rs, alphakm1, tmp1, tmp2, bkm1
1391    real(dp), allocatable :: qij(:), P(:)
1392
1393    nxov = size(rhs)
1394    allocate(qij(nAtom))
1395
1396    ! diagonal preconditioner
1397    ! P^-1 = 1 / (A+B)_ia,ia (diagonal of the supermatrix sum A+B)
1398    allocate(P(nxov))
1399    do ia = 1, nxov
1400      qij = transChrg%qTransIJ(ia, iAtomStart, stimc, c, getij, win)
1401      call hemv(qTmp, gammaMat, qij)
1402      rs = 4.0_dp * dot_product(qij, qTmp) + wij(ia)
1403      P(ia) = 1.0_dp / rs
1404    end do
1405
1406    ! Free some space, before entering the apbw routine
1407    deallocate(qij)
1408
1409    ! unit vector as initial guess solution
1410    rhs2(:) = 1.0_dp / sqrt(real(nxov,dp))
1411
1412    ! action of matrix on vector
1413    call apbw(rkm1, rhs2, wij, nxov, natom, win, nmatup, getij, iAtomStart, stimc, c, gammaMat,&
1414        & transChrg)
1415
1416    rkm1(:) = rhs - rkm1
1417    zkm1(:) = P * rkm1
1418    pkm1(:) = zkm1
1419
1420    ! Iteration: should be convergent in at most nxov steps for a quadradic surface, so set higher
1421    do k = 1, nxov**2
1422
1423      ! action of matrix on vector
1424      call apbw(apk, pkm1, wij, nxov, natom, win, nmatup, getij, iAtomStart, stimc, c, gammaMat,&
1425          & transChrg)
1426
1427      tmp1 = dot_product(rkm1, zkm1)
1428      tmp2 = dot_product(pkm1, apk)
1429      alphakm1 = tmp1 / tmp2
1430
1431      rhs2 = rhs2 + alphakm1 * pkm1
1432
1433      rkm1 = rkm1 -alphakm1 * apk
1434
1435      tmp2 = dot_product(rkm1, rkm1)
1436
1437      ! residual
1438      if (tmp2 <= epsilon(1.0_dp)**2) then
1439        exit
1440      end if
1441
1442      if (k == nxov**2) then
1443        call error("solveZVectorEq : Z vector not converged!")
1444      end if
1445
1446      zkm1(:) = P * rkm1
1447
1448      tmp2 = dot_product(zkm1, rkm1)
1449
1450      ! Fletcher–Reeves update
1451      bkm1 = tmp2 / tmp1
1452
1453      pkm1 = zkm1 + bkm1 * pkm1
1454
1455    end do
1456
1457    rhs(:) = rhs2(:)
1458
1459  end subroutine solveZVectorPrecond
1460
1461
1462  !> Calculate Z-dependent parts of the W-vectors and divide diagonal elements of W_ij and W_ab by
1463  !> 2.
1464  subroutine calcWvectorZ(zz, win, homo, nocc, nmatup, getij, iAtomStart, stimc, c, gammaMat,&
1465      & grndEigVal, wov, woo, wvv, transChrg)
1466
1467    !> Z vector
1468    real(dp), intent(in) :: zz(:)
1469
1470    !> index array for single particle transitions
1471    integer, intent(in) :: win(:)
1472
1473    !> highest occupied level
1474    integer, intent(in) :: homo
1475
1476    !> number of filled levels
1477    integer, intent(in) :: nocc
1478
1479    !> number of same spin excitations
1480    integer, intent(in) :: nmatup
1481
1482    !> index array between transitions in square and 1D representations
1483    integer, intent(in) :: getij(:,:)
1484
1485    !> index array for S and H0 ground state square matrices
1486    integer, intent(in) :: iAtomStart(:)
1487
1488    !> overlap times ground state wavefunctions
1489    real(dp), intent(in) :: stimc(:,:,:)
1490
1491    !> ground state wavefunctions
1492    real(dp), intent(in) :: c(:,:,:)
1493
1494    !> softened coulomb matrix
1495    real(dp), intent(in) :: gammaMat(:,:)
1496
1497    !> ground state MO-energies
1498    real(dp), intent(in) :: grndEigVal(:)
1499
1500    !> W vector occupied-virtual part
1501    real(dp), intent(inout) :: wov(:)
1502
1503    !> W vector occupied part
1504    real(dp), intent(inout) :: woo(:)
1505
1506    !> W vector virtual part
1507    real(dp), intent(inout) :: wvv(:)
1508
1509    !> machinery for transition charges between single particle levels
1510    type(TTransCharges), intent(in) :: transChrg
1511
1512    integer :: nxov, nxoo, nxvv, natom
1513    integer :: ij, ia, ab, i, j, a, b, iAt1
1514    real(dp), allocatable :: qij(:), gamxpyq(:), zq(:)
1515    logical, parameter :: updwn = .true.
1516
1517    nxov = size(zz)
1518    natom = size(gammaMat, dim=1)
1519    nxoo = size(woo)
1520    nxvv = size(wvv)
1521    ALLOCATE(qij(natom))
1522    ALLOCATE(gamxpyq(natom))
1523    ALLOCATE(zq(natom))
1524
1525    ! Adding missing epsilon_i * Z_ia term to W_ia
1526    do ia = 1, nxov
1527      call indxov(win, ia, getij, i, a)
1528      wov(ia) = wov(ia) + zz(ia) * grndEigVal(i)
1529    end do
1530
1531    ! Missing sum_kb 4 K_ijkb Z_kb term in W_ij: zq(iAt1) = sum_kb q^kb(iAt1) Z_kb
1532    zq(:) = 0.0_dp
1533    call transChrg%qMatVec(iAtomStart, stimc, c, getij, win, zz, zq)
1534
1535    call hemv(gamxpyq, gammaMat, zq)
1536
1537
1538    ! sum_iAt1 qij(iAt1) gamxpyq(iAt1)
1539    do ij = 1, nxoo
1540      call indxoo(ij, i, j)
1541      qij(:) = transq(i, j, iAtomStart, updwn, stimc, c)
1542      ! W contains 1/2 for i == j.
1543      woo(ij) = woo(ij) + 4.0_dp * sum(qij * gamxpyq)
1544    end do
1545
1546    ! Divide diagonal elements of W_ij by 2.
1547    do ij = 1, nxoo
1548      call indxoo(ij, i, j)
1549      if (i == j) then
1550        woo(ij) = 0.5_dp * woo(ij)
1551      end if
1552    end do
1553
1554    ! Divide diagonal elements of W_ab by 2.
1555    do ab = 1, nxvv
1556      call indxvv(homo, ab, a, b)
1557      if (a == b) then
1558        wvv(ab) = 0.5_dp * wvv(ab)
1559      end if
1560    end do
1561
1562  end subroutine calcWvectorZ
1563
1564
1565  !> Mulliken population for a square density matrix and overlap
1566  !> Note: assumes both triangles of both square matrices are filled
1567  subroutine getExcMulliken(iAtomStart, pc, s, dqex)
1568
1569    !> indexing array for atoms
1570    integer, intent(in) :: iAtomStart(:)
1571
1572    !> density matrix
1573    real(dp), intent(in) :: pc(:,:)
1574
1575    !> overlap matrix
1576    real(dp), intent(in) :: s(:,:)
1577
1578    !> output atomic charges
1579    real(dp), intent(out) :: dqex(:)
1580
1581    real(dp) :: tmp(size(pc,dim=1))
1582    integer :: iAt1
1583
1584    @:ASSERT(all(shape(pc)==shape(s)))
1585
1586    tmp = sum(pc * s,dim=2)
1587    dqex(:) = 0.0_dp
1588    do iAt1 = 1, size(dqex)
1589      dqex(iAt1) = sum(tmp(iAtomStart(iAt1):iAtomStart(iAt1 + 1) -1))
1590    end do
1591
1592  end subroutine getExcMulliken
1593
1594
1595  !> Excited state Mulliken charges and dipole moments written to disc
1596  subroutine writeExcMulliken(sym, nstat, dq, dqex, coord0, fdMulliken)
1597
1598    !> symmetry label
1599    character, intent(in) :: sym
1600
1601    !> state index
1602    integer, intent(in) :: nstat
1603
1604    !> ground state gross charge
1605    real(dp), intent(in) :: dq(:)
1606
1607    !> change in atomic charges from ground to excited state
1608    real(dp), intent(in) :: dqex(:)
1609
1610    !> central cell coordinates
1611    real(dp), intent(in) :: coord0(:,:)
1612
1613    !> file unit for Mulliken data
1614    integer, intent(in) :: fdMulliken
1615
1616    integer :: natom, m
1617    real(dp) :: dipol(3), dipabs
1618
1619    natom = size(dq)
1620
1621    @:ASSERT(size(dq) == size(dqex))
1622    @:ASSERT(all(shape(coord0) == [3,nAtom]))
1623
1624    ! Output of excited state Mulliken charges
1625    open(fdMulliken, file=excitedQOut,position="append")
1626    write(fdMulliken, "(a,a,i2)") "# MULLIKEN CHARGES of excited state ",&
1627        & sym, nstat
1628    write(fdMulliken, "(a,2x,A,i4)") "#", 'Natoms =',natom
1629    write(fdMulliken, "('#',1X,A4,T15,A)")'Atom','netCharge'
1630    write(fdMulliken,'("#",41("="))')
1631    do m = 1,  natom
1632      write(fdMulliken,"(i5,1x,f16.8)") m, -dq(m) - dqex(m)
1633    end do
1634    close(fdMulliken)
1635
1636    ! Calculation of excited state dipole moment
1637    dipol(:) = -1.0_dp * matmul(coord0, dq + dqex)
1638    dipabs = sqrt(sum(dipol**2))
1639
1640    open(fdMulliken, file=excitedDipoleOut, position="append")
1641    write(fdMulliken, "(a,a,i2)") "Mulliken analysis of excited state ",&
1642        & sym, nstat
1643    write(fdMulliken, '(42("="))')
1644    write(fdMulliken, "(a)") " "
1645    write(fdMulliken, "(a)") "Mulliken exc. state dipole moment [Debye]"
1646    write(fdMulliken, '(42("="))')
1647    write(fdMulliken, "(3f14.8)") (dipol(m) * au__Debye, m = 1, 3)
1648    write(fdMulliken, "(a)") " "
1649    write(fdMulliken, "(a)") "Norm of exc. state dipole moment [Debye]"
1650    write(fdMulliken, '(42("="))')
1651    write(fdMulliken, "(e20.12)") dipabs * au__Debye
1652    write(fdMulliken, *)
1653    close(fdMulliken)
1654
1655  end subroutine writeExcMulliken
1656
1657
1658  !> Calculate transition moments for transitions between Kohn-Sham states, including spin-flipping
1659  !> transitions
1660  subroutine calcTransitionDipoles(coord0, win, nmatup, getij, iAtomStart, stimc, grndEigVecs,&
1661      & snglPartTransDip)
1662
1663    !> Atomic positions
1664    real(dp), intent(in) :: coord0(:,:)
1665
1666    !> single particle transition index
1667    integer, intent(in) :: win(:)
1668
1669    !> number of same-spin transitions
1670    integer, intent(in) :: nmatup
1671
1672    !> index array for ground state square matrices
1673    integer, intent(in) :: iAtomStart(:)
1674
1675    !> index array for excitation pairs
1676    integer, intent(in) :: getij(:,:)
1677
1678    !> overlap times ground state wavefunctions
1679    real(dp), intent(in) :: stimc(:,:,:)
1680
1681    !> ground state wavefunctions
1682    real(dp), intent(in) :: grndEigVecs(:,:,:)
1683
1684    !> resulting transition dipoles
1685    real(dp), intent(out) :: snglPartTransDip(:,:)
1686
1687    integer :: nxov, natom
1688    integer :: indm, ii, jj
1689    real(dp), allocatable :: qij(:)
1690    logical :: updwn
1691
1692    nxov = size(win)
1693    natom = size(coord0, dim=2)
1694
1695    ALLOCATE(qij(natom))
1696
1697    ! Calculate transition dipole elements
1698    do indm = 1, nxov
1699      call indxov(win, indm, getij, ii, jj)
1700      updwn = (win(indm) <= nmatup)
1701      qij(:) = transq(ii, jj, iAtomStart, updwn, stimc, grndEigVecs)
1702      snglPartTransDip(indm, :) = matmul(coord0, qij)
1703    end do
1704
1705  end subroutine calcTransitionDipoles
1706
1707
1708  !> Calculation of force from derivatives of excitation energy
1709  !> 1. we need the ground and excited Mulliken charges
1710  !> 2. we need P,(T,Z),W, X + Y from linear response
1711  !> 3. calculate dsmndr, dhmndr (dS/dR, dh/dR), dgabda (dGamma_{IAt1,IAt2}/dR_{IAt1}),
1712  !> dgext (dGamma-EXT_{IAt1,k}/dR_{IAt1})
1713  subroutine addGradients(sym, nxov, natom, species0, iAtomStart, norb, homo, nocc, nmatup, getij,&
1714      & win, grndEigVecs, pc, stimc, dq, dqex, gammaMat, HubbardU, spinW, shift, woo, wov, wvv,&
1715      & transChrg, xpy, coord0, orb, skHamCont, skOverCont, derivator, rhoSqr, excgrad)
1716
1717    !> symmetry of the transition
1718    character, intent(in) :: sym
1719
1720    !> number of single particle transitions to include
1721    integer, intent(in) :: nxov
1722
1723    !> number of central cell atoms
1724    integer, intent(in) :: natom
1725
1726    !> central cell chemical species
1727    integer, intent(in) :: species0(:)
1728
1729    !> index array for S and H0 ground state square matrices
1730    integer, intent(in) :: iAtomStart(:)
1731
1732    !> number of orbitals for ground state system
1733    integer, intent(in) :: norb
1734
1735    !> number of highest occupied state in ground state
1736    integer, intent(in) :: homo
1737
1738    !> number of occupied states in calculation (not neccessarily same as HOMO in the case of
1739    !> windowing)
1740    integer, intent(in) :: nocc
1741
1742    !> single particle transition index
1743    integer, intent(in) :: win(:)
1744
1745    !> number of up->up transitions
1746    integer, intent(in) :: nmatup
1747
1748    !> index array from composite transition index to specific single particle states
1749    integer, intent(in) :: getij(:,:)
1750
1751    !> ground state eigenvectors
1752    real(dp), intent(in) :: grndEigVecs(:,:,:)
1753
1754    !> transition density matrix
1755    real(dp), intent(in) :: pc(:,:)
1756
1757    !> overlap times ground state eigenvectors
1758    real(dp), intent(in) :: stimc(:,:,:)
1759
1760    !> ground state gross charges
1761    real(dp), intent(in) :: dq(:)
1762
1763    !> charge differences from ground to excited state
1764    real(dp), intent(in) :: dqex(:)
1765
1766    !> softened coulomb matrix
1767    real(dp), intent(in) :: gammaMat(:,:)
1768
1769    !> ground state Hubbard U values
1770    real(dp), intent(in) :: HubbardU(:)
1771
1772    !> ground state spin derivatives for each species
1773    real(dp), intent(in) :: spinW(:)
1774
1775    !> ground state potentials (shift vector)
1776    real(dp), intent(in) :: shift(:)
1777
1778    !> W vector occupied part
1779    real(dp), intent(in) :: woo(:)
1780
1781    !> W vector occupied-virtual part
1782    real(dp), intent(in) :: wov(:)
1783
1784    !> W vector virtual part
1785    real(dp), intent(in) :: wvv(:)
1786
1787    !> machinery for transition charges between single particle levels
1788    type(TTransCharges), intent(in) :: transChrg
1789
1790    !> X+Y Furche term
1791    real(dp), intent(in) :: xpy(:)
1792
1793    !> central cell atomic coordinates
1794    real(dp), intent(in) :: coord0(:,:)
1795
1796    !> data type for atomic orbital information
1797    type(TOrbitals), intent(in) :: orb
1798
1799    !> H0 data
1800    type(OSlakoCont), intent(in) :: skHamCont
1801
1802    !> overlap data
1803    type(OSlakoCont), intent(in) :: skOverCont
1804
1805    !> Differentiatior for the non-scc matrices
1806    class(NonSccDiff), intent(in) :: derivator
1807
1808    !> ground state density matrix for spin-free case
1809    real(dp), intent(in) :: rhoSqr(:,:)
1810
1811    !> resulting excited state gradient
1812    real(dp), intent(out) :: excgrad(:,:)
1813
1814    real(dp), allocatable :: shift_excited(:), xpyq(:)
1815    real(dp), allocatable :: shxpyq(:), xpycc(:,:), wcc(:,:)
1816    real(dp), allocatable :: qij(:), temp(:)
1817    real(dp), allocatable :: dH0(:,:,:), dS(:,:,:)
1818    integer :: ia, i, j, a, b, ab, ij, m, n, mu, nu, xyz, iAt1, iAt2
1819    integer :: indalpha, indalpha1, indbeta, indbeta1
1820    integer :: iSp1, iSp2
1821    real(dp) :: tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, rab
1822    real(dp) :: diffvec(3), dgab(3), tmp3a, tmp3b
1823
1824    integer :: nxoo, nxvv
1825
1826    ALLOCATE(shift_excited(natom))
1827    ALLOCATE(xpyq(natom))
1828    ALLOCATE(shxpyq(natom))
1829    ALLOCATE(xpycc(norb, norb))
1830    ALLOCATE(wcc(norb, norb))
1831    ALLOCATE(qij(natom))
1832    ALLOCATE(temp(norb))
1833
1834    ALLOCATE(dH0(orb%mOrb, orb%mOrb, 3))
1835    ALLOCATE(dS(orb%mOrb, orb%mOrb, 3))
1836
1837    nxoo = size(woo)
1838    nxvv = size(wvv)
1839
1840    excgrad = 0.0_dp
1841
1842    ! excited state potentials at atomic sites
1843    call hemv(shift_excited, gammaMat, dqex)
1844
1845    ! xypq(alpha) = sum_ia (X+Y)_ia q^ia(alpha)
1846    ! complexity norb * norb * norb
1847    xpyq(:) = 0.0_dp
1848    call transChrg%qMatVec(iAtomStart, stimc, grndEigVecs, getij, win, xpy,xpyq)
1849
1850    ! complexity norb * norb
1851    shxpyq(:) = 0.0_dp
1852    if (sym == "S") then
1853      call hemv(shxpyq, gammaMat, xpyq)
1854    else
1855      shxpyq(:) = xpyq(:) * spinW(species0)
1856    end if
1857
1858    ! calculate xpycc
1859    ! (xpycc)_{mu nu} = sum_{ia} (X + Y)_{ia} (grndEigVecs(mu,i)grndEigVecs(nu,a)
1860    ! + grndEigVecs(nu,i)grndEigVecs(mu,a))
1861    ! complexity norb * norb * norb
1862    !
1863    ! xpycc(mu,nu) = sum_ia (X+Y)_ia grndEigVecs(mu,i) grndEigVecs(nu,a)
1864    ! xpycc(mu, nu) += sum_ia (X+Y)_ia grndEigVecs(mu,a) grndEigVecs(nu,i)
1865    xpycc(:,:) = 0.0_dp
1866    do ia = 1, nxov
1867      call indxov(win, ia, getij, i, a)
1868      ! should replace with DSYR2 call :
1869      do nu = 1, norb
1870        do mu = 1, norb
1871          xpycc(mu,nu) = xpycc(mu,nu) + xpy(ia) *&
1872              & ( grndEigVecs(mu,i,1)*grndEigVecs(nu,a,1)&
1873              & + grndEigVecs(mu,a,1)*grndEigVecs(nu,i,1) )
1874        end do
1875      end do
1876    end do
1877
1878    ! calculate wcc = c_mu,i * W_ij * c_j,nu. We have only W_ab b > a and W_ij j > i:
1879    ! wcc(m,n) = sum_{pq, p <= q} w_pq (grndEigVecs(mu,p)grndEigVecs(nu,q)
1880    ! + grndEigVecs(nu,p)grndEigVecs(mu,q))
1881    ! complexity norb * norb * norb
1882
1883    ! calculate the occ-occ part
1884    wcc(:,:) = 0.0_dp
1885
1886    do ij = 1, nxoo
1887      call indxoo(ij, i, j)
1888      ! replace with DSYR2 call :
1889      do mu = 1, norb
1890        do nu = 1, norb
1891          wcc(mu,nu) = wcc(mu,nu) + woo(ij) *&
1892              & ( grndEigVecs(mu,i,1)*grndEigVecs(nu,j,1)&
1893              & + grndEigVecs(mu,j,1)*grndEigVecs(nu,i,1) )
1894        end do
1895      end do
1896
1897    end do
1898
1899    ! calculate the occ-virt part : the same way as for xpycc
1900    do ia = 1, nxov
1901      call indxov(win, ia, getij, i, a)
1902      ! again replace with DSYR2 call :
1903      do nu = 1, norb
1904        do mu = 1, norb
1905          wcc(mu,nu) = wcc(mu,nu) + wov(ia) *&
1906              & ( grndEigVecs(mu,i,1)*grndEigVecs(nu,a,1)&
1907              & + grndEigVecs(mu,a,1)*grndEigVecs(nu,i,1) )
1908        end do
1909      end do
1910    end do
1911
1912    ! calculate the virt - virt part
1913    do ab =1, nxvv
1914      call indxvv(homo, ab, a, b)
1915      ! replace with DSYR2 call :
1916      do mu = 1, norb
1917        do nu = 1, norb
1918          wcc(mu,nu) = wcc(mu,nu) + wvv(ab) *&
1919              & ( grndEigVecs(mu,a,1)*grndEigVecs(nu,b,1)&
1920              & + grndEigVecs(mu,b,1)*grndEigVecs(nu,a,1) )
1921        end do
1922      end do
1923    end do
1924
1925
1926    ! now calculating the force complexity : norb * norb * 3
1927
1928    ! as have already performed norb**3 operation to get here,
1929    ! calculate for all atoms
1930
1931    ! BA: only for non-periodic systems!
1932    do iAt1 = 1, nAtom
1933      indalpha = iAtomStart(iAt1)
1934      indalpha1 = iAtomStart(iAt1 + 1) -1
1935      iSp1 = species0(iAt1)
1936
1937      do iAt2 = 1, iAt1 - 1
1938        indbeta = iAtomStart(iAt2)
1939        indbeta1 = iAtomStart(iAt2 + 1) -1
1940        iSp2 = species0(iAt2)
1941
1942        diffvec = coord0(:,iAt1) - coord0(:,iAt2)
1943        rab = sqrt(sum(diffvec**2))
1944
1945        ! now holds unit vector in direction
1946        diffvec = diffvec / rab
1947
1948        ! calculate the derivative of gamma
1949        dgab(:) = diffvec(:) * (-1.0_dp/rab**2 - expGammaPrime(rab, HubbardU(iSp1), HubbardU(iSp2)))
1950
1951        tmp3a = dq(iAt1) * dqex(iAt2) + dqex(iAt1) * dq(iAt2)
1952
1953        if (sym == "S") then
1954          tmp3b = 4.0_dp * xpyq(iAt1) * xpyq(iAt2)
1955        else
1956          tmp3b = 0.0_dp
1957        end if
1958
1959        excgrad(:,iAt1) = excgrad(:,iAt1) + dgab(:) * ( tmp3a + tmp3b )
1960        excgrad(:,iAt2) = excgrad(:,iAt2) - dgab(:) * ( tmp3a + tmp3b )
1961
1962        tmp5 = shift_excited(iAt1) + shift_excited(iAt2)
1963        tmp7 = 2.0_dp * ( shxpyq(iAt1) + shxpyq(iAt2) )
1964
1965        call derivator%getFirstDeriv(dH0, skHamCont, coord0, species0,&
1966            & iAt1, iAt2, orb)
1967        call derivator%getFirstDeriv(dS, skOverCont, coord0, species0,&
1968            & iAt1, iAt2, orb)
1969
1970        do xyz = 1, 3
1971
1972          tmp1 = 0.0_dp
1973          tmp2 = 0.0_dp
1974          tmp3 = 0.0_dp
1975          tmp4 = 0.0_dp
1976          tmp6 = 0.0_dp
1977
1978          do mu = indalpha, indalpha1
1979            do nu = indbeta, indbeta1
1980              m = mu - indalpha + 1
1981              n = nu - indbeta + 1
1982
1983              tmp1 = tmp1 + 2.0_dp * dH0(n,m,xyz) * pc(mu,nu)
1984              tmp2 = tmp2 + dS(n,m,xyz) * pc(mu,nu) * (shift(iAt1)+shift(iAt2))
1985              tmp3 = tmp3 - dS(n,m,xyz) * wcc(mu,nu)
1986              tmp4 = tmp4 + tmp5 * dS(n,m,xyz) * rhoSqr(mu,nu)
1987              tmp6 = tmp6 + tmp7 * dS(n,m,xyz) * xpycc(mu,nu)
1988            end do
1989          end do
1990          excgrad(xyz,iAt1) = excgrad(xyz,iAt1)&
1991              & + tmp1 + tmp2 + tmp4 + tmp6 + tmp3
1992          excgrad(xyz,iAt2) = excgrad(xyz,iAt2)&
1993              & - tmp1 - tmp2 - tmp4 - tmp6 - tmp3
1994        end do
1995      end do
1996    end do
1997
1998  end subroutine addGradients
1999
2000
2001  !> Write out excitations projected onto ground state
2002  subroutine writeCoeffs(tt, grndEigVecs, occ, nocc, fdCoeffs, tCoeffs, tIncGroundState,&
2003      & occNatural, naturalOrbs)
2004
2005    !> T part of the matrix
2006    real(dp), intent(in) :: tt(:,:)
2007
2008    !> ground state eigenvectors
2009    real(dp), intent(in) :: grndEigVecs(:,:,:)
2010
2011    !> ground state occupations
2012    real(dp), intent(in) :: occ(:,:)
2013
2014    !> number of filled states
2015    integer, intent(in) :: nocc
2016
2017    !> file descriptor to write data into
2018    integer, intent(in) :: fdCoeffs
2019
2020    !> save the coefficients of the natural orbitals
2021    logical, intent(in) :: tCoeffs
2022
2023    !> include the ground state as well as the transition part
2024    logical, intent(in) :: tIncGroundState
2025
2026    !> Natural orbital occupation numbers
2027    real(dp), intent(out), optional :: occNatural(:)
2028
2029    !> Natural orbitals
2030    real(dp), intent(out), optional :: naturalOrbs(:,:,:)
2031
2032    real(dp), allocatable :: t2(:,:), occtmp(:)
2033    integer :: norb, ii, jj, mm
2034
2035    norb = size(tt, dim=1)
2036
2037    if (present(occNatural).or.tCoeffs) then
2038
2039      ALLOCATE(t2(norb, norb))
2040      t2 = tt
2041      if (tIncGroundState) then
2042        do ii = 1, nocc
2043          t2(ii,ii) = t2(ii,ii) + occ(ii,1)
2044        end do
2045      end if
2046
2047      if (present(occNatural)) then
2048        naturalOrbs(:,:,1) = t2
2049        call evalCoeffs(naturalOrbs(:,:,1), occNatural, grndEigVecs(:,:,1))
2050        if (tCoeffs) then
2051          ALLOCATE(occtmp(size(occ)))
2052          occTmp = occNatural
2053        end if
2054      else
2055        ALLOCATE(occtmp(size(occ)))
2056        occtmp = 0.0_dp
2057        call evalCoeffs(t2, occtmp, grndEigVecs(:,:,1))
2058      end if
2059
2060      ! Better to get this by post-processing DFTB+ output, but here for
2061      ! compatibility at the moment
2062      if (tCoeffs) then
2063        open(fdCoeffs, file=excitedCoefsOut, position="append")
2064        write(fdCoeffs,*) 'T F'
2065        do ii = 1, norb
2066          jj = norb - ii + 1
2067          write(fdCoeffs, '(1x,i3,1x,f13.10,1x,f13.10)') ii, occtmp(jj), 2.0_dp
2068          write(fdCoeffs, '(6(f13.10,1x))') (cmplx(t2(mm,jj), kind=dp),&
2069              & mm = 1, norb)
2070        end do
2071        close(fdCoeffs)
2072      end if
2073
2074    end if
2075
2076  end subroutine writeCoeffs
2077
2078
2079  !> Project MO density matrix onto ground state orbitals
2080  subroutine evalCoeffs(t2, occ, eig)
2081
2082    !> density matrix
2083    real(dp), intent(inout) :: t2(:,:)
2084
2085    !> resulting natural orbital occupations
2086    real(dp), intent(out) :: occ(:)
2087
2088    !> 'natural' eigenvectors
2089    real(dp), intent(in) :: eig(:,:)
2090
2091    real(dp), allocatable :: coeffs(:,:)
2092
2093    ALLOCATE(coeffs(size(occ),size(occ)))
2094
2095    call heev(t2, occ, 'U', 'V')
2096    call gemm(coeffs, eig, t2)
2097    t2 = coeffs
2098
2099  end subroutine evalCoeffs
2100
2101
2102  !> Write out transitions from ground to excited state along with single particle transitions and
2103  !> dipole strengths
2104  subroutine writeExcitations(sym, osz, nexc, nmatup, getij, win, eval, evec, wij, fdXplusY,&
2105      & fdTrans, fdTradip, transitionDipoles, tWriteTagged, fdTagged, taggedWriter, fdExc, Ssq)
2106
2107    !> Symmetry label for the type of transition
2108    character, intent(in) :: sym
2109
2110    !> oscillator strengths for transitions from ground to excited states
2111    real(dp), intent(in) :: osz(:)
2112
2113    !> number of excited states to solve for
2114    integer, intent(in) :: nexc
2115
2116    !> number of same spin excitations
2117    integer, intent(in) :: nmatup
2118
2119    !> index array between transitions in square and 1D representations
2120    integer, intent(in) :: getij(:,:)
2121
2122    !> index array for single particle excitions
2123    integer, intent(in) :: win(:)
2124
2125    !> excitation energies
2126    real(dp), intent(in) :: eval(:)
2127
2128    !> eigenvectors of excited states
2129    real(dp), intent(in) :: evec(:,:)
2130
2131    !> single particle excitation energies
2132    real(dp), intent(in) :: wij(:)
2133
2134    !> single particle transition dipole moments
2135    real(dp), intent(in) :: transitionDipoles(:,:)
2136
2137    !> should tagged information be written out
2138    logical, intent(in) :: tWriteTagged
2139
2140    !> file unit for transition dipoles
2141    integer, intent(in) :: fdTradip
2142
2143    !> file unit for X+Y data
2144    integer, intent(in) :: fdXplusY
2145
2146    !> file unit for transitions
2147    integer, intent(in) :: fdTrans
2148
2149    !> file unit for tagged output (> -1 for write out)
2150    integer, intent(in) :: fdTagged
2151
2152    !> tagged writer
2153    type(TTaggedWriter), intent(inout) :: taggedWriter
2154
2155    !> file unit for excitation energies
2156    integer, intent(in) :: fdExc
2157
2158    !> For spin polarized systems, measure of spin
2159    real(dp), intent(in), optional :: Ssq(:)
2160
2161    integer :: nmat
2162    integer :: i, j, iweight, indo, m, n
2163    integer :: iDeg
2164    real(dp), allocatable :: wvec(:)
2165    real(dp), allocatable :: xply(:)
2166    real(dp), allocatable :: eDeg(:)
2167    real(dp), allocatable :: oDeg(:)
2168    integer, allocatable :: wvin(:)
2169    real(dp) :: rsqw, weight, wvnorm
2170    logical :: updwn, tSpin
2171    character :: sign
2172
2173    @:ASSERT(fdExc > 0)
2174
2175    tSpin = present(Ssq)
2176    nmat = size(wij)
2177    ALLOCATE(wvec(nmat))
2178    ALLOCATE(wvin(nmat))
2179    ALLOCATE(xply(nmat))
2180    ALLOCATE(eDeg(nexc))
2181    ALLOCATE(oDeg(nexc))
2182    wvec = 0.0_dp
2183    wvin = 0
2184    xply = 0.0_dp
2185    eDeg = 0.0_dp
2186    oDeg = 0.0_dp
2187
2188    if(fdXplusY > 0) then
2189      write(fdXplusY,*) nmat, nexc
2190    end if
2191
2192    do i = 1, nexc
2193      if (eval(i) > 0.0_dp) then
2194
2195        ! calculate weight of single particle transitions
2196        rsqw = 1.0_dp / sqrt(eval(i))
2197        ! (X+Y)^ia_I = sqrt(wij) / sqrt(omega) * F^ia_I
2198        xply(:) = sqrt(rsqw) * sqrt(wij(:)) * evec(:,i)
2199        wvec(:) = xply(:)**2
2200        wvnorm = 1.0_dp / sqrt(sum(wvec**2))
2201        wvec(:) = wvec(:) * wvnorm
2202
2203        ! find largest coefficient in CI - should use maxloc
2204        call index_heap_sort(wvin,wvec)
2205        wvin = wvin(size(wvin):1:-1)
2206        wvec = wvec(wvin)
2207
2208        weight = wvec(1)
2209        iweight = wvin(1)
2210
2211        call indxov(win, iweight, getij, m, n)
2212        sign = sym
2213        if (tSpin) then
2214          sign = " "
2215          write(fdExc,&
2216              & '(1x,f10.3,4x,f14.8,2x,i5,3x,a,1x,i5,7x,f6.3,2x,f10.3,4x,&
2217              & f6.3)')&
2218              & Hartree__eV * sqrt(eval(i)), osz(i), m, '->', n, weight,&
2219              & Hartree__eV * wij(iweight), Ssq(i)
2220        else
2221          write(fdExc,&
2222              & '(1x,f10.3,4x,f14.8,5x,i5,3x,a,1x,i5,7x,f6.3,2x,f10.3,6x,a)')&
2223              & Hartree__eV * sqrt(eval(i)), osz(i), m, '->', n, weight,&
2224              & Hartree__eV * wij(iweight), sign
2225        end if
2226
2227        if(fdXplusY > 0) then
2228          if (tSpin) then
2229            updwn = (win(iweight) <= nmatup)
2230            sign = "D"
2231            if (updwn) sign = "U"
2232          end if
2233          write(fdXplusY,'(1x,i5,3x,a,3x,ES17.10)') i,sign, sqrt(eval(i))
2234          write(fdXplusY,'(6(1x,ES17.10))') xply(:)
2235        endif
2236
2237        if (fdTrans > 0) then
2238          write(fdTrans, '(2x,a,T12,i5,T21,ES17.10,1x,a,2x,a)')&
2239              & 'Energy ', i,  Hartree__eV * sqrt(eval(i)), 'eV', sign
2240          write(fdTrans,*)
2241          write(fdTrans,'(2x,a,9x,a,8x,a)')&
2242              & 'Transition', 'Weight', 'KS [eV]'
2243          write(fdTrans,'(1x,45("="))')
2244
2245          sign = " "
2246          do j = 1, nmat
2247            !if (wvec(j) < 1e-4_dp) exit ! ??????
2248            indo = wvin(j)
2249            call indxov(win, indo, getij, m, n)
2250            if (tSpin) then
2251              updwn = (win(indo) <= nmatup)
2252              sign = "D"
2253              if (updwn) sign = "U"
2254            end if
2255            write(fdTrans,&
2256                & '(i5,3x,a,1x,i5,1x,1a,T22,f10.8,T33,f14.8)')&
2257                & m, '->', n, sign, wvec(j), Hartree__eV * wij(wvin(j))
2258          end do
2259          write(fdTrans,*)
2260        end if
2261
2262        if(fdTradip > 0) then
2263          write(fdTradip, '(1x,i5,1x,f10.3,2x,3(ES13.6))')&
2264              & i, Hartree__eV * sqrt(eval(i)), (transitionDipoles(i,j)&
2265              & * au__Debye, j=1,3)
2266        endif
2267      else
2268
2269        ! find largest coefficient in CI - should use maxloc
2270        call index_heap_sort(wvin,wvec)
2271        wvin = wvin(size(wvin):1:-1)
2272        wvec = wvec(wvin)
2273
2274        weight = wvec(1)
2275        iweight = wvin(1)
2276        call indxov(win, iweight, getij, m, n)
2277        sign = sym
2278
2279        if (tSpin) then
2280          sign = " "
2281          write(fdExc,&
2282              & '(6x,A,T12,4x,f14.8,2x,i5,3x,a,1x,i5,7x,A,2x,f10.3,4x,f6.3)')&
2283              & '< 0', osz(i), m, '->', n, '-', Hartree__eV * wij(iweight),&
2284              & Ssq(i)
2285        else
2286          write(fdExc,&
2287              & '(6x,A,T12,4x,f14.8,2x,i5,3x,a,1x,i5,7x,f6.3,2x,f10.3,6x,a)')&
2288              & '< 0', osz(i), m, '->', n, weight,&
2289              & Hartree__eV * wij(iweight), sign
2290        end if
2291
2292        if(fdXplusY > 0) then
2293          if (tSpin) then
2294            updwn = (win(iweight) <= nmatup)
2295            sign = "D"
2296            if (updwn) sign = "U"
2297          end if
2298          write(fdXplusY,'(1x,i5,3x,a,3x,A)') i,sign, '-'
2299        endif
2300
2301        if (fdTrans > 0) then
2302          write(fdTrans, '(2x,a,1x,i5,5x,a,1x,a,3x,a)')&
2303              & 'Energy ', i,  '-', 'eV', sign
2304          write(fdTrans,*)
2305        end if
2306
2307        if(fdTradip > 0) then
2308          write(fdTradip, '(1x,i5,1x,A)') i, '-'
2309        endif
2310
2311      end if
2312
2313    end do
2314
2315    ! Determine degenerate levels and sum oscillator strength over any degenerate levels
2316    iDeg = 1
2317    eDeg(1) = eval(1)
2318    oDeg(1) = osz(1)
2319    do i = 2, nexc
2320      if(abs(eval(i)-eval(i-1)) < elecTolMax) then
2321        oDeg(iDeg) = oDeg(iDeg) + osz(i)
2322      else
2323        iDeg = iDeg + 1
2324        eDeg(iDeg) = eval(i)
2325        oDeg(iDeg) = osz(i)
2326      endif
2327    end do
2328    if (tWriteTagged) then
2329      call taggedWriter%write(fdTagged, tagLabels%excEgy, eDeg(:iDeg))
2330      call taggedWriter%write(fdTagged, tagLabels%excOsc, oDeg(:iDeg))
2331    end if
2332
2333  end subroutine writeExcitations
2334
2335
2336  !> Create transition density matrix in MO basis P = T + 1/2 Z symmetric (paper has T + Z
2337  !> asymmetric) (Zab = Zij = 0, Tia = 0)
2338  subroutine calcPMatrix(t, rhs, win, getij, pc)
2339
2340    !> T matrix
2341    real(dp), intent(in) :: t(:,:)
2342
2343    !> Z matrix
2344    real(dp), intent(in) :: rhs(:)
2345
2346    !> index array for single particle transitions
2347    integer, intent(in) :: win(:)
2348
2349    !> array of the occupied->virtual pairs (nTransitions,occ 1 or virtual 2)
2350    integer, intent(in) :: getij(:,:)
2351
2352    !> resulting excited state density matrix
2353    real(dp), intent(out) :: pc(:,:)
2354
2355    integer :: ia, i, a
2356
2357    pc = 0.0_dp
2358    do ia = 1, size(rhs)
2359      call indxov(win, ia, getij, i, a)
2360      pc(i,a) = rhs(ia)
2361    end do
2362    pc = 0.5_dp * ( pc + transpose(pc) )
2363
2364    pc = pc + t
2365
2366  end subroutine calcPMatrix
2367
2368
2369  !> Write single particle excitations to a file as well as potentially to tagged output file (in
2370  !> that case, summing over degeneracies)
2371  subroutine writeSPExcitations(wij, win, nmatup, getij, fdSPTrans, sposz, nxov, tSpin)
2372
2373    !> single particle excitation energies
2374    real(dp), intent(in) :: wij(:)
2375
2376    !> index array for single particle transitions
2377    integer, intent(in) :: win(:)
2378
2379    !> number of transitions within same spin channel
2380    integer, intent(in) :: nmatup
2381
2382    !> index from composite index to occupied and virtual single particle states
2383    integer, intent(in) :: getij(:,:)
2384
2385    !> file descriptor for the single particle excitation data
2386    integer, intent(in) :: fdSPTrans
2387
2388    !> single particle oscilation strengths
2389    real(dp), intent(in) :: sposz(:)
2390
2391    !> Number of included single particle excitations to print out (assumes that win and wij are
2392    !> sorted so that the wanted transitions are first in the array)
2393    integer, intent(in) :: nxov
2394
2395    !> is this a spin-polarized calculation?
2396    logical, intent(in) :: tSpin
2397
2398    integer :: indm, m, n
2399    logical :: updwn
2400    character :: sign
2401
2402    @:ASSERT(size(sposz)>=nxov)
2403
2404    if (fdSPTrans > 0) then
2405      ! single particle excitations
2406      open(fdSPTrans, file=singlePartOut, position="rewind", status="replace")
2407      write(fdSPTrans,*)
2408      write(fdSPTrans,'(7x,a,7x,a,8x,a)') '#      w [eV]',&
2409          & 'Osc.Str.', 'Transition'
2410      write(fdSPTrans,*)
2411      write(fdSPTrans,'(1x,58("="))')
2412      write(fdSPTrans,*)
2413      do indm = 1, nxov
2414        call indxov(win, indm, getij, m, n)
2415        sign = " "
2416        if (tSpin) then
2417          updwn = (win(indm) <= nmatup)
2418          if (updwn) then
2419            sign = "U"
2420          else
2421            sign = "D"
2422          end if
2423        end if
2424        write(fdSPTrans,&
2425            & '(1x,i7,3x,f8.3,3x,f13.7,4x,i5,3x,a,1x,i5,1x,1a)')&
2426            & indm, Hartree__eV * wij(indm), sposz(indm), m, '->', n, sign
2427      end do
2428      write(fdSPTrans,*)
2429      close(fdSPTrans)
2430    end if
2431
2432  end subroutine writeSPExcitations
2433
2434end module dftbp_linrespgrad
2435