-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblock_diag_zgeev_common.f90
134 lines (133 loc) · 5.56 KB
/
block_diag_zgeev_common.f90
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
! subroutine block_zgeev(h,e,eps)
! integer, parameter :: ok = rk
complex(ok), intent(inout) :: h(:,:,:)! In: h(:,:,1) = general matrix to be diagonalized
! Out: h(:,:,1) = Left eigenvectors
! h(:,:,2) = Right eigenvectors
complex(ok), intent(out) :: e(:) ! Out: Eigenvalues. Note that eigenvalues are NOT sorted
real(ok), intent(in), optional :: eps ! Threshold for treating elements as zero;
! Missing or negative value will be replaces by the
! ! default threshold of 1000*spacing(maxval(abs(h))
!
integer(ik) :: sz ! Number of rows in matrix h
integer(ik) :: n_avail ! Number of available rows in matrix h
integer(ik) :: ir ! Row index
integer(ik) :: alloc
logical, allocatable :: available(:) ! True for each row of h which has not been processed yet
real(ok) :: thr ! zero threshold
integer(ik), allocatable :: blk_rows(:) ! List of rows in the current connected block
integer(ik) :: blk_sz ! Size of the current connected block
complex(ok), allocatable :: tmp_h(:,:,:) ! Temporary used for diagonalizing the connected block
complex(ok), allocatable :: tmp_e(:) ! ditto
logical :: block_grown ! .true. if block has grown on the current pass; do another pass
!
call TimerStart('block_geev (complex)')
sz = size(h,dim=1)
if (sz/=size(h,dim=2) .or. sz/=size(e)) stop 'block_diag%block_geev - dimensions mismatch'
if (size(h,dim=3)/=2) stop 'block_diag%block_geev - third dimension of h is not 2?!'
!
thr = 1e3_ok * spacing(maxval(abs(h(:,:,1))))
if (present(eps)) then
if (eps>=0) thr = eps
end if
if (verbose>=1) then
write (out,"('Block-diagonalization zero threshold is ',g20.10)") thr
end if
!
allocate (available(sz),blk_rows(sz),stat=alloc)
if (alloc/=0) stop 'block_diag%block_geev - allocation failed (1)'
!
available(:) = .true.
n_avail = sz
block_loop: do while(n_avail>0)
blk_sz = 0
grow_block: do
block_grown = .false.
find_first_unused: do ir=1,sz
if (.not.available(ir)) cycle find_first_unused
if (blk_sz==0) then
!
! Find the first unused row
!
call add_row
else
!
! If this row is connected to any of the rows already in the list,
! add it as well. Make sure to check both row and column connections
!
if (any(abs(h( blk_rows(1:blk_sz),ir,1))>thr) .or. &
any(abs(h(ir,blk_rows(1:blk_sz) ,1))>thr)) call add_row
end if
end do find_first_unused
if (blk_sz==0) stop 'block_diag%block_geev - logic error: no block found'
if (.not.block_grown) exit grow_block
! Otherwise, we need another pass to make sure we have not missed any connected rows
end do grow_block
!
if (verbose>=1) then
write (out,"('Found connected block of dimension ',i8,' containing rows/columns:')") blk_sz
write (out,"(10(1x,i8))") blk_rows(1:blk_sz)
write (out,"()")
end if
!
! Extract the connected block
!
allocate (tmp_h(blk_sz,blk_sz,2),tmp_e(blk_sz),stat=alloc)
if (alloc/=0) stop 'block_diag%block_geev - allocation failed (2)'
tmp_h(:,:,1) = h(blk_rows(1:blk_sz),blk_rows(1:blk_sz),1)
!
if (verbose>=2) then
write (out,"('The connected block is: ')")
call print_matrix(tmp_h(:,:,1),12_ik,'g12.4')
end if
!
! A bit of extra debugging here: zero out the connected block in h(:,:),
! then make sure that there are no additional connections for the columns
! in the current block. Once this is done, zero the remainder of the
! rows/columns
!
h(blk_rows(1:blk_sz),blk_rows(1:blk_sz),1) = 0
if (any(abs(h( blk_rows(1:blk_sz),:,1))>1.1_ok*thr) .or. &
any(abs(h(:,blk_rows(1:blk_sz), 1))>1.1_ok*thr)) then
stop 'block_diag%block_geev - logic error: found non-zeros in an uncoupled block'
end if
h( blk_rows(1:blk_sz),:,:) = 0
h(:,blk_rows(1:blk_sz), :) = 0
!
! Diagonalize the sub-block
!
call lapack_geev(tmp_h,tmp_e)
if (verbose>=1) then
write (out,"('Block eigenvalues are:')")
write (out,"(5(1x,g20.12))") tmp_e
write (out,"()")
end if
if (verbose>=2) then
write (out,"('Block left eigenvectors are:')")
call print_matrix(tmp_h(:,:,1),20,'g20.12')
write (out,"('Block right eigenvectors are:')")
call print_matrix(tmp_h(:,:,2),20,'g20.12')
end if
!
! Store eigenvalues and eigenvectors back into the input/output matrix
!
e(blk_rows(1:blk_sz)) = tmp_e
h(blk_rows(1:blk_sz),blk_rows(1:blk_sz),:) = tmp_h
!
deallocate (tmp_h,tmp_e)
end do block_loop
!
! There is no universal sensible order for complex eigenvalues, so leave them alone
!
deallocate (available,blk_rows)
call TimerStop('block_geev (complex)')
!
contains
!
subroutine add_row
blk_sz = blk_sz + 1
blk_rows(blk_sz) = ir
available(ir) = .false.
n_avail = n_avail - 1
block_grown = .true.
end subroutine add_row
! end subroutine block_zgeev