summaryrefslogtreecommitdiff
path: root/Trivac/src/ALBEIGS.f90
blob: de3a1c5add18f4857cc789671fdc8974722dd976 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
!
!----------------------------------------------------------------------------
!
!Purpose:
! Find a few eigenvalues and eigenvectors for the standard eigenvalue problem
! A*x = lambda*x using the implicit restarted Arnoldi method (IRAM). 
!
!Copyright:
! Copyright (C) 2020 Ecole Polytechnique de Montreal
! This library is free software; you can redistribute it and/or
! modify it under the terms of the GNU Lesser General Public
! License as published by the Free Software Foundation; either
! version 2.1 of the License, or (at your option) any later version.
!
!Author(s): A. Hebert
!
!Reference:
! J. Baglama, "Augmented Block Householder Arnoldi Method,"
! Linear Algebra Appl., 429, Issue 10, 2315-2334 (2008).
!
!Parameters: input
! atv     function pointer for the matrix-vector product returning Ab
!         where b is input. The format for atv is "x=atv(b,n,blsz,iter,...)"
! n       order of matrix A.
! blsz    block size of the Arnoldi Hessenberg matrix (blsz=3 recommended)
! K_org   number of desired eigenvalues
! maxit   maximum number of iterations
! tol     tolerance used for convergence (tol=1.0d-6 recommended)
! impx    print parameter: =0: no print; =1: minimum printing.
! iptrk   L_TRACK pointer to the tracking information
! ipsys   L_SYSTEM pointer to system matrices
! ipflux  L_FLUX pointer to the solution
!
!Parameters: output
! iter    actual number of iterations
! V       eigenvector matrix
! D       eigenvalue diagonal matrix
!
!----------------------------------------------------------------------------
!
subroutine ALBEIGS(atv,n,blsz,K_org,maxit,tol,impx,iter,V,D,iptrk,ipsys,ipflux)
  use GANLIB
  implicit complex(kind=8)(a-h,o-z)
  !----
  !  Subroutine arguments
  !----
  interface
    function atv(b,n,blsz,iter,iptrk,ipsys,ipflux) result(x)
      use GANLIB
      integer, intent(in) :: n,blsz,iter
      complex(kind=8), dimension(n,blsz), intent(in) :: b
      complex(kind=8), dimension(n,blsz) :: x
      type(c_ptr) iptrk,ipsys,ipflux
    end function atv
  end interface
  integer, intent(in) :: n,blsz,K_org,maxit,impx
  real(kind=8), intent(in) :: tol
  integer, intent(out) :: iter
  complex(kind=8), dimension(n,K_org), intent(inout) :: V
  complex(kind=8), dimension(K_org,K_org), intent(out) :: D
  type(c_ptr) iptrk,ipsys,ipflux
  !----
  !  Local variables
  !----
  integer :: Hsz_n,Hsz_m,Tsz_m,H_sz1,H_sz2,adjust
  real(kind=8) :: tol2
  integer, parameter :: nbls_init=10 ! Maximum number of Arnoldi vectors.
  integer, parameter :: iunout=6
  real(kind=8), parameter :: eps=epsilon(tol)
  complex(kind=8), dimension(1,1) :: onebyone
  complex(kind=8), dimension(2,2) :: twobytwo
  logical :: eig_conj
  character(len=1) :: conv
  character(len=131) :: hsmg
  !----
  !  Allocatable arrays
  !----
  integer, allocatable, dimension(:) :: vadjust,iset
  real(kind=8), allocatable, dimension(:) :: residuals,tau
  real(kind=8), allocatable, dimension(:,:) :: work2d_r,w,QR,T_Schur
  complex(kind=8), allocatable, dimension(:) :: V_sign,work1d
  complex(kind=8), allocatable, dimension(:,:) :: T_blk,H_R,DD,Q,R,work2d
  complex(kind=8), pointer, dimension(:) :: DH_eig
  complex(kind=8), pointer, dimension(:,:) :: T,T_old,H,H_old,VC,VC_old,H_eig,H_eigv

  ! Resize Krylov subspace if blsz*nbls (i.e. number of Arnoldi vectors)
  ! is larger than n (i.e. the size of the matrix A).
  if(impx > 1) write(iunout,'(/23H ALBEIGS: IRAM solution)')
  nbls = nbls_init
  if(blsz*nbls >= n) then
    nbls = floor(real(n)/real(blsz))
    write(6,'(26h ALBEIGS: Changing nbls to,i5)') nbls
  endif

  ! Increase the number of desired values to help increase convergence.
  ! Set K_org+adjust(1) to be next multiple of blsz > K_org.
  allocate(vadjust(blsz))
  do i=1,blsz
    vadjust(i)=mod(K_org+i-1,blsz)
  enddo
  adjust=findlc(vadjust,0)
  deallocate(vadjust)
  K = K_org + adjust
  allocate(VC(n,blsz), T(blsz,blsz))

  ! Check for input errors in the structure array.
  if(K <= 0)    call XABORT('ALBEIGS: K must be a positive value.')
  if(K > n)     call XABORT('ALBEIGS: K is too large.')
  if(blsz <= 0) call XABORT('ALBEIGS: blsz must be a positive value.')
  if(blsz > K_org)  call XABORT('ALBEIGS: blsz <= K_org expected.')

  ! Automatically adjust Krylov subspace to accommodate larger values of K.
  if(blsz*(nbls-1) - blsz - K - 1 < 0) then
    nbls = ceiling((K+1)/blsz+2.1)
    write(6,'(26h ALBEIGS: Changing nbls to,i5)') nbls
  endif
  if(blsz*nbls >= n) then
    nbls = floor(n/blsz-0.1)
    write(6,'(26h ALBEIGS: Changing nbls to,i5)') nbls
  endif
  if(blsz*(nbls-1) - blsz - K - 1 < 0)  call XABORT('ALBEIGS: K is too large.')
  VC(:n,:blsz) = V(:n,:blsz) ! set initial eigenvector estimate
  
  tol2 = tol
  if(tol < eps) tol2 = eps ! Set tolerance to machine precision if tol < eps.

  allocate(tau(n),residuals(K_org))
  ! allocate adjustable T matrix in the WY representation of the Householder
  ! products.
  m = blsz     ! Current number of columns in the matrix VC.
  nullify(DH_eig, H_eig, H_eigv)
  allocate(H(blsz,0))
  iter = 0     ! Main loop iteration count.
  do
    iter = iter+1
    if(iter > maxit) then
      call XABORT('ALBEIGS: maximum number of IRAM iterations reached')
    endif
    allocate(T_blk(blsz,blsz)); T_blk(:blsz,:blsz)=0.0d0;
    ! Compute the block Householder Arnoldi decomposition.
    if(blsz*(nbls+1) > n) nbls = floor(real(n)/real(blsz))-1

    ! Begin of main iteration loop for the Augmented Block Householder Arnoldi
    ! decomposition.
    do
      if(m > blsz*(nbls+1)) exit
      allocate(work2d_r(n-m+blsz,blsz))
      work2d_r(:n-m+blsz,:blsz) = real(VC(m-blsz+1:n,m-blsz+1:m))
      call ALST2F(n-m+blsz,n-m+blsz,blsz,work2d_r(:,:),tau)
      VC(m-blsz+1:n,m-blsz+1:m) = work2d_r(:n-m+blsz,:blsz)
      deallocate(work2d_r)
      if(m > blsz) then
        H_old => H
        allocate(H(m,m-blsz)); H(:m,:m-blsz) = 0.0d0;
        H(:m-blsz,:m-2*blsz) = H_old(:,:)
        deallocate(H_old)
        H(:m-blsz,m-2*blsz+1:m-blsz) = VC(:m-blsz,m-blsz+1:m)
        do i=m-blsz+1,m
          H(i,i-blsz:m-blsz) = VC(i,i:m)
        enddo
      endif
      m2 = 2*m
      if(m2 > n) m2=n
      do j=1,blsz
        VC(1:m-blsz+j,m-blsz+j) = 0.0d0
      enddo
      do i=m-blsz+1,m
        VC(i,i)=1.0d0
      enddo
      onebyone=matmul(tcg(VC(:,m-blsz+1:m-blsz+1)),VC(:,m-blsz+1:m-blsz+1))
      T_blk(1,1) = -2.0d0/onebyone(1,1)
      do i=2,blsz
        T_blk(:i,i:i) = tcg(matmul(tcg(VC(:,m-blsz+i:m-blsz+i)),VC(:,m-blsz+1:m-blsz+i)))
        T_blk(i,i) = -2.0d0/T_blk(i,i)
        T_blk(:i-1,i) = T_blk(i,i)*matmul(T_blk(:i-1,:i-1),T_blk(:i-1,i))
      enddo

      ! Matrix T expansion
      if(m == blsz) then
        T(:m,:m) = T_blk(:m,:m)
      else
        T_old => T
        allocate(T(m,m))
        T(:m-blsz,:m-blsz) = T_old(:m-blsz,:m-blsz); T(m-blsz+1:m,:m-blsz) = 0.0d0;
        T(:m-blsz,m-blsz+1:m) = matmul(matmul(T_old,tcg(matmul(tcg(VC(:,m-blsz+1:m)),VC(:,1:m-blsz)))),T_blk)
        T(m-blsz+1:m,m-blsz+1:m) = T_blk(:blsz,:blsz)
        deallocate(T_old)
      endif

      ! Resize and reactualize VC
      if(m <= blsz*nbls) then
        VC_old => VC
        allocate(VC(n,m+blsz))
        do j=1,m
          VC(:n,j) = VC_old(:n,j)
        enddo
        deallocate(VC_old)
        VC(:,m+1:m+blsz) = matmul(VC(:,:m),matmul(T,tcg(VC(m-blsz+1:m,:m))))
        do i=1,blsz
          VC(i+m-blsz,i+m) = VC(i+m-blsz,i+m) + 1.0d0
        enddo
        VC(:,m+1:m+blsz) = atv(VC(:,m+1:m+blsz),n,blsz,iter,iptrk,ipsys,ipflux)
        allocate(work2d(n,blsz))
        work2d(:,:) = matmul(VC(:,:m),tcg(matmul(matmul(tcg(VC(:,m+1:m+blsz)),VC(:,:m)),T)))
        VC(:,m+1:m+blsz) = VC(:,m+1:m+blsz) + work2d(:,:)
        deallocate(work2d)
      endif
      m = m + blsz
    enddo
    deallocate(T_blk)

    ! Determine the size of the block Hessenberg matrix H. Possible truncation may occur
    ! if an invariant subspace has been found.
    Hsz_n = size(H,1); Hsz_m = size(H,2);
    
    ! Compute the eigenvalue decomposition of the block Hessenberg H(:Hsz_m,:).
    if(associated(H_eigv)) deallocate(H_eigv,H_eig,DH_eig)
    allocate(H_eigv(Hsz_m,Hsz_m), H_eig(Hsz_m,Hsz_m), DH_eig(Hsz_m))
    allocate(work2d_r(Hsz_m, Hsz_m))
    work2d_r(:Hsz_m,:Hsz_m)=real(H(:Hsz_m,:Hsz_m))
    call ALHQR(Hsz_m, Hsz_m, work2d_r, 200, jter, H_eigv, H_eig)
    deallocate(work2d_r)
    do i=1,Hsz_m
      DH_eig(i) = H_eig(i,i)
    enddo

    ! Check the accuracy of the computation of the eigenvalues of the
    ! Hessenberg matrix. This is used to monitor balancing.
    conv = 'F';  ! Boolean to determine if all desired eigenpairs have converged.

    ! Sort the eigenvalue and eigenvector arrays.
    allocate(iset(Hsz_m),work1d(Hsz_m),work2d(Hsz_m,Hsz_m))
    call ALINDX(Hsz_m, DH_eig(:), iset)
    do i=1,Hsz_m
      work1d(i) = DH_eig(iset(i))
      work2d(:Hsz_m,i) = H_eigv(:Hsz_m,iset(i))
    enddo
    DH_eig(:Hsz_m) = work1d(:Hsz_m); H_eigv(:Hsz_m,:Hsz_m) = work2d(:Hsz_m,:Hsz_m)
    deallocate(work2d,work1d,iset)

    ! Compute the residuals for the K_org Ritz values.
    residuals(:K_org) = sqrt(sum(abs(matmul(H(Hsz_n-blsz+1:Hsz_n,Hsz_m-blsz+1:Hsz_m), &
             H_eigv(Hsz_m-blsz+1:Hsz_m,:K_org)))**2, 1))
    if(impx > 1) write(iunout,200) iter,residuals(:K_org)
             
    ! Check for convergence.
    conv = 'T'
    do i=1,K_org
      if(residuals(i) >= tol2*abs(DH_eig(i))) conv = 'F'
    enddo

    ! Adjust K to include more vectors as the number of vectors converge.
    K =  K_org + adjust
    do i=1,K_org
      if(residuals(i) < eps*abs(DH_eig(i))) K = K+1
    enddo
    if(K > Hsz_m - 2*blsz-1) K = Hsz_m - 2*blsz-1

    ! Determine if K splits a conjugate pair. If so replace K with K + 1.
    if(aimag(DH_eig(K)) /= 0.0d0) then
      eig_conj = .true.
      if(K < Hsz_m) then
        if(abs(aimag(DH_eig(K)) + aimag(DH_eig(K+1))) < sqrt(eps)) then
          K = K + 1
          eig_conj = .false.
        endif
      endif
      if(K > 1 .and. eig_conj) then
        if(abs(aimag(DH_eig(K)) + aimag(DH_eig(K-1))) < sqrt(eps)) then
          eig_conj = .false.
        endif
      endif
      if(eig_conj) then
        write(hsmg,'(9h ALBEIGS:,i5,25h-th conjugate pair split.)') K
        call XABORT(hsmg)
      endif
    endif

    ! If all desired Ritz values converged then exit main loop.
    if(conv == 'T')  exit

    ! Compute the QR factorization of H_eigv(:,:K).
    allocate(iset(Hsz_m))
    nset=0
    do i=1,Hsz_m
      if(abs(aimag(DH_eig(i))) > 1.0d-10) then
        nset=nset+1
        iset(nset)=i
      endif
    enddo
    allocate(Q(Hsz_m,K))
    if(nset == 0) then
      Q(:,:) = H_eigv(:,:K)
    else
      ! Convert the complex eigenvectors of the eigenvalue decomposition of H
      ! to real vectors and convert the complex diagonal matrix to block diagonal.
      allocate(work2d(Hsz_m,Hsz_m)); work2d(:Hsz_m,:Hsz_m) = 0.0d0;
      do i=1,Hsz_m
        work2d(i,i) = 1.0d0
      enddo
      twobytwo(1,1) = cmplx(1.0d0, 0.0d0, kind=8); twobytwo(2,1) = cmplx(0.0d0, 1.0d0, kind=8);
      twobytwo(1,2) = cmplx(1.0d0, 0.0d0, kind=8); twobytwo(2,2) = -cmplx(0.0d0, 1.0d0, kind=8);
      do i=1,Hsz_m
        ii=findlc(iset(:nset),i)
        if(mod(ii-1,2)+1.eq.1) then
          if(conjg(DH_eig(i)) /= DH_eig(i+1)) call XABORT('ALBEIGS: invalid diagonal')
          work2d(i:i+1,i:i+1) = twobytwo;
        endif
      enddo
      call ALINVC(Hsz_m,work2d,Hsz_m,ier)
      if(ier /= 0) call XABORT('ALBEIGS: singular matrix(1)')
      Q(:,:) = matmul(H_eigv(:,:Hsz_m),work2d(:Hsz_m,:K))
      deallocate(work2d)
    endif
    deallocate(iset)
    allocate(work2d_r(Hsz_m,K))
    do i=1,Hsz_m
      work2d_r(i,:K) = real(Q(i,:K))
    enddo
    deallocate(Q)
    call ALST2F(Hsz_m,Hsz_m,K,work2d_r(:,:K),tau)
    allocate(QR(Hsz_m,K)); QR(:Hsz_m,:K) = 0.0d0;
    do i=1,K
      QR(i,i) = 1.0d0
    enddo
    do j = K,1,-1
      allocate(w(Hsz_m-j+1,1))
      w(:,:) = reshape((/1.0d0, work2d_r(j+1:Hsz_m,j)/), (/Hsz_m-j+1, 1/))
      QR(j:Hsz_m,:) = QR(j:Hsz_m,:)+tau(j)*matmul(w,matmul(transpose(w),QR(j:Hsz_m,:)))
      deallocate(w)
    enddo
    deallocate(work2d_r)

    ! The Schur matrix for H.
    allocate(T_Schur(K,K))
    T_Schur = matmul(matmul(transpose(QR),real(H(:Hsz_m,:))),QR)
    do i=3,K
      T_Schur(i,:i-2) = 0.0d0
    enddo

    ! Compute the starting vectors and the residual vectors from the Householder
    ! WY form. The starting vectors will be the first K Schur vectors and the
    ! residual vectors are stored as the last blsz vectors in the Householder WY form.
    Tsz_m = size(T,1)
    VC(:,Hsz_n-blsz+1:Hsz_n)= matmul(VC(:,:Tsz_m),matmul(T,tcg(VC(Tsz_m-blsz+1:Tsz_m,:Tsz_m))))
    do i=Tsz_m-blsz+1,Tsz_m-blsz+blsz
      VC(i,i) = VC(i,i) + 1.0d0
    enddo
    allocate(work2d(n,K))
    do j=1,K
      work2d(:,j) = matmul(VC(:,:Hsz_m),matmul(T(:Hsz_m,:Hsz_m),matmul(tcg(VC(:Hsz_m,:Hsz_m)),QR(:,j))))
    enddo
    do j=1,K
      VC(:,j) = work2d(:,j)
      VC(:Hsz_m,j) = QR(:Hsz_m,j) + VC(:Hsz_m,j)
    enddo
    deallocate(work2d)

    ! Set the size of the large matrix VC and move the residual vectors.
    m = K + 2*blsz; VC(:,K+1:K+blsz) = VC(:,Hsz_n-blsz+1:Hsz_n);

    ! Set the new starting vector(s) to be the desired vectors VC(:,:K) with the
    ! residual vectors VC(:,Hsz_n-blsz+1:Hsz_n). Place all vectors in the compact
    ! WY form of the Householder product. Compute the next set of vectors by
    ! computing A*VC(:,Hsz_n-blsz+1:Hsz_n) and store this in VC(:,Hsz_n+1:Hsz_n+blsz).
    m2=m-blsz
    allocate(R(m2,m2), V_sign(m2), DD(m2,m2))
    R(:m2,:m2) = 0.0d0; V_sign(:m2) = 1.0d0; DD(:m2,:m2) = 0.0d0;
    deallocate(T)
    allocate(T(m2,m2))
    T = VC(:m2,:m2)
    VC(:,m2+1:m) = atv(VC(:,m2-blsz+1:m2),n,blsz,iter,iptrk,ipsys,ipflux)
    do i =1,m2
      V_sign(i) = VC(i,i)/abs(VC(i,i))
      if(VC(i,i) == 0.0d0) V_sign(i)=1.0d0
      R(i,i) = -V_sign(i)
      Vdot = 1.0d0 + V_sign(i)*VC(i,i)  ! Dot product of Householder vectors.
      VC(i,i) = VC(i,i) + V_sign(i)     ! Reflection to the ith axis.
      DD(i,i) = 1.0d0/VC(i,i)           ! Used for scaling. Note: VC(i,i) >= 1.
      VC(:m2,i+1:m2) = VC(:m2,i+1:m2) - (V_sign(i)/Vdot)*matmul(VC(:m2,i:i),VC(i:i,i+1:m2))
    enddo
    VC(:m2,:m2) = matmul(VC(:m2,:m2),DD)
    deallocate(DD, V_sign)
    VC(:,m2+1:m) = matmul(VC(:,m2+1:m),R(m2-blsz+1:m2,m2-blsz+1:m2))
    T = matmul(T,R)
    do i=1,m2
      VC(i,i+1:m2) = 0.0d0
      T(i,i) = T(i,i) - 1.0d0
    enddo
    allocate(H_R(m2,m2))
    H_R(:m2,:m2) = tcg(VC(:m2,:m2))
    call ALINVC(m2,H_R,m2,ier)
    if(ier /= 0) call XABORT('ALBEIGS: singular matrix(2)')
    T(:m2,:m2) = matmul(T, H_R)
    H_R(:m2,:m2) = VC(:m2,:m2)
    call ALINVC(m2,H_R,m2,ier)
    if(ier /= 0) call XABORT('ALBEIGS: singular matrix(3)')
    T(:m2,:m2) = matmul(H_R, T)
    do i=2,m2
      T(i,:i-1) = 0.0d0
    enddo
    H_R(:m2,:m2) = matmul(T,tcg(VC(:m2,:m2)))
    call ALINVC(m2,H_R,m2,ier)
    if(ier /= 0) call XABORT('ALBEIGS: singular matrix(4)')
    allocate(work2d(n-m2,m2))
    do j=1,m2
      work2d(:n-m2,j) = matmul(VC(m2+1:n,:m2),matmul(R(:m2,:m2),H_R(:m2,j)))
    enddo
    do j=1,m2
      VC(m2+1:n,j) = work2d(:n-m2,j)
    enddo
    deallocate(work2d, H_R)
    VC(:,m2+1:m) = VC(:,m2+1:m) + matmul(VC(:,:m2),tcg(matmul(matmul(tcg(VC(:,m2+1:m)),VC(:,:m2)),T)))

    ! Compute the first K columns and K+blsz rows of the matrix H, used in augmenting.
    allocate(H_R(blsz,blsz))
    H_R(:blsz,:blsz) = H(Hsz_n-blsz+1:Hsz_n,Hsz_m-blsz+1:Hsz_m)
    deallocate(H)
    allocate(H(K+blsz,K)); H(:K+blsz,:K)=0.0d0;
    H(:K,:K) = matmul(R(:K,:K),matmul(T_Schur(:K,:K),R(:K,:K)))
    H(K+1:K+blsz,:K) = matmul(R(K+1:K+blsz,K+1:K+blsz),matmul(H_R, &
                           matmul(QR(Hsz_m-(blsz-1):Hsz_m,:K),R(:K,:K))))
    deallocate(T_Schur, H_R, R, QR)
  enddo
  deallocate(residuals,tau)

  ! Truncated eigenvalue and eigenvector arrays to include only desired eigenpairs.
  Tsz_m = size(T,1); H_sz1 = size(H_eigv,1); H_sz2 = size(H_eigv,2);
  do j=1,H_sz2
    VC(:,j) = matmul(VC(:,:Tsz_m),matmul(T,matmul(tcg(VC(:H_sz1,:Tsz_m)),H_eigv(:H_sz1,j))))
  enddo
  VC(:H_sz1,:H_sz2) = H_eigv + VC(:H_sz1,:H_sz2)

  ! Set the first K_org eigensolutions
  D(:K_org,:K_org)=0.0d0
  do i=1,K_org
    V(:,i) = VC(:,i)
    D(i,i) = DH_eig(i)
  enddo
  deallocate(H_eigv,H_eig,DH_eig,VC)
  return
  !
  200 format(25h ALBEIGS: outer iteration,i4,12h  residuals=,1p,10e12.4/(41x,10e12.4))

  contains
  function tcg(ac) result(bc)
    ! function emulating complex conjugate transpose in Matlab
    complex(kind=8), dimension(:,:), intent(in) :: ac
    complex(kind=8), dimension(size(ac,2),size(ac,1)) :: bc
    bc(:,:)=transpose(conjg(ac(:,:)))
  end function tcg
  function findlc(iset,itest) result(ii)
    ! function emulating the findloc function in Fortran 2008
    integer, dimension(:), intent(in) :: iset
    integer, intent(in) :: itest
    integer :: ii
    ii=0
    do j=1,size(iset)
      if(iset(j) == itest) then
        ii=j
        exit
      endif
    enddo
  end function findlc
end subroutine ALBEIGS