1!--------------------------------------------------------------------------------------------------!
2!   CP2K: A general program to perform molecular dynamics simulations                              !
3!   Copyright (C) 2000 - 2020  CP2K developers group                                               !
4!--------------------------------------------------------------------------------------------------!
5
6! **************************************************************************************************
7MODULE atom_admm_methods
8   USE atom_operators,                  ONLY: atom_basis_projection_overlap,&
9                                              atom_int_release,&
10                                              atom_int_setup
11   USE atom_output,                     ONLY: atom_print_basis
12   USE atom_types,                      ONLY: &
13        atom_basis_type, atom_integrals, atom_orbitals, atom_p_type, atom_type, create_atom_orbs, &
14        init_atom_basis, lmat, release_atom_basis, release_atom_orbs
15   USE atom_utils,                      ONLY: atom_consistent_method,&
16                                              atom_denmat,&
17                                              atom_trace,&
18                                              eeri_contract
19   USE atom_xc,                         ONLY: atom_dpot_lda,&
20                                              atom_vxc_lda,&
21                                              atom_vxc_lsd
22   USE input_constants,                 ONLY: do_rhf_atom,&
23                                              do_rks_atom,&
24                                              do_rohf_atom,&
25                                              do_uhf_atom,&
26                                              do_uks_atom,&
27                                              xc_funct_no_shortcut
28   USE input_section_types,             ONLY: section_vals_duplicate,&
29                                              section_vals_get_subs_vals,&
30                                              section_vals_get_subs_vals2,&
31                                              section_vals_release,&
32                                              section_vals_remove_values,&
33                                              section_vals_type,&
34                                              section_vals_val_set
35   USE kinds,                           ONLY: dp
36   USE mathlib,                         ONLY: invmat_symm,&
37                                              jacobi
38#include "./base/base_uses.f90"
39
40   IMPLICIT NONE
41
42   PRIVATE
43   PUBLIC  :: atom_admm
44
45   CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'atom_admm_methods'
46
47CONTAINS
48
49! **************************************************************************************************
50!> \brief Analysis of ADMM approximation to exact exchange.
51!> \param atom_info     information about the atomic kind. Two-dimensional array of size
52!>                      (electronic-configuration, electronic-structure-method)
53!> \param admm_section  ADMM print section
54!> \param iw            output file unit
55!> \par History
56!>    * 07.2016 created [Juerg Hutter]
57! **************************************************************************************************
58   SUBROUTINE atom_admm(atom_info, admm_section, iw)
59
60      TYPE(atom_p_type), DIMENSION(:, :), POINTER        :: atom_info
61      TYPE(section_vals_type), POINTER                   :: admm_section
62      INTEGER, INTENT(IN)                                :: iw
63
64      CHARACTER(len=*), PARAMETER :: routineN = 'atom_admm', routineP = moduleN//':'//routineN
65
66      CHARACTER(LEN=2)                                   :: btyp
67      INTEGER                                            :: i, ifun, j, l, m, maxl, mo, n, na, nb, &
68                                                            zval
69      LOGICAL                                            :: pp_calc, rhf
70      REAL(KIND=dp) :: admm1_k_energy, admm2_k_energy, admmq_k_energy, dfexc_admm1, dfexc_admm2, &
71         dfexc_admmq, dxc, dxk, el1, el2, elq, elref, fexc_optx_admm1, fexc_optx_admm2, &
72         fexc_optx_admmq, fexc_optx_ref, fexc_pbex_admm1, fexc_pbex_admm2, fexc_pbex_admmq, &
73         fexc_pbex_ref, ref_energy, xsi
74      REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: lamat
75      REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: admm1_k, admm2_k, admm_xcmat, admm_xcmata, &
76         admm_xcmatb, admmq_k, ovlap, ref_k, ref_xcmat, ref_xcmata, ref_xcmatb, sinv, siref, tmat
77      TYPE(atom_basis_type), POINTER                     :: admm_basis, ref_basis
78      TYPE(atom_integrals), POINTER                      :: admm_int, ref_int
79      TYPE(atom_orbitals), POINTER                       :: admm1_orbs, admm2_orbs, admmq_orbs, &
80                                                            ref_orbs
81      TYPE(atom_type), POINTER                           :: atom
82      TYPE(section_vals_type), POINTER                   :: basis_section, xc_fun, xc_fun_section, &
83                                                            xc_optx, xc_pbex, xc_section
84
85      IF (iw > 0) THEN
86         WRITE (iw, '(/,T2,A)') &
87            '!-----------------------------------------------------------------------------!'
88         WRITE (iw, '(T30,A)') "Analysis of ADMM approximations"
89         WRITE (iw, '(T2,A)') &
90            '!-----------------------------------------------------------------------------!'
91      END IF
92
93      ! setup an xc section
94      NULLIFY (xc_section, xc_pbex, xc_optx)
95      CALL section_vals_duplicate(atom_info(1, 1)%atom%xc_section, xc_section)
96      xc_fun_section => section_vals_get_subs_vals(xc_section, "XC_FUNCTIONAL")
97      ! Overwrite possible shortcut
98      CALL section_vals_val_set(xc_fun_section, "_SECTION_PARAMETERS_", &
99                                i_val=xc_funct_no_shortcut)
100      ifun = 0
101      DO
102         ifun = ifun + 1
103         xc_fun => section_vals_get_subs_vals2(xc_fun_section, i_section=ifun)
104         IF (.NOT. ASSOCIATED(xc_fun)) EXIT
105         CALL section_vals_remove_values(xc_fun)
106      END DO
107      ! PBEX
108      CALL section_vals_duplicate(xc_section, xc_pbex)
109      xc_fun_section => section_vals_get_subs_vals(xc_pbex, "XC_FUNCTIONAL")
110      CALL section_vals_val_set(xc_fun_section, "PBE%_SECTION_PARAMETERS_", l_val=.TRUE.)
111      CALL section_vals_val_set(xc_fun_section, "PBE%SCALE_X", r_val=1.0_dp)
112      CALL section_vals_val_set(xc_fun_section, "PBE%SCALE_C", r_val=0.0_dp)
113      ! OPTX
114      CALL section_vals_duplicate(xc_section, xc_optx)
115      xc_fun_section => section_vals_get_subs_vals(xc_optx, "XC_FUNCTIONAL")
116      CALL section_vals_val_set(xc_fun_section, "OPTX%_SECTION_PARAMETERS_", l_val=.TRUE.)
117      CALL section_vals_val_set(xc_fun_section, "OPTX%SCALE_X", r_val=1.0_dp)
118
119      ! ADMM basis set
120      zval = atom_info(1, 1)%atom%z
121      pp_calc = atom_info(1, 1)%atom%pp_calc
122      btyp = "AE"
123      IF (pp_calc) btyp = "PP"
124      ALLOCATE (admm_basis)
125      basis_section => section_vals_get_subs_vals(admm_section, "ADMM_BASIS")
126      NULLIFY (admm_basis%grid)
127      CALL init_atom_basis(admm_basis, basis_section, zval, btyp)
128      IF (iw > 0) THEN
129         CALL atom_print_basis(admm_basis, iw, " ADMM Basis")
130      END IF
131      ! reference basis set
132      ref_basis => atom_info(1, 1)%atom%basis
133      ! integrals
134      ALLOCATE (ref_int, admm_int)
135      CALL atom_int_setup(ref_int, ref_basis, eri_exchange=.TRUE.)
136      CALL atom_int_setup(admm_int, admm_basis, eri_exchange=.TRUE.)
137      DO l = 0, lmat
138         IF (admm_int%n(l) /= admm_int%nne(l)) THEN
139            IF (iw > 0) WRITE (iw, *) "ADMM Basis is linear dependent ", l, admm_int%n(l), admm_int%nne(l)
140            CPABORT("ADMM basis is linear dependent")
141         END IF
142      END DO
143      ! mixed overlap
144      na = MAXVAL(admm_basis%nbas(:))
145      nb = MAXVAL(ref_basis%nbas(:))
146      ALLOCATE (ovlap(1:na, 1:nb, 0:lmat))
147      CALL atom_basis_projection_overlap(ovlap, admm_basis, ref_basis)
148      ! Inverse of ADMM overlap matrix
149      ALLOCATE (sinv(1:na, 1:na, 0:lmat))
150      DO l = 0, lmat
151         n = admm_basis%nbas(l)
152         IF (n < 1) CYCLE
153         sinv(1:n, 1:n, l) = admm_int%ovlp(1:n, 1:n, l)
154         CALL invmat_symm(sinv(1:n, 1:n, l))
155      END DO
156      ! ADMM transformation matrix
157      ALLOCATE (tmat(1:na, 1:nb, 0:lmat))
158      DO l = 0, lmat
159         n = admm_basis%nbas(l)
160         m = ref_basis%nbas(l)
161         IF (n < 1 .OR. m < 1) CYCLE
162         tmat(1:n, 1:m, l) = MATMUL(sinv(1:n, 1:n, l), ovlap(1:n, 1:m, l))
163      END DO
164      ! Inverse of REF overlap matrix
165      ALLOCATE (siref(1:nb, 1:nb, 0:lmat))
166      DO l = 0, lmat
167         n = ref_basis%nbas(l)
168         IF (n < 1) CYCLE
169         siref(1:n, 1:n, l) = ref_int%ovlp(1:n, 1:n, l)
170         CALL invmat_symm(siref(1:n, 1:n, l))
171      END DO
172
173      DO i = 1, SIZE(atom_info, 1)
174         DO j = 1, SIZE(atom_info, 2)
175            atom => atom_info(i, j)%atom
176            IF (atom_consistent_method(atom%method_type, atom%state%multiplicity)) THEN
177               ref_orbs => atom%orbitals
178               ALLOCATE (ref_k(1:nb, 1:nb, 0:lmat))
179               SELECT CASE (atom%method_type)
180               CASE (do_rks_atom, do_rhf_atom)
181                  ! restricted
182                  rhf = .TRUE.
183                  ref_k = 0.0_dp
184                  CALL eeri_contract(ref_k, ref_int%eeri, ref_orbs%pmat, ref_basis%nbas)
185                  ref_energy = 0.5_dp*atom_trace(ref_k, ref_orbs%pmat)
186               CASE (do_uks_atom, do_uhf_atom)
187                  ! unrestricted
188                  rhf = .FALSE.
189                  ref_k = 0.0_dp
190                  CALL eeri_contract(ref_k, ref_int%eeri, ref_orbs%pmata, ref_basis%nbas)
191                  ref_energy = atom_trace(ref_k, ref_orbs%pmata)
192                  ref_k = 0.0_dp
193                  CALL eeri_contract(ref_k, ref_int%eeri, ref_orbs%pmatb, ref_basis%nbas)
194                  ref_energy = ref_energy + atom_trace(ref_k, ref_orbs%pmatb)
195               CASE (do_rohf_atom)
196                  CPABORT("ADMM not available")
197               CASE DEFAULT
198                  CPABORT("ADMM not available")
199               END SELECT
200               DEALLOCATE (ref_k)
201               ! reference number of electrons
202               elref = atom_trace(ref_int%ovlp, ref_orbs%pmat)
203               ! admm orbitals and density matrices
204               mo = MAXVAL(atom%state%maxn_calc)
205               NULLIFY (admm1_orbs, admm2_orbs, admmq_orbs)
206               CALL create_atom_orbs(admm1_orbs, na, mo)
207               CALL create_atom_orbs(admm2_orbs, na, mo)
208               CALL create_atom_orbs(admmq_orbs, na, mo)
209               ALLOCATE (lamat(1:mo, 1:mo))
210               ALLOCATE (admm1_k(1:na, 1:na, 0:lmat))
211               ALLOCATE (admm2_k(1:na, 1:na, 0:lmat))
212               ALLOCATE (admmq_k(1:na, 1:na, 0:lmat))
213               IF (rhf) THEN
214                  DO l = 0, lmat
215                     n = admm_basis%nbas(l)
216                     m = ref_basis%nbas(l)
217                     mo = atom%state%maxn_calc(l)
218                     IF (n < 1 .OR. m < 1 .OR. mo < 1) CYCLE
219                     admm2_orbs%wfn(1:n, 1:mo, l) = MATMUL(tmat(1:n, 1:m, l), ref_orbs%wfn(1:m, 1:mo, l))
220                     CALL lowdin_matrix(admm2_orbs%wfn(1:n, 1:mo, l), lamat(1:mo, 1:mo), admm_int%ovlp(1:n, 1:n, l))
221                     admm1_orbs%wfn(1:n, 1:mo, l) = MATMUL(admm2_orbs%wfn(1:n, 1:mo, l), lamat(1:mo, 1:mo))
222                  END DO
223                  CALL atom_denmat(admm1_orbs%pmat, admm1_orbs%wfn, admm_basis%nbas, atom%state%occupation, &
224                                   atom%state%maxl_occ, atom%state%maxn_occ)
225                  CALL atom_denmat(admm2_orbs%pmat, admm2_orbs%wfn, admm_basis%nbas, atom%state%occupation, &
226                                   atom%state%maxl_occ, atom%state%maxn_occ)
227                  el1 = atom_trace(admm_int%ovlp, admm1_orbs%pmat)
228                  el2 = atom_trace(admm_int%ovlp, admm2_orbs%pmat)
229                  xsi = elref/el2
230                  admmq_orbs%pmat = xsi*admm2_orbs%pmat
231                  elq = atom_trace(admm_int%ovlp, admmq_orbs%pmat)
232                  admmq_orbs%wfn = SQRT(xsi)*admm2_orbs%wfn
233                  !
234                  admm1_k = 0.0_dp
235                  CALL eeri_contract(admm1_k, admm_int%eeri, admm1_orbs%pmat, admm_basis%nbas)
236                  admm1_k_energy = 0.5_dp*atom_trace(admm1_k, admm1_orbs%pmat)
237                  admm2_k = 0.0_dp
238                  CALL eeri_contract(admm2_k, admm_int%eeri, admm2_orbs%pmat, admm_basis%nbas)
239                  admm2_k_energy = 0.5_dp*atom_trace(admm2_k, admm2_orbs%pmat)
240                  admmq_k = 0.0_dp
241                  CALL eeri_contract(admmq_k, admm_int%eeri, admmq_orbs%pmat, admm_basis%nbas)
242                  admmq_k_energy = 0.5_dp*atom_trace(admmq_k, admmq_orbs%pmat)
243               ELSE
244                  DO l = 0, lmat
245                     n = admm_basis%nbas(l)
246                     m = ref_basis%nbas(l)
247                     mo = atom%state%maxn_calc(l)
248                     IF (n < 1 .OR. m < 1 .OR. mo < 1) CYCLE
249                     admm2_orbs%wfna(1:n, 1:mo, l) = MATMUL(tmat(1:n, 1:m, l), ref_orbs%wfna(1:m, 1:mo, l))
250                     CALL lowdin_matrix(admm2_orbs%wfna(1:n, 1:mo, l), lamat, admm_int%ovlp(1:n, 1:n, l))
251                     admm1_orbs%wfna(1:n, 1:mo, l) = MATMUL(admm2_orbs%wfna(1:n, 1:mo, l), lamat(1:mo, 1:mo))
252                     admm2_orbs%wfnb(1:n, 1:mo, l) = MATMUL(tmat(1:n, 1:m, l), ref_orbs%wfnb(1:m, 1:mo, l))
253                     CALL lowdin_matrix(admm2_orbs%wfnb(1:n, 1:mo, l), lamat, admm_int%ovlp(1:n, 1:n, l))
254                     admm1_orbs%wfnb(1:n, 1:mo, l) = MATMUL(admm2_orbs%wfnb(1:n, 1:mo, l), lamat(1:mo, 1:mo))
255                  END DO
256                  CALL atom_denmat(admm1_orbs%pmata, admm1_orbs%wfna, admm_basis%nbas, atom%state%occa, &
257                                   atom%state%maxl_occ, atom%state%maxn_occ)
258                  CALL atom_denmat(admm1_orbs%pmatb, admm1_orbs%wfnb, admm_basis%nbas, atom%state%occb, &
259                                   atom%state%maxl_occ, atom%state%maxn_occ)
260                  admm1_orbs%pmat = admm1_orbs%pmata + admm1_orbs%pmatb
261                  CALL atom_denmat(admm2_orbs%pmata, admm2_orbs%wfna, admm_basis%nbas, atom%state%occa, &
262                                   atom%state%maxl_occ, atom%state%maxn_occ)
263                  CALL atom_denmat(admm2_orbs%pmatb, admm2_orbs%wfnb, admm_basis%nbas, atom%state%occb, &
264                                   atom%state%maxl_occ, atom%state%maxn_occ)
265                  admm2_orbs%pmat = admm2_orbs%pmata + admm2_orbs%pmatb
266                  elref = atom_trace(ref_int%ovlp, ref_orbs%pmata)
267                  el2 = atom_trace(admm_int%ovlp, admm2_orbs%pmata)
268                  xsi = elref/el2
269                  admmq_orbs%pmata = xsi*admm2_orbs%pmata
270                  admmq_orbs%wfna = SQRT(xsi)*admm2_orbs%wfna
271                  elref = atom_trace(ref_int%ovlp, ref_orbs%pmatb)
272                  el2 = atom_trace(admm_int%ovlp, admm2_orbs%pmatb)
273                  xsi = elref/el2
274                  admmq_orbs%pmatb = xsi*admm2_orbs%pmatb
275                  admmq_orbs%wfnb = SQRT(xsi)*admm2_orbs%wfnb
276                  admmq_orbs%pmat = admmq_orbs%pmata + admmq_orbs%pmatb
277                  el1 = atom_trace(admm_int%ovlp, admm1_orbs%pmat)
278                  el2 = atom_trace(admm_int%ovlp, admm2_orbs%pmat)
279                  elq = atom_trace(admm_int%ovlp, admmq_orbs%pmat)
280                  elref = atom_trace(ref_int%ovlp, ref_orbs%pmat)
281                  !
282                  admm1_k = 0.0_dp
283                  CALL eeri_contract(admm1_k, admm_int%eeri, admm1_orbs%pmata, admm_basis%nbas)
284                  admm1_k_energy = atom_trace(admm1_k, admm1_orbs%pmata)
285                  admm1_k = 0.0_dp
286                  CALL eeri_contract(admm1_k, admm_int%eeri, admm1_orbs%pmatb, admm_basis%nbas)
287                  admm1_k_energy = admm1_k_energy + atom_trace(admm1_k, admm1_orbs%pmatb)
288                  admm2_k = 0.0_dp
289                  CALL eeri_contract(admm2_k, admm_int%eeri, admm2_orbs%pmata, admm_basis%nbas)
290                  admm2_k_energy = atom_trace(admm2_k, admm2_orbs%pmata)
291                  admm2_k = 0.0_dp
292                  CALL eeri_contract(admm2_k, admm_int%eeri, admm2_orbs%pmatb, admm_basis%nbas)
293                  admm2_k_energy = admm2_k_energy + atom_trace(admm2_k, admm2_orbs%pmatb)
294                  admmq_k = 0.0_dp
295                  CALL eeri_contract(admmq_k, admm_int%eeri, admmq_orbs%pmata, admm_basis%nbas)
296                  admmq_k_energy = atom_trace(admmq_k, admmq_orbs%pmata)
297                  admmq_k = 0.0_dp
298                  CALL eeri_contract(admmq_k, admm_int%eeri, admmq_orbs%pmatb, admm_basis%nbas)
299                  admmq_k_energy = admmq_k_energy + atom_trace(admmq_k, admmq_orbs%pmatb)
300               END IF
301               DEALLOCATE (lamat)
302               !
303               ! ADMM correction terms
304               maxl = atom%state%maxl_occ
305               IF (rhf) THEN
306                  ALLOCATE (ref_xcmat(1:nb, 1:nb, 0:lmat))
307                  ALLOCATE (admm_xcmat(1:na, 1:na, 0:lmat))
308                  ! PBEX
309                  CALL atom_vxc_lda(ref_basis, ref_orbs%pmat, maxl, xc_pbex, fexc_pbex_ref, ref_xcmat)
310                  CALL atom_vxc_lda(admm_basis, admm1_orbs%pmat, maxl, xc_pbex, fexc_pbex_admm1, admm_xcmat)
311                  CALL atom_vxc_lda(admm_basis, admm2_orbs%pmat, maxl, xc_pbex, fexc_pbex_admm2, admm_xcmat)
312                  CALL atom_vxc_lda(admm_basis, admmq_orbs%pmat, maxl, xc_pbex, fexc_pbex_admmq, admm_xcmat)
313                  ! OPTX
314                  CALL atom_vxc_lda(ref_basis, ref_orbs%pmat, maxl, xc_optx, fexc_optx_ref, ref_xcmat)
315                  CALL atom_vxc_lda(admm_basis, admm1_orbs%pmat, maxl, xc_optx, fexc_optx_admm1, admm_xcmat)
316                  CALL atom_vxc_lda(admm_basis, admm2_orbs%pmat, maxl, xc_optx, fexc_optx_admm2, admm_xcmat)
317                  CALL atom_vxc_lda(admm_basis, admmq_orbs%pmat, maxl, xc_optx, fexc_optx_admmq, admm_xcmat)
318                  ! LINX
319                  CALL atom_dpot_lda(ref_basis, ref_orbs%pmat, admm_basis, admm1_orbs%pmat, &
320                                     maxl, "LINX", dfexc_admm1)
321                  CALL atom_dpot_lda(ref_basis, ref_orbs%pmat, admm_basis, admm2_orbs%pmat, &
322                                     maxl, "LINX", dfexc_admm2)
323                  CALL atom_dpot_lda(ref_basis, ref_orbs%pmat, admm_basis, admmq_orbs%pmat, &
324                                     maxl, "LINX", dfexc_admmq)
325                  DEALLOCATE (ref_xcmat, admm_xcmat)
326               ELSE
327                  ALLOCATE (ref_xcmata(1:nb, 1:nb, 0:lmat))
328                  ALLOCATE (ref_xcmatb(1:nb, 1:nb, 0:lmat))
329                  ALLOCATE (admm_xcmata(1:na, 1:na, 0:lmat))
330                  ALLOCATE (admm_xcmatb(1:na, 1:na, 0:lmat))
331                  ! PBEX
332                  CALL atom_vxc_lsd(ref_basis, ref_orbs%pmata, ref_orbs%pmatb, maxl, xc_pbex, fexc_pbex_ref, &
333                                    ref_xcmata, ref_xcmatb)
334                  CALL atom_vxc_lsd(admm_basis, admm1_orbs%pmata, admm1_orbs%pmatb, maxl, xc_pbex, &
335                                    fexc_pbex_admm1, admm_xcmata, admm_xcmatb)
336                  CALL atom_vxc_lsd(admm_basis, admm2_orbs%pmata, admm2_orbs%pmatb, maxl, xc_pbex, &
337                                    fexc_pbex_admm2, admm_xcmata, admm_xcmatb)
338                  CALL atom_vxc_lsd(admm_basis, admmq_orbs%pmata, admmq_orbs%pmatb, maxl, xc_pbex, &
339                                    fexc_pbex_admmq, admm_xcmata, admm_xcmatb)
340                  CALL atom_vxc_lsd(ref_basis, ref_orbs%pmata, ref_orbs%pmatb, maxl, xc_optx, fexc_optx_ref, &
341                                    ref_xcmata, ref_xcmatb)
342                  CALL atom_vxc_lsd(admm_basis, admm1_orbs%pmata, admm1_orbs%pmatb, maxl, xc_optx, &
343                                    fexc_optx_admm1, admm_xcmata, admm_xcmatb)
344                  CALL atom_vxc_lsd(admm_basis, admm2_orbs%pmata, admm2_orbs%pmatb, maxl, xc_optx, &
345                                    fexc_optx_admm2, admm_xcmata, admm_xcmatb)
346                  CALL atom_vxc_lsd(admm_basis, admmq_orbs%pmata, admmq_orbs%pmatb, maxl, xc_optx, &
347                                    fexc_optx_admmq, admm_xcmata, admm_xcmatb)
348                  DEALLOCATE (ref_xcmata, ref_xcmatb, admm_xcmata, admm_xcmatb)
349               END IF
350               !
351
352               IF (iw > 0) THEN
353                  WRITE (iw, "(/,A,I3,T48,A,I3)") " Electronic Structure Setting: ", i, &
354                     " Electronic Structure Method: ", j
355                  WRITE (iw, "(' Norm of ADMM Basis projection ',T61,F20.10)") el2/elref
356                  WRITE (iw, "(' Reference Exchange Energy [Hartree]',T61,F20.10)") ref_energy
357                  ! ADMM1
358                  dxk = ref_energy - admm1_k_energy
359                  WRITE (iw, "(A,F20.10,T60,A,F13.10)") " ADMM1 METHOD: Energy ", admm1_k_energy, &
360                     " Error: ", dxk
361                  dxc = fexc_pbex_ref - fexc_pbex_admm1
362                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "PBEX Correction  ", dxc, dxc/dxk*100._dp, &
363                     " Error: ", dxk - dxc
364                  dxc = fexc_optx_ref - fexc_optx_admm1
365                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "OPTX Correction  ", dxc, dxc/dxk*100._dp, &
366                     " Error: ", dxk - dxc
367                  dxc = dfexc_admm1
368                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "LINX Correction  ", dxc, dxc/dxk*100._dp, &
369                     " Error: ", dxk - dxc
370                  ! ADMM2
371                  dxk = ref_energy - admm2_k_energy
372                  WRITE (iw, "(A,F20.10,T60,A,F13.10)") " ADMM2 METHOD: Energy ", admm2_k_energy, &
373                     " Error: ", dxk
374                  dxc = fexc_pbex_ref - fexc_pbex_admm2
375                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "PBEX Correction  ", dxc, dxc/dxk*100._dp, &
376                     " Error: ", dxk - dxc
377                  dxc = fexc_optx_ref - fexc_optx_admm2
378                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "OPTX Correction  ", dxc, dxc/dxk*100._dp, &
379                     " Error: ", dxk - dxc
380                  dxc = dfexc_admm2
381                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "LINX Correction  ", dxc, dxc/dxk*100._dp, &
382                     " Error: ", dxk - dxc
383                  ! ADMMQ
384                  dxk = ref_energy - admmq_k_energy
385                  WRITE (iw, "(A,F20.10,T60,A,F13.10)") " ADMMQ METHOD: Energy ", admmq_k_energy, &
386                     " Error: ", dxk
387                  dxc = fexc_pbex_ref - fexc_pbex_admmq
388                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "PBEX Correction  ", dxc, dxc/dxk*100._dp, &
389                     " Error: ", dxk - dxc
390                  dxc = fexc_optx_ref - fexc_optx_admmq
391                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "OPTX Correction  ", dxc, dxc/dxk*100._dp, &
392                     " Error: ", dxk - dxc
393                  dxc = dfexc_admmq
394                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "LINX Correction  ", dxc, dxc/dxk*100._dp, &
395                     " Error: ", dxk - dxc
396                  ! ADMMS
397                  dxk = ref_energy - admmq_k_energy
398                  WRITE (iw, "(A,F20.10,T60,A,F13.10)") " ADMMS METHOD: Energy ", admmq_k_energy, &
399                     " Error: ", dxk
400                  dxc = fexc_pbex_ref - fexc_pbex_admmq*xsi**(2._dp/3._dp)
401                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "PBEX Correction  ", dxc, dxc/dxk*100._dp, &
402                     " Error: ", dxk - dxc
403                  dxc = fexc_optx_ref - fexc_optx_admmq*xsi**(2._dp/3._dp)
404                  WRITE (iw, "(T10,A,F12.6,F12.3,'%',T60,A,F13.10)") "OPTX Correction  ", dxc, dxc/dxk*100._dp, &
405                     " Error: ", dxk - dxc
406               END IF
407               !
408               DEALLOCATE (admm1_k, admm2_k, admmq_k)
409               !
410               CALL release_atom_orbs(admm1_orbs)
411               CALL release_atom_orbs(admm2_orbs)
412               CALL release_atom_orbs(admmq_orbs)
413            END IF
414         END DO
415      END DO
416
417      ! clean up
418      CALL atom_int_release(ref_int)
419      CALL atom_int_release(admm_int)
420      CALL release_atom_basis(admm_basis)
421      DEALLOCATE (ref_int, admm_int, admm_basis)
422      DEALLOCATE (ovlap, sinv, tmat, siref)
423
424      CALL section_vals_release(xc_pbex)
425      CALL section_vals_release(xc_optx)
426      CALL section_vals_release(xc_section)
427
428      IF (iw > 0) THEN
429         WRITE (iw, '(/,T2,A)') &
430            '!------------------------------End of ADMM analysis---------------------------!'
431      END IF
432
433   END SUBROUTINE atom_admm
434
435! **************************************************************************************************
436!> \brief ...
437!> \param wfn ...
438!> \param lamat ...
439!> \param ovlp ...
440! **************************************************************************************************
441   SUBROUTINE lowdin_matrix(wfn, lamat, ovlp)
442      REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: wfn
443      REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: lamat
444      REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: ovlp
445
446      INTEGER                                            :: i, j, k, n
447      REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: w
448      REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: vmat
449
450      n = SIZE(wfn, 2)
451      IF (n > 0) THEN
452         lamat = MATMUL(TRANSPOSE(wfn), MATMUL(ovlp, wfn))
453         ALLOCATE (w(n), vmat(n, n))
454         CALL jacobi(lamat(1:n, 1:n), w(1:n), vmat(1:n, 1:n))
455         w(1:n) = 1.0_dp/SQRT(w(1:n))
456         DO i = 1, n
457            DO j = 1, n
458               lamat(i, j) = 0.0_dp
459               DO k = 1, n
460                  lamat(i, j) = lamat(i, j) + vmat(i, k)*w(k)*vmat(j, k)
461               END DO
462            END DO
463         END DO
464         DEALLOCATE (vmat, w)
465      END IF
466
467   END SUBROUTINE lowdin_matrix
468
469END MODULE atom_admm_methods
470