MPI-SCATCI  2.0
An MPI version of SCATCI
SCALAPACKDiagonalizer_module.f90
Go to the documentation of this file.
1 ! Copyright 2019
2 !
3 ! For a comprehensive list of the developers that contributed to these codes
4 ! see the UK-AMOR website.
5 !
6 ! This file is part of UKRmol-in (UKRmol+ suite).
7 !
8 ! UKRmol-in is free software: you can redistribute it and/or modify
9 ! it under the terms of the GNU General Public License as published by
10 ! the Free Software Foundation, either version 3 of the License, or
11 ! (at your option) any later version.
12 !
13 ! UKRmol-in is distributed in the hope that it will be useful,
14 ! but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ! GNU General Public License for more details.
17 !
18 ! You should have received a copy of the GNU General Public License
19 ! along with UKRmol-in (in source/COPYING). Alternatively, you can also visit
20 ! <https://www.gnu.org/licenses/>.
21 
33 
34  use blas_lapack_gbl, only: blasint
35  use const_gbl, only: stdout
37  use mpi_gbl, only: master, myrank, mpi_xermsg
38  use precisn, only: wp
40  use basematrix_module, only: basematrix
43  use options_module, only: options
44  use parallelization_module, only: grid => process_grid
46  use timing_module, only: master_timer
47 
48  implicit none
49 
51 
52  private
53 
55  contains
56  procedure, public :: diagonalize => diagonalize_scalapack
57  procedure, public :: diagonalize_backend => diagonalize_backend_scalapack
58  procedure, public :: process_solution
59  end type scalapackdiagonalizer
60 
61 contains
62 
63  subroutine diagonalize_scalapack (this, matrix_elements, num_eigenpair, dresult, all_procs, option, integrals)
64 
65  class(scalapackdiagonalizer) :: this
66  class(diagonalizerresult) :: dresult
67  class(basematrix), intent(in) :: matrix_elements
68  class(baseintegral), intent(in) :: integrals
69  type(options), intent(in) :: option
70  integer, intent(in) :: num_eigenpair
71  logical, intent(in) :: all_procs
72 
73  real(wp), allocatable :: z_mat(:,:), w(:)
74  integer :: ierr
75 
76  write (stdout, "('Utilizing Optimized SCALAPACK matrix')")
77  write (stdout, "('N: ',I0)") matrix_elements % get_matrix_size()
78  write (stdout, "('Requested # of eigenpairs ',I0)") num_eigenpair
79 
80  ! call the diagonalization backend
81  select type(matrix_elements)
82  class is (scalapackmatrix)
83  call this % diagonalize_backend(matrix_elements, num_eigenpair, z_mat, w, all_procs, option, integrals)
84  call this % process_solution(matrix_elements, num_eigenpair, z_mat, w, dresult, all_procs, option, integrals)
85  end select
86 
87  end subroutine diagonalize_scalapack
88 
89 
90  subroutine diagonalize_backend_scalapack (this, matrix_elements, num_eigenpair, z_mat, w, all_procs, option, integrals)
91 
92  class(scalapackdiagonalizer) :: this
93  class(scalapackmatrix), intent(in) :: matrix_elements
94  class(baseintegral), intent(in) :: integrals
95  type(options), intent(in) :: option
96  integer, intent(in) :: num_eigenpair
97  logical, intent(in) :: all_procs
98  real(wp), allocatable, intent(inout) :: z_mat(:,:), w(:)
99 
100  real(wp), allocatable :: work(:), pdsyevd_lwork(:), pdormtr_lwork(:)
101  integer(blasint), allocatable :: iwork(:)
102  integer(blasint) :: lwork, liwork, npcol, mat_dimen, info, loc_r, loc_c, one = 1
103  integer :: ierr
104 
105  write (stdout, "('Diagonalization will be done with SCALAPACK')")
106 
107  npcol = grid % gpcols
108  loc_r = matrix_elements % local_row_dimen
109  loc_c = matrix_elements % local_col_dimen
110  mat_dimen = matrix_elements % get_matrix_size()
111 
112  ! allocate the eigenvectors
113  allocate(z_mat(loc_r, loc_c), w(mat_dimen), stat = ierr)
114  if (ierr /= 0) then
115  call mpi_xermsg('SCALAPACKDiagonalizer_module', 'diagonalize_backend_SCALAPACK', 'Memory allocation error.', ierr, 1)
116  end if
117 
118  ! Calculating the workspace size is quite a bit of alchemy (no pun intended). The ScaLAPACK subroutine `pdsyevd` has some
119  ! well-defined requirements which it correctly returns on a workspace query. However, when eigenvectors are desired,
120  ! `pdsyevd` eventually calls `pdormtr` and requirements of that subroutine are not fully included in the "optimal" work
121  ! size returned by `pdsyevd`. From the inspection of the ScaLAPACK code, `pdsyevd` will always require 2*mat_dimen elements
122  ! on top of any `pdormtr` workspace. So, we query both subroutines for the real workspace size and use the larger of
123  !
124  ! lwork[pdsyevd]
125  !
126  ! and
127  !
128  ! lwork[pdormtr] + 2*mat_dimen
129 
130  liwork = 7*mat_dimen + 8*npcol + 2
131 
132  allocate(pdsyevd_lwork(1), pdormtr_lwork(1), iwork(liwork))
133 
134  call pdsyevd('V', 'L', mat_dimen, matrix_elements % a_local_matrix, one, one, matrix_elements % descr_a_mat, w, z_mat, &
135  one, one, matrix_elements % descr_z_mat, pdsyevd_lwork, -one, iwork, liwork, info)
136  call pdormtr('L', 'L', 'N', mat_dimen, mat_dimen, matrix_elements % a_local_matrix, one, one, &
137  matrix_elements % descr_a_mat, w, z_mat, one, one, matrix_elements % descr_z_mat, pdormtr_lwork, -one, info)
138 
139  lwork = ceiling(max(pdsyevd_lwork(1), pdormtr_lwork(1) + 2 * mat_dimen))
140 
141  allocate(work(lwork), stat = info)
142 
143  if (info == 0) then
144  write (stdout, "('ScaLAPACK workspace size: lwork = ',I0,', liwork = ',I0)") lwork, liwork
145  else
146  call mpi_xermsg('SCALAPACKDiagonalizer_module', 'DoSCALAPACKMat', 'Memory allocation error', int(info), 1)
147  end if
148 
149  ! And now the distributed diagonalization itself.
150 
151  call pdsyevd('V', 'L', mat_dimen, matrix_elements % a_local_matrix, one, one, matrix_elements % descr_a_mat, w, z_mat, &
152  one, one, matrix_elements % descr_z_mat, work, lwork, iwork, liwork, info)
153 
154  if (info == 0) then
155  write (stdout, "(/'Diagonalization finished successfully!')")
156  else
157  call mpi_xermsg('SCALAPACKDiagonalizer_module', 'DoSCALAPACKMat', 'PDSYEVD returned non-zero code', int(info), 1)
158  end if
159 
160  end subroutine diagonalize_backend_scalapack
161 
162 
163  subroutine process_solution (this, matrix_elements, num_eigenpair, z_mat, w, dresult, all_procs, option, integrals)
164 
165  class(scalapackdiagonalizer) :: this
166  class(diagonalizerresult) :: dresult
167  class(scalapackmatrix), intent(in) :: matrix_elements
168  class(baseintegral), intent(in) :: integrals
169  type(options), intent(in) :: option
170  integer, intent(in) :: num_eigenpair
171  logical, intent(in) :: all_procs
172  real(wp), allocatable, intent(inout) :: z_mat(:,:), w(:)
173 
174  real(wp), allocatable :: global_vector(:)
175 
176  integer(blasint) :: myrow, mycol, nprow, npcol, blacs_context, i, j, iprow, ipcol
177  integer(blasint) :: mat_dimen, nb, loc_r, loc_c, jdimen, idimen
178 
179  blacs_context = grid % gcntxt
180  myrow = grid % mygrow
181  mycol = grid % mygcol
182  nprow = grid % gprows
183  npcol = grid % gpcols
184 
185  mat_dimen = matrix_elements % get_matrix_size()
186  loc_r = matrix_elements % local_row_dimen
187  loc_c = matrix_elements % local_col_dimen
188  nb = matrix_elements % scal_block_size
189 
190  if (iand(dresult % vector_storage, save_continuum_coeffs) /= 0 .and. &
191  iand(dresult % vector_storage, save_l2_coeffs) == 0) then
192  write (stdout, '(/," Only the continuum part of the eigenvectors will be saved to disk.")')
193  write (stdout, '(" Index of the last continuum configuration: ",i10)') dresult % continuum_dimen
194  end if
195 
196  ! save the results to disk
197  allocate(global_vector(mat_dimen))
198  call blacs_barrier(blacs_context, 'a')
199 
200  ! store Hamiltonian information into the CDENPROP vector
201  if (iand(option % vector_storage_method, pass_to_cdenprop) /= 0) then
202  call dresult % export_header(option, integrals)
203  end if
204 
205  ! write Hamiltonian information into the output disk file (the current context master will do this)
206  if (grid % grank == master) then
207  call dresult % write_header(option, integrals)
208  end if
209 
210  ! save eigenvalues by master of the context (to disk), or by everyone (to internal arrays)
211  if (all_procs .or. grid % grank == master) then
212  call dresult % handle_eigenvalues(w(1:num_eigenpair), matrix_elements % diagonal, num_eigenpair, int(mat_dimen))
213  end if
214 
215  ! reconstruct and save the eigenvectors to the disk file
216  call master_timer % start_timer("Collect eigenvectors")
217  do jdimen = 1, num_eigenpair
218 
219  ! clear the local copy of the eigenvector
220  global_vector = 0
221 
222  ! set non-zero elements of global_vector by processes that own those elements
223  do idimen = 1, mat_dimen
224  call infog2l(idimen, jdimen, matrix_elements % descr_z_mat, nprow, npcol, myrow, mycol, i, j, iprow, ipcol)
225  if (myrow == iprow .and. mycol == ipcol) then
226  global_vector(idimen) = z_mat(i,j)
227  end if
228  end do
229 
230  ! collect the eigenvector...
231  if (all_procs) then
232  ! ... to all members of the context and let all of them 'handle' the eigenvector
233  call dgsum2d(blacs_context, 'all', ' ', mat_dimen, 1, global_vector, mat_dimen, -1, -1)
234  call dresult % handle_eigenvector(global_vector, int(mat_dimen))
235  else
236  ! ... to master of the context and let it 'handle' the eigenvector
237  call dgsum2d(blacs_context, 'all', ' ', mat_dimen, 1, global_vector, mat_dimen, 0, 0)
238  if (grid % grank == master) then
239  call dresult % handle_eigenvector(global_vector, int(mat_dimen))
240  end if
241  end if
242 
243  end do
244  call master_timer % stop_timer("Collect eigenvectors")
245 
246  ! fill cdenprop data structure for further processing of eigenvectors, if required
247  if (iand(dresult % vector_storage, pass_to_cdenprop) /= 0) then
248  !Distributed eigenvectors and the descriptor:
249  if (allocated(dresult % ci_vec % CV)) deallocate(dresult % ci_vec % CV)
250  call move_alloc(z_mat, dresult % ci_vec % CV)
251  dresult % ci_vec % blacs_context = blacs_context
252  dresult % ci_vec % local_row_dimen = loc_r
253  dresult % ci_vec % local_col_dimen = loc_c
254  dresult % ci_vec % scal_block_size = nb
255  dresult % ci_vec % myrow = myrow
256  dresult % ci_vec % mycol = mycol
257  dresult % ci_vec % nprow = nprow
258  dresult % ci_vec % npcol = npcol
259  dresult % ci_vec % mat_dimen = matrix_elements % get_matrix_size()
260  dresult % ci_vec % descr_CV_mat = matrix_elements % descr_z_mat
261  dresult % ci_vec % lda = matrix_elements % lda
262  dresult % ci_vec % CV_is_scalapack = .true.
263 
264  dresult % ci_vec % mat_dimen_r = dresult % ci_vec % mat_dimen
265  dresult % ci_vec % mat_dimen_c = dresult % ci_vec % mat_dimen
266 
267  !Eigenvalues and phases:
268  call dresult % export_eigenvalues(w(1:num_eigenpair), matrix_elements % diagonal, num_eigenpair, &
269  int(mat_dimen), dresult % ci_vec % ei, dresult % ci_vec % iphz)
270 
271  write (stdout, '(/," Eigensolutions exported into the CIVect structure.",/)')
272  end if
273 
274  ! finish writing the solution file and let other processes access it
275  if (grid % grank == master) then
276  call dresult % finalize_solutions(option)
277  end if
278 
279  if (allocated(z_mat)) deallocate(z_mat)
280 
281  call blacs_barrier(blacs_context, 'a')
282 
283  end subroutine process_solution
284 
diagonalizerresult_module::diagonalizerresult
Output from diagonalization.
Definition: DiagonalizerResult_module.f90:48
consts_mpi_ci::save_l2_coeffs
integer, parameter save_l2_coeffs
Keep only L2 section of the eigenvectors (omit continuum channels)
Definition: consts_mpi_ci.f90:112
timing_module
Timing module.
Definition: Timing_Module.f90:28
options_module::options
Calculation setup for a single Hamiltonian diagonalization.
Definition: Options_module.f90:74
consts_mpi_ci::save_continuum_coeffs
integer, parameter save_continuum_coeffs
Omit L2 section of the eigenvectors (only keep continuum channels)
Definition: consts_mpi_ci.f90:108
parallelization_module::process_grid
type(processgrid) process_grid
Definition: Parallelization_module.F90:80
scalapackdiagonalizer_module::diagonalize_backend_scalapack
subroutine diagonalize_backend_scalapack(this, matrix_elements, num_eigenpair, z_mat, w, all_procs, option, integrals)
Definition: SCALAPACKDiagonalizer_module.f90:91
basematrix_module
Base matrix module.
Definition: BaseMatrix_module.f90:28
consts_mpi_ci
MPI SCATCI Constants module.
Definition: consts_mpi_ci.f90:31
scalapackdiagonalizer_module::process_solution
subroutine process_solution(this, matrix_elements, num_eigenpair, z_mat, w, dresult, all_procs, option, integrals)
Definition: SCALAPACKDiagonalizer_module.f90:164
basematrix_module::basematrix
Base matrix type.
Definition: BaseMatrix_module.f90:47
baseintegral_module
Base integral module.
Definition: BaseIntegral_module.f90:28
parallelization_module
Distribution of processes into a grid.
Definition: Parallelization_module.F90:29
scalapackmatrix_module::scalapackmatrix
Definition: SCALAPACKMatrix_module.f90:47
diagonalizerresult_module
Diagonalizer result data object.
Definition: DiagonalizerResult_module.f90:28
diagonalizer_module
Base type for all diagonalizers.
Definition: Diagonalizer_module.f90:30
diagonalizer_module::basediagonalizer
Definition: Diagonalizer_module.f90:34
baseintegral_module::baseintegral
Definition: BaseIntegral_module.f90:41
scalapackdiagonalizer_module::scalapackdiagonalizer
Definition: SCALAPACKDiagonalizer_module.f90:54
scalapackdiagonalizer_module::diagonalize_scalapack
subroutine diagonalize_scalapack(this, matrix_elements, num_eigenpair, dresult, all_procs, option, integrals)
Definition: SCALAPACKDiagonalizer_module.f90:64
options_module
Options module.
Definition: Options_module.f90:41
timing_module::master_timer
type(timer), public master_timer
Definition: Timing_Module.f90:74
scalapackmatrix_module
ScaLAPACK matrix module.
Definition: SCALAPACKMatrix_module.f90:32
scalapackdiagonalizer_module
Diagonalizer type using ScaLAPACK backend.
Definition: SCALAPACKDiagonalizer_module.f90:32
consts_mpi_ci::pass_to_cdenprop
integer, parameter pass_to_cdenprop
Eigenpairs will be saved in memory and passed to CDENPROP and outer interface.
Definition: consts_mpi_ci.f90:110